Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
18
gaia/apps/camera/test/unit/.jshintrc
Executable file
18
gaia/apps/camera/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"requirejs",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
678
gaia/apps/camera/test/unit/app_test.js
Executable file
678
gaia/apps/camera/test/unit/app_test.js
Executable file
|
|
@ -0,0 +1,678 @@
|
|||
/*jshint maxlen:false*/
|
||||
|
||||
suite('app', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'lib/camera/camera',
|
||||
'view',
|
||||
'lib/geo-location',
|
||||
'lib/orientation',
|
||||
'lib/setting',
|
||||
'lib/pinch'
|
||||
], function(App, Camera, View, GeoLocation, orientation, Setting, Pinch) {
|
||||
self.App = App;
|
||||
self.View = View;
|
||||
self.Camera = Camera;
|
||||
self.Geolocation = GeoLocation;
|
||||
self.orientation = orientation;
|
||||
self.Setting = Setting;
|
||||
self.Pinch = Pinch;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var mocks = {
|
||||
doc: function() {
|
||||
return {
|
||||
addEventListener: sinon.spy(),
|
||||
hidden: false
|
||||
};
|
||||
},
|
||||
win: function() {
|
||||
return {
|
||||
addEventListener: sinon.spy(),
|
||||
dispatchEvent: sinon.stub(),
|
||||
location: {
|
||||
hash: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
|
||||
if (!navigator.mozCameras) {
|
||||
navigator.mozCameras = {
|
||||
getListOfCameras: function() { return []; },
|
||||
getCamera: function() {},
|
||||
release: function() {}
|
||||
};
|
||||
}
|
||||
|
||||
navigator.mozSettings = {
|
||||
addObserver: function() {},
|
||||
removeObserver: function() {}
|
||||
};
|
||||
|
||||
var options = this.options = {
|
||||
doc: mocks.doc(),
|
||||
win: mocks.win(),
|
||||
el: document.createElement('div'),
|
||||
geolocation: sinon.createStubInstance(this.Geolocation),
|
||||
orientation: this.orientation,
|
||||
pinch: sinon.createStubInstance(this.Pinch),
|
||||
activity: {},
|
||||
camera: sinon.createStubInstance(this.Camera),
|
||||
require: sinon.stub(),
|
||||
settings: {
|
||||
geolocation: sinon.createStubInstance(this.Setting),
|
||||
spinnerTimeouts: sinon.createStubInstance(this.Setting),
|
||||
keyDownEvents: sinon.createStubInstance(this.Setting),
|
||||
countdown: sinon.createStubInstance(this.Setting)
|
||||
},
|
||||
views: {},
|
||||
controllers: {
|
||||
battery: sinon.spy(),
|
||||
overlay: sinon.spy(),
|
||||
hud: sinon.spy(),
|
||||
countdown: sinon.spy(),
|
||||
controls: sinon.spy(),
|
||||
viewfinder: sinon.spy(),
|
||||
camera: sinon.spy(),
|
||||
settings: sinon.spy(),
|
||||
activity: sinon.spy(),
|
||||
storage: sinon.spy(),
|
||||
|
||||
// Lazy loaded
|
||||
lazy: [
|
||||
'controllers/preview-gallery',
|
||||
'controllers/confirm',
|
||||
'controllers/sounds'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.loadingView = sinon.createStubInstance(this.View);
|
||||
this.loadingView.appendTo.returns(this.loadingView);
|
||||
options.LoadingView = function() { return self.loadingView; };
|
||||
|
||||
// Sandbox to put all our stubs in
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
// Sinon take over setTimeout
|
||||
this.clock = sinon.useFakeTimers();
|
||||
|
||||
// Stub out all methods
|
||||
this.sandbox.stub(options.activity);
|
||||
this.sandbox.stub(options.focusRing);
|
||||
this.sandbox.stub(options.hud);
|
||||
|
||||
// Sometimes we have to spy on the prototype,
|
||||
// this is because methods get bound and passed
|
||||
// directly as callbacks. We set spys on prototype
|
||||
// methods before any of this happens, so that the
|
||||
// spy is always at the root of any call.
|
||||
this.sandbox.spy(this.App.prototype, 'boot');
|
||||
this.sandbox.spy(this.App.prototype, 'showSpinner');
|
||||
this.sandbox.spy(this.App.prototype, 'clearSpinner');
|
||||
|
||||
// Aliases
|
||||
this.settings = options.settings;
|
||||
this.pinch = options.pinch;
|
||||
|
||||
// Create the app
|
||||
this.app = new this.App(options);
|
||||
|
||||
this.sandbox.spy(this.app, 'on');
|
||||
this.sandbox.spy(this.app, 'set');
|
||||
this.sandbox.spy(this.app, 'once');
|
||||
this.sandbox.spy(this.app, 'emit');
|
||||
this.sandbox.spy(this.app, 'firer');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
this.clock.restore();
|
||||
delete navigator.mozCameras;
|
||||
});
|
||||
|
||||
suite('App()', function() {
|
||||
test('Should store the injected dependencies on the instance', function() {
|
||||
var options = this.options;
|
||||
var app = this.app;
|
||||
|
||||
assert.ok(app.el === options.el);
|
||||
assert.ok(app.inSecureMode === false);
|
||||
assert.ok(app.geolocation === options.geolocation);
|
||||
assert.ok(app.camera === options.camera);
|
||||
assert.ok(app.storage === options.storage);
|
||||
assert.ok(app.settings === options.settings);
|
||||
assert.ok(app.sounds === options.sounds);
|
||||
assert.ok(app.controllers === options.controllers);
|
||||
});
|
||||
|
||||
test('Should detect secure mode', function() {
|
||||
var options = this.options;
|
||||
options.win.location.hash = '#secure';
|
||||
|
||||
// need to `new App()` again here (and disregard `this.app`)
|
||||
// because we have changed the option mock.
|
||||
var app = new this.App(options);
|
||||
|
||||
assert.ok(app.inSecureMode === true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#boot()', function() {
|
||||
setup(function() {
|
||||
this.app.boot();
|
||||
});
|
||||
|
||||
test('Should run each of the controllers,' +
|
||||
'passing itself as first argument', function() {
|
||||
var controllers = this.app.controllers;
|
||||
var app = this.app;
|
||||
|
||||
assert.ok(controllers.overlay.calledWith(app));
|
||||
assert.ok(controllers.battery.calledWith(app));
|
||||
assert.ok(controllers.hud.calledWith(app));
|
||||
assert.ok(controllers.controls.calledWith(app));
|
||||
assert.ok(controllers.viewfinder.calledWith(app));
|
||||
assert.ok(controllers.camera.calledWith(app));
|
||||
});
|
||||
|
||||
test('Should bind to `visibilitychange` event', function() {
|
||||
var call = this.app.doc.addEventListener.getCall(0);
|
||||
assert.ok(call.args[0] === 'visibilitychange');
|
||||
assert.ok(typeof call.args[1] === 'function');
|
||||
});
|
||||
|
||||
test('Should bind to `beforeunload` event', function() {
|
||||
var addEventListener = this.app.win.addEventListener;
|
||||
assert.ok(addEventListener.calledWith('beforeunload', this.app.onBeforeUnload));
|
||||
});
|
||||
|
||||
test('Should run the activity controller before controls or camera', function() {
|
||||
var activity = this.app.controllers.activity;
|
||||
var controls = this.app.controllers.controls;
|
||||
var camera = this.app.controllers.camera;
|
||||
|
||||
assert.isTrue(activity.calledBefore(controls));
|
||||
assert.isTrue(activity.calledBefore(camera));
|
||||
});
|
||||
|
||||
test('Should watch location only once storage confirmed healthy', function() {
|
||||
var geolocationWatch = this.app.geolocationWatch;
|
||||
assert.ok(this.app.once.calledWith('storage:checked:healthy', geolocationWatch));
|
||||
});
|
||||
|
||||
test('Should clear loading screen when camera is ready', function() {
|
||||
var on = this.app.on.withArgs('ready');
|
||||
var callback = on.args[0][1];
|
||||
|
||||
// Call the callback and make sure
|
||||
// that `clearSpinner` was called.
|
||||
callback();
|
||||
sinon.assert.calledOnce(this.App.prototype.clearSpinner);
|
||||
});
|
||||
|
||||
test('It calls `showSpinner`', function() {
|
||||
sinon.assert.calledWith(this.App.prototype.showSpinner, 'requestingCamera');
|
||||
});
|
||||
|
||||
suite('pinch', function() {
|
||||
test('It listens to the pinch `changed` event', function() {
|
||||
sinon.assert.called(this.pinch.on, 'changed');
|
||||
});
|
||||
|
||||
test('It disables pinch while the preview-gallery is open', function() {
|
||||
sinon.assert.called(this.app.on, 'previewgallery:opened', this.pinch.disable);
|
||||
sinon.assert.called(this.app.on, 'previewgallery:closed', this.pinch.enable);
|
||||
});
|
||||
|
||||
test('It disables pinch while settings is open', function() {
|
||||
sinon.assert.called(this.app.on, 'settings:opened', this.pinch.disable);
|
||||
sinon.assert.called(this.app.on, 'settings:closed', this.pinch.enable);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#geolocationWatch()', function() {
|
||||
test('Should *not* watch location if in activity', function() {
|
||||
this.app.hidden = false;
|
||||
this.app.activity.pick = true;
|
||||
|
||||
this.app.geolocationWatch();
|
||||
this.clock.tick(2000);
|
||||
|
||||
assert.isFalse(this.app.geolocation.watch.called);
|
||||
});
|
||||
|
||||
test('Should *not* watch location if app hidden', function() {
|
||||
this.app.hidden = true;
|
||||
this.app.activity.pick = false;
|
||||
|
||||
this.app.geolocationWatch();
|
||||
this.clock.tick(2000);
|
||||
|
||||
assert.isFalse(this.app.geolocation.watch.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('once(\'viewfinder:visible\')', function() {
|
||||
setup(function() {
|
||||
|
||||
// Stop annoying logs
|
||||
this.sandbox.stub(console, 'log');
|
||||
|
||||
this.spy = this.app.once.withArgs('viewfinder:visible');
|
||||
this.callback = this.spy.args[0][1];
|
||||
|
||||
sinon.spy(this.app, 'loadLazyControllers');
|
||||
sinon.stub(this.app, 'clearSpinner');
|
||||
|
||||
// Call the callback to test
|
||||
this.callback();
|
||||
});
|
||||
|
||||
test('Should fire a `criticalpathdone` event', function() {
|
||||
assert.isTrue(this.app.emit.calledWith('criticalpathdone'));
|
||||
});
|
||||
|
||||
test('Should flag `this.criticalPathDone`', function() {
|
||||
assert.isTrue(this.app.criticalPathDone);
|
||||
});
|
||||
|
||||
test('Should load non-critical controllers', function() {
|
||||
sinon.assert.called(this.app.loadLazyControllers);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#bindEvents()', function() {
|
||||
setup(function() {
|
||||
this.app.firer.restore();
|
||||
|
||||
sinon.stub(this.app, 'firer');
|
||||
|
||||
this.app.firer
|
||||
.withArgs('busy')
|
||||
.returns('<busy-firer>');
|
||||
|
||||
this.app.firer
|
||||
.withArgs('localized')
|
||||
.returns('<localized-firer>');
|
||||
|
||||
this.sandbox.stub(this.app, 'onCountdown');
|
||||
this.options.settings.countdown.selected.returns('off');
|
||||
});
|
||||
|
||||
suite('default', function() {
|
||||
setup(function() {
|
||||
this.app.bindEvents();
|
||||
});
|
||||
|
||||
test('Should listen for visibilitychange on document', function() {
|
||||
sinon.assert.calledWith(this.app.doc.addEventListener, 'visibilitychange');
|
||||
});
|
||||
|
||||
test('Should relay window \'localized\' event', function() {
|
||||
sinon.assert.calledWith(this.app.doc.addEventListener, 'DOMRetranslated', '<localized-firer>');
|
||||
});
|
||||
|
||||
test('It indicates the app is \'busy\' when the camera \'willchange\'', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'camera:willchange', '<busy-firer>');
|
||||
});
|
||||
|
||||
test('Should listen for \'willchange\' to lazy load once', function() {
|
||||
sinon.assert.calledWith(this.app.once, 'camera:willchange', this.app.onWillChange);
|
||||
});
|
||||
|
||||
test('Should listen for \'activity\' to lazy load once', function() {
|
||||
sinon.assert.calledWith(this.app.once, 'activity', this.app.onActivity);
|
||||
});
|
||||
|
||||
test('Should listen for \'preview\' to lazy load once', function() {
|
||||
sinon.assert.calledWith(this.app.once, 'preview', this.app.onPreview);
|
||||
});
|
||||
|
||||
test('Should listen for \'newthumbnail\' to lazy load once', function() {
|
||||
sinon.assert.calledWith(this.app.once, 'newthumbnail', this.app.onNewThumbnail);
|
||||
});
|
||||
|
||||
test('Should listen for change:selected on countdown if disabled', function() {
|
||||
sinon.assert.calledWith(this.options.settings.countdown.once, 'change:selected', this.app.onCountdown);
|
||||
});
|
||||
});
|
||||
|
||||
test('Should listen for change:selected on countdown if disabled', function() {
|
||||
this.options.settings.countdown.selected.returns('2sec');
|
||||
this.app.bindEvents();
|
||||
sinon.assert.called(this.app.onCountdown);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onVisibilityChange', function() {
|
||||
test('Should update the `app.hidden` property', function() {
|
||||
this.app.doc.hidden = true;
|
||||
this.app.onVisibilityChange();
|
||||
assert.equal(this.app.hidden, true);
|
||||
|
||||
this.app.doc.hidden = false;
|
||||
this.app.onVisibilityChange();
|
||||
assert.equal(this.app.hidden, false);
|
||||
});
|
||||
|
||||
test('Should emit a \'visible\' event when the document is not hidden', function() {
|
||||
this.app.doc.hidden = false;
|
||||
this.app.onVisibilityChange();
|
||||
assert.isTrue(this.app.emit.calledWith('visible'));
|
||||
});
|
||||
|
||||
test('Should emit a \'hidden\' event when the document is hidden', function() {
|
||||
this.app.doc.hidden = true;
|
||||
this.app.onVisibilityChange();
|
||||
assert.isTrue(this.app.emit.calledWith('hidden'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onHidden', function() {
|
||||
setup(function() {
|
||||
this.app.onHidden();
|
||||
});
|
||||
|
||||
test('Should stop watching location', function() {
|
||||
assert.ok(this.app.geolocation.stopWatching.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onVisible()', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.app, 'geolocationWatch');
|
||||
this.sandbox.spy(this.app.orientation, 'lock');
|
||||
this.app.onVisible();
|
||||
});
|
||||
|
||||
test('Should begin watching location again', function() {
|
||||
sinon.assert.called(this.app.geolocationWatch);
|
||||
});
|
||||
|
||||
test('It locks the orientation to portrait', function() {
|
||||
sinon.assert.called(this.app.orientation.lock);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onBusy()', function() {
|
||||
setup(function() {
|
||||
this.app.settings.spinnerTimeouts.get
|
||||
.withArgs('takingPicture')
|
||||
.returns(1500);
|
||||
|
||||
this.app.settings.spinnerTimeouts.get
|
||||
.withArgs('requestingCamera')
|
||||
.returns(600);
|
||||
|
||||
sinon.stub(this.app, 'showSpinner');
|
||||
});
|
||||
|
||||
test('It calls showSpinner with type if busy type recongnised', function() {
|
||||
this.app.onBusy('takingPicture');
|
||||
sinon.assert.calledWith(this.app.showSpinner, 'takingPicture');
|
||||
this.app.showSpinner.reset();
|
||||
|
||||
this.app.onBusy('requestingCamera');
|
||||
sinon.assert.calledWith(this.app.showSpinner, 'requestingCamera');
|
||||
this.app.showSpinner.reset();
|
||||
});
|
||||
|
||||
test('Should not show loading screen if busy type not recongnised', function() {
|
||||
this.app.onBusy('unknownType');
|
||||
sinon.assert.notCalled(this.app.showSpinner);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#showSpinner()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(window, 'clearTimeout');
|
||||
|
||||
this.app.settings.spinnerTimeouts.get
|
||||
.withArgs('takingPicture')
|
||||
.returns(1500);
|
||||
|
||||
this.app.settings.spinnerTimeouts.get
|
||||
.withArgs('requestingCamera')
|
||||
.returns(600);
|
||||
|
||||
});
|
||||
|
||||
test('Should append a loading view to the app element and show', function() {
|
||||
this.app.showSpinner('takingPicture');
|
||||
this.clock.tick(1500);
|
||||
sinon.assert.calledWith(this.app.views.loading.appendTo, this.app.el);
|
||||
sinon.assert.called(this.app.views.loading.show);
|
||||
});
|
||||
|
||||
test('Should clear any existing timeouts', function() {
|
||||
this.sandbox.stub(window, 'setTimeout').returns('<timeout-id>');
|
||||
this.app.showSpinner('requestingCamera');
|
||||
this.app.showSpinner('requestingCamera');
|
||||
sinon.assert.calledWith(window.clearTimeout, '<timeout-id>');
|
||||
});
|
||||
|
||||
test('It should not require a type', function() {
|
||||
this.app.showSpinner();
|
||||
this.clock.tick(1);
|
||||
sinon.assert.calledWith(this.app.views.loading.appendTo, this.app.el);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#clearSpinner()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(window, 'clearTimeout');
|
||||
this.sandbox.stub(window, 'setTimeout').returns('<timeout-id>');
|
||||
this.app.views.loading = sinon.createStubInstance(this.View);
|
||||
this.app.views.loading.hide.callsArg(0);
|
||||
});
|
||||
|
||||
test('Should clear loadingTimeout', function() {
|
||||
this.app.views.loading = null;
|
||||
this.app.showSpinner();
|
||||
this.app.clearSpinner();
|
||||
sinon.assert.calledWith(window.clearTimeout, '<timeout-id>');
|
||||
});
|
||||
|
||||
test('Should hide, then destroy the view', function() {
|
||||
var view = this.app.views.loading;
|
||||
this.app.clearSpinner();
|
||||
|
||||
sinon.assert.called(view.hide);
|
||||
assert.ok(view.destroy.calledAfter(view.hide));
|
||||
});
|
||||
|
||||
test('Should clear reference to `app.views.loading`', function() {
|
||||
this.app.clearSpinner();
|
||||
assert.equal(this.app.views.loading, null);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onActivity', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyController');
|
||||
this.app.onActivity();
|
||||
});
|
||||
|
||||
test('Should lazy load confirm controller', function() {
|
||||
sinon.assert.calledWith(this.app.loadLazyController, 'controllers/confirm');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onCountdown', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyController');
|
||||
this.app.onCountdown();
|
||||
});
|
||||
|
||||
test('Should lazy load countdown controller', function() {
|
||||
sinon.assert.calledWith(this.app.loadLazyController, 'controllers/countdown');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onWillChange', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyController');
|
||||
this.app.onWillChange();
|
||||
});
|
||||
|
||||
test('Should lazy load recording-timer controller', function() {
|
||||
sinon.assert.calledWith(this.app.loadLazyController, 'controllers/recording-timer');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onNewThumbnail', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyController');
|
||||
this.app.onNewThumbnail();
|
||||
});
|
||||
|
||||
test('Should lazy load recording-timer controller', function() {
|
||||
sinon.assert.calledWith(this.app.loadLazyController, 'controllers/preview-gallery');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onPreview', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyController').returns({
|
||||
then: sinon.stub().callsArg(0)
|
||||
});
|
||||
this.app.onPreview();
|
||||
});
|
||||
|
||||
test('Should lazy load preview gallery controller', function() {
|
||||
sinon.assert.calledWith(this.app.loadLazyController, 'controllers/preview-gallery');
|
||||
});
|
||||
|
||||
test('Should re-emit `preview`', function() {
|
||||
sinon.assert.calledWith(this.app.emit, 'preview');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#loadLazyController()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.app, 'loadLazyControllers').returns('<promise>');
|
||||
this.app.controllers.lazy = [];
|
||||
});
|
||||
|
||||
test('Should add to lazy load controllers if critical path not reached', function() {
|
||||
var promise = this.app.loadLazyController('<controller>');
|
||||
assert.isTrue(this.app.controllers.lazy[0] === '<controller>');
|
||||
assert.isTrue(promise === undefined);
|
||||
});
|
||||
|
||||
test('Should lazy load immediately if critical path done', function() {
|
||||
this.app.criticalPathDone = true;
|
||||
var promise = this.app.loadLazyController('<controller>');
|
||||
sinon.assert.called(this.app.loadLazyControllers);
|
||||
assert.isTrue(promise === '<promise>');
|
||||
assert.isTrue(this.app.dynamicLazy['<controller>'] === '<promise>');
|
||||
});
|
||||
|
||||
test('Should return existing lazy load promise', function() {
|
||||
this.app.criticalPathDone = true;
|
||||
this.app.dynamicLazy['<controller>'] = '<existing-promise>';
|
||||
var promise = this.app.loadLazyController('<controller>');
|
||||
sinon.assert.notCalled(this.app.loadLazyControllers);
|
||||
assert.isTrue(promise === '<existing-promise>');
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#loadLazyControllers()', function() {
|
||||
setup(function() {
|
||||
this.fakeControllers = [ sinon.spy(), sinon.spy(), sinon.spy() ];
|
||||
this.lazyPromise = this.app.loadLazyControllers(this.app.controllers.lazy);
|
||||
this.callback = this.app.require.withArgs(this.app.controllers.lazy).args[0][1];
|
||||
this.callback.apply(window, this.fakeControllers);
|
||||
});
|
||||
|
||||
test('It requires the lazy controllers', function() {
|
||||
sinon.assert.calledWith(this.app.require, this.app.controllers.lazy);
|
||||
});
|
||||
|
||||
test('It runs each controller, passing in the App instance', function() {
|
||||
var self = this;
|
||||
this.fakeControllers.forEach(function(fakeController) {
|
||||
sinon.assert.calledWith(fakeController, self.app);
|
||||
});
|
||||
});
|
||||
|
||||
test('It emits `lazyloaded`', function() {
|
||||
sinon.assert.calledWith(this.app.emit, 'lazyloaded');
|
||||
});
|
||||
|
||||
test('It fulfills the promise', function(done) {
|
||||
this.lazyPromise.then(done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('after critical path', function() {
|
||||
setup(function() {
|
||||
|
||||
// Stop annoying logs
|
||||
this.sandbox.stub(console, 'log');
|
||||
this.app.boot();
|
||||
this.app.emit('viewfinder:visible');
|
||||
});
|
||||
|
||||
test('It loads lazy controllers', function() {
|
||||
sinon.assert.calledWith(this.app.require, this.app.controllers.lazy);
|
||||
});
|
||||
|
||||
test('It fires the \'loaded\' event only when:', function(done) {
|
||||
var loadLazyControllersRequireCallback = this.app.require.args[0][1];
|
||||
|
||||
// 1. Lazy controllers have loaded
|
||||
loadLazyControllersRequireCallback();
|
||||
|
||||
// 2. Storage has been checked
|
||||
this.app.emit('storage:checked');
|
||||
|
||||
this.app.once('loaded', done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('App#onKeyDown', function() {
|
||||
setup(function() {
|
||||
this.settings.keyDownEvents.get.withArgs('volumedown')
|
||||
.returns('capture');
|
||||
this.settings.keyDownEvents.get.withArgs('volumeup')
|
||||
.returns('capture');
|
||||
this.settings.keyDownEvents.get.withArgs('camera')
|
||||
.returns('capture');
|
||||
this.settings.keyDownEvents.get.withArgs('mozcamerafocusadjust')
|
||||
.returns('focus');
|
||||
});
|
||||
|
||||
test('`volumedown` key emits a `keydown:capture` event', function() {
|
||||
this.app.onKeyDown({ key: 'volumedown' });
|
||||
sinon.assert.calledWith(this.app.emit, 'keydown:capture');
|
||||
});
|
||||
|
||||
test('`volumeup` key emits a `keydown:capture` event', function() {
|
||||
this.app.onKeyDown({ key: 'volumeup' });
|
||||
sinon.assert.calledWith(this.app.emit, 'keydown:capture');
|
||||
});
|
||||
|
||||
test('`camera` key emits a `keydown:capture` event', function() {
|
||||
this.app.onKeyDown({ key: 'camera' });
|
||||
sinon.assert.calledWith(this.app.emit, 'keydown:capture');
|
||||
});
|
||||
|
||||
test('`focus` key emits a `keydown:focus` event', function() {
|
||||
this.app.onKeyDown({ key: 'mozcamerafocusadjust' });
|
||||
sinon.assert.calledWith(this.app.emit, 'keydown:focus');
|
||||
});
|
||||
});
|
||||
});
|
||||
295
gaia/apps/camera/test/unit/controllers/activity_test.js
Executable file
295
gaia/apps/camera/test/unit/controllers/activity_test.js
Executable file
|
|
@ -0,0 +1,295 @@
|
|||
suite('controllers/activity', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/activity',
|
||||
'lib/setting',
|
||||
], function(App, ActivityController, Setting) {
|
||||
self.ActivityController = ActivityController.ActivityController;
|
||||
self.Setting = Setting;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.win = { location: {} };
|
||||
this.app.activity = {};
|
||||
this.app.settings = {
|
||||
mode: sinon.createStubInstance(this.Setting),
|
||||
pictureSizes: sinon.createStubInstance(this.Setting),
|
||||
recorderProfiles: sinon.createStubInstance(this.Setting),
|
||||
activity: sinon.createStubInstance(this.Setting)
|
||||
};
|
||||
|
||||
// Stub `mozSetMessageHandler`
|
||||
navigator.mozSetMessageHandler = navigator.mozSetMessageHandler || function() {};
|
||||
this.sandbox.stub(navigator, 'mozSetMessageHandler');
|
||||
|
||||
// Aliases
|
||||
this.settings = this.app.settings;
|
||||
this.activity = this.app.activity;
|
||||
|
||||
this.controller = new this.ActivityController(this.app);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('ActivityController#configure()', function() {
|
||||
test('Should should store the activity type on `app.activity`', function() {
|
||||
this.controller.getName = sinon.stub().returns('pick');
|
||||
this.controller.configure();
|
||||
assert.equal(this.app.activity.pick, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ActivityController#bindEvents()', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.controller, 'setupListener');
|
||||
});
|
||||
|
||||
test('It binds a listener straight away if an activity name was found', function() {
|
||||
this.controller.name = 'pick';
|
||||
this.controller.bindEvents();
|
||||
sinon.assert.calledWith(navigator.mozSetMessageHandler, 'activity');
|
||||
});
|
||||
|
||||
test('It binds a listener straight after critical path if no activity name was found', function() {
|
||||
this.controller.name = undefined;
|
||||
this.controller.bindEvents();
|
||||
sinon.assert.calledWith(this.app.once, 'criticalpathdone');
|
||||
|
||||
// Call the callback
|
||||
this.app.once.withArgs('criticalpathdone').args[0][1]();
|
||||
sinon.assert.calledWith(navigator.mozSetMessageHandler, 'activity');
|
||||
});
|
||||
});
|
||||
|
||||
suite('ActivityController#getName()', function() {
|
||||
test('It returns the name after the #', function() {
|
||||
this.app.win.location.hash = '#pick';
|
||||
var name = this.controller.getName();
|
||||
assert.equal(name, 'pick');
|
||||
});
|
||||
|
||||
test('It returns undefined if no hash fragment is present', function() {
|
||||
this.app.win.location.hash = '';
|
||||
var name = this.controller.getName();
|
||||
assert.equal(name, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Activity#confgureMode()', function() {
|
||||
test('Should get \'picture\' and \'video\' modes for SMS \'pick\' activity', function() {
|
||||
var activity = {
|
||||
'source': {
|
||||
'data': {
|
||||
'type': ['image/*', 'audio/*', 'video/*'],
|
||||
'maxFileSizeBytes': 307200
|
||||
},
|
||||
'name': 'pick'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['picture', 'audio', 'video']);
|
||||
});
|
||||
|
||||
test('Should get \'picture\' and \'video\' modes input[type="file"] \'pick\' activity', function() {
|
||||
var activity = {
|
||||
'source': {
|
||||
'data': {
|
||||
'type': [],
|
||||
'nocrop': true
|
||||
},
|
||||
'name': 'pick'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['picture', 'video']);
|
||||
});
|
||||
|
||||
test('Should get \'picture\' mode for input[type="file"] \'pick\' activity', function() {
|
||||
var activity = {
|
||||
'source': {
|
||||
'data': {
|
||||
'type': ['image/gif', 'image/jpeg', 'image/pjpeg',
|
||||
'image/png', 'image/svg+xml', 'image/tiff',
|
||||
'image/vnd.microsoft.icon'],
|
||||
'nocrop': true
|
||||
},
|
||||
'name': 'pick'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['picture']);
|
||||
});
|
||||
|
||||
test('Should be able to cope with String as well as Array', function() {
|
||||
var activity = {
|
||||
source: {
|
||||
data: { type: 'image/jpeg' },
|
||||
name: 'pick'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['picture']);
|
||||
});
|
||||
|
||||
test('Should get [\'picture\', \'video\'] modes for Lockscreen/Gallery \'record\' activity', function() {
|
||||
var activity = {
|
||||
source: {
|
||||
data: { type: 'photos' },
|
||||
name: 'record'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['picture', 'video']);
|
||||
});
|
||||
|
||||
test('Should get [\'video\', \'picture\'] modes for ' +
|
||||
'Video \'record\' activity', function() {
|
||||
var activity = {
|
||||
source: {
|
||||
data: { type: 'videos' },
|
||||
name: 'record'
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.configureMode(activity);
|
||||
var modes = this.settings.mode.filterOptions.args[0][0];
|
||||
assert.deepEqual(modes, ['video', 'picture']);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ActivityController#onMessage()', function() {
|
||||
setup(function() {
|
||||
this.activity = {
|
||||
source: {
|
||||
name: 'pick',
|
||||
data: {
|
||||
type: 'image/jpeg',
|
||||
maxFileSizeBytes: 100
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
sinon.spy(this.controller, 'configureMode');
|
||||
sinon.stub(this.controller, 'getMaxPixelSize').returns('<max-pixel-size>');
|
||||
this.controller.onMessage(this.activity);
|
||||
});
|
||||
|
||||
test('Should emit app events', function() {
|
||||
sinon.assert.calledWith(this.app.emit, 'activity');
|
||||
sinon.assert.calledWith(this.app.emit, 'activity:pick');
|
||||
});
|
||||
|
||||
test('Should configure mode', function() {
|
||||
sinon.assert.called(this.controller.configureMode);
|
||||
});
|
||||
|
||||
test('Should keep a reference to the activity', function() {
|
||||
assert.equal(this.controller.activity, this.activity);
|
||||
});
|
||||
|
||||
test('Should pass `data.maxFileSizeBytes`', function() {
|
||||
var data = this.app.emit.withArgs('activity:pick').args[0][1];
|
||||
assert.equal(data.maxFileSizeBytes, 100);
|
||||
});
|
||||
|
||||
test('Should pass `data.name`', function() {
|
||||
var data = this.app.emit.withArgs('activity:pick').args[0][1];
|
||||
assert.equal(data.name, 'pick');
|
||||
});
|
||||
|
||||
test('Should pass `data.maxPixelSize`', function() {
|
||||
var data = this.app.emit.withArgs('activity:pick').args[0][1];
|
||||
assert.equal(data.maxPixelSize, '<max-pixel-size>');
|
||||
});
|
||||
|
||||
test('Should not do anything if name is unrecognised', function() {
|
||||
this.app.emit.reset();
|
||||
|
||||
this.activity.source.name = 'unknown';
|
||||
this.controller.onMessage(this.activity);
|
||||
|
||||
assert.isFalse(this.app.emit.calledWith('activity'));
|
||||
assert.isFalse(this.app.emit.calledWith('activity:unknown'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ActivityController#getMaxPixelSize()', function() {
|
||||
setup(function() {
|
||||
|
||||
this.activity = {
|
||||
source: {
|
||||
name: 'pick',
|
||||
data: {
|
||||
type: 'image/jpeg',
|
||||
maxFileSizeBytes: 100,
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this.settings.activity.get
|
||||
.withArgs('maxPickPixelSize')
|
||||
.returns(480000);
|
||||
|
||||
this.settings.activity.get
|
||||
.withArgs('maxPixelSizeScaleFactor')
|
||||
.returns(2.5);
|
||||
});
|
||||
|
||||
test('Should return pixel estimate of maxFileSizeBytes if supplied', function() {
|
||||
var result = this.controller.getMaxPixelSize(this.activity);
|
||||
assert.equal(result, 267);
|
||||
});
|
||||
|
||||
test('Should return a pixel estimate based on width height if supplied', function() {
|
||||
delete this.activity.source.data.maxFileSizeBytes;
|
||||
var result = this.controller.getMaxPixelSize(this.activity);
|
||||
assert.equal(result, 25000);
|
||||
});
|
||||
|
||||
test('Should return `maxPickPixelSize` if neither supplied', function() {
|
||||
delete this.activity.source.data.maxFileSizeBytes;
|
||||
delete this.activity.source.data.width;
|
||||
delete this.activity.source.data.height;
|
||||
|
||||
var result = this.controller.getMaxPixelSize(this.activity);
|
||||
assert.equal(result, this.settings.activity.get('maxPickPixelSize'));
|
||||
});
|
||||
|
||||
test('Should be able to cope with only one dimension being specified', function() {
|
||||
delete this.activity.source.data.maxFileSizeBytes;
|
||||
delete this.activity.source.data.height;
|
||||
var result = this.controller.getMaxPixelSize(this.activity);
|
||||
assert.equal(result, (1/3 * 100000));
|
||||
|
||||
delete this.activity.source.data.width;
|
||||
this.activity.source.data.height = 100;
|
||||
result = this.controller.getMaxPixelSize(this.activity);
|
||||
assert.equal(result, (1/3 * 100000));
|
||||
});
|
||||
});
|
||||
});
|
||||
235
gaia/apps/camera/test/unit/controllers/battery_test.js
Executable file
235
gaia/apps/camera/test/unit/controllers/battery_test.js
Executable file
|
|
@ -0,0 +1,235 @@
|
|||
suite('controllers/battery', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/battery',
|
||||
'lib/settings',
|
||||
'lib/setting',
|
||||
'views/notification'
|
||||
], function(
|
||||
App, BatteryController, Settings, Setting, NotificationView) {
|
||||
self.BatteryController = BatteryController.BatteryController;
|
||||
self.NotificationView = NotificationView;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var levels = {
|
||||
low: 15,
|
||||
verylow: 10,
|
||||
critical: 6,
|
||||
shutdown: 5,
|
||||
healthy: 100
|
||||
};
|
||||
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.battery = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.battery.get.withArgs('levels').returns(levels);
|
||||
this.app.l10n = { get: sinon.stub() };
|
||||
this.app.localized = sinon.stub().returns(true);
|
||||
this.app.views = {
|
||||
notification: sinon.createStubInstance(this.NotificationView)
|
||||
};
|
||||
|
||||
// Fake battery
|
||||
this.app.battery = {
|
||||
addEventListener: sinon.stub(),
|
||||
level: 1,
|
||||
charging: false
|
||||
};
|
||||
|
||||
// Shortcuts
|
||||
this.notification = this.app.views.notification;
|
||||
|
||||
// Keep reference of
|
||||
// things we want to restore
|
||||
this.backup = {
|
||||
mozSettings: navigator.mozSettings
|
||||
};
|
||||
|
||||
// Mock object that mimicks
|
||||
// mozSettings get API. Inside
|
||||
// tests set this.mozSettingsGetResult
|
||||
// define the result of the mock call.
|
||||
navigator.mozSettings = {
|
||||
createLock: function() { return this; },
|
||||
get: sinon.stub(),
|
||||
set: sinon.stub(),
|
||||
addObserver: sinon.stub()
|
||||
};
|
||||
|
||||
navigator.mozSettings.get.withArgs('powersave.enabled').returns(
|
||||
Promise.resolve({'powersave.enabled': false})
|
||||
);
|
||||
|
||||
// Our test instance
|
||||
this.controller = new this.BatteryController(this.app);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.mozSettings = this.backup.mozSettings;
|
||||
});
|
||||
|
||||
suite('BatteryController()', function() {
|
||||
test('Should listen to the following events', function() {
|
||||
assert.ok(this.app.on.calledWith('change:batteryStatus'));
|
||||
assert.ok(this.app.on.calledWith('change:recording'));
|
||||
assert.ok(this.app.battery.addEventListener.calledWith('levelchange'));
|
||||
assert.ok(this.app.battery.addEventListener.calledWith('chargingchange'));
|
||||
assert.ok(navigator.mozSettings.addObserver.calledWith('powersave.enabled'));
|
||||
});
|
||||
|
||||
test('Should update the status initially', function() {
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'healthy'));
|
||||
});
|
||||
|
||||
test('Should query power save state', function() {
|
||||
assert.ok(navigator.mozSettings.get.calledWith('powersave.enabled'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('BatteryController#updateStatus()', function() {
|
||||
test('should call app.set with batteryStatus and battery charging' +
|
||||
' on updateStatus', function() {
|
||||
this.app.battery.charging = true;
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'charging'));
|
||||
});
|
||||
|
||||
test('should call app.set with batteryStatus and low battery level' +
|
||||
' on updateStatus', function() {
|
||||
this.app.battery.level = 0.11;
|
||||
this.controller.app.get
|
||||
.withArgs('batteryStatus')
|
||||
.returns('charging');
|
||||
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'low'));
|
||||
});
|
||||
|
||||
test('should call app.set with batteryStatus and verylow battery' +
|
||||
' level on updateStatus', function() {
|
||||
this.app.battery.level = 0.10;
|
||||
this.controller.app.get
|
||||
.withArgs('batteryStatus')
|
||||
.returns('low');
|
||||
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'verylow'));
|
||||
});
|
||||
|
||||
test('should call app.set with batteryStatus and critical battery' +
|
||||
' level on updateStatus', function() {
|
||||
this.app.battery.level = 0.06;
|
||||
this.controller.app.get
|
||||
.withArgs('batteryStatus')
|
||||
.returns('verylow');
|
||||
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'critical'));
|
||||
});
|
||||
|
||||
test('should call app.set with batteryStatus and battery shutdown' +
|
||||
' level on updateStatus', function() {
|
||||
this.app.battery.level = 0.05;
|
||||
this.controller.app.get
|
||||
.withArgs('batteryStatus')
|
||||
.returns('critical');
|
||||
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'shutdown'));
|
||||
});
|
||||
|
||||
test('should call app.set with batteryStatus and battery healthy' +
|
||||
' level on updateStatus', function() {
|
||||
this.app.battery.level = 1;
|
||||
this.controller.app.get
|
||||
.withArgs('batteryStatus')
|
||||
.returns('charging');
|
||||
|
||||
this.controller.updateStatus();
|
||||
assert.ok(this.app.set.calledWith('batteryStatus', 'healthy'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('BatteryController#onStatusChange()', function() {
|
||||
test('Should display notification if required', function() {
|
||||
this.controller.onStatusChange('low');
|
||||
assert.isTrue(this.notification.display.called);
|
||||
this.notification.display.reset();
|
||||
|
||||
this.controller.onStatusChange('verylow');
|
||||
assert.isTrue(this.notification.display.called);
|
||||
this.notification.display.reset();
|
||||
|
||||
this.controller.onStatusChange('critical');
|
||||
assert.isTrue(this.notification.display.called);
|
||||
this.notification.display.reset();
|
||||
|
||||
this.controller.onStatusChange('healthy');
|
||||
assert.isFalse(this.notification.display.called);
|
||||
this.notification.display.reset();
|
||||
|
||||
this.controller.onStatusChange('shutdown');
|
||||
assert.isFalse(this.notification.display.called);
|
||||
this.notification.display.reset();
|
||||
});
|
||||
|
||||
test('Should always clear the last notification', function() {
|
||||
this.controller.onStatusChange('low');
|
||||
assert.isTrue(this.notification.clear.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('BatteryController#updatePowerSave()', function() {
|
||||
setup(function() {
|
||||
this.controller.powerSave = true;
|
||||
this.controller.powerSaveEnabled = true;
|
||||
this.app.get.withArgs('recording').returns(false);
|
||||
});
|
||||
|
||||
test('Should emit `battery:powersave` if changed', function() {
|
||||
this.controller.powerSave = false;
|
||||
this.controller.updatePowerSave();
|
||||
assert.isTrue(this.controller.powerSave);
|
||||
assert.ok(this.app.emit.calledWith('battery:powersave', true));
|
||||
});
|
||||
|
||||
test('Should disable if recording', function() {
|
||||
this.app.get.withArgs('recording').returns(true);
|
||||
this.controller.updatePowerSave();
|
||||
assert.isFalse(this.controller.powerSave);
|
||||
assert.ok(this.app.emit.calledWith('battery:powersave', false));
|
||||
});
|
||||
|
||||
test('Should disable if power save disabled', function() {
|
||||
this.controller.powerSaveEnabled = false;
|
||||
this.controller.updatePowerSave();
|
||||
assert.isFalse(this.controller.powerSave);
|
||||
assert.ok(this.app.emit.calledWith('battery:powersave', false));
|
||||
});
|
||||
});
|
||||
|
||||
suite('BatteryController#onPowerSaveChange()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'updatePowerSave');
|
||||
this.controller.onPowerSaveChange({settingValue: 'result'});
|
||||
});
|
||||
|
||||
test('Should cache power save enabled state', function() {
|
||||
assert.isTrue(this.controller.powerSaveEnabled === 'result');
|
||||
});
|
||||
|
||||
test('Should call `updatePowerSave`', function() {
|
||||
assert.ok(this.controller.updatePowerSave.called);
|
||||
});
|
||||
});
|
||||
});
|
||||
762
gaia/apps/camera/test/unit/controllers/camera_test.js
Executable file
762
gaia/apps/camera/test/unit/controllers/camera_test.js
Executable file
|
|
@ -0,0 +1,762 @@
|
|||
/*jshint maxlen:false*/
|
||||
|
||||
suite('controllers/camera', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/camera',
|
||||
'lib/camera/camera',
|
||||
'lib/settings',
|
||||
'lib/setting',
|
||||
'lib/geo-location'
|
||||
], function(
|
||||
App, CameraController, Camera, Settings, Setting, GeoLocation) {
|
||||
self.CameraController = CameraController.CameraController;
|
||||
self.GeoLocation = GeoLocation;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
self.Camera = Camera;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.camera = sinon.createStubInstance(this.Camera);
|
||||
this.app.geolocation = sinon.createStubInstance(this.GeoLocation);
|
||||
this.app.views = {
|
||||
notification: {
|
||||
display: sinon.spy()
|
||||
}
|
||||
};
|
||||
|
||||
// Activity
|
||||
this.app.activity = {};
|
||||
|
||||
this.app.storage = {
|
||||
getItem: sinon.stub(),
|
||||
setItem: sinon.stub()
|
||||
};
|
||||
|
||||
this.app.get.withArgs('batteryStatus').returns('healthy');
|
||||
|
||||
// Settings
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.cameras = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.mode = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.pictureSizes = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.isoModes = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.recorderProfiles = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.flashModes = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.whiteBalance = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.hdr = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.flashModesPicture = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.countdown = sinon.createStubInstance(this.Setting);
|
||||
|
||||
// Aliases
|
||||
this.settings = this.app.settings;
|
||||
this.notification = this.app.views.notification;
|
||||
this.storage = this.app.storage;
|
||||
this.camera = this.app.camera;
|
||||
|
||||
this.camera.cameraList = ['back', 'front'];
|
||||
|
||||
// Test instance (some tests create there own)
|
||||
this.controller = new this.CameraController(this.app);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('CameraController()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.CameraController.prototype, 'shutdownCamera');
|
||||
});
|
||||
|
||||
test('Should filter the `cameras` setting based on the camera list', function() {
|
||||
sinon.assert.calledWith(this.settings.cameras.filterOptions, this.camera.cameraList);
|
||||
});
|
||||
|
||||
test('Should load camera on app `visible`', function() {
|
||||
assert.isTrue(this.app.on.calledWith('visible', this.controller.loadCamera));
|
||||
});
|
||||
|
||||
test('Should teardown camera on app `hidden`', function() {
|
||||
assert.isTrue(this.app.on.calledWith('hidden', this.controller.shutdownCamera));
|
||||
});
|
||||
|
||||
test('Should relay focus change events', function() {
|
||||
assert.isTrue(this.camera.on.calledWith('change:focus'));
|
||||
assert.isTrue(this.app.firer.calledWith('camera:focusstatechanged'));
|
||||
});
|
||||
|
||||
test('Should listen to storage:changed', function() {
|
||||
assert.isTrue(this.app.on.calledWith('storage:changed'));
|
||||
});
|
||||
|
||||
test('Should listen to \'configured\' event', function() {
|
||||
assert.isTrue(this.camera.on.calledWith('configured'));
|
||||
});
|
||||
|
||||
test('Should listen to the \'activity:pick\' event', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'activity:pick');
|
||||
});
|
||||
|
||||
test('Should query battery status', function() {
|
||||
sinon.assert.calledWith(this.app.get, 'batteryStatus');
|
||||
assert.isFalse(this.controller.lowBattery);
|
||||
});
|
||||
});
|
||||
|
||||
suite('camera.on(\'configured\')', function() {
|
||||
test('Should relay via app event', function() {
|
||||
sinon.assert.calledWith(this.camera.on, 'configured');
|
||||
sinon.assert.calledWith(this.app.firer, 'camera:configured');
|
||||
});
|
||||
});
|
||||
|
||||
suite('on(\'activity:pick\')', function() {
|
||||
setup(function() {
|
||||
var spy = this.app.on.withArgs('activity:pick');
|
||||
this.callback = spy.args[0][1];
|
||||
|
||||
this.data = { maxFileSizeBytes: 100 };
|
||||
this.callback(this.data);
|
||||
});
|
||||
|
||||
test('Should set `maxFileSizeBytes` on camera', function() {
|
||||
sinon.assert.calledWith(this.camera.set, 'maxFileSizeBytes', 100);
|
||||
});
|
||||
|
||||
test('Should disable the camera boot config caching', function() {
|
||||
this.camera.cacheConfig = true;
|
||||
this.callback({});
|
||||
assert.isFalse(this.camera.cacheConfig);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onSettingsConfigured()', function() {
|
||||
setup(function() {
|
||||
|
||||
// Mock object that mimicks
|
||||
// mozSettings get API. Inside
|
||||
// tests set this.mozSettingsGetResult
|
||||
// define the result of the mock call.
|
||||
navigator.mozSettings = {
|
||||
createLock: function() { return this; },
|
||||
get: function(key) {
|
||||
var mozSettings = this;
|
||||
setTimeout(function() {
|
||||
var result = {};
|
||||
result[key] = 'the-result';
|
||||
mozSettings.onsuccess({
|
||||
target: {
|
||||
result: result
|
||||
}
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
this.app.settings.flashModes.selected.returns('on');
|
||||
this.app.settings.isoModes.get = sinon.spy();
|
||||
this.app.settings.isoModes.selected.returns({key: 'auto'});
|
||||
this.app.settings.pictureSizes.selected.returns({ width: 480, height: 640 });
|
||||
this.app.settings.recorderProfiles.selected.returns('cif');
|
||||
this.app.settings.hdr.selected.returns('on');
|
||||
|
||||
// Returns `this` to allow chaining
|
||||
this.app.camera.setRecorderProfile.returns(this.app.camera);
|
||||
this.app.camera.setPictureSize.returns(this.app.camera);
|
||||
this.app.camera.configureZoom.returns(this.app.camera);
|
||||
|
||||
this.controller = new this.CameraController(this.app);
|
||||
this.controller.onSettingsConfigured();
|
||||
});
|
||||
|
||||
test('Should set flashMode', function() {
|
||||
assert.ok(this.camera.setFlashMode.called);
|
||||
});
|
||||
|
||||
test('Should set hdr', function() {
|
||||
assert.ok(this.camera.setHDR.calledWith('on'));
|
||||
});
|
||||
|
||||
test('Should set recorderProfile', function() {
|
||||
assert.ok(this.camera.setRecorderProfile.calledWith('cif'));
|
||||
});
|
||||
|
||||
test('Should set pictureSize', function() {
|
||||
var arg = this.app.camera.setPictureSize.args[0][0];
|
||||
|
||||
assert.ok(arg.width === 480);
|
||||
assert.ok(arg.height === 640);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onRecordingChange', function() {
|
||||
test('Should set `recording` to true if started', function() {
|
||||
this.controller.onRecordingChange('started');
|
||||
assert.ok(this.app.set.calledWith('recording', true));
|
||||
});
|
||||
|
||||
test('Should set `recording` to true if stopped', function() {
|
||||
this.controller.onRecordingChange('stopped');
|
||||
assert.ok(this.app.set.calledWith('recording', false));
|
||||
});
|
||||
|
||||
test('Should not set `recording` if starting or stopping', function() {
|
||||
var self = this;
|
||||
['error', 'starting', 'stopping'].forEach(function(recording) {
|
||||
self.controller.onRecordingChange(recording);
|
||||
sinon.assert.notCalled(self.app.set);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onCaptureKey', function() {
|
||||
setup(function() {
|
||||
this.app.busy = false;
|
||||
this.controller.lowBattery = false;
|
||||
});
|
||||
|
||||
test('`keydown:capture` triggers capture', function() {
|
||||
var callback = this.app.on.withArgs('keydown:capture').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.called(this.camera.capture);
|
||||
});
|
||||
|
||||
test('It calls preventDefault if the capture call doesn\'t return false', function() {
|
||||
var callback = this.app.on.withArgs('keydown:capture').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
this.camera.capture.returns(false);
|
||||
callback(event);
|
||||
sinon.assert.notCalled(event.preventDefault);
|
||||
|
||||
this.camera.capture.returns(undefined);
|
||||
callback(event);
|
||||
sinon.assert.called(event.preventDefault);
|
||||
});
|
||||
|
||||
test('It doesnt capture if countdown is active', function() {
|
||||
this.controller.countdown = 999;
|
||||
var callback = this.app.on.withArgs('keydown:capture').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.notCalled(this.camera.capture);
|
||||
});
|
||||
|
||||
test('It doesnt capture if confirm overlay is shown', function() {
|
||||
this.app.get.withArgs('confirmViewVisible').returns(true);
|
||||
var callback = this.app.on.withArgs('keydown:capture').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.notCalled(this.camera.capture);
|
||||
});
|
||||
|
||||
test('It doesnt capture if busy', function() {
|
||||
this.app.busy = true;
|
||||
var callback = this.app.on.withArgs('keydown:capture').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.notCalled(this.camera.capture);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onFocusKey', function() {
|
||||
setup(function() {
|
||||
this.app.busy = false;
|
||||
this.camera.focus = {
|
||||
focus: this.sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('`keydown:focus` triggers focus', function() {
|
||||
var callback = this.app.on.withArgs('keydown:focus').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.called(this.camera.focus.focus);
|
||||
});
|
||||
|
||||
test('It doesnt focus if busy', function() {
|
||||
this.app.busy = true;
|
||||
var callback = this.app.on.withArgs('keydown:focus').args[0][1];
|
||||
var event = { preventDefault: sinon.spy() };
|
||||
|
||||
callback(event);
|
||||
sinon.assert.notCalled(this.camera.focus.focus);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#setMode()', function() {
|
||||
test('It sets the flash mode', function() {
|
||||
this.controller.setMode();
|
||||
sinon.assert.called(this.notification.display);
|
||||
sinon.assert.called(this.camera.setFlashMode);
|
||||
});
|
||||
|
||||
test('It emits `camera:willchange` event', function() {
|
||||
this.controller.setMode();
|
||||
sinon.assert.calledWith(this.app.emit, 'camera:willchange');
|
||||
});
|
||||
|
||||
test('It sets the mode once the viewfinder is hidden', function() {
|
||||
this.controller.setMode('my-mode');
|
||||
sinon.assert.calledWith(this.app.once, 'viewfinder:hidden');
|
||||
|
||||
// call the callback
|
||||
this.app.once.withArgs('viewfinder:hidden').args[0][1]();
|
||||
sinon.assert.calledWith(this.camera.setMode, 'my-mode');
|
||||
});
|
||||
|
||||
test('It doesn\'t set the do anything if the mode didn\'t change', function() {
|
||||
this.camera.isMode.returns(true);
|
||||
this.controller.setMode('my-mode');
|
||||
assert.isFalse(this.app.emit.calledWith('camera:willchange'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#setCamera()', function() {
|
||||
test('It emits `camera:willchange` event', function() {
|
||||
this.controller.setMode();
|
||||
sinon.assert.calledWith(this.app.emit, 'camera:willchange');
|
||||
});
|
||||
|
||||
test('It sets the camera once the viewfinder is hidden', function() {
|
||||
this.controller.setCamera('back');
|
||||
sinon.assert.calledWith(this.app.once, 'viewfinder:hidden');
|
||||
|
||||
// call the callback
|
||||
this.app.once.withArgs('viewfinder:hidden').args[0][1]();
|
||||
sinon.assert.calledWith(this.camera.setCamera, 'back');
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#updatePictureSize()', function() {
|
||||
setup(function() {
|
||||
this.settings.mode.selected
|
||||
.withArgs('key')
|
||||
.returns('picture');
|
||||
|
||||
this.settings.pictureSizes.selected
|
||||
.withArgs('data')
|
||||
.returns({ width: 400, height: 300 });
|
||||
});
|
||||
|
||||
test('It emits `camera:willchange` event', function() {
|
||||
this.controller.updatePictureSize();
|
||||
sinon.assert.calledWith(this.app.emit, 'camera:willchange');
|
||||
});
|
||||
|
||||
test('It sets the camera once the viewfinder is hidden', function() {
|
||||
this.controller.updatePictureSize();
|
||||
sinon.assert.calledWith(this.app.once, 'viewfinder:hidden');
|
||||
|
||||
// call the callback
|
||||
this.app.once.withArgs('viewfinder:hidden').args[0][1]();
|
||||
sinon.assert.calledWith(this.camera.setPictureSize, { width: 400, height: 300 });
|
||||
});
|
||||
|
||||
test('It doesn\'t proceed if `pictureSize` didn\'t change', function() {
|
||||
this.camera.isPictureSize.returns(true);
|
||||
this.controller.updatePictureSize();
|
||||
assert.isFalse(this.app.emit.calledWith('camera:willchange'));
|
||||
});
|
||||
|
||||
suite('`video` mode', function() {
|
||||
setup(function() {
|
||||
this.settings.mode.selected
|
||||
.withArgs('key')
|
||||
.returns('video');
|
||||
});
|
||||
|
||||
test('It doesn\'t emit `camera:willchange` if in `video` mode', function() {
|
||||
this.controller.updatePictureSize();
|
||||
assert.isFalse(this.app.emit.calledWith('camera:willchange'));
|
||||
});
|
||||
|
||||
test('It calls `camera.setPictureSize`, but doesn\'t reconfigure camera', function() {
|
||||
this.controller.updatePictureSize();
|
||||
sinon.assert.calledWith(
|
||||
this.camera.setPictureSize,
|
||||
{ width: 400, height: 300 },
|
||||
{ configure: false }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#updateRecorderProfile()', function() {
|
||||
setup(function() {
|
||||
this.settings.mode.selected
|
||||
.withArgs('key')
|
||||
.returns('video');
|
||||
|
||||
this.settings.recorderProfiles.selected
|
||||
.withArgs('key')
|
||||
.returns('720p');
|
||||
});
|
||||
|
||||
test('It emits `camera:willchange` event', function() {
|
||||
this.controller.updateRecorderProfile();
|
||||
sinon.assert.calledWith(this.app.emit, 'camera:willchange');
|
||||
});
|
||||
|
||||
test('It sets the camera once the viewfinder is hidden', function() {
|
||||
this.controller.updateRecorderProfile();
|
||||
sinon.assert.calledWith(this.app.once, 'viewfinder:hidden');
|
||||
|
||||
// call the callback
|
||||
this.app.once.withArgs('viewfinder:hidden').args[0][1]();
|
||||
sinon.assert.calledWith(this.camera.setRecorderProfile, '720p');
|
||||
});
|
||||
|
||||
test('It doesn\'t proceed if `recorderProfile` didn\'t change', function() {
|
||||
this.camera.isRecorderProfile.returns(true);
|
||||
this.controller.updateRecorderProfile();
|
||||
assert.isFalse(this.app.emit.calledWith('camera:willchange'));
|
||||
});
|
||||
|
||||
suite('`picture` mode', function() {
|
||||
setup(function() {
|
||||
this.settings.mode.selected
|
||||
.withArgs('key')
|
||||
.returns('picture');
|
||||
});
|
||||
|
||||
test('It doesn\'t emit `camera:willchange` if in `video` mode', function() {
|
||||
this.controller.updateRecorderProfile();
|
||||
assert.isFalse(this.app.emit.calledWith('camera:willchange'));
|
||||
});
|
||||
|
||||
test('It calls `camera.setRecorderProfile`, but doesn\'t reconfigure camera', function() {
|
||||
this.controller.updateRecorderProfile();
|
||||
sinon.assert.calledWith(
|
||||
this.camera.setRecorderProfile,
|
||||
'720p',
|
||||
{ configure: false }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onHDRChange()', function() {
|
||||
test('Should set flashModesPicture \'off\' when hdr is set to \'on\'',
|
||||
function() {
|
||||
var flashModesPicture = this.app.settings.flashModesPicture;
|
||||
|
||||
flashModesPicture.selected.withArgs('key').returns('on');
|
||||
this.controller.onHDRChange('on');
|
||||
|
||||
assert.ok(flashModesPicture.select.calledWith('off'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#setHDR()', function() {
|
||||
setup(function() {
|
||||
this.settings.hdr.selected
|
||||
.withArgs('key')
|
||||
.returns('on');
|
||||
});
|
||||
|
||||
test('Should call camera.setHDR with given value', function() {
|
||||
this.controller.setHDR();
|
||||
sinon.assert.calledWith(this.camera.setHDR, 'on');
|
||||
});
|
||||
|
||||
test('Should not call camera.setHDR if `hdrDisabled`', function() {
|
||||
this.controller.hdrDisabled = true;
|
||||
this.controller.setHDR();
|
||||
sinon.assert.notCalled(this.camera.setHDR);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onFlashModeChange()', function() {
|
||||
setup(function() {
|
||||
this.settings.hdr.selected
|
||||
.withArgs('key')
|
||||
.returns('on');
|
||||
});
|
||||
|
||||
test('Should change hdr to off if flash is on', function() {
|
||||
this.controller.hdrDisabled = false;
|
||||
this.controller.onFlashModeChange('on');
|
||||
assert.ok(this.settings.hdr.select.calledWith('off'));
|
||||
});
|
||||
|
||||
test('Should not change HDR if flash is off', function() {
|
||||
this.controller.hdrDisabled = false;
|
||||
this.controller.onFlashModeChange('off');
|
||||
assert.ok(!this.settings.hdr.select.called);
|
||||
});
|
||||
|
||||
test('Should not change HDR if HDR is off', function() {
|
||||
this.settings.hdr.selected.withArgs('key').returns('off');
|
||||
this.controller.hdrDisabled = true;
|
||||
this.controller.onFlashModeChange('auto');
|
||||
assert.ok(!this.settings.hdr.select.called);
|
||||
});
|
||||
|
||||
test('Should not change HDR if HDR is disabled', function() {
|
||||
this.controller.hdrDisabled = true;
|
||||
this.controller.onFlashModeChange('auto');
|
||||
assert.ok(!this.settings.hdr.select.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#capture()', function() {
|
||||
setup(function() {
|
||||
this.controller.galleryOpen = false;
|
||||
this.controller.lowBattery = false;
|
||||
sinon.spy(this.controller, 'startCountdown');
|
||||
});
|
||||
|
||||
test('Should not start countdown if no countdown setting is set', function() {
|
||||
this.app.settings.countdown.selected.returns(0);
|
||||
this.app.get.withArgs('timerActive').returns(false);
|
||||
this.app.get.withArgs('recording').returns(false);
|
||||
this.controller.capture();
|
||||
sinon.assert.notCalled(this.controller.startCountdown);
|
||||
});
|
||||
|
||||
test('Should not start countdown if countdown is already active', function() {
|
||||
this.app.settings.countdown.selected.returns(5);
|
||||
this.controller.countdown = 999;
|
||||
this.app.get.withArgs('recording').returns(false);
|
||||
this.controller.capture();
|
||||
sinon.assert.notCalled(this.controller.startCountdown);
|
||||
});
|
||||
|
||||
test('Should not start countdown if recording', function() {
|
||||
this.app.settings.countdown.selected.returns(5);
|
||||
this.app.get.withArgs('timerActive').returns(false);
|
||||
this.app.get.withArgs('recording').returns(true);
|
||||
this.controller.capture();
|
||||
sinon.assert.notCalled(this.controller.startCountdown);
|
||||
});
|
||||
|
||||
test('Should otherwise start countdown', function() {
|
||||
this.app.settings.countdown.selected.returns(5);
|
||||
this.app.get.withArgs('timerActive').returns(false);
|
||||
this.app.get.withArgs('recording').returns(false);
|
||||
this.controller.capture();
|
||||
sinon.assert.calledOnce(this.controller.startCountdown);
|
||||
});
|
||||
|
||||
test('Should pass the current geolocation position', function() {
|
||||
this.app.geolocation.position = 123;
|
||||
this.controller.capture();
|
||||
assert.ok(this.app.camera.capture.args[0][0].position === 123);
|
||||
});
|
||||
|
||||
test('Should return false if low battery', function() {
|
||||
this.controller.lowBattery = true;
|
||||
assert.ok(this.controller.capture() === false);
|
||||
});
|
||||
|
||||
test('Should return false if gallery open', function() {
|
||||
this.controller.galleryOpen = true;
|
||||
assert.ok(this.controller.capture() === false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onBatteryStatusChange()', function() {
|
||||
setup(function() {
|
||||
this.controller.loadCamera = sinon.stub();
|
||||
sinon.spy(this.controller, 'shutdownCamera');
|
||||
this.controller.clearCountdown = sinon.stub();
|
||||
});
|
||||
|
||||
test('Should call onBatteryStatuchange on \'change:batteryStatus\'',
|
||||
function() {
|
||||
this.app.on.calledWith('change:batteryStatus', this.onBatteryStatusChange);
|
||||
});
|
||||
|
||||
test('Should load camera on `healthy` status', function() {
|
||||
this.controller.onBatteryStatusChange('healthy');
|
||||
assert.isTrue(this.controller.loadCamera.called);
|
||||
});
|
||||
|
||||
test('Should shutdown camera on `shutdown` status', function() {
|
||||
this.controller.onBatteryStatusChange('shutdown');
|
||||
assert.isTrue(this.controller.shutdownCamera.called);
|
||||
});
|
||||
|
||||
test('Should clear countdown on `shutdown` status', function() {
|
||||
this.controller.onBatteryStatusChange('shutdown');
|
||||
sinon.assert.calledOnce(this.controller.clearCountdown);
|
||||
});
|
||||
|
||||
test('Should prevent capture on low battery', function() {
|
||||
this.controller.onBatteryStatusChange('shutdown');
|
||||
this.controller.capture();
|
||||
assert.isFalse(this.camera.capture.called);
|
||||
|
||||
this.controller.onBatteryStatusChange('healthy');
|
||||
this.controller.capture();
|
||||
assert.isTrue(this.camera.capture.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#startCountdown()', function() {
|
||||
setup(function() {
|
||||
this.app.settings.countdown.selected.withArgs('value').returns(5);
|
||||
this.clock = this.sinon.useFakeTimers();
|
||||
this.controller.lowBattery = false;
|
||||
});
|
||||
|
||||
test('Emits a `countdown:started` event', function() {
|
||||
this.controller.startCountdown();
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:started', 5);
|
||||
});
|
||||
|
||||
test('Emits a `countdown:tick` event each second', function() {
|
||||
this.controller.startCountdown();
|
||||
this.clock.tick(1000);
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:tick', 4);
|
||||
this.clock.tick(1000);
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:tick', 3);
|
||||
this.clock.tick(1000);
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:tick', 2);
|
||||
this.clock.tick(1000);
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:tick', 1);
|
||||
});
|
||||
|
||||
test('Emits a `countdown:ended` event once it reaches 0', function() {
|
||||
this.controller.startCountdown();
|
||||
this.clock.tick(5000);
|
||||
sinon.assert.calledWith(this.app.emit, 'countdown:ended');
|
||||
});
|
||||
|
||||
test('It calls camera.capture once ended', function() {
|
||||
this.controller.startCountdown();
|
||||
this.clock.tick(5000);
|
||||
sinon.assert.calledOnce(this.camera.capture);
|
||||
});
|
||||
|
||||
test('It clears the coundown when the app base el is clicked', function() {
|
||||
var proto = this.controller.constructor.prototype;
|
||||
var clearCountdown = this.sinon.spy(proto, 'clearCountdown');
|
||||
|
||||
this.app.on.reset();
|
||||
|
||||
var controller = new this.CameraController(this.app);
|
||||
var callback = this.app.on.withArgs('click').args[0][1];
|
||||
|
||||
controller.startCountdown();
|
||||
this.clock.tick(2000);
|
||||
callback();
|
||||
|
||||
sinon.assert.calledOnce(clearCountdown);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#loadCamera()', function() {
|
||||
setup(function() {
|
||||
this.app.hidden = false;
|
||||
this.app.activity.pick = false;
|
||||
this.app.isSharingActivity = sinon.stub();
|
||||
this.app.isSharingActivity.returns(false);
|
||||
this.app.showSpinner = sinon.stub();
|
||||
this.controller.galleryOpen = false;
|
||||
this.controller.lowBattery = false;
|
||||
});
|
||||
|
||||
test('It doesn\'t load the camera if the battery is low', function() {
|
||||
this.controller.lowBattery = true;
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.notCalled(this.camera.load);
|
||||
});
|
||||
|
||||
test('It does load the camera if the battery is not low', function() {
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.called(this.camera.load);
|
||||
});
|
||||
|
||||
test('It doesn\'t load the camera if gallery is open', function() {
|
||||
this.controller.galleryOpen = true;
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.notCalled(this.camera.load);
|
||||
});
|
||||
|
||||
test('It does load the camera if gallery is closed', function() {
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.called(this.camera.load);
|
||||
});
|
||||
|
||||
test('It doesn\'t load the camera if app is hidden', function() {
|
||||
this.app.hidden = true;
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.notCalled(this.camera.load);
|
||||
sinon.assert.notCalled(this.app.showSpinner);
|
||||
});
|
||||
|
||||
test('It does load the camera if app is visible', function() {
|
||||
this.controller.loadCamera();
|
||||
sinon.assert.called(this.camera.load);
|
||||
sinon.assert.notCalled(this.app.showSpinner);
|
||||
});
|
||||
|
||||
test('It shows the spinner if loading and requested', function() {
|
||||
this.controller.loadCamera(true);
|
||||
sinon.assert.called(this.app.showSpinner);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#shutdownCamera()', function() {
|
||||
setup(function() {
|
||||
this.controller.shutdownCamera();
|
||||
});
|
||||
|
||||
test('Should stop shutdown the camera', function() {
|
||||
assert.isTrue(this.camera.shutdown.called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('CameraController#onStorageChanged()', function() {
|
||||
test('Should stop recording if not available', function() {
|
||||
this.controller.onStorageChanged('unavailable');
|
||||
assert.isTrue(this.camera.stopRecording.called);
|
||||
});
|
||||
|
||||
test('Should not stop recording if available', function() {
|
||||
this.controller.onStorageChanged('available');
|
||||
assert.isTrue(this.camera.stopRecording.notCalled);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onGalleryOpened()', function() {
|
||||
test('Should store gallery open state', function() {
|
||||
this.controller.shutdownCamera = sinon.stub();
|
||||
this.controller.galleryOpen = false;
|
||||
this.controller.onGalleryOpened();
|
||||
assert.isTrue(this.controller.galleryOpen);
|
||||
sinon.assert.called(this.controller.shutdownCamera);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CameraController#onGalleryClosed()', function() {
|
||||
test('It loads the camera', function() {
|
||||
this.controller.loadCamera = sinon.stub();
|
||||
this.controller.galleryOpen = true;
|
||||
this.controller.onGalleryClosed();
|
||||
assert.isFalse(this.controller.galleryOpen);
|
||||
sinon.assert.calledWith(this.controller.loadCamera, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
88
gaia/apps/camera/test/unit/controllers/confirm_test.js
Executable file
88
gaia/apps/camera/test/unit/controllers/confirm_test.js
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suite('controllers/confirm', function() {
|
||||
suiteSetup(function(done) {
|
||||
var modules = this.modules = {};
|
||||
requirejs([
|
||||
'controllers/confirm',
|
||||
'views/confirm'
|
||||
], function(Controller, ConfirmView) {
|
||||
modules.controller = Controller;
|
||||
modules.ConfirmView = ConfirmView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var ConfirmView = this.modules.ConfirmView;
|
||||
var Controller = this.modules.controller;
|
||||
|
||||
this.app = {
|
||||
activity: {},
|
||||
camera: { on: sinon.spy() },
|
||||
ConfirmView: ConfirmView,
|
||||
on: sinon.spy()
|
||||
};
|
||||
|
||||
this.controller = new Controller(this.app);
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
this.sandbox.spy(this.app, 'ConfirmView');
|
||||
this.sandbox.stub(ConfirmView.prototype);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('ConfirmController()', function() {
|
||||
test('Should bind to app `newmedia` event', function() {
|
||||
assert.ok(this.app.on.calledWith('newmedia'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ConfirmController#onRecordingChange()', function() {
|
||||
setup(function() {
|
||||
this.app.activity.pick = true;
|
||||
this.controller.recording = true;
|
||||
sinon.stub(this.controller, 'renderView');
|
||||
});
|
||||
|
||||
test('Should set `recording` to true if starting', function() {
|
||||
this.controller.recording = false;
|
||||
this.controller.onRecordingChange('starting');
|
||||
assert.isTrue(this.controller.recording);
|
||||
});
|
||||
|
||||
test('Should set `recording` to false if error', function() {
|
||||
this.controller.onRecordingChange('error');
|
||||
assert.isFalse(this.controller.recording);
|
||||
});
|
||||
|
||||
test('Should render view if stopped', function() {
|
||||
this.controller.onRecordingChange('stopped');
|
||||
sinon.assert.called(this.controller.renderView);
|
||||
});
|
||||
|
||||
test('Should not do anything if recording failed and stopped', function() {
|
||||
this.controller.recording = false;
|
||||
this.controller.onRecordingChange('stopped');
|
||||
sinon.assert.notCalled(this.controller.renderView);
|
||||
});
|
||||
|
||||
test('Should not do anything if there is no active activity', function() {
|
||||
this.app.activity.pick = false;
|
||||
this.controller.onRecordingChange('stopped');
|
||||
sinon.assert.notCalled(this.controller.renderView);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ConfirmController#onNewMedia()', function() {
|
||||
test('Should not do anything if there is no active activity', function() {
|
||||
this.app.activity.pick = false;
|
||||
this.controller.onNewMedia({});
|
||||
assert.ok(this.app.ConfirmView.notCalled);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
214
gaia/apps/camera/test/unit/controllers/controls_test.js
Executable file
214
gaia/apps/camera/test/unit/controllers/controls_test.js
Executable file
|
|
@ -0,0 +1,214 @@
|
|||
|
||||
suite('controllers/controls', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'lib/camera/camera',
|
||||
'controllers/controls',
|
||||
'views/controls',
|
||||
'lib/settings',
|
||||
'lib/setting'
|
||||
], function(
|
||||
App, Camera, ControlsController, ControlsView, Settings, Setting) {
|
||||
self.App = App;
|
||||
self.Camera = Camera;
|
||||
self.ControlsController = ControlsController.ControlsController;
|
||||
self.ControlsView = ControlsView;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.camera = sinon.createStubInstance(this.Camera);
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.mode = sinon.createStubInstance(this.Setting);
|
||||
this.app.views = { controls: sinon.createStubInstance(this.ControlsView) };
|
||||
this.app.activity = { allowedTypes: {} };
|
||||
|
||||
// Fake available modes
|
||||
this.app.settings.mode.get
|
||||
.withArgs('options')
|
||||
.returns([{ key: 'picture' }, { key: 'video' }]);
|
||||
|
||||
// Aliases
|
||||
this.controls = this.app.views.controls;
|
||||
this.view = this.app.views.controls;
|
||||
this.settings = this.app.settings;
|
||||
|
||||
this.controller = new this.ControlsController(this.app);
|
||||
this.state = {};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
delete this.controller;
|
||||
});
|
||||
|
||||
suite('ControlsController()', function() {
|
||||
test('Should listen to the following events', function() {
|
||||
assert.ok(this.app.on.calledWith('localized', this.view.localize));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:opened',
|
||||
this.view.hide));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:closed',
|
||||
this.view.show));
|
||||
});
|
||||
|
||||
test('Should *not* show the cancel button when *not* within a \'pick\' activity', function() {
|
||||
assert.isTrue(this.app.views.controls.set.calledWith('cancel', false));
|
||||
});
|
||||
|
||||
test('Should show the cancel button when within activity', function() {
|
||||
this.app.activity.pick = true;
|
||||
this.state.controller = new this.ControlsController(this.app);
|
||||
assert.isTrue(this.app.views.controls.set.calledWith('cancel', true));
|
||||
});
|
||||
|
||||
test('It sets the mode to the value of the \'mode\' setting', function() {
|
||||
|
||||
// Test 'picture'
|
||||
this.app.settings.mode.selected.returns('picture');
|
||||
var controller = new this.ControlsController(this.app);
|
||||
sinon.assert.calledWith(this.view.setMode, 'picture');
|
||||
this.view.set.reset();
|
||||
|
||||
// Test 'video'
|
||||
this.app.settings.mode.selected.returns('video');
|
||||
controller = new this.ControlsController(this.app);
|
||||
sinon.assert.calledWith(this.view.setMode, 'video');
|
||||
this.view.set.reset();
|
||||
});
|
||||
|
||||
test('Should call the preview when click on thumbnail', function() {
|
||||
assert.ok(this.view.on.calledWith('click:thumbnail'));
|
||||
});
|
||||
|
||||
test('Should remove the capture button highlight when shutter fires', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'camera:shutter');
|
||||
|
||||
// Call the callback
|
||||
this.app.on.withArgs('camera:shutter').args[0][1]();
|
||||
|
||||
sinon.assert.calledWith(this.view.unset, 'capture-active');
|
||||
});
|
||||
|
||||
test('Should disable the controls when the camera is busy', function() {
|
||||
// Call the callback and check the view was disabled
|
||||
this.app.on.withArgs('busy').args[0][1]();
|
||||
sinon.assert.called(this.view.disable);
|
||||
});
|
||||
|
||||
suite('app.once(\'loaded\')', function() {
|
||||
setup(function() {
|
||||
// Call the callback
|
||||
this.app.once.withArgs('loaded').args[0][1]();
|
||||
});
|
||||
|
||||
test('It enables the controls', function() {
|
||||
sinon.assert.called(this.view.enable);
|
||||
});
|
||||
|
||||
test('It \'restores\' the controls when the camera is \'ready\' from thereon after', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'ready', this.controller.restore);
|
||||
});
|
||||
});
|
||||
|
||||
test('Should hide the controls when the countdown is started', function() {
|
||||
sinon.assert.calledWith(
|
||||
this.app.on,
|
||||
'countdown:started',
|
||||
this.controller.onCountdownStarted
|
||||
);
|
||||
});
|
||||
|
||||
test('Should restore the controls when the countdown is cleared', function() {
|
||||
sinon.assert.calledWith(
|
||||
this.app.on,
|
||||
'countdown:ended',
|
||||
this.controller.onCountdownStopped
|
||||
);
|
||||
});
|
||||
|
||||
test('Should disable the view intitially until camera is ready', function() {
|
||||
sinon.assert.called(this.view.disable);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Set view screen reader visibility based on settings opened/closed',
|
||||
function() {
|
||||
test('settings opened -> hide view from screen reader', function() {
|
||||
// Check the view is hidden from the screen reader.
|
||||
this.app.on.withArgs('settings:opened').args[0][1]();
|
||||
sinon.assert.calledWith(this.view.setScreenReaderVisible, false);
|
||||
});
|
||||
|
||||
test('settings closed -> show view to screen reader', function() {
|
||||
// Check the view is visible to the screen reader.
|
||||
this.app.on.withArgs('settings:closed').args[0][1]();
|
||||
sinon.assert.calledWith(this.view.setScreenReaderVisible, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController#configureMode()', function() {
|
||||
test('It\'s not switchable when only one mode is available', function() {
|
||||
|
||||
// Fake avaialable modes
|
||||
this.app.settings.mode.get
|
||||
.withArgs('options')
|
||||
.returns([{ key: 'picture' }]);
|
||||
|
||||
this.controller.configureMode();
|
||||
assert.isTrue(this.view.disable.calledWith('switch'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController#onCaptureClick', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.controller, 'captureHighlightOn');
|
||||
this.controller.onCaptureClick();
|
||||
});
|
||||
|
||||
test('Should highlight the capture button', function() {
|
||||
assert.isTrue(this.controller.captureHighlightOn.calledOnce);
|
||||
});
|
||||
|
||||
test('Should fire a \'capture\' event on the app', function() {
|
||||
assert.isTrue(this.app.emit.calledWith('capture'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController#onCameraWillChange', function() {
|
||||
test('When camera is reconfiguring it suspends the mode switch', function() {
|
||||
this.controller.onCameraWillChange();
|
||||
assert.isTrue(this.view.suspendModeSwitch.calledWith(true));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController#onCameraConfigured', function() {
|
||||
test('When camera is configured it unsuspends the mode switch', function() {
|
||||
this.controller.onCameraConfigured();
|
||||
assert.isTrue(this.view.suspendModeSwitch.calledWith(false));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController#onRecordingChange', function() {
|
||||
test('When recording view\'s setCaptureLabel should be called', function() {
|
||||
[true, false].forEach(function(recording) {
|
||||
this.controller.onRecordingChange(recording);
|
||||
assert.isTrue(this.view.setCaptureLabel.calledWith(recording));
|
||||
}, this);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsController.onViewModeChanged()', function() {
|
||||
test('It switches to the next mode setting', function() {
|
||||
this.controller.onViewModeChanged();
|
||||
sinon.assert.notCalled(this.view.enable);
|
||||
sinon.assert.called(this.settings.mode.next);
|
||||
});
|
||||
});
|
||||
});
|
||||
242
gaia/apps/camera/test/unit/controllers/hud_test.js
Executable file
242
gaia/apps/camera/test/unit/controllers/hud_test.js
Executable file
|
|
@ -0,0 +1,242 @@
|
|||
suite('controllers/hud', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'lib/camera/camera',
|
||||
'controllers/hud',
|
||||
'views/hud',
|
||||
'views/controls',
|
||||
'views/viewfinder',
|
||||
'views/notification',
|
||||
'lib/settings',
|
||||
'lib/setting'
|
||||
], function(
|
||||
App, Camera, HudController, HudView, ControlsView,
|
||||
ViewfinderView, NotificationView, Settings, Setting
|
||||
) {
|
||||
self.HudController = HudController.HudController;
|
||||
self.NotificationView = NotificationView;
|
||||
self.ViewfinderView = ViewfinderView;
|
||||
self.ControlsView = ControlsView;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
self.HudView = HudView;
|
||||
self.Camera = Camera;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.camera = sinon.createStubInstance(this.Camera);
|
||||
this.app.l10n = { get: sinon.spy(function(value) { return value; }) };
|
||||
this.app.views = {
|
||||
notification: sinon.createStubInstance(this.NotificationView),
|
||||
viewfinder: sinon.createStubInstance(this.ViewfinderView),
|
||||
controls: sinon.createStubInstance(this.ControlsView),
|
||||
hud: sinon.createStubInstance(this.HudView)
|
||||
};
|
||||
|
||||
// Stub 'cameras' setting
|
||||
this.app.settings = {};
|
||||
this.app.settings.cameras = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.flashModes = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.settingsMenu = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.mode = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.cameras.get.withArgs('options').returns([]);
|
||||
|
||||
// For convenience
|
||||
this.notification = this.app.views.notification;
|
||||
this.viewfinder = this.app.views.viewfinder;
|
||||
this.controls = this.app.views.controls;
|
||||
this.settings = this.app.settings;
|
||||
this.camera = this.app.camera;
|
||||
this.view = this.app.views.hud;
|
||||
|
||||
// Our test instance
|
||||
this.controller = new this.HudController(this.app);
|
||||
});
|
||||
|
||||
suite('HudController()', function() {
|
||||
test('Should listen to the following events', function() {
|
||||
assert.ok(this.app.settings.flashModes.on.calledWith('change:selected'));
|
||||
assert.ok(this.app.settings.mode.on.calledWith('change:selected'));
|
||||
assert.ok(this.app.settings.cameras.on.calledWith('change:selected'));
|
||||
|
||||
assert.ok(this.app.on.calledWith('settings:configured'));
|
||||
assert.ok(this.app.on.calledWith('ready'));
|
||||
assert.ok(this.app.on.calledWith('busy'));
|
||||
assert.ok(this.app.on.calledWith('localized'));
|
||||
assert.ok(this.app.on.calledWith('change:recording'));
|
||||
assert.ok(this.app.on.calledWith('countdown:started'));
|
||||
assert.ok(this.app.on.calledWith('countdown:ended'));
|
||||
assert.ok(this.app.on.calledWith('settings:opened'));
|
||||
assert.ok(this.app.on.calledWith('settings:closed'));
|
||||
|
||||
assert.ok(this.app.on.calledWith('previewgallery:opened',
|
||||
this.view.hide));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:closed',
|
||||
this.view.show));
|
||||
});
|
||||
|
||||
test('Should update the flash support once settings are configured', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'settings:configured', this.controller.updateFlashSupport);
|
||||
});
|
||||
|
||||
test('Should set \'camera\' to \'busy\' on view when busy', function() {
|
||||
assert.ok(this.view.setter.calledWith('camera', 'busy'));
|
||||
assert.ok(this.app.on.calledWith('busy'));
|
||||
});
|
||||
|
||||
test('Should set \'camera\' to \'ready\' on view when ready', function() {
|
||||
assert.ok(this.view.setter.calledWith('camera', 'ready'));
|
||||
assert.ok(this.app.on.calledWith('ready'));
|
||||
});
|
||||
|
||||
test('Should set \'countdown\' to \'active\' on view when started', function() {
|
||||
assert.ok(this.view.setter.calledWith('countdown', 'active'));
|
||||
assert.ok(this.app.on.calledWith('countdown:started'));
|
||||
});
|
||||
|
||||
test('Should set \'countdown\' to \'inactive\' on view when countdown ended or cleared', function() {
|
||||
assert.ok(this.view.setter.calledWith('countdown', 'inactive'));
|
||||
assert.ok(this.app.on.calledWith('countdown:ended'));
|
||||
});
|
||||
|
||||
test('Should set `recording` state on hud', function() {
|
||||
assert.ok(this.view.setter.calledWith('recording'));
|
||||
assert.ok(this.app.on.calledWith('change:recording'));
|
||||
});
|
||||
|
||||
test('Should enable camera button depending on support', function() {
|
||||
this.settings.cameras.get
|
||||
.withArgs('options')
|
||||
.returns(['back']);
|
||||
|
||||
sinon.assert.calledWith(this.view.enable, 'camera', false);
|
||||
|
||||
this.settings.cameras.get
|
||||
.withArgs('options')
|
||||
.returns(['back', 'front']);
|
||||
|
||||
this.controller = new this.HudController(this.app);
|
||||
sinon.assert.calledWith(this.view.enable, 'camera', true);
|
||||
});
|
||||
|
||||
test('Should disable flash initially until support is known', function() {
|
||||
sinon.assert.calledWith(this.view.disable, 'flash');
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#localize()', function() {
|
||||
setup(function() {
|
||||
this.controller.view = {
|
||||
setFlashModeLabel: sinon.spy(),
|
||||
setCameraLabel: sinon.spy(),
|
||||
setMenuLabel: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('Should cycle to the next flash setting', function() {
|
||||
this.controller.localize();
|
||||
assert.ok(this.controller.view.setFlashModeLabel.called);
|
||||
assert.ok(this.controller.view.setCameraLabel.called);
|
||||
assert.ok(this.controller.view.setMenuLabel.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#onFlashClick()', function() {
|
||||
setup(function() {
|
||||
this.settings.hdr = {
|
||||
selected: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('Should cycle to the next flash setting', function() {
|
||||
this.controller.onFlashClick();
|
||||
assert.ok(this.settings.flashModes.next.calledOnce);
|
||||
});
|
||||
|
||||
test('Should set the new value on the hud view', function() {
|
||||
this.settings.flashModes.selected.withArgs('key').returns('auto');
|
||||
this.controller.onFlashClick();
|
||||
assert.ok(this.view.set.calledWith('flashMode', 'auto'));
|
||||
});
|
||||
|
||||
test('Should notify', function() {
|
||||
sinon.spy(this.controller, 'notify');
|
||||
this.controller.onFlashClick();
|
||||
assert.ok(this.controller.notify.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#notify()', function() {
|
||||
setup(function() {
|
||||
this.settings.hdr = {
|
||||
selected: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('Should display a notification', function() {
|
||||
this.controller.onFlashClick();
|
||||
assert.ok(this.notification.display.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#updateFlashMode()', function() {
|
||||
setup(function() {
|
||||
this.settings.flashModes = {
|
||||
selected: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('Should update the flash mode', function() {
|
||||
this.controller.updateFlashMode();
|
||||
assert.ok(this.settings.flashModes.selected.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#updateCamera()', function() {
|
||||
setup(function() {
|
||||
this.settings.cameras = {
|
||||
selected: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
test('Should update the camera', function() {
|
||||
this.controller.updateCamera();
|
||||
assert.ok(this.settings.cameras.selected.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('HudController#onCameraClick()', function() {
|
||||
setup(function() {
|
||||
this.callback = this.view.on.withArgs('click:camera').args[0][1];
|
||||
});
|
||||
|
||||
test('Should clear notifications', function() {
|
||||
this.callback();
|
||||
sinon.assert.called(this.notification.clear);
|
||||
});
|
||||
|
||||
test('Should call move to next available camera', function() {
|
||||
this.callback();
|
||||
sinon.assert.calledOnce(this.settings.cameras.next);
|
||||
});
|
||||
|
||||
test('Should debounce (immediate) rapid button taps', function() {
|
||||
this.callback();
|
||||
sinon.assert.called(this.settings.cameras.next);
|
||||
this.callback();
|
||||
this.callback();
|
||||
this.callback();
|
||||
this.callback();
|
||||
sinon.assert.calledOnce(this.settings.cameras.next);
|
||||
});
|
||||
});
|
||||
});
|
||||
100
gaia/apps/camera/test/unit/controllers/indicators_test.js
Executable file
100
gaia/apps/camera/test/unit/controllers/indicators_test.js
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
suite('controllers/indicators', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'controllers/indicators',
|
||||
'views/indicators',
|
||||
'lib/setting',
|
||||
'app'
|
||||
], function(IndicatorsController, IndicatorsView, Setting, App) {
|
||||
self.IndicatorsController = IndicatorsController.IndicatorsController;
|
||||
self.IndicatorsView = IndicatorsView;
|
||||
self.Setting = Setting;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.settings = {
|
||||
hdr: sinon.createStubInstance(this.Setting),
|
||||
mode: sinon.createStubInstance(this.Setting),
|
||||
countdown: sinon.createStubInstance(this.Setting),
|
||||
indicators: sinon.createStubInstance(this.Setting)
|
||||
};
|
||||
this.app.views = {
|
||||
indicators: sinon.createStubInstance(this.IndicatorsView)
|
||||
};
|
||||
|
||||
// Shortcuts
|
||||
this.view = this.app.views.indicators;
|
||||
this.settings = this.app.settings;
|
||||
|
||||
// Return values
|
||||
this.settings.hdr.selected.returns('on');
|
||||
this.settings.countdown.selected.returns('on');
|
||||
this.app.get.withArgs('batteryStatus').returns('healthy');
|
||||
|
||||
// Create our test instance
|
||||
this.controller = new this.IndicatorsController(this.app);
|
||||
});
|
||||
|
||||
suite('IndicatorsController()', function() {
|
||||
test('Should bind settings change events', function() {
|
||||
assert.ok(this.app.settings.countdown.on.calledWith('change:selected'));
|
||||
assert.ok(this.app.settings.mode.on.calledWith('change:selected'));
|
||||
assert.ok(this.app.settings.hdr.on.calledWith('change:selected'));
|
||||
assert.ok(this.app.on.calledWith('change:batteryStatus'));
|
||||
assert.ok(this.app.on.calledWith('settings:configured'));
|
||||
assert.ok(this.app.on.calledWith('settings:opened'));
|
||||
assert.ok(this.app.on.calledWith('settings:closed'));
|
||||
|
||||
assert.ok(this.app.on.calledWith('previewgallery:opened',
|
||||
this.view.hide));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:closed',
|
||||
this.view.show));
|
||||
});
|
||||
|
||||
test('It sets the battery status initially', function() {
|
||||
sinon.assert.calledWith(this.view.set, 'battery', 'healthy');
|
||||
});
|
||||
|
||||
test('It sets hdr status initially', function() {
|
||||
sinon.assert.calledWith(this.view.set, 'hdr', 'on');
|
||||
});
|
||||
|
||||
test('It sets countdown status initially', function() {
|
||||
sinon.assert.calledWith(this.view.set, 'countdown', 'on');
|
||||
});
|
||||
|
||||
test('It reconfigures when the settings change', function() {
|
||||
sinon.assert.calledWith(this.app.on, 'settings:configured', this.controller.configure);
|
||||
});
|
||||
|
||||
test('It binds view setter for aria-hidden true', function() {
|
||||
sinon.assert.calledWith(this.view.setter, 'ariaHidden', true);
|
||||
});
|
||||
|
||||
test('It binds view setter for aria-hidden false', function() {
|
||||
sinon.assert.calledWith(this.view.setter, 'ariaHidden', false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('IndicatorsController#configure()', function() {
|
||||
test('Should set the current \'hdr\' and \'countdown\' keys' +
|
||||
'on the view', function() {
|
||||
var view = this.app.views.indicators;
|
||||
|
||||
this.app.settings.indicators.get.returns(this.enabledValues);
|
||||
this.controller = new this.IndicatorsController(this.app);
|
||||
this.controller.configure();
|
||||
|
||||
assert.ok(view.set.calledWith('countdown', 'on'));
|
||||
assert.ok(view.set.calledWith('hdr', 'on'));
|
||||
});
|
||||
});
|
||||
});
|
||||
127
gaia/apps/camera/test/unit/controllers/media_test.js
Executable file
127
gaia/apps/camera/test/unit/controllers/media_test.js
Executable file
|
|
@ -0,0 +1,127 @@
|
|||
suite('controllers/media', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/media',
|
||||
'lib/settings',
|
||||
], function(App, MediaController, Settings) {
|
||||
self.MediaController =
|
||||
MediaController.MediaController;
|
||||
self.Settings = Settings;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.require = sinon.stub();
|
||||
this.app.activity = {};
|
||||
this.app.settings.previewGallery = {
|
||||
get: sinon.spy()
|
||||
};
|
||||
|
||||
// Our test instance
|
||||
this.controller = new this.MediaController(this.app);
|
||||
});
|
||||
|
||||
suite('MediaController()', function() {
|
||||
setup(function() {
|
||||
this.controller.settings = {
|
||||
previewGallery: {
|
||||
get: sinon.spy()
|
||||
}
|
||||
};
|
||||
|
||||
this.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
test('Should listen to the following events', function() {
|
||||
assert.ok(this.app.on.calledWith('storage:itemdeleted'));
|
||||
assert.ok(this.app.on.calledWith('storage:changed'));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:deletevideo'));
|
||||
assert.ok(this.app.on.calledWith('previewgallery:deletepicture'));
|
||||
assert.ok(this.app.on.calledWith('newmedia'));
|
||||
assert.ok(this.app.on.calledWith('hidden'));
|
||||
assert.ok(this.app.on.calledWith('lazyloaded'));
|
||||
});
|
||||
|
||||
test('Check onNewMedia callback', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/fileName',
|
||||
isVideo: true
|
||||
};
|
||||
|
||||
this.app.activity = {
|
||||
active: false
|
||||
};
|
||||
|
||||
this.controller.items.unshift = sinon.spy();
|
||||
this.controller.updateThumbnail = sinon.spy();
|
||||
this.controller.onNewMedia(item);
|
||||
assert.ok(this.controller.items.unshift.called);
|
||||
assert.ok(this.controller.updateThumbnail.called);
|
||||
});
|
||||
|
||||
test('Should Check Storage Changed', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/fileName',
|
||||
isVideo: true
|
||||
};
|
||||
|
||||
var data = {
|
||||
path: 'root/fileName'
|
||||
};
|
||||
|
||||
this.app.activity = {
|
||||
active: false
|
||||
};
|
||||
|
||||
this.controller.configure = sinon.spy();
|
||||
this.controller.updateThumbnail = sinon.spy();
|
||||
this.controller.onNewMedia(item);
|
||||
this.controller.onStorageChanged('unavailable');
|
||||
assert.ok(this.controller.configure.called);
|
||||
assert.ok(this.controller.updateThumbnail.called);
|
||||
});
|
||||
|
||||
test('Should Check the Item Deleted', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/fileName',
|
||||
isVideo: true
|
||||
};
|
||||
|
||||
var data = {
|
||||
path: 'root/fileName'
|
||||
};
|
||||
|
||||
this.app.activity = {
|
||||
active: false
|
||||
};
|
||||
|
||||
this.controller.updateThumbnail = sinon.spy();
|
||||
this.controller.updatePreviewGallery = sinon.spy();
|
||||
this.controller.onNewMedia(item);
|
||||
this.controller.onItemDeleted(data);
|
||||
sinon.assert.calledWith(this.app.emit, 'media:deleted');
|
||||
});
|
||||
|
||||
test('Should close the preview on blur if in \'secureMode\'', function() {
|
||||
this.app.inSecureMode = true;
|
||||
this.controller.closePreview = sinon.spy();
|
||||
this.controller.configure = sinon.spy();
|
||||
this.controller.updateThumbnail = sinon.spy();
|
||||
this.controller.onHidden();
|
||||
assert.ok(this.controller.configure.called);
|
||||
assert.ok(this.controller.updateThumbnail.called);
|
||||
sinon.assert.calledWith(this.app.emit, 'media:configured');
|
||||
});
|
||||
});
|
||||
});
|
||||
141
gaia/apps/camera/test/unit/controllers/overlay_test.js
Executable file
141
gaia/apps/camera/test/unit/controllers/overlay_test.js
Executable file
|
|
@ -0,0 +1,141 @@
|
|||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suite('controllers/overlay', function() {
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/overlay',
|
||||
'views/overlay'
|
||||
], function(App, controller, Overlay) {
|
||||
self.Controller = controller;
|
||||
self.Overlay = Overlay;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.activity = {
|
||||
pick: false,
|
||||
cancel: sinon.spy()
|
||||
};
|
||||
this.app.require = sinon.stub();
|
||||
this.app.require.callsArgWith(1, this.Overlay);
|
||||
|
||||
this.controller = new this.Controller(this.app);
|
||||
});
|
||||
|
||||
suite('OverlayController()', function() {
|
||||
test('Should bind to events', function() {
|
||||
assert.ok(this.app.on.calledWith('storage:changed'));
|
||||
assert.ok(this.app.on.calledWith('change:batteryStatus'));
|
||||
assert.ok(this.app.on.calledWith('camera:requesting'));
|
||||
assert.ok(this.app.on.calledWith('camera:error'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayController#onStorageChanged()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'updateOverlay');
|
||||
});
|
||||
|
||||
test('Should *not* insert a new overlay', function() {
|
||||
this.controller.onStorageChanged('available');
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'storage', false, 'available');
|
||||
});
|
||||
|
||||
test('Should create overlay whenever the value is not \'available\'', function() {
|
||||
this.controller.onStorageChanged('foo');
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'storage', true, 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayController#onBatteryChanged()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'updateOverlay');
|
||||
});
|
||||
|
||||
test('Should create overlay if status is shutdown', function() {
|
||||
this.controller.onBatteryChanged('shutdown');
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'battery', true, 'shutdown');
|
||||
});
|
||||
|
||||
test('Should destroy overlay if previous is shutdown', function() {
|
||||
this.controller.onBatteryChanged('foo');
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'battery', false, 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayController#onCameraRequesting()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'updateOverlay');
|
||||
});
|
||||
|
||||
test('Should destroy overlay', function() {
|
||||
this.controller.onCameraRequesting();
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'cameraError', false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayController#onCameraError()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'updateOverlay');
|
||||
});
|
||||
|
||||
test('Should create overlay', function() {
|
||||
this.controller.onCameraError('foo');
|
||||
sinon.assert.calledWith(this.controller.updateOverlay, 'cameraError', true, 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayController#updateOverlay()', function() {
|
||||
setup(function() {
|
||||
this.OverlayProto = this.Overlay.prototype;
|
||||
sinon.spy(this.OverlayProto, 'initialize');
|
||||
sinon.stub(this.OverlayProto, 'appendTo', function() { return this; });
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.OverlayProto.initialize.restore();
|
||||
this.OverlayProto.appendTo.restore();
|
||||
});
|
||||
|
||||
test('Should not insert overlay when key is unknown', function() {
|
||||
this.controller.updateOverlay('storage', true, 'foobar');
|
||||
assert.isFalse(this.OverlayProto.appendTo.called);
|
||||
});
|
||||
|
||||
test('Should insert overlay when key is known', function() {
|
||||
this.controller.updateOverlay('storage', true, 'unavailable');
|
||||
assert.isTrue(this.OverlayProto.appendTo.called);
|
||||
});
|
||||
|
||||
test('Should not be closable if type is \'request-fail\'', function() {
|
||||
this.controller.updateOverlay('cameraError', true, 'request-fail');
|
||||
assert.isFalse(this.OverlayProto.initialize.args[0][0].closable);
|
||||
|
||||
this.app.activity.pick = true;
|
||||
this.controller.updateOverlay('cameraError', true, 'request-fail');
|
||||
assert.isFalse(this.OverlayProto.initialize.args[0][0].closable);
|
||||
});
|
||||
|
||||
test('Should not be closable if activity not pending', function() {
|
||||
this.controller.updateOverlay('storage', true, 'unavailable');
|
||||
assert.isFalse(this.OverlayProto.initialize.args[0][0].closable);
|
||||
});
|
||||
|
||||
test('Should be closable only if activity is pending', function() {
|
||||
this.app.activity.pick = true;
|
||||
this.controller.updateOverlay('storage', true, 'unavailable');
|
||||
assert.isTrue(this.OverlayProto.initialize.args[0][0].closable);
|
||||
});
|
||||
|
||||
test('Should append the overlay to the body', function() {
|
||||
this.controller.updateOverlay('storage', true, 'unavailable');
|
||||
assert.isTrue(this.OverlayProto.appendTo.calledWith(document.body));
|
||||
});
|
||||
});
|
||||
});
|
||||
294
gaia/apps/camera/test/unit/controllers/preview-gallery_test.js
Executable file
294
gaia/apps/camera/test/unit/controllers/preview-gallery_test.js
Executable file
|
|
@ -0,0 +1,294 @@
|
|||
suite('controllers/preview-gallery', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/preview-gallery',
|
||||
'lib/settings',
|
||||
'views/preview-gallery',
|
||||
'lib/storage',
|
||||
'lib/orientation'
|
||||
], function(
|
||||
App,PreviewGalleryController, Settings, PreviewGalleryView,
|
||||
Storage, orientation) {
|
||||
self.PreviewGalleryController =
|
||||
PreviewGalleryController.PreviewGalleryController;
|
||||
self.Settings = Settings;
|
||||
self.PreviewGalleryView = PreviewGalleryView;
|
||||
self.Storage = Storage;
|
||||
self.App = App;
|
||||
self.orientation = orientation;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.require = sinon.stub();
|
||||
this.app.activity = {};
|
||||
|
||||
// Fake dialog calls the
|
||||
// 'delete' callback sync.
|
||||
this.app.dialog = {
|
||||
show: function(title, msg, cancel, del) { del.callback(); },
|
||||
hide: sinon.stub()
|
||||
};
|
||||
|
||||
// Our test instance
|
||||
this.previewGalleryController = new this.PreviewGalleryController(this.app);
|
||||
|
||||
// For convenience
|
||||
this.camera = this.app.camera;
|
||||
this.previewGallery = this.previewGalleryController.view;
|
||||
this.controller = this.previewGalleryController;
|
||||
this.storage = this.app.storage;
|
||||
this.dialog = this.app.dialog;
|
||||
});
|
||||
|
||||
suite('PreviewGalleryController()', function() {
|
||||
setup(function() {
|
||||
var MozActivity = function() {};
|
||||
if (!window.MozActivity) {
|
||||
window.MozActivity = MozActivity;
|
||||
}
|
||||
sinon.stub(window, 'MozActivity');
|
||||
|
||||
this.previewGalleryController.storage = {
|
||||
deleteImage: sinon.spy(),
|
||||
deleteVideo: sinon.spy(),
|
||||
on: sinon.spy()
|
||||
};
|
||||
|
||||
this.previewGalleryController.settings = {
|
||||
activity: {
|
||||
get: sinon.spy()
|
||||
},
|
||||
};
|
||||
|
||||
this.previewGalleryController.resizeImageAndSave =
|
||||
function(options, done) {
|
||||
done(options.blob);
|
||||
};
|
||||
|
||||
this.app.require.callsArgWith(1,
|
||||
this.previewGalleryController.resizeImageAndSave);
|
||||
|
||||
this.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.MozActivity.restore();
|
||||
});
|
||||
|
||||
test('Should listen to the following events', function() {
|
||||
this.previewGalleryController.bindEvents();
|
||||
|
||||
assert.ok(this.app.on.calledWith('preview', this.controller.openPreview));
|
||||
assert.ok(this.app.on.calledWith('hidden', this.controller.closePreview));
|
||||
assert.ok(this.app.on.calledWith('media:configured', this.controller.updatePreviewGallery));
|
||||
assert.ok(this.app.on.calledWith('media:deleted', this.controller.updatePreviewGallery));
|
||||
});
|
||||
|
||||
test('Should open the gallery app when gallery button is pressed',
|
||||
function() {
|
||||
this.previewGalleryController.onGalleryButtonClick();
|
||||
var arg = window.MozActivity.args[0][0];
|
||||
|
||||
assert.ok(arg.name === 'browse');
|
||||
assert.ok(arg.data.type === 'photos');
|
||||
});
|
||||
|
||||
test('Should shareCurrentItem whose type is image', function() {
|
||||
var item = {
|
||||
blob: new Blob(['empty-image'], {'type': 'image/jpeg'}),
|
||||
filepath: 'root/folder1/folder2/fileName',
|
||||
isImage: true
|
||||
};
|
||||
this.previewGalleryController.items = [item];
|
||||
this.previewGalleryController.currentItemIndex = 0;
|
||||
this.previewGalleryController.shareCurrentItem();
|
||||
|
||||
assert.ok(this.app.emit.calledWith('busy', 'resizingImage'));
|
||||
|
||||
// Get first argument, of first call
|
||||
var arg = window.MozActivity.args[0][0];
|
||||
|
||||
assert.ok(arg.name === 'share');
|
||||
assert.ok(arg.data.type === 'image/*');
|
||||
assert.ok(arg.data.number === 1);
|
||||
assert.ok(arg.data.blobs[0] === item.blob);
|
||||
assert.ok(arg.data.filenames[0] === 'fileName');
|
||||
assert.ok(arg.data.filepaths[0] === item.filepath);
|
||||
});
|
||||
|
||||
test('Should shareCurrentItem whose type is video', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/folder1/folder2/fileName',
|
||||
isVideo: true
|
||||
};
|
||||
this.previewGalleryController.items = [item];
|
||||
this.previewGalleryController.currentItemIndex = 0;
|
||||
this.previewGalleryController.shareCurrentItem();
|
||||
|
||||
// Get first argument, of first call
|
||||
var arg = window.MozActivity.args[0][0];
|
||||
|
||||
assert.ok(arg.name === 'share');
|
||||
assert.ok(arg.data.type === 'video/*');
|
||||
assert.ok(arg.data.number === 1);
|
||||
assert.ok(arg.data.blobs[0] === item.blob);
|
||||
assert.ok(arg.data.filenames[0] === 'fileName');
|
||||
assert.ok(arg.data.filepaths[0] === item.filepath);
|
||||
});
|
||||
|
||||
suite('PreviewGalleryController#deleteCurrentItem()', function() {
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
test('Should deleteCurrentItem which is image', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/fileName',
|
||||
isVideo: false
|
||||
};
|
||||
|
||||
this.previewGalleryController.items = [item];
|
||||
this.previewGalleryController.currentItemIndex = 0;
|
||||
this.previewGalleryController.deleteCurrentItem();
|
||||
|
||||
assert.ok(this.app.emit.calledWith('previewgallery:deletepicture', 'root/fileName'));
|
||||
});
|
||||
|
||||
test('Should deleteCurrentItem which is video', function() {
|
||||
var item = {
|
||||
blob: {},
|
||||
filepath: 'root/fileName',
|
||||
isVideo: true
|
||||
};
|
||||
|
||||
this.previewGalleryController.items = [item];
|
||||
this.previewGalleryController.currentItemIndex = 0;
|
||||
this.previewGalleryController.deleteCurrentItem();
|
||||
|
||||
assert.ok(this.app.emit.calledWith('previewgallery:deletevideo', 'root/fileName'));
|
||||
});
|
||||
});
|
||||
|
||||
test('Should go to next image on handleSwipe(\'left\')', function() {
|
||||
this.previewGalleryController.items = [1,2,3];
|
||||
this.previewGalleryController.currentItemIndex = 1;
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.handleSwipe('left');
|
||||
assert.ok(this.previewGalleryController.previewItem.called);
|
||||
assert.equal(this.previewGalleryController.currentItemIndex, 2);
|
||||
});
|
||||
|
||||
test('Should go to previous image on handleSwipe(\'right\')', function() {
|
||||
this.previewGalleryController.items = [1,2,3];
|
||||
this.previewGalleryController.currentItemIndex = 1;
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.handleSwipe('right');
|
||||
assert.ok(this.previewGalleryController.previewItem.called);
|
||||
assert.equal(this.previewGalleryController.currentItemIndex, 0);
|
||||
});
|
||||
|
||||
test('Should not go to next image if there is not one', function() {
|
||||
this.previewGalleryController.items = [1,2];
|
||||
this.previewGalleryController.currentItemIndex = 1;
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.handleSwipe('left');
|
||||
assert.isFalse(this.previewGalleryController.previewItem.called);
|
||||
assert.equal(this.previewGalleryController.currentItemIndex, 1);
|
||||
});
|
||||
|
||||
test('Should not go to previous image if there is not one', function() {
|
||||
this.previewGalleryController.items = [1,2];
|
||||
this.previewGalleryController.currentItemIndex = 0;
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.handleSwipe('right');
|
||||
assert.isFalse(this.previewGalleryController.previewItem.called);
|
||||
assert.equal(this.previewGalleryController.currentItemIndex, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('PreviewGalleryController#openPreview()', function() {
|
||||
setup(function() {
|
||||
this.app.require.callsArgWith(1, this.PreviewGalleryView, sinon.stub());
|
||||
sinon.stub(this.controller, 'previewItem');
|
||||
this.controller.settings = {
|
||||
activity: {
|
||||
get: sinon.spy()
|
||||
},
|
||||
previewGallery: {
|
||||
get: sinon.spy()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('Should call previewItem', function() {
|
||||
this.controller.openPreview();
|
||||
assert.isTrue(this.controller.previewItem.called);
|
||||
});
|
||||
|
||||
// XXX: this is really a view test, but we don't have tests for the view yet
|
||||
test('Should lock and unlock orientation when opening and closing view',
|
||||
function() {
|
||||
this.orientation.unlock = sinon.spy();
|
||||
this.orientation.lock = sinon.spy();
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.openPreview();
|
||||
assert.isTrue(this.orientation.unlock.called);
|
||||
assert.isTrue(this.previewGalleryController.previewItem.called);
|
||||
assert.isFalse(this.orientation.lock.called);
|
||||
this.previewGalleryController.closePreview();
|
||||
assert.isTrue(this.orientation.lock.called);
|
||||
});
|
||||
|
||||
// XXX: this is really a view test, but we don't have tests for the view yet
|
||||
test('Should add and remove a resize handler when opening and closing view',
|
||||
function() {
|
||||
var add = sinon.spy(window, 'addEventListener').withArgs('resize');
|
||||
var remove =
|
||||
sinon.spy(window, 'removeEventListener').withArgs('resize');
|
||||
this.previewGalleryController.previewItem = sinon.spy();
|
||||
this.previewGalleryController.openPreview();
|
||||
assert.ok(add.calledTwice); // twice because VideoPlayer does too
|
||||
this.previewGalleryController.closePreview();
|
||||
assert.equal(remove.callCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('PreviewGalleryController#closePreview()', function() {
|
||||
setup(function() {
|
||||
this.controller.view = new this.PreviewGalleryView();
|
||||
this.previewGalleryView = this.controller.view;
|
||||
this.controller.view.close = sinon.spy();
|
||||
this.controller.view.destroy = sinon.spy();
|
||||
this.controller.closePreview();
|
||||
});
|
||||
|
||||
test('Should set `previewGalleryOpen` to `false` on app', function() {
|
||||
assert.isTrue(this.previewGalleryView.close.called);
|
||||
assert.isTrue(this.previewGalleryView.destroy.called);
|
||||
assert.isTrue(this.controller.view === null);
|
||||
});
|
||||
});
|
||||
|
||||
suite('PreviewGalleryController#onPreviewOptionClick()', function() {
|
||||
setup(function() {
|
||||
this.controller.view = new this.PreviewGalleryView();
|
||||
this.controller.view.showOptionsMenu = sinon.spy();
|
||||
this.controller.onOptionsClick();
|
||||
});
|
||||
|
||||
test('Should open previewOption on onPreviewOptionClick', function() {
|
||||
assert.isTrue(this.controller.view.showOptionsMenu.called);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
84
gaia/apps/camera/test/unit/controllers/recording-timer_test.js
Executable file
84
gaia/apps/camera/test/unit/controllers/recording-timer_test.js
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
'use strict';
|
||||
|
||||
suite('controllers/recording-timer', function() {
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'app',
|
||||
'controllers/recording-timer',
|
||||
'views/recording-timer'
|
||||
], function(App, RecordingTimerController, RecordingTimerView) {
|
||||
self.RecordingTimerController = RecordingTimerController.RecordingTimerController;
|
||||
self.RecordingTimerView = RecordingTimerView;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.view = sinon.createStubInstance(this.RecordingTimerView);
|
||||
this.app.el = {};
|
||||
|
||||
// Alias
|
||||
this.view = this.app.view;
|
||||
|
||||
// Create out test instance
|
||||
this.controller = new this.RecordingTimerController(this.app);
|
||||
});
|
||||
|
||||
suite('RecordingTimerController()', function() {
|
||||
test('Should respond to recording change', function() {
|
||||
assert.isTrue(this.app.on.calledWith('change:recording'));
|
||||
});
|
||||
|
||||
test('Should put the view in the DOM', function() {
|
||||
assert.isTrue(this.view.appendTo.calledWith(this.app.el));
|
||||
});
|
||||
|
||||
test('Should update the view value when the recording time changes', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:recorderTimeUpdate', this.view.value));
|
||||
});
|
||||
});
|
||||
|
||||
suite('RecordingTimerController#onRecordingChange()', function() {
|
||||
test('Should hide the view if not recording', function() {
|
||||
this.controller.onRecordingChange(false);
|
||||
assert.isTrue(this.view.hide.called);
|
||||
});
|
||||
|
||||
test('Should show and reset the view if recording', function() {
|
||||
this.controller.onRecordingChange(true);
|
||||
assert.isTrue(this.view.show.called);
|
||||
assert.isTrue(this.view.value.calledWith(0));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Accessibility Tests', function() {
|
||||
|
||||
var app,
|
||||
view,
|
||||
controller;
|
||||
|
||||
setup(function() {
|
||||
app = sinon.createStubInstance(this.App);
|
||||
app.view = new this.RecordingTimerView();
|
||||
view = app.view;
|
||||
controller = new this.RecordingTimerController(app);
|
||||
});
|
||||
|
||||
test('Explicitly test for RecordingTimer visibility', function() {
|
||||
// App is in its initialized state
|
||||
assert.isFalse(view.el.classList.contains('visible'));
|
||||
|
||||
// Fire start recording event
|
||||
controller.onRecordingChange(true);
|
||||
assert.isTrue(view.el.classList.contains('visible'));
|
||||
|
||||
// Fire stop recording event
|
||||
controller.onRecordingChange(false);
|
||||
assert.isFalse(view.el.classList.contains('visible'));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
504
gaia/apps/camera/test/unit/controllers/settings_test.js
Executable file
504
gaia/apps/camera/test/unit/controllers/settings_test.js
Executable file
|
|
@ -0,0 +1,504 @@
|
|||
suite('controllers/settings', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'controllers/settings',
|
||||
'lib/settings',
|
||||
'lib/setting',
|
||||
'app',
|
||||
'views/notification',
|
||||
'views/settings'
|
||||
], function(
|
||||
SettingsController, Settings, Setting,
|
||||
App, NotificationViews, SettingsView) {
|
||||
self.SettingsController = SettingsController.SettingsController;
|
||||
self.NotificationViews = NotificationViews;
|
||||
self.SettingsView = SettingsView;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.require = sinon.stub();
|
||||
this.app.require.callsArgWith(1, this.SettingsView);
|
||||
|
||||
// Settings
|
||||
this.app.el = {};
|
||||
this.app.activity = { active: false };
|
||||
this.app.formatPictureSizes = sinon.stub();
|
||||
this.app.formatRecorderProfiles = sinon.stub();
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.mode = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.pictureSizes = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.pictureSizesFront = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.pictureSizesBack = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.recorderProfiles = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.recorderProfilesFront = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.recorderProfilesBack = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.flashModesPicture = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.flashModesVideo = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.hdr = sinon.createStubInstance(this.Setting);
|
||||
this.app.views = {
|
||||
notification: sinon.createStubInstance(this.NotificationViews)
|
||||
};
|
||||
|
||||
// SettingsView mock
|
||||
this.app.SettingsView = sinon.spy(function() {
|
||||
var view = sinon.createStubInstance(self.SettingsView);
|
||||
view.render.returns(view);
|
||||
view.appendTo.returns(view);
|
||||
view.on.returns(view);
|
||||
view.fadeOut.callsArg(0);
|
||||
return view;
|
||||
});
|
||||
|
||||
document.l10n = {
|
||||
formatValue: key => {
|
||||
return {
|
||||
then(fn) { fn('LOCALIZED'); }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Shortcut
|
||||
this.notification = this.app.views.notification;
|
||||
this.settings = this.app.settings;
|
||||
|
||||
// Create test instance
|
||||
this.controller = new this.SettingsController(this.app);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
delete document.l10n;
|
||||
});
|
||||
|
||||
suite('SettingsController()', function() {
|
||||
test('Should setup aliases for `recorderProfiles`, `pictureSizes` and `flashModes`', function() {
|
||||
sinon.assert.calledThrice(this.settings.alias);
|
||||
});
|
||||
|
||||
test('Should toggle the settings menu on \'settings:toggle\'', function() {
|
||||
assert.isTrue(this.app.on.calledWith('settings:toggle', this.controller.toggleSettings));
|
||||
});
|
||||
|
||||
test('Should update the settings when the camera hardware changes', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:newcamera', this.controller.onNewCamera));
|
||||
});
|
||||
|
||||
test('Should format pictreSize titles when the app is localized', function() {
|
||||
assert.isTrue(this.app.on.calledWith('localized', this.controller.formatPictureSizeTitles));
|
||||
});
|
||||
|
||||
test('Should listen to \'camera:newcamera\'', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:newcamera', this.controller.onNewCamera));
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#configurePictureSizes()', function() {
|
||||
setup(function() {
|
||||
this.pictureSizes = [];
|
||||
this.exclude = ['480x320'];
|
||||
|
||||
this.app.formatPictureSizes.returns('formatted');
|
||||
|
||||
this.settings.pictureSizes.get
|
||||
.withArgs('exclude')
|
||||
.returns(this.exclude);
|
||||
|
||||
this.settings.pictureSizes.current = sinon.stub()
|
||||
.returns(this.settings.pictureSizesBack);
|
||||
|
||||
document.l10n = {
|
||||
formatValue: function(id, args) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
this.settings.pictureSizes.get
|
||||
.withArgs('options')
|
||||
.returns([]);
|
||||
|
||||
// Run it
|
||||
this.controller.configurePictureSizes({
|
||||
pictureSize: { height: 1536, width: 2048 },
|
||||
capabilities: {
|
||||
pictureSizes: this.pictureSizes
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Should format the raw pictureSizes list', function() {
|
||||
assert.isTrue(this.app.formatPictureSizes.calledWith(this.pictureSizes));
|
||||
});
|
||||
|
||||
test('Should reset pictureSize options with formatted list', function() {
|
||||
assert.isTrue(this.settings.pictureSizes.resetOptions.calledWith('formatted'));
|
||||
});
|
||||
|
||||
test('Should pass the raw recorderProfiles into formatRecorderProfiles', function() {
|
||||
var arg1 = this.app.formatPictureSizes.args[0][0];
|
||||
assert.equal(arg1, this.pictureSizes);
|
||||
});
|
||||
|
||||
test('Should pass the `exclude`', function() {
|
||||
var options = this.app.formatPictureSizes.args[0][1];
|
||||
assert.equal(options.exclude, this.exclude);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#configureRecorderProfiles()', function() {
|
||||
setup(function() {
|
||||
this.recorderProfiles = {};
|
||||
this.formatted = ['1080p', '720p', '480p'];
|
||||
this.exclude = ['1080p'];
|
||||
|
||||
this.newCamera = {
|
||||
recorderProfile: '720p',
|
||||
pictureSize: { height: 1536, width: 2048 },
|
||||
capabilities: {
|
||||
recorderProfiles: this.recorderProfiles
|
||||
}
|
||||
};
|
||||
|
||||
this.app.formatRecorderProfiles
|
||||
.returns(this.formatted);
|
||||
|
||||
this.settings.recorderProfiles.get
|
||||
.withArgs('exclude')
|
||||
.returns(this.exclude);
|
||||
|
||||
this.settings.recorderProfiles.current = sinon.stub()
|
||||
.returns(this.settings.recorderProfilesBack);
|
||||
|
||||
// Run it
|
||||
this.controller.configureRecorderProfiles(this.newCamera);
|
||||
});
|
||||
|
||||
test('Should format the raw recorderProfiles list', function() {
|
||||
assert.isTrue(this.app.formatRecorderProfiles.calledWith(this.recorderProfiles));
|
||||
});
|
||||
|
||||
test('Should reset recorderProfiles options with formatted list', function() {
|
||||
var resetOptions = this.settings.recorderProfiles.resetOptions;
|
||||
assert.isTrue(resetOptions.calledWith(this.formatted));
|
||||
});
|
||||
|
||||
test('Should pass the raw recorderProfiles into formatRecorderProfiles', function() {
|
||||
var arg1 = this.app.formatRecorderProfiles.args[0][0];
|
||||
assert.equal(arg1, this.recorderProfiles);
|
||||
});
|
||||
|
||||
test('Should pass `exclude` option to formatRecorderProfiles', function() {
|
||||
var options = this.app.formatRecorderProfiles.args[0][1];
|
||||
assert.equal(options.exclude, this.exclude);
|
||||
});
|
||||
|
||||
test('Should pick the last formatted recorderProfile if `maxFileSize` is defined', function() {
|
||||
this.settings.recorderProfiles.resetOptions.reset();
|
||||
|
||||
this.settings.recorderProfiles.get
|
||||
.withArgs('maxFileSizeBytes')
|
||||
.returns(100);
|
||||
|
||||
this.controller.configureRecorderProfiles(this.newCamera);
|
||||
var arg = this.settings.recorderProfiles.resetOptions.args[0][0];
|
||||
|
||||
assert.deepEqual(arg, ['480p']);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#onOptionTap()', function() {
|
||||
setup(function() {
|
||||
this.setting = sinon.createStubInstance(this.Setting);
|
||||
sinon.stub(this.controller, 'closeSettings').callsArg(0);
|
||||
sinon.stub(this.controller, 'notify');
|
||||
});
|
||||
|
||||
test('Should select the given key on the given option', function() {
|
||||
this.controller.onOptionTap('the-key', this.setting);
|
||||
assert.isTrue(this.setting.select.calledWith('the-key'));
|
||||
});
|
||||
|
||||
test('Should close the settings menu', function() {
|
||||
this.controller.onOptionTap('the-key', this.setting);
|
||||
assert.isTrue(this.controller.closeSettings.called);
|
||||
});
|
||||
|
||||
test('It shows a notification after the settings have closed', function() {
|
||||
this.controller.onOptionTap('the-key', this.setting);
|
||||
assert.isTrue(this.controller.notify.calledAfter(this.controller.closeSettings));
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#notify()', function() {
|
||||
setup(function() {
|
||||
this.setting = sinon.createStubInstance(this.Setting);
|
||||
this.setting.selected.withArgs('title').returns('l10n-key');
|
||||
});
|
||||
|
||||
test('Should display a notification', function() {
|
||||
this.controller.notify(this.setting);
|
||||
assert.isTrue(this.notification.display.called);
|
||||
});
|
||||
|
||||
test('Should not display a notification if flagged `false`', function() {
|
||||
this.setting.get.withArgs('notifications').returns(false);
|
||||
this.controller.notify(this.setting);
|
||||
sinon.assert.notCalled(this.notification.display);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#validMenuItem()', function() {
|
||||
test('Should return `true` if supported', function() {
|
||||
var output;
|
||||
|
||||
this.app.settings.hdr.supported.returns(true);
|
||||
output = this.controller.validMenuItem({ key: 'hdr' });
|
||||
assert.isTrue(output);
|
||||
|
||||
this.app.settings.hdr.supported.returns(false);
|
||||
output = this.controller.validMenuItem({ key: 'hdr' });
|
||||
assert.isFalse(output);
|
||||
});
|
||||
|
||||
test('Should cope with undefined settings', function() {
|
||||
assert.isFalse(this.controller.validMenuItem({ key: 'fake' }));
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#onNewCamera()', function() {
|
||||
setup(function() {
|
||||
this.newCamera = {
|
||||
recorderProfile: '720p',
|
||||
pictureSize: { height: 1536, width: 2048 },
|
||||
capabilities: {
|
||||
hdr: ['on', 'off'],
|
||||
pictureSizes: [],
|
||||
recorderProfiles: [],
|
||||
flashModes: []
|
||||
}
|
||||
};
|
||||
|
||||
this.settings.pictureSizes.current = sinon.stub()
|
||||
.returns(this.settings.pictureSizesBack);
|
||||
|
||||
this.settings.recorderProfiles.current = sinon.stub()
|
||||
.returns(this.settings.recorderProfilesBack);
|
||||
|
||||
this.app.settings.pictureSizes.get.returns([]);
|
||||
this.app.formatRecorderProfiles
|
||||
.returns(['1080p', '720p', '480p' ]);
|
||||
});
|
||||
|
||||
test('Should filter both \'picture\' and \'video\' flashModes', function() {
|
||||
var picture = this.settings.flashModesPicture;
|
||||
var video = this.settings.flashModesVideo;
|
||||
var capabilities = this.newCamera.capabilities;
|
||||
|
||||
this.controller.onNewCamera(this.newCamera);
|
||||
|
||||
assert.ok(picture.filterOptions.calledWith(capabilities.flashModes));
|
||||
assert.ok(video.filterOptions.calledWith(capabilities.flashModes));
|
||||
});
|
||||
|
||||
test('Should filter hdr', function() {
|
||||
var capabilities = this.newCamera.capabilities;
|
||||
this.controller.onNewCamera(this.newCamera);
|
||||
assert.ok(this.settings.hdr.filterOptions.calledWith(capabilities.hdr));
|
||||
});
|
||||
|
||||
test('Should reset pictureSizes and recorderProfiles options', function() {
|
||||
var recorderProfiles = this.settings.recorderProfiles;
|
||||
var pictureSizes = this.settings.pictureSizes;
|
||||
|
||||
this.controller.onNewCamera(this.newCamera);
|
||||
|
||||
assert.ok(recorderProfiles.resetOptions.called);
|
||||
assert.ok(pictureSizes.resetOptions.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#toggleSettings()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'closeSettings');
|
||||
sinon.stub(this.controller, 'openSettings');
|
||||
});
|
||||
|
||||
test('Should close the settings if a `view` is present', function() {
|
||||
this.controller.view = 'defined';
|
||||
this.controller.toggleSettings();
|
||||
assert.isTrue(this.controller.closeSettings.called);
|
||||
});
|
||||
|
||||
test('Should open the settings if a `view` is not present', function() {
|
||||
this.controller.view = undefined;
|
||||
this.controller.toggleSettings();
|
||||
assert.isTrue(this.controller.openSettings.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#openSettings()', function() {
|
||||
setup(function() {
|
||||
this.menuItems = [];
|
||||
sinon.stub(this.controller, 'menuItems').returns(this.menuItems);
|
||||
|
||||
// Call it
|
||||
this.controller.openSettings();
|
||||
});
|
||||
|
||||
|
||||
test('Should create a new SettingsView passing menu items', function() {
|
||||
var options = this.app.SettingsView.args[0][0];
|
||||
|
||||
assert.isTrue(this.app.SettingsView.called);
|
||||
assert.equal(options.items, this.menuItems);
|
||||
});
|
||||
|
||||
test('Should render and appendTo app.el', function() {
|
||||
assert.isTrue(this.controller.view.render.called);
|
||||
assert.isTrue(this.controller.view.appendTo.calledWith(this.app.el));
|
||||
});
|
||||
|
||||
test('Should bind to `click:close` and `click:option` events', function() {
|
||||
assert.isTrue(this.controller.view.on.calledWith('click:close'));
|
||||
assert.isTrue(this.controller.view.on.calledWith('click:option'));
|
||||
});
|
||||
|
||||
test('Should emit a `settings:open` event', function() {
|
||||
assert.isTrue(this.app.emit.calledWith('settings:opened'));
|
||||
});
|
||||
|
||||
test('Should not do anything if a view already exists', function() {
|
||||
this.app.SettingsView.reset();
|
||||
this.controller.view = 'defined';
|
||||
this.controller.openSettings();
|
||||
assert.isFalse(this.app.SettingsView.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#closeSettings()', function() {
|
||||
setup(function() {
|
||||
// Open settings first so we have a view
|
||||
sinon.stub(this.controller, 'menuItems').returns(this.menuItems);
|
||||
this.controller.openSettings();
|
||||
this.view = this.controller.view;
|
||||
});
|
||||
|
||||
test('Should not do anything if no view exists', function() {
|
||||
this.controller.view = undefined;
|
||||
this.controller.closeSettings();
|
||||
assert.isFalse(this.app.emit.calledWith('settings:closed'));
|
||||
});
|
||||
|
||||
test('Should destroy the view', function() {
|
||||
this.controller.closeSettings();
|
||||
assert.isTrue(this.view.destroy.called);
|
||||
});
|
||||
|
||||
test('Should null out `view`', function() {
|
||||
this.controller.closeSettings();
|
||||
assert.equal(this.controller.view, null);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#formatPictureSizeTitles()', function() {
|
||||
setup(function(done) {
|
||||
this.options = [
|
||||
{
|
||||
key: '400x300',
|
||||
data: {
|
||||
mp: 0,
|
||||
width: 400,
|
||||
height: 300,
|
||||
aspect: '4:3'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '1600x900',
|
||||
data: {
|
||||
mp: 1,
|
||||
width: 1600,
|
||||
height: 900,
|
||||
aspect: '16:9'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '3200x1800',
|
||||
data: {
|
||||
mp: 6,
|
||||
width: 3200,
|
||||
height: 1800,
|
||||
aspect: '16:9'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.settings.pictureSizes.get
|
||||
.withArgs('options')
|
||||
.returns(this.options);
|
||||
|
||||
document.l10n = {
|
||||
formatValue: function(id, args) {
|
||||
return Promise.resolve('MP');
|
||||
}
|
||||
};
|
||||
|
||||
// Call the test subject
|
||||
this.controller.formatPictureSizeTitles().then(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Should include MP value for > 1MP', function() {
|
||||
assert.isTrue(this.options[2].title.indexOf('6MP') > -1, this.options[2].title);
|
||||
assert.isFalse(this.options[0].title.indexOf('MP') > -1, this.options[0].title);
|
||||
});
|
||||
|
||||
test('Should include the resolution', function() {
|
||||
assert.isTrue(this.options[0].title.indexOf('400x300') > -1);
|
||||
});
|
||||
|
||||
test('Should use localized \'MP\' string', function() {
|
||||
document.l10n = {
|
||||
formatValue: function(id, args) {
|
||||
return Promise.resolve('MP');
|
||||
}
|
||||
};
|
||||
|
||||
this.controller.formatPictureSizeTitles();
|
||||
assert.isTrue(this.options[2].title.indexOf('MP') > -1, this.options[2].title);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsController#onPickActivity()', function() {
|
||||
setup(function() {
|
||||
this.onPickActivity = this.app.on.withArgs('activity:pick').args[0][1];
|
||||
});
|
||||
|
||||
test('Should not save settings changes when in \'pick\' activity', function() {
|
||||
this.onPickActivity({});
|
||||
sinon.assert.called(this.settings.dontSave);
|
||||
});
|
||||
|
||||
test('Should set maxPixelSize on pictureSizes settings', function() {
|
||||
this.onPickActivity({ maxPixelSize: 100 });
|
||||
sinon.assert.calledWith(this.settings.pictureSizesFront.set, 'maxPixelSize', 100);
|
||||
sinon.assert.calledWith(this.settings.pictureSizesBack.set, 'maxPixelSize', 100);
|
||||
});
|
||||
|
||||
test('Should set maxPixelSize on pictureSizes settings', function() {
|
||||
this.onPickActivity({ maxFileSizeBytes: 100 });
|
||||
sinon.assert.calledWith(this.settings.recorderProfilesFront.set, 'maxFileSizeBytes', 100);
|
||||
sinon.assert.calledWith(this.settings.recorderProfilesBack.set, 'maxFileSizeBytes', 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
164
gaia/apps/camera/test/unit/controllers/storage_test.js
Executable file
164
gaia/apps/camera/test/unit/controllers/storage_test.js
Executable file
|
|
@ -0,0 +1,164 @@
|
|||
suite('controllers/storage', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'controllers/storage',
|
||||
'lib/setting',
|
||||
'lib/storage',
|
||||
'app'
|
||||
], function(StorageController, Setting, Storage, App) {
|
||||
self.StorageController = StorageController.StorageController;
|
||||
self.Setting = Setting;
|
||||
self.Storage = Storage;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.storage = sinon.createStubInstance(this.Storage);
|
||||
this.app.storage.createVideoFilepath = 'test';
|
||||
this.app.camera = {};
|
||||
this.app.settings = {
|
||||
pictureSizes: sinon.createStubInstance(this.Setting)
|
||||
};
|
||||
|
||||
// Aliases
|
||||
this.camera = this.app.camera;
|
||||
this.settings = this.app.settings;
|
||||
this.storage = this.app.storage;
|
||||
|
||||
this.settings.pictureSizes.selected
|
||||
.withArgs('data')
|
||||
.returns({ width: 400, height: 300 });
|
||||
|
||||
// Create a test instance
|
||||
this.controller = new this.StorageController(this.app);
|
||||
});
|
||||
|
||||
suite('StorageController()', function() {
|
||||
test('Should set the camera `camera.createVideoFilepath`', function() {
|
||||
assert.equal(this.camera.createVideoFilepath, 'test');
|
||||
});
|
||||
|
||||
test('Should update maxFileSize when picture size changes', function() {
|
||||
var on = this.settings.pictureSizes.on;
|
||||
assert.isTrue(on.calledWith('change:selected', this.controller.updateMaxFileSize));
|
||||
});
|
||||
|
||||
test('Should update maxFileSize when settings are configured', function() {
|
||||
assert.isTrue(this.app.on.calledWith('settings:configured', this.controller.updateMaxFileSize));
|
||||
});
|
||||
|
||||
test('Should delete a picture from storage when one is deleted from the preview gallery', function() {
|
||||
assert.isTrue(this.app.on.calledWith('previewgallery:deletepicture', this.storage.deletePicture));
|
||||
});
|
||||
|
||||
test('Should delete a video from storage when one is deleted from the preview gallery', function() {
|
||||
assert.isTrue(this.app.on.calledWith('previewgallery:deletevideo', this.storage.deleteVideo));
|
||||
});
|
||||
|
||||
test('Should media when the camera generates it', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:newimage', this.controller.storePicture));
|
||||
assert.isTrue(this.app.on.calledWith('camera:newvideo', this.controller.storeVideo));
|
||||
});
|
||||
|
||||
test('Should check storage when the app becomes visible', function() {
|
||||
assert.isTrue(this.app.on.calledWith('visible', this.storage.check));
|
||||
});
|
||||
|
||||
test('Should set the maxFileSize initially', function() {
|
||||
assert.isTrue(this.storage.setMaxFileSize.calledOnce);
|
||||
});
|
||||
|
||||
test('Should run an intitial storage check', function() {
|
||||
this.storage.setMaxFileSize.restore();
|
||||
this.controller = new this.StorageController(this.app);
|
||||
assert.isTrue(this.storage.check.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('StorageController#storePicture()', function() {
|
||||
setup(function() {
|
||||
this.storage.addPicture.callsArgWith(1, null, '<filepath>', '<abspath>', '<file-blob>');
|
||||
|
||||
this.picture = { blob: '<memory-blob>' };
|
||||
this.controller.storePicture(this.picture);
|
||||
});
|
||||
|
||||
test('Should store the picture', function() {
|
||||
assert.isTrue(this.storage.addPicture.calledWith('<memory-blob>'));
|
||||
});
|
||||
|
||||
test('Should emit a `newmedia` app event', function() {
|
||||
assert.isTrue(this.app.emit.calledWith('newmedia', this.picture));
|
||||
});
|
||||
|
||||
test('Should switch the <memory-blob> with the <file-blob>', function() {
|
||||
assert.equal(this.picture.blob, '<file-blob>');
|
||||
});
|
||||
|
||||
test('Should store the <filepath>', function() {
|
||||
assert.equal(this.picture.filepath, '<filepath>');
|
||||
});
|
||||
});
|
||||
|
||||
suite('StorageController#storeVideo()', function() {
|
||||
setup(function() {
|
||||
this.storage.addPicture.callsArgWith(2, null, '<filepath>', '<abspath>', '<file-blob>');
|
||||
|
||||
this.video = {
|
||||
blob: '<file-blob>',
|
||||
filepath: 'path/to/video.3gp',
|
||||
poster: { blob: '<poster-memory-blob>' }
|
||||
};
|
||||
|
||||
this.controller.storeVideo(this.video);
|
||||
});
|
||||
|
||||
test('Should store the picture', function() {
|
||||
assert.isTrue(this.storage.addPicture.calledWith('<poster-memory-blob>'));
|
||||
});
|
||||
|
||||
test('Should store the poster image', function() {
|
||||
var blob = this.storage.addPicture.args[0][0];
|
||||
var options = this.storage.addPicture.args[0][1];
|
||||
|
||||
assert.equal(blob, '<poster-memory-blob>');
|
||||
assert.equal(options.filepath, 'path/to/video.jpg');
|
||||
});
|
||||
|
||||
test('Should emit a `newmedia` app event', function() {
|
||||
assert.isTrue(this.app.emit.calledWith('newmedia', this.video));
|
||||
});
|
||||
|
||||
test('Should switch the <poster-memory-blob> with the <file-blob>', function() {
|
||||
assert.equal(this.video.poster.blob, '<file-blob>');
|
||||
})
|
||||
|
||||
test('Should flag it as `isVideo`', function() {
|
||||
assert.equal(this.video.isVideo, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('StorageController#updateMaxFileSize()', function() {
|
||||
setup(function() {
|
||||
// Reset calls in constructor
|
||||
this.storage.setMaxFileSize.reset();
|
||||
});
|
||||
|
||||
test('Should call `storage.setMaxFileSize` with picture file size estimate', function() {
|
||||
this.settings.pictureSizes.selected.withArgs('data').returns({ width: 400, height: 300 });
|
||||
this.controller.updateMaxFileSize();
|
||||
|
||||
var expected = (400 * 300 / 2) + 25000;
|
||||
var bytes = this.storage.setMaxFileSize.args[0][0];
|
||||
|
||||
assert.equal(this.storage.setMaxFileSize.callCount, 1);
|
||||
assert.equal(bytes, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
360
gaia/apps/camera/test/unit/controllers/viewfinder_test.js
Executable file
360
gaia/apps/camera/test/unit/controllers/viewfinder_test.js
Executable file
|
|
@ -0,0 +1,360 @@
|
|||
'use strict';
|
||||
|
||||
suite('controllers/viewfinder', function() {
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
|
||||
requirejs([
|
||||
'app',
|
||||
'lib/camera/camera',
|
||||
'controllers/viewfinder',
|
||||
'views/viewfinder',
|
||||
'views/focus',
|
||||
'views/faces',
|
||||
'lib/settings',
|
||||
'lib/setting'
|
||||
], function(
|
||||
App, Camera, ViewfinderController, ViewfinderView,
|
||||
FocusView, FacesView, Settings, Setting) {
|
||||
self.ViewfinderController = ViewfinderController.ViewfinderController;
|
||||
self.ViewfinderView = ViewfinderView;
|
||||
self.FocusView = FocusView;
|
||||
self.FacesView = FacesView;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
self.Camera = Camera;
|
||||
self.App = App;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
// App
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.camera = sinon.createStubInstance(this.Camera);
|
||||
this.app.activity = {};
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.grid = sinon.createStubInstance(this.Setting);
|
||||
this.app.views = {
|
||||
viewfinder: sinon.createStubInstance(this.ViewfinderView),
|
||||
focus: sinon.createStubInstance(this.FocusView),
|
||||
faces: sinon.createStubInstance(this.FacesView)
|
||||
};
|
||||
this.app.Pinch = sinon.stub();
|
||||
this.app.Pinch.prototype.on = sinon.stub();
|
||||
|
||||
// Fake elements
|
||||
this.app.views.viewfinder.els = { video: {} };
|
||||
|
||||
// Settings
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.grid = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.viewfinder = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.cameras = sinon.createStubInstance(this.Setting);
|
||||
this.app.settings.zoom = sinon.createStubInstance(this.Setting);
|
||||
|
||||
// Default return values
|
||||
this.app.settings.viewfinder.get.withArgs('scaleType').returns('fill');
|
||||
this.app.settings.grid.selected.withArgs('key').returns('off');
|
||||
|
||||
// Test instance
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
|
||||
// Shortcuts
|
||||
this.viewfinder = this.controller.views.viewfinder;
|
||||
this.focusRing = this.controller.views.focus;
|
||||
this.faces = this.controller.views.faces;
|
||||
this.focus = this.controller.views.focus;
|
||||
this.settings = this.app.settings;
|
||||
this.camera = this.app.camera;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('ViewfinderController()', function() {
|
||||
test('Should stop the stream when the PreviewGallery is opened', function() {
|
||||
assert.isTrue(this.app.on.calledWith('previewgallery:opened',
|
||||
this.controller.onGalleryOpened));
|
||||
});
|
||||
|
||||
test('Should start the stream when the PreviewGallery is closed', function() {
|
||||
assert.isTrue(this.app.on.calledWith('previewgallery:closed',
|
||||
this.controller.onGalleryClosed));
|
||||
});
|
||||
|
||||
test('Should stop the stream preview is false', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:previewactive', this.controller.onPreviewActive));
|
||||
});
|
||||
|
||||
test('Should hide the grid when the settings menu opened', function() {
|
||||
assert.isTrue(this.app.on.calledWith('settings:opened',
|
||||
this.controller.onSettingsOpened));
|
||||
});
|
||||
|
||||
test('Should show the grid again when the settings menu is closed', function() {
|
||||
assert.isTrue(this.app.on.calledWith('settings:closed',
|
||||
this.controller.onSettingsClosed));
|
||||
});
|
||||
|
||||
test('Should flash viewfinder shutter when camera shutter fires', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:shutter', this.viewfinder.shutter));
|
||||
});
|
||||
|
||||
test('Should respond to `zoomchanged` event', function() {
|
||||
assert.isTrue(this.camera.on.calledWith('zoomchanged'));
|
||||
});
|
||||
|
||||
test('Should should set the foucsRing state when focus changes', function() {
|
||||
assert.isTrue(this.app.on.calledWith('camera:focusstatechanged', this.focusRing.setFocusState));
|
||||
});
|
||||
|
||||
test('Should set the scaleType on the view', function() {
|
||||
assert.equal(this.viewfinder.scaleType, 'fill');
|
||||
});
|
||||
|
||||
test('Should set grid on/off', function() {
|
||||
assert.isTrue(this.viewfinder.set.calledWith('grid', 'off'));
|
||||
this.app.settings.grid.selected.withArgs('key').returns('on');
|
||||
this.viewfinder.set.reset();
|
||||
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('grid', 'on'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#createViews()', function() {
|
||||
setup(function() {
|
||||
this.controller.createViews();
|
||||
});
|
||||
|
||||
test('It appends views to the viewfinder views', function() {
|
||||
assert.isTrue(this.focus.appendTo.calledWith(this.viewfinder.el));
|
||||
assert.isTrue(this.faces.appendTo.calledWith(this.viewfinder.el));
|
||||
assert.isTrue(this.viewfinder.appendTo.calledWith(this.app.el));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#show()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(window, 'clearTimeout');
|
||||
this.sandbox.stub(window, 'setTimeout').returns('<timeout-id>');
|
||||
this.app.criticalPathDone = true;
|
||||
});
|
||||
|
||||
test('It fades the viewfinder in straight away if the critical path is incomplete', function() {
|
||||
this.app.criticalPathDone = false;
|
||||
this.controller.show();
|
||||
sinon.assert.called(this.viewfinder.fadeIn);
|
||||
sinon.assert.notCalled(window.setTimeout);
|
||||
});
|
||||
|
||||
test('It fades the viewfinder in after 280ms timeout to avoid flicker', function() {
|
||||
this.controller.show();
|
||||
sinon.assert.calledWith(window.setTimeout, this.viewfinder.fadeIn, 280);
|
||||
});
|
||||
|
||||
test('It clears any existing timeouts to avoid multiple scheduled timeouts', function() {
|
||||
this.controller.show();
|
||||
this.controller.show();
|
||||
sinon.assert.calledWith(window.clearTimeout, '<timeout-id>');
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#hide()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(window, 'setTimeout').returns('<timeout-id>');
|
||||
this.sandbox.stub(window, 'clearTimeout');
|
||||
});
|
||||
|
||||
test('It fades out the viewfinder', function() {
|
||||
this.controller.hide();
|
||||
sinon.assert.called(this.viewfinder.fadeOut);
|
||||
});
|
||||
|
||||
test('It clears any fadeTimeout hanging around', function() {
|
||||
this.controller.show();
|
||||
this.controller.hide();
|
||||
sinon.assert.called(window.clearTimeout, '<timeout-id>');
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#loadStream()', function() {
|
||||
test('Should load preview stream into viewfinder video element', function() {
|
||||
var video = this.viewfinder.els.video;
|
||||
this.controller.loadStream();
|
||||
assert.isTrue(this.camera.loadStreamInto.calledWith(video));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#configurePreview()', function() {
|
||||
setup(function() {
|
||||
this.settings.cameras.selected.withArgs('key').returns('back');
|
||||
this.camera.getSensorAngle.returns(90);
|
||||
this.camera.previewSize.returns({ width: 400, height: 300 });
|
||||
});
|
||||
|
||||
test('Should pass current previewSize', function() {
|
||||
this.controller.configurePreview();
|
||||
var previewSize = this.viewfinder.updatePreview.args[0][0];
|
||||
assert.deepEqual(previewSize, { width: 400, height: 300 });
|
||||
});
|
||||
|
||||
test('Should pass the sensor angle', function() {
|
||||
this.controller.configurePreview();
|
||||
var arg = this.viewfinder.updatePreview.args[0][1];
|
||||
assert.equal(arg, 90);
|
||||
});
|
||||
|
||||
test('Should indicate if selected camera is front', function() {
|
||||
var arg;
|
||||
|
||||
// Back
|
||||
this.controller.configurePreview();
|
||||
arg = this.viewfinder.updatePreview.args[0][2];
|
||||
assert.equal(arg, false);
|
||||
this.viewfinder.updatePreview.reset();
|
||||
|
||||
// Front
|
||||
this.settings.cameras.selected.withArgs('key').returns('front');
|
||||
this.controller.configurePreview();
|
||||
arg = this.viewfinder.updatePreview.args[0][2];
|
||||
assert.equal(arg, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onZoomConfigured()', function() {
|
||||
setup(function() {
|
||||
this.camera.isZoomSupported.returns(true);
|
||||
this.settings.zoom.enabled.returns(true);
|
||||
|
||||
this.camera.getMinimumZoom.returns(0);
|
||||
this.camera.getMaximumZoom.returns(3);
|
||||
});
|
||||
|
||||
test('Should call enableZoom on the viewfinder', function() {
|
||||
this.controller.onZoomConfigured();
|
||||
assert.isTrue(this.viewfinder.enableZoom.calledWith(0, 3));
|
||||
});
|
||||
|
||||
test('Should disable zoom if camera doesn\'t support zoom', function() {
|
||||
this.camera.isZoomSupported.returns(false);
|
||||
|
||||
this.controller.onZoomConfigured();
|
||||
assert.isTrue(this.viewfinder.disableZoom.called);
|
||||
});
|
||||
|
||||
test('Should disable zoom if zoom disabled in settings', function() {
|
||||
this.settings.zoom.enabled.returns(false);
|
||||
|
||||
this.controller.onZoomConfigured();
|
||||
assert.isTrue(this.viewfinder.disableZoom.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onSettingsOpened()', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.controller, 'hideGrid');
|
||||
this.controller.onSettingsOpened();
|
||||
});
|
||||
|
||||
test('Hide grid and hide viewfinder view from the screen reader',
|
||||
function() {
|
||||
sinon.assert.called(this.controller.hideGrid);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('ariaHidden', true));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onSettingsClosed()', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.controller, 'configureGrid');
|
||||
this.controller.onSettingsClosed();
|
||||
});
|
||||
|
||||
test('Configure grid and show viewfinder view to the screen reader',
|
||||
function() {
|
||||
sinon.assert.called(this.controller.configureGrid);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('ariaHidden', false));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onGalleryOpened()', function() {
|
||||
setup(function() {
|
||||
this.controller.onGalleryOpened();
|
||||
});
|
||||
|
||||
test('When gallery is open, the viewfinder view should be disabled and ' +
|
||||
'hidden from screen reader', function() {
|
||||
sinon.assert.called(this.viewfinder.disable);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('ariaHidden', true));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onGalleryClosed()', function() {
|
||||
setup(function() {
|
||||
this.controller.onGalleryClosed();
|
||||
});
|
||||
|
||||
test('When gallery is closed, the viewfinder view should be enabled and ' +
|
||||
'visible to screen reader', function() {
|
||||
sinon.assert.called(this.viewfinder.enable);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('ariaHidden', false));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#onPowerSave', function() {
|
||||
test('When power save enabled, the viewfinder should disable screen ' +
|
||||
'wakelocks', function() {
|
||||
this.controller.onPowerSave(false);
|
||||
assert.isTrue(this.viewfinder.els.video.mozUseScreenWakeLock);
|
||||
});
|
||||
|
||||
test('When power save disabled, the viewfinder should enable screen ' +
|
||||
'wakelocks', function() {
|
||||
this.controller.onPowerSave(true);
|
||||
assert.isFalse(this.viewfinder.els.video.mozUseScreenWakeLock);
|
||||
});
|
||||
});
|
||||
|
||||
suite('click:viewfinder', function() {
|
||||
test('Should set the grid depending on the setting', function() {
|
||||
this.app.settings.grid.selected.withArgs('key').returns('on');
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('grid', 'on'));
|
||||
|
||||
this.app.settings.grid.selected.withArgs('key').returns('off');
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
assert.isTrue(this.viewfinder.set.calledWith('grid', 'off'));
|
||||
});
|
||||
|
||||
test('Should set viewfinder fill depending on the setting', function() {
|
||||
this.app.settings.viewfinder.get.withArgs('scaleType').returns('fill');
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
assert.equal(this.viewfinder.scaleType, 'fill');
|
||||
|
||||
this.app.settings.viewfinder.get.withArgs('scaleType').returns('fit');
|
||||
this.controller = new this.ViewfinderController(this.app);
|
||||
assert.equal(this.viewfinder.scaleType, 'fit');
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#calculateFaceCircle()', function() {
|
||||
test('Draw circle from a square', function() {
|
||||
var circle = this.controller.calculateFaceCircle({top: 50, left: 50, width: 10, height: 10});
|
||||
assert.deepEqual(circle, {x: 50, y: 50, diameter: 10});
|
||||
});
|
||||
|
||||
test('Draw circle from a 2:1 rectangle', function() {
|
||||
var circle = this.controller.calculateFaceCircle({top: 100, left: 50, width: 20, height: 10});
|
||||
assert.deepEqual(circle, {x: 50, y: 95, diameter: 20});
|
||||
});
|
||||
|
||||
test('Draw circle from a 1:2 rectangle', function() {
|
||||
var circle = this.controller.calculateFaceCircle({top: 100, left: 50, width: 10, height: 20});
|
||||
assert.deepEqual(circle, {x: 45, y: 100, diameter: 20});
|
||||
});
|
||||
});
|
||||
});
|
||||
135
gaia/apps/camera/test/unit/controllers/zoom-bar_test.js
Executable file
135
gaia/apps/camera/test/unit/controllers/zoom-bar_test.js
Executable file
|
|
@ -0,0 +1,135 @@
|
|||
'use strict';
|
||||
|
||||
suite('controllers/zoom-bar', function() {
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
|
||||
requirejs([
|
||||
'app',
|
||||
'lib/camera/camera',
|
||||
'lib/settings',
|
||||
'lib/setting',
|
||||
'controllers/zoom-bar',
|
||||
'views/zoom-bar'
|
||||
], function(
|
||||
App, Camera, Settings, Setting, ZoomBarController, ZoomBarView) {
|
||||
self.ZoomBarController = ZoomBarController.ZoomBarController;
|
||||
self.ZoomBarView = ZoomBarView;
|
||||
self.Camera = Camera;
|
||||
self.App = App;
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
// App
|
||||
this.app = sinon.createStubInstance(this.App);
|
||||
this.app.camera = sinon.createStubInstance(this.Camera);
|
||||
this.app.views = {
|
||||
zoombar: sinon.createStubInstance(this.ZoomBarView)
|
||||
};
|
||||
|
||||
// Settings
|
||||
this.app.settings = sinon.createStubInstance(this.Settings);
|
||||
this.app.settings.zoom = sinon.createStubInstance(this.Setting);
|
||||
|
||||
// Test instance
|
||||
this.controller = new this.ZoomBarController(this.app);
|
||||
|
||||
// Shortcuts
|
||||
this.zoombar = this.controller.view;
|
||||
this.camera = this.app.camera;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('ViewfinderController()', function() {
|
||||
test('Should configure zoom when zoomconfigured event is emitted', function() {
|
||||
assert.isTrue(this.camera.on.calledWith('zoomconfigured',
|
||||
this.controller.onZoomConfigured));
|
||||
});
|
||||
|
||||
test('Should set the zoom when zoomchanged event is emitted', function() {
|
||||
assert.isTrue(this.camera.on.calledWith('zoomchanged',
|
||||
this.controller.setZoom));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderController#createViews()', function() {
|
||||
setup(function() {
|
||||
this.controller.createView();
|
||||
});
|
||||
|
||||
test('It appends view', function() {
|
||||
assert.isTrue(this.zoombar.appendTo.calledWith(this.zoombar.el));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ZoomBar#onChange()', function() {
|
||||
setup(function() {
|
||||
this.camera.getMaximumZoom.returns(10);
|
||||
this.camera.getMinimumZoom.returns(1);
|
||||
this.controller.onChange(5);
|
||||
});
|
||||
|
||||
test('It calls setZoom with the correct value', function() {
|
||||
assert.isTrue(this.camera.setZoom.calledWith(1.45));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ZoomBar#onZoomConfigured()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.controller, 'configureZoomBar');
|
||||
sinon.stub(this.controller, 'setZoom');
|
||||
this.controller.onZoomConfigured(5);
|
||||
});
|
||||
|
||||
test('It sets zoom and hides', function() {
|
||||
assert.isTrue(this.controller.configureZoomBar.calledOnce);
|
||||
assert.isTrue(this.controller.setZoom.calledWith(5));
|
||||
assert.isTrue(this.zoombar.hide.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ZoomBar#configureZoomBar()', function() {
|
||||
test('It disables zoom if not supported by camera', function() {
|
||||
this.camera.isZoomSupported.returns(false);
|
||||
this.app.settings.zoom.enabled.returns(true);
|
||||
this.controller.configureZoomBar();
|
||||
assert.isFalse(this.controller.enableZoom);
|
||||
});
|
||||
|
||||
test('It enables zoom if supported by camera', function() {
|
||||
this.camera.isZoomSupported.returns(true);
|
||||
this.app.settings.zoom.enabled.returns(true);
|
||||
this.controller.configureZoomBar();
|
||||
assert.isTrue(this.controller.enableZoom);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ZoomBar#setZoom()', function() {
|
||||
setup(function() {
|
||||
this.camera.getMaximumZoom.returns(11);
|
||||
this.camera.getMinimumZoom.returns(1);
|
||||
});
|
||||
|
||||
test('It sets the zoom with the correct value if enabled', function() {
|
||||
this.controller.enableZoom = true;
|
||||
this.controller.setZoom(4);
|
||||
assert.isTrue(this.zoombar.setValue.calledWith(30));
|
||||
});
|
||||
|
||||
test('It does not set the zoom if disabled', function() {
|
||||
this.controller.enableZoom = false;
|
||||
this.controller.setZoom(4);
|
||||
assert.isFalse(this.zoombar.setValue.calledWith(30));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
658
gaia/apps/camera/test/unit/lib/camera-coordinates_test.js
Executable file
658
gaia/apps/camera/test/unit/lib/camera-coordinates_test.js
Executable file
|
|
@ -0,0 +1,658 @@
|
|||
suite('lib/camera-coordinates', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['lib/camera-coordinates'], function(cameraCoordinates) {
|
||||
// alias
|
||||
self.faceToCamera = cameraCoordinates.faceToCamera;
|
||||
self.faceToPixels = cameraCoordinates.faceToPixels;
|
||||
self.areaToCamera = cameraCoordinates.private.areaToCamera;
|
||||
self.areaToPixels = cameraCoordinates.private.areaToPixels;
|
||||
self.mirrorAreaCamera = cameraCoordinates.private.mirrorAreaCamera;
|
||||
self.mirrorAreaPixels = cameraCoordinates.private.mirrorAreaPixels;
|
||||
self.rotateArea = cameraCoordinates.private.rotateArea;
|
||||
self.sizeToCamera = cameraCoordinates.private.sizeToCamera;
|
||||
self.sizeToPixels = cameraCoordinates.private.sizeToPixels;
|
||||
self.toCamera = cameraCoordinates.private.toCamera;
|
||||
self.toPixels = cameraCoordinates.private.toPixels;
|
||||
self.rotatePoint = cameraCoordinates.private.rotatePoint;
|
||||
|
||||
self.equalAreas = function(area1, area2) {
|
||||
var equal = area1.top === area2.top &&
|
||||
area1.left === area2.left &&
|
||||
area1.bottom === area2.bottom &&
|
||||
area1.right === area2.right &&
|
||||
area1.width === area2.width &&
|
||||
area1.height === area2.height;
|
||||
if (!equal) {
|
||||
console.log('Areas are not equal: ' +
|
||||
JSON.stringify(area1) + ' /= ' + JSON.stringify(area2));
|
||||
}
|
||||
return equal;
|
||||
};
|
||||
done();
|
||||
});
|
||||
this.viewportWidth = 800;
|
||||
this.viewportHeight = 600;
|
||||
});
|
||||
|
||||
suite('#faceToCamera', function() {
|
||||
suite('The face should be converted from pixel coordinates to camera and back to pixels', function() {
|
||||
|
||||
test('face is at the center of the viewport', function() {
|
||||
var face = {
|
||||
top: 200,
|
||||
left: 300,
|
||||
bottom: 400,
|
||||
right: 600,
|
||||
height: 200,
|
||||
width: 300
|
||||
};
|
||||
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, facePixelCooordinates));
|
||||
});
|
||||
|
||||
test('face is at the top left quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 230,
|
||||
right: 230,
|
||||
height: 230,
|
||||
width: 230
|
||||
};
|
||||
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, facePixelCooordinates));
|
||||
});
|
||||
|
||||
test('face is at the top right quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 50,
|
||||
left: 500,
|
||||
bottom: 80,
|
||||
right: 530,
|
||||
height: 30,
|
||||
width: 30
|
||||
};
|
||||
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, facePixelCooordinates));
|
||||
});
|
||||
|
||||
test('face is at the bottom left quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 400,
|
||||
left: 200,
|
||||
bottom: 475,
|
||||
right: 275,
|
||||
height: 75,
|
||||
width: 75
|
||||
};
|
||||
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, facePixelCooordinates));
|
||||
});
|
||||
|
||||
test('face is at the bottom right quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 400,
|
||||
left: 500,
|
||||
bottom: 500,
|
||||
right: 600,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, facePixelCooordinates));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#faceToPixels', function() {
|
||||
suite('The face should be converted from camera to pixel coordinates and back to camera coordinates', function() {
|
||||
|
||||
test('face is at the center of the viewport', function() {
|
||||
// var face = {
|
||||
// top: -25,
|
||||
// left: -25,
|
||||
// bottom: 25,
|
||||
// right: 25,
|
||||
// height: 50,
|
||||
// width: 50
|
||||
// };
|
||||
var face = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, faceCameraCoordinates));
|
||||
});
|
||||
|
||||
test('face is at the top left quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: -1000,
|
||||
left: -1000,
|
||||
bottom: -950,
|
||||
right: -950,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, faceCameraCoordinates));
|
||||
});
|
||||
|
||||
test('face is at the top right quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: -500,
|
||||
left: 500,
|
||||
bottom: -450,
|
||||
right: 550,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, faceCameraCoordinates));
|
||||
});
|
||||
|
||||
test('face is at the bottom left quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 500,
|
||||
left: -500,
|
||||
bottom: 550,
|
||||
right: -450,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, faceCameraCoordinates));
|
||||
});
|
||||
|
||||
test('face is at the bottom right quadrant of the viewport', function() {
|
||||
var face = {
|
||||
top: 500,
|
||||
left: 500,
|
||||
bottom: 550,
|
||||
right: 550,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
|
||||
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
|
||||
assert.ok(this.equalAreas(face, faceCameraCoordinates));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#areaToCamera', function() {
|
||||
suite('The area should be converted from pixel coordinates to camera and back to pixels', function() {
|
||||
|
||||
test('area is at the center of the viewport', function() {
|
||||
var area = {
|
||||
top: 200,
|
||||
left: 300,
|
||||
bottom: 400,
|
||||
right: 600,
|
||||
height: 200,
|
||||
width: 300
|
||||
};
|
||||
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaPixels));
|
||||
});
|
||||
|
||||
test('area is at the top left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 230,
|
||||
right: 230,
|
||||
height: 230,
|
||||
width: 230
|
||||
};
|
||||
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaPixels));
|
||||
});
|
||||
|
||||
test('area is at the top right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 50,
|
||||
left: 500,
|
||||
bottom: 80,
|
||||
right: 530,
|
||||
height: 30,
|
||||
width: 30
|
||||
};
|
||||
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaPixels));
|
||||
});
|
||||
|
||||
test('area is at the bottom left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 400,
|
||||
left: 200,
|
||||
bottom: 475,
|
||||
right: 275,
|
||||
height: 75,
|
||||
width: 75
|
||||
};
|
||||
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaPixels));
|
||||
});
|
||||
|
||||
test('area is at the bottom right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 400,
|
||||
left: 500,
|
||||
bottom: 500,
|
||||
right: 600,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaPixels));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#areaToPixels', function() {
|
||||
suite('The area should be converted from camera to pixel coordinates and back to camera coordinates', function() {
|
||||
test('area is at the center of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaCamera));
|
||||
});
|
||||
|
||||
test('area is at the top left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaCamera));
|
||||
});
|
||||
|
||||
test('area is at the top right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaCamera));
|
||||
});
|
||||
|
||||
test('area is at the bottom left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaCamera));
|
||||
});
|
||||
|
||||
test('area is at the bottom right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
|
||||
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(this.equalAreas(area, areaCamera));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#mirrorAreaCamera', function() {
|
||||
suite('The area should be properly mirrored', function() {
|
||||
test('area is at the center of the viewport', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaCamera(area);
|
||||
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
|
||||
test('area is at the top left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -100,
|
||||
left: -100,
|
||||
bottom: -50,
|
||||
right: -50,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaCamera(area);
|
||||
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
|
||||
test('area is at the top right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: -100,
|
||||
left: 300,
|
||||
bottom: -50,
|
||||
right: 350,
|
||||
height: 50,
|
||||
width: 50
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaCamera(area);
|
||||
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
|
||||
test('area is at the bottom left quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 100,
|
||||
left: -200,
|
||||
bottom: 200,
|
||||
right: -100,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaCamera(area);
|
||||
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
|
||||
test('area is at the bottom right quadrant of the viewport', function() {
|
||||
var area = {
|
||||
top: 800,
|
||||
left: 800,
|
||||
bottom: 900,
|
||||
right: 900,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaCamera(area);
|
||||
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#mirrorAreaPixels', function() {
|
||||
suite('The area should be properly mirrored', function() {
|
||||
test('area is at the center of the viewport', function() {
|
||||
var area = {
|
||||
top: 100,
|
||||
left: 300,
|
||||
bottom: 200,
|
||||
right: 400,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var mirroredArea = this.mirrorAreaPixels(area, this.viewportWidth);
|
||||
var doubledMirrorArea = this.mirrorAreaPixels(mirroredArea, this.viewportWidth);
|
||||
assert.ok(this.equalAreas(area, doubledMirrorArea));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('#rotateArea', function() {
|
||||
suite('The area should be properly rotated', function() {
|
||||
test('area rotates 0 degrees', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var expectedResult = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var rotatedArea = this.rotateArea(area, 0);
|
||||
assert.ok(this.equalAreas(rotatedArea, expectedResult));
|
||||
});
|
||||
|
||||
test('area rotates 90 degrees', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var expectedResult = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var rotatedArea = this.rotateArea(area, 90);
|
||||
assert.ok(this.equalAreas(rotatedArea, expectedResult));
|
||||
});
|
||||
|
||||
test('area rotates 180 degrees', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var expectedResult = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var rotatedArea = this.rotateArea(area, 180);
|
||||
assert.ok(this.equalAreas(rotatedArea, expectedResult));
|
||||
});
|
||||
|
||||
test('area rotates 270 degrees', function() {
|
||||
var area = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var expectedResult = {
|
||||
top: -50,
|
||||
left: -50,
|
||||
bottom: 50,
|
||||
right: 50,
|
||||
height: 100,
|
||||
width: 100
|
||||
};
|
||||
var rotatedArea = this.rotateArea(area, 270);
|
||||
assert.ok(this.equalAreas(rotatedArea, expectedResult));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('#sizeToCamera', function() {
|
||||
test('The size should be converted from pixels to camera coordinates and back to pixels', function() {
|
||||
var width = 50;
|
||||
var height = 100;
|
||||
var sizeCamera = this.sizeToCamera(width, height, this.viewportWidth, this.viewportHeight);
|
||||
var sizePixels = this.sizeToPixels(sizeCamera.width, sizeCamera.height, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(sizePixels.width === width);
|
||||
assert.ok(sizePixels.height === height);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#sizeToPixels', function() {
|
||||
test('The size should be converted from camera coordinates to pixels and back to camera', function() {
|
||||
var width = 50;
|
||||
var height = 100;
|
||||
var sizePixels = this.sizeToPixels(width, height, this.viewportWidth, this.viewportHeight);
|
||||
var sizeCamera = this.sizeToCamera(sizePixels.width, sizePixels.height, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(sizeCamera.width === width);
|
||||
assert.ok(sizeCamera.height === height);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#toCamera', function() {
|
||||
test('The coordinate should be transformed from pixels to camera coordinates', function() {
|
||||
var x = 50;
|
||||
var y = 100;
|
||||
var camera = this.toCamera(x, y, this.viewportWidth, this.viewportHeight);
|
||||
var pixels = this.toPixels(camera.x, camera.y, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(pixels.x === x);
|
||||
assert.ok(pixels.y === y);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#toPixels', function() {
|
||||
test('The coordinate should be transformed from camera coordinates to pixels', function() {
|
||||
var x = 50;
|
||||
var y = 100;
|
||||
var pixels = this.toPixels(x, y, this.viewportWidth, this.viewportHeight);
|
||||
var camera = this.toCamera(pixels.x, pixels.y, this.viewportWidth, this.viewportHeight);
|
||||
assert.ok(camera.x === x);
|
||||
assert.ok(camera.y === y);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#rotatePoint', function() {
|
||||
test('The point coordinates should rotate 0 degrees', function() {
|
||||
var x = 50;
|
||||
var y = 100;
|
||||
var point = this.rotatePoint(x, y, 0);
|
||||
assert.ok(x === point.x);
|
||||
assert.ok(y === point.y);
|
||||
});
|
||||
|
||||
test('The point coordinates should rotate 90 degrees', function() {
|
||||
var x = 760;
|
||||
var y = 134;
|
||||
var point = this.rotatePoint(x, y, 90);
|
||||
assert.ok(x === point.y);
|
||||
assert.ok(y === -point.x);
|
||||
});
|
||||
|
||||
test('The point coordinates should rotate 180 degrees', function() {
|
||||
var x = 34;
|
||||
var y = 100;
|
||||
var point = this.rotatePoint(x, y, 180);
|
||||
assert.ok(x === -point.x);
|
||||
assert.ok(y === -point.y);
|
||||
});
|
||||
|
||||
test('The point coordinates should rotate 270 degrees', function() {
|
||||
var x = 200;
|
||||
var y = 200;
|
||||
var point = this.rotatePoint(x, y, 270);
|
||||
assert.ok(x === -point.y);
|
||||
assert.ok(y === point.x);
|
||||
});
|
||||
|
||||
test('Angle should be clamped to 0 degrees', function() {
|
||||
var x = 0;
|
||||
var y = 240;
|
||||
var point1 = this.rotatePoint(x, y, 0);
|
||||
var point2 = this.rotatePoint(x, y, -20);
|
||||
assert.ok(point1.x === point2.x);
|
||||
assert.ok(point1.y === point2.y);
|
||||
});
|
||||
|
||||
test('Angle should be clamped to 90 degrees', function() {
|
||||
var x = 123;
|
||||
var y = -321;
|
||||
var point1 = this.rotatePoint(x, y, 90);
|
||||
var point2 = this.rotatePoint(x, y, -260);
|
||||
assert.ok(point1.x === point2.x);
|
||||
assert.ok(point1.y === point2.y);
|
||||
});
|
||||
|
||||
test('Angle should be clamped to 90 degrees', function() {
|
||||
var x = 123;
|
||||
var y = 321;
|
||||
var point1 = this.rotatePoint(x, y, 90);
|
||||
var point2 = this.rotatePoint(x, y, 123);
|
||||
assert.ok(point1.x === point2.x);
|
||||
assert.ok(point1.y === point2.y);
|
||||
});
|
||||
|
||||
test('Angle should be clamped to 180 degrees', function() {
|
||||
var x = -100;
|
||||
var y = 100;
|
||||
var point1 = this.rotatePoint(x, y, 180);
|
||||
var point2 = this.rotatePoint(x, y, 140);
|
||||
assert.ok(point1.x === point2.x);
|
||||
assert.ok(point1.y === point2.y);
|
||||
});
|
||||
|
||||
test('Angle should be clamped to 270 degrees', function() {
|
||||
var x = -500;
|
||||
var y = -500;
|
||||
var point1 = this.rotatePoint(x, y, 230);
|
||||
var point2 = this.rotatePoint(x, y, 270);
|
||||
assert.ok(point1.x === point2.x);
|
||||
assert.ok(point1.y === point2.y);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
235
gaia/apps/camera/test/unit/lib/camera-utils_test.js
Executable file
235
gaia/apps/camera/test/unit/lib/camera-utils_test.js
Executable file
|
|
@ -0,0 +1,235 @@
|
|||
/*jshint maxlen:false*/
|
||||
/*global req*/
|
||||
'use strict';
|
||||
|
||||
suite('utils/camera-utils', function() {
|
||||
var CameraUtils;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
|
||||
this.modules = {};
|
||||
|
||||
requirejs([
|
||||
'lib/camera-utils'
|
||||
], function(_CameraUtils) {
|
||||
CameraUtils = self.modules.CameraUtils = _CameraUtils;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
suite('scaleSizeToFitViewport', function() {
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
test('Should scale image size to *FIT* viewport ' +
|
||||
'preserving aspect ratio', function() {
|
||||
var viewportSize, imageSize, outputSize;
|
||||
|
||||
// Case where image size exactly matches viewport size
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has larger width, smaller height than viewport
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 800,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1152);
|
||||
|
||||
// Case where image size has larger height, smaller width than viewport
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1440
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 640);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has larger width, larger height than viewport
|
||||
// (different aspect ratio)
|
||||
viewportSize = {
|
||||
width: 600,
|
||||
height: 800
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1152
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 500);
|
||||
assert.equal(outputSize.height, 800);
|
||||
|
||||
// Case where image size has larger width, larger height than viewport
|
||||
// (same aspect ratio)
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 1080,
|
||||
height: 1920
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has smaller width, smaller height than viewport
|
||||
// (different aspect ratio)
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1152
|
||||
};
|
||||
imageSize = {
|
||||
width: 600,
|
||||
height: 800
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 960);
|
||||
|
||||
// Case where image size has smaller width, smaller height than viewport
|
||||
// (same aspect ratio)
|
||||
viewportSize = {
|
||||
width: 1080,
|
||||
height: 1920
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 1080);
|
||||
assert.equal(outputSize.height, 1920);
|
||||
});
|
||||
});
|
||||
|
||||
suite('scaleSizeToFillViewport', function() {
|
||||
setup(function() {});
|
||||
|
||||
test('Should scale image size to *FILL* viewport ' +
|
||||
'preserving aspect ratio', function() {
|
||||
var viewportSize, imageSize, outputSize;
|
||||
|
||||
// Case where image size exactly matches viewport size
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has larger width, smaller height than viewport
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 800,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 800);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has larger height, smaller width than viewport
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1440
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1440);
|
||||
|
||||
// Case where image size has larger width, larger height than viewport
|
||||
// (different aspect ratio)
|
||||
viewportSize = {
|
||||
width: 600,
|
||||
height: 800
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1152
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 600);
|
||||
assert.equal(outputSize.height, 960);
|
||||
|
||||
// Case where image size has larger width, larger height than viewport
|
||||
// (same aspect ratio)
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
imageSize = {
|
||||
width: 1080,
|
||||
height: 1920
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 720);
|
||||
assert.equal(outputSize.height, 1280);
|
||||
|
||||
// Case where image size has smaller width, smaller height than viewport
|
||||
// (different aspect ratio)
|
||||
viewportSize = {
|
||||
width: 720,
|
||||
height: 1152
|
||||
};
|
||||
imageSize = {
|
||||
width: 600,
|
||||
height: 800
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 864);
|
||||
assert.equal(outputSize.height, 1152);
|
||||
|
||||
// Case where image size has smaller width, smaller height than viewport
|
||||
// (same aspect ratio)
|
||||
viewportSize = {
|
||||
width: 1080,
|
||||
height: 1920
|
||||
};
|
||||
imageSize = {
|
||||
width: 720,
|
||||
height: 1280
|
||||
};
|
||||
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
|
||||
assert.equal(outputSize.width, 1080);
|
||||
assert.equal(outputSize.height, 1920);
|
||||
});
|
||||
});
|
||||
});
|
||||
1493
gaia/apps/camera/test/unit/lib/camera/camera_test.js
Executable file
1493
gaia/apps/camera/test/unit/lib/camera/camera_test.js
Executable file
File diff suppressed because it is too large
Load diff
677
gaia/apps/camera/test/unit/lib/camera/focus_test.js
Executable file
677
gaia/apps/camera/test/unit/lib/camera/focus_test.js
Executable file
|
|
@ -0,0 +1,677 @@
|
|||
suite('lib/camera/focus', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['lib/camera/focus'], function(Focus) {
|
||||
self.Focus = Focus;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
// Fake mozCamera
|
||||
this.mozCamera = {
|
||||
_listeners: {},
|
||||
capabilities: {
|
||||
focusModes : ["auto", "infinity", "normal",
|
||||
"macro", "continuous-picture", "continuous-video" ],
|
||||
},
|
||||
autoFocus: sinon.stub().returns({
|
||||
then: function(onSuccess, onError) {
|
||||
onSuccess(true);
|
||||
}
|
||||
}),
|
||||
setFocusAreas: sinon.stub(),
|
||||
setMeteringAreas: sinon.stub(),
|
||||
stopContinuousFocus: sinon.stub(),
|
||||
resumeContinuousFocus: sinon.stub(),
|
||||
startFaceDetection: sinon.spy(),
|
||||
stopFaceDetection: sinon.spy(),
|
||||
addEventListener: function(eventname, listener) {
|
||||
this._listeners[eventname] = listener;
|
||||
},
|
||||
removeEventListener: function(eventname, listener) {
|
||||
if (this._listeners.hasOwnProperty(eventname)) {
|
||||
if (this._listeners[eventname] === listener) {
|
||||
delete this._listeners[eventname];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.focus = new this.Focus({});
|
||||
this.focus.mozCamera = this.mozCamera;
|
||||
});
|
||||
|
||||
suite('Focus#configure()', function() {
|
||||
setup(function() {
|
||||
this.focus.configureFocusModes = sinon.spy();
|
||||
});
|
||||
|
||||
test('mozCamera should be assigned \'auto\' mode by default', function() {
|
||||
this.focus.configure(this.mozCamera);
|
||||
assert.ok(this.focus.configureFocusModes.called);
|
||||
assert.equal(this.mozCamera.focusMode, 'auto');
|
||||
});
|
||||
|
||||
test('user preferences override defaults', function() {
|
||||
this.focus.continuousAutoFocus = false;
|
||||
this.focus.configure(this.mozCamera, 'continuous-picture');
|
||||
assert.ok(this.focus.configureFocusModes.called);
|
||||
assert.equal(this.mozCamera.focusMode, 'auto');
|
||||
});
|
||||
|
||||
test('continuous-picture selected on picture mode if user pref is set to CAF', function() {
|
||||
this.focus.continuousAutoFocus = true;
|
||||
this.focus.configure(this.mozCamera, 'picture');
|
||||
assert.ok(this.focus.configureFocusModes.called);
|
||||
assert.equal(this.mozCamera.focusMode, 'continuous-picture');
|
||||
});
|
||||
|
||||
test('continuous-video selected on video mode if user pref is set to CAF', function() {
|
||||
this.focus.continuousAutoFocus = true;
|
||||
this.focus.configure(this.mozCamera, 'video');
|
||||
assert.ok(this.focus.configureFocusModes.called);
|
||||
assert.equal(this.mozCamera.focusMode, 'continuous-video');
|
||||
});
|
||||
|
||||
test('if not valid focus mode is passed the first available is picked', function() {
|
||||
this.focus.configure(this.mozCamera, 'invalid-focus-mode');
|
||||
assert.ok(this.focus.configureFocusModes.called);
|
||||
assert.equal(this.mozCamera.focusMode, 'auto');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#getMode', function() {
|
||||
setup(function() {
|
||||
this.mozCamera.focusMode = 'auto';
|
||||
});
|
||||
|
||||
test('it returns the current focus mode', function() {
|
||||
this.focus.startFaceDetection();
|
||||
assert.ok(this.focus.getMode() === 'auto');
|
||||
});
|
||||
|
||||
test('it returns the suspended focus mode', function() {
|
||||
this.mozCamera.focusMode = 'continuous-picture';
|
||||
assert.ok(this.focus.getMode() === 'continuous-picture');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#configureFocusModes()', function() {
|
||||
setup(function() {
|
||||
var userPreferences = {
|
||||
touchFocus: true,
|
||||
faceDetection: false
|
||||
};
|
||||
|
||||
this.focus = new this.Focus(userPreferences);
|
||||
this.focus.mozCamera = this.mozCamera;
|
||||
this.sandbox.stub(this.focus, 'isTouchFocusSupported');
|
||||
this.sandbox.stub(this.focus, 'isFaceDetectionSupported');
|
||||
this.sandbox.stub(this.focus, 'startFaceDetection');
|
||||
this.focus.isTouchFocusSupported.returns(true);
|
||||
this.focus.isFaceDetectionSupported.returns(true);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('continous autofocus is enabled by default if supported', function() {
|
||||
this.focus.configureFocusModes();
|
||||
assert.equal(this.focus.continuousAutoFocus, true);
|
||||
});
|
||||
|
||||
test('continous autofocus is disabled if user preferences desable it', function() {
|
||||
this.focus.userPreferences.continuousAutoFocus = false;
|
||||
this.focus.configureFocusModes();
|
||||
assert.equal(this.focus.continuousAutoFocus, false);
|
||||
});
|
||||
|
||||
test('touch to focus is enabled by default if available', function() {
|
||||
this.focus.configureFocusModes();
|
||||
assert.equal(this.focus.touchFocus, true);
|
||||
});
|
||||
|
||||
test('touch to focus is disabled if user preferences disable it', function() {
|
||||
this.focus.userPreferences.touchFocus = false;
|
||||
this.focus.configureFocusModes();
|
||||
assert.equal(this.focus.touchFocus, false);
|
||||
});
|
||||
|
||||
test('touch to focus is disabled if hardware doesn\'t support it', function() {
|
||||
this.focus.isTouchFocusSupported.returns(false);
|
||||
this.focus.configureFocusModes();
|
||||
assert.equal(this.focus.touchFocus, false);
|
||||
});
|
||||
|
||||
test('touch to focus disabled if hardware doesn\'t support it and even if user preferences enable it', function() {
|
||||
this.focus.userPreferences.touchFocus = true;
|
||||
this.focus.isTouchFocusSupported.returns(false);
|
||||
this.focus.configureFocusModes('picture');
|
||||
assert.equal(this.focus.touchFocus, false);
|
||||
});
|
||||
|
||||
test('face detection is disabled if not supported by HW', function() {
|
||||
this.focus.userPreferences.faceDetection = true;
|
||||
this.focus.isFaceDetectionSupported.returns(false);
|
||||
this.focus.configureFocusModes('picture');
|
||||
assert.ok(!this.focus.faceDetection);
|
||||
});
|
||||
|
||||
test('face detection is disabled if disabled in user preferences', function() {
|
||||
this.focus.userPreferences.faceDetection = false;
|
||||
this.focus.isFaceDetectionSupported.returns(true);
|
||||
this.focus.configureFocusModes('picture');
|
||||
assert.ok(!this.focus.faceDetection);
|
||||
});
|
||||
|
||||
test('face detection is disabled in video mode', function() {
|
||||
this.focus.userPreferences.faceDetection = true;
|
||||
this.focus.isFaceDetectionSupported.returns(true);
|
||||
this.focus.configureFocusModes('video');
|
||||
assert.ok(!this.focus.faceDetection);
|
||||
});
|
||||
|
||||
test('face detection is enabled in picture mode if supported by HW and enabled in user preferences', function() {
|
||||
this.focus.userPreferences.faceDetection = true;
|
||||
this.focus.isFaceDetectionSupported.returns(true);
|
||||
this.focus.configureFocusModes('picture');
|
||||
assert.ok(this.focus.faceDetection);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#startFaceDetection', function() {
|
||||
setup(function() {
|
||||
this.focus.faceDetection = true;
|
||||
});
|
||||
|
||||
test('mozCamera startFaceDetection is called to start face detection if face detection enabled', function() {
|
||||
this.focus.startFaceDetection();
|
||||
assert.ok(this.mozCamera.startFaceDetection.called);
|
||||
});
|
||||
|
||||
test('mozCamera startFaceDetection is not called to start face detection if face detection disabled', function() {
|
||||
this.focus.faceDetection = false;
|
||||
this.focus.startFaceDetection();
|
||||
assert.ok(!this.mozCamera.startFaceDetection.called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#clearFaceDetection', function() {
|
||||
setup(function() {
|
||||
this.focus.faceDetection = true;
|
||||
this.sandbox.spy(this.focus, 'focusOnLargestFace');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('focusOnLargestFace is called to start face detection if face detection enabled', function() {
|
||||
this.focus.clearFaceDetection();
|
||||
assert.ok(this.focus.focusOnLargestFace.calledWith([]));
|
||||
});
|
||||
|
||||
test('focusOnLargestFace is called with empty list when clearing faces', function() {
|
||||
this.focus.faceDetection = false;
|
||||
this.focus.clearFaceDetection();
|
||||
assert.ok(this.focus.focusOnLargestFace.calledWith([]));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#stopFaceDetection', function() {
|
||||
setup(function() {
|
||||
this.focus.faceDetection = true;
|
||||
this.focus.mozCamera = this.mozCamera;
|
||||
this.sandbox.spy(this.focus, 'stopFaceDetection');
|
||||
this.sandbox.spy(this.focus, 'clearFaceDetection');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('mozCamera stopFaceDetection is called to stop face detection', function() {
|
||||
this.focus.stopFaceDetection();
|
||||
assert.ok(this.mozCamera.stopFaceDetection.called);
|
||||
});
|
||||
|
||||
test('mozCamera stopFaceDetection is not called if face detection is not available', function() {
|
||||
this.mozCamera.stopFaceDetection = false;
|
||||
this.focus.stopFaceDetection();
|
||||
assert.ok(!this.mozCamera.stopFaceDetection.called);
|
||||
assert.ok(this.focus.clearFaceDetection.called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#suspendFaceDetection', function() {
|
||||
setup(function() {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
this.focus.faceDetection = true;
|
||||
this.focus.faceDetectionSuspended = undefined;
|
||||
this.focus.faceFocused = true;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.clock.restore();
|
||||
});
|
||||
|
||||
test('mozCamera suspendFaceDetection resets timers', function() {
|
||||
this.focus.suspendFaceDetection(500, 300);
|
||||
this.clock.tick(500);
|
||||
assert.ok(this.focus.faceDetectionSuspended);
|
||||
this.clock.tick(300);
|
||||
assert.ok(!this.focus.faceFocused);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#resumeContinuousFocus', function() {
|
||||
setup(function() {
|
||||
this.focus.mozCamera = this.mozCamera;
|
||||
this.focus.previousMode = 'auto';
|
||||
this.mozCamera.focusMode = 'continuous-picture';
|
||||
});
|
||||
|
||||
test('mozCamera resumeContinuousFocus is called', function() {
|
||||
this.focus.resumeContinuousFocus();
|
||||
assert.ok(!this.focus.suspendedMode);
|
||||
assert.ok(this.focus.mozCamera.resumeContinuousFocus.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#onfocus', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(this.focus, 'updateFocusState');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('should call updateFocusState', function() {
|
||||
this.focus.configureFocusModes();
|
||||
var listener = this.mozCamera._listeners['focus'];
|
||||
assert.ok(listener);
|
||||
listener({ newState: 'focusing' });
|
||||
assert.ok(this.focus.updateFocusState.calledWith('focusing'));
|
||||
listener({ newState: 'focused' });
|
||||
assert.ok(this.focus.updateFocusState.calledWith('focused'));
|
||||
listener({ newState: 'unfocused' });
|
||||
assert.ok(this.focus.updateFocusState.calledWith('fail'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#updateFocusState', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(this.focus, 'onAutoFocusChanged');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('should call onAutoFocusChanged', function() {
|
||||
this.focus.focusState = 'focused';
|
||||
this.focus.updateFocusState('focusing');
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
this.focus.updateFocusState('focused');
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focused'));
|
||||
this.focus.updateFocusState('focusing');
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
this.focus.updateFocusState('fail');
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('fail'));
|
||||
});
|
||||
|
||||
test('should not call onAutoFocusChanged', function() {
|
||||
this.focus.focusState = 'focused';
|
||||
this.focus.updateFocusState('focused');
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
this.focus.updateFocusState('fail');
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
this.focus.focusState = 'focusing';
|
||||
this.focus.updateFocusState('focusing');
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#suspendContinuousFocus', function() {
|
||||
setup(function() {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
this.focus.mozCamera = this.mozCamera;
|
||||
this.sandbox.spy(this.focus, 'resumeContinuousFocus');
|
||||
this.focus.continuousModeTimer = undefined;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
this.clock.restore();
|
||||
});
|
||||
|
||||
test('mozCamera resumeContinuousFocus is called', function() {
|
||||
this.focus.suspendContinuousFocus(500);
|
||||
assert.ok(this.focus.continuousModeTimer !== undefined);
|
||||
this.clock.tick(500);
|
||||
assert.ok(this.focus.resumeContinuousFocus.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#focusOnLargestFace', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(this.focus, 'onFacesDetected');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('it should not focus on any face if face detection is suspended', function() {
|
||||
this.focus.touchFocus = true;
|
||||
this.focus.faceDetectionSuspended = true;
|
||||
this.focus.focusOnLargestFace([{
|
||||
id: 3,
|
||||
score: 80,
|
||||
bounds: {
|
||||
height: 300,
|
||||
width: 300
|
||||
}
|
||||
}]);
|
||||
assert.ok(this.focus.onFacesDetected.calledWith([]));
|
||||
});
|
||||
|
||||
test('it should focus on the singleton face', function() {
|
||||
this.focus.touchFocus = true;
|
||||
this.focus.faceDetectionSuspended = false;
|
||||
var face = {
|
||||
id: 3,
|
||||
score: 80,
|
||||
bounds: {
|
||||
height: 300,
|
||||
width: 300
|
||||
}
|
||||
};
|
||||
this.focus.focusOnLargestFace([face]);
|
||||
assert.ok(
|
||||
Object.is(this.focus.onFacesDetected.firstCall.args[0][0], face)
|
||||
);
|
||||
});
|
||||
|
||||
test('it should focus on the singleton face (event)', function() {
|
||||
this.focus.touchFocus = true;
|
||||
this.focus.faceDetectionSuspended = false;
|
||||
var face = {
|
||||
id: 3,
|
||||
score: 80,
|
||||
bounds: {
|
||||
height: 300,
|
||||
width: 300
|
||||
}
|
||||
};
|
||||
var event = { faces: [face] };
|
||||
this.focus.handleFaceDetectionEvent(event);
|
||||
assert.ok(
|
||||
Object.is(this.focus.onFacesDetected.firstCall.args[0][0], face)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#focus()', function() {
|
||||
setup(function() {
|
||||
this.focus.focusMode = 'auto';
|
||||
this.focus.onAutoFocusChanged = sinon.spy();
|
||||
});
|
||||
|
||||
test('mozCamera autoFocus is called if focus mode is auto', function() {
|
||||
this.mozCamera.focusMode = 'auto';
|
||||
this.focus.focus(function() {});
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
assert.ok(this.mozCamera.autoFocus.called);
|
||||
});
|
||||
|
||||
test('mozCamera autoFocus is called if focus mode is continuous-picture', function() {
|
||||
this.mozCamera.focusMode = 'continuous-picture';
|
||||
this.focus.focus(function() {});
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
assert.ok(this.mozCamera.autoFocus.called);
|
||||
});
|
||||
|
||||
test('mozCamera autoFocus is not called if focus mode is neither auto nor continuous-picture', function() {
|
||||
this.mozCamera.focusMode = 'infinity';
|
||||
this.focus.focus(function() {});
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
assert.ok(!this.mozCamera.autoFocus.called);
|
||||
});
|
||||
|
||||
test('mozCamera autoFocus callback is called with error and focus state switches to false', function() {
|
||||
var onFocused = sinon.spy();
|
||||
this.mozCamera.autoFocus = sinon.stub();
|
||||
this.mozCamera.focusMode = 'auto';
|
||||
this.mozCamera.autoFocus.returns({
|
||||
then: function(resolve, reject) {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
this.focus.focus(onFocused);
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
assert.ok(this.mozCamera.autoFocus.called);
|
||||
assert.ok(onFocused.calledWith('failed'));
|
||||
assert.ok(!this.focus.focused);
|
||||
});
|
||||
|
||||
test('mozCamera autoFocus callback is called with success and focus state switches to true', function() {
|
||||
var onFocused = sinon.spy();
|
||||
this.mozCamera.autoFocus = sinon.stub();
|
||||
this.mozCamera.focusMode = 'auto';
|
||||
this.mozCamera.autoFocus.returns({
|
||||
then: function(resolve, reject) {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
this.focus.focus(onFocused);
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
assert.ok(this.mozCamera.autoFocus.called);
|
||||
assert.ok(onFocused.calledWith('focused'));
|
||||
assert.ok(this.focus.focused);
|
||||
});
|
||||
|
||||
test('with focus mode different than auto or continuous-picture callback is called with no arguments and focus state doesn\'t change', function(done) {
|
||||
var onFocused = function(result) {
|
||||
assert.equal(result, undefined);
|
||||
done();
|
||||
};
|
||||
var previousFocusState;
|
||||
this.focus.focused = true;
|
||||
previousFocusState = this.focus.focused;
|
||||
this.mozCamera.autoFocus = sinon.stub();
|
||||
this.mozCamera.focusMode = 'infinity';
|
||||
this.focus.focus(onFocused);
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
assert.ok(!this.mozCamera.autoFocus.called);
|
||||
assert.ok(previousFocusState === this.focus.focused);
|
||||
});
|
||||
|
||||
test('Should call the focus callback with interrupted state if autofocus is interrupted', function() {
|
||||
var onFocused = sinon.spy();
|
||||
this.mozCamera.autoFocus = sinon.stub();
|
||||
this.mozCamera.autoFocus.returns({
|
||||
then: function(resolve, reject) {
|
||||
var error = new Error();
|
||||
error.name = 'NS_ERROR_IN_PROGRESS';
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
this.mozCamera.focusMode = 'auto';
|
||||
this.focus.focused = true;
|
||||
this.focus.focus(onFocused);
|
||||
assert.ok(onFocused.calledWith('interrupted'));
|
||||
assert.ok(this.focus.focused === false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#pause()', function() {
|
||||
setup(function() {
|
||||
this.focus.stopContinuousFocus = sinon.spy();
|
||||
this.focus.stopFaceDetection = sinon.spy();
|
||||
});
|
||||
|
||||
test('Should do nothing if already paused', function() {
|
||||
this.focus.paused = true;
|
||||
this.focus.pause();
|
||||
assert.ok(!this.focus.stopContinuousFocus.called);
|
||||
assert.ok(!this.focus.stopFaceDetection.called);
|
||||
assert.ok(this.focus.paused);
|
||||
|
||||
});
|
||||
|
||||
test('Should stop face detection and continuous focus', function() {
|
||||
this.focus.paused = false;
|
||||
this.focus.pause();
|
||||
assert.ok(this.focus.stopContinuousFocus.called);
|
||||
assert.ok(this.focus.stopFaceDetection.called);
|
||||
assert.ok(this.focus.paused);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#reset()', function() {
|
||||
test('metering areas and focus areas are reset if touch focus enabled', function() {
|
||||
this.focus.touchFocus = true;
|
||||
this.focus.resetFocusAreas();
|
||||
assert.ok(this.focus.mozCamera.setFocusAreas.calledWith([]));
|
||||
assert.ok(this.focus.mozCamera.setMeteringAreas.calledWith([]));
|
||||
});
|
||||
|
||||
test('metering areas and focus areas are not reset if touch focus disabled', function() {
|
||||
this.focus.touchFocus = false;
|
||||
this.focus.resetFocusAreas();
|
||||
assert.ok(!this.focus.mozCamera.setFocusAreas.called);
|
||||
assert.ok(!this.focus.mozCamera.setMeteringAreas.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#resume()', function() {
|
||||
setup(function() {
|
||||
this.focus.resumeContinuousFocus = sinon.spy();
|
||||
});
|
||||
|
||||
test('should do nothing if not paused', function() {
|
||||
this.focus.paused = false;
|
||||
this.focus.resume();
|
||||
assert.ok(!this.focus.resumeContinuousFocus.called);
|
||||
assert.ok(!this.focus.paused);
|
||||
});
|
||||
|
||||
test('resumeContinuousFocus called if focus mode is continuous-picture', function() {
|
||||
this.focus.paused = true;
|
||||
this.focus.mozCamera.focusMode = 'continuous-picture'
|
||||
this.focus.resume();
|
||||
assert.ok(this.focus.resumeContinuousFocus.called);
|
||||
assert.ok(!this.focus.paused);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#isTouchFocusSupported()', function() {
|
||||
setup(function() {
|
||||
this.focus.mozCamera.capabilities = {};
|
||||
});
|
||||
|
||||
test('it returns false if touch to focus not supported by hardware', function() {
|
||||
this.focus.mozCamera.capabilities.maxFocusAreas = 0;
|
||||
this.focus.mozCamera.capabilities.maxMeteringAreas = 0;
|
||||
assert.ok(!this.focus.isTouchFocusSupported());
|
||||
});
|
||||
|
||||
test('it returns true if touch to focus supported by hardware', function() {
|
||||
this.focus.mozCamera.capabilities.maxFocusAreas = 4;
|
||||
this.focus.mozCamera.capabilities.maxMeteringAreas = 4;
|
||||
assert.ok(this.focus.isTouchFocusSupported());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#isFaceDetectionSupported()', function() {
|
||||
|
||||
test('it returns true if hardware supports face detection', function() {
|
||||
this.focus.mozCamera.capabilities = {
|
||||
maxDetectedFaces: 5
|
||||
};
|
||||
this.focus.mozCamera.startFaceDetection = sinon.stub();
|
||||
assert.ok(this.focus.isFaceDetectionSupported() === true);
|
||||
});
|
||||
|
||||
test('it returns false if hardware doesn\'t support face detection', function() {
|
||||
this.focus.mozCamera.capabilities = {
|
||||
maxDetectedFaces: 0
|
||||
};
|
||||
this.focus.mozCamera.startFaceDetection = undefined;
|
||||
assert.ok(this.focus.isFaceDetectionSupported() === false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Focus#updateFocusArea()', function() {
|
||||
setup(function() {
|
||||
this.focus.focus = sinon.spy();
|
||||
this.focus.stopContinuousFocus = sinon.spy();
|
||||
this.focus.suspendFaceDetection = sinon.spy();
|
||||
this.focus.onAutoFocusChanged = sinon.spy();
|
||||
});
|
||||
|
||||
test('it returns without doing anything if touch to focus disabled', function() {
|
||||
this.focus.touchFocus = false;
|
||||
this.focus.updateFocusArea();
|
||||
assert.ok(!this.focus.stopContinuousFocus.called);
|
||||
assert.ok(!this.focus.suspendFaceDetection.calledWith(10000));
|
||||
assert.ok(!this.focus.mozCamera.setFocusAreas.called);
|
||||
assert.ok(!this.focus.mozCamera.setMeteringAreas.called);
|
||||
assert.ok(!this.focus.focus.called);
|
||||
assert.ok(!this.focus.onAutoFocusChanged.called);
|
||||
});
|
||||
|
||||
test('it updates focus area if touch to focus is enabled', function() {
|
||||
this.focus.touchFocus = true;
|
||||
this.focus.updateFocusArea();
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
assert.ok(this.focus.stopContinuousFocus.called);
|
||||
assert.ok(this.focus.suspendFaceDetection.calledWith(10000));
|
||||
assert.ok(this.focus.mozCamera.setFocusAreas.called);
|
||||
assert.ok(this.focus.mozCamera.setMeteringAreas.called);
|
||||
assert.ok(this.focus.focus.called);
|
||||
});
|
||||
|
||||
test('Should call focus callback with fail state if updating the focus area fails', function() {
|
||||
this.focus.touchFocus = true;
|
||||
var onFocused = sinon.spy();
|
||||
this.focus.updateFocusArea(null, onFocused);
|
||||
assert.ok(this.focus.focus.called);
|
||||
this.focus.focus.callArgWith(0, 'failed');
|
||||
assert.ok(onFocused.calledWith('failed'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Focus#resumeCamera', function() {
|
||||
setup(function() {
|
||||
this.sandbox.spy(this.focus, 'onAutoFocusChanged');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('should call onAutoFocusChanged after pause', function() {
|
||||
this.focus.focusState = 'focusing';
|
||||
this.focus.pause();
|
||||
this.focus.updateFocusState('focusing');
|
||||
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
43
gaia/apps/camera/test/unit/lib/dcf_test.js
Executable file
43
gaia/apps/camera/test/unit/lib/dcf_test.js
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
require('/shared/js/format.js');
|
||||
|
||||
suite('Format Timer Unit Tests', function() {
|
||||
/*global asyncStorage*/
|
||||
'use strict';
|
||||
var subject;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
requirejs([
|
||||
'lib/dcf'
|
||||
], function(dcf) {
|
||||
subject = dcf;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('#createDCFFilename', function() {
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
var stub = this.sandbox.stub(asyncStorage, 'getItem');
|
||||
subject.init();
|
||||
// Trigger the callback
|
||||
stub.getCall(0).args[1]({
|
||||
file: 'moz',
|
||||
dir: '1'
|
||||
});
|
||||
});
|
||||
|
||||
test('=> pads the filename', function() {
|
||||
var result;
|
||||
var storage = {
|
||||
get: function(filename) {
|
||||
result = filename;
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
subject.createDCFFilename(storage, 'image');
|
||||
assert.equal(result, 'DCIM/1MZLLA/IMG_0moz.jpg');
|
||||
});
|
||||
});
|
||||
});
|
||||
64
gaia/apps/camera/test/unit/lib/format-picture-sizes_test.js
Executable file
64
gaia/apps/camera/test/unit/lib/format-picture-sizes_test.js
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
suite('lib/picture-sizes/format-picture-sizes', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['lib/format-picture-sizes'], function(formatPictureSizes) {
|
||||
self.formatPictureSizes = formatPictureSizes;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sizes = [
|
||||
{
|
||||
height: 1536,
|
||||
width: 2048
|
||||
},
|
||||
{
|
||||
height: 1200,
|
||||
width: 1600
|
||||
},
|
||||
{
|
||||
height: 240,
|
||||
width: 320
|
||||
},
|
||||
{
|
||||
height: 480,
|
||||
width: 640
|
||||
},
|
||||
{
|
||||
height: 288,
|
||||
width: 352
|
||||
}
|
||||
];
|
||||
|
||||
this.options = this.formatPictureSizes(this.sizes);
|
||||
});
|
||||
|
||||
test('Should be sorted by resolution', function() {
|
||||
assert.equal(this.options[0].key, '2048x1536');
|
||||
assert.equal(this.options[4].key, '320x240');
|
||||
});
|
||||
|
||||
test('Should exlude given keys', function() {
|
||||
var options = this.formatPictureSizes(this.sizes, { exclude: { keys: ['2048x1536'] }});
|
||||
var found = options.some(function(size) { return size.key === '2048x1536'; });
|
||||
assert.isFalse(found);
|
||||
});
|
||||
|
||||
test('Should exlude given aspects', function() {
|
||||
var options = this.formatPictureSizes(this.sizes, { exclude: { aspects: ['11:9'] }});
|
||||
var found = options.some(function(size) { return size.data.aspect === '11:9'; });
|
||||
assert.isFalse(found);
|
||||
});
|
||||
|
||||
test('Should not include sizes above the `maxPixelSize` option', function() {
|
||||
var maxPixelSize = 307200;
|
||||
var options = this.formatPictureSizes(this.sizes, { maxPixelSize: maxPixelSize });
|
||||
var someGreater = options.some(function(item) { return item.pixelSize > maxPixelSize; });
|
||||
|
||||
assert.isFalse(someGreater, 'should be none greater than: ' + maxPixelSize);
|
||||
});
|
||||
});
|
||||
155
gaia/apps/camera/test/unit/lib/format-recorder-profiles_test.js
Executable file
155
gaia/apps/camera/test/unit/lib/format-recorder-profiles_test.js
Executable file
|
|
@ -0,0 +1,155 @@
|
|||
suite('lib/format-recorder-profiles', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['lib/format-recorder-profiles'], function(formatRecorderProfiles) {
|
||||
self.formatRecorderProfiles = formatRecorderProfiles;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.profiles = {
|
||||
'low': {
|
||||
format: '3gp',
|
||||
video: {
|
||||
codec: 'mpeg4sp',
|
||||
bitrate: 128000,
|
||||
framerate: 15,
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
audio: {
|
||||
codec: 'amrnb',
|
||||
bitrate: 12200,
|
||||
samplerate: 8000,
|
||||
channels: 1
|
||||
}
|
||||
},
|
||||
'high': {
|
||||
format: 'mp4',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
bitrate: 12000000,
|
||||
framerate: 30,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
},
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
bitrate: 96000,
|
||||
samplerate: 48000,
|
||||
channels: 1
|
||||
}
|
||||
},
|
||||
'cif': {
|
||||
format: 'mp4',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
bitrate: 1200000,
|
||||
framerate: 30,
|
||||
width: 352,
|
||||
height: 288
|
||||
},
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
bitrate: 96000,
|
||||
samplerate: 48000,
|
||||
channels: 1
|
||||
},
|
||||
key: 'cif'
|
||||
},
|
||||
'480p': {
|
||||
format: 'mp4',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
bitrate: 5000000,
|
||||
framerate: 30,
|
||||
width: 720,
|
||||
height: 480
|
||||
},
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
bitrate: 96000,
|
||||
samplerate: 48000,
|
||||
channels: 1
|
||||
}
|
||||
},
|
||||
'720p': {
|
||||
format: 'mp4',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
bitrate: 8000000,
|
||||
framerate: 30,
|
||||
width: 1280,
|
||||
height: 720
|
||||
},
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
bitrate: 96000,
|
||||
samplerate: 48000,
|
||||
channels: 1
|
||||
}
|
||||
},
|
||||
'1080p': {
|
||||
format: 'mp4',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
bitrate: 12000000,
|
||||
framerate: 30,
|
||||
width: 1920,
|
||||
height: 1080
|
||||
},
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
bitrate: 96000,
|
||||
samplerate: 48000,
|
||||
channels: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.profiles.default = this.profiles['720p'];
|
||||
|
||||
this.result = this.formatRecorderProfiles(this.profiles, {
|
||||
exclude: ['high', 'low', 'default']
|
||||
});
|
||||
});
|
||||
|
||||
test('Should have formatted title string', function() {
|
||||
var items = this.result;
|
||||
|
||||
assert.equal(items[0].title, '1080p 1920x1080 16:9');
|
||||
assert.equal(items[1].title, '720p 1280x720 16:9');
|
||||
assert.equal(items[2].title, '480p 720x480 3:2');
|
||||
assert.equal(items[3].title, 'cif 352x288 11:9');
|
||||
});
|
||||
|
||||
test('Should be sorted by pixel count', function() {
|
||||
var items = this.result;
|
||||
var first = items[0];
|
||||
var last = items[items.length - 1];
|
||||
|
||||
assert.equal(first.key, '1080p');
|
||||
assert.equal(last.key, 'cif');
|
||||
});
|
||||
|
||||
test('Should exclude given keys', function() {
|
||||
var exclude = ['default', 'low', 'high', 'other'];
|
||||
var items = this.formatRecorderProfiles(this.profiles, { exclude: exclude });
|
||||
var found = items.some(function(item) { return exclude.indexOf(item.key) > -1; });
|
||||
|
||||
assert.isFalse(found);
|
||||
assert.equal(items.length, 4);
|
||||
});
|
||||
|
||||
test('Should store the raw profile', function() {
|
||||
assert.equal(this.result[0].raw, this.profiles['1080p']);
|
||||
});
|
||||
|
||||
test('Should store the `key`', function() {
|
||||
assert.equal(this.result[0].key, '1080p');
|
||||
});
|
||||
});
|
||||
27
gaia/apps/camera/test/unit/lib/format-timer_test.js
Executable file
27
gaia/apps/camera/test/unit/lib/format-timer_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Formattimer Test.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/format.js');
|
||||
|
||||
suite('Format Timer Unit Tests', function() {
|
||||
var subject;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
requirejs([
|
||||
'lib/format-timer'
|
||||
], function(formattimer) {
|
||||
subject = formattimer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('#exports', function() {
|
||||
test('=> pads minutes and seconds', function() {
|
||||
assert.equal(subject(0), '00:00');
|
||||
assert.equal(subject(60000), '01:00');
|
||||
assert.equal(subject(1000000), '16:40');
|
||||
});
|
||||
});
|
||||
});
|
||||
74
gaia/apps/camera/test/unit/lib/geo-location_test.js
Executable file
74
gaia/apps/camera/test/unit/lib/geo-location_test.js
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suite('geolocation', function() {
|
||||
var GeoLocation;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
requirejs(['lib/geo-location'], function(geolocation) {
|
||||
GeoLocation = geolocation;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.geolocation = new GeoLocation();
|
||||
sinon.stub(navigator.geolocation, 'watchPosition');
|
||||
sinon.stub(navigator.geolocation, 'clearWatch');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.geolocation.watchPosition.restore();
|
||||
navigator.geolocation.clearWatch.restore();
|
||||
});
|
||||
|
||||
suite('GeoLocation#watch()', function() {
|
||||
test('Should begin watching geolocation position if' +
|
||||
'no watcher currently in place', function() {
|
||||
this.geolocation.watch();
|
||||
assert.isTrue(navigator.geolocation.watchPosition.calledOnce);
|
||||
});
|
||||
|
||||
test('Should begin watching geolocation position if' +
|
||||
'no watcher currently in place', function() {
|
||||
this.geolocation.watcher = 1;
|
||||
this.geolocation.watch();
|
||||
assert.isFalse(navigator.geolocation.watchPosition.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('GeoLocation#stopWatching()', function() {
|
||||
test('Should call navigator.geolocation.clearWatch passing' +
|
||||
'a reference to the watcher', function() {
|
||||
this.geolocation.watcher = 1;
|
||||
this.geolocation.stopWatching();
|
||||
assert.isTrue(navigator.geolocation.clearWatch.calledWith(1));
|
||||
});
|
||||
|
||||
test('Should unset the watcher', function() {
|
||||
this.geolocation.watcher = 1;
|
||||
this.geolocation.stopWatching();
|
||||
assert.ok(!this.geolocation.watcher);
|
||||
});
|
||||
});
|
||||
|
||||
suite('GeoLocation#setPosition()', function() {
|
||||
test('Should store passed geolocation object on instance', function() {
|
||||
this.geolocation.setPosition({
|
||||
timestamp: 1234,
|
||||
coords: {
|
||||
latitude: 1,
|
||||
longitude: 2,
|
||||
altitude: 3
|
||||
}
|
||||
});
|
||||
|
||||
var position = this.geolocation.position;
|
||||
|
||||
assert.ok(position.timestamp === 1234);
|
||||
assert.ok(position.latitude === 1);
|
||||
assert.ok(position.longitude === 2);
|
||||
assert.ok(position.altitude === 3);
|
||||
});
|
||||
});
|
||||
});
|
||||
191
gaia/apps/camera/test/unit/lib/pinch_test.js
Executable file
191
gaia/apps/camera/test/unit/lib/pinch_test.js
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
suite('lib/pinch', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'lib/pinch'
|
||||
], function(Pinch) {
|
||||
self.Pinch = Pinch;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.el = {
|
||||
addEventListener: sinon.spy(),
|
||||
removeEventListener: sinon.spy()
|
||||
};
|
||||
|
||||
this.pinch = new this.Pinch(this.el);
|
||||
this.pinch.emit = sinon.spy();
|
||||
});
|
||||
|
||||
suite('Pinch()', function() {
|
||||
test('Should attach to specified element', function() {
|
||||
assert.isTrue(this.el.addEventListener.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Pinch#attach()', function() {
|
||||
test('Should attach touchstart to specified element', function() {
|
||||
this.pinch.attach(this.el);
|
||||
|
||||
assert.isTrue(this.el.addEventListener.calledWith('touchstart',
|
||||
this.pinch.onTouchStart));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Pinch#detach()', function() {
|
||||
test('Should detach touchstart from specified element', function() {
|
||||
this.pinch.detach(this.el);
|
||||
|
||||
assert.isTrue(this.el.removeEventListener.calledWith('touchstart',
|
||||
this.pinch.onTouchStart));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Pinch#onTouchStart()', function() {
|
||||
setup(function() {
|
||||
this.pinch.isPinching = false;
|
||||
});
|
||||
|
||||
test('Should NOT emit `started` for one touch', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}]
|
||||
});
|
||||
|
||||
assert.isFalse(this.pinch.emit.calledWith('started'));
|
||||
});
|
||||
|
||||
test('Should emit `started` for two touches', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}]
|
||||
});
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}, {}]
|
||||
});
|
||||
|
||||
assert.isTrue(this.pinch.isPinching);
|
||||
assert.isTrue(this.pinch.emit.calledWith('started'));
|
||||
});
|
||||
|
||||
test('Should remember last two touches for two touches', function() {
|
||||
var touchA = { identifier: 0 };
|
||||
var touchB = { identifier: 1 };
|
||||
this.pinch.onTouchStart({
|
||||
touches: [touchA, touchB]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.lastTouchA, touchA);
|
||||
assert.equal(this.pinch.lastTouchB, touchB);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Pinch#onTouchMove()', function() {
|
||||
setup(function() {
|
||||
this.pinch.isPinching = false;
|
||||
});
|
||||
|
||||
test('Should NOT emit `change` for one touch', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}]
|
||||
});
|
||||
this.pinch.onTouchMove({
|
||||
touches: [{}]
|
||||
});
|
||||
|
||||
assert.isFalse(this.pinch.emit.calledWith('changed'));
|
||||
});
|
||||
|
||||
test('Should emit `change` for two touches', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}]
|
||||
});
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}, {}]
|
||||
});
|
||||
this.pinch.onTouchMove({
|
||||
touches: [{}, {}]
|
||||
});
|
||||
|
||||
assert.isTrue(this.pinch.emit.calledWith('changed'));
|
||||
});
|
||||
|
||||
test('Should track touch "A" for two touches', function() {
|
||||
var touchA1 = { identifier: 0 };
|
||||
var touchA2 = { identifier: 0 };
|
||||
this.pinch.onTouchStart({
|
||||
touches: [touchA1, {}]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.lastTouchA, touchA1);
|
||||
|
||||
this.pinch.onTouchMove({
|
||||
touches: [touchA2, {}]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.lastTouchA, touchA2);
|
||||
});
|
||||
|
||||
test('Should track touch "B" for two touches', function() {
|
||||
var touchB1 = { identifier: 1 };
|
||||
var touchB2 = { identifier: 1 };
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}, touchB1]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.lastTouchB, touchB1);
|
||||
|
||||
this.pinch.onTouchMove({
|
||||
touches: [{}, touchB2]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.lastTouchB, touchB2);
|
||||
});
|
||||
|
||||
test('Should calculate delta for two touches', function() {
|
||||
var touchA1 = { identifier: 0, pageX: -1, pageY: -1 };
|
||||
var touchA2 = { identifier: 0, pageX: -2, pageY: -2 };
|
||||
var touchB1 = { identifier: 1, pageX: 1, pageY: 1 };
|
||||
var touchB2 = { identifier: 1, pageX: 2, pageY: 2 };
|
||||
this.pinch.onTouchStart({
|
||||
touches: [touchA1, touchB1]
|
||||
});
|
||||
this.pinch.onTouchMove({
|
||||
touches: [touchA2, touchB2]
|
||||
});
|
||||
|
||||
assert.equal(this.pinch.emit.args[1][1].toFixed(4), '2.8284');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Pinch#onTouchEnd()', function() {
|
||||
setup(function() {
|
||||
this.pinch.isPinching = false;
|
||||
});
|
||||
|
||||
test('Should NOT emit `ended` for one touch', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}]
|
||||
});
|
||||
this.pinch.onTouchEnd({
|
||||
touches: []
|
||||
});
|
||||
|
||||
assert.isFalse(this.pinch.emit.calledWith('ended'));
|
||||
});
|
||||
|
||||
test('Should emit `ended` for two touches', function() {
|
||||
this.pinch.onTouchStart({
|
||||
touches: [{}, {}]
|
||||
});
|
||||
this.pinch.onTouchEnd({
|
||||
touches: [{}]
|
||||
});
|
||||
|
||||
assert.isFalse(this.pinch.isPinching);
|
||||
assert.isTrue(this.pinch.emit.calledWith('ended'));
|
||||
});
|
||||
});
|
||||
});
|
||||
156
gaia/apps/camera/test/unit/lib/setting-alias_test.js
Executable file
156
gaia/apps/camera/test/unit/lib/setting-alias_test.js
Executable file
|
|
@ -0,0 +1,156 @@
|
|||
suite('lib/setting-alias', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'lib/setting',
|
||||
'lib/setting-alias',
|
||||
], function(Setting, SettingAlias) {
|
||||
self.Setting = Setting;
|
||||
self.SettingAlias = SettingAlias;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
|
||||
// We use this object to store state
|
||||
// as nested Suites and tests' context
|
||||
// inherits from this context.
|
||||
this.state = {};
|
||||
this.state.value = 'a';
|
||||
|
||||
this.settings = {
|
||||
a: new this.Setting({ key: 'a' }),
|
||||
b: new this.Setting({ key: 'b' })
|
||||
};
|
||||
|
||||
this.alias = new this.SettingAlias({
|
||||
key: 'my-alias',
|
||||
settings: this.settings,
|
||||
get: function() {
|
||||
return this.settings[self.state.value];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingAlias()', function() {
|
||||
test('It stores `key` and `settings` options', function() {
|
||||
assert.equal(this.alias.key, 'my-alias');
|
||||
assert.equal(this.alias.settings, this.settings);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingAlias#current()', function() {
|
||||
test('It returns the current setting', function() {
|
||||
assert.equal(this.alias.current().key, 'a');
|
||||
this.state.value = 'b';
|
||||
assert.equal(this.alias.current().key, 'b');
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingAlias#on()', function() {
|
||||
test('Should fire callback when *current* setting emits event', function() {
|
||||
var callback = sinon.spy();
|
||||
this.alias.on('event', callback);
|
||||
this.settings.a.fire('event');
|
||||
this.settings.b.fire('event');
|
||||
sinon.assert.calledOnce(callback);
|
||||
});
|
||||
|
||||
test('Should pass arguments to the callback', function() {
|
||||
var callback = sinon.spy();
|
||||
this.alias.on('event', callback);
|
||||
this.settings.a.fire('event', 'a', 'b');
|
||||
assert.ok(callback.calledWith('a', 'b'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingAlias#off()', function() {
|
||||
test('it calls the callback once', function() {
|
||||
var callback = sinon.spy();
|
||||
|
||||
this.alias.on('event', callback);
|
||||
this.settings.a.fire('event');
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
});
|
||||
|
||||
test('It only removes the listeners that match the callback given', function() {
|
||||
var callback1 = sinon.spy();
|
||||
var callback2 = sinon.stub();
|
||||
|
||||
this.alias.on('event', callback1);
|
||||
this.alias.on('event', callback2);
|
||||
|
||||
this.settings.a.fire('event');
|
||||
|
||||
sinon.assert.calledOnce(callback1);
|
||||
sinon.assert.calledOnce(callback2);
|
||||
|
||||
callback1.reset();
|
||||
callback2.reset();
|
||||
|
||||
this.alias.off('event', callback1);
|
||||
this.settings.a.fire('event');
|
||||
|
||||
sinon.assert.calledOnce(callback2);
|
||||
sinon.assert.notCalled(callback1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingAlias#get()', function() {
|
||||
test('It calls the underlying Setting\'s `.get()` method', function() {
|
||||
assert.equal(this.alias.get('key'), 'a');
|
||||
this.state.value = 'b';
|
||||
assert.equal(this.alias.get('key'), 'b');
|
||||
});
|
||||
});
|
||||
|
||||
test('It forwards the setting mathod calls onto the current setting', function() {
|
||||
sinon.stub(this.settings.a);
|
||||
sinon.stub(this.settings.b);
|
||||
|
||||
this.alias.filterOptions();
|
||||
this.alias.resetOptions();
|
||||
this.alias.supported();
|
||||
this.alias.selected();
|
||||
this.alias.select();
|
||||
this.alias.next();
|
||||
this.alias.get();
|
||||
this.alias.set();
|
||||
|
||||
sinon.assert.calledOnce(this.settings.a.filterOptions);
|
||||
sinon.assert.calledOnce(this.settings.a.resetOptions);
|
||||
sinon.assert.calledOnce(this.settings.a.supported);
|
||||
sinon.assert.calledOnce(this.settings.a.selected);
|
||||
sinon.assert.calledOnce(this.settings.a.select);
|
||||
sinon.assert.calledOnce(this.settings.a.next);
|
||||
sinon.assert.calledOnce(this.settings.a.get);
|
||||
sinon.assert.calledOnce(this.settings.a.set);
|
||||
|
||||
// Change state
|
||||
this.state.value = 'b';
|
||||
|
||||
this.alias.filterOptions();
|
||||
this.alias.resetOptions();
|
||||
this.alias.supported();
|
||||
this.alias.selected();
|
||||
this.alias.select();
|
||||
this.alias.next();
|
||||
this.alias.get();
|
||||
this.alias.set();
|
||||
|
||||
sinon.assert.calledOnce(this.settings.b.filterOptions);
|
||||
sinon.assert.calledOnce(this.settings.b.resetOptions);
|
||||
sinon.assert.calledOnce(this.settings.b.supported);
|
||||
sinon.assert.calledOnce(this.settings.b.selected);
|
||||
sinon.assert.calledOnce(this.settings.b.select);
|
||||
sinon.assert.calledOnce(this.settings.b.next);
|
||||
sinon.assert.calledOnce(this.settings.b.get);
|
||||
sinon.assert.calledOnce(this.settings.b.set);
|
||||
});
|
||||
});
|
||||
409
gaia/apps/camera/test/unit/lib/setting_test.js
Executable file
409
gaia/apps/camera/test/unit/lib/setting_test.js
Executable file
|
|
@ -0,0 +1,409 @@
|
|||
suite('lib/setting', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['lib/setting'], function(Setting) {
|
||||
self.Setting = Setting;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.storage = {
|
||||
getItem: sinon.stub(),
|
||||
setItem: sinon.stub()
|
||||
};
|
||||
});
|
||||
|
||||
suite('Setting()', function() {
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
test('Should store the key on self', function() {
|
||||
var setting = new this.Setting({ key: 'mykey', options: [] });
|
||||
assert.ok(setting.key === 'mykey');
|
||||
});
|
||||
|
||||
test('Should save when selection is change, only if marked as `persistent`', function() {
|
||||
var on = this.sandbox.spy(this.Setting.prototype, 'on');
|
||||
var settings1 = new this.Setting({
|
||||
key: 'key',
|
||||
persistent: true,
|
||||
options: []
|
||||
});
|
||||
|
||||
assert.ok(on.calledWith('change:selected', settings1.save));
|
||||
on.reset();
|
||||
|
||||
var settings2 = new this.Setting({ key: 'key', options: [] });
|
||||
assert.ok(!on.calledWith('change:selected', settings2.save));
|
||||
});
|
||||
|
||||
test('Should default to selecting first option if ' +
|
||||
'`selected` is undefined', function() {
|
||||
var setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' }
|
||||
]
|
||||
});
|
||||
|
||||
assert.ok(setting.get('selected') === 'a');
|
||||
});
|
||||
|
||||
test('Should flag `options.defined` if options initially defined', function() {
|
||||
var setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' }
|
||||
]
|
||||
});
|
||||
|
||||
assert.isTrue(setting.options.defined);
|
||||
|
||||
setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: []
|
||||
});
|
||||
|
||||
assert.isFalse(setting.options.defined);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#select()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should select the first option if no key match', function() {
|
||||
this.setting.select('b');
|
||||
assert.ok(this.setting.get('selected') === 'b');
|
||||
|
||||
// Fallback to first
|
||||
this.setting.select('c');
|
||||
assert.ok(this.setting.get('selected') === 'a');
|
||||
this.setting.select(3);
|
||||
assert.ok(this.setting.get('selected') === 'a');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#resetOptions()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
selected: 'b',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' },
|
||||
{ key: 'c' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should update the `selected` option to first if no new options match selected', function() {
|
||||
this.setting.resetOptions([
|
||||
{ key: 'd' },
|
||||
{ key: 'e' },
|
||||
{ key: 'f' }
|
||||
]);
|
||||
|
||||
assert.equal(this.setting.selected('key'), 'd');
|
||||
});
|
||||
|
||||
test('Should maintain the selected key if a key of new options matches', function() {
|
||||
this.setting.resetOptions([
|
||||
{ key: 'c' },
|
||||
{ key: 'b' },
|
||||
{ key: 'a' }
|
||||
]);
|
||||
|
||||
assert.equal(this.setting.selected('key'), 'b');
|
||||
});
|
||||
|
||||
test('Should maintain the order', function() {
|
||||
var options;
|
||||
|
||||
this.setting.resetOptions([
|
||||
{ key: 'a' },
|
||||
{ key: 'b' },
|
||||
{ key: 'c' }
|
||||
]);
|
||||
|
||||
options = this.setting.get('options');
|
||||
assert.equal(options[0].key, 'a');
|
||||
assert.equal(options[1].key, 'b');
|
||||
assert.equal(options[2].key, 'c');
|
||||
|
||||
this.setting.resetOptions([
|
||||
{ key: 'c' },
|
||||
{ key: 'b' },
|
||||
{ key: 'a' }
|
||||
]);
|
||||
|
||||
options = this.setting.get('options');
|
||||
assert.equal(options[0].key, 'c');
|
||||
assert.equal(options[1].key, 'b');
|
||||
assert.equal(options[2].key, 'a');
|
||||
});
|
||||
|
||||
test('Should not fire a `change` event', function() {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.setting.on('change', spy);
|
||||
this.setting.resetOptions([{ key: 'c' }]);
|
||||
|
||||
assert.isFalse(spy.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#filterOptions()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'key',
|
||||
selected: 'b',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' },
|
||||
{ key: 'c' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should filter down the available options', function() {
|
||||
this.setting.filterOptions(['a', 'b']);
|
||||
|
||||
var available = this.setting.get('options');
|
||||
var found = available.some(function(option) { return option.key === 'c'; });
|
||||
|
||||
assert.isFalse(found, 'c should not longer be present');
|
||||
});
|
||||
|
||||
test('Should have no available options if argument is falsy', function() {
|
||||
var available;
|
||||
|
||||
this.setting.filterOptions(null);
|
||||
available = this.setting.get('options');
|
||||
assert.equal(available.length, 0);
|
||||
|
||||
this.setting.filterOptions(undefined);
|
||||
available = this.setting.get('options');
|
||||
assert.equal(available.length, 0);
|
||||
|
||||
this.setting.filterOptions(false);
|
||||
available = this.setting.get('options');
|
||||
assert.equal(available.length, 0);
|
||||
});
|
||||
|
||||
test('Should always filter the original config', function() {
|
||||
var available;
|
||||
|
||||
this.setting.filterOptions(['a', 'b']);
|
||||
available = this.setting.get('options');
|
||||
assert.equal(available.length, 2);
|
||||
assert.equal(available[0].key, 'a');
|
||||
assert.equal(available[1].key, 'b');
|
||||
|
||||
this.setting.filterOptions(['c']);
|
||||
available = this.setting.get('options');
|
||||
assert.equal(available.length, 1);
|
||||
assert.equal(available[0].key, 'c');
|
||||
});
|
||||
|
||||
test('Should maintain `selected` if in filter list', function() {
|
||||
this.setting.filterOptions(['c', 'b']);
|
||||
assert.equal(this.setting.selected('key'), 'b');
|
||||
});
|
||||
|
||||
test('Should update the `selected` match first if not in filter list', function() {
|
||||
this.setting.filterOptions(['c', 'a']);
|
||||
assert.equal(this.setting.selected('key'), 'a');
|
||||
});
|
||||
|
||||
test('Should fire a `change` event', function() {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.setting.on('change', spy);
|
||||
this.setting.filterOptions(['c']);
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
});
|
||||
|
||||
test('Should cope with keys that aren\'t defined', function() {
|
||||
this.setting.filterOptions(['a', 'c', 'jibberish']);
|
||||
var options = this.setting.get('options');
|
||||
|
||||
assert.equal(options.length, 2);
|
||||
assert.equal(options[0].key, 'a');
|
||||
assert.equal(options[1].key, 'c');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#selected()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: [
|
||||
{ key: 'a', title: 'i am a' },
|
||||
{ key: 'b', title: 'i am b' },
|
||||
{ key: 'c', title: 'i am c' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should return the currently selected option', function() {
|
||||
var selected = this.setting.selected();
|
||||
assert.ok(selected.title === 'i am a');
|
||||
|
||||
this.setting.select('b');
|
||||
|
||||
selected = this.setting.selected();
|
||||
assert.ok(selected.title === 'i am b');
|
||||
});
|
||||
|
||||
test('Should return the given key from the selected option', function() {
|
||||
var title = this.setting.selected('title');
|
||||
assert.ok(title === 'i am a');
|
||||
|
||||
this.setting.select('b');
|
||||
|
||||
var key = this.setting.selected('key');
|
||||
assert.ok(key === 'b');
|
||||
});
|
||||
|
||||
test('Should return `undefined` if no available options', function() {
|
||||
this.setting.filterOptions([]);
|
||||
|
||||
assert.equal(this.setting.selected(), undefined);
|
||||
assert.equal(this.setting.selected('key'), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#next()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'key',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' },
|
||||
{ key: 'c' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should set the `selected` value to the next option', function() {
|
||||
this.setting.next();
|
||||
assert.ok(this.setting.selected().key === 'b');
|
||||
this.setting.next();
|
||||
assert.ok(this.setting.selected().key === 'c');
|
||||
this.setting.next();
|
||||
assert.ok(this.setting.selected().key === 'a');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#supported()', function() {
|
||||
setup(function() {
|
||||
this.config = {
|
||||
title: 'My title',
|
||||
options: [{ key: 'a' }, { key: 'b' }]
|
||||
};
|
||||
});
|
||||
|
||||
test('Should return false if disabled', function() {
|
||||
var setting;
|
||||
|
||||
this.config.disabled = true;
|
||||
setting = new this.Setting(this.config);
|
||||
assert.isFalse(setting.supported());
|
||||
|
||||
this.config.disabled = false;
|
||||
setting = new this.Setting(this.config);
|
||||
assert.isTrue(setting.supported());
|
||||
|
||||
delete this.config.disabled;
|
||||
setting = new this.Setting(this.config);
|
||||
assert.isTrue(setting.supported());
|
||||
});
|
||||
|
||||
test('Should return true if not disabled and has options', function() {
|
||||
var setting;
|
||||
|
||||
setting = new this.Setting(this.config);
|
||||
assert.isTrue(setting.supported());
|
||||
|
||||
this.config.options = [];
|
||||
setting = new this.Setting(this.config);
|
||||
assert.isFalse(setting.supported());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#fetch()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'foo',
|
||||
storage: this.storage
|
||||
});
|
||||
});
|
||||
|
||||
test('Should `select` silently the given key if one is in storage', function() {
|
||||
sinon.spy(this.setting, 'selected');
|
||||
|
||||
this.setting.fetch();
|
||||
assert.isFalse(this.setting.selected.called);
|
||||
|
||||
this.storage.getItem
|
||||
.withArgs('setting:' + this.setting.key)
|
||||
.returns('the-key');
|
||||
|
||||
this.setting.fetch();
|
||||
assert.isFalse(this.setting.selected.calledWith('the-key'));
|
||||
});
|
||||
|
||||
test('Should not fire a `change` event', function() {
|
||||
var spy = sinon.spy();
|
||||
|
||||
this.storage.getItem
|
||||
.withArgs('setting:' + this.setting.key)
|
||||
.returns('the-key');
|
||||
|
||||
this.setting.on('change', spy);
|
||||
this.setting.fetch();
|
||||
|
||||
assert.isFalse(spy.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setting#save()', function() {
|
||||
setup(function() {
|
||||
this.setting = new this.Setting({
|
||||
key: 'foo',
|
||||
storage: this.storage,
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test('Should call `setItem` with the current selected option key', function() {
|
||||
var key = 'setting:' + this.setting.key;
|
||||
var value = this.setting.selected('key');
|
||||
|
||||
this.setting.save();
|
||||
assert.isTrue(this.storage.setItem.calledWith(key, value));
|
||||
});
|
||||
});
|
||||
});
|
||||
100
gaia/apps/camera/test/unit/lib/settings_test.js
Executable file
100
gaia/apps/camera/test/unit/lib/settings_test.js
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
suite('lib/settings', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'lib/settings',
|
||||
'lib/setting'
|
||||
], function(Settings, Setting) {
|
||||
self.Settings = Settings;
|
||||
self.Setting = Setting;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.settings = new this.Settings({
|
||||
setting1: {
|
||||
some: 'stuff',
|
||||
options: [
|
||||
{ key: 'a' },
|
||||
{ key: 'b' },
|
||||
{ key: 'c' }
|
||||
],
|
||||
persistent: true
|
||||
},
|
||||
setting2: { some: 'stuff' },
|
||||
setting3: { some: 'stuff' }
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings()', function() {
|
||||
test('Should store each setting on self by key', function() {
|
||||
assert.isTrue(this.settings.setting1 instanceof this.Setting);
|
||||
assert.isTrue(this.settings.setting2 instanceof this.Setting);
|
||||
assert.isTrue(this.settings.setting3 instanceof this.Setting);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#fetch()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.settings.setting1, 'fetch');
|
||||
sinon.stub(this.settings.setting2, 'fetch');
|
||||
sinon.stub(this.settings.setting3, 'fetch');
|
||||
});
|
||||
|
||||
test('Should call fetch() on each setting', function() {
|
||||
this.settings.fetch();
|
||||
assert.isTrue(this.settings.setting1.fetch.called);
|
||||
assert.isTrue(this.settings.setting2.fetch.called);
|
||||
assert.isTrue(this.settings.setting3.fetch.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#alias()', function() {
|
||||
setup(function() {
|
||||
sinon.spy(this.settings, 'SettingAlias');
|
||||
this.settings.alias({
|
||||
key: 'myAlias',
|
||||
settings: {},
|
||||
get: function() {}
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.settings.SettingAlias.restore();
|
||||
});
|
||||
|
||||
test('Should call SettingAlias', function() {
|
||||
assert.isTrue(this.settings.SettingAlias.called);
|
||||
});
|
||||
|
||||
test('Should store the alias on self by key', function() {
|
||||
assert.isDefined(this.settings.myAlias);
|
||||
});
|
||||
|
||||
test('Should pass in the given key', function() {
|
||||
var options = this.settings.SettingAlias.args[0][0];
|
||||
assert.equal(options.key, 'myAlias');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#dontSave()', function() {
|
||||
test('Should remove any save event handlers', function() {
|
||||
var setting1 = this.settings.setting1;
|
||||
var setting2 = this.settings.setting2;
|
||||
var setting3 = this.settings.setting3;
|
||||
|
||||
sinon.spy(setting1, 'off');
|
||||
sinon.spy(setting2, 'off');
|
||||
sinon.spy(setting3, 'off');
|
||||
|
||||
this.settings.dontSave();
|
||||
|
||||
assert.isTrue(setting1.off.calledWith('change:selected', setting1.save));
|
||||
assert.isTrue(setting2.off.calledWith('change:selected', setting2.save));
|
||||
assert.isTrue(setting3.off.calledWith('change:selected', setting3.save));
|
||||
});
|
||||
});
|
||||
});
|
||||
230
gaia/apps/camera/test/unit/lib/sounds_test.js
Executable file
230
gaia/apps/camera/test/unit/lib/sounds_test.js
Executable file
|
|
@ -0,0 +1,230 @@
|
|||
require('/shared/test/unit/mocks/mock_audio.js')
|
||||
|
||||
var mocksHelperForSounds = new MocksHelper([
|
||||
'Audio'
|
||||
]).init();
|
||||
|
||||
suite('Sounds', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
|
||||
var Sounds;
|
||||
var list = [
|
||||
{
|
||||
name: 'shutter',
|
||||
url: 'resources/sounds/shutter.opus',
|
||||
setting: 'camera.sound.enabled'
|
||||
},
|
||||
{
|
||||
name: 'countdown',
|
||||
url: 'resources/sounds/countdown.opus',
|
||||
setting: 'camera.sound.enabled'
|
||||
},
|
||||
{
|
||||
name: 'recordingStart',
|
||||
url: 'resources/sounds/camcorder_start.opus',
|
||||
setting: 'camera.sound.enabled'
|
||||
},
|
||||
{
|
||||
name: 'recordingEnd',
|
||||
url: 'resources/sounds/camcorder_end.opus',
|
||||
setting: 'camera.sound.enabled'
|
||||
}
|
||||
];
|
||||
|
||||
mocksHelperForSounds.attachTestHelpers();
|
||||
|
||||
suiteSetup(function(done) {
|
||||
requirejs(['lib/sounds'], function(sounds) {
|
||||
Sounds = sounds;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
this.clock = sinon.useFakeTimers();
|
||||
this.sounds = new Sounds();
|
||||
|
||||
// A sound to pass to APIs
|
||||
this.mockSound = {
|
||||
name: 'camera',
|
||||
url: 'resources/sounds/shutter.opus',
|
||||
setting: 'camera.sound.enabled'
|
||||
};
|
||||
|
||||
// Keep reference of
|
||||
// things we want to restore
|
||||
this.backup = {
|
||||
mozSettings: navigator.mozSettings
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.mozSettings = this.backup.mozSettings;
|
||||
this.sandbox.restore();
|
||||
this.clock.restore();
|
||||
});
|
||||
|
||||
suite('Sounds()', function() {
|
||||
test('Should have an empty items object', function() {
|
||||
var items = this.sounds.items;
|
||||
assert.ok(this.sounds.hasOwnProperty('items'));
|
||||
assert.ok(typeof items === 'object');
|
||||
assert.ok(Object.keys(items).length === 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Sounds#add()', function() {
|
||||
setup(function() {
|
||||
|
||||
// Stub `isEnabled` and call the callback with `true`
|
||||
this.sandbox.stub(this.sounds, 'isEnabled');
|
||||
this.sounds.isEnabled.callsArgWith(1, true);
|
||||
|
||||
// Simple stubs
|
||||
this.sandbox.stub(this.sounds, 'setEnabled');
|
||||
this.sandbox.stub(this.sounds, 'observeSetting');
|
||||
});
|
||||
|
||||
test('Should add each of the sounds to the this.items hash', function() {
|
||||
this.sounds.add(list[0]);
|
||||
this.sounds.add(list[1]);
|
||||
this.sounds.add(list[2]);
|
||||
this.sounds.add(list[3]);
|
||||
assert.ok(this.sounds.items.shutter);
|
||||
assert.ok(this.sounds.items.countdown);
|
||||
assert.ok(this.sounds.items.recordingStart);
|
||||
assert.ok(this.sounds.items.recordingEnd);
|
||||
});
|
||||
|
||||
test('Should call `isEnabled`', function() {
|
||||
this.sounds.add(list[0]);
|
||||
assert.ok(this.sounds.isEnabled.called);
|
||||
});
|
||||
|
||||
test('Should call `setEnabled` with the result of `isEnabled`', function() {
|
||||
var setEnabled = this.sounds.setEnabled;
|
||||
this.sounds.add(list[0]);
|
||||
assert.ok(setEnabled.calledWith(this.sounds.items.shutter, true));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Sounds#isEnabled()', function() {
|
||||
setup(function() {
|
||||
var self = this;
|
||||
|
||||
// Mock object that mimicks
|
||||
// mozSettings get API. Inside
|
||||
// tests set this.mozSettingsGetResult
|
||||
// define the result of the mock call.
|
||||
navigator.mozSettings = {
|
||||
createLock: function() { return this; },
|
||||
get: function(key) {
|
||||
var mozSettings = this;
|
||||
setTimeout(function() {
|
||||
var result = {};
|
||||
result[key] = 'the-result';
|
||||
mozSettings.onsuccess({
|
||||
target: {
|
||||
result: result
|
||||
}
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('Should not error if navigator.mozSettings is undefined', function() {
|
||||
navigator.mozSettings = undefined;
|
||||
this.sounds.isEnabled(this.mockSound);
|
||||
});
|
||||
|
||||
test('Should return the result from mozSettings API', function() {
|
||||
this.sounds.isEnabled(this.mockSound, function(result) {
|
||||
assert.ok(result === 'the-result');
|
||||
});
|
||||
|
||||
// Move time forwards
|
||||
// so the callback fires
|
||||
this.clock.tick(1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Sounds#observeSetting()', function() {
|
||||
setup(function() {
|
||||
this.sandbox.stub(this.sounds, 'setEnabled');
|
||||
navigator.mozSettings = {
|
||||
addObserver: function(key, callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test('Should not error if navigator.mozSettings is undefined', function() {
|
||||
navigator.mozSettings = undefined;
|
||||
this.sounds.observeSetting(this.mockSound);
|
||||
});
|
||||
|
||||
test('Should call setEnabled with the value passed' +
|
||||
'to the observe callback', function() {
|
||||
this.sounds.observeSetting(this.mockSound);
|
||||
|
||||
// Manually call the callback
|
||||
// checking that `setEnabled` was
|
||||
// called with the expected arguments
|
||||
navigator.mozSettings.callback({ settingValue: true });
|
||||
assert.ok(this.sounds.setEnabled.calledWith(this.mockSound, true));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Sounds#createAudio()', function() {
|
||||
test('Should return an Audio object with the given src', function() {
|
||||
var url = this.mockSound.url;
|
||||
var audio = this.sounds.createAudio(url);
|
||||
assert.ok(audio instanceof window.Audio);
|
||||
assert.ok(~audio.src.indexOf(url));
|
||||
});
|
||||
|
||||
test('Should set the mozAudioChannel type', function() {
|
||||
var audio = this.sounds.createAudio(this.mockSound.url);
|
||||
assert.ok(audio.mozAudioChannelType === 'notification');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Sounds#playSound()', function() {
|
||||
setup(function() {
|
||||
this.mockSound.audio = {
|
||||
play: sinon.spy(),
|
||||
cloneNode: function() { return this; }
|
||||
};
|
||||
});
|
||||
|
||||
test('Should *not* play the sound if it\'s not enabled', function() {
|
||||
this.sounds.playSound(this.mockSound);
|
||||
assert.ok(this.mockSound.audio.play.notCalled);
|
||||
});
|
||||
|
||||
test('Should play the sound if it\'s enabled', function() {
|
||||
this.mockSound.enabled = true;
|
||||
this.sounds.playSound(this.mockSound);
|
||||
assert.ok(this.mockSound.audio.play.called);
|
||||
});
|
||||
|
||||
test('Should throw exception if sound is undefined', function() {
|
||||
try {
|
||||
this.sounds.playSound();
|
||||
} catch (e) {
|
||||
assert.ok('correctly threw exception');
|
||||
return;
|
||||
}
|
||||
|
||||
assert.ok(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
214
gaia/apps/camera/test/unit/lib/storage_test.js
Executable file
214
gaia/apps/camera/test/unit/lib/storage_test.js
Executable file
|
|
@ -0,0 +1,214 @@
|
|||
suite('lib/storage', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'lib/storage'
|
||||
], function(Storage) {
|
||||
self.Storage = Storage;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = sinon.useFakeTimers();
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
this.video = {};
|
||||
this.video.addEventListener = sinon.spy();
|
||||
this.video.delete = sinon.stub().returns(this.video);
|
||||
|
||||
this.picture = {};
|
||||
this.picture.addEventListener = sinon.spy();
|
||||
this.picture.removeEventListener = sinon.spy();
|
||||
this.picture.delete = sinon.stub().returns(this.picture);
|
||||
|
||||
// Stub getDeviceStorage
|
||||
if (!navigator.getDeviceStorage) { navigator.getDeviceStorage = function() {}; }
|
||||
this.sandbox.stub(navigator, 'getDeviceStorage');
|
||||
|
||||
navigator.getDeviceStorage
|
||||
.withArgs('pictures')
|
||||
.returns(this.picture);
|
||||
|
||||
navigator.getDeviceStorage
|
||||
.withArgs('videos')
|
||||
.returns(this.video);
|
||||
|
||||
// Stub getDeviceStorages
|
||||
if (!navigator.getDeviceStorages) { navigator.getDeviceStorages = function() {}; }
|
||||
this.sandbox.stub(navigator, 'getDeviceStorages')
|
||||
.withArgs('pictures').returns([this.picture])
|
||||
.withArgs('videos').returns([this.video]);
|
||||
|
||||
// Stub mozSettings
|
||||
navigator.mozSettings = {
|
||||
addObserver: sinon.stub()
|
||||
};
|
||||
|
||||
var options = {
|
||||
dcf: {
|
||||
init: sinon.spy(),
|
||||
createDCFFilename: sinon.stub().callsArgWith(2, 'filename.file')
|
||||
},
|
||||
require: sinon.stub().callsArg(1)
|
||||
};
|
||||
|
||||
// The test instance
|
||||
this.storage = new this.Storage(options);
|
||||
// Storage is a singleton. This forces reconfiguration for each suite
|
||||
this.storage.configure();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.clock.restore();
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('Storage()', function() {
|
||||
test('Should listen for change events', function() {
|
||||
assert.isTrue(this.picture.addEventListener.calledWith('change'));
|
||||
assert.isTrue(navigator.mozSettings.addObserver.called);
|
||||
});
|
||||
|
||||
test('Should set state when receiving change events with available reason', function() {
|
||||
this.sandbox.stub(this.storage, 'setState');
|
||||
this.sandbox.stub(this.storage, 'check');
|
||||
this.storage.onStorageChange({
|
||||
reason: 'available'
|
||||
});
|
||||
assert.isTrue(this.storage.setState.called);
|
||||
assert.isTrue(this.storage.check.called);
|
||||
});
|
||||
|
||||
test('Should set state when receiving change events with unavailable reason', function() {
|
||||
this.sandbox.stub(this.storage, 'setState');
|
||||
this.sandbox.stub(this.storage, 'check');
|
||||
this.storage.onStorageChange({
|
||||
reason: 'unavailable'
|
||||
});
|
||||
assert.isTrue(this.storage.setState.called);
|
||||
assert.isTrue(this.storage.check.called);
|
||||
});
|
||||
|
||||
test('Should set state when receiving change events with shared reason', function() {
|
||||
this.sandbox.stub(this.storage, 'setState');
|
||||
this.sandbox.stub(this.storage, 'check');
|
||||
this.storage.onStorageChange({
|
||||
reason: 'shared'
|
||||
});
|
||||
assert.isTrue(this.storage.setState.called);
|
||||
assert.isTrue(this.storage.check.called);
|
||||
});
|
||||
|
||||
test('Should not set state when receiving change events with unexpected reason', function() {
|
||||
this.sandbox.stub(this.storage, 'setState');
|
||||
this.sandbox.stub(this.storage, 'check');
|
||||
this.storage.onStorageChange({
|
||||
reason: 'foo'
|
||||
});
|
||||
assert.isFalse(this.storage.setState.called);
|
||||
assert.isTrue(this.storage.check.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Storage#addPicture()', function() {
|
||||
setup(function() {
|
||||
this.picture.addNamed = sinon.spy(function() { return this.addNamed.req; });
|
||||
this.picture.addNamed.req = { result: '/path/to/picture.jpg' };
|
||||
|
||||
this.picture.get = sinon.spy(function() { return this.get.req; });
|
||||
this.picture.get.req = { result: 'memory-backed-blob' };
|
||||
|
||||
this.callback = sinon.spy();
|
||||
|
||||
// Happy path
|
||||
this.storage.addPicture('fake-blob', this.callback);
|
||||
this.picture.addNamed.req.onsuccess({ target: this.picture.addNamed.req });
|
||||
this.picture.get.req.onsuccess({ target: this.picture.get.req });
|
||||
});
|
||||
|
||||
test('Should create a filename if one not given', function() {
|
||||
assert.isTrue(this.storage.dcf.createDCFFilename.calledWith(this.picture, 'image'));
|
||||
});
|
||||
|
||||
test('Should add the given blob to picture storage', function() {
|
||||
assert.isTrue(this.picture.addNamed.calledWith('fake-blob', 'filename.file'));
|
||||
});
|
||||
|
||||
test('Should refetch the memory-backed-blob', function() {
|
||||
assert.isTrue(this.picture.get.calledWith('filename.file'));
|
||||
});
|
||||
|
||||
test('Should callback passing the relative path, absolute path and memory-backed-blob', function() {
|
||||
assert.isTrue(this.callback.calledWith(
|
||||
null,
|
||||
'filename.file',
|
||||
'/path/to/picture.jpg',
|
||||
'memory-backed-blob'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#isSpace()', function() {
|
||||
setup(function() {
|
||||
var picture = this.picture;
|
||||
var self = this;
|
||||
this.event = { target: { result: 100 } };
|
||||
this.picture.freeSpace = function() {
|
||||
setTimeout(function() { picture.onsuccess(self.event); });
|
||||
return picture;
|
||||
};
|
||||
});
|
||||
|
||||
test('Should return false if space < `maxFileSize` value', function() {
|
||||
this.storage.maxFileSize = 50;
|
||||
this.storage.isSpace(function(result) {
|
||||
assert.isTrue(result);
|
||||
});
|
||||
|
||||
this.clock.tick(1);
|
||||
});
|
||||
|
||||
test('Should return true if space > `maxFileSize` value', function() {
|
||||
this.storage.maxFileSize = 200;
|
||||
this.storage.isSpace(function(result) {
|
||||
assert.isFalse(result);
|
||||
});
|
||||
|
||||
this.clock.tick(1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#deletePicture()', function() {
|
||||
test('Should call the deviceStorageAPI', function() {
|
||||
this.storage.deletePicture('path/to/picture.jpg');
|
||||
assert.isTrue(this.picture.delete.calledWith('path/to/picture.jpg'));
|
||||
});
|
||||
|
||||
test('Should work when called out of context', function() {
|
||||
var fn = this.storage.deletePicture;
|
||||
fn('path/to/picture.jpg');
|
||||
assert.isTrue(this.picture.delete.calledWith('path/to/picture.jpg'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('Settings#deleteVideo()', function() {
|
||||
test('Should call the deviceStorageAPI', function() {
|
||||
this.storage.deleteVideo('path/video.3gp');
|
||||
assert.isTrue(this.video.delete.calledWith('path/video.3gp'));
|
||||
});
|
||||
|
||||
test('Should delete matching poster image', function() {
|
||||
this.storage.deleteVideo('path/video.3gp');
|
||||
assert.isTrue(this.picture.delete.calledWith('path/video.jpg'));
|
||||
});
|
||||
|
||||
test('Should work when called out of context', function() {
|
||||
var fn = this.storage.deleteVideo;
|
||||
fn('path/video.3gp');
|
||||
assert.isTrue(this.video.delete.calledWith('path/video.3gp'));
|
||||
});
|
||||
});
|
||||
});
|
||||
67
gaia/apps/camera/test/unit/lib/string-utils_test.js
Executable file
67
gaia/apps/camera/test/unit/lib/string-utils_test.js
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
/*jshint maxlen:false*/
|
||||
/*global req*/
|
||||
'use strict';
|
||||
|
||||
suite('utils/string-utils', function() {
|
||||
var StringUtils;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
|
||||
this.modules = {};
|
||||
|
||||
requirejs([
|
||||
'lib/string-utils'
|
||||
], function(_StringUtils) {
|
||||
StringUtils = self.modules.StringUtils = _StringUtils;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
suite('toCamelCase', function() {
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
test('Should convert hyphenated string to camel-case', function() {
|
||||
var sourceStr = 'this-string-should-be-camel-cased';
|
||||
var outputStr = StringUtils.toCamelCase(sourceStr);
|
||||
|
||||
assert.equal(outputStr, 'thisStringShouldBeCamelCased');
|
||||
});
|
||||
|
||||
test('Should convert snake-case string to camel-case', function() {
|
||||
var sourceStr = 'THIS_STRING_SHOULD_BE_CAMEL_CASED';
|
||||
var outputStr = StringUtils.toCamelCase(sourceStr);
|
||||
|
||||
assert.equal(outputStr, 'thisStringShouldBeCamelCased');
|
||||
});
|
||||
});
|
||||
|
||||
suite('toHyphenate', function() {
|
||||
setup(function() {
|
||||
|
||||
});
|
||||
|
||||
test('Should convert camel-case string to hyphenated', function() {
|
||||
var sourceStr = 'thisStringShouldBeHyphenated';
|
||||
var outputStr = StringUtils.toHyphenate(sourceStr);
|
||||
|
||||
assert.equal(outputStr, 'this-string-should-be-hyphenated');
|
||||
});
|
||||
});
|
||||
|
||||
suite('lastPathComponent', function() {
|
||||
setup(function() {});
|
||||
|
||||
test('Should get the last path component', function() {
|
||||
assert.equal(StringUtils.lastPathComponent('this/is/a/path'), 'path');
|
||||
assert.equal(StringUtils.lastPathComponent('abc'), 'abc');
|
||||
});
|
||||
});
|
||||
});
|
||||
12
gaia/apps/camera/test/unit/setup.js
Executable file
12
gaia/apps/camera/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
// Shim for non FXOS environments
|
||||
if (!navigator.getDeviceStorage) {
|
||||
navigator.getDeviceStorage = function() {};
|
||||
}
|
||||
|
||||
// Load RequireJS and use same config as the app uses
|
||||
requireApp('camera/bower_components/requirejs/index.js');
|
||||
requireApp('camera/js/config/require.js');
|
||||
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
281
gaia/apps/camera/test/unit/views/controls_test.js
Executable file
281
gaia/apps/camera/test/unit/views/controls_test.js
Executable file
|
|
@ -0,0 +1,281 @@
|
|||
suite('views/controls', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'views/controls',
|
||||
'drag'
|
||||
], function(ControlsView, Drag) {
|
||||
self.ControlsView = ControlsView;
|
||||
self.Drag = Drag;
|
||||
self.style = loadCss('/style/controls.css', function() { done(); });
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
this.style.remove();
|
||||
});
|
||||
|
||||
function loadCss(url, done) {
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
link.onload = done;
|
||||
document.head.appendChild(link);
|
||||
return link;
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
this.image = {
|
||||
classList: {
|
||||
add: function() {}
|
||||
}
|
||||
};
|
||||
|
||||
window.URL = window.URL || {};
|
||||
window.URL.createObjectURL = window.URL.createObjectURL || function() {};
|
||||
window.URL.revokeObjectURL = window.URL.revokeObjectURL || function() {};
|
||||
this.sandbox.stub(window.URL, 'createObjectURL').returns('<object-url>');
|
||||
this.sandbox.stub(window.URL, 'revokeObjectURL');
|
||||
|
||||
this.sandbox.stub(window, 'Image', function() { return self.image; });
|
||||
this.sandbox.spy(this.ControlsView.prototype, 'onSwitchTapped');
|
||||
|
||||
this.drag = sinon.createStubInstance(this.Drag);
|
||||
this.view = new this.ControlsView({ drag: this.drag });
|
||||
this.view.els.image = undefined;
|
||||
this.classes = this.view.el.classList;
|
||||
|
||||
this.sandbox.stub(this.view.els.thumbnail);
|
||||
this.sandbox.spy(this.view, 'set');
|
||||
this.sandbox.spy(this.view, 'unset');
|
||||
this.sandbox.spy(this.view, 'emit');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('ControlsView#setThumbnail()', function() {
|
||||
test('Should set the thumbnail image src to the object url', function() {
|
||||
this.view.els.image = this.image;
|
||||
this.view.setThumbnail('<blob>');
|
||||
assert.equal(this.view.els.image.src, '<object-url>');
|
||||
});
|
||||
|
||||
test('Should revokeObjectURL if image is already set', function() {
|
||||
this.view.setThumbnail('<blob-1>');
|
||||
sinon.assert.notCalled(window.URL.revokeObjectURL);
|
||||
|
||||
this.view.setThumbnail('<blob-2>');
|
||||
sinon.assert.calledWith(window.URL.revokeObjectURL, '<object-url>');
|
||||
});
|
||||
|
||||
test('Should set thumbnail to `true`', function() {
|
||||
this.view.setThumbnail('<blob>');
|
||||
sinon.assert.calledWith(this.view.set, 'thumbnail', true);
|
||||
});
|
||||
|
||||
test('Should create an Image and add it to the thmbnail container', function() {
|
||||
this.view.setThumbnail('<blob>');
|
||||
sinon.assert.called(window.Image);
|
||||
sinon.assert.called(this.view.els.thumbnail.appendChild, this.image);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#set()', function() {
|
||||
test('Should add the class if only one argument is given', function() {
|
||||
this.view.set('key');
|
||||
assert.isTrue(this.classes.contains('key'));
|
||||
});
|
||||
|
||||
test('Should add a key/value class if a truthy value is given', function() {
|
||||
this.view.set('key', 'val');
|
||||
assert.isTrue(this.classes.contains('key-val'));
|
||||
});
|
||||
|
||||
test('Should remove the previous class when the value is changed', function() {
|
||||
this.view.set('key', 'val');
|
||||
assert.isTrue(this.classes.contains('key-val'));
|
||||
|
||||
this.view.set('key', 'val2');
|
||||
assert.isFalse(this.classes.contains('key-val1'));
|
||||
assert.isTrue(this.classes.contains('key-val2'));
|
||||
});
|
||||
|
||||
test('Should only accept string keys', function() {
|
||||
this.view.set(false);
|
||||
assert.isFalse(this.classes.contains('false'));
|
||||
this.view.set(0);
|
||||
assert.isFalse(this.classes.contains('0'));
|
||||
});
|
||||
|
||||
test('Should remove the class for any falsy values', function() {
|
||||
this.view.set('key');
|
||||
assert.isTrue(this.classes.contains('key'));
|
||||
this.view.set('key', undefined);
|
||||
assert.isFalse(this.classes.contains('key'));
|
||||
|
||||
this.view.set('key');
|
||||
assert.isTrue(this.classes.contains('key'));
|
||||
this.view.set('key', 0);
|
||||
assert.isFalse(this.classes.contains('key'));
|
||||
|
||||
this.view.set('key');
|
||||
assert.isTrue(this.classes.contains('key'));
|
||||
this.view.set('key', null);
|
||||
assert.isFalse(this.classes.contains('key'));
|
||||
|
||||
this.view.set('key');
|
||||
assert.isTrue(this.classes.contains('key'));
|
||||
this.view.set('key', '');
|
||||
assert.isFalse(this.classes.contains('key'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#setScreenReaderVisible()', function() {
|
||||
test('hide the view from screen reader', function() {
|
||||
this.view.setScreenReaderVisible(false);
|
||||
assert.equal(this.view.el.getAttribute('aria-hidden'), 'true');
|
||||
});
|
||||
|
||||
test('show the view to the screen reader', function() {
|
||||
this.view.el.setAttribute('aria-hidden', true);
|
||||
this.view.setScreenReaderVisible(true);
|
||||
assert.equal(this.view.el.getAttribute('aria-hidden'), 'false');
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#enable()', function() {
|
||||
test('Should add an enabled class if no value is given', function() {
|
||||
this.view.enable();
|
||||
assert.isTrue(this.classes.contains('enabled'));
|
||||
});
|
||||
|
||||
test('Should add a key-enabled class if key is given', function() {
|
||||
this.view.enable('key');
|
||||
assert.isTrue(this.classes.contains('key-enabled'));
|
||||
});
|
||||
|
||||
test('Should remove \'disabled\' class', function() {
|
||||
this.classes.add('disabled');
|
||||
this.view.enable();
|
||||
assert.isFalse(this.classes.contains('disabled'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#disabled()', function() {
|
||||
test('Should add an \'disables\' class if no value is given', function() {
|
||||
this.view.disable();
|
||||
assert.isTrue(this.classes.contains('disabled'));
|
||||
});
|
||||
|
||||
test('Should add a key-enabled class if key is given', function() {
|
||||
this.view.disable('key');
|
||||
assert.isTrue(this.classes.contains('key-disabled'));
|
||||
});
|
||||
|
||||
test('Should remove \'enabled\' class', function() {
|
||||
this.classes.add('enabled');
|
||||
this.view.disable();
|
||||
assert.isFalse(this.classes.contains('enabled'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#onSwitchTapped()', function() {
|
||||
setup(function() {
|
||||
this.event = {
|
||||
preventDefault: sinon.spy(),
|
||||
stopPropagation: sinon.spy()
|
||||
};
|
||||
this.spy = this.ControlsView.prototype.onSwitchTapped;
|
||||
this.view.enable();
|
||||
});
|
||||
|
||||
test('It emits a `modechanged` event', function() {
|
||||
this.view.onSwitchTapped(this.event);
|
||||
sinon.assert.calledWith(this.view.emit, 'modechanged');
|
||||
});
|
||||
|
||||
test('It prevents default to stop the event becoming a click', function() {
|
||||
this.view.onSwitchTapped(this.event);
|
||||
sinon.assert.called(this.event.preventDefault);
|
||||
});
|
||||
|
||||
test('It is debounced to defend against button bashing', function() {
|
||||
this.view.setupSwitch();
|
||||
var callback = this.drag.on.withArgs('tapped').args[0][1];
|
||||
|
||||
callback(this.event);
|
||||
callback(this.event);
|
||||
callback(this.event);
|
||||
callback(this.event);
|
||||
|
||||
sinon.assert.calledOnce(this.spy);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#onSwitchSnapped()', function() {
|
||||
setup(function() {
|
||||
this.view.set('mode', 'picture');
|
||||
});
|
||||
|
||||
test('It fires \'modechanged\' when the switch changes position', function() {
|
||||
|
||||
// Didn't change
|
||||
this.view.onSwitchSnapped({ x: 'left' });
|
||||
assert.isFalse(this.view.emit.calledWith('modechanged'));
|
||||
|
||||
// Changed
|
||||
this.view.onSwitchSnapped({ x: 'right' });
|
||||
assert.isTrue(this.view.emit.calledWith('modechanged'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('ControlsView#suspendModeSwitch()', function() {
|
||||
test('It ignores user input on switch when suspended', function() {
|
||||
this.view.suspendModeSwitch(true);
|
||||
assert.ok(this.view.set.calledWith('switch-toggle-disabled'));
|
||||
});
|
||||
|
||||
test('It accepts user input on switch when unsuspended', function() {
|
||||
this.view.suspendModeSwitch(false);
|
||||
assert.ok(this.view.unset.calledWith('switch-toggle-disabled'));
|
||||
});
|
||||
});
|
||||
|
||||
test('The switch should appear in the video position when set before the view is in the DOM', function() {
|
||||
var view = new this.ControlsView();
|
||||
view.setMode('video');
|
||||
view.appendTo(document.body);
|
||||
assert.equal(view.drag.handle.el.style.transform, 'translate(64px, 0px)');
|
||||
assert.equal(view.els.switch.getAttribute('data-l10n-id'),
|
||||
'video-mode-button');
|
||||
});
|
||||
|
||||
test('ControlsView#localize', function() {
|
||||
var view = new this.ControlsView();
|
||||
view.localize();
|
||||
for (var el in view.elsL10n) {
|
||||
assert.equal(view.els[el].getAttribute('data-l10n-id'), view.elsL10n[el]);
|
||||
}
|
||||
assert.equal(view.els.switch.getAttribute('data-l10n-id'),
|
||||
'picture-mode-button');
|
||||
});
|
||||
|
||||
test('ControlsView#setCaptureLabel', function() {
|
||||
var view = new this.ControlsView();
|
||||
[true, false].forEach(function(recording) {
|
||||
view.setCaptureLabel(recording);
|
||||
assert.equal(view.els.capture.getAttribute('data-l10n-id'),
|
||||
recording ? 'stop-capture-button' : 'capture-button');
|
||||
});
|
||||
});
|
||||
});
|
||||
36
gaia/apps/camera/test/unit/views/face_test.js
Executable file
36
gaia/apps/camera/test/unit/views/face_test.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
suite('views/face', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/face'], function(FaceView) {
|
||||
self.FaceView = FaceView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
// Shortcuts
|
||||
this.view = new this.FaceView();
|
||||
|
||||
});
|
||||
|
||||
suite('FaceView#setPosition()', function() {
|
||||
test('Should set top left css values', function() {
|
||||
this.view.setPosition(42, 101);
|
||||
assert.equal(this.view.el.style.left, '42px');
|
||||
assert.equal(this.view.el.style.top, '101px');
|
||||
});
|
||||
});
|
||||
|
||||
suite('FaceView#setDiameter()', function() {
|
||||
test('Should set width height css values', function() {
|
||||
this.view.setDiameter(50);
|
||||
assert.equal(this.view.el.style.width, '50px');
|
||||
assert.equal(this.view.el.style.height, '50px');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
75
gaia/apps/camera/test/unit/views/faces_test.js
Executable file
75
gaia/apps/camera/test/unit/views/faces_test.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
suite('views/faces', function() {
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/faces'], function(FacesView) {
|
||||
self.FacesView = FacesView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
this.FaceView = sinon.spy();
|
||||
this.FaceView.prototype = {
|
||||
FaceView: sinon.spy(),
|
||||
hide: sinon.spy(),
|
||||
appendTo: sinon.spy(),
|
||||
setPosition: sinon.spy(),
|
||||
setDiameter: sinon.spy(),
|
||||
show: sinon.spy()
|
||||
};
|
||||
// Shortcuts
|
||||
this.view = new this.FacesView({
|
||||
FaceView: this.FaceView
|
||||
});
|
||||
});
|
||||
|
||||
suite('FacesView#createFaces()', function() {
|
||||
test('Should create Face Views for the given number of faces', function() {
|
||||
this.view.createFaces(10);
|
||||
assert.ok(this.view.faces.length === 10);
|
||||
this.view.createFaces(5);
|
||||
assert.ok(this.view.faces.length === 15);
|
||||
});
|
||||
});
|
||||
|
||||
suite('FacesView#renderFace()', function() {
|
||||
test('Should show and configure the face view based on the face object', function() {
|
||||
var face = {
|
||||
x: 320,
|
||||
y: 400,
|
||||
diameter: 300
|
||||
};
|
||||
var faceView = new this.FaceView();
|
||||
this.view.renderFace(face, faceView);
|
||||
assert.ok(faceView.setPosition.called);
|
||||
assert.ok(faceView.setDiameter.called);
|
||||
assert.ok(faceView.show.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('FacesView#hideFaces()', function() {
|
||||
test('Should call hide once for each face view', function() {
|
||||
var faceView = {
|
||||
hide: sinon.spy()
|
||||
};
|
||||
this.view.faces = [faceView, faceView, faceView];
|
||||
this.view.hideFaces();
|
||||
assert.ok(faceView.hide.calledThrice);
|
||||
});
|
||||
});
|
||||
|
||||
suite('FacesView#clear()', function() {
|
||||
test('Should remove faces DOM elements and reset faces', function() {
|
||||
this.view.el.removeChild = sinon.spy();
|
||||
this.view.faces = ['face', 'face', 'face'];
|
||||
this.view.clear();
|
||||
assert.ok(this.view.el.removeChild.calledThrice);
|
||||
assert.ok(this.view.faces.length === 0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
45
gaia/apps/camera/test/unit/views/hud_test.js
Executable file
45
gaia/apps/camera/test/unit/views/hud_test.js
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
suite('views/hud', function() {
|
||||
'use strict';
|
||||
|
||||
var view;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/hud'], function(HudView) {
|
||||
self.HudView = HudView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
view = new this.HudView();
|
||||
});
|
||||
|
||||
test('HudView#setFlashMode()', function() {
|
||||
[{ title: 'flash-on', icon: 'flash-on' },
|
||||
{ title: 'flash-off', icon: 'flash-off' },
|
||||
{ title: 'flash-auto', icon: 'flash-auto' }].forEach(function(mode) {
|
||||
view.setFlashMode(mode);
|
||||
assert.equal(view.els.flash.dataset.icon, mode.icon);
|
||||
assert.equal(view.els.flash.getAttribute('data-l10n-id'),
|
||||
mode.title + '-button');
|
||||
});
|
||||
});
|
||||
|
||||
test('HudView#setCamera()', function() {
|
||||
[{ title: 'toggle-camera-rear', icon: 'toggle-camera-rear' },
|
||||
{ title: 'toggle-camera-front', icon: 'toggle-camera-front' }].forEach(
|
||||
function(mode) {
|
||||
view.setCamera(mode);
|
||||
assert.equal(view.els.camera.dataset.icon, mode.icon);
|
||||
assert.equal(view.els.camera.getAttribute('data-l10n-id'),
|
||||
mode.title + '-button');
|
||||
});
|
||||
});
|
||||
|
||||
test('HudView#setMenuLabel()', function() {
|
||||
view.els.settings.removeAttribute('data-l10n-id');
|
||||
view.setMenuLabel();
|
||||
assert.equal(view.els.settings.getAttribute('data-l10n-id'), 'menu-button');
|
||||
});
|
||||
});
|
||||
178
gaia/apps/camera/test/unit/views/notification_test.js
Executable file
178
gaia/apps/camera/test/unit/views/notification_test.js
Executable file
|
|
@ -0,0 +1,178 @@
|
|||
suite('views/notification', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/notification'], function(NotificationView) {
|
||||
self.NotificationView = NotificationView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.view = new this.NotificationView();
|
||||
this.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
suite('NotificationView#display()', function() {
|
||||
suite('Temporary', function() {
|
||||
test('Should add the item to the view', function() {
|
||||
assert.equal(this.view.el.getAttribute('role'), 'presentation');
|
||||
this.view.display({ text: 'l10nId' });
|
||||
var child = this.view.el.children[0];
|
||||
assert.ok(child);
|
||||
assert.equal(child.getAttribute('role'), 'status');
|
||||
assert.equal(child.getAttribute('aria-live'), 'assertive');
|
||||
});
|
||||
|
||||
test('Should add `text` as textContent', function() {
|
||||
this.view.display({ text: 'l10nId' });
|
||||
var child = this.view.el.children[0];
|
||||
assert.equal(child.firstChild.getAttribute('data-l10n-id'), 'l10nId');
|
||||
assert.equal(child.getAttribute('role'), 'status');
|
||||
assert.equal(child.getAttribute('aria-live'), 'assertive');
|
||||
});
|
||||
|
||||
test('Should assign className to the item element', function() {
|
||||
this.view.display({ text: 'l10nId', className: 'bar' });
|
||||
var child = this.view.el.children[0];
|
||||
assert.isTrue(child.classList.contains('bar'));
|
||||
assert.equal(child.getAttribute('role'), 'status');
|
||||
assert.equal(child.getAttribute('aria-live'), 'assertive');
|
||||
});
|
||||
|
||||
test('Should remove the last non-persistent item that is in the way', function() {
|
||||
this.view.display({ text: 'l10nId' });
|
||||
var first = this.view.el.children[0];
|
||||
|
||||
assert.ok(first);
|
||||
assert.equal(first.firstChild.getAttribute('data-l10n-id'), 'l10nId');
|
||||
|
||||
this.view.display({ text: 'l10nId2' });
|
||||
var second = this.view.el.children[0];
|
||||
|
||||
assert.ok(second);
|
||||
assert.equal(first.parentNode, null, 'not in dom');
|
||||
assert.equal(second.firstChild.getAttribute('data-l10n-id'),
|
||||
'l10nId2');
|
||||
});
|
||||
|
||||
test('Should auto clear notification after 3s', function() {
|
||||
this.view.display({ text: 'l10nId' });
|
||||
var first = this.view.el.children[0];
|
||||
|
||||
assert.ok(first);
|
||||
assert.equal(first.firstChild.getAttribute('data-l10n-id'), 'l10nId');
|
||||
|
||||
this.clock.tick(1000);
|
||||
assert.ok(first.parentNode, 'is in dom');
|
||||
|
||||
this.clock.tick(1000);
|
||||
assert.ok(first.parentNode, 'is in dom');
|
||||
|
||||
this.clock.tick(1000);
|
||||
assert.equal(first.parentNode, null, 'not in dom');
|
||||
});
|
||||
|
||||
test('Should not mutate the passed object', function() {
|
||||
var options = { text: 'l10nId' };
|
||||
|
||||
this.view.display(options);
|
||||
|
||||
assert.equal(Object.keys(options).length, 1);
|
||||
assert.equal(options.text, 'l10nId');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Persistent', function() {
|
||||
test('Should remain in the DOM until cleared', function() {
|
||||
var id = this.view.display({ text: 'l10nId', persistent: true });
|
||||
var el = this.view.el.children[0];
|
||||
|
||||
assert.ok(el.parentNode, 'is in dom');
|
||||
|
||||
this.clock.tick(1000);
|
||||
assert.ok(el.parentNode, 'is in dom');
|
||||
|
||||
this.clock.tick(3000);
|
||||
assert.ok(el.parentNode, 'is in dom');
|
||||
|
||||
this.view.clear(id);
|
||||
assert.ok(!el.parentNode, 'not in dom');
|
||||
});
|
||||
|
||||
test('Should hide to show temporary notifications, then return', function() {
|
||||
var els = {};
|
||||
|
||||
this.view.display({ text: 'l10nId', persistent: true, className: 'persistent' });
|
||||
els.persistent = this.view.el.querySelector('.persistent');
|
||||
|
||||
assert.ok(els.persistent.parentNode, 'should be in DOM');
|
||||
assert.isFalse(els.persistent.classList.contains('hidden'));
|
||||
|
||||
this.view.display({ text: 'l10nId', className: 'temporary' });
|
||||
els.temporary = this.view.el.querySelector('.temporary');
|
||||
|
||||
assert.ok(els.temporary.parentNode, 'should be in DOM');
|
||||
assert.isTrue(els.persistent.classList.contains('hidden'));
|
||||
|
||||
this.clock.tick(1000);
|
||||
assert.isTrue(els.persistent.classList.contains('hidden'));
|
||||
|
||||
this.clock.tick(3000);
|
||||
assert.ok(!els.temporary.parentNode);
|
||||
assert.isFalse(els.persistent.classList.contains('hidden'));
|
||||
});
|
||||
|
||||
test('Should replace first persistent item with second', function() {
|
||||
var els = {};
|
||||
|
||||
this.view.display({ text: 'l10nId', persistent: true, className: 'persistent1' });
|
||||
els.persistent1 = this.view.el.querySelector('.persistent1');
|
||||
|
||||
assert.ok(els.persistent1.parentNode, 'should be in DOM');
|
||||
|
||||
this.view.display({ text: 'l10nId', persistent: true, className: 'persistent2' });
|
||||
els.persistent2 = this.view.el.querySelector('.persistent2');
|
||||
|
||||
assert.ok(els.persistent2.parentNode, 'should be in DOM');
|
||||
assert.ok(!els.persistent1.parentNode, 'should not be in DOM');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('NotificationView#clear()', function() {
|
||||
test('Should be able to clear notifications early', function() {
|
||||
var id = this.view.display({ text: 'l10nId' });
|
||||
var el = this.view.el.children[0];
|
||||
|
||||
assert.ok(el.parentNode);
|
||||
this.view.clear(id);
|
||||
assert.equal(el.parentNode, null);
|
||||
});
|
||||
|
||||
test('Should not throw when attempting to clear a notification twice', function() {
|
||||
var id = this.view.display({ text: 'l10nId' });
|
||||
var el = this.view.el.children[0];
|
||||
|
||||
assert.ok(el.parentNode);
|
||||
this.view.clear(id);
|
||||
assert.equal(el.parentNode, null);
|
||||
this.view.clear(id);
|
||||
});
|
||||
|
||||
test('Should show hidden persistent notification', function() {
|
||||
var persistent = this.view.display({ text: 'l10nId', persistent: true });
|
||||
var els = {};
|
||||
|
||||
els.persistent = this.view.el.children[0];
|
||||
assert.ok(els.persistent.parentNode, 'is in the dom');
|
||||
|
||||
var temporary = this.view.display({ text: 'l10nId' });
|
||||
assert.isTrue(els.persistent.classList.contains('hidden'), 'is hidden');
|
||||
|
||||
this.view.clear(temporary);
|
||||
assert.isFalse(els.persistent.classList.contains('hidden'), 'is not hidden');
|
||||
});
|
||||
});
|
||||
});
|
||||
52
gaia/apps/camera/test/unit/views/overlay_test.js
Executable file
52
gaia/apps/camera/test/unit/views/overlay_test.js
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
suite('views/overlay', function() {
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'views/overlay'
|
||||
], function(OverlayView) {
|
||||
self.OverlayView = OverlayView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.view = new this.OverlayView({
|
||||
type: 'unavailable',
|
||||
closable: false
|
||||
});
|
||||
});
|
||||
|
||||
suite('OverlayView#getOverlayData()', function() {
|
||||
test('Should return correct data for \'unavailable\'', function() {
|
||||
var output = this.view.getData('unavailable');
|
||||
assert.equal(output.title, 'nocard2-title');
|
||||
assert.equal(output.body, 'nocard3-text');
|
||||
});
|
||||
|
||||
test('Should return correct data for \'nospace\'', function() {
|
||||
var output = this.view.getData('nospace');
|
||||
assert.equal(output.title, 'nospace2-title');
|
||||
assert.equal(output.body, 'nospace2-text');
|
||||
});
|
||||
|
||||
test('Should return correct data for \'shared\'', function() {
|
||||
var output = this.view.getData('shared');
|
||||
assert.equal(output.title, 'pluggedin2-title');
|
||||
assert.equal(output.body, 'pluggedin2-text');
|
||||
});
|
||||
|
||||
test('Should return correct data for \'request-fail\'', function() {
|
||||
var output = this.view.getData('request-fail');
|
||||
assert.equal(output.title, 'camera-unavailable-title');
|
||||
assert.equal(output.body, 'camera-unavailable-text');
|
||||
});
|
||||
|
||||
test('Should return false for unknown key', function() {
|
||||
var output = this.view.getData('something-not-known');
|
||||
assert.equal(output, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
95
gaia/apps/camera/test/unit/views/preview-gallery_test.js
Executable file
95
gaia/apps/camera/test/unit/views/preview-gallery_test.js
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
suite('views/preview-gallery', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/preview-gallery'], function(previewGalleryView) {
|
||||
self.previewGalleryView = previewGalleryView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.view = new this.previewGalleryView();
|
||||
this.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
suite('previewGalleryView#showOptionsMenu()', function() {
|
||||
test('Should show the option menu', function() {
|
||||
this.view.showOptionsMenu();
|
||||
assert.equal(this.view.optionsMenuContainer.children[0].getAttribute(
|
||||
'data-type'), 'action');
|
||||
});
|
||||
});
|
||||
|
||||
suite('previewGalleryView#onFrameWheel', function() {
|
||||
var mockWheelRight = {
|
||||
type: 'wheel', deltaMode: 2, DOM_DELTA_PAGE: 2, deltaX: 1
|
||||
}, mockWheelLeft = {
|
||||
type: 'wheel', deltaMode: 2, DOM_DELTA_PAGE: 2, deltaX: -1
|
||||
}, mockWheelDown = {
|
||||
type: 'wheel', deltaMode: 2, DOM_DELTA_PAGE: 2, deltaY: 1
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
sinon.spy(this.view, 'emit');
|
||||
sinon.stub(this.view, 'onVideoPaused');
|
||||
});
|
||||
|
||||
test('wheel right', function() {
|
||||
this.view.onFrameWheel(mockWheelRight);
|
||||
assert.isTrue(this.view.emit.calledWith('swipe', 'left'));
|
||||
sinon.assert.notCalled(this.view.onVideoPaused);
|
||||
});
|
||||
|
||||
test('wheel left', function() {
|
||||
this.view.onFrameWheel(mockWheelLeft);
|
||||
assert.isTrue(this.view.emit.calledWith('swipe', 'right'));
|
||||
sinon.assert.notCalled(this.view.onVideoPaused);
|
||||
});
|
||||
|
||||
test('wheel right when video is playing', function() {
|
||||
this.view.videoPlaying = true;
|
||||
this.view.onFrameWheel(mockWheelRight);
|
||||
assert.isTrue(this.view.emit.calledWith('swipe', 'left'));
|
||||
sinon.assert.calledOnce(this.view.onVideoPaused);
|
||||
});
|
||||
|
||||
test('wheel down', function() {
|
||||
this.view.onFrameWheel(mockWheelDown);
|
||||
sinon.assert.notCalled(this.view.emit);
|
||||
});
|
||||
});
|
||||
|
||||
suite('previewGalleryView#updateCountText', function() {
|
||||
setup(function() {
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
test('Accessible labelling', function() {
|
||||
var total = 1, current = 3;
|
||||
this.view.updateCountText(current, total);
|
||||
assert.equal(this.view.els.mediaFrame.getAttribute('data-l10n-id'),
|
||||
'media-frame');
|
||||
assert.deepEqual(JSON.parse(this.view.els.mediaFrame.getAttribute(
|
||||
'data-l10n-args')), { total: total, current: current });
|
||||
});
|
||||
});
|
||||
|
||||
suite('Options menu', function() {
|
||||
test('showOptionsMenu', function() {
|
||||
assert.isFalse(this.view.el.classList.contains('action-menu'));
|
||||
this.view.showOptionsMenu();
|
||||
assert.ok(this.view.el.classList.contains('action-menu'));
|
||||
});
|
||||
|
||||
test('hideOptionsMenu', function() {
|
||||
this.view.optionsMenuContainer = document.createElement('div');
|
||||
this.view.el.appendChild(this.view.optionsMenuContainer);
|
||||
this.view.el.classList.add('action-menu');
|
||||
|
||||
this.view.hideOptionsMenu();
|
||||
assert.isFalse(this.view.el.classList.contains('action-menu'));
|
||||
});
|
||||
});
|
||||
});
|
||||
58
gaia/apps/camera/test/unit/views/setting-options_test.js
Executable file
58
gaia/apps/camera/test/unit/views/setting-options_test.js
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
suite('views/setting-options', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'views/setting-options'
|
||||
], function(SettingOptionsView) {
|
||||
self.SettingOptionsView = SettingOptionsView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
this.view = new this.SettingOptionsView({
|
||||
model: { on: sinon.spy(), get: sinon.spy() },
|
||||
l10n: {
|
||||
formatValue: function(id) {
|
||||
return Promise.resolve(id);
|
||||
},
|
||||
}
|
||||
});
|
||||
this.view.els.ul = document.createElement('ul');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('SettingOptionsView#renderOption()', function() {
|
||||
var option = {
|
||||
key: 'key',
|
||||
title: 'title'
|
||||
};
|
||||
|
||||
test('localizable not selected', function() {
|
||||
this.view.renderOption(true, option);
|
||||
var rendered = this.view.els.key;
|
||||
|
||||
assert.equal(rendered.getAttribute('role'), 'option');
|
||||
assert.equal(rendered.getAttribute('data-l10n-id'), 'title');
|
||||
assert.isNull(rendered.getAttribute('aria-selected'));
|
||||
});
|
||||
|
||||
test('not localizable selected', function() {
|
||||
// Make current option selected.
|
||||
this.view.selectedKey = option.key;
|
||||
|
||||
this.view.renderOption(false, option);
|
||||
var rendered = this.view.els.key;
|
||||
|
||||
assert.equal(rendered.getAttribute('role'), 'option');
|
||||
assert.equal(rendered.textContent, 'title');
|
||||
assert.equal(rendered.getAttribute('aria-selected'), 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
49
gaia/apps/camera/test/unit/views/setting_test.js
Executable file
49
gaia/apps/camera/test/unit/views/setting_test.js
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
suite('views/setting', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'views/setting'
|
||||
], function(SettingView) {
|
||||
self.SettingView = SettingView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
this.model = {
|
||||
on: sinon.spy(),
|
||||
selected: sinon.stub().returns({ title: 'title' }),
|
||||
get: sinon.stub().returns({ title: 'title', value: 'value' })
|
||||
};
|
||||
this.model.get.withArgs('title').returns('title');
|
||||
this.view = new this.SettingView({ model: this.model });
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('SettingView#render()', function() {
|
||||
setup(function() {
|
||||
this.view.l10n = {
|
||||
formatValue: function(id) {
|
||||
return Promise.resolve(id);
|
||||
},
|
||||
setAttributes: sinon.spy()
|
||||
};
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
test('render setting accessibility', function(done) {
|
||||
assert.equal(this.view.el.getAttribute('role'), 'option');
|
||||
Promise.resolve().then(() => {
|
||||
assert.isTrue(this.view.l10n.setAttributes.calledWith(this.view.el,
|
||||
'setting-option-title', { value: 'title' }));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
78
gaia/apps/camera/test/unit/views/settings_test.js
Executable file
78
gaia/apps/camera/test/unit/views/settings_test.js
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
suite('views/settings', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs([
|
||||
'views/settings',
|
||||
'views/setting-options'
|
||||
], function(SettingsView, OptionsView) {
|
||||
self.SettingsView = SettingsView;
|
||||
self.OptionsView = OptionsView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var self = this;
|
||||
this.sandbox = sinon.sandbox.create();
|
||||
|
||||
var OptionsView = sinon.spy(function() {
|
||||
var view = sinon.createStubInstance(self.OptionsView);
|
||||
view.render.returns(view);
|
||||
view.appendTo.returns(view);
|
||||
view.on.returns(view);
|
||||
return view;
|
||||
});
|
||||
|
||||
this.view = new this.SettingsView({ OptionsView: OptionsView });
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sandbox.restore();
|
||||
});
|
||||
|
||||
suite('SettingsView#onClick()', function() {
|
||||
test('Should not let clicks propagate', function() {
|
||||
var spy = sinon.spy();
|
||||
this.view.onClick({ stopPropagation: spy });
|
||||
assert.isTrue(spy.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsView#showSetting()', function() {
|
||||
setup(function() {
|
||||
this.model = {};
|
||||
this.sandbox.spy(this.view, 'showPane');
|
||||
this.view.showSetting(this.model);
|
||||
});
|
||||
|
||||
test('Should create and append a new OptionsView', function() {
|
||||
assert.isTrue(this.view.OptionsView.called);
|
||||
assert.isTrue(this.view.optionsView.appendTo.called);
|
||||
});
|
||||
|
||||
test('Should pass the model into the OptionsView', function() {
|
||||
var options = this.view.OptionsView.args[0][0];
|
||||
assert.equal(options.model, this.model);
|
||||
});
|
||||
|
||||
test('Should show pane 2', function() {
|
||||
assert.isTrue(this.view.showPane.calledWith(2));
|
||||
});
|
||||
});
|
||||
|
||||
suite('SettingsView#goBack()', function() {
|
||||
test('Should destroy the options view (sync)', function() {
|
||||
var options = this.view.optionsView = { destroy: sinon.spy() };
|
||||
this.view.goBack();
|
||||
assert.isTrue(options.destroy.called);
|
||||
});
|
||||
|
||||
test('Should switch back to pane 1', function() {
|
||||
sinon.spy(this.view, 'showPane');
|
||||
this.view.goBack();
|
||||
assert.isTrue(this.view.showPane.calledWith(1));
|
||||
});
|
||||
});
|
||||
});
|
||||
39
gaia/apps/camera/test/unit/views/timer_test.js
Executable file
39
gaia/apps/camera/test/unit/views/timer_test.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
suite('views/countdown', function() {
|
||||
'use strict';
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/countdown'], function(CountdownView) {
|
||||
self.CountdownView = CountdownView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.countdown = new this.CountdownView();
|
||||
this.countdown.emit = sinon.spy();
|
||||
});
|
||||
|
||||
suite('CountdownView#set()', function() {
|
||||
test('Should update the text content of the counter', function() {
|
||||
this.countdown.set(1000);
|
||||
assert.ok(this.countdown.el.innerHTML.indexOf(1000) > -1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('CountdownView#reset()', function() {
|
||||
test('Should clear the countdown text', function() {
|
||||
this.countdown.set(1000);
|
||||
assert.isTrue(this.countdown.el.innerHTML.indexOf('1000') > -1);
|
||||
this.countdown.reset();
|
||||
assert.isFalse(this.countdown.el.innerHTML.indexOf('1000') > -1);
|
||||
});
|
||||
|
||||
test('Should remove `immanent` class', function() {
|
||||
this.countdown.setImmanent(true);
|
||||
assert.isTrue(this.countdown.el.classList.contains('immanent'));
|
||||
this.countdown.reset();
|
||||
assert.isFalse(this.countdown.el.classList.contains('immanent'));
|
||||
});
|
||||
});
|
||||
});
|
||||
184
gaia/apps/camera/test/unit/views/viewfinder_test.js
Executable file
184
gaia/apps/camera/test/unit/views/viewfinder_test.js
Executable file
|
|
@ -0,0 +1,184 @@
|
|||
suite('views/viewfinder', function() {
|
||||
'use strict';
|
||||
|
||||
var SENSOR_ANGLE = 90;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var self = this;
|
||||
requirejs(['views/viewfinder'], function(ViewfinderView) {
|
||||
self.ViewfinderView = ViewfinderView;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.viewfinder = new this.ViewfinderView();
|
||||
this.viewfinder.width = 300;
|
||||
this.viewfinder.height = 500;
|
||||
});
|
||||
|
||||
suite('ViewfinderView#updatePreview()', function() {
|
||||
setup(function() {
|
||||
sinon.stub(this.viewfinder, 'updatePreviewMetrics');
|
||||
});
|
||||
|
||||
test('Should prefer this.scaleType if defined', function() {
|
||||
var preview = { width: 1600, height: 900 };
|
||||
var scaleType;
|
||||
|
||||
this.viewfinder.scaleType = 'fit';
|
||||
this.viewfinder.updatePreview(preview, SENSOR_ANGLE);
|
||||
scaleType = this.viewfinder.updatePreviewMetrics.args[0][3];
|
||||
assert.equal(scaleType, 'fit');
|
||||
|
||||
this.viewfinder.scaleType = 'foobar';
|
||||
this.viewfinder.updatePreview(preview, SENSOR_ANGLE);
|
||||
scaleType = this.viewfinder.updatePreviewMetrics.args[1][3];
|
||||
assert.equal(scaleType, 'foobar');
|
||||
});
|
||||
|
||||
test('Should choose \'fill\' scaleType if preview aspect > container aspect', function() {
|
||||
var scaleType;
|
||||
|
||||
this.viewfinder.scaleType = undefined;
|
||||
|
||||
this.viewfinder.updatePreview({ width: 1600, height: 900 }, SENSOR_ANGLE);
|
||||
scaleType = this.viewfinder.updatePreviewMetrics.args[0][3];
|
||||
assert.equal(scaleType, 'fill');
|
||||
|
||||
this.viewfinder.updatePreview({ width: 400, height: 300 }, SENSOR_ANGLE);
|
||||
scaleType = this.viewfinder.updatePreviewMetrics.args[1][3];
|
||||
assert.equal(scaleType, 'fit');
|
||||
});
|
||||
|
||||
test('Should add negative scale style if `mirror` argument is true', function() {
|
||||
var preview = { width: 1600, height: 900 };
|
||||
|
||||
this.viewfinder.updatePreview(preview, SENSOR_ANGLE, true);
|
||||
assert.isTrue(this.viewfinder.els.videoContainer.style.transform.indexOf('scale(-1') === -1);
|
||||
|
||||
this.viewfinder.updatePreview(preview, SENSOR_ANGLE, false);
|
||||
assert.isFalse(this.viewfinder.els.videoContainer.style.transform.indexOf('scale(-1') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ViewfinderView#updatePreviewMetrics()', function() {
|
||||
setup(function() {
|
||||
this.viewfinder.container = {
|
||||
width: this.viewfinder.height,
|
||||
height: this.viewfinder.width
|
||||
};
|
||||
|
||||
this.container = this.viewfinder.container;
|
||||
});
|
||||
|
||||
test('Should set a \'scale-type\' attribute', function() {
|
||||
var previewSize = { width: 400, height: 300 };
|
||||
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fill');
|
||||
assert.equal(this.viewfinder.el.getAttribute('scale-type'), 'fill');
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fit');
|
||||
assert.equal(this.viewfinder.el.getAttribute('scale-type'), 'fit');
|
||||
});
|
||||
|
||||
suite('\'fill\'', function() {
|
||||
test('Should match frame width to container width when preview aspect > container aspect', function() {
|
||||
var previewSize = { width: 1600, height: 900 };
|
||||
|
||||
// Run test-case
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fill');
|
||||
|
||||
// Frame
|
||||
var frame = {
|
||||
width: parseInt(this.viewfinder.els.frame.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.frame.style.height, 10)
|
||||
};
|
||||
|
||||
// Video container
|
||||
var video = {
|
||||
width: parseInt(this.viewfinder.els.videoContainer.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.videoContainer.style.height, 10)
|
||||
};
|
||||
|
||||
assert.equal(frame.width, this.container.height);
|
||||
assert.ok(frame.height > this.container.width);
|
||||
|
||||
assert.equal(video.height, this.container.height);
|
||||
assert.ok(video.width > this.container.width);
|
||||
});
|
||||
|
||||
test('Should match frame height to container height when preview aspect < container aspect', function() {
|
||||
var previewSize = { width: 400, height: 300 };
|
||||
|
||||
// Run test-case
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fill');
|
||||
|
||||
var frame = {
|
||||
width: parseInt(this.viewfinder.els.frame.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.frame.style.height, 10)
|
||||
};
|
||||
|
||||
var video = {
|
||||
width: parseInt(this.viewfinder.els.videoContainer.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.videoContainer.style.height, 10)
|
||||
};
|
||||
|
||||
assert.ok(frame.width > this.container.height);
|
||||
assert.equal(frame.height, this.container.width);
|
||||
|
||||
assert.ok(video.height > this.container.height);
|
||||
assert.equal(video.width, this.container.width);
|
||||
});
|
||||
});
|
||||
|
||||
suite('\'fit\'', function() {
|
||||
test('Should match frame height to container height when preview aspect > container aspect', function() {
|
||||
var previewSize = { width: 1600, height: 900 };
|
||||
|
||||
// Run test-case
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fit');
|
||||
|
||||
var frame = {
|
||||
width: parseInt(this.viewfinder.els.frame.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.frame.style.height, 10)
|
||||
};
|
||||
|
||||
var video = {
|
||||
width: parseInt(this.viewfinder.els.videoContainer.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.videoContainer.style.height, 10)
|
||||
};
|
||||
|
||||
assert.equal(frame.height, this.container.width);
|
||||
assert.ok(frame.width < this.container.height);
|
||||
|
||||
assert.equal(video.width, this.container.width);
|
||||
assert.ok(video.height < this.container.height);
|
||||
});
|
||||
|
||||
test('Should match frame width to container width when preview aspect < container aspect', function() {
|
||||
var previewSize = { width: 400, height: 300 };
|
||||
|
||||
// Run test-case
|
||||
this.viewfinder.updatePreviewMetrics(previewSize, SENSOR_ANGLE, false, 'fit');
|
||||
|
||||
var frame = {
|
||||
width: parseInt(this.viewfinder.els.frame.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.frame.style.height, 10)
|
||||
};
|
||||
|
||||
var video = {
|
||||
width: parseInt(this.viewfinder.els.videoContainer.style.width, 10),
|
||||
height: parseInt(this.viewfinder.els.videoContainer.style.height, 10)
|
||||
};
|
||||
|
||||
// Check width is equal and height is shorter
|
||||
assert.equal(frame.width, this.container.height);
|
||||
assert.ok(frame.height < this.container.width);
|
||||
|
||||
// Check container width is shorter and container height is equal
|
||||
assert.ok(video.width < this.container.width);
|
||||
assert.equal(video.height, this.container.height);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue