VulpesOS/gaia/apps/system/test/unit/card_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

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);
});
});