Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
3443 lines
114 KiB
JavaScript
Executable file
3443 lines
114 KiB
JavaScript
Executable file
/* global AppWindow, ScreenLayout, MockService, IconsHelper,
|
|
MocksHelper, BaseModule, MockContextMenu,
|
|
MockAppTransitionController, MockPermissionSettings, DocumentFragment,
|
|
MockAudioChannelController, AppChrome,
|
|
MockWebManifestHelper, MockPromise */
|
|
'use strict';
|
|
|
|
requireApp('system/shared/test/unit/mocks/mock_manifest_helper.js');
|
|
requireApp('system/shared/test/unit/mocks/mock_settings_helper.js');
|
|
requireApp('system/shared/test/unit/mocks/mock_settings_listener.js');
|
|
requireApp('system/test/unit/mock_context_menu.js');
|
|
requireApp('system/test/unit/mock_applications.js');
|
|
requireApp('system/test/unit/mock_app_chrome.js');
|
|
requireApp('system/test/unit/mock_screen_layout.js');
|
|
requireApp('system/test/unit/mock_lazy_loader.js');
|
|
requireApp('system/test/unit/mock_app_transition_controller.js');
|
|
requireApp('system/test/unit/mock_audio_channel_controller.js');
|
|
requireApp('system/shared/test/unit/mocks/mock_service.js');
|
|
requireApp('system/shared/test/unit/mocks/mock_permission_settings.js');
|
|
require('/shared/test/unit/mocks/mock_web_manifest_helper.js');
|
|
require('/shared/test/unit/mocks/mock_promise.js');
|
|
require('/shared/test/unit/mocks/mock_icons_helper.js');
|
|
|
|
var mocksForAppWindow = new MocksHelper([
|
|
'Applications', 'SettingsListener',
|
|
'ManifestHelper', 'WebManifestHelper',
|
|
'ScreenLayout', 'AppChrome', 'IconsHelper',
|
|
'AppTransitionController', 'Service', 'LazyLoader'
|
|
]).init();
|
|
|
|
suite('system/AppWindow', function() {
|
|
var realPermissionSettings;
|
|
mocksForAppWindow.attachTestHelpers();
|
|
|
|
var fakeDOMRequest = {
|
|
onsuccess: function() {},
|
|
onerror: function() {},
|
|
then: function(success, error) {
|
|
this.onsuccess = function(evt) {
|
|
success(evt.target.result);
|
|
};
|
|
this.onerror = function() {
|
|
error();
|
|
};
|
|
}
|
|
};
|
|
|
|
setup(function(done) {
|
|
this.sinon.useFakeTimers();
|
|
|
|
window.Service = MockService;
|
|
|
|
realPermissionSettings = navigator.mozPermissionSettings;
|
|
navigator.mozPermissionSettings = MockPermissionSettings;
|
|
MockPermissionSettings.mSetup();
|
|
|
|
this.sinon.stub(document, 'getElementById', function() {
|
|
return document.createElement('div');
|
|
});
|
|
this.sinon.stub(DocumentFragment.prototype, 'getElementById', function() {
|
|
return document.createElement('div');
|
|
});
|
|
|
|
this.sinon.stub(HTMLElement.prototype, 'querySelector',
|
|
function() {
|
|
return document.createElement('div');
|
|
});
|
|
requireApp('system/js/browser_config_helper.js');
|
|
requireApp('system/js/browser_frame.js');
|
|
requireApp('system/js/base_module.js');
|
|
requireApp('system/js/app_window.js');
|
|
requireApp('system/js/browser_mixin.js', function() {
|
|
this.sinon.stub(BaseModule, 'instantiate', function(name) {
|
|
if (name === 'BrowserContextMenu') {
|
|
return MockContextMenu;
|
|
}
|
|
});
|
|
done();
|
|
}.bind(this));
|
|
});
|
|
|
|
teardown(function() {
|
|
navigator.mozPermissionSettings = realPermissionSettings;
|
|
delete window.Service;
|
|
});
|
|
|
|
var fakeAppConfig1 = {
|
|
url: 'app://www.fake/index.html',
|
|
manifest: {},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
var fakeAppConfig2 = {
|
|
url: 'app://www.fake2/index.html',
|
|
manifest: {},
|
|
manifestURL: 'app://wwww.fake2/ManifestURL',
|
|
origin: 'app://www.fake2'
|
|
};
|
|
|
|
var fakeAppConfig3 = {
|
|
url: 'app://www.fake3/index.html',
|
|
manifest: {},
|
|
manifestURL: 'app://wwww.fake3/ManifestURL',
|
|
origin: 'app://www.fake3'
|
|
};
|
|
|
|
var fakeAppConfig4 = {
|
|
url: 'app://www.fake4/index.html',
|
|
manifest: {},
|
|
origin: 'app://www.fake4'
|
|
};
|
|
|
|
var fakePrivateConfig = {
|
|
url: 'http://www.private/index.html',
|
|
manifest: null,
|
|
origin: 'http://www.private',
|
|
isPrivate: true
|
|
};
|
|
|
|
var fakeAppConfigBackground = {
|
|
url: 'app://www.fakebackground/index.html',
|
|
manifest: {},
|
|
origin: 'app://www.fakebackground',
|
|
stayBackground: true
|
|
};
|
|
|
|
var fakeAppConfigWithIcon = {
|
|
url: 'app://www.fake4/index.html',
|
|
manifest: {
|
|
icons: {
|
|
'100': '/xxxx.png'
|
|
}
|
|
},
|
|
manifestURL: 'app://wwww.fake4/ManifestURL',
|
|
origin: 'app://www.fake4'
|
|
};
|
|
|
|
var fakeAppConfigWithMultiIcon = {
|
|
url: 'app://www.fake4/index.html',
|
|
manifest: {
|
|
icons: {
|
|
'30': '/foo.jpg',
|
|
'60': '/bar.jpg',
|
|
'120': '/xxxx.png',
|
|
'240': '/yyyy.ico',
|
|
'300': '/lol.gif',
|
|
'400': '/xd.img'
|
|
}
|
|
},
|
|
manifestURL: 'app://wwww.fake4/ManifestURL',
|
|
origin: 'app://www.fake4'
|
|
};
|
|
|
|
var fakeSearchAppConfig = {
|
|
url: 'app://search.gaiamobile.org/newtab.html',
|
|
manifest: {
|
|
role: 'search'
|
|
},
|
|
manifestURL: 'app://search.gaiamobile.org/ManifestURL',
|
|
origin: 'app://search.gaiamobile.org'
|
|
};
|
|
|
|
var fakeWrapperConfig = {
|
|
url: 'http://www.fake5/index.html',
|
|
origin: 'http://www.fake5',
|
|
title: 'Fakebook'
|
|
};
|
|
|
|
var fakeChromeConfigWithoutNavigation = {
|
|
url: 'http://www.fakeChrome2/index.html',
|
|
origin: 'http://www.fakeChrome2',
|
|
manifest: {
|
|
chrome: { 'navigation': false }
|
|
}
|
|
};
|
|
|
|
var fakeChromeConfigWithNavigationBar = {
|
|
url: 'http://www.fakeChrome2/index.html',
|
|
origin: 'http://www.fakeChrome2',
|
|
manifest: {
|
|
chrome: { 'navigation': false, 'bar': true }
|
|
}
|
|
};
|
|
|
|
var fakeAppConfigCertified = {
|
|
url: 'app://www.fakecertified/index.html',
|
|
manifest: {
|
|
type: 'certified'
|
|
},
|
|
origin: 'app://www.fake4'
|
|
};
|
|
|
|
var fakeAppWithName = {
|
|
url: 'http://fakeapp.com/index.html',
|
|
manifest: {
|
|
name: 'Fake App Name'
|
|
},
|
|
origin: 'http://fakeapp.com'
|
|
};
|
|
|
|
var fakeInputAppConfig = {
|
|
url: 'app://www.fakeinput/index.html',
|
|
manifest: {},
|
|
manifestURL: 'app://www.fakeinput/ManifestURL',
|
|
origin: 'app://www.fakeinput',
|
|
isInputMethod: true
|
|
};
|
|
|
|
test('Sanity check instances dont share .element', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
assert.isFalse(app1.element === app2.element);
|
|
});
|
|
|
|
test('App created with instanceID', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
assert.isTrue(app1.instanceID !== app2.instanceID);
|
|
});
|
|
|
|
test('Automatically enable navigation for a brower window.', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
assert.equal(app1.config.chrome.scrollable, true);
|
|
});
|
|
|
|
test('setActive', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
this.sinon.stub(app2, '_setActive');
|
|
this.sinon.stub(app1, 'getTopMostWindow').returns(app2);
|
|
app1.setActive(true);
|
|
assert.isTrue(app2._setActive.calledWith(true));
|
|
});
|
|
|
|
suite('Resize', function() {
|
|
var app1;
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
// Inject mozBrowser API to app iframe
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
this.sinon.stub(app1.browser.element, 'addNextPaintListener');
|
|
});
|
|
teardown(function() {});
|
|
|
|
test('Resize in foreground', function(done) {
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
this.sinon.stub(app1, 'reviveBrowser');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.isTrue(app1.resized);
|
|
assert.isTrue(app1.reviveBrowser.called);
|
|
|
|
assert.isTrue(app1.browser.element.addNextPaintListener.calledOnce);
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('Resize in background', function() {
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(false);
|
|
app1.resize();
|
|
assert.isUndefined(app1.resized);
|
|
|
|
assert.isFalse(app1.browser.element.addNextPaintListener.calledOnce);
|
|
});
|
|
|
|
test('Resize if we are fullscreen', function(done) {
|
|
MockService.mockQueryWith('getHeightFor', 480);
|
|
var stubIsFullScreen = this.sinon.stub(app1, 'isFullScreen');
|
|
stubIsFullScreen.returns(true);
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.equal(app1.height, MockService.mockQueryWith('getHeightFor'));
|
|
|
|
assert.isTrue(app1.browser.element.addNextPaintListener.calledOnce);
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('Resize if we are not fullscreen', function(done) {
|
|
MockService.mockQueryWith('getHeightFor', 430);
|
|
var stubIsFullScreen = this.sinon.stub(app1, 'isFullScreen');
|
|
stubIsFullScreen.returns(false);
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.equal(app1.height, MockService.mockQueryWith('getHeightFor'));
|
|
|
|
assert.isTrue(app1.browser.element.addNextPaintListener.calledOnce);
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('Send message to appChrome: w/o keyboard', function(done) {
|
|
MockService.mockQueryWith('keyboardEnabled', false);
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
var stubbroadcast = this.sinon.stub(app1, 'broadcast');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.isTrue(stubbroadcast.calledWith('withoutkeyboard'));
|
|
|
|
assert.isTrue(app1.browser.element.addNextPaintListener.calledOnce);
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('Send message to appChrome: w/ keyboard', function(done) {
|
|
MockService.mockQueryWith('keyboardEnabled', true);
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
var stubbroadcast = this.sinon.stub(app1, 'broadcast');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.isTrue(stubbroadcast.calledWith('withkeyboard'));
|
|
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('Reset the screenshot overlay visibilty', function(done) {
|
|
app1.screenshotOverlay.style.visibility = 'hidden';
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
var p = app1.resize();
|
|
assert.equal(app1.screenshotOverlay.style.visibility, '');
|
|
|
|
assert.isTrue(app1.browser.element.addNextPaintListener.calledOnce);
|
|
app1.browser.element.addNextPaintListener.firstCall.args[0]();
|
|
p.catch((e) => { throw e || 'Should not reject'; }).then(done, done);
|
|
});
|
|
|
|
test('No navigation setting in manifest', function() {
|
|
var spy = this.sinon.spy(window, 'AppChrome');
|
|
new AppWindow(fakeChromeConfigWithoutNavigation); // jshint ignore:line
|
|
assert.isFalse(spy.calledWithNew());
|
|
});
|
|
|
|
test('No navigation setting in manifest - app - appChrome', function() {
|
|
var spy = this.sinon.spy(window, 'AppChrome');
|
|
var aw = new AppWindow(fakeChromeConfigWithoutNavigation);
|
|
assert.isFalse(spy.calledWithNew());
|
|
aw.element.dispatchEvent(new CustomEvent('_opened'));
|
|
assert.isTrue(spy.calledWithNew());
|
|
});
|
|
|
|
test('No navigation setting in manifest - wrapper - appChrome', function() {
|
|
var spy = this.sinon.spy(window, 'AppChrome');
|
|
new AppWindow(fakeWrapperConfig); // jshint ignore:line
|
|
assert.isTrue(spy.calledWithNew());
|
|
});
|
|
|
|
test('Navigation bar in manifest - appChrome', function() {
|
|
var spy = this.sinon.spy(window, 'AppChrome');
|
|
var aw = new AppWindow(fakeChromeConfigWithNavigationBar);
|
|
aw.element.dispatchEvent(new CustomEvent('_opened'));
|
|
assert.isTrue(spy.calledWithNew());
|
|
});
|
|
|
|
test('resize to bottom window thru top most window', function(done) {
|
|
var popups = openPopups(5);
|
|
injectFakeMozBrowserAPI(popups[0].browser.element);
|
|
this.sinon.stub(popups[0].browser.element, 'addNextPaintListener');
|
|
injectFakeMozBrowserAPI(popups[1].browser.element);
|
|
this.sinon.stub(popups[1].browser.element, 'addNextPaintListener');
|
|
injectFakeMozBrowserAPI(popups[2].browser.element);
|
|
this.sinon.stub(popups[2].browser.element, 'addNextPaintListener');
|
|
injectFakeMozBrowserAPI(popups[3].browser.element);
|
|
this.sinon.stub(popups[3].browser.element, 'addNextPaintListener');
|
|
injectFakeMozBrowserAPI(popups[4].browser.element);
|
|
this.sinon.stub(popups[4].browser.element, 'addNextPaintListener');
|
|
var stubAppIsActive = this.sinon.stub(popups[0], 'isActive');
|
|
stubAppIsActive.returns(true);
|
|
var p = popups[0].resize();
|
|
|
|
assert.isTrue(popups[0].browser.element.addNextPaintListener.calledOnce);
|
|
popups[0].browser.element.addNextPaintListener.firstCall.args[0]();
|
|
assert.isTrue(popups[1].browser.element.addNextPaintListener.calledOnce);
|
|
popups[1].browser.element.addNextPaintListener.firstCall.args[0]();
|
|
assert.isTrue(popups[2].browser.element.addNextPaintListener.calledOnce);
|
|
popups[2].browser.element.addNextPaintListener.firstCall.args[0]();
|
|
assert.isTrue(popups[3].browser.element.addNextPaintListener.calledOnce);
|
|
popups[3].browser.element.addNextPaintListener.firstCall.args[0]();
|
|
assert.isTrue(popups[4].browser.element.addNextPaintListener.calledOnce);
|
|
popups[4].browser.element.addNextPaintListener.firstCall.args[0]();
|
|
|
|
p.then(() => undefined, (e) => { throw e || 'Should not reject'; })
|
|
.then(done, done);
|
|
});
|
|
});
|
|
|
|
suite('Render', function() {
|
|
var visibleSpy;
|
|
|
|
setup(function() {
|
|
visibleSpy = this.sinon.stub(AppWindow.prototype, 'setVisible');
|
|
});
|
|
|
|
test('apps with background flag launched in background', function() {
|
|
new AppWindow(fakeAppConfigBackground); // jshint ignore:line
|
|
sinon.assert.calledWith(visibleSpy, false);
|
|
});
|
|
|
|
test('homescreen is launched at background', function() {
|
|
var renderSpy = this.sinon.stub(AppWindow.prototype, 'render');
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
renderSpy.restore();
|
|
app.isHomescreen = true;
|
|
app.render();
|
|
sinon.assert.calledWith(visibleSpy, false);
|
|
});
|
|
});
|
|
|
|
suite('Orientations', function() {
|
|
var fakeAppConfigWithPortraitOrientation = {
|
|
url: 'app://www.fake.portrait/index.html',
|
|
manifest: {
|
|
orientation: 'portrait'
|
|
},
|
|
manifestURL: 'app://www.fake.portrait/ManifestURL',
|
|
origin: 'app://www.fake.portrait'
|
|
};
|
|
var fakeAppConfigWithLandscapeOrientation = {
|
|
url: 'app://www.fake.landscape/index.html',
|
|
manifest: {
|
|
orientation: 'landscape'
|
|
},
|
|
manifestURL: 'app://www.fake.landscape/ManifestURL',
|
|
origin: 'app://www.fake.landscape'
|
|
};
|
|
var fakeAppConfigWithLandscapePrimaryOrientation = {
|
|
url: 'app://www.fake.landscape.primary/index.html',
|
|
manifest: {
|
|
orientation: 'landscape-primary'
|
|
},
|
|
manifestURL: 'app://www.fake.landscape.primary/ManifestURL',
|
|
origin: 'app://www.fake.landscape.primary'
|
|
};
|
|
var fakeAppConfigWithDefaultOrientation = {
|
|
url: 'app://www.fake.default/index.html',
|
|
manifest: {
|
|
orientation: 'default'
|
|
},
|
|
manifestURL: 'app://www.fake.default/ManifestURL',
|
|
origin: 'app://www.fake.default'
|
|
};
|
|
var fakeAppConfigWithLandscapeSecondaryOrientation = {
|
|
url: 'app://www.fake.landscape.secondary/index.html',
|
|
manifest: {
|
|
orientation: 'landscape-secondary'
|
|
},
|
|
manifestURL: 'app://wwww.fake.landscape.secondary/ManifestURL',
|
|
origin: 'app://www.fake.landscape.secondary'
|
|
};
|
|
|
|
var fakeAppConfigWithPortraitPrimaryOrientation = {
|
|
url: 'app://www.fake.portrait.primary/index.html',
|
|
manifest: {
|
|
orientation: 'portrait-primary'
|
|
},
|
|
manifestURL: 'app://wwww.fake.portrait.primary/ManifestURL',
|
|
origin: 'app://wwww.fake.portrait.primary'
|
|
};
|
|
|
|
var fakeAppConfigWithPortraitSecondaryOrientation = {
|
|
url: 'app://www.fake.portrait.secondary/index.html',
|
|
manifest: {
|
|
orientation: 'portrait-secondary'
|
|
},
|
|
manifestURL: 'app://wwww.fake.portrait.secondary/ManifestURL',
|
|
origin: 'app://wwww.fake.portrait.secondary'
|
|
};
|
|
|
|
var fakeAppConfigWithOrientationArray = {
|
|
url: 'app://www.fake.array/index.html',
|
|
manifest: {
|
|
orientation: ['portrait-secondary', 'landscape']
|
|
},
|
|
manifestURL: 'app://wwww.fake.array/ManifestURL',
|
|
origin: 'app://wwww.fake.array'
|
|
};
|
|
|
|
test('setOrientation()', function() {
|
|
var popups = openPopups(4);
|
|
this.sinon.stub(popups[0], 'isActive').returns(true);
|
|
this.sinon.stub(popups[1], 'isActive').returns(true);
|
|
this.sinon.stub(popups[2], 'isActive').returns(true);
|
|
var stubLockOrientation1 = this.sinon.stub(popups[1], 'lockOrientation');
|
|
var stubLockOrientation2 = this.sinon.stub(popups[2], 'lockOrientation');
|
|
popups[0].setOrientation();
|
|
assert.isFalse(stubLockOrientation1.called);
|
|
assert.isTrue(stubLockOrientation2.called);
|
|
});
|
|
|
|
var stubScreenMozLockOrientation, stubScreenMozUnlockOrientation;
|
|
suite('lockOrientation()', function() {
|
|
setup(function() {
|
|
MockService.mockQueryWith('defaultOrientation', 'portrait-primary');
|
|
MockService.mockQueryWith('globalOrientation', null);
|
|
stubScreenMozLockOrientation =
|
|
this.sinon.stub(screen, 'mozLockOrientation');
|
|
stubScreenMozUnlockOrientation =
|
|
this.sinon.stub(screen, 'mozUnlockOrientation');
|
|
});
|
|
test('No orientation entry in manifest should unlock screen orientation.',
|
|
function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozUnlockOrientation.called);
|
|
});
|
|
test('default orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithDefaultOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('default'));
|
|
});
|
|
test('portrait orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithPortraitOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('portrait'));
|
|
});
|
|
test('landscape orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithLandscapeOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('landscape'));
|
|
});
|
|
test('portrait-primary orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithPortraitPrimaryOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('portrait-primary'));
|
|
});
|
|
test('portrait-secondary orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithPortraitSecondaryOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('portrait-secondary'));
|
|
});
|
|
test('landscape-primary orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithLandscapePrimaryOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('landscape-primary'));
|
|
});
|
|
test('landscape-secondary orientation', function() {
|
|
var app = new AppWindow(fakeAppConfigWithLandscapeSecondaryOrientation);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith('landscape-secondary'));
|
|
});
|
|
test('array of orientations', function() {
|
|
var app = new AppWindow(fakeAppConfigWithOrientationArray);
|
|
app.lockOrientation();
|
|
assert.isTrue(stubScreenMozLockOrientation
|
|
.calledWith(['portrait-secondary', 'landscape']));
|
|
});
|
|
});
|
|
|
|
test('rotatingDegree on / default is portrait-primary', function() {
|
|
MockService.mockQueryWith('defaultOrientation', 'portrait-primary');
|
|
MockService.mockQueryWith('isDefaultPortrait', true);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
assert.isTrue(typeof(app1.rotatingDegree) !== 'undefined');
|
|
var app2 = new AppWindow(fakeAppConfigWithDefaultOrientation);
|
|
assert.isTrue(app2.rotatingDegree === 0);
|
|
var app3 = new AppWindow(fakeAppConfigWithPortraitOrientation);
|
|
assert.isTrue(app3.rotatingDegree === 0);
|
|
var app4 = new AppWindow(fakeAppConfigWithLandscapeOrientation);
|
|
assert.isTrue(app4.rotatingDegree === 90);
|
|
var app5 = new AppWindow(fakeAppConfigWithPortraitPrimaryOrientation);
|
|
assert.isTrue(app5.rotatingDegree === 0);
|
|
var app6 = new AppWindow(fakeAppConfigWithPortraitSecondaryOrientation);
|
|
assert.isTrue(app6.rotatingDegree === 180);
|
|
var app7 = new AppWindow(fakeAppConfigWithLandscapePrimaryOrientation);
|
|
assert.isTrue(app7.rotatingDegree === 90);
|
|
var app8 = new AppWindow(fakeAppConfigWithLandscapeSecondaryOrientation);
|
|
assert.isTrue(app8.rotatingDegree === 270);
|
|
});
|
|
|
|
test('rotatingDegree on / default is landscape-primary', function() {
|
|
MockService.mockQueryWith('defaultOrientation', 'landscape-primary');
|
|
MockService.mockQueryWith('isDefaultPortrait', false);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
assert.isTrue(typeof(app1.rotatingDegree) !== 'undefined');
|
|
var app3 = new AppWindow(fakeAppConfigWithPortraitOrientation);
|
|
assert.isTrue(app3.rotatingDegree === 270);
|
|
var app4 = new AppWindow(fakeAppConfigWithLandscapeOrientation);
|
|
assert.isTrue(app4.rotatingDegree === 0);
|
|
var app5 = new AppWindow(fakeAppConfigWithPortraitPrimaryOrientation);
|
|
assert.isTrue(app5.rotatingDegree === 270);
|
|
var app6 = new AppWindow(fakeAppConfigWithPortraitSecondaryOrientation);
|
|
assert.isTrue(app6.rotatingDegree === 90);
|
|
var app7 = new AppWindow(fakeAppConfigWithLandscapePrimaryOrientation);
|
|
assert.isTrue(app7.rotatingDegree === 0);
|
|
var app8 = new AppWindow(fakeAppConfigWithLandscapeSecondaryOrientation);
|
|
assert.isTrue(app8.rotatingDegree === 180);
|
|
});
|
|
|
|
test('closing Rotation Degree / default is portrait-primary', function() {
|
|
MockService.mockQueryWith('defaultOrientation', 'portrait-primary');
|
|
MockService.mockQueryWith('fetchCurrentOrientation', 'portrait-primary');
|
|
MockService.mockQueryWith('isDefaultPortrait', true);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var angle1 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle1, 0);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation',
|
|
'portrait-secondary');
|
|
var angle2 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle2, 180);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation', 'landscape-primary');
|
|
var angle3 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle3, 270);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation',
|
|
'landscape-secondary');
|
|
var angle4 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle4, 90);
|
|
});
|
|
|
|
test('closing Rotation Degree / default is landscape-primary', function() {
|
|
MockService.mockQueryWith('defaultOrientation', 'landscape-primary');
|
|
MockService.mockQueryWith('fetchCurrentOrientation', 'portrait-primary');
|
|
MockService.mockQueryWith('isDefaultPortrait', false);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var angle1 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle1, 90);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation',
|
|
'portrait-secondary');
|
|
var angle2 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle2, 270);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation', 'landscape-primary');
|
|
var angle3 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle3, 0);
|
|
|
|
MockService.mockQueryWith('fetchCurrentOrientation',
|
|
'landscape-secondary');
|
|
var angle4 = app1.determineClosingRotationDegree();
|
|
assert.equal(angle4, 180);
|
|
});
|
|
});
|
|
|
|
suite('Fullscreen, FullscreenLayout', function() {
|
|
var fakeAppConfig1FullScreen = {
|
|
url: 'app://www.fake/index.html',
|
|
manifest: {
|
|
'fullscreen': true
|
|
},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
var fakeAppConfig1FullScreenLayout = {
|
|
url: 'app://www.fake/index.html',
|
|
manifest: {
|
|
'fullscreen_layout': true
|
|
},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
var fakeWrapper = {
|
|
url: 'app://www.fake.com/index.html',
|
|
origin: 'app://www.fake.com'
|
|
};
|
|
|
|
test('isFullScreen', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
assert.isFalse(app1.isFullScreen());
|
|
assert.isFalse(app1.element.classList.contains('fullscreen-app'));
|
|
|
|
var appW = new AppWindow(fakeWrapper);
|
|
assert.isFalse(appW.isFullScreen());
|
|
assert.isFalse(appW.element.classList.contains('fullscreen-app'));
|
|
|
|
var app1f = new AppWindow(fakeAppConfig1FullScreen);
|
|
assert.isTrue(app1f.isFullScreen());
|
|
assert.isTrue(app1f.element.classList.contains('fullscreen-app'));
|
|
|
|
var app1fl = new AppWindow(fakeAppConfig1FullScreenLayout);
|
|
assert.isTrue(app1fl.isFullScreen());
|
|
assert.isTrue(app1fl.element.classList.contains('fullscreen-app'));
|
|
});
|
|
|
|
test('isFullScreenLayout', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
assert.isFalse(app1.isFullScreenLayout());
|
|
|
|
var appW = new AppWindow(fakeWrapper);
|
|
assert.isFalse(appW.isFullScreenLayout());
|
|
assert.isFalse(app1.element.classList.contains('fullscreen-app'));
|
|
|
|
var app1f = new AppWindow(fakeAppConfig1FullScreenLayout);
|
|
assert.isTrue(app1f.isFullScreenLayout());
|
|
});
|
|
});
|
|
|
|
suite('Display Modes', function() {
|
|
var fakeAppConfigDisplayMinimalUi = {
|
|
url: 'http://www.fake/index.html',
|
|
manifest: {
|
|
'display': 'minimal-ui'
|
|
},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
var fakeAppConfigDisplayStandalone = {
|
|
url: 'http://www.fake/index.html',
|
|
manifest: {
|
|
'display': 'standalone'
|
|
},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
var fakeAppConfigDisplayFullscreen = {
|
|
url: 'http://www.fake/index.html',
|
|
manifest: {
|
|
'display': 'fullscreen'
|
|
},
|
|
manifestURL: 'app://wwww.fake/ManifestURL',
|
|
origin: 'app://www.fake'
|
|
};
|
|
|
|
test('minimal-ui', function() {
|
|
var app1 = new AppWindow(fakeAppConfigDisplayMinimalUi);
|
|
assert.isFalse(app1.isFullScreen());
|
|
assert.isTrue(app1.config.chrome.scrollable);
|
|
assert.isTrue(app1.config.chrome.maximized);
|
|
});
|
|
|
|
test('standalone', function() {
|
|
var app1 = new AppWindow(fakeAppConfigDisplayStandalone);
|
|
assert.isFalse(app1.isFullScreen());
|
|
assert.isFalse(app1.config.chrome.scrollable);
|
|
assert.isFalse(app1.config.chrome.maximized);
|
|
});
|
|
|
|
test('fullscreen', function() {
|
|
var app1 = new AppWindow(fakeAppConfigDisplayFullscreen);
|
|
assert.isTrue(app1.isFullScreen());
|
|
assert.isFalse(app1.config.chrome.scrollable);
|
|
assert.isFalse(app1.config.chrome.maximized);
|
|
});
|
|
});
|
|
|
|
suite('ScreenshotOverlay State Control', function() {
|
|
var app1;
|
|
var app2;
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
app2 = new AppWindow(fakeAppConfig2);
|
|
// Inject mozBrowser API to app iframe
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
});
|
|
|
|
test('when cards view is shown and hidden', function() {
|
|
var fakeScreenshotBlob = 'blob:d3958f5c-0777-0845-9dcf-2cb28783acaf';
|
|
this.sinon.stub(app1, 'requestScreenshotURL', function() {
|
|
return fakeScreenshotBlob;
|
|
});
|
|
|
|
app1.element.dispatchEvent(new CustomEvent('_cardviewbeforeshow'));
|
|
assert.isTrue(app1.screenshotOverlay.classList.contains('visible'),
|
|
'Overlay should be visible after beforeshow is received');
|
|
assert.isFalse(app1.element.classList.contains('no-screenshot'),
|
|
'Shouldnt have no-screenshot class when there is one');
|
|
|
|
app1.element.dispatchEvent(new CustomEvent('_cardviewclosed'));
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'),
|
|
'Overlay should be hidden after closed is received');
|
|
});
|
|
|
|
test('when cards view is shown and theres no screenshot', function() {
|
|
this.sinon.stub(app1, 'requestScreenshotURL', function() {
|
|
return null;
|
|
});
|
|
app1.element.dispatchEvent(new CustomEvent('_cardviewbeforeshow'));
|
|
assert.isTrue(app1.element.classList.contains('no-screenshot'),
|
|
'has no-screenshot class ' +
|
|
'when requestScreenshotURL returns falsey');
|
|
|
|
app1.element.dispatchEvent(new CustomEvent('_cardviewshown'));
|
|
app1.element.dispatchEvent(new CustomEvent('_cardviewclosed'));
|
|
assert.isFalse(app1.element.classList.contains('no-screenshot'),
|
|
'removes no-screenshot class');
|
|
});
|
|
|
|
test('show overlay when revealed by an edge swipe', function() {
|
|
app1.screenshotOverlay.classList.remove('visible');
|
|
app1.element.dispatchEvent(new CustomEvent('_sheetdisplayed'));
|
|
assert.isTrue(app1.screenshotOverlay.classList.contains('visible'),
|
|
'Overlay should be visible after sheetdisplayed');
|
|
});
|
|
|
|
test('hide overlay when a sheet gesture ends', function() {
|
|
app1.screenshotOverlay.classList.add('visible');
|
|
app1.element.dispatchEvent(new CustomEvent('_sheetsgestureend'));
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isFalse(app1.element.classList.contains('overlay'));
|
|
});
|
|
|
|
test('hide overlay when a sheet gesture ends even if the app is active',
|
|
function() {
|
|
this.sinon.stub(app1, 'isActive').returns(true);
|
|
app1.screenshotOverlay.classList.add('visible');
|
|
app1.element.dispatchEvent(new CustomEvent('_sheetsgestureend'));
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isFalse(app1.element.classList.contains('overlay'));
|
|
});
|
|
|
|
test('showScreenshotOverlay', function() {
|
|
var stubRequestScreenshotURL =
|
|
this.sinon.stub(app1, 'requestScreenshotURL');
|
|
app1._showScreenshotOverlay();
|
|
assert.isTrue(stubRequestScreenshotURL.called);
|
|
assert.isTrue(app1.screenshotOverlay.classList.contains('visible'));
|
|
});
|
|
|
|
test('show the frontest app ScreenshotOverlay', function() {
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
var stubRequestScreenshotURL =
|
|
this.sinon.stub(app2, 'requestScreenshotURL');
|
|
app1._showScreenshotOverlay();
|
|
assert.isTrue(stubRequestScreenshotURL.called);
|
|
assert.isTrue(app2.screenshotOverlay.classList.contains('visible'));
|
|
});
|
|
|
|
test('should not show the frontest app ScreenshotOverlay if it is ' +
|
|
'not active', function() {
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(false);
|
|
app1._showScreenshotOverlay();
|
|
assert.isFalse(app2.screenshotOverlay.classList.contains('visible'));
|
|
});
|
|
|
|
test('should return promise and resolve directly if the frontest app is' +
|
|
' active and its screenshot is visible', function(done) {
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
app2.screenshotOverlay.classList.add('visible');
|
|
app1._showScreenshotOverlay().then(() => {
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('hideScreenshotOverlay', function() {
|
|
app1.screenshotOverlay.classList.add('visible');
|
|
app1.element.classList.add('overlay');
|
|
app1._hideScreenshotOverlay();
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
|
|
assert.isTrue(app1.element.classList.contains('overlay'));
|
|
this.sinon.clock.tick(); // We wait for the next tick
|
|
assert.isFalse(app1.element.classList.contains('overlay'));
|
|
});
|
|
|
|
test('hideScreenshotOverlay and its front window', function() {
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
app2.screenshotOverlay.classList.add('visible');
|
|
app1.screenshotOverlay.classList.add('visible');
|
|
app2.element.classList.add('overlay');
|
|
app1.element.classList.add('overlay');
|
|
app1._hideScreenshotOverlay();
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isFalse(app2.screenshotOverlay.classList.contains('visible'));
|
|
assert.isTrue(app1.element.classList.contains('overlay'));
|
|
assert.isTrue(app2.element.classList.contains('overlay'));
|
|
this.sinon.clock.tick(); // We wait for the next tick
|
|
assert.isFalse(app1.element.classList.contains('overlay'));
|
|
assert.isFalse(app2.element.classList.contains('overlay'));
|
|
});
|
|
|
|
test('hideScreenshotOverlay noop when the screenshot is not displayed',
|
|
function() {
|
|
app1._screenshotOverlayState = 'none';
|
|
app1.screenshotOverlay.classList.remove('visible');
|
|
app1.element.classList.add('overlay');
|
|
app1._hideScreenshotOverlay();
|
|
|
|
this.sinon.clock.tick(); // We wait for the next tick
|
|
assert.isTrue(app1.element.classList.contains('overlay'));
|
|
});
|
|
|
|
test('Request screenshotURL', function() {
|
|
assert.isNull(app1.requestScreenshotURL());
|
|
var stubCreateObjectURL = this.sinon.stub(URL, 'createObjectURL');
|
|
var stubRevokeObjectURL = this.sinon.stub(URL, 'revokeObjectURL');
|
|
|
|
app1._screenshotBlob = 'fakeBlob';
|
|
|
|
stubCreateObjectURL.returns('fakeURL');
|
|
var url = app1.requestScreenshotURL();
|
|
assert.isNotNull(url);
|
|
assert.isTrue(stubCreateObjectURL.calledWith('fakeBlob'));
|
|
this.sinon.clock.tick(200);
|
|
assert.isTrue(stubRevokeObjectURL.calledWith('fakeURL'));
|
|
});
|
|
|
|
test('Show identification overlay when showing screenshot', function() {
|
|
app1._showScreenshotOverlay();
|
|
assert.isTrue(app1.element.classList.contains('overlay'));
|
|
});
|
|
});
|
|
|
|
suite('Repaint', function() {
|
|
test('full repaint', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var callback = this.sinon.spy();
|
|
var stubGetScreenshot = this.sinon.stub(app1, 'getScreenshot');
|
|
app1.tryWaitForFullRepaint(callback);
|
|
stubGetScreenshot.getCall(0).args[0]();
|
|
this.sinon.clock.tick(0);
|
|
assert.isTrue(callback.called);
|
|
});
|
|
});
|
|
|
|
suite('Transition', function() {
|
|
test('Open', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var transitionController = new MockAppTransitionController();
|
|
app1.transitionController = transitionController;
|
|
var stubRequireOpen =
|
|
this.sinon.stub(transitionController, 'requireOpen');
|
|
app1.open();
|
|
assert.isTrue(stubRequireOpen.called);
|
|
app1.open('Orz');
|
|
assert.isTrue(stubRequireOpen.calledWith('Orz'));
|
|
});
|
|
|
|
test('Close', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var transitionController = new MockAppTransitionController();
|
|
app1.transitionController = transitionController;
|
|
var stubRequireClose =
|
|
this.sinon.stub(transitionController, 'requireClose');
|
|
app1.close();
|
|
assert.isTrue(stubRequireClose.called);
|
|
app1.close('XD');
|
|
assert.isTrue(stubRequireClose.calledWith('XD'));
|
|
});
|
|
});
|
|
|
|
test('publish', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubbroadcast = this.sinon.stub(app1, 'broadcast');
|
|
var stubDispatchEvent = this.sinon.stub(window, 'dispatchEvent');
|
|
app1.publish('I-hate-you');
|
|
assert.isTrue(stubDispatchEvent.called);
|
|
assert.equal(stubDispatchEvent.getCall(0).args[0].type, 'appI-hate-you');
|
|
assert.isTrue(stubbroadcast.calledWith('I-hate-you'));
|
|
});
|
|
|
|
test('broadcast', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubDispatchEvent = this.sinon.stub(app1.element, 'dispatchEvent');
|
|
app1.broadcast('I-love-you');
|
|
assert.isTrue(stubDispatchEvent.called);
|
|
assert.equal(stubDispatchEvent.getCall(0).args[0].type, '_I-love-you');
|
|
});
|
|
|
|
test('Focus should be delivered to front active window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
var stubFrontFocus = this.sinon.stub(app2, 'focus');
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app1, 'isActive').returns(true);
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
app1.broadcast('focus');
|
|
|
|
assert.isTrue(stubFrontFocus.called);
|
|
});
|
|
|
|
test('setFrameBackground', function() {
|
|
ScreenLayout.setDefault({
|
|
tiny: true
|
|
});
|
|
|
|
var app = new AppWindow(fakeAppConfigWithIcon);
|
|
this.sinon.clock.tick(0);
|
|
var background = app.element.style.backgroundImage;
|
|
var backgroundSize = app.element.style.backgroundSize;
|
|
var idBackground = app.identificationIcon.style.backgroundImage;
|
|
assert.isTrue(app.splashed);
|
|
assert.isDefined(app._splash);
|
|
assert.equal(background, 'url("' + app._splash + '")');
|
|
assert.equal(backgroundSize, '120px 120px');
|
|
assert.equal(idBackground, 'url("' + app._splash + '")');
|
|
});
|
|
|
|
test('get Icon Splash with Multi Icons, dppx=1', function() {
|
|
// Overwrite value for testing dppx
|
|
var _devicePixelRatio = window.devicePixelRatio;
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
configurable: true,
|
|
value: 1
|
|
});
|
|
|
|
ScreenLayout.setDefault({
|
|
tiny: true
|
|
});
|
|
|
|
var app = new AppWindow(fakeAppConfigWithMultiIcon);
|
|
|
|
this.sinon.clock.tick(0);
|
|
assert.isTrue(
|
|
app._splash.indexOf(fakeAppConfigWithMultiIcon.
|
|
manifest.icons['120']) >= 0);
|
|
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
configurable: true,
|
|
value: _devicePixelRatio
|
|
});
|
|
});
|
|
|
|
test('get frame for screenshot should fetch top most window', function() {
|
|
var popups = openPopups(5);
|
|
assert.equal(popups[0].getFrameForScreenshot(),
|
|
popups[4].browser.element);
|
|
});
|
|
|
|
test('get Icon Splash with Multi Icons, dppx=2', function() {
|
|
// Overwrite value for testing dppx
|
|
var _devicePixelRatio = window.devicePixelRatio;
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
configurable: true,
|
|
value: 2
|
|
});
|
|
|
|
ScreenLayout.setDefault({
|
|
tiny: true
|
|
});
|
|
|
|
var app = new AppWindow(fakeAppConfigWithMultiIcon);
|
|
|
|
this.sinon.clock.tick(0);
|
|
assert.isTrue(
|
|
app._splash.indexOf(fakeAppConfigWithMultiIcon.
|
|
manifest.icons['240']) >= 0);
|
|
|
|
Object.defineProperty(window, 'devicePixelRatio', {
|
|
configurable: true,
|
|
value: _devicePixelRatio
|
|
});
|
|
});
|
|
|
|
test('get Private Browser Splash Icon', function() {
|
|
var app = new AppWindow(fakePrivateConfig);
|
|
assert.isTrue(app._splash.indexOf('pb_icon.png') !== -1);
|
|
});
|
|
|
|
var fakeMozBrowserIframe = {
|
|
focus: function() {},
|
|
blur: function() {},
|
|
setVisible: function() {},
|
|
setVisibleForScreenReader: function() {},
|
|
goBack: function() {},
|
|
goForward: function() {},
|
|
reload: function() {},
|
|
stop: function() {},
|
|
getCanGoForward: function() {
|
|
return fakeDOMRequest;
|
|
},
|
|
getCanGoBack: function() {
|
|
return fakeDOMRequest;
|
|
},
|
|
getScreenshot: function() {
|
|
return fakeDOMRequest;
|
|
},
|
|
addNextPaintListener: function() {},
|
|
removeNextPaintListener: function() {},
|
|
setActive: function() {},
|
|
setNFCFocus: function() {}
|
|
};
|
|
|
|
function injectFakeMozBrowserAPI(iframe) {
|
|
for (var method in fakeMozBrowserIframe) {
|
|
iframe[method] = fakeMozBrowserIframe[method];
|
|
}
|
|
}
|
|
|
|
test('we do not need to wait if there is screenshot layer covered',
|
|
function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var callback = this.sinon.spy();
|
|
var stubWaitForNextPaint = this.sinon.stub(app1, 'waitForNextPaint');
|
|
var stubEnsureFullRepaint =
|
|
this.sinon.stub(app1, 'tryWaitForFullRepaint');
|
|
|
|
app1.loaded = true;
|
|
app1.screenshotOverlay.classList.add('visible');
|
|
app1.ready(callback);
|
|
assert.isFalse(stubEnsureFullRepaint.called);
|
|
assert.isFalse(stubWaitForNextPaint.called);
|
|
this.sinon.clock.tick(0);
|
|
assert.isTrue(callback.calledOnce);
|
|
});
|
|
|
|
suite('ready', function() {
|
|
var app1;
|
|
var stubWaitForNextPaint;
|
|
var stubEnsureFullRepaint;
|
|
var showScreenshotOverlay;
|
|
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
stubWaitForNextPaint = this.sinon.stub(app1, 'waitForNextPaint');
|
|
stubEnsureFullRepaint = this.sinon.stub(app1, 'tryWaitForFullRepaint');
|
|
showScreenshotOverlay = this.sinon.stub(app1, '_showScreenshotOverlay');
|
|
app1.loaded = true;
|
|
});
|
|
|
|
teardown(function() {
|
|
stubWaitForNextPaint.restore();
|
|
stubEnsureFullRepaint.restore();
|
|
showScreenshotOverlay.restore();
|
|
});
|
|
|
|
test('Full repaint', function() {
|
|
var callback = this.sinon.spy();
|
|
app1.ready(callback);
|
|
assert.isTrue(stubEnsureFullRepaint.called);
|
|
assert.isTrue(stubWaitForNextPaint.called);
|
|
stubEnsureFullRepaint.getCall(0).args[0]();
|
|
stubWaitForNextPaint.getCall(0).args[0]();
|
|
this.sinon.clock.tick(0);
|
|
assert.isTrue(callback.calledOnce);
|
|
});
|
|
});
|
|
|
|
suite('Browser Mixin', function() {
|
|
var fakeDOMRequest;
|
|
|
|
setup(function() {
|
|
fakeDOMRequest = {
|
|
onsuccess: function() {},
|
|
onerror: function() {}
|
|
};
|
|
});
|
|
|
|
test('MozBrowser API: setActive', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
this.sinon.stub(app1.browser.element, 'setActive');
|
|
MockService.mockQueryWith('getTopMostUI', { name: 'Rocketbar' });
|
|
app1._setActive(false);
|
|
assert.isTrue(app1.browser.element.setActive.calledWith(false));
|
|
MockService.mockQueryWith('getTopMostUI', { name: 'AppWindowManager' });
|
|
app1._setActive(true);
|
|
assert.isTrue(app1.browser.element.setActive.calledWith(true));
|
|
});
|
|
|
|
test('MozBrowser API: simple methods', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stubBack = this.sinon.stub(app1.browser.element, 'goBack');
|
|
var stubForward = this.sinon.stub(app1.browser.element, 'goForward');
|
|
var stubReload = this.sinon.stub(app1.browser.element, 'reload');
|
|
var stubStop = this.sinon.stub(app1.browser.element, 'stop');
|
|
|
|
app1.back();
|
|
assert.isTrue(stubBack.called);
|
|
app1.forward();
|
|
assert.isTrue(stubForward.called);
|
|
app1.reload();
|
|
assert.isTrue(stubReload.called);
|
|
app1.stop();
|
|
assert.isTrue(stubStop.called);
|
|
});
|
|
|
|
suite('focus/blur', function() {
|
|
var app1;
|
|
var stubBlur;
|
|
var stubFocus;
|
|
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
stubBlur = this.sinon.stub(app1.browser.element, 'blur');
|
|
stubFocus = this.sinon.stub(app1.browser.element, 'focus');
|
|
});
|
|
|
|
teardown(function() {
|
|
stubBlur.restore();
|
|
stubFocus.restore();
|
|
});
|
|
|
|
test('blur should not be called without focus', function() {
|
|
var stub = this.sinon.stub(app1, 'getActiveElement', function() {
|
|
return document.body;
|
|
});
|
|
app1.blur();
|
|
assert.isFalse(stubBlur.called);
|
|
stub.restore();
|
|
});
|
|
|
|
test('blur should be called with focus', function() {
|
|
var stub = this.sinon.stub(app1, 'getActiveElement', function() {
|
|
return app1.browser.element;
|
|
});
|
|
app1.blur();
|
|
assert.isTrue(stubBlur.called);
|
|
stub.restore();
|
|
});
|
|
|
|
test('focus should be called without focus', function() {
|
|
var stub = this.sinon.stub(app1, 'getActiveElement', function() {
|
|
return document.body;
|
|
});
|
|
app1.focus();
|
|
assert.isTrue(stubFocus.called);
|
|
stub.restore();
|
|
});
|
|
|
|
test('focus should not be called with focus', function() {
|
|
var stub = this.sinon.stub(app1, 'getActiveElement', function() {
|
|
return app1.browser.element;
|
|
});
|
|
app1.focus();
|
|
assert.isFalse(stubFocus.called);
|
|
stub.restore();
|
|
});
|
|
});
|
|
|
|
suite('focus and context menu', function() {
|
|
var app1;
|
|
var stubFocus;
|
|
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
stubFocus = this.sinon.stub(app1.browser.element, 'focus');
|
|
});
|
|
|
|
teardown(function() {
|
|
stubFocus.restore();
|
|
app1.contextmenu = undefined;
|
|
});
|
|
|
|
test('Context menu is not shown', function() {
|
|
app1.focus();
|
|
assert.isTrue(stubFocus.called);
|
|
});
|
|
|
|
test('Context menu is shown', function() {
|
|
app1.contextmenu = {
|
|
isShown: function() {
|
|
return true;
|
|
},
|
|
focus: function() {
|
|
|
|
}
|
|
};
|
|
|
|
var stubContextMenuFocus = this.sinon.stub(app1.contextmenu, 'focus');
|
|
app1.focus();
|
|
assert.isFalse(stubFocus.called);
|
|
assert.isTrue(stubContextMenuFocus.called);
|
|
stubContextMenuFocus.restore();
|
|
});
|
|
});
|
|
|
|
test('MozBrowser API: getScreenshot', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 320);
|
|
MockService.mockQueryWith('LayoutManager.height', 480);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stubScreenshot = this.sinon.stub(app1.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
|
|
stubScreenshot.returns(fakeDOMRequest);
|
|
|
|
var callback1 = this.sinon.spy();
|
|
app1.getScreenshot(callback1);
|
|
sinon.assert.calledWith(stubScreenshot,
|
|
sinon.match.number, sinon.match.number, 'image/jpeg');
|
|
fakeDOMRequest.onsuccess({ target: { result: 'fakeBlob' } });
|
|
assert.equal(app1._screenshotBlob, 'fakeBlob');
|
|
assert.isTrue(callback1.calledWith('fakeBlob'));
|
|
|
|
fakeDOMRequest.onerror();
|
|
assert.isTrue(callback1.called);
|
|
});
|
|
|
|
test('MozBrowser API: getScreenshot (with frontWindow active)', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
injectFakeMozBrowserAPI(app2.browser.element);
|
|
var stubScreenshot = this.sinon.stub(app1.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
var stubScreenshot2 = this.sinon.stub(app2.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
|
|
var callback1 = this.sinon.spy();
|
|
app1.frontWindow = app2;
|
|
app1.getScreenshot(callback1);
|
|
|
|
assert.isFalse(stubScreenshot.called);
|
|
assert.isTrue(stubScreenshot2.called);
|
|
fakeDOMRequest.onsuccess({ target: { result: 'fakeBlob' } });
|
|
assert.equal(app2._screenshotBlob, 'fakeBlob');
|
|
assert.isTrue(callback1.calledWith('fakeBlob'));
|
|
|
|
fakeDOMRequest.onerror();
|
|
assert.isTrue(callback1.called);
|
|
});
|
|
|
|
test('MozBrowser API: getScreenshot (ignore frontWindow)', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
injectFakeMozBrowserAPI(app2.browser.element);
|
|
var stubScreenshot = this.sinon.stub(app1.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
var stubScreenshot2 = this.sinon.stub(app2.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
|
|
var callback1 = this.sinon.spy();
|
|
app1.frontWindow = app2;
|
|
app1.getScreenshot(callback1, null, null, null, true);
|
|
|
|
assert.isTrue(stubScreenshot.called);
|
|
assert.isFalse(stubScreenshot2.called);
|
|
fakeDOMRequest.onsuccess({ target: { result: 'fakeBlob' } });
|
|
assert.equal(app1._screenshotBlob, 'fakeBlob');
|
|
assert.isTrue(callback1.calledWith('fakeBlob'));
|
|
|
|
fakeDOMRequest.onerror();
|
|
assert.isTrue(callback1.called);
|
|
});
|
|
|
|
test('MozBrowser API: getScreenshot for homescreen', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 320);
|
|
MockService.mockQueryWith('LayoutManager.height', 480);
|
|
var home = new AppWindow(fakeAppConfig1);
|
|
home.isHomescreen = true;
|
|
|
|
injectFakeMozBrowserAPI(home.browser.element);
|
|
var stubScreenshot = this.sinon.stub(home.browser.element,
|
|
'getScreenshot').returns(fakeDOMRequest);
|
|
|
|
home.getScreenshot();
|
|
|
|
// should take screenshot blob with png fromat
|
|
sinon.assert.calledWith(stubScreenshot,
|
|
sinon.match.number, sinon.match.number, 'image/png');
|
|
});
|
|
|
|
test('MozBrowser API: getGoForward', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stubCanGoForward = this.sinon.stub(app1.browser.element,
|
|
'getCanGoForward').returns(fakeDOMRequest);
|
|
|
|
stubCanGoForward.returns(fakeDOMRequest);
|
|
|
|
var callback = this.sinon.spy();
|
|
app1.canGoForward(callback);
|
|
assert.isTrue(stubCanGoForward.called);
|
|
fakeDOMRequest.onsuccess({ target: { result: true } });
|
|
assert.isTrue(callback.calledWith(true));
|
|
|
|
fakeDOMRequest.onsuccess({ target: { result: false } });
|
|
assert.isTrue(callback.calledWith(false));
|
|
|
|
fakeDOMRequest.onerror();
|
|
assert.isTrue(callback.called);
|
|
});
|
|
|
|
test('MozBrowser API: getGoBack', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stubCanGoBack = this.sinon.stub(app1.browser.element,
|
|
'getCanGoBack').returns(fakeDOMRequest);
|
|
|
|
var callback = this.sinon.spy();
|
|
app1.canGoBack(callback);
|
|
assert.isTrue(stubCanGoBack.called);
|
|
fakeDOMRequest.onsuccess({ target: { result: true } });
|
|
assert.isTrue(callback.calledWith(true));
|
|
|
|
fakeDOMRequest.onsuccess({ target: { result: false } });
|
|
assert.isTrue(callback.calledWith(false));
|
|
|
|
fakeDOMRequest.onerror();
|
|
assert.isTrue(callback.called);
|
|
});
|
|
|
|
test('MozBrowser API: NextPaint (pass callback)', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var callback = this.sinon.spy();
|
|
var stubAddNextPaintListener = this.sinon.stub(app1.browser.element,
|
|
'addNextPaintListener');
|
|
|
|
app1.waitForNextPaint(callback);
|
|
assert.isTrue(stubAddNextPaintListener.called);
|
|
stubAddNextPaintListener.getCall(0).args[0]();
|
|
assert.isTrue(callback.called);
|
|
|
|
var callback2 = this.sinon.spy();
|
|
app1.waitForNextPaint(callback2);
|
|
this.sinon.clock.tick(app1.NEXTPAINT_TIMEOUT);
|
|
assert.isTrue(callback2.called);
|
|
});
|
|
|
|
test('MozBrowser API: NextPaint (Promise)', function(done) {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stubAddNextPaintListener = this.sinon.stub(app1.browser.element,
|
|
'addNextPaintListener');
|
|
|
|
var p = app1.waitForNextPaint();
|
|
assert.isTrue(stubAddNextPaintListener.called);
|
|
stubAddNextPaintListener.getCall(0).args[0]();
|
|
p.then(function() {
|
|
var p2 = app1.waitForNextPaint();
|
|
this.sinon.clock.tick(app1.NEXTPAINT_TIMEOUT);
|
|
|
|
return p2;
|
|
}.bind(this)).then(done, done);
|
|
});
|
|
});
|
|
|
|
suite('setVisible', function() {
|
|
test('setVisible: true should revive browser', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
app1.setVisible(false);
|
|
this.sinon.stub(app1, 'reviveBrowser');
|
|
|
|
app1.setVisible(true);
|
|
assert.isTrue(app1.reviveBrowser.called);
|
|
});
|
|
|
|
test('setVisible: true should call handleScrollAreaChanged', function() {
|
|
var app1 = new AppWindow(fakeChromeConfigWithNavigationBar);
|
|
app1.element.dispatchEvent(new CustomEvent('_opened'));
|
|
app1.setVisible(false);
|
|
|
|
var handleScrollAreaChangedCalled = false;
|
|
app1.appChrome.handleScrollAreaChanged = () => {
|
|
handleScrollAreaChangedCalled = true;
|
|
};
|
|
|
|
app1.setVisible(true);
|
|
assert.isTrue(handleScrollAreaChangedCalled);
|
|
});
|
|
|
|
test('setVisible: true', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
app1.setVisible(false);
|
|
var spyShowFrame = this.sinon.spy(app1,
|
|
'_showFrame');
|
|
|
|
var mozBrowserSetVisible = this.sinon.stub(app1.browser.element,
|
|
'setVisible');
|
|
|
|
app1.setVisible(true);
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isTrue(spyShowFrame.called);
|
|
assert.isFalse(app1.element.classList.contains('inactive'));
|
|
assert.equal(app1.browser.element.getAttribute('aria-hidden'), 'false');
|
|
assert.isTrue(mozBrowserSetVisible.calledOnce);
|
|
assert.isTrue(mozBrowserSetVisible.calledWith(true));
|
|
});
|
|
|
|
test('setVisible: true, multiple times', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
app1.setVisible(false);
|
|
|
|
var stub_showFrame = this.sinon.stub(app1,
|
|
'_showFrame');
|
|
|
|
app1.setVisible(true);
|
|
this.sinon.clock.tick();
|
|
app1.setVisible(true);
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isTrue(stub_showFrame.calledOnce);
|
|
});
|
|
|
|
test('setVisible: false', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var spyHideFrame = this.sinon.spy(app1,
|
|
'_hideFrame');
|
|
|
|
var mozBrowserSetVisible = this.sinon.stub(app1.browser.element,
|
|
'setVisible');
|
|
|
|
|
|
app1.setVisible(false);
|
|
assert.isTrue(spyHideFrame.called);
|
|
assert.isTrue(app1.element.classList.contains('inactive'));
|
|
assert.equal(app1.browser.element.getAttribute('aria-hidden'), 'true');
|
|
assert.isTrue(mozBrowserSetVisible.calledOnce);
|
|
assert.isTrue(mozBrowserSetVisible.calledWith(false));
|
|
});
|
|
|
|
test('setVisible: false, multiple times', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var stub_hideFrame = this.sinon.stub(app1,
|
|
'_hideFrame');
|
|
|
|
app1.setVisible(false);
|
|
this.sinon.clock.tick();
|
|
app1.setVisible(false);
|
|
assert.isTrue(stub_hideFrame.calledOnce);
|
|
});
|
|
|
|
test('setVisible to front window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
app1.setVisible(false);
|
|
|
|
var stubApp2SetVisible = this.sinon.stub(app2, 'setVisible');
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
app2.rearWindow = app1;
|
|
|
|
app1.setVisible(true);
|
|
assert.isTrue(stubApp2SetVisible.calledWith(true));
|
|
|
|
app1.setVisible(false);
|
|
assert.isTrue(stubApp2SetVisible.calledWith(false));
|
|
});
|
|
|
|
test('doNotPropagate', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
app1.setVisible(false);
|
|
|
|
this.sinon.stub(app2, 'setVisible');
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app2, 'isActive').returns(true);
|
|
app2.rearWindow = app1;
|
|
app1.setVisible(true, true);
|
|
assert.isFalse(app2.setVisible.called);
|
|
});
|
|
|
|
test('setVisible: homescreen', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
app1.isHomescreen = true;
|
|
app1.setVisible(true);
|
|
|
|
var stub_hideFrame = this.sinon.stub(app1,
|
|
'_hideFrame');
|
|
|
|
app1.setVisible(false);
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isTrue(stub_hideFrame.calledOnce);
|
|
});
|
|
|
|
test('setVisible: homescreen child', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
app1.frontWindow = app2;
|
|
app2.rearWindow = app1;
|
|
app1.isHomescreen = true;
|
|
|
|
app1.setVisible(false);
|
|
assert.isFalse(app1.screenshotOverlay.classList.contains('visible'));
|
|
assert.isFalse(app2.screenshotOverlay.classList.contains('visible'));
|
|
});
|
|
|
|
test('setVisible: called twice', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
app1.setVisible(false);
|
|
var stubMixinSetVisible = this.sinon.stub(app1, '_setVisible');
|
|
|
|
app1.setVisible(true);
|
|
assert.isTrue(stubMixinSetVisible.calledOnce,
|
|
'should call _setVisible once!');
|
|
|
|
app1.setVisible(true);
|
|
assert.isTrue(stubMixinSetVisible.calledOnce,
|
|
'calling setVisible again should *not* call _setVisible!');
|
|
});
|
|
});
|
|
|
|
suite('setVisibleForScreenReader', function() {
|
|
test('setVisibleForScreenReader: false', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
|
|
app1.setVisibleForScreenReader(false);
|
|
|
|
assert.equal(app1.element.getAttribute('aria-hidden'), 'true');
|
|
assert.equal(app1.browser.element.getAttribute('aria-hidden'), 'true');
|
|
});
|
|
|
|
test('setVisibleForScreenReader: true', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
|
|
app1.setVisibleForScreenReader(true);
|
|
|
|
assert.equal(app1.element.getAttribute('aria-hidden'), 'false');
|
|
assert.equal(app1.browser.element.getAttribute('aria-hidden'), 'false');
|
|
});
|
|
|
|
test('preserve screenreader access for frontWindow', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
this.sinon.stub(app2, 'isVisible').returns(true);
|
|
app1.frontWindow = app2;
|
|
|
|
app1.setVisibleForScreenReader(false);
|
|
assert.equal(app1.element.getAttribute('aria-hidden'), 'false');
|
|
assert.equal(app1.browser.element.getAttribute('aria-hidden'), 'false');
|
|
});
|
|
});
|
|
|
|
suite('apply and unapplyStyle', function() {
|
|
test('applyStyle', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
app.element.style.opacity = '0.5';
|
|
|
|
app.applyStyle({
|
|
MozTransform: 'scale(2)',
|
|
fontSize: 'large'
|
|
});
|
|
assert.equal(app.element.style.MozTransform, 'scale(2)');
|
|
assert.equal(app.element.style.fontSize, 'large');
|
|
// is non-destructive
|
|
assert.equal(app.element.style.opacity, '0.5');
|
|
});
|
|
test('unapplyStyle', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
app.applyStyle({
|
|
MozTransform: 'scale(2)',
|
|
fontSize: 'large',
|
|
scale: '0.5'
|
|
});
|
|
app.unapplyStyle({ MozTransform: true, fontSize: true });
|
|
assert.ok(!app.element.style.MozTransform);
|
|
assert.ok(!app.element.style.fontSize);
|
|
assert.equal(app.element.style.scale, '0.5');
|
|
});
|
|
|
|
});
|
|
|
|
suite('enter/leaveTaskManager', function() {
|
|
test('class gets added and removed', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
var closeStub = this.sinon.stub(app, 'close');
|
|
|
|
app.transitionController = new MockAppTransitionController();
|
|
assert.isFalse(app.element.classList.contains('in-task-manager'));
|
|
app.enterTaskManager();
|
|
assert.isTrue(app.element.classList.contains('in-task-manager'));
|
|
app.leaveTaskManager();
|
|
assert.isFalse(app.element.classList.contains('in-task-manager'));
|
|
assert.isTrue(closeStub.calledOnce, 'app.close was called');
|
|
});
|
|
|
|
test('leaveTaskManager: element.style cleanup', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
app.transitionController = new MockAppTransitionController();
|
|
var unapplyStyleStub = sinon.stub(app, 'unapplyStyle');
|
|
app.applyStyle({
|
|
fontSize: '11',
|
|
MozTransform: 'scale(2)'
|
|
});
|
|
app.applyStyle({
|
|
pointerEvents: 'none'
|
|
});
|
|
app.leaveTaskManager();
|
|
|
|
// ensure unapplyStyle gets called with aggregated property list
|
|
assert.isTrue(unapplyStyleStub.calledOnce);
|
|
var unapplyProps = unapplyStyleStub.getCall(0).args[0];
|
|
assert.equal(Object.keys(unapplyProps).length, 3);
|
|
assert.ok('fontSize' in unapplyProps);
|
|
assert.ok('MozTransform' in unapplyProps);
|
|
assert.ok('pointerEvents' in unapplyProps);
|
|
});
|
|
});
|
|
|
|
suite('transform', function(){
|
|
test('transform composes correct string value', function(){
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
var transformProps = {
|
|
scale: 0.5,
|
|
translateX: '10px',
|
|
rotateY: '10deg'
|
|
};
|
|
// although order isn't important, it should come out looking like this:
|
|
var expectedStr = 'scale(0.5) translateX(10px) rotateY(10deg)';
|
|
app.transform(transformProps);
|
|
assert.equal(app.element.style.MozTransform, expectedStr);
|
|
});
|
|
});
|
|
|
|
suite('Event handlers', function() {
|
|
var fakeTransitionController = {
|
|
requireOpen: function() {},
|
|
requireClose: function() {},
|
|
CLOSING_TRANSITION_TIMEOUT: 350
|
|
};
|
|
|
|
suite('Hidewindow event', function() {
|
|
var app1;
|
|
var attention;
|
|
var stubSetVisible;
|
|
var stubIsActive;
|
|
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
attention = new AppWindow(fakeAppConfig2);
|
|
stubSetVisible = this.sinon.stub(app1, 'setVisible');
|
|
stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
});
|
|
|
|
teardown(function() {
|
|
stubSetVisible.restore();
|
|
stubIsActive.restore();
|
|
});
|
|
|
|
test('Set window as invisible', function() {
|
|
app1.handleEvent({
|
|
type: '_hidewindow',
|
|
detail: attention
|
|
});
|
|
assert.isTrue(stubSetVisible.calledWith(false));
|
|
});
|
|
|
|
test('Do not set the visibility ' +
|
|
'when attention window show up on its opener', function() {
|
|
attention.parentWindow = app1;
|
|
app1.handleEvent({
|
|
type: '_hidewindow',
|
|
detail: attention
|
|
});
|
|
assert.isFalse(stubSetVisible.called);
|
|
});
|
|
|
|
test('Do not set the visibility ' +
|
|
'when the window is not active', function() {
|
|
stubIsActive.returns(false);
|
|
app1.handleEvent({
|
|
type: '_hidewindow',
|
|
detail: attention
|
|
});
|
|
assert.isFalse(stubSetVisible.called);
|
|
});
|
|
});
|
|
|
|
test('ActivityDone event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
var spyOpen = this.sinon.spy(app1, 'open');
|
|
var spyClose = this.sinon.spy(app2, 'close');
|
|
var stubIsActive = this.sinon.stub(app2, 'isActive');
|
|
app1.setCalleeWindow(app2);
|
|
stubIsActive.returns(true);
|
|
|
|
assert.deepEqual(app1.calleeWindow, app2);
|
|
assert.deepEqual(app2.callerWindow, app1);
|
|
|
|
app2.handleEvent({
|
|
type: 'mozbrowseractivitydone'
|
|
});
|
|
|
|
assert.isNull(app1.calleeWindow);
|
|
assert.isNull(app2.callerWindow);
|
|
assert.isTrue(spyOpen.calledWith('in-from-left'));
|
|
assert.isTrue(spyClose.calledWith('out-to-right'));
|
|
});
|
|
|
|
test('No transition when the callee is caller', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var spyOpen = this.sinon.spy(app1, 'open');
|
|
var spyClose = this.sinon.spy(app1, 'close');
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
app1.setCalleeWindow(app1);
|
|
stubIsActive.returns(true);
|
|
|
|
assert.deepEqual(app1.calleeWindow, app1);
|
|
assert.deepEqual(app1.callerWindow, app1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowseractivitydone'
|
|
});
|
|
|
|
assert.isNull(app1.calleeWindow);
|
|
assert.isNull(app1.callerWindow);
|
|
assert.isFalse(spyOpen.calledWith('in-from-left'));
|
|
assert.isFalse(spyClose.calledWith('out-to-right'));
|
|
});
|
|
|
|
test('We should open the base window if we are not', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app1Base = new AppWindow(fakeAppConfig2);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
var spyOpen = this.sinon.spy(app1Base, 'open');
|
|
var spyClose = this.sinon.spy(app2, 'close');
|
|
app1.CLASS_NAME = 'ActivityWindow';
|
|
|
|
var stubIsActive = this.sinon.stub(app2, 'isActive');
|
|
app1Base.calleeWindow = app2;
|
|
app2.callerWindow = app1Base;
|
|
stubIsActive.returns(true);
|
|
|
|
app2.handleEvent({
|
|
type: 'mozbrowseractivitydone'
|
|
});
|
|
|
|
assert.isTrue(spyOpen.calledWith('in-from-left'));
|
|
assert.isTrue(spyClose.calledWith('out-to-right'));
|
|
});
|
|
|
|
test('Destroy should clear rearWindow.', function() {
|
|
var popups = openPopups(2);
|
|
popups[1].destroy();
|
|
assert.isNull(popups[0].frontWindow);
|
|
assert.isNull(popups[1].rearWindow);
|
|
});
|
|
|
|
test('Destroy should clear previousWindow.', function() {
|
|
var sheets = openSheets(2);
|
|
sheets[1].destroy();
|
|
assert.isNull(sheets[0].nextWindow);
|
|
assert.isNull(sheets[1].previousWindow);
|
|
});
|
|
|
|
test('App crashed and suspending is disabled', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubKill = this.sinon.stub(app1, 'kill');
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
MockService.mockQueryWith('suspendingAppWindow', false);
|
|
app1.handleEvent({
|
|
type: 'mozbrowsererror',
|
|
detail: {
|
|
type: 'fatal'
|
|
}
|
|
});
|
|
assert.isTrue(app1.isCrashed);
|
|
assert.isTrue(stubKill.called);
|
|
assert.isTrue(stubPublish.calledWith('crashed'));
|
|
});
|
|
|
|
test('App crashed and suspending is enabled' +
|
|
'when app is inactive',
|
|
function() {
|
|
var apps = openPopups(2);
|
|
var app1 = apps[0];
|
|
var popup1 = apps[1];
|
|
var stubKill = this.sinon.stub(popup1, 'kill');
|
|
var stubDestroyBrowser = this.sinon.stub(app1, 'destroyBrowser');
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
|
|
stubIsActive.returns(false);
|
|
MockService.mockQueryWith('suspendingAppWindow', true);
|
|
AppWindow.SUSPENDING_ENABLED = true;
|
|
app1.handleEvent({
|
|
type: 'mozbrowsererror',
|
|
detail: {
|
|
type: 'fatal'
|
|
}
|
|
});
|
|
assert.isFalse(app1.isCrashed);
|
|
assert.isTrue(stubDestroyBrowser.called);
|
|
assert.isTrue(stubKill.called);
|
|
AppWindow.SUSPENDING_ENABLED = false;
|
|
});
|
|
|
|
test('App crashed and suspending is enabled ' +
|
|
'when the app is active',
|
|
function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubKill = this.sinon.stub(app1, 'kill');
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
AppWindow.SUSPENDING_ENABLED = true;
|
|
app1.handleEvent({
|
|
type: 'mozbrowsererror',
|
|
detail: {
|
|
type: 'fatal'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubKill.called);
|
|
assert.isTrue(app1.isCrashed);
|
|
AppWindow.SUSPENDING_ENABLED = false;
|
|
});
|
|
|
|
test('Close event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubKill = this.sinon.stub(app1, 'kill');
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserclose'
|
|
});
|
|
|
|
assert.isTrue(stubKill.called);
|
|
});
|
|
|
|
test('Closed while homescreen at bottom', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
app1.isHomescreen = true;
|
|
app1.loaded = true;
|
|
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
injectFakeMozBrowserAPI(app2.browser.element);
|
|
var stubScreenshot = this.sinon.stub(app1.browser.element,
|
|
'getScreenshot');
|
|
var stubScreenshot2 = this.sinon.stub(app2.browser.element,
|
|
'getScreenshot');
|
|
|
|
app1.handleEvent({
|
|
type: '_closed'
|
|
});
|
|
|
|
assert.isFalse(stubScreenshot.called,
|
|
'should never take screenshot on _closed when homescreen');
|
|
assert.isFalse(stubScreenshot2.called,
|
|
'should never take screenshot on _closed when homescreen');
|
|
});
|
|
|
|
test('Closed while system is busy and homescreen at bottom', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
app1.isHomescreen = true;
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
|
|
MockService.mockQueryWith('isBusyLoading', true);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
injectFakeMozBrowserAPI(app2.browser.element);
|
|
var stubScreenshot = this.sinon.stub(app1.browser.element,
|
|
'getScreenshot');
|
|
var stubScreenshot2 = this.sinon.stub(app2.browser.element,
|
|
'getScreenshot');
|
|
|
|
app2.rearWindow = app1;
|
|
app2.handleEvent({
|
|
type: '_closed'
|
|
});
|
|
|
|
assert.isFalse(stubScreenshot.called,
|
|
'should never take screenshot on _closed when homescreen');
|
|
assert.isFalse(stubScreenshot2.called,
|
|
'should never take screenshot on _closed when homescreen');
|
|
});
|
|
|
|
test('Kill a child window.', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
|
|
var app1parent = new AppWindow(fakeAppConfig2);
|
|
injectFakeMozBrowserAPI(app1parent.browser.element);
|
|
|
|
var app1child = new AppWindow(fakeAppConfig3);
|
|
injectFakeMozBrowserAPI(app1child.browser.element);
|
|
|
|
app1.nextWindow = app1child;
|
|
app1child.previousWindow = app1;
|
|
app1.previousWindow = app1parent;
|
|
app1parent.nextWindow = app1;
|
|
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
var stubKillChild = this.sinon.stub(app1child, 'kill');
|
|
var stubOpenParent = this.sinon.stub(app1parent, 'open');
|
|
var stubCloseSelf = this.sinon.stub(app1, 'close');
|
|
stubIsActive.returns(true);
|
|
|
|
app1.transitionController = fakeTransitionController;
|
|
app1.kill();
|
|
assert.isTrue(stubOpenParent.calledWith('in-from-left'));
|
|
assert.isTrue(stubCloseSelf.calledWith('out-to-right'));
|
|
assert.isTrue(stubKillChild.called);
|
|
assert.isNull(app1.nextWindow);
|
|
|
|
var stubDestroy = this.sinon.stub(app1, 'destroy');
|
|
|
|
/** global */
|
|
app1.element.dispatchEvent(new CustomEvent('_closed'));
|
|
|
|
assert.isTrue(stubDestroy.called);
|
|
});
|
|
|
|
test('kill guards against missed transitions', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
|
|
// Ensure that the closed event does not trigger the destroy method.
|
|
this.sinon.stub(app.element, 'addEventListener');
|
|
|
|
this.sinon.stub(app, 'isActive').returns(true);
|
|
this.sinon.stub(app, 'close');
|
|
|
|
app.transitionController = fakeTransitionController;
|
|
app.kill();
|
|
assert.ok(app.close.notCalled);
|
|
|
|
this.sinon.clock.tick(
|
|
fakeTransitionController.CLOSING_TRANSITION_TIMEOUT);
|
|
assert.ok(app.close.calledOnce);
|
|
assert.ok(app.close.calledWith('immediate'));
|
|
});
|
|
|
|
test('Destroy active window directly when the bottom app is not Active',
|
|
function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
|
|
// Ensure that the closed event does not trigger the destroy method.
|
|
this.sinon.stub(app, 'getBottomMostWindow').returns({
|
|
isActive: function() {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
var destroyStub = this.sinon.stub(app, 'destroy');
|
|
|
|
app.kill();
|
|
assert.ok(destroyStub.calledOnce);
|
|
});
|
|
|
|
test('Load event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserloadstart'
|
|
});
|
|
|
|
assert.isTrue(app1.loading);
|
|
assert.isTrue(!app1.loaded);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserloadend',
|
|
detail: {
|
|
backgroundColor: 'transparent'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(app1.loaded);
|
|
assert.isFalse(app1.loading);
|
|
});
|
|
|
|
test('mozbrowser events before _opened', function() {
|
|
var spy = this.sinon.spy(window, 'AppChrome');
|
|
var app1 = new AppWindow(fakeChromeConfigWithNavigationBar);
|
|
var events = [{ type: 'mozbrowserloadstart' },
|
|
{ type: 'mozbrowsererror',
|
|
detail: { type: '' } },
|
|
{ type: 'mozbrowsermetachange',
|
|
detail: {} },
|
|
{ type: 'mozbrowsermetachange',
|
|
detail: {different: 'detail'} }];
|
|
var expected = [{ type: 'mozbrowserloadstart' },
|
|
{ type: '_loading' },
|
|
{ type: 'mozbrowsererror' },
|
|
{ type: 'mozbrowsermetachange',
|
|
detail: {} },
|
|
{ type: 'mozbrowsermetachange',
|
|
detail: {different: 'detail'} }];
|
|
|
|
events.forEach(app1.handleEvent, app1);
|
|
assert.isFalse(spy.calledWithNew());
|
|
|
|
var chromeEventSpy = this.sinon.stub(AppChrome.prototype, 'handleEvent');
|
|
app1.element.dispatchEvent(new CustomEvent('_opened'));
|
|
|
|
expected.forEach((event) => {
|
|
sinon.assert.calledWith(chromeEventSpy, event);
|
|
});
|
|
});
|
|
|
|
test('metachange event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
this.sinon.stub(app1, 'publish');
|
|
|
|
var evt = {
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'name',
|
|
content: 'content'
|
|
}
|
|
};
|
|
|
|
app1.handleEvent(evt);
|
|
|
|
assert.equal(app1.meta[evt.detail.name], evt.detail.content);
|
|
assert(app1.publish.calledWith('metachange'));
|
|
});
|
|
|
|
test('Locationchange event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var url = app1.config.url;
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://fakeURL.changed'
|
|
});
|
|
|
|
assert.equal(app1.config.url, 'http://fakeURL.changed');
|
|
app1.config.url = url;
|
|
});
|
|
|
|
test('Locationchange event resets favicons', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var url = app1.config.url;
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://fakeURL.changed'
|
|
});
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsericonchange',
|
|
detail: {
|
|
href: 'http://fakeURL.favicon',
|
|
sizes: 60
|
|
}
|
|
});
|
|
assert.equal(Object.keys(app1.favicons).length, 1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://fakeURL.changed2'
|
|
});
|
|
assert.equal(Object.keys(app1.favicons).length, 0);
|
|
app1.config.url = url;
|
|
});
|
|
|
|
test('Locationchange event to same URL', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var url = app1.config.url;
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://fakeURL.changed'
|
|
});
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsericonchange',
|
|
detail: {
|
|
href: 'http://fakeURL.favicon',
|
|
sizes: 60
|
|
}
|
|
});
|
|
var favicons = app1.favicons;
|
|
assert.equal(Object.keys(favicons).length, 1);
|
|
|
|
this.sinon.stub(app1, 'publish');
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://fakeURL.changed'
|
|
});
|
|
assert.deepEqual(app1.favicons, favicons);
|
|
// we *do* expect publish to be called,
|
|
// not doing so seems to break marionette tests
|
|
assert.isTrue(app1.publish.calledOnce);
|
|
app1.config.url = url;
|
|
});
|
|
|
|
test('Locationchange event resets name and title', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://example.com/page.html'
|
|
});
|
|
|
|
assert.equal(app1.name, 'example.com');
|
|
assert.equal(app1.title, 'http://example.com/page.html');
|
|
});
|
|
|
|
test('VisibilityChange event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowservisibilitychange',
|
|
detail: {
|
|
visible: false
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWith('background'));
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowservisibilitychange',
|
|
detail: {
|
|
visible: true
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWith('foreground'));
|
|
});
|
|
|
|
test('Localized event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var spyManifestHelper = this.sinon.stub(window, 'ManifestHelper');
|
|
spyManifestHelper.returns({
|
|
name: 'Mon Application',
|
|
displayName: 'Bref',
|
|
short_name: 'Bref'
|
|
});
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
|
|
app1.handleEvent({
|
|
type: '_localized'
|
|
});
|
|
|
|
assert.isTrue(spyManifestHelper.calledWithNew());
|
|
assert.isTrue(spyManifestHelper.calledWithExactly(app1.manifest));
|
|
assert.isTrue(stubPublish.calledWithExactly('namechanged'));
|
|
assert.equal(app1.identificationTitle.textContent, 'Bref');
|
|
assert.equal(app1.name, 'Bref');
|
|
});
|
|
|
|
test('focus event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubFocus = this.sinon.stub(app1, 'focus');
|
|
|
|
app1.handleEvent({
|
|
type: '_focus'
|
|
});
|
|
|
|
assert.isTrue(stubFocus.calledOnce);
|
|
});
|
|
|
|
test('blur event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubBlur = this.sinon.stub(app1, 'blur');
|
|
|
|
app1.handleEvent({
|
|
type: '_blur'
|
|
});
|
|
|
|
assert.isTrue(stubBlur.calledOnce);
|
|
});
|
|
|
|
test('titlechange event', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsertitlechange',
|
|
detail: 'newtitile'
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWithExactly('titlechange'));
|
|
assert.equal(
|
|
app1.identificationTitle.textContent, 'newtitile',
|
|
'title should be changed since it is not an app'
|
|
);
|
|
|
|
var app2 = new AppWindow(fakeAppConfig1);
|
|
app2.handleEvent({
|
|
type: 'mozbrowsertitlechange',
|
|
detail: 'newtitile'
|
|
});
|
|
assert.equal(
|
|
app2.identificationTitle.textContent, '',
|
|
'title should not be changed since it is an app'
|
|
);
|
|
});
|
|
|
|
test('titlechange event > private browser', function() {
|
|
var defaultPbTitle = 'Private Browser Splash';
|
|
var privateApp = new AppWindow(fakePrivateConfig);
|
|
privateApp.identificationTitle.textContent = defaultPbTitle;
|
|
privateApp.handleEvent({
|
|
type: 'mozbrowsertitlechange',
|
|
detail: 'new title - does not update overlay'
|
|
});
|
|
assert.equal(privateApp.identificationTitle.textContent, defaultPbTitle);
|
|
});
|
|
|
|
test('iconchange event', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsericonchange',
|
|
detail: {
|
|
sizes: '32x32',
|
|
href: 'favicon.ico'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWithExactly('iconchange'));
|
|
assert.deepEqual(app1.favicons, {'favicon.ico': {sizes: ['32x32']}});
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsericonchange',
|
|
detail: {
|
|
sizes: '32x32',
|
|
href: 'another.ico'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWithExactly('iconchange'));
|
|
assert.deepEqual(app1.favicons, {
|
|
'favicon.ico': {sizes: ['32x32']},
|
|
'another.ico': {sizes: ['32x32']}
|
|
});
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsericonchange',
|
|
detail: {
|
|
sizes: '16x16',
|
|
href: 'favicon.ico'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(stubPublish.calledWithExactly('iconchange'));
|
|
assert.deepEqual(app1.favicons, {
|
|
'favicon.ico': {sizes: ['32x32', '16x16']},
|
|
'another.ico': {sizes: ['32x32']}
|
|
});
|
|
});
|
|
|
|
test('Orientation change event on app', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
this.sinon.stub(app1, 'isActive').returns(false);
|
|
app1.width = 320;
|
|
app1.height = 460;
|
|
MockService.mockQueryWith('keyboardEnabled', false);
|
|
MockService.mockQueryWith('LayoutManager.width', 480);
|
|
MockService.mockQueryWith('getHeightFor', 300);
|
|
|
|
app1.handleEvent({
|
|
type: '_orientationchange'
|
|
});
|
|
|
|
assert.equal(app1.element.style.width, '480px');
|
|
assert.equal(app1.element.style.height, '300px');
|
|
|
|
assert.equal(app1.screenshotOverlay.style.visibility, 'hidden');
|
|
});
|
|
|
|
test('Orientation change event on app with match orientation', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
this.sinon.stub(app1, 'isActive').returns(false);
|
|
app1.width = 320;
|
|
app1.height = 460;
|
|
MockService.mockQueryWith('LayoutManager.width', 320);
|
|
MockService.mockQueryWith('getHeightFor', 460);
|
|
|
|
app1.screenshotOverlay.style.visibility = 'hidden';
|
|
|
|
app1.handleEvent({
|
|
type: '_orientationchange'
|
|
});
|
|
|
|
assert.equal(app1.screenshotOverlay.style.visibility, '');
|
|
});
|
|
|
|
test('Orientation change event on active app', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 320);
|
|
MockService.mockQueryWith('getHeightFor', 480);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
// Inject mozBrowser API to app iframe
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
injectFakeMozBrowserAPI(app2.browser.element);
|
|
|
|
app1.frontWindow = app2;
|
|
this.sinon.stub(app1, 'isActive').returns(true);
|
|
this.sinon.stub(app2, 'broadcast');
|
|
|
|
MockService.mockQueryWith('getHeightFor', 380);
|
|
app1.handleEvent({
|
|
type: '_orientationchange'
|
|
});
|
|
|
|
assert.isTrue(app2.broadcast.calledWith('orientationchange'));
|
|
assert.equal(app1.element.style.width,
|
|
MockService.mQueries['LayoutManager.width'] + 'px');
|
|
assert.equal(app1.element.style.height, '380px');
|
|
});
|
|
|
|
test('Orientation change event on active but not top most app', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 480);
|
|
MockService.mockQueryWith('getHeightFor', 320);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
// Inject mozBrowser API to app iframe
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
|
|
this.sinon.stub(app1, 'isActive').returns(true);
|
|
|
|
MockService.mockQueryWith('keyboardHeight', 100);
|
|
app1.handleEvent({
|
|
type: '_orientationchange',
|
|
detail: true
|
|
});
|
|
|
|
assert.equal(app1.element.style.width,
|
|
MockService.mQueries['LayoutManager.width'] + 'px');
|
|
assert.equal(app1.element.style.height,
|
|
MockService.mQueries.getHeightFor + 'px');
|
|
});
|
|
|
|
test('Orientation change event on active homescreen app', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 480);
|
|
MockService.mockQueryWith('getHeightFor', 320);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubLockOrientation = this.sinon.stub(app1, 'lockOrientation');
|
|
this.sinon.stub(app1, 'isActive').returns(true);
|
|
app1.isHomescreen = true;
|
|
app1.width = 320;
|
|
app1.height = 460;
|
|
|
|
MockService.mockQueryWith('LayoutManager.width', 460);
|
|
MockService.mockQueryWith('LayoutManager.height', 320);
|
|
MockService.mockQueryWith('getTopMostWindow', app1);
|
|
|
|
app1.handleEvent({
|
|
type: '_orientationchange'
|
|
});
|
|
|
|
assert.isTrue(stubLockOrientation.calledOnce,
|
|
'when active app is homescreen, we should call lockOrientation to' +
|
|
'prevent it is modified by other background app');
|
|
assert.equal(app1.element.style.width, '460px');
|
|
assert.equal(app1.element.style.height, '320px');
|
|
MockService.mockQueryWith('getTopMostWindow', null);
|
|
});
|
|
|
|
test('Orientation change event on fullscreen app', function() {
|
|
MockService.mockQueryWith('LayoutManager.width', 480);
|
|
MockService.mockQueryWith('getHeightFor', 320);
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
this.sinon.stub(app1, 'isActive').returns(false);
|
|
this.sinon.stub(app1, 'isFullScreen').returns(true);
|
|
app1.width = 320;
|
|
app1.height = 480;
|
|
MockService.mockQueryWith('LayoutManager.width', 480);
|
|
MockService.mockQueryWith('LayoutManager.height', 320);
|
|
|
|
app1.handleEvent({
|
|
type: '_orientationchange'
|
|
});
|
|
|
|
assert.equal(app1.element.style.width, '480px');
|
|
assert.equal(app1.element.style.height, '320px');
|
|
|
|
assert.equal(app1.screenshotOverlay.style.visibility, 'hidden');
|
|
});
|
|
|
|
test('Swipe in event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var atc1 = {
|
|
switchTransitionState: function() {}
|
|
};
|
|
var publishStub = this.sinon.stub(app1, 'publish');
|
|
var switchTransitionState =
|
|
this.sinon.stub(atc1, 'switchTransitionState');
|
|
var revive = this.sinon.stub(app1, 'reviveBrowser');
|
|
app1.transitionController = atc1;
|
|
|
|
app1.handleEvent({
|
|
type: '_swipein'
|
|
});
|
|
|
|
assert.isTrue(switchTransitionState.calledWith('opened'));
|
|
assert.isTrue(revive.called);
|
|
sinon.assert.calledTwice(publishStub);
|
|
sinon.assert.calledWith(publishStub, 'opening');
|
|
sinon.assert.calledWith(publishStub, 'opened');
|
|
});
|
|
|
|
test('Swipe in event while app is being crashed', function(){
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var transitionController = new MockAppTransitionController();
|
|
app1.transitionController = transitionController;
|
|
var stubClearTransitionClasses =
|
|
this.sinon.stub(transitionController, 'clearTransitionClasses');
|
|
app1.isCrashed = true;
|
|
|
|
app1.handleEvent({
|
|
type: '_swipein'
|
|
});
|
|
|
|
assert.isTrue(stubClearTransitionClasses.called);
|
|
});
|
|
|
|
test('Swipe out event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
injectFakeMozBrowserAPI(app1.browser.element);
|
|
var atc1 = {
|
|
switchTransitionState: function() {}
|
|
};
|
|
var publishStub = this.sinon.stub(app1, 'publish');
|
|
var switchTransitionState =
|
|
this.sinon.stub(atc1, 'switchTransitionState');
|
|
app1.transitionController = atc1;
|
|
|
|
app1.handleEvent({
|
|
type: '_swipeout'
|
|
});
|
|
|
|
assert.isTrue(switchTransitionState.calledWith('closed'));
|
|
sinon.assert.calledWith(publishStub, 'closing');
|
|
sinon.assert.calledWith(publishStub, 'closed');
|
|
});
|
|
|
|
test('Shrinking start event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubGetScreenshot = this.sinon.stub(app1,'getScreenshot',
|
|
// We want to check if callback provided as getScreenshot argument
|
|
// calls all the necessary methods
|
|
(callback) => { callback(); }
|
|
);
|
|
this.sinon.stub(app1, '_showScreenshotOverlay')
|
|
.returns(new Promise(function(resolve) {
|
|
resolve();
|
|
assert.isTrue(stubSetVisible.calledWith(false),
|
|
'setVisble in callback');
|
|
}));
|
|
var stubSetVisible = this.sinon.stub(app1, 'setVisible');
|
|
var stubBroadcast = this.sinon.stub(app1, 'broadcast');
|
|
|
|
app1.handleEvent({
|
|
type: '_shrinkingstart'
|
|
});
|
|
|
|
assert.isTrue(stubBroadcast.calledWith('blur'));
|
|
assert.isTrue(stubGetScreenshot.calledOnce, 'getScreenshot');
|
|
});
|
|
|
|
test('Shrinking stop event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubSetVisible = this.sinon.stub(app1, 'setVisible');
|
|
var stubBroadcast = this.sinon.stub(app1, 'broadcast');
|
|
|
|
app1.handleEvent({
|
|
type: '_shrinkingstop'
|
|
});
|
|
|
|
assert.isTrue(stubSetVisible.calledWith(true), 'setVisible');
|
|
assert.isTrue(stubBroadcast.calledWith('focus'));
|
|
});
|
|
|
|
test('browsersecuritychange event', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
// expect indeterminate state before first event
|
|
var publishStub = this.sinon.stub(app1, 'publish');
|
|
assert.equal(app1.getSSLState(), '',
|
|
'SSL state is empty before the first security change');
|
|
|
|
|
|
['broken', 'secure', 'insecure'].forEach(function(state, i) {
|
|
app1.handleEvent({
|
|
type: 'mozbrowsersecuritychange',
|
|
detail: {
|
|
'state': state
|
|
}
|
|
});
|
|
assert.equal(app1.getSSLState(), state);
|
|
assert.equal(publishStub.getCall(i).args[0], 'securitychange');
|
|
assert.equal(publishStub.getCall(i).args[1], state);
|
|
});
|
|
});
|
|
|
|
suite('mozbrowsermanifestchange event', function() {
|
|
var manifestURL = 'https://examples.com/webapp.json';
|
|
var manifestResult = {
|
|
'short_name': 'App', 'name': 'My App',
|
|
'icons': [{ src: new window.URL('icon.png', manifestURL) }]
|
|
};
|
|
|
|
setup(function() {
|
|
this.sinon.stub(window, 'Promise', MockPromise);
|
|
});
|
|
|
|
test('valid web manifest', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var fakeManifestResultPromise = new MockPromise();
|
|
this.sinon.stub(window.Promise, 'resolve')
|
|
.returns(fakeManifestResultPromise);
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermanifestchange',
|
|
detail: {
|
|
'href': manifestURL
|
|
}
|
|
});
|
|
fakeManifestResultPromise.mFulfillToValue(manifestResult);
|
|
|
|
assert.equal(app1.webManifestURL, manifestURL);
|
|
assert.ok(app1.webManifest);
|
|
assert.equal(app1.webManifest.name, 'My App');
|
|
assert.equal(app1.name, 'App');
|
|
assert.ok(app1.webManifest.icons[0]);
|
|
});
|
|
|
|
test('invalid web manifest url', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
this.sinon.stub(MockWebManifestHelper, 'getManifest').throws();
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermanifestchange',
|
|
detail: {
|
|
'href': null
|
|
}
|
|
});
|
|
|
|
assert.ok(!app1.webManifest,
|
|
'webManifest property not populated without webManifestURL');
|
|
});
|
|
});
|
|
});
|
|
|
|
test('getIconBlob', function() {
|
|
var blobPromise = new MockPromise();
|
|
this.sinon.stub(window, 'Promise', MockPromise);
|
|
this.sinon.stub(IconsHelper, 'getIconBlob', function() {
|
|
return blobPromise;
|
|
});
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var dataURI = 'data:image/png;base64,abc+';
|
|
this.sinon.stub(app1, 'getIconBlob').returns(blobPromise);
|
|
|
|
var promisedIcon = app1.getSiteIconUrl();
|
|
promisedIcon.then(function(icon) {
|
|
assert.ok(icon && icon.url);
|
|
assert.equal(icon.url, dataURI);
|
|
});
|
|
blobPromise.mFulfillToValue({ url: dataURI });
|
|
});
|
|
|
|
suite('getSiteIconUrl', function() {
|
|
var app1;
|
|
var SIZE = 64;
|
|
var origin = 'http://test.com';
|
|
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
});
|
|
|
|
|
|
test('getSiteIconUrl', function() {
|
|
this.sinon.stub(window, 'Promise', MockPromise);
|
|
app1.webManifestURL = 'https://example.com/webapp.json';
|
|
app1.webManifest = {};
|
|
var blobPromise = new MockPromise();
|
|
var dataURI = 'data:image/png;base64,abc+';
|
|
this.sinon.stub(app1, 'getIconBlob').returns(blobPromise);
|
|
|
|
var promisedIcon = app1.getSiteIconUrl();
|
|
promisedIcon.then(function(icon) {
|
|
assert.ok(icon && icon.url);
|
|
assert.equal(icon.url, dataURI);
|
|
});
|
|
blobPromise.mFulfillToValue({ url: dataURI });
|
|
});
|
|
|
|
test('getSiteIconUrl passes along correct origin', function() {
|
|
app1.manifestURL = 'https://example.com/webapp.json';
|
|
app1.origin = 'https://app-origin.com/with#hash';
|
|
app1.manifest = {
|
|
origin: origin,
|
|
icons: {
|
|
'64': '/test.png'
|
|
}
|
|
};
|
|
|
|
this.sinon.stub(app1, 'getIconBlob', function (url, size, place, site) {
|
|
assert.equal(site.origin, 'https://app-origin.com');
|
|
});
|
|
|
|
app1.getSiteIconUrl(SIZE);
|
|
});
|
|
|
|
test('getSiteIconUrl uses manifest icons if available', function() {
|
|
app1.manifestURL = 'https://example.com/webapp.json';
|
|
app1.manifest = {
|
|
origin: origin,
|
|
icons: {
|
|
'64': '/test.png'
|
|
}
|
|
};
|
|
|
|
this.sinon.stub(app1, 'getIconBlob', function (url, size, place, site) {
|
|
assert.isTrue(size === SIZE);
|
|
assert.isTrue(site.manifest === app1.manifest);
|
|
});
|
|
|
|
app1.getSiteIconUrl(SIZE);
|
|
});
|
|
});
|
|
|
|
test('Change URL at run time', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubIsActive = this.sinon.stub(app1, 'isActive');
|
|
stubIsActive.returns(true);
|
|
app1.modifyURLatBackground('http://changed.url');
|
|
assert.isTrue(app1.browser.element.src.indexOf('http://changed.url') < 0);
|
|
|
|
stubIsActive.returns(false);
|
|
app1.modifyURLatBackground('http://changed.url');
|
|
assert.isTrue(app1.browser.element.src.indexOf('http://changed.url') >= 0);
|
|
});
|
|
|
|
test('Launch wrapper should take title from app name', function() {
|
|
var app1 = new AppWindow(fakeAppWithName);
|
|
this.sinon.clock.tick();
|
|
assert.equal(app1.name, 'Fake App Name');
|
|
assert.equal(app1.identificationTitle.textContent, 'Fake App Name');
|
|
});
|
|
|
|
test('Launch wrapper should take title from hostname if no name provided',
|
|
function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
this.sinon.clock.tick();
|
|
assert.equal(app1.name, 'www.fake5');
|
|
assert.equal(app1.identificationTitle.textContent, 'www.fake5');
|
|
});
|
|
|
|
test('revive browser', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
var stub_setVisble = this.sinon.stub(app1, '_setVisible');
|
|
app1.browser = null;
|
|
app1.reviveBrowser();
|
|
assert.isNotNull(app1.browser);
|
|
assert.isFalse(app1.isCrashed);
|
|
assert.isFalse(app1.suspended);
|
|
assert.isTrue(stub_setVisble.calledWith(false));
|
|
assert.isTrue(stubPublish.calledWith('resumed'));
|
|
assert.isTrue(app1.browser.element.classList.contains('hidden'));
|
|
});
|
|
|
|
test('destroy browser', function() {
|
|
var app1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
app1.destroyBrowser();
|
|
assert.isNull(app1.browser);
|
|
assert.isNull(app1.iframe);
|
|
assert.isTrue(app1.suspended);
|
|
assert.isTrue(stubPublish.calledWith('suspended'));
|
|
assert.equal(app1._sslState, '');
|
|
});
|
|
|
|
test('set child window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var child = new AppWindow(fakeAppConfig2);
|
|
app1.setNextWindow(child);
|
|
assert.deepEqual(app1.nextWindow, child);
|
|
var childNew = new AppWindow(fakeAppConfig3);
|
|
var stubKillOldChild = this.sinon.stub(child, 'kill');
|
|
app1.setNextWindow(childNew);
|
|
assert.isTrue(stubKillOldChild.called);
|
|
assert.deepEqual(app1.nextWindow, childNew);
|
|
});
|
|
|
|
suite('isActive', function() {
|
|
var testApp;
|
|
setup(function() {
|
|
testApp = new AppWindow(fakeAppConfig1);
|
|
});
|
|
|
|
test('dom is removed', function() {
|
|
testApp.element = null;
|
|
assert.isFalse(testApp.isActive());
|
|
});
|
|
|
|
test('app is in queue to show', function() {
|
|
testApp.element.classList.add('will-become-active');
|
|
assert.isTrue(testApp.isActive());
|
|
});
|
|
|
|
test('app is in queue to hide', function() {
|
|
testApp.transitionController = {
|
|
'_transitionState': 'opened'
|
|
};
|
|
testApp.element.classList.add('will-become-inactive');
|
|
assert.isFalse(testApp.isActive());
|
|
});
|
|
|
|
test('app doesnot have transitionController', function() {
|
|
testApp.transitionController = null;
|
|
assert.isFalse(testApp.isActive());
|
|
});
|
|
|
|
test('app doesnot is in opened state', function() {
|
|
testApp.transitionController = {
|
|
'_transitionState': 'opened'
|
|
};
|
|
assert.isTrue(testApp.isActive());
|
|
});
|
|
|
|
test('app doesnot is in opening state', function() {
|
|
testApp.transitionController = {
|
|
'_transitionState': 'opening'
|
|
};
|
|
assert.isTrue(testApp.isActive());
|
|
});
|
|
|
|
test('app doesnot is in closing state', function() {
|
|
testApp.transitionController = {
|
|
'_transitionState': 'closing'
|
|
};
|
|
assert.isFalse(testApp.isActive());
|
|
});
|
|
});
|
|
|
|
suite('shouldResize', function() {
|
|
var testApp;
|
|
setup(function() {
|
|
testApp = new AppWindow(fakeAppConfig1);
|
|
});
|
|
|
|
test('app is in queue to hide', function() {
|
|
testApp.element.classList.add('will-become-inactive');
|
|
assert.isTrue(testApp.shouldResize());
|
|
});
|
|
|
|
test('app is active', function() {
|
|
this.sinon.stub(testApp, 'isActive').returns(true);
|
|
assert.isTrue(testApp.shouldResize());
|
|
});
|
|
|
|
test('app is inactive', function() {
|
|
this.sinon.stub(testApp, 'isActive').returns(false);
|
|
assert.isFalse(testApp.shouldResize());
|
|
});
|
|
});
|
|
|
|
test('isSheetTransitioning', function() {
|
|
var testApp = new AppWindow(fakeAppConfig1);
|
|
testApp.element.classList.add('inside-edges');
|
|
assert.isTrue(testApp.isSheetTransitioning());
|
|
});
|
|
|
|
suite('isTransitioning', function() {
|
|
var testApp;
|
|
setup(function() {
|
|
testApp = new AppWindow(fakeAppConfig1);
|
|
});
|
|
|
|
test('app is opening', function() {
|
|
testApp.transitionController = null;
|
|
testApp.element.classList.add('transition-opening');
|
|
assert.isTrue(testApp.isTransitioning());
|
|
|
|
testApp.transitionController = {
|
|
_transitionState: undefined
|
|
};
|
|
assert.isFalse(testApp.isTransitioning());
|
|
|
|
testApp.transitionController._transitionState = 'opening';
|
|
assert.isTrue(testApp.isTransitioning());
|
|
|
|
});
|
|
|
|
test('app is closing', function() {
|
|
testApp.transitionController = null;
|
|
testApp.element.classList.add('transition-closing');
|
|
assert.isTrue(testApp.isTransitioning());
|
|
|
|
testApp.transitionController = {
|
|
_transitionState: undefined
|
|
};
|
|
assert.isFalse(testApp.isTransitioning());
|
|
|
|
testApp.transitionController._transitionState = 'closing';
|
|
assert.isTrue(testApp.isTransitioning());
|
|
});
|
|
|
|
});
|
|
|
|
test('isBrowser', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig4);
|
|
assert.isFalse(app1.isBrowser());
|
|
assert.isTrue(app2.isBrowser());
|
|
assert.isTrue(app2.element.classList.contains('browser'));
|
|
});
|
|
|
|
test('isPrivateBrowser', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakePrivateConfig);
|
|
assert.isFalse(app1.isPrivateBrowser());
|
|
assert.isTrue(app2.isPrivateBrowser());
|
|
assert.isTrue(app2.element.classList.contains('private'));
|
|
});
|
|
|
|
test('isCertified', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfigCertified);
|
|
assert.isFalse(app1.isCertified());
|
|
assert.isTrue(app2.isCertified());
|
|
});
|
|
|
|
test('navigate', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var app2 = new AppWindow(fakeAppConfig4);
|
|
var popup = new AppWindow(fakeAppConfig2);
|
|
var url = 'http://changed.url';
|
|
|
|
this.sinon.stub(app1, 'reConfig');
|
|
app1.navigate(url);
|
|
assert.isTrue(app1.browser.element.src.indexOf(url) < 0);
|
|
assert.isTrue(app1.reConfig.notCalled);
|
|
|
|
app2.navigate(url);
|
|
assert.isTrue(app2.browser.element.src.indexOf(url) !== -1);
|
|
|
|
app2.frontWindow = popup;
|
|
app2.navigate(url);
|
|
assert.isNull(app2.frontWindow);
|
|
});
|
|
|
|
test('navigate app -> browser', function() {
|
|
var app1 = new AppWindow(fakeSearchAppConfig);
|
|
var url = 'http://changed.url';
|
|
|
|
this.sinon.stub(app1, 'reConfig');
|
|
this.sinon.stub(app1, '_unregisterAudioChannels');
|
|
this.sinon.stub(app1, '_registerAudioChannels');
|
|
app1.navigate(url);
|
|
|
|
assert.ok(app1.reConfig.calledOnce);
|
|
assert.ok(app1._unregisterAudioChannels.calledOnce);
|
|
assert.ok(app1._registerAudioChannels.calledOnce);
|
|
assert.ok(app1.element.classList.contains('browser'));
|
|
});
|
|
|
|
suite('Sub component', function() {
|
|
var app;
|
|
setup(function() {
|
|
window.AudioChannelController = MockAudioChannelController;
|
|
app = new AppWindow(fakeAppConfig1);
|
|
var element = app.browser.element;
|
|
element.allowedAudioChannels = [
|
|
{ name: 'normal' }, { name: 'content' }
|
|
];
|
|
app.installSubComponents();
|
|
element.dispatchEvent(new CustomEvent('mozbrowserloadstart'));
|
|
});
|
|
|
|
teardown(function() {
|
|
delete window.AudioChannelController;
|
|
});
|
|
|
|
test('installSubComponents', function() {
|
|
assert.equal(app.audioChannels.size, 2);
|
|
assert.equal(app.audioChannels.get('normal').name, 'normal');
|
|
assert.equal(app.audioChannels.get('content').name, 'content');
|
|
});
|
|
|
|
test('uninstallSubComponents', function() {
|
|
var spy = {};
|
|
for (let propertyName in AppWindow.SUB_COMPONENTS) {
|
|
if (app[propertyName] && app[propertyName].destroy) {
|
|
spy[propertyName] = this.sinon.spy(app[propertyName], 'destroy');
|
|
}
|
|
}
|
|
for (let propertyName in AppWindow.SUB_MODULES) {
|
|
if (app[propertyName] && app[propertyName].stop) {
|
|
spy[propertyName] = this.sinon.spy(app[propertyName], 'stop');
|
|
}
|
|
}
|
|
|
|
var normalChannel = app.audioChannels.get('normal');
|
|
var contentChannel = app.audioChannels.get('content');
|
|
this.sinon.spy(normalChannel, 'destroy');
|
|
this.sinon.spy(contentChannel, 'destroy');
|
|
|
|
app.uninstallSubComponents();
|
|
|
|
for (let propertyName in AppWindow.SUB_COMPONENTS) {
|
|
if (spy[propertyName]) {
|
|
assert.isTrue(spy[propertyName].calledOnce);
|
|
}
|
|
assert.isNull(app[propertyName]);
|
|
}
|
|
for (let propertyName in AppWindow.SUB_MODULES) {
|
|
if (spy[propertyName]) {
|
|
assert.isTrue(spy[propertyName].calledOnce);
|
|
}
|
|
assert.isNull(app[propertyName]);
|
|
}
|
|
|
|
assert.ok(normalChannel.destroy.calledOnce);
|
|
assert.ok(contentChannel.destroy.calledOnce);
|
|
assert.deepEqual(app.audioChannels, null);
|
|
});
|
|
});
|
|
|
|
suite('fadeOut', function() {
|
|
var app1;
|
|
setup(function() {
|
|
app1 = new AppWindow(fakeAppConfig1);
|
|
});
|
|
|
|
test('app is active', function() {
|
|
this.sinon.stub(app1, 'isActive', function() {return true;});
|
|
app1.fadeOut();
|
|
assert.isFalse(app1.element.classList.contains('fadeout'));
|
|
});
|
|
|
|
test('app not active', function() {
|
|
this.sinon.stub(app1, 'isActive', function() {return false;});
|
|
app1.fadeOut();
|
|
assert.isTrue(app1.element.classList.contains('fadeout'));
|
|
});
|
|
});
|
|
|
|
test('showDefaultContextMenu', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
// Nothing goes wrong if contextmenu is undefined
|
|
app.showDefaultContextMenu();
|
|
|
|
app.contextmenu = MockContextMenu;
|
|
var stubCtx = this.sinon.stub(app.contextmenu, 'showDefaultMenu');
|
|
app.showDefaultContextMenu();
|
|
assert.isTrue(stubCtx.called);
|
|
});
|
|
|
|
test('hideContextMenu', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
// Nothing goes wrong if contextmenu is undefined
|
|
app.showDefaultContextMenu();
|
|
|
|
app.contextmenu = MockContextMenu;
|
|
var stubCtxShow = this.sinon.stub(app.contextmenu, 'showDefaultMenu');
|
|
app.showDefaultContextMenu();
|
|
assert.isTrue(stubCtxShow.called);
|
|
|
|
var stubCtxHide = this.sinon.stub(app.contextmenu, 'hide');
|
|
app.hideContextMenu();
|
|
assert.isTrue(stubCtxHide.called);
|
|
});
|
|
|
|
function genFakeConfig(id) {
|
|
return {
|
|
url: 'app://www.fake' + id + '/index.html',
|
|
manifest: {},
|
|
manifestURL: 'app://wwww.fake' + id + '/ManifestURL',
|
|
origin: 'app://www.fake' + id
|
|
};
|
|
}
|
|
|
|
function openPopups(count) {
|
|
var popups = [];
|
|
for (var i = 0; i < count; i++) {
|
|
popups.push(new AppWindow(genFakeConfig(i)));
|
|
if (i > 0) {
|
|
popups[i - 1].frontWindow = popups[i];
|
|
popups[i].rearWindow = popups[i - 1];
|
|
}
|
|
}
|
|
return popups;
|
|
}
|
|
|
|
suite('Kill embedded windows', function() {
|
|
test('kill popup from root', function() {
|
|
var popups = openPopups(3);
|
|
var stubKill = this.sinon.stub(popups[2], 'kill');
|
|
popups[0].kill();
|
|
assert.isTrue(stubKill.called);
|
|
});
|
|
|
|
test('kill chain from a node', function() {
|
|
var popups = openPopups(5);
|
|
var stubKill = this.sinon.stub(popups[4], 'kill');
|
|
popups[2].kill();
|
|
assert.isTrue(stubKill.called);
|
|
});
|
|
});
|
|
|
|
test('getTopMostWindow()', function() {
|
|
var popups = openPopups(5);
|
|
assert.deepEqual(popups[0].getTopMostWindow(), popups[4]);
|
|
assert.deepEqual(popups[1].getTopMostWindow(), popups[4]);
|
|
assert.deepEqual(popups[4].getTopMostWindow(), popups[4]);
|
|
});
|
|
|
|
test('getBottomMostWindow()', function() {
|
|
var popups = openPopups(5);
|
|
assert.deepEqual(popups[4].getBottomMostWindow(), popups[0]);
|
|
assert.deepEqual(popups[3].getBottomMostWindow(), popups[0]);
|
|
assert.deepEqual(popups[0].getBottomMostWindow(), popups[0]);
|
|
});
|
|
|
|
function openSheets(count) {
|
|
var sheets = [];
|
|
for (var i = 0; i < count; i++) {
|
|
sheets.push(new AppWindow(genFakeConfig(i)));
|
|
if (i > 0) {
|
|
sheets[i - 1].nextWindow = sheets[i];
|
|
sheets[i].previousWindow = sheets[i - 1];
|
|
}
|
|
}
|
|
return sheets;
|
|
}
|
|
|
|
suite('Test child windows', function() {
|
|
test('kill chain from root', function() {
|
|
var sheets = openSheets(3);
|
|
var stubKill = this.sinon.stub(sheets[2], 'kill');
|
|
sheets[0].kill();
|
|
assert.isTrue(stubKill.called);
|
|
});
|
|
|
|
test('kill chain from a node', function() {
|
|
var sheets = openSheets(5);
|
|
var stubKill = this.sinon.stub(sheets[4], 'kill');
|
|
sheets[2].kill();
|
|
assert.isTrue(stubKill.called);
|
|
});
|
|
|
|
test('get root and leaf window', function() {
|
|
var sheets = openSheets(7);
|
|
assert.equal(sheets[0].getLeafWindow(), sheets[6]);
|
|
assert.equal(sheets[3].getRootWindow(), sheets[0]);
|
|
});
|
|
|
|
test('get active window', function() {
|
|
var sheets = openSheets(7);
|
|
var stubIsActive = this.sinon.stub(sheets[2], 'isActive');
|
|
stubIsActive.returns(true);
|
|
assert.equal(sheets[0].getActiveWindow(), sheets[2]);
|
|
});
|
|
|
|
test('get next window', function() {
|
|
var sheets = openSheets(7);
|
|
var stubIsActive = this.sinon.stub(sheets[2], 'isActive');
|
|
stubIsActive.returns(true);
|
|
assert.equal(sheets[0].getNext(), sheets[3]);
|
|
});
|
|
|
|
test('get previous window', function() {
|
|
var sheets = openSheets(7);
|
|
var stubIsActive = this.sinon.stub(sheets[2], 'isActive');
|
|
stubIsActive.returns(true);
|
|
assert.equal(sheets[0].getPrev(), sheets[1]);
|
|
});
|
|
});
|
|
|
|
test('Popup is visible only when bottom and itself are both active',
|
|
function() {
|
|
var popups = openPopups(3);
|
|
this.sinon.stub(popups[0], 'isActive').returns(false);
|
|
this.sinon.stub(popups[1], 'isActive').returns(true);
|
|
this.sinon.stub(popups[2], 'isActive').returns(true);
|
|
assert.isFalse(popups[2].isVisible());
|
|
|
|
var popups2 = openPopups(4);
|
|
this.sinon.stub(popups2[0], 'isActive').returns(true);
|
|
this.sinon.stub(popups2[1], 'isActive').returns(true);
|
|
this.sinon.stub(popups2[2], 'isActive').returns(true);
|
|
this.sinon.stub(popups2[3], 'isActive').returns(false);
|
|
assert.isTrue(popups2[2].isVisible());
|
|
assert.isFalse(popups2[3].isVisible());
|
|
});
|
|
|
|
test('front window should dispatch events on its element',
|
|
function() {
|
|
var popups = openPopups(2);
|
|
var caught = false;
|
|
var caughtOnParent = false;
|
|
popups[1].element.addEventListener('appfake', function() {
|
|
caught = true;
|
|
});
|
|
popups[0].element.addEventListener('appfake', function() {
|
|
caughtOnParent = true;
|
|
});
|
|
popups[1].publish('fake');
|
|
assert.isTrue(caught);
|
|
assert.isFalse(caughtOnParent);
|
|
});
|
|
|
|
suite('Theme Group', function() {
|
|
test('Added', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'theme-group',
|
|
content: 'theme-media',
|
|
type: 'added'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(app1.element.classList.contains('theme-media'));
|
|
});
|
|
|
|
test('Sanitazation', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'theme-group',
|
|
content: 'hidden',
|
|
type: 'added'
|
|
}
|
|
});
|
|
|
|
assert.isFalse(app1.element.classList.contains('hidden'));
|
|
});
|
|
|
|
test('Changed', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'theme-group',
|
|
content: 'theme-settings',
|
|
type: 'changed'
|
|
}
|
|
});
|
|
|
|
assert.isTrue(app1.element.classList.contains('theme-settings'));
|
|
});
|
|
|
|
test('Removed', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'theme-group',
|
|
content: 'theme-media',
|
|
type: 'added'
|
|
}
|
|
});
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'theme-group',
|
|
content: 'theme-media',
|
|
type: 'removed'
|
|
}
|
|
});
|
|
|
|
assert.isFalse(app1.element.classList.contains('theme-media'));
|
|
});
|
|
});
|
|
|
|
suite('inScope', function() {
|
|
test('Same domain is in the scope', function() {
|
|
var scope = 'http://domain.com';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isTrue(app.inScope(scope));
|
|
});
|
|
|
|
test('Scope with paths are allowed', function() {
|
|
var scope = 'http://domain.com/test';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test/page1'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isTrue(app.inScope(scope));
|
|
});
|
|
|
|
test('Different domain is not in the scope', function() {
|
|
var scope = 'http://domain2.com';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isFalse(app.inScope(scope));
|
|
});
|
|
|
|
test('Subdomains are not in the scope', function() {
|
|
var scope = 'http://test.domain.com';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isFalse(app.inScope(scope));
|
|
});
|
|
|
|
test('Different schemes are not in the scope', function() {
|
|
var scope = 'https://domain.com';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isFalse(app.inScope(scope));
|
|
});
|
|
|
|
test('Returns false on non browser windows', function() {
|
|
var scope = 'http://domain.com/test';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test/page1'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(false);
|
|
assert.isFalse(app.inScope(scope));
|
|
});
|
|
|
|
test('Scope changes on locationchange', function() {
|
|
var scope = 'http://test.domain.com';
|
|
var appConfig = {
|
|
url: 'http://domain.com/test'
|
|
};
|
|
var app = new AppWindow(appConfig);
|
|
this.sinon.stub(app, 'isBrowser').returns(true);
|
|
assert.isFalse(app.inScope(scope));
|
|
|
|
app.handleEvent({
|
|
type: 'mozbrowserlocationchange',
|
|
detail: 'http://test.domain.com/test'
|
|
});
|
|
assert.isTrue(app.inScope(scope));
|
|
});
|
|
});
|
|
|
|
suite('application-name', function() {
|
|
test('application-name for browser window', function() {
|
|
var browser1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(browser1, 'publish');
|
|
|
|
browser1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'application-name',
|
|
content: 'title1'
|
|
}
|
|
});
|
|
|
|
assert.equal(browser1.name, 'title1');
|
|
assert.isTrue(stubPublish.calledWith('namechanged'));
|
|
stubPublish.restore();
|
|
});
|
|
|
|
test('application-name with empty string does not update', function() {
|
|
var browser1 = new AppWindow(fakeWrapperConfig);
|
|
var stubPublish = this.sinon.stub(browser1, 'publish');
|
|
|
|
browser1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: ' ',
|
|
content: 'title1'
|
|
}
|
|
});
|
|
var hostname = new URL(fakeWrapperConfig.url).hostname;
|
|
assert.equal(browser1.name, hostname);
|
|
assert.isFalse(stubPublish.calledWith('namechanged'));
|
|
});
|
|
|
|
test('application-name for app window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stubPublish = this.sinon.stub(app1, 'publish');
|
|
|
|
app1.handleEvent({
|
|
type: 'mozbrowsermetachange',
|
|
detail: {
|
|
name: 'application-name',
|
|
content: 'title1'
|
|
}
|
|
});
|
|
|
|
assert.isFalse(app1.name == 'title1');
|
|
assert.isFalse(stubPublish.calledOnce);
|
|
stubPublish.restore();
|
|
});
|
|
});
|
|
|
|
test('Should not be killable if it has an attention window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var attention = new AppWindow(fakeAppConfig1);
|
|
app1.attentionWindow = attention;
|
|
assert.isFalse(app1.killable());
|
|
});
|
|
|
|
test('Should not be killable if we are homescreen window', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
app1.isHomescreen = true;
|
|
assert.isFalse(app1.killable());
|
|
});
|
|
|
|
test('Has attention permission', function() {
|
|
MockPermissionSettings.permissions.attention = 'deny';
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
assert.isFalse(app1.hasPermission('attention'));
|
|
|
|
MockPermissionSettings.permissions.attention = 'allow';
|
|
var app2 = new AppWindow(fakeAppConfig2);
|
|
assert.isTrue(app2.hasPermission('attention'));
|
|
});
|
|
|
|
test('Show()', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
app1.hide();
|
|
assert.isTrue(app1.element.classList.contains('hidden'));
|
|
|
|
app1.show();
|
|
assert.isFalse(app1.element.classList.contains('hidden'));
|
|
});
|
|
|
|
test('Sub-component destruction', function() {
|
|
var app1 = new AppWindow(fakeAppConfig1);
|
|
var stub = this.sinon.stub(app1.transitionController, 'destroy');
|
|
app1.destroy();
|
|
assert.ok(stub.calledOnce);
|
|
});
|
|
|
|
test('No AppChrome for InputMethod AppWindows', function() {
|
|
var appInput = new AppWindow(fakeInputAppConfig);
|
|
appInput.element.dispatchEvent(new CustomEvent('_opened'));
|
|
assert.equal(appInput.appChrome, undefined);
|
|
});
|
|
|
|
test('Should bypass touch event to statusbar submodule', function() {
|
|
var app = new AppWindow(fakeAppConfig1);
|
|
app.statusbar = {
|
|
handleStatusbarTouch: this.sinon.spy()
|
|
};
|
|
var fakeTouchEvt = new CustomEvent('touchstart');
|
|
app.handleStatusbarTouch(fakeTouchEvt, 24);
|
|
assert.isTrue(app.statusbar.handleStatusbarTouch.calledWith(
|
|
fakeTouchEvt, 24));
|
|
});
|
|
});
|