Commit 62fabe5c authored by Richard Torenvliet's avatar Richard Torenvliet

Worked on a viewer application in the browser

parent 9c87511e
...@@ -62,3 +62,6 @@ test: ...@@ -62,3 +62,6 @@ test:
test_modules: test_modules:
python -m py.test -f src/*/*_test.py python -m py.test -f src/*/*_test.py
run:
python server.py
import json
import base64
from glob import glob
from tornado import websocket, web, ioloop
BASE = 'viewer/app'
FILES_DIR = 'data/'
FACE_DB = '{}{}'.format(FILES_DIR, 'imm_face_db')
class SocketHandler(websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
self.images = glob('{}/*.jpg'.format(FACE_DB))
websocket.WebSocketHandler.__init__(self, *args, **kwargs)
def check_origin(self, origin):
return True
def open(self):
print("WebSocket opened")
self.write_message(json.dumps({'n_images': len(self.images)}))
def on_message(self, message):
message = json.loads(message)
image_index = message['image_index']
filename = self.images[image_index]
image = None
with open(filename, "rb") as f:
image = base64.b64encode(f.read())
self.write_message(json.dumps({'image': image}))
def on_close(self):
print("WebSocket closed")
#class ApiHandler(web.RequestHandler):
# def __init__(self, *args, **kwargs):
# self.images = glob('{}/*.jpg'.format(FACE_DB))
# websocket.RequestHandler.__init__(self, *args, **kwargs)
#
# @web.asynchronous
# def get(self, *args):
# self.finish()
#
# if self.get_argument('images'):
# self.get_images()
#
# #id = self.get_argument("id")
# #value = self.get_argument("value")
# #data = {"id": id, "value" : value}
# #data = json.dumps(data)
#
# #self.write_message(data)
#
# @web.asynchronous
# def post(self):
# pass
app = web.Application([
(r'/reconstruction', SocketHandler),
])
if __name__ == '__main__':
app.listen(8888)
ioloop.IOLoop.instance().start()
...@@ -28,6 +28,81 @@ import pca ...@@ -28,6 +28,81 @@ import pca
import aam import aam
class ReconstructCanvas(Widget):
def __init__(self, **kwargs):
super(ReconstructCanvas, self).__init__(**kwargs)
self.canvas.clear()
with self.canvas:
self.texture = InstructionGroup()
def build_texture(self, filename, Vt_texture, input_points, mean_value_points,
mean_values_texture, triangles, n_texture_components):
self.texture.clear()
#image_width, image_height = self.get_rendered_size()
#InputPoints = imm.IMMPoints(filename=filename)
#input_image = InputPoints.get_image()
##buf = np.zeros(input_image.shape, dtype=np.uint8)
##buf = np.repeat(np.array([255, 0, 0]), input_image.shape[0] * input_image.shape[1])
buf = cv2.imread('data/test_data/red_500x500.png')
h, w, c = buf.shape
#InputPoints.get_scaled_points((input_image.shape))
#offset_x, offset_y, w, h = InputPoints.get_bounding_box()
#MeanPoints = imm.IMMPoints(points_list=mean_value_points)
#utils.reconstruct_texture(
# input_image, # src image
# buf, # dst image
# Vt_texture, # Vt
# InputPoints, # shape points input
# MeanPoints, # shape points mean
# mean_values_texture, # mean texture
# triangles, # triangles
# n_texture_components # learned n_texture_components
#)
#ratio_x = image_width / w
#ratio_y = image_height / h
#buf = cv2.resize(np.asarray(buf, np.uint8), (int(image_width), int(image_height)))
#buf = buf[offset_y: offset_y + h, offset_x: offset_x + w]
#size = 64 * 64 * 3
#buf = [int(x * 255 / size) for x in range(size)]
#print buf
#cv2.imshow('original', buf)
#buf = cv2.flip(buf, 0)
# buf = buf.tostring()
#buf = b''.join(map(chr, buf))
#texture = Texture.create(size=self.size, colorfmt='rgb')
#texture.blit_buffer(buf, colorfmt='rgb', bufferfmt='ubyte')
#pos_x = self.center[0] - self.size[0] / 2.0
#pos_y = self.center[1] - self.size[1] / 2.0
texture = Texture.create(size=(w, h), colorfmt="bgr")
#arr = np.ndarray(shape=[16, 16, 3], dtype=np.uint8)
# fill your numpy array here
data = buf.tostring()
texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="bgr")
pos_x = self.center[0] - self.size[0] / 2.0
pos_y = self.center[1] - self.size[1] / 2.0
self.texture.add(
Rectangle(texture=texture, size=(w, h), pos=(pos_x, pos_y))
)
self.canvas.add(self.texture)
self.canvas.ask_update()
class ImageCanvas(Widget): class ImageCanvas(Widget):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(ImageCanvas, self).__init__(**kwargs) super(ImageCanvas, self).__init__(**kwargs)
...@@ -38,9 +113,7 @@ class ImageCanvas(Widget): ...@@ -38,9 +113,7 @@ class ImageCanvas(Widget):
with self.canvas: with self.canvas:
self.image = Image(pos=self.pos, size=self.size, source=self.filename_image) self.image = Image(pos=self.pos, size=self.size, source=self.filename_image)
self.mesh = Mesh(mode='triangle_fan')
self.triangles = InstructionGroup() self.triangles = InstructionGroup()
self.texture = InstructionGroup()
self.outline = InstructionGroup() self.outline = InstructionGroup()
self.bind(pos=self.update_rect, size=self.update_rect) self.bind(pos=self.update_rect, size=self.update_rect)
...@@ -77,49 +150,6 @@ class ImageCanvas(Widget): ...@@ -77,49 +150,6 @@ class ImageCanvas(Widget):
self.image.source = self.filename_image self.image.source = self.filename_image
self.canvas.ask_update() self.canvas.ask_update()
def build_texture(self, filename, Vt_texture, input_points, mean_value_points,
mean_values_texture, triangles, n_texture_components):
self.texture.clear()
image_width, image_height = self.get_rendered_size()
InputPoints = imm.IMMPoints(filename=filename)
input_image = InputPoints.get_image()
buf = np.zeros(input_image.shape, dtype=np.uint8)
InputPoints.get_scaled_points((input_image.shape))
offset_x, offset_y, w, h = InputPoints.get_bounding_box()
MeanPoints = imm.IMMPoints(points_list=mean_value_points)
utils.reconstruct_texture(
input_image, # src image
buf, # dst image
Vt_texture, # Vt
InputPoints, # shape points input
MeanPoints, # shape points mean
mean_values_texture, # mean texture
triangles, # triangles
n_texture_components # learned n_texture_components
)
ratio_x = image_width / w
ratio_y = image_height / h
buf = cv2.resize(np.asarray(buf, np.uint8), (int(image_width), int(image_height)))
#buf = buf[offset_y: offset_y + h, offset_x: offset_x + w]
cv2.imshow('original', buf)
buf = b''.join(map(chr, buf.flatten()))
texture = Texture.create(size=(image_width, image_height), colorfmt='bgr')
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.texture.add(Rectangle(texture=texture, pos=(offset_x, offset_y), size=(w, h)))
self.canvas.add(self.texture)
self.canvas.ask_update()
def build_line_grid(self, r_shape, triangles): def build_line_grid(self, r_shape, triangles):
self.triangles.clear() self.triangles.clear()
...@@ -254,7 +284,7 @@ class RootWidget(BoxLayout): ...@@ -254,7 +284,7 @@ class RootWidget(BoxLayout):
self.ids['image_viewer'].update_rect() self.ids['image_viewer'].update_rect()
self.ids['image_viewer'].update_image(self.filename) self.ids['image_viewer'].update_image(self.filename)
self.ids['image_viewer'].build_line_grid(r_shape, self.triangles) self.ids['image_viewer'].build_line_grid(r_shape, self.triangles)
self.ids['image_viewer'].build_texture( self.ids['reconstruct_viewer'].build_texture(
self.files[self.index], self.files[self.index],
self.eigenv_texture, self.eigenv_texture,
input_points, input_points,
......
...@@ -10,10 +10,12 @@ ...@@ -10,10 +10,12 @@
<RootWidget> <RootWidget>
BoxLayout: BoxLayout:
orientation: 'horizontal' orientation: 'vertical'
BoxLayout: BoxLayout:
ImageCanvas ImageCanvas
id: image_viewer id: image_viewer
ReconstructCanvas
id: reconstruct_viewer
BoxLayout: BoxLayout:
orientation: 'vertical' orientation: 'vertical'
BoxLayout: BoxLayout:
......
{
"directory": "bower_components",
"analytics": false
}
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.js]
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
indent_style = space
indent_size = 2
[*.css]
indent_style = space
indent_size = 2
[*.html]
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
{
/**
Ember CLI sends analytics information by default. The data is completely
anonymous, but there are times when you might want to disable this behavior.
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
}
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
# dependencies
/node_modules
/bower_components
# misc
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
testem.log
{
"predef": [
"document",
"window",
"-Promise"
],
"bass": true,
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true
}
---
language: node_js
node_js:
- "4"
sudo: false
cache:
directories:
- node_modules
before_install:
- npm config set spin false
- npm install -g bower
- npm install phantomjs-prebuilt
install:
- npm install
- bower install
script:
- npm test
{
"ignore_dirs": ["tmp", "dist"]
}
# Viewer
This README outlines the details of collaborating on this Ember application.
A short introduction of this app could easily go here.
## Prerequisites
You will need the following things properly installed on your computer.
* [Git](http://git-scm.com/)
* [Node.js](http://nodejs.org/) (with NPM)
* [Bower](http://bower.io/)
* [Ember CLI](http://ember-cli.com/)
* [PhantomJS](http://phantomjs.org/)
## Installation
* `git clone <repository-url>` this repository
* change into the new directory
* `npm install`
* `bower install`
## Running / Development
* `ember server`
* Visit your app at [http://localhost:4200](http://localhost:4200).
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details
### Running Tests
* `ember test`
* `ember test --server`
### Building
* `ember build` (development)
* `ember build --environment production` (production)
### Deploying
Specify what it takes to deploy your app.
## Further Reading / Useful Links
* [ember.js](http://emberjs.com/)
* [ember-cli](http://ember-cli.com/)
* Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)
import Ember from 'ember';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
let App;
Ember.MODEL_FACTORY_INJECTIONS = true;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver
});
loadInitializers(App, config.modulePrefix);
export default App;
import Ember from 'ember';
export default Ember.Component.extend({
websockets: inject.service(),
socketRef: null,
images: null,
init() {
/*
* 2) The next step you need to do is to create your actual websocket. Calling socketFor
* will retrieve a cached websocket if one exists or in this case it
* will create a new one for us.
*/
const socket = get(this, 'websockets').socketFor('ws://localhost:8888/reconstruction/images');
/*
* 3) The next step is to define your event handlers. All event handlers
* are added via the `on` method and take 3 arguments: event name, callback
* function, and the context in which to invoke the callback. All 3 arguments
* are required.
*/
socket.on('open', this.openHandler, this);
socket.on('message', this.messageHandler, this);
socket.on('close', this.closeHandler, this);
this.set('socketRef', socket);
},
willDestroyElement() {
const socket = this.get('socketRef');
/*
* 4) The final step is to remove all of the listeners you have setup.
*/
socket.off('open', this.openHandler);
socket.off('message', this.messageHandler);
socket.off('close', this.closeHandler);
},
openHandler(event) {
console.log(`On open event has been called: ${event}`);
},
messageHandler(event) {
var message = JSON.parse(event.data);
this.set('image', message.image);
console.log(`Message: ${'received image'}`);
},
closeHandler(event) {
console.log(`On close event has been called: ${event}`);
},
actions: {
get_image() {
const socket = this.get('socketRef');
socket.send(
JSON.stringify({image_index: 1}
));
},
}
});
import Ember from 'ember';
const { get, inject } = Ember;
export default Ember.Controller.extend({
title: 'title',
websockets: inject.service(),
socketRef: null,
image: null,
n_images: 0,
init() {
const socket = get(this, 'websockets').socketFor('ws://localhost:8888/reconstruction');
socket.on('open', this.openHandler, this);
socket.on('message', this.messageHandler, this);
socket.on('close', this.closeHandler, this);
this.set('socketRef', socket);
},
willDestroyElement() {
const socket = this.get('socketRef');
/*
* Remove all of the listeners you have setup.
*/
socket.off('open', this.openHandler);
socket.off('message', this.messageHandler);
socket.off('close', this.closeHandler);
},
openHandler(event) {
//var message = JSON.parse(event.data);
//this.set('n_images', message.n_images);
console.log(event);
console.log(event.data.n_images);
console.log(`On open event has been called: ${event}`);
},
messageHandler(event) {
//var message = JSON.parse(event.data);
//this.set('image', message.image);
//console.log(`Message: ${'received image'}`);
},
closeHandler(event) {
console.log(`On close event has been called: ${event}`);
},
actions: {
get_image() {
const socket = this.get('socketRef');
socket.send(
JSON.stringify({image_index: 1}
));
},
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Viewer</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/viewer.css">
{{content-for "head-footer"}}
</head>
<body>
{{content-for "body"}}
<script src="assets/vendor.js"></script>
<script src="assets/viewer.js"></script>
{{content-for "body-footer"}}
</body>
</html>
import Resolver from 'ember-resolver';
export default Resolver;
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('reconstruction');
});
export default Router;
<h1>Reconstruction</h1>
<h3><button {{action "get_image"}}>{{title}}</button></h3>
<h2>{{n_images}}</h2>
{{#if image }}
<img src='data:image/jpg;base64,{{image}}' alt='missing image'>
{{/if}}
{
"name": "viewer",
"dependencies": {
"ember": "~2.6.0",
"ember-cli-shims": "0.1.1",
"ember-cli-test-loader": "0.2.2",
"ember-qunit-notifications": "0.1.0",
"urijs": "^1.18.1",
"basscss": "~8.0.1"
}
}
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'viewer',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};
/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
});
app.import('bower_components/basscss/modules/align/index.css');
app.import('bower_components/basscss/modules/border/index.css');
app.import('bower_components/basscss/modules/flexbox/index.css');
app.import('bower_components/basscss/modules/grid/index.css');
app.import('bower_components/basscss/modules/hide/index.css');
app.import('bower_components/basscss/modules/layout/index.css');
app.import('bower_components/basscss/modules/margin/index.css');
app.import('bower_components/basscss/modules/padding/index.css');
app.import('bower_components/basscss/modules/position/index.css');
app.import('bower_components/basscss/modules/type-scale/index.css');
app.import('bower_components/basscss/modules/typography/index.css');
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
return app.toTree();
};
{
"name": "viewer",
"version": "0.0.0",
"description": "Small description for viewer goes here",
"private": true,
"directories": {
"doc": "doc",
"test": "tests"
},
"scripts": {
"build": "ember build",
"start": "ember server",
"test": "ember test"
},
"repository": "",
"engines": {
"node": ">= 0.10.0"
},
"author": "",
"license": "MIT",
"devDependencies": {
"broccoli-asset-rev": "^2.4.2",
"ember-ajax": "^2.0.1",
"ember-cli": "2.6.2",
"ember-cli-app-version": "^1.0.0",
"ember-cli-babel": "^5.1.6",
"ember-cli-dependency-checker": "^1.2.0",
"ember-cli-htmlbars": "^1.0.3",
"ember-cli-htmlbars-inline-precompile": "^0.3.1",
"ember-cli-inject-live-reload": "^1.4.0",
"ember-cli-jshint": "^1.0.0",
"ember-cli-qunit": "^1.4.0",
"ember-cli-release": "^0.2.9",
"ember-cli-sri": "^2.1.0",
"ember-cli-uglify": "^1.2.0",
"ember-data": "^2.6.0",
"ember-export-application-global": "^1.0.5",
"ember-load-initializers": "^0.5.1",
"ember-resolver": "^2.0.3",
"ember-websockets": "4.0.1",
"ember-welcome-page": "^1.0.1",
"loader.js": "^4.0.1",
"mock-socket": "3.0.1",
"three": "0.78.0"
}
}
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
<!-- Most restrictive policy: -->
<site-control permitted-cross-domain-policies="none"/>
<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
-->
</cross-domain-policy>
# http://www.robotstxt.org
User-agent: *
Disallow:
/*jshint node:true*/
module.exports = {
"framework": "qunit",
"test_page": "tests/index.html?hidepassed",
"disable_watching": true,
"launch_in_ci": [
"PhantomJS"
],
"launch_in_dev": [
"PhantomJS",
"Chrome"
]
};
{
"predef": [
"document",
"window",
"location",
"setTimeout",
"$",
"-Promise",
"define",
"console",
"visit",
"exists",
"fillIn",
"click",
"keyEvent",
"triggerEvent",
"find",
"findWithAssert",
"wait",
"DS",
"andThen",
"currentURL",
"currentPath",
"currentRouteName"
],
"node": false,
"browser": false,
"boss": true,
"curly": true,
"debug": false,
"devel": false,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true
}
import Ember from 'ember';
export default function destroyApp(application) {
Ember.run(application, 'destroy');
}
import { module } from 'qunit';
import Ember from 'ember';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';
const { RSVP: { Promise } } = Ember;
export default function(name, options = {}) {
module(name, {
beforeEach() {
this.application = startApp();
if (options.beforeEach) {
return options.beforeEach.apply(this, arguments);
}
},
afterEach() {
let afterEach = options.afterEach && options.afterEach.apply(this, arguments);
return Promise.resolve(afterEach).then(() => destroyApp(this.application));
}
});
}
import Resolver from '../../resolver';
import config from '../../config/environment';
const resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
export default function startApp(attrs) {
let application;
let attributes = Ember.merge({}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
Ember.run(() => {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Viewer Tests</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
{{content-for "test-head"}}
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/viewer.css">
<link rel="stylesheet" href="assets/test-support.css">
{{content-for "head-footer"}}
{{content-for "test-head-footer"}}
</head>
<body>
{{content-for "body"}}
{{content-for "test-body"}}
<script src="testem.js" integrity=""></script>
<script src="assets/vendor.js"></script>
<script src="assets/test-support.js"></script>
<script src="assets/viewer.js"></script>
<script src="assets/tests.js"></script>
<script src="assets/test-loader.js"></script>
{{content-for "body-footer"}}
{{content-for "test-body-footer"}}
</body>
</html>
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('images-loader', 'Integration | Component | images loader', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{images-loader}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#images-loader}}
template block text
{{/images-loader}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});
import resolver from './helpers/resolver';
import {
setResolver
} from 'ember-qunit';
setResolver(resolver);
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:reconstruction', 'Unit | Controller | reconstruction', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.subject();
assert.ok(controller);
});
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:test-controller', 'Unit | Controller | test controller', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.subject();
assert.ok(controller);
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment