Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,14 @@
{
"extends": "../../../../.jshintrc",
"node": true,
"predef": [
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"marionette",
"marionetteScriptFinished"
]
}

View file

@ -0,0 +1,124 @@
'use strict';
var APP_URL = 'app://smart-home.gaiamobile.org';
var Keys = {
'up': '\ue013',
'right': '\ue014',
'down': '\ue015',
'left': '\ue012',
'enter': '\ue006',
'esc': '\ue00c'
};
var assert = require('chai').assert;
var containsClass = function(elem, className) {
if (elem) {
return elem.getAttribute('class').indexOf(className) > -1;
}
return false;
};
// Bug 1207453 - Skip the test due to unknown test enviroment issue for now.
// We should investigate the issue and re-enable the test later.
marionette.skip('Smart Home', function() {
var opts = {
hostOptions: {
screen: {
width: 1920,
height: 1080
}
}
};
var client = marionette.client({
profile: opts,
// XXX: Set this to true once Accessibility is implemented in TV
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
setup(function() {
// Launch test app
client.apps.launch(APP_URL);
client.apps.switchToApp(APP_URL);
});
suite('> Card Filter', function() {
test('Hide card filter when enter edit mode and show card filter when' +
' exit edit mode ', { devices: ['tv'] }, function () {
var editButton = client.findElement('#edit-button');
var element = client.findElement('#main-section');
var searchButton = client.findElement('#search-button');
var deckTvCard = client.findElement('#card-list > .card');
var deckTvButton = client.findElement('.app-button.deck-tv');
client.waitFor(function() {
return containsClass(deckTvCard, 'focused');
});
client.waitFor(function() {
return containsClass(deckTvButton, 'focused');
});
element.sendKeys(Keys.up);
client.waitFor(function() {
return containsClass(searchButton, 'focused');
});
element.sendKeys(Keys.right);
client.waitFor(function() {
return containsClass(editButton, 'focused');
});
element.sendKeys(Keys.enter);
client.waitFor(function() {
return element.getAttribute('data-mode') === 'edit';
});
var cardFilter = client.findElement('#filter-tab-group');
assert.isTrue(containsClass(cardFilter, 'hidden'));
element.sendKeys(Keys.up);
element.sendKeys(Keys.enter);
assert.isFalse(containsClass(cardFilter, 'hidden'));
});
test('Collapse folder when filter is focused',
{ devices: ['tv'] }, function() {
// add new folder
client.executeScript(function() {
var home = window.wrappedJSObject.home;
var newFolder = home.cardManager.insertNewFolder({id: 'new-folder'},
home.cardManager._cardList.length);
var card = home.cardManager._deserializeCardEntry({group: 'application',
manifestURL: 'app://tv-epg.gaiamobile.org/manifest.webapp',
type: 'Application'
});
newFolder.addCard(card);
});
var element = client.findElement('#main-section');
var newFolderCard =
client.findElement('#card-list div.card:last-child');
client.waitFor(function() {
return containsClass(newFolderCard, 'focused');
});
// move focus to card filter
element.sendKeys(Keys.down);
element.sendKeys(Keys.down);
// wait until animation of card filter ends
var cardFilter = client.findElement('#filter-tab-group');
client.waitFor(function() {
return !containsClass(cardFilter, 'closed');
});
// wait for folder to be collapsed
client.helper.waitForElementToDisappear('#folder-list .card');
});
});
});

View file

@ -0,0 +1,18 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML"
]
}

View file

@ -0,0 +1,186 @@
/* global MockSmartButton, MockMenuGroup, CardFilter */
'use strict';
require('/shared/test/unit/mocks/mock_event_target.js');
require('/bower_components/evt/index.js');
requireApp('smart-home/test/unit/mock_smart_button.js');
requireApp('smart-home/test/unit/mock_menu_group.js');
requireApp('smart-home/js/card_filter.js');
suite('smart-home/CardFilter', function() {
var cardFilter;
var mockMenuGroup;
var mockSmartButtons;
var generateSmartButtons = function() {
return [
new MockSmartButton('filter'),
new MockSmartButton('tv'),
new MockSmartButton('dashbaord'),
new MockSmartButton('device'),
new MockSmartButton('application')
];
};
suite('> start', function() {
setup(function() {
mockSmartButtons = generateSmartButtons();
mockSmartButtons.forEach(function(button) {
sinon.spy(button, 'addEventListener');
});
mockMenuGroup = new MockMenuGroup();
sinon.stub(mockMenuGroup, 'querySelectorAll', function() {
return mockSmartButtons;
});
sinon.spy(mockMenuGroup, 'addEventListener');
cardFilter = new CardFilter();
});
teardown(function() {
mockMenuGroup = undefined;
cardFilter = undefined;
mockSmartButtons = undefined;
});
test('start() should register "click" event listeners on smart-button',
function() {
cardFilter.start(mockMenuGroup);
assert.isTrue(mockMenuGroup.querySelectorAll.calledOnce);
mockSmartButtons.forEach(function(button) {
assert.isTrue(button.addEventListener.calledOnce);
});
});
test('start() should listen to "opened" event on menu group', function() {
cardFilter.start(mockMenuGroup);
assert.isTrue(mockMenuGroup.addEventListener.calledOnce);
var call = mockMenuGroup.addEventListener.firstCall;
assert.equal(call.args[0], 'opened');
assert.isTrue(typeof call.args[1] === 'function');
});
});
suite('> stop', function() {
setup(function() {
mockSmartButtons = generateSmartButtons();
mockSmartButtons.forEach(function(button) {
sinon.spy(button, 'removeEventListener');
});
mockMenuGroup = new MockMenuGroup();
sinon.stub(mockMenuGroup, 'querySelectorAll', function() {
return mockSmartButtons;
});
cardFilter = new CardFilter();
cardFilter.start(mockMenuGroup);
});
teardown(function() {
mockMenuGroup = undefined;
cardFilter = undefined;
mockSmartButtons = undefined;
});
test('stop() should remove "click" event listeners on smart-button',
function() {
cardFilter.stop();
mockSmartButtons.forEach(function(button) {
assert.isTrue(button.removeEventListener.calledOnce);
});
});
});
suite('> handleEvent', function() {
setup(function() {
mockSmartButtons = generateSmartButtons();
mockMenuGroup = new MockMenuGroup();
sinon.stub(mockMenuGroup, 'querySelectorAll', function() {
return mockSmartButtons;
});
cardFilter = new CardFilter();
sinon.spy(cardFilter, 'handleEvent');
cardFilter.start(mockMenuGroup);
});
teardown(function() {
mockMenuGroup = undefined;
cardFilter = undefined;
mockSmartButtons = undefined;
});
test('handleEvent() should be called with event argument', function() {
mockSmartButtons[0].dispatchEvent({
type: 'click'
});
assert.isTrue(cardFilter.handleEvent.calledOnce);
assert.equal(cardFilter.handleEvent.getCall(0).args[0].type, 'click');
});
});
suite('> filter', function() {
setup(function() {
mockSmartButtons = generateSmartButtons();
mockMenuGroup = new MockMenuGroup();
sinon.stub(mockMenuGroup, 'querySelectorAll', function() {
return mockSmartButtons;
});
cardFilter = new CardFilter();
cardFilter.filter = 'filter';
sinon.spy(cardFilter, 'emit');
cardFilter.start(mockMenuGroup);
});
teardown(function() {
mockMenuGroup = undefined;
cardFilter = undefined;
mockSmartButtons = undefined;
});
test('should fire filterchanged event when filter is changed', function() {
cardFilter.filter = 'tv';
assert.isTrue(cardFilter.emit.calledOnce);
assert.equal(cardFilter.emit.getCall(0).args[0], 'filterchanged');
assert.equal(cardFilter.emit.getCall(0).args[1], 'tv');
});
});
suite('> show and hide', function() {
setup(function() {
mockSmartButtons = generateSmartButtons();
mockMenuGroup = new MockMenuGroup();
sinon.stub(mockMenuGroup, 'querySelectorAll', function() {
return mockSmartButtons;
});
sinon.spy(mockMenuGroup.classList, 'add');
sinon.spy(mockMenuGroup.classList, 'remove');
cardFilter = new CardFilter();
cardFilter.filter = 'filter';
cardFilter.start(mockMenuGroup);
});
teardown(function() {
mockMenuGroup.classList.add.restore();
mockMenuGroup.classList.remove.restore();
mockMenuGroup = undefined;
cardFilter = undefined;
mockSmartButtons = undefined;
});
test('should add "hidden" class to menu group when calling hide()',
function() {
cardFilter.hide();
assert.isTrue(mockMenuGroup.classList.add.calledWithExactly('hidden'));
});
test('should remove "hidden" class from menu group when calling show()',
function() {
cardFilter.show();
assert.isTrue(
mockMenuGroup.classList.remove.calledWithExactly('hidden'));
});
});
});

