Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
281 lines
8.2 KiB
JavaScript
Executable file
281 lines
8.2 KiB
JavaScript
Executable file
/* global AppWindow, Card, MocksHelper, MockPromise, Icon,
|
|
TaskManagerUtils, SwipeToKillMotion */
|
|
'use strict';
|
|
|
|
require('/shared/js/sanitizer.js');
|
|
requireApp('system/test/unit/mock_app_window.js');
|
|
require('/shared/test/unit/mocks/mock_promise.js');
|
|
require('/shared/js/homescreens/icon.js');
|
|
|
|
var mocksForCard = new MocksHelper([
|
|
'AppWindow'
|
|
]).init();
|
|
|
|
suite('system/Card', function() {
|
|
|
|
function makeApp(config) {
|
|
var appWindow = new AppWindow({
|
|
launchTime: 4,
|
|
title: config.title,
|
|
name: config.name || 'dummyapp',
|
|
manifest: {
|
|
orientation: config.orientation || 'portrait-primary'
|
|
},
|
|
isPrivate: config.isPrivate,
|
|
rotatingDegree: config.rotatingDegree || 0,
|
|
getScreenshot: function(callback) {
|
|
callback();
|
|
},
|
|
origin: config.origin || 'app://' +
|
|
(config.name || 'dummyapp') + '.gaiamobile.org',
|
|
url: config.url,
|
|
blur: function() {}
|
|
});
|
|
appWindow.browser.element.src = appWindow.origin + '/index.html';
|
|
if (config.isBrowser) {
|
|
appWindow.isBrowser = () => config.isBrowser;
|
|
}
|
|
return appWindow;
|
|
}
|
|
|
|
mocksForCard.attachTestHelpers();
|
|
var mockManager;
|
|
var cardsList;
|
|
var iconDataURI = 'data:image/png;base64,abc+';
|
|
var getIconPromise;
|
|
|
|
suiteSetup(function(done) {
|
|
cardsList = document.createElement('ul');
|
|
cardsList.id = 'cards-list';
|
|
document.body.appendChild(cardsList);
|
|
|
|
requireApp('system/js/service.js');
|
|
requireApp('system/js/base_ui.js');
|
|
requireApp('system/js/task_manager_utils.js');
|
|
requireApp('system/js/card.js', done);
|
|
});
|
|
|
|
setup(function() {
|
|
getIconPromise = new MockPromise();
|
|
this.sinon.stub(AppWindow.prototype, 'getSiteIconUrl')
|
|
.returns(getIconPromise);
|
|
mockManager = {
|
|
SWIPE_UP_THRESHOLD: 480/4,
|
|
cardsList: cardsList
|
|
};
|
|
|
|
this.sinon.stub(Icon.prototype, 'renderBlob', function() {
|
|
this.elem.style.backgroundImage = 'url(' + this.uri + ')';
|
|
});
|
|
});
|
|
teardown(function() {
|
|
cardsList.innerHTML = '';
|
|
if (getIconPromise.then.called) {
|
|
getIconPromise.mFulfillToValue({originalUrl: iconDataURI, blob: {}});
|
|
}
|
|
});
|
|
|
|
suite('render > ', function() {
|
|
|
|
test('does nothing when stayInvisible is set', function() {
|
|
var card = new Card(makeApp({
|
|
name: 'anyapp',
|
|
title: 'sometitle'
|
|
}), { stayInvisible: true });
|
|
|
|
assert.equal(card.element.style.display, 'none');
|
|
assert.equal(card.title, undefined);
|
|
});
|
|
|
|
test('adds browser class for browser windows', function(){
|
|
var card = new Card(makeApp({
|
|
name: 'browserapp',
|
|
isBrowser: true,
|
|
title: 'Page title'
|
|
}));
|
|
|
|
assert.ok(card.element.classList.contains('browser'),
|
|
'has browser class');
|
|
assert.equal(
|
|
card.element.querySelector('.title').textContent,
|
|
card.app.title);
|
|
assert.ok(!card.element.classList.contains('.private'));
|
|
});
|
|
|
|
test('adds private class for private windows', function(){
|
|
var card = new Card(makeApp({
|
|
name: 'privatewindow',
|
|
isBrowser: true,
|
|
isPrivate: true
|
|
}));
|
|
|
|
assert.ok(card.element.classList.contains('private'));
|
|
});
|
|
|
|
test('unkillable card', function(){
|
|
var app = makeApp({
|
|
name: 'privatewindow',
|
|
isBrowser: true,
|
|
isPrivate: true
|
|
});
|
|
app.attentionWindow = true; // makes the card unkillable
|
|
var card = new Card(app);
|
|
|
|
assert.equal(
|
|
card.element.querySelector('.close-button').style.visibility,
|
|
'hidden');
|
|
});
|
|
|
|
test('app security for browser windows', function() {
|
|
var app = makeApp({
|
|
name: 'browserwindow',
|
|
isBrowser: true
|
|
});
|
|
app.getSSLState = () => 'broken';
|
|
var card = new Card(app);
|
|
|
|
assert.equal(card.element.dataset.ssl, 'broken');
|
|
});
|
|
|
|
test('browser windows display URL in their subTitle', function() {
|
|
var app = makeApp({
|
|
name: 'browserwindow',
|
|
isBrowser: true
|
|
});
|
|
app.config.url = 'https://someorigin.org/foo';
|
|
var card = new Card(app);
|
|
this.sinon.stub(TaskManagerUtils, 'getDisplayUrlForApp')
|
|
.returns('someorigin.org/foo');
|
|
|
|
assert.ok(card.element.classList.contains('show-subtitle'));
|
|
assert.equal(
|
|
card.element.querySelector('.subtitle-url').textContent,
|
|
'someorigin.org/foo');
|
|
});
|
|
|
|
});
|
|
|
|
suite('loadIcon > ', function() {
|
|
|
|
test('calls loadAppIcon', function(){
|
|
this.sinon.stub(TaskManagerUtils, 'loadAppIcon')
|
|
.returns(Promise.resolve());
|
|
|
|
var card = new Card(makeApp({ name: 'app' }));
|
|
card.loadIcon();
|
|
|
|
assert.ok(TaskManagerUtils.loadAppIcon.called);
|
|
assert.equal(TaskManagerUtils.loadAppIcon.firstCall.args[0], card.app);
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
suite('system/SwipeToKillMotion', function() {
|
|
var motion, el, setTranslateY;
|
|
var currentY;
|
|
|
|
setup(function() {
|
|
el = document.createElement('div');
|
|
setTranslateY = this.sinon.stub();
|
|
currentY = 0;
|
|
motion = new SwipeToKillMotion(el, {
|
|
setTranslateY(newY) {
|
|
currentY = newY;
|
|
setTranslateY(currentY);
|
|
}
|
|
});
|
|
});
|
|
|
|
teardown(function() {
|
|
|
|
});
|
|
|
|
function touch(subtype, x, y) {
|
|
var evt = new CustomEvent('touch' + subtype);
|
|
evt.touches = evt.targetTouches = [];
|
|
if (arguments.length > 1) {
|
|
evt.touches.push({
|
|
clientX: x, clientY: y
|
|
});
|
|
}
|
|
return evt;
|
|
}
|
|
|
|
test('swipe up to kill', function() {
|
|
var dropStub = this.sinon.stub();
|
|
var willDragStub = this.sinon.stub();
|
|
el.addEventListener('card-dropped', dropStub);
|
|
el.addEventListener('card-will-drag', willDragStub);
|
|
|
|
motion.el.dispatchEvent(touch('start', 0, 0));
|
|
motion.el.dispatchEvent(touch('move', 0, -10));
|
|
assert.isTrue(motion.activelyDragging);
|
|
assert.equal(currentY, '-10px');
|
|
motion.el.dispatchEvent(touch('move', 0, -20));
|
|
motion.el.dispatchEvent(touch('move', 0, -30));
|
|
motion.el.dispatchEvent(touch('move', 0, -40));
|
|
motion.el.dispatchEvent(touch('end'));
|
|
assert.isFalse(motion.activelyDragging);
|
|
|
|
assert.equal(currentY, '-200%');
|
|
assert.isTrue(dropStub.calledOnce);
|
|
assert.isTrue(dropStub.firstCall.args[0].detail.willKill);
|
|
assert.isTrue(willDragStub.calledOnce);
|
|
});
|
|
|
|
test('swipe up but without enough velocity', function() {
|
|
var dropStub = this.sinon.stub();
|
|
el.addEventListener('card-dropped', dropStub);
|
|
|
|
motion.el.dispatchEvent(touch('start', 0, 0));
|
|
motion.el.dispatchEvent(touch('move', 0, -10));
|
|
motion.el.dispatchEvent(touch('move', 0, -20));
|
|
motion.el.dispatchEvent(touch('move', 0, -20));
|
|
motion.el.dispatchEvent(touch('move', 0, -10));
|
|
motion.el.dispatchEvent(touch('end'));
|
|
|
|
assert.equal(currentY, '0px');
|
|
assert.isTrue(dropStub.calledOnce);
|
|
assert.isFalse(dropStub.firstCall.args[0].detail.willKill);
|
|
});
|
|
|
|
test('touch events are intercepted by the card', function() {
|
|
var touchEvent = touch('start', 0, 0);
|
|
this.sinon.spy(touchEvent, 'stopPropagation');
|
|
motion.el.dispatchEvent(touchEvent);
|
|
assert.isTrue(touchEvent.stopPropagation.called);
|
|
});
|
|
|
|
test('swipe up, but event canceled', function() {
|
|
var dropStub = this.sinon.stub();
|
|
el.addEventListener('card-will-drag', (evt) => {
|
|
evt.preventDefault();
|
|
});
|
|
|
|
motion.el.dispatchEvent(touch('start', 0, 0));
|
|
motion.el.dispatchEvent(touch('move', 0, -10));
|
|
assert.equal(currentY, '0px');
|
|
motion.el.dispatchEvent(touch('move', 0, -20));
|
|
motion.el.dispatchEvent(touch('move', 0, -30));
|
|
motion.el.dispatchEvent(touch('move', 0, -40));
|
|
motion.el.dispatchEvent(touch('end'));
|
|
|
|
assert.equal(currentY, '0px');
|
|
assert.isFalse(dropStub.calledOnce); // no drop event expected
|
|
});
|
|
|
|
test('click while dragging should be ignored', function() {
|
|
motion.el.dispatchEvent(touch('start', 0, 0));
|
|
motion.el.dispatchEvent(touch('move', 0, -10));
|
|
motion.el.dispatchEvent(touch('move', 0, -20));
|
|
motion.el.dispatchEvent(touch('move', 0, -30));
|
|
|
|
var click = new CustomEvent('click', { cancelable: true });
|
|
this.sinon.spy(click, 'stopPropagation');
|
|
motion.el.dispatchEvent(click);
|
|
assert.isTrue(click.defaultPrevented);
|
|
assert.isTrue(click.stopPropagation.called);
|
|
});
|
|
|
|
});
|