View file

@ -0,0 +1,623 @@
'use strict';
/* global Application, CardManager, Deck, Folder, AsyncSemaphore,
MockCardStore, MockPipedPromise, MockXMLHttpRequest */
require('/bower_components/evt/index.js');
require('/shared/js/async_semaphore.js');
require('/shared/js/uuid.js');
require('/shared/js/smart-screen/shared_utils.js');
require('/shared/test/unit/mocks/mock_piped_promise.js');
require('/shared/test/unit/mocks/mock_card_store.js');
require('/shared/test/unit/mocks/mock_xml_http_request.js');
require('/shared/js/smart-screen/cards/card.js');
require('/shared/js/smart-screen/cards/deck.js');
require('/shared/js/smart-screen/cards/folder.js');
require('/shared/js/smart-screen/cards/application.js');
suite('smart-home/CardManager', function() {
var realPipedPromise;
var decks;
var apps;
var folders;
suiteSetup(function(done) {
realPipedPromise = window.PipedPromise;
// real PipedPromise will make test failed because it waits for
// last promise to be resolved. But we need to call done in the last
// promise. So we use a MockPipedPromise (which is just a genuine native
// Promise) instead.
window.PipedPromise = MockPipedPromise;
decks = {
dashboard: new Deck({
name: {
raw: 'Dashboard'
},
group: 'dashboard',
nativeApp: {
name: 'Dashboard',
removable: false,
manifest: {},
manifestURL: 'app://dashboard.gaiamobile.org/manifest.webapp'
}
}),
tv: new Deck({
name: {
raw: 'TV'
},
group: 'tv',
nativeApp: {
name: 'TV',
removable: false,
manifest: {},
manifestURL: 'app://tv-deck.gaiamobile.org/manifest.webapp'
}
})
};
apps = {
music: new Application({
name: {
raw: 'Music'
},
group: 'application',
nativeApp: {
name: 'Music',
removable: false,
manifest: {},
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'
}
}),
video: new Application({
name: {
raw: 'Video'
},
group: 'application',
nativeApp: {
name: 'Video',
removable: false,
manifest: {},
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'
}
}),
gallery: new Application({
name: {
raw: 'Gallery'
},
group: 'application',
nativeApp: {
name: 'Gallery',
removable: false,
manifest: {},
manifestURL: 'app://gallery.gaiamobile.org/manifest.webapp'
}
})
};
folders = {
emptyOne: new Folder({
name: {
raw: 'New Empty Folder'
}
}),
emptyTwo: new Folder({
name: {
raw: 'New Empty Folder 2'
}
}),
nonEmpty: new Folder({
name: {
raw: 'Non Empty'
},
_cardsInFolder: [apps.video, apps.gallery]
})
};
require('/shared/js/smart-screen/card_manager.js', function() {
done();
});
});
suiteTeardown(function() {
decks = undefined;
apps = undefined;
folders = undefined;
window.PipedPromise = realPipedPromise;
});
function prepareCardManagerForTesting() {
var result = new CardManager();
// We bypass init() because it involves navigator.mozApps.mgmt
// Instead we need to prepare necessary properties of cardManager
result._asyncSemaphore = new AsyncSemaphore();
result._cardStore = MockCardStore;
result._cardList = [
decks.dashboard,
apps.music,
decks.tv,
folders.nonEmpty
];
result.installedApps = {
'app://dashboard.gaiamobile.org/manifest.webapp': {
name: 'Dashboard',
removable: false,
manifest: {},
manifestURL: 'app://dashboard.gaiamobile.org/manifest.webapp'
},
'app://music.gaiamobile.org/manifest.webapp': {
name: 'Music',
removable: false,
manifest: {},
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'
},
'app://tv-deck.gaiamobile.org/manifest.webapp': {
name: 'TV',
removable: false,
manifest: {},
manifestURL: 'app://tv-deck.gaiamobile.org/manifest.webapp'
},
'app://video.gaiamobile.org/manifest.webapp': {
name: 'Video',
removable: false,
manifest: {},
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'
},
'app://gallery.gaiamobile.org/manifest.webapp': {
name: 'Gallery',
removable: false,
manifest: {},
manifestURL: 'app://gallery.gaiamobile.org/manifest.webapp'
},
};
return result;
}
suite('_bestMatchingIcon()', function() {
var cardManager;
var app;
var manifestWithIconsInOrder = {
icons: {
'32': '/style/icons/32.png',
'64': '/style/icons/64.png',
'128': '/style/icons/128.png',
'256': '/style/icons/256.png'
}
};
var manifestWithIconsInRandom = {
icons: {
'32': '/style/icons/32.png',
'256': '/style/icons/256.png',
'128': '/style/icons/128.png',
'64': '/style/icons/64.png'
}
};
setup(function() {
cardManager = new CardManager();
app = {
origin: 'app://stub.gaiamobile.org/'
};
});
teardown(function() {
cardManager = undefined;
app = undefined;
});
test('call without preferredSize', function() {
var actual = cardManager._bestMatchingIcon(app, manifestWithIconsInOrder);
assert.equal(actual, 'app://stub.gaiamobile.org/style/icons/256.png');
});
test('call with random order of icon size and ' +
'without preferredSize ', function() {
var actual =
cardManager._bestMatchingIcon(app, manifestWithIconsInRandom);
assert.equal(actual, 'app://stub.gaiamobile.org/style/icons/256.png');
});
test('call with preferredSize', function() {
var actual =
cardManager._bestMatchingIcon(app, manifestWithIconsInOrder, 100);
assert.equal(actual, 'app://stub.gaiamobile.org/style/icons/128.png');
});
test('call with random order of icon size and ' +
'preferredSize ', function() {
var actual =
cardManager._bestMatchingIcon(app, manifestWithIconsInRandom, 100);
assert.equal(actual, 'app://stub.gaiamobile.org/style/icons/128.png');
});
});
suite('_reloadCardList()', function() {
var cardManager;
var realXMLHttpRequest;
setup(function() {
cardManager = new CardManager();
// We bypass init() because it involves navigator.mozApps.mgmt
// Instead we need to prepare necessary properties of cardManager
cardManager._cardList = [];
cardManager._cardStore = MockCardStore;
cardManager._asyncSemaphore = new AsyncSemaphore();
realXMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = MockXMLHttpRequest;
this.sinon.spy(cardManager, 'writeCardlistInCardStore');
});
teardown(function() {
MockCardStore.mClearData();
window.XMLHttpRequest = realXMLHttpRequest;
cardManager = undefined;
});
test('should load cardList from datastore if possible', function(done) {
MockCardStore.mPrepareData('cardList', [{
'name': 'Television',
'type': 'Deck'
}]);
cardManager._reloadCardList().then(function() {
assert.isTrue(cardManager._cardList.length > 0);
assert.equal(cardManager._cardList[0].name, 'Television');
}).then(done, done);
});
test('should load cardList from file if nothing in datastore',
function(done) {
cardManager._reloadCardList().then(function() {
assert.isTrue(cardManager._cardList.length > 0);
assert.equal(cardManager._cardList[0].name, 'Devices');
assert.isTrue(cardManager.writeCardlistInCardStore.calledOnce);
}, function(reason) {
assert.fail('should not reject promise due to ' + reason);
}).then(done, done);
// XXX: We should remove window.setTimeout in test but since we are
// guarding this test with done(), let's just put it as is until we
// have better solution
window.setTimeout(function() {
MockXMLHttpRequest.triggerReadyStateChange({
status: 200,
response: {
'card_list': [{
'name': 'Devices',
'type': 'Deck'
}]
}
});
});
});
});
suite('findCardFromCardList()', function() {
var cardManager;
var dashboardCardId;
var folderCardId;
setup(function() {
cardManager = prepareCardManagerForTesting();
dashboardCardId = cardManager._cardList[0].cardId;
folderCardId = cardManager._cardList[3].cardId;
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
dashboardCardId = undefined;
folderCardId = undefined;
});
test('should find card based on cardId', function() {
var card = cardManager.findCardFromCardList({
cardId: dashboardCardId
});
assert.equal(card.name.raw, 'Dashboard');
});
test('should find folder based on cardId', function() {
var card = cardManager.findCardFromCardList({
cardId: folderCardId
});
assert.isTrue(card instanceof Folder);
assert.equal(card.name.raw, 'Non Empty');
});
test('should return undefined by querying non-existing launchURL',
function() {
var card = cardManager.findCardFromCardList({
manifestURL: 'app://tv-deck.gaiamobile.org/manifest.webapp',
launchURL: 'app://tv-deck.gaiamobile.org/#42'
});
assert.isUndefined(card);
});
test('should return undefined if not found', function() {
var card = cardManager.findCardFromCardList({
cardId: 'incorrect-card-id'
});
assert.isUndefined(card);
});
test('should find card inside of folder', function() {
var card = cardManager.findCardFromCardList({
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'
});
assert.equal(card.name.raw, 'Video');
});
test('should be able to find folder by querying via cardEntry', function() {
var folderEntry = folders.nonEmpty.serialize();
var card = cardManager.findCardFromCardList({
cardEntry: folderEntry
});
assert.equal(card, folders.nonEmpty);
});
});
suite('getFilteredCardList()', function() {
var cardManager;
setup(function() {
cardManager = prepareCardManagerForTesting();
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('should get only apps if filter="application"', function(done) {
var filter = 'application';
cardManager.getFilteredCardList(filter).then(function(cards) {
assert.isTrue(cards.length === 1);
}).then(done, done);
});
test('should get all cards if filter="all"', function(done) {
var filter = 'all';
cardManager.getFilteredCardList(filter).then(function(cards) {
assert.isTrue(cards.length === cardManager._cardList.length);
}).then(done, done);
});
test('should get empty array if filter is undefined', function(done) {
cardManager.getFilteredCardList().then(function(cards) {
assert.isTrue(cards.length === 0);
}).then(done, done);
});
});
suite('insertNewFolder()', function() {
var cardManager;
var dashboardCardId;
setup(function() {
cardManager = new CardManager();
// We bypass init() because it involves navigator.mozApps.mgmt
// Instead we need to prepare necessary properties of cardManager
cardManager._asyncSemaphore = new AsyncSemaphore();
cardManager._cardStore = MockCardStore;
cardManager._cardList = [decks.dashboard];
cardManager.installedApps = {
'app://dashboard.gaiamobile.org/manifest.webapp': {
name: 'Dashboard',
removable: false,
manifest: {},
manifestURL: 'app://dashboard.gaiamobile.org/manifest.webapp'
}
};
dashboardCardId = cardManager._cardList[0].cardId;
this.sinon.spy(cardManager, 'writeFolderInCardStore');
this.sinon.spy(cardManager, 'writeCardlistInCardStore');
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('should return new folder instance whenever a new folder is inserted',
function() {
var newFolder = cardManager.insertNewFolder('an empty folder');
assert.ok(newFolder.folderId);
assert.equal(newFolder.state, Folder.STATES.DETACHED);
});
test('should write to data store when folder has content', function() {
var newFolder = cardManager.insertNewFolder('a test folder');
assert.isFalse(cardManager.writeCardlistInCardStore.calledOnce);
newFolder.addCard(new Application({
name: 'Music',
group: 'application'
}));
assert.isTrue(cardManager.writeCardlistInCardStore.calledOnce);
});
test('should insert at correct location if index is specified', function() {
var targetLocation = 0;
var newFolder =
cardManager.insertNewFolder('a test folder', targetLocation);
assert.isFalse(cardManager.writeCardlistInCardStore.calledOnce);
newFolder.addCard(apps.music);
assert.isTrue(cardManager.writeCardlistInCardStore.calledOnce);
assert.equal(cardManager._cardList[targetLocation], newFolder);
});
});
suite('insertCard()', function() {
var cardManager;
setup(function() {
cardManager = prepareCardManagerForTesting();
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('should not be able to insert the same card twice', function() {
var expectedCardListLength = cardManager._cardList.length;
cardManager.insertCard({
cardEntry: {
'type': 'Application',
'group': 'app',
'manifestURL': 'app://music.gaiamobile.org/manifest.webapp'
}
});
assert.equal(cardManager._cardList.length, expectedCardListLength);
});
test('should be able to insert same app twice with different launchURL',
function() {
var expectedCardListLength = cardManager._cardList.length + 1;
cardManager.insertCard({
cardEntry: {
'type': 'Application',
'group': 'tv',
'manifestURL': 'app://tv-deck.gaiamobile.org/manifest.webapp',
'launchURL': 'app://tv-deck.gaiamobile.org/#42'
}
});
assert.equal(cardManager._cardList.length, expectedCardListLength);
});
});
suite('removeCard()', function() {
var cardManager;
setup(function() {
cardManager = prepareCardManagerForTesting();
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('should be able to remove card with index specified', function() {
assert.ok(
cardManager.findCardFromCardList({
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'})
);
cardManager.removeCard(1); // remove card of 'Music' app
assert.isUndefined(
cardManager.findCardFromCardList({
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'})
);
});
test('should be able to remove card within folder', function(done) {
assert.ok(
cardManager.findCardFromCardList({
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'})
);
cardManager.getCardList().then(function(cardList) {
cardManager.removeCard(cardList[3].getCardList()[0]);
assert.isUndefined(
cardManager.findCardFromCardList({
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'})
);
}).then(done, done);
});
});
suite('writeCardlistInCardStore()', function() {
var cardManager;
setup(function() {
cardManager = new CardManager();
// We bypass init() because it involves navigator.mozApps.mgmt
// Instead we need to prepare necessary properties of cardManager
cardManager._asyncSemaphore = new AsyncSemaphore();
cardManager._cardStore = MockCardStore;
cardManager._cardList = [
decks.dashbaord,
folders.emptyOne,
folders.emptyTwo,
apps.music
];
cardManager.installedApps = {
'app://dashboard.gaiamobile.org/manifest.webapp': {
name: 'Dashboard',
removable: false,
manifest: {},
manifestURL: 'app://dashboard.gaiamobile.org/manifest.webapp'
},
'app://music.gaiamobile.org/manifest.webapp': {
name: 'Music',
removable: false,
manifest: {},
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'
}
};
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('write with empty folder should eliminate it from card list',
function(done) {
var expectedLength = cardManager._cardList.length - 2;
cardManager.writeCardlistInCardStore({
cleanEmptyFolder: true
}).then(function() {
assert.equal(cardManager._cardList.length, expectedLength);
assert.isTrue(cardManager._cardList.indexOf(folders.emptyOne) < 0);
}).then(done, done);
});
});
suite('swapCard()', function() {
var cardManager;
setup(function() {
cardManager = prepareCardManagerForTesting();
});
teardown(function() {
MockCardStore.mClearData();
cardManager = undefined;
});
test('swap adjacent cards', function() {
var card1 = cardManager._cardList[0];
var card2 = cardManager._cardList[1];
cardManager.swapCard(card1, card2);
assert.equal(cardManager._cardList[0], card2);
assert.equal(cardManager._cardList[1], card1);
});
test('swap non-adjacent cards', function() {
var card1 = cardManager._cardList[0];
var card2 = cardManager._cardList[2];
cardManager.swapCard(card1, card2);
assert.equal(cardManager._cardList[0], card2);
assert.equal(cardManager._cardList[2], card1);
});
});
});

View file

@ -0,0 +1,158 @@
/* global MockCardFilter, FilterManager, MockXScrollable, MockCardManager,
MockHome */
'use strict';
require('/bower_components/evt/index.js');
requireApp('smart-home/test/unit/mock_card_filter.js');
requireApp('smart-home/test/unit/mock_x_scrollable.js');
requireApp('smart-home/test/unit/mock_card_manager.js');
requireApp('smart-home/test/unit/mock_home.js');
requireApp('smart-home/js/filter_manager.js');
suite('smart-gome/FilterManager', function() {
var setupDOM = function() {
var filterTabGroup = document.createElement('menu');
filterTabGroup.id = 'filter-tab-group';
filterTabGroup.dataset.iconType = 'filter';
filterTabGroup.classList.add('filter-tab-group');
var bubbles = document.createElement('div');
bubbles.id = 'bubbles';
var cardListElem = document.createElement('div');
cardListElem.id = 'card-list';
cardListElem.setAttribute('smart-bubbles', 'true');
cardListElem.dataset.defaultItem = '0';
document.body.appendChild(filterTabGroup);
document.body.appendChild(bubbles);
document.body.appendChild(cardListElem);
};
var teardownDOM = function() {
document.body.removeChild(document.getElementById('filter-tab-group'));
document.body.removeChild(document.getElementById('bubbles'));
document.body.removeChild(document.getElementById('card-list'));
};
suite('> filterchanged event', function() {
var realCardFilter;
var filterManager;
setup(function() {
realCardFilter = window.CardFilter;
window.CardFilter = MockCardFilter;
setupDOM();
filterManager = new FilterManager();
filterManager.init({
cardListElem: document.getElementById('card-list'),
cardScrollable: new MockXScrollable(),
home: new MockHome(),
cardManager: new MockCardManager()
});
filterManager._smartBubblesElem.play = function() {};
filterManager._smartBubblesElem.stopImmediately = function() {};
sinon.spy(filterManager._smartBubblesElem, 'play');
sinon.spy(filterManager._smartBubblesElem, 'stopImmediately');
});
teardown(function() {
window.cardFilter = realCardFilter;
filterManager._smartBubblesElem.play.restore();
filterManager._smartBubblesElem.stopImmediately.restore();
filterManager = undefined;
teardownDOM();
});
test('start playing animation when filterchanged is fired', function() {
var cardListElem = document.getElementById('card-list');
filterManager._cardFilter.mFireEvent('filterchanged', 'tv');
assert.equal(
cardListElem.getAttribute('smart-bubbles-direction'), 'down');
assert.isTrue(filterManager._smartBubblesElem.play.calledOnce);
});
test('stop playing animation when filterchanged is fired before previous ' +
' animation ends', function() {
filterManager._isFilterChanging = true;
filterManager._cardFilter.mFireEvent('filterchanged', 'tv');
assert.isTrue(filterManager._smartBubblesElem.stopImmediately.calledOnce);
});
});
suite('> all-items-bubbled event', function() {
var realCardFilter;
var filterManager;
var mockXScrollable;
var mockCardManager;
var mockHome;
var cardListElem;
setup(function() {
realCardFilter = window.CardFilter;
window.CardFilter = MockCardFilter;
setupDOM();
cardListElem = document.getElementById('card-list');
mockXScrollable = new MockXScrollable();
mockCardManager = new MockCardManager();
filterManager = new FilterManager();
mockHome = new MockHome();
filterManager.init({
cardListElem: cardListElem,
cardScrollable: mockXScrollable,
home: mockHome,
cardManager: mockCardManager
});
filterManager._smartBubblesElem.play = function() {};
filterManager._smartBubblesElem.stopImmediately = function() {};
sinon.spy(filterManager, 'getFilterByIconName');
sinon.spy(mockXScrollable, 'clean');
sinon.spy(mockCardManager, 'getFilteredCardList');
});
teardown(function() {
window.cardFilter = realCardFilter;
filterManager.getFilterByIconName.restore();
filterManager = undefined;
mockXScrollable.clean.restore();
mockXScrollable = undefined;
mockCardManager.getFilteredCardList.restore();
mockCardManager = undefined;
mockHome = undefined;
teardownDOM();
});
test('clean scrollable and get filtered cards when bubbling down ' +
'animation ends', function() {
// pretend that we already went through 'onFilterChanged'
filterManager._isFilterChanging = true;
cardListElem.setAttribute('smart-bubbles-direction', 'down');
filterManager._smartBubblesElem.dispatchEvent(
new CustomEvent('all-items-bubbled'));
assert.isTrue(mockXScrollable.clean.calledOnce);
assert.isTrue(mockCardManager.getFilteredCardList.calledOnce);
});
test('reset _isFilterChanging to false when bubbling up animation ends',
function() {
// pretend that we already went through 'onFilterChanged'
filterManager._isFilterChanging = true;
cardListElem.removeAttribute('smart-bubbles-direction');
filterManager._smartBubblesElem.dispatchEvent(
new CustomEvent('all-items-bubbled'));
assert.isFalse(mockXScrollable.clean.calledOnce);
assert.isFalse(mockCardManager.getFilteredCardList.calledOnce);
});
});
});

View file

@ -0,0 +1,144 @@
'use strict';
/* global Deck, Folder, Application */
require('/bower_components/evt/index.js');
require('/shared/js/uuid.js');
require('/shared/js/smart-screen/cards/card.js');
require('/shared/js/smart-screen/cards/application.js');
require('/shared/js/smart-screen/cards/deck.js');
require('/shared/js/smart-screen/cards/folder.js');
suite('Folder', function() {
var folder;
var stubFolder;
var nonEmptyFolder;
var apps;
var deck;
var eventSpy;
setup(function() {
apps = {
music: new Application({
name: 'Music',
nativeApp: {
name: 'Music',
removable: false,
manifest: {},
manifestURL: 'app://music.gaiamobile.org/manifest.webapp'
}
}),
video: new Application({
name: 'Video',
nativeApp: {
name: 'Video',
removable: false,
manifest: {},
manifestURL: 'app://video.gaiamobile.org/manifest.webapp'
}
}),
gallery: new Application({
name: 'Gallery',
nativeApp: {
name: 'Gallery',
removable: false,
manifest: {},
manifestURL: 'app://gallery.gaiamobile.org/manifest.webapp'
}
})
};
folder = new Folder({
name: 'folder under test'
});
deck = new Deck({
nativeApp: {},
name: 'a deck'
});
stubFolder = new Folder({
name: 'folder as stub'
});
nonEmptyFolder = new Folder({
name: 'non empty',
_cardsInFolder: [apps.music, apps.video, apps.gallery]
});
eventSpy = this.sinon.spy();
nonEmptyFolder.on('folder-changed', eventSpy);
nonEmptyFolder.on('card-inserted', eventSpy);
nonEmptyFolder.on('card-removed', eventSpy);
nonEmptyFolder.on('card-updated', eventSpy);
nonEmptyFolder.on('card-swapped', eventSpy);
});
teardown(function() {
folder = undefined;
deck = undefined;
stubFolder = undefined;
nonEmptyFolder = undefined;
apps = undefined;
eventSpy = undefined;
});
var assertFolderIsEmpty = function(target) {
assert.isTrue(target._cardsInFolder.length === 0);
};
var assertFolderIsDirty = function(target) {
assert.equal(target.state, Folder.STATES.DIRTY);
};
test('should be able to add deck into folder', function() {
assertFolderIsEmpty(folder);
folder.addCard(deck);
assert.equal(folder.getCardList()[0], deck);
assertFolderIsDirty(folder);
});
test('should not be able to add folder into folder', function() {
assertFolderIsEmpty(stubFolder);
folder.addCard(stubFolder);
assertFolderIsEmpty(stubFolder);
assert.equal(stubFolder.state, Folder.STATES.NORMAL);
});
test('should be able to remove deck from folder', function() {
folder.getCardList()[0] = deck;
folder.removeCard(deck);
assertFolderIsEmpty(folder);
assertFolderIsDirty(folder);
});
test('should be able to update card in folder', function() {
var index = 0;
var cardToBeUpdated = nonEmptyFolder.getCardList()[index];
cardToBeUpdated.name = 'new gallery';
assert.isFalse(eventSpy.called);
nonEmptyFolder.updateCard(cardToBeUpdated, index);
assert.equal(
nonEmptyFolder.getCardList()[0].cardId, cardToBeUpdated.cardId);
assert.equal(nonEmptyFolder.getCardList()[0].name, cardToBeUpdated.name);
assert.isTrue(eventSpy.calledWithExactly(nonEmptyFolder));
assert.isTrue(eventSpy.calledWithExactly(cardToBeUpdated, index));
assertFolderIsDirty(nonEmptyFolder);
});
test('should be able to swap cards in folder', function() {
var card1 = nonEmptyFolder.getCardList()[0];
var card2 = nonEmptyFolder.getCardList()[2];
assert.isFalse(eventSpy.called);
nonEmptyFolder.swapCard(card1, card2);
assert.equal(nonEmptyFolder.getCardList()[0], card2);
assert.equal(nonEmptyFolder.getCardList()[2], card1);
assert.isTrue(eventSpy.calledWithExactly(nonEmptyFolder));
assert.isTrue(eventSpy.calledWithExactly(card2, card1, 0, 2));
assertFolderIsDirty(nonEmptyFolder);
});
});

View file

@ -0,0 +1,359 @@
'use strict';
/* global Folder, navigator, MockL10n, document, MocksHelper,
XScrollable, MozActivity, SpatialNavigator, CardManager, MessageHandler,
Home, SearchBar, FilterManager, Edit, Utils, Application, MockMozActivity,
FTEWizard */
require('/bower_components/evt/index.js');
require('/shared/js/uuid.js');
require('/shared/js/smart-screen/cards/card.js');
require('/shared/js/smart-screen/cards/application.js');
require('/shared/js/smart-screen/cards/deck.js');
require('/shared/js/smart-screen/cards/folder.js');
require('/shared/js/smart-screen/cards/app_bookmark.js');
require('/shared/js/smart-screen/clock.js');
require('/shared/test/unit/mocks/mock_event_target.js');
require('mock_card_manager.js');
require('mock_search_bar.js');
require('mock_menu_group.js');
require('mock_message_handler.js');
require('mock_x_scrollable.js');
require('mock_card_filter.js');
require('mock_edit.js');
require('mock_animations.js');
require('mock_utils.js');
require('mock_filter_manager.js');
require('/shared/test/unit/mocks/smart-screen/mock_spatial_navigator.js');
require('/shared/test/unit/mocks/smart-screen/mock_key_navigation_adapter.js');
require('/shared/test/unit/mocks/smart-screen/mock_f_t_e_wizard.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/tv_apps/smart-home/js/home.js');
var mocksHelperForHomeTest = new MocksHelper([
'CardManager',
'CardFilter',
'SearchBar',
'MessageHandler',
'XScrollable',
'SpatialNavigator',
'KeyNavigationAdapter',
'Animations',
'Edit',
'MozActivity',
'Utils',
'FilterManager',
'FTEWizard'
]).init();
suite('home', function() {
var realL10n = navigator.mozL10n;
var realMozActivity = window.MozActivity;
var searchButton, settingsButton, editButton, timeElem;
var filterTabGroup, filterAllButton;
var subject;
var fakeTimer;
mocksHelperForHomeTest.attachTestHelpers();
suiteSetup(function() {
realL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
searchButton = document.createElement('button');
searchButton.id = 'search-button';
document.body.appendChild(searchButton);
settingsButton = document.createElement('button');
settingsButton.id = 'settings-button';
document.body.appendChild(settingsButton);
editButton = document.createElement('button');
editButton.id = 'edit-button';
document.body.appendChild(editButton);
timeElem = document.createElement('div');
timeElem.id = 'time';
document.body.appendChild(timeElem);
filterTabGroup = document.createElement('menu-group');
filterTabGroup.once = function() {};
filterTabGroup.open = function() {};
filterTabGroup.close = function() {};
filterTabGroup.id = 'filter-tab-group';
filterAllButton = document.createElement('smart-button');
filterAllButton.id = 'filter-all-button';
filterTabGroup.appendChild(filterAllButton);
document.body.appendChild(filterTabGroup);
});
suiteTeardown(function() {
navigator.mozL10n = realL10n;
document.body.removeChild(searchButton);
document.body.removeChild(settingsButton);
document.body.removeChild(editButton);
document.body.removeChild(timeElem);
document.body.removeChild(filterTabGroup);
});
setup(function() {
subject = new Home();
subject.cardScrollable = new XScrollable();
subject.folderScrollable = new XScrollable();
subject.messageHandler = new MessageHandler();
subject.spatialNavigator = new SpatialNavigator();
subject.cardManager = new CardManager();
subject.searchBar = new SearchBar();
subject.filterManager = new FilterManager();
subject._fteWizard = new FTEWizard();
subject.edit = new Edit();
subject.cardListElem = document.createElement('div');
subject.spatialNavigator.m_focusedElement = document.createElement('div');
subject.isNavigable = true;
subject.settingsButton = settingsButton;
subject.editButton = editButton;
subject.searchButton = searchButton;
fakeTimer = this.sinon.useFakeTimers();
window.MozActivity = MockMozActivity;
});
teardown(function() {
window.MozActivity = realMozActivity;
});
suite('visibilityChange > ', function() {
suite('visible > ', function() {
setup(function() {
Object.defineProperty(document, 'visibilityState', {
value: 'visible',
writeable: true,
configurable: true
});
});
test('should set focus back on current focused element',
function(done) {
subject.spatialNavigator.focus = done;
subject.onVisibilityChange();
assert.isTrue(Utils.holdFocusForAnimation.calledOnce);
});
test('should start activity', function(done) {
subject.messageHandler.resumeActivity = done;
subject.onVisibilityChange();
});
});
suite('invisible > ', function() {
setup(function() {
Object.defineProperty(document, 'visibilityState', {
value: 'hidden',
writeable: true,
configurable: true
});
});
test('should stop activity when invisible', function() {
var spy = this.sinon.spy(subject.messageHandler, 'stopActivity');
subject.onVisibilityChange();
assert.isTrue(spy.calledOnce);
});
test('should reset filter', function() {
var spy = this.sinon.spy(subject.filterManager, 'resetFilter');
subject.onVisibilityChange();
assert.isTrue(spy.calledOnce);
});
});
});
suite('initClock >', function() {
test('should set time after time elapsed', function() {
subject.initClock();
var stub = this.sinon.stub(subject, 'updateClock');
fakeTimer.tick(1000);
assert.isTrue(stub.calledOnce);
});
});
suite('onCardInserted >', function() {
test('should insert a card into scrollable', function() {
var card1 = new Folder({});
var card2 = new Folder({});
var stub = this.sinon.stub();
var testCase1 = stub.withArgs(sinon.match.truthy, 0);
var testCase2 = stub.withArgs(sinon.match.truthy, 1);
subject.cardScrollable.insertNodeBefore = stub;
subject.onCardInserted(subject.cardScrollable, card1, 0);
assert.isTrue(testCase1.calledOnce);
assert.isFalse(testCase2.calledOnce);
subject.onCardInserted(subject.cardScrollable, card2, 1);
assert.isTrue(testCase2.calledOnce);
});
});
suite('onCardRemoved >', function() {
test('should remove specified cards from scrollable', function() {
var stub = this.sinon.stub();
subject.cardScrollable.removeNodes = stub;
subject.onCardRemoved(subject.cardScrollable, [0, 1]);
assert.isTrue(stub.withArgs([0, 1]).calledOnce);
});
});
suite('onMove >', function() {
test('should ask spatial navigator for next focus on non-scrollable target',
function() {
subject.spatialNavigator.m_focusedElement = document.createElement('div');
var stub = this.sinon.stub();
subject.spatialNavigator.move = stub;
subject.onMove('left');
assert.isTrue(stub.calledWith('left'));
});
test('should ask scrollable to move its focus on scrollable target',
function() {
var scrollable = new XScrollable();
scrollable.move = this.sinon.stub();
scrollable.move.returns(true);
subject.spatialNavigator.m_focusedElement = scrollable;
subject.onMove('right');
assert.isTrue(scrollable.move.calledWith('right'));
});
test('should ask scrollable to move its focus on scrollable target, but ' +
'fall-back to spatial navigator when scrollable didn\'t handle it',
function() {
var scrollable = new XScrollable();
scrollable.move = this.sinon.stub();
scrollable.move.returns(false);
subject.spatialNavigator.m_focusedElement = scrollable;
var spatialNavigatorMoveStub = this.sinon.stub();
subject.spatialNavigator.move = spatialNavigatorMoveStub;
subject.onMove('up');
assert.isTrue(scrollable.move.calledWith('up'));
assert.isTrue(spatialNavigatorMoveStub.calledWith('up'));
});
// TODO: add case
// 'should bypass logic of onMove if edit.onMove returns true'
});
suite('onEnter >', function() {
test('should open settings on settings button', function() {
subject.handleFocus(settingsButton);
assert.equal(MozActivity.calls.length, 0);
subject.onEnter();
assert.equal(MozActivity.calls[0].name, 'configure');
});
test('should toggle edit mode on edit button', function() {
subject.handleFocus(editButton);
subject.edit.toggleEditMode = this.sinon.stub();
subject.onEnter();
assert.isTrue(subject.edit.toggleEditMode.calledOnce);
});
test('should launch application on card ', function() {
var testCard = document.createElement('div');
testCard.dataset.cardId = '123';
var testApp = new Application({});
testApp.launch = this.sinon.stub();
subject.cardManager.m_card['123'] = testApp;
subject.handleFocus(testCard);
subject.onEnter();
assert.isTrue(testApp.launch.calledOnce);
});
// TODO: add case
// 'should bypass logic of onEnter if edit.onEnter returns true'
});
suite('onSearchBarShown >', function() {
setup(function() {
this.sinon.stub(document, 'addEventListener')
.withArgs('visibilitychange').callsArg(1);
subject.searchBar.hide = this.sinon.stub();
});
test('should fire search activity', function() {
assert.equal(MozActivity.calls.length, 0);
subject.onSearchBarShown();
assert.equal(MozActivity.calls[0].name, 'search');
});
test('should hide search bar after visibility has changed', function(done) {
subject.searchBar.hide = function() {
assert.isTrue(true);
done();
};
subject.onSearchBarShown();
});
});
suite('onSearchBarHidden >', function() {
test('should show search button again', function() {
searchButton.classList.add('hidden');
subject.onSearchBarHidden();
assert.isFalse(searchButton.classList.contains('hidden'));
});
});
suite('handleFocus >', function() {
test('should switch focus to scrollable target', function() {
var spy = this.sinon.spy(subject.cardScrollable, 'focus');
subject.handleFocus(subject.cardScrollable);
assert.isTrue(spy.calledOnce);
});
test('should switch focus to next focus for normal target', function() {
var spy = this.sinon.spy(editButton, 'focus');
subject.handleFocus(subject.editButton);
assert.isTrue(spy.calledOnce);
});
test('should open menu for menu group target', function() {
var stub = this.sinon.stub(subject, 'handleFocusMenuGroup');
subject.handleFocus(filterTabGroup);
assert.isTrue(stub.calledOnce);
});
test('should call checkFocusedGroup for non-menu elements', function() {
var stub = this.sinon.stub(subject, 'checkFocusedGroup');
subject.handleFocus(settingsButton);
assert.isTrue(stub.withArgs(settingsButton).calledOnce);
});
});
suite('checkFocusedGroup >', function() {
test('should close group menu when blur', function() {
subject.handleFocusMenuGroup(filterTabGroup);
var stub = this.sinon.stub(filterTabGroup, 'close');
subject.checkFocusedGroup();
assert.isTrue(stub.calledOnce);
});
});
// TODO: Test items:
// handleFocusMenuGroup >
// should switch navigable target to child elements of menu on open
// should switch navigable target back to menu iteslf on close
// handleScrollableItemFocus >
// should focus specified item of scrollable
// handleScrollableItemUnfocus >
// should blur specified item of scrollable
// openSettings >
// should launch configure activity
// updateClock >
// should update clock display
// restartClock >
// should stop and start clock display again
});

View file

@ -0,0 +1,13 @@
/* globals evt */
(function(exports) {
'use strict';
var MockAnimations = evt({
init: function() {},
doBubbleAnimation: function(parentElem, childClass, length, cb) {
cb();
}
});
exports.MockAnimations = MockAnimations;
})(window);

View file

@ -0,0 +1,48 @@
(function(exports) {
'use strict';
var MockCardFilter = function() {
this.mEventHandlers = {};
};
MockCardFilter.FILTERS = Object.freeze({
'ALL': 'filter',
'TV': 'tv',
'DASHBOARD': 'dashboard',
'DEVICE': 'device',
'APPLICATION': 'application'
});
MockCardFilter.prototype = {
start: function() {},
stop: function() {},
on: function(eventName, callback) {
if (typeof callback === 'function') {
if (!this.mEventHandlers[eventName]) {
this.mEventHandlers[eventName] = [];
}
this.mEventHandlers[eventName].push(callback);
}
},
off: function(eventName, callback) {
var handlers = this.mEventHandlers[eventName];
if (typeof callback === 'function' && handlers) {
var index = handlers.indexOf(callback);
handlers.splice(index, 1);
}
},
mFireEvent: function(eventName, detail) {
var handlers = this.mEventHandlers[eventName];
if (handlers) {
handlers.forEach(function(callback) {
if (typeof callback === 'function') {
callback(detail);
}
});
}
}
};
exports.MockCardFilter = MockCardFilter;
}(window));

View file

@ -0,0 +1,40 @@
/* global Promise */
(function(exports) {
'use strict';
var MockCardManager = function() {};
MockCardManager.prototype = {
_cardList: [],
m_card: {},
init: function() {
return null;
},
mPrepareFilteredCardList: function(filteredCardList) {
this.mFilteredCardList = filteredCardList;
},
getFilteredCardList: function(name) {
var that = this;
return new Promise(function(resolve, reject) {
resolve(that.mFilteredCardList);
});
},
getCardList: function getCardList() {
return Promise.resolve(this._cardList);
},
findCardFromCardList: function(param) {
return this.m_card[param.cardId];
},
resolveCardName: function() {
return 'cardname';
}
};
exports.MockCardManager = MockCardManager;
}(window));

View file

@ -0,0 +1,16 @@
/* globals evt */
(function(exports) {
'use strict';
var MockEdit = function() {
};
MockEdit.prototype = evt({
init: function() {},
onMove: function() { return false; },
onEnter: function() {return false; }
});
exports.MockEdit = MockEdit;
})(window);

View file

@ -0,0 +1,15 @@
(function(exports) {
'use strict';
var MockFilterManager = function() {};
MockFilterManager.prototype = {
init: function() {
return null;
},
resetFilter: function() {}
};
exports.MockFilterManager = MockFilterManager;
}(window));

View file

@ -0,0 +1,15 @@
(function(exports) {
'use strict';
var MockHome = function() {};
MockHome.prototype = {
edit: {
on: function() {},
off: function() {}
},
createCardNode: function() {}
};
exports.MockHome = MockHome;
}(window));

View file

@ -0,0 +1,15 @@
/* global MockEventTarget */
(function(exports) {
'use strict';
var MockMenuGroup = function() {};
MockMenuGroup.prototype = Object.create(MockEventTarget.prototype);
MockMenuGroup.prototype.changeIcon = function() {};
MockMenuGroup.prototype.querySelectorAll = function() {};
MockMenuGroup.prototype.classList = {
add: function() {},
remove: function() {}
};
exports.MockMenuGroup = MockMenuGroup;
}(window));

View file

@ -0,0 +1,11 @@
'use strict';
var MockMessageHandler = function() {
};
MockMessageHandler.prototype = {
init: function() {},
stopActivity: function() {},
resumeActivity: function() { return false; }
};

View file

@ -0,0 +1,14 @@
/* globals evt */
(function(exports) {
'use strict';
var MockSearchBar = function() {
};
MockSearchBar.prototype = evt({
init: function() {}
});
exports.MockSearchBar = MockSearchBar;
})(window);

View file

@ -0,0 +1,17 @@
/* global MockEventTarget */
(function(exports) {
'use strict';
var MockSmartButton = function(iconType) {
this.dataset = {
iconType: iconType
};
this.classList = {
add: function() {},
remove: function() {}
};
};
MockSmartButton.prototype = Object.create(MockEventTarget.prototype);
exports.MockSmartButton = MockSmartButton;
}(window));

View file

@ -0,0 +1,12 @@
(function(exports) {
'use strict';
var MockUtils = {
mHoldFocusForAnimationCalled: false,
holdFocusForAnimation: sinon.stub(),
mTeardown: function() {
this.holdFocusForAnimation = sinon.stub();
}
};
exports.MockUtils = MockUtils;
})(window);

View file

@ -0,0 +1,27 @@
/* globals evt */
(function(exports) {
'use strict';
var MockXScrollable = function() {
this.currentItem = document.createElement('button');
};
MockXScrollable.prototype = evt({
m_items: [],
m_colspan: 0,
CLASS_NAME: 'XScrollable',
addNode: function() {},
resetScroll: function() {},
clean: function() {},
getNode: function(index) {
return this.m_items[index];
},
setColspanOnFocus: function(colspan) {
this.m_colspan = colspan;
},
focus: function() {}
});
exports.MockXScrollable = MockXScrollable;
}(window));

View file

@ -0,0 +1,4 @@
'use strict';
/* global require */
require('/shared/test/unit/mocks/mocks_helper.js');