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,18 @@
/* exported MockTabBar */
'use strict';
var MockTabBar = {
init: function() {},
playlistArray: {
indexOf: function() {
return -1;
}
},
view: {
classList: {
toggle: function() {}
}
}
};

View file

@ -0,0 +1,18 @@
/* exported MockTitleBar */
'use strict';
var MockTitleBar = {
init: function() {},
view: function() {
return document.getElementById('title');
},
playerIcon: function() {
return document.getElementById('title-player');
},
showBackArrow: function() {},
changeTitleText: function() {}
};

View file

@ -0,0 +1,130 @@
/* global ListView, Sanitizer, MocksHelper, ModeManager, FocusEvent,
MODE_LIST */
'use strict';
require('/shared/js/sanitizer.js');
require('/js/utils.js');
require('/js/ui/views/list_view.js');
require('/test/unit/mock_db.js');
require('/test/unit/mock_music.js');
require('/test/unit/metadata/mock_album_art_cache.js');
require('/test/unit/ui/views/mock_modemanager.js');
require('/test/unit/ui/views/mock_subList_view.js');
require('/test/unit/mock_lazy_loader.js');
var mocksForListViewHelper = new MocksHelper([
'LazyLoader',
'AlbumArtCache',
'ModeManager',
'SubListView',
'App',
'Database'
]).init();
suite('ListView test', function() {
mocksForListViewHelper.attachTestHelpers();
suiteSetup(function() {
ModeManager._view = ListView;
this.element = document.createElement('div');
this.element.id = 'views-list';
document.body.appendChild(this.element);
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
Sanitizer.createSafeHTML(
`<form id="views-list-search" role="search" class="skin-dark">
<button id="views-list-search-close" data-l10n-id="search-close"
type="submit">close</button>
<p>
<input id="views-list-search-input" type="search"
x-inputmode="verbatim"
data-l10n-id="search-music" placeholder="SearcH MusiC">
<button id="views-list-search-clear" type="reset">Clear</button>
</p>
</form>
<div id="views-list-anchor" role="listbox"></div>`
));
this.listView = ListView;
this.listView.init();
this.listView.activate( {
key: 'metadata.album',
range: null,
direction: 'nextunique',
option: 'album'
});
});
suiteTeardown(function() {
document.body.removeChild(this.element);
this.element = null;
});
suite('activate', function() {
test('content', function() {
// Mock data has 4 albums, with 3 different letters. So, 7 list items
var children = this.listView.anchor.children;
var count = children.length;
assert.equal(count, 7);
children = this.listView.anchor.querySelectorAll('.list-item');
count = children.length;
assert.equal(count, 4);
var first = this.listView.anchor.firstChild;
assert.equal(first.tagName, 'LI');
assert.ok(first.classList.contains('list-header'));
assert.equal(first.getAttribute('role'), 'heading');
assert.equal(first.textContent, 'C');
first = this.listView.anchor.querySelector('.list-item');
assert.equal(first.tagName, 'LI');
assert.equal(first.getAttribute('role'), 'presentation');
assert.equal(first.textContent, 'Crime of the CenturySupertramp');
var second = children[1];
assert.equal(second.tagName, 'LI');
assert.equal(first.getAttribute('role'), 'presentation');
assert.equal(second.textContent, 'Crisis? What Crisis?Supertramp');
});
});
suite('events', function() {
var spy;
test('click tile', function() {
spy = this.sinon.spy(ListView, 'activateSubListView');
var listItem = document.querySelector('.list-item > [role="option"]');
assert.ok(listItem);
listItem.click();
assert.ok(spy.called,
'ListView.activateSubListView() should have been called');
});
test('click search close', function() {
spy = this.sinon.spy(ListView, 'hideSearch');
document.getElementById('views-list-search-close').click();
assert.ok(spy.called, 'ListView.hideSearch() should have been called');
});
test('focus on search', function() {
// make sure this return the right value
this.sinon.stub(ModeManager, 'currentMode', () => MODE_LIST );
var spy = this.sinon.spy(ModeManager, 'start');
var input = document.getElementById('views-list-search-input');
input.dispatchEvent(new FocusEvent('focus'));
assert.ok(spy.called, 'ModeManager.start() should have been called');
});
});
});

View file

@ -0,0 +1,9 @@
/* exported MockListView */
'use strict';
var MockListView = {
init: function() {},
activate: function() {},
cancelEnumeration: function () {},
update: function() {}
};

View file

@ -0,0 +1,31 @@
/* exported MockModeManager, MODE_LIST, MODE_SUBLIST, MODE_TILES, MODE_PLAYER,
MODE_PICKER, MODE_SEARCH_FROM_TILES, MODE_SEARCH_FROM_LIST,
SETTINGS_OPTION_KEY */
'use strict';
var MODE_LIST = 0;
var MODE_TILES = 1;
var MODE_PLAYER = 2;
var MODE_SUBLIST = 3;
var MODE_SEARCH_FROM_TILES = 5;
var MODE_SEARCH_FROM_LIST = 6;
var MODE_PICKER = 7;
var SETTINGS_OPTION_KEY = 'settings_option_key';
var MockModeManager = {
_view: null,
get currentMode() {
return 0;
},
start: function() {},
push: function() {},
waitForView: function(mode, callback) {
assert.ok(this._view, 'You didn\'t set MockModeManager._view');
callback(this._view);
}
};

View file

@ -0,0 +1,11 @@
/* exported MockPlayerView */
'use strict';
var MockPlayerView = {
get isQueued() { return true; },
currentFileInfo: { metadata: {} },
playingBlob: { type: null },
init: function() {},
stop: function() {},
};

View file

@ -0,0 +1,6 @@
/* exported MockSearchView */
'use strict';
var MockSearchView = {
init: function() {}
};

View file

@ -0,0 +1,10 @@
/* exported MockSubListView */
'use strict';
var MockSubListView = {
init: function() {},
activate: function(option, data, index, keyRange, direction, callback) {
callback();
}
};

View file

@ -0,0 +1,7 @@
/* exported MockTilesView */
'use strict';
var MockTilesView = {
init: function() {},
hideSearch: function() {}
};

View file

@ -0,0 +1,300 @@
/* global MockL10n, ModeManager, MocksHelper, loadBodyHTML,
MODE_TILES, MODE_LIST, MODE_SUBLIST, MODE_PLAYER,
MODE_SEARCH_FROM_TILES, MODE_SEARCH_FROM_LIST, MODE_PICKER,
TilesView, ListView, SubListView, PlayerView, SearchView */
'use strict';
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_async_storage.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/test/unit/mock_lazy_loader.js');
require('/test/unit/mock_music.js');
require('/test/unit/ui/mock_title_bar.js');
require('/test/unit/ui/mock_tab_bar.js');
require('/test/unit/ui/views/mock_tiles_view.js');
require('/test/unit/ui/views/mock_list_view.js');
require('/test/unit/ui/views/mock_subList_view.js');
require('/test/unit/ui/views/mock_player_view.js');
require('/test/unit/ui/views/mock_search_view.js');
require('/js/ui/views/mode_manager.js');
var mocksForModeManagerHelper = new MocksHelper([
'LazyLoader',
'asyncStorage',
'App',
'TitleBar',
'TabBar',
'TilesView',
'ListView',
'SubListView',
'PlayerView',
'SearchView'
]).init();
suite('Mode Manager Test', function() {
var realL10n;
var mm;
mocksForModeManagerHelper.attachTestHelpers();
suiteSetup(function() {
realL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
loadBodyHTML('/index.html');
mm = ModeManager;
});
suiteTeardown(function() {
navigator.mozL10n = realL10n;
});
suite('Start some views', function() {
test('Start TILES', function() {
mm.start(MODE_TILES, function() {
assert.equal(mm.currentMode, MODE_TILES);
});
});
test('Start LIST', function() {
mm.start(MODE_LIST, function() {
assert.equal(mm.currentMode, MODE_LIST);
});
});
test('Start PICKER', function() {
mm.start(MODE_PICKER, function() {
assert.equal(mm.currentMode, MODE_PICKER);
});
});
test('Start SEARCH(From Tiles)', function() {
mm.start(MODE_SEARCH_FROM_TILES, function() {
assert.equal(mm.currentMode, MODE_SEARCH_FROM_TILES);
});
});
test('Start SEARCH(From List)', function() {
mm.start(MODE_SEARCH_FROM_LIST, function() {
assert.equal(mm.currentMode, MODE_SEARCH_FROM_LIST);
});
});
});
suite('Start then push some views', function() {
test('TILES > PLAYER', function() {
mm.start(MODE_TILES, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
test('LIST > SUBLIST', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_SUBLIST, function() {
assert.equal(mm.currentMode, MODE_SUBLIST);
});
});
});
test('LIST > PLAYER', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
test('PICKER > PLAYER', function() {
mm.start(MODE_PICKER, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
test('SEARCH(From Tiles) > PLAYER', function() {
mm.start(MODE_SEARCH_FROM_TILES, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
test('SEARCH(From List) > PLAYER', function() {
mm.start(MODE_SEARCH_FROM_LIST, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
test('LIST > SUBLIST > PLAYER', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
});
test('SEARCH(From Tiles) > SUBLIST > PLAYER', function() {
mm.start(MODE_SEARCH_FROM_TILES, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
});
test('SEARCH(From List) > SUBLIST > PLAYER', function() {
mm.start(MODE_SEARCH_FROM_LIST, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
assert.equal(mm.currentMode, MODE_PLAYER);
});
});
});
});
});
suite('Start then pop some views', function() {
test('TILES > PLAYER > TILES', function() {
mm.start(MODE_TILES, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
assert.equal(mm.currentMode, MODE_TILES);
});
});
});
test('LIST > PLAYER > LIST', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
assert.equal(mm.currentMode, MODE_LIST);
});
});
});
test('LIST > SUBLIST > LIST', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_SUBLIST, function() {
mm.pop();
assert.equal(mm.currentMode, MODE_LIST);
});
});
});
test('PICKER > PLAYER > PICKER', function() {
mm.start(MODE_PICKER, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
assert.equal(mm.currentMode, MODE_PICKER);
});
});
});
test('LIST > SUBLIST > PLAYER > SUBLIST > LIST', function() {
mm.start(MODE_LIST, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
mm.pop();
assert.equal(mm.currentMode, MODE_LIST);
});
});
});
});
test('SEARCH(Tiles) > SUBLIST > PLAYER > SUBLIST > SEARCH', function() {
mm.start(MODE_SEARCH_FROM_TILES, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
mm.pop();
assert.equal(mm.currentMode, MODE_SEARCH_FROM_TILES);
});
});
});
});
test('SEARCH(List) > SUBLIST > PLAYER > SUBLIST > SEARCH', function() {
mm.start(MODE_SEARCH_FROM_LIST, function() {
mm.push(MODE_SUBLIST, function() {
mm.push(MODE_PLAYER, function() {
mm.pop();
mm.pop();
assert.equal(mm.currentMode, MODE_SEARCH_FROM_LIST);
});
});
});
});
});
suite('Initialize some views', function() {
setup(function() {
for (var mode in mm.views) {
mm.views[mode].isLoaded = false;
}
});
test('Initialize TILES', function() {
var spy = this.sinon.spy(TilesView, 'init');
mm.waitForView(MODE_TILES, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
test('Initialize LIST', function() {
var spy = this.sinon.spy(ListView, 'init');
mm.waitForView(MODE_LIST, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
test('Initialize SUBLIST', function() {
var spy = this.sinon.spy(SubListView, 'init');
mm.waitForView(MODE_SUBLIST, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
test('Initialize PLAYER', function() {
var spy = this.sinon.spy(PlayerView, 'init');
mm.waitForView(MODE_PLAYER, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
test('Initialize SEARCH(Tiles)', function() {
var spy = this.sinon.spy(SearchView, 'init');
mm.waitForView(MODE_SEARCH_FROM_TILES, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
test('Initialize SEARCH(List)', function() {
var spy = this.sinon.spy(SearchView, 'init');
mm.waitForView(MODE_SEARCH_FROM_LIST, (view) => {
assert.ok(spy.calledOnce);
assert.equal(view.isLoaded, true);
});
});
});
});

View file

@ -0,0 +1,197 @@
/* global INTERRUPT_BEGIN, KeyboardEvent, KeyEvent, loadBodyHTML, MockL10n,
MocksHelper, PlaybackQueue, PlayerView, PLAYSTATUS_PAUSED,
PLAYSTATUS_PLAYING */
'use strict';
require('/js/ui/views/player_view.js');
require('/js/queue.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_async_storage.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/test/unit/ui/views/mock_modemanager.js');
var mocksForPlayerViewHelper = new MocksHelper([
'asyncStorage'
]).init();
function mockAudioElement() {
// we need to replace the audio element
var audioNode = document.getElementById('player-audio');
var parent = audioNode.parentNode;
parent.removeChild(audioNode);
audioNode = document.createElement('div');
audioNode.id = 'player-audio';
audioNode.currentTime = 100;
parent.appendChild(audioNode);
}
suite('Player View Test', function() {
var pv, ratings;
var realL10n = navigator.mozL10n;
mocksForPlayerViewHelper.attachTestHelpers();
function testRatingsAriaChecked(checkedIndex) {
pv.setRatings(checkedIndex);
Array.prototype.forEach.call(ratings, function(rating, index) {
assert.equal(checkedIndex - 1 === index ? 'true' : 'false',
rating.getAttribute('aria-checked'));
});
}
suiteSetup(function(done) {
navigator.mozL10n = MockL10n;
//Insert the star-rating bar into the dom
loadBodyHTML('/index.html');
//Initialize the Player View
pv = PlayerView;
//Override #setSeekBar with stub to avoid excess work in init()
sinon.stub(pv, 'setSeekBar');
PlaybackQueue.loadSettings().then(() => {
pv.init();
ratings = pv.ratings;
done();
});
});
suiteTeardown(function() {
navigator.mozL10n = realL10n;
});
suite('#setRating accessibility', function() {
test('aria-checked="true" when star matches star-rating. false otherwise',
function() {
[0, 1, 2, 3, 4, 5].forEach(testRatingsAriaChecked);
});
});
suite('show info', function() {
var spy;
setup(function() {
spy = this.sinon.spy(PlayerView, 'showInfo');
});
test('on player cover', function() {
['player-cover', 'player-cover-image-1',
'player-cover-image-2'].forEach(function(id) {
document.getElementById(id).click();
assert.ok(spy.called,
'PlayerView.showInfo should be called by click on ' + id);
});
});
test('on repeat', function() {
document.getElementById('player-album-repeat').click();
assert.ok(spy.called,
'PlayerView.showInfo should be called by click on repeat');
});
});
suite('update remote play status on audio event', function() {
var spy;
setup(function() {
spy = this.sinon.spy(PlayerView, 'updateRemotePlayStatus');
});
function sendEventAndTest(event, status) {
PlayerView.audio.dispatchEvent(new CustomEvent(event));
assert.ok(spy.called);
if (status) {
assert.equal(PlayerView.playStatus, status);
}
}
test('on play', function() {
sendEventAndTest('play', PLAYSTATUS_PLAYING);
});
test('on pause', function() {
sendEventAndTest('pause', PLAYSTATUS_PAUSED);
});
test('on interrupt begin', function() {
sendEventAndTest('mozinterruptbegin', INTERRUPT_BEGIN);
});
test('on interrupt end', function() {
sendEventAndTest('mozinterruptend', PLAYSTATUS_PLAYING);
});
});
suite('update seekbar', function() {
var spy;
setup(function() {
spy = this.sinon.spy(PlayerView, 'updateSeekBar');
});
test('on duration change', function() {
PlayerView.audio.dispatchEvent(new CustomEvent('durationchange'));
assert.ok(spy.called);
});
test('on timeupdate', function() {
PlayerView.audio.dispatchEvent(new CustomEvent('timeupdate'));
assert.ok(spy.called);
});
test('on visibilitychange', function() {
window.dispatchEvent(new CustomEvent('visibilitychange'));
assert.ok(spy.called);
});
});
suite('context menu', function() {
var spy;
setup(function() {
mockAudioElement();
spy = this.sinon.spy(PlayerView, 'startFastSeeking');
});
function sendEvent(id) {
var el = document.getElementById(id);
var ev = document.createEvent('MouseEvents');
ev.initMouseEvent('contextmenu', true, false, window, 0, 0, 0, 0, 0,
false, false, false, false, 2, null);
el.dispatchEvent(ev);
}
test('previous button', function() {
sendEvent('player-controls-previous');
assert.ok(spy.called);
});
test('next button', function() {
sendEvent('player-controls-next');
assert.ok(spy.called);
});
});
suite('keyboard seek', function() {
var spy;
setup(function() {
mockAudioElement();
spy = this.sinon.spy(PlayerView, 'seekAudio');
});
function sendEventAndTest(key) {
PlayerView.seekSlider.dispatchEvent(new KeyboardEvent('keypress',
{ keyCode: key }));
assert.ok(spy.called);
}
test('key up seek', function() {
sendEventAndTest(KeyEvent.DOM_VK_UP);
});
test('key down seek', function() {
sendEventAndTest(KeyEvent.DOM_VK_DOWN);
});
});
});

View file

@ -0,0 +1,138 @@
/* global SearchView, Sanitizer, MocksHelper, FocusEvent */
'use strict';
require('/shared/js/sanitizer.js');
require('/js/ui/views/search_view.js');
require('/test/unit/mock_music.js');
require('/test/unit/ui/views/mock_modemanager.js');
require('/test/unit/ui/mock_tab_bar.js');
var mocksForSearchViewHelper = new MocksHelper([
'App',
'ModeManager',
'TabBar'
]).init();
suite('SearchView', function() {
mocksForSearchViewHelper.attachTestHelpers();
suiteSetup(function() {
this.element = document.createElement('div');
this.element.setAttribute('id', 'views-search');
this.element.setAttribute('action', 'back');
this.element.setAttribute('no-font-fit', null);
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
Sanitizer.createSafeHTML(
`<form id="views-search-form" role="search" class="skin-dark">
<button id="views-search-close" data-l10n-id="search-close"
type="submit">close</button>
<p>
<input id="views-search-input" type="search" dir="auto"
x-inputmode="verbatim"
data-l10n-id="search-music" placeholder="SearcH MusiC">
<button id="views-search-clear" type="reset">Clear</button>
</p>
</form>
<div id="views-search-no-result" class="search-category hidden">
<p id="search-no-result" data-l10n-id="search-no-result">No
musiC founD</p>
</div>
<div id="views-search-anchor">
<div id="views-search-artists" class="search-category hidden">
<gaia-subheader skin="dark">
<span data-l10n-id="search-artists">ArtistS</span>
(<span class="search-result-count"></span>)
</gaia-subheader>
<div class="search-results" role="listbox"></div>
</div>
<div id="views-search-albums" class="search-category hidden">
<gaia-subheader skin="dark">
<span data-l10n-id="search-albums">AlbumS</span>
(<span class="search-result-count"></span>)
</gaia-subheader>
<div class="search-results" role="listbox"></div>
</div>
<div id="views-search-titles" class="search-category hidden">
<gaia-subheader skin="dark">
<span data-l10n-id="search-titles">TrackS</span>
(<span class="search-result-count"></span>)
</gaia-subheader>
<div class="search-results" role="listbox"></div>
</div>
</div>`
));
document.body.appendChild(this.element);
this.searchView = SearchView;
this.searchView.init();
});
suiteTeardown(function() {
document.body.removeChild(this.element);
this.element = null;
});
// send a fake touchend event to el
function sendTouch(el) {
var touchendEvent = document.createEvent('TouchEvent');
touchendEvent.initTouchEvent('touchend', true, true, window,
0, false, false, false, false,
null, null, null);
el.dispatchEvent(touchendEvent);
}
suite('test focus', function() {
var spy;
setup(function() {
spy = this.sinon.spy(SearchView, 'switchContext');
var searchInput = document.getElementById('views-search-input');
searchInput.dispatchEvent(new FocusEvent('focus'));
});
test('test context switched', function() {
assert.ok(spy.called, 'SearchView.switchContext() should be called');
assert.equal(SearchView.searchContext, SearchView.context.ALL);
}, this);
});
suite('test tap clear', function() {
var spy;
var searchInput;
setup(function() {
spy = this.sinon.spy(SearchView, 'clear');
searchInput = document.getElementById('views-search-input');
searchInput.value = 'this is not the search you are looking for';
var searchClear = document.getElementById('views-search-clear');
sendTouch(searchClear);
});
test('test that search is now empty', function() {
assert.ok(spy.called, 'SearchView.clear() should be called');
assert.equal(searchInput.value, '');
});
});
suite('test close', function() {
var spy, spy2;
setup(function() {
spy = this.sinon.spy(SearchView, 'hide');
spy2 = this.sinon.spy(SearchView, 'openResult');
var searchClose = document.getElementById('views-search-close');
searchClose.click();
});
test('test search is hidden', function() {
assert.ok(spy.called, 'SearchView.hide() should be called');
assert.ok(!spy2.called, 'SearchVie.openResult() should not be called');
}, this);
});
});

View file

@ -0,0 +1,121 @@
/* global SubListView, Sanitizer, MocksHelper, ModeManager, MockL10n */
'use strict';
require('/shared/js/sanitizer.js');
require('/js/utils.js');
require('/js/ui/views/subList_view.js');
require('/test/unit/mock_db.js');
require('/test/unit/metadata/mock_album_art_cache.js');
require('/test/unit/ui/mock_tab_bar.js');
require('/test/unit/ui/views/mock_modemanager.js');
require('/test/unit/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_l10n.js');
var mocksForSubListViewHelper = new MocksHelper([
'LazyLoader',
'AlbumArtCache',
'ModeManager',
'TabBar',
'Database'
]).init();
suite('SubListView test', function() {
var realL10n = navigator.mozL10n;
mocksForSubListViewHelper.attachTestHelpers();
suiteSetup(function(done) {
navigator.mozL10n = MockL10n;
ModeManager._view = SubListView;
this.element = document.createElement('div');
this.element.id = 'views-sublist';
document.body.appendChild(this.element);
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
Sanitizer.createSafeHTML(
`<div id="views-sublist-header">
<img id="views-sublist-header-image">
<div id="views-sublist-header-controls">
<bdi id="views-sublist-header-name">Name</bdi>
<button id="views-sublist-controls-play"
class="album-controls-button" data-icon="play"
data-l10n-id="playbackPlay"></button>
<button id="views-sublist-controls-shuffle"
class="album-controls-button" data-icon="shuffle"
data-l10n-id="shuffle-toggle"></button>
</div>
</div>
<div id="views-scrollable-sublist-anchor">
<div id="views-sublist-anchor" role="listbox">
</div>
</div>`
));
this.subListView = SubListView;
this.subListView.init();
var data = {
metadata: {
album: 'Crime of the Century',
artist: 'Supertramp',
title: 'School'
}
};
this.subListView.activate('album', data, 0, null, 'next',
() => done() );
});
suiteTeardown(function() {
document.body.removeChild(this.element);
this.element = null;
navigator.mozL10n = realL10n;
});
suite('activate', function() {
test('content', function() {
var children = this.subListView.anchor.children;
var count = children.length;
assert.equal(count, 4);
children = this.subListView.anchor.querySelectorAll('.list-item');
count = children.length;
assert.equal(count, 4);
var first = this.subListView.anchor.firstChild;
assert.equal(first.tagName, 'LI');
assert.equal(first.getAttribute('role'), 'presentation');
assert.equal(first.textContent, 'School');
var second = children[1];
assert.equal(second.tagName, 'LI');
assert.equal(first.getAttribute('role'), 'presentation');
assert.equal(second.textContent, 'Lady');
});
});
suite('events', function() {
var spy;
test('click tile', function() {
spy = this.sinon.spy(ModeManager, 'push');
var listItem = document.querySelector('.list-item > [role="option"]');
assert.ok(listItem);
listItem.click();
assert.ok(spy.called, 'ModeManager.push() should have been called');
});
test('click shuffle', function() {
spy = this.sinon.spy(ModeManager, 'push');
document.getElementById('views-sublist-controls-shuffle').click();
assert.ok(spy.called, 'ModeManager.push() should have been called');
});
});
});

View file

@ -0,0 +1,115 @@
/* global TilesView, Sanitizer, MocksHelper, ModeManager, FocusEvent,
MODE_TILES */
'use strict';
require('/shared/js/sanitizer.js');
require('/js/ui/views/tiles_view.js');
require('/test/unit/mock_db.js');
require('/test/unit/metadata/mock_album_art_cache.js');
require('/test/unit/ui/views/mock_modemanager.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
var mocksForTilesViewHelper = new MocksHelper([
'LazyLoader',
'AlbumArtCache',
'ModeManager',
'Database'
]).init();
suite('Tiles view test', function() {
mocksForTilesViewHelper.attachTestHelpers();
suiteSetup(function() {
this.element = document.createElement('div');
this.element.id = 'views-tiles';
document.body.appendChild(this.element);
this.element.innerHTML = Sanitizer.unwrapSafeHTML(
Sanitizer.createSafeHTML(
`<form id="views-tiles-search" role="search" class="skin-dark">
<button id="views-tiles-search-close" data-l10n-id="search-close"
type="submit">close</button>
<p>
<input id="views-tiles-search-input" type="search" dir="auto"
x-inputmode="verbatim"
data-l10n-id="search-music" placeholder="SearcH MusiC">
<button id="views-tiles-search-clear" type="reset">Clear</button>
</p>
</form>
<div id="views-tiles-anchor"></div>`
));
this.tilesView = TilesView;
this.tilesView.init();
});
suiteTeardown(function() {
document.body.removeChild(this.element);
this.element = null;
});
suite('activate', function() {
setup(function(done) {
this.tilesView.activate(() => done());
});
test('content', function() {
var children = this.tilesView.anchor.childNodes;
var count = children.length;
assert.equal(count, 4);
var first = this.tilesView.anchor.firstChild;
assert.equal(first.tagName, 'DIV');
assert.ok(first.classList.contains('main-tile'));
assert.ok(first.classList.contains('tile'));
assert.equal(first.textContent, 'SupertrampCrime of the Century');
var second = children[1];
assert.equal(second.tagName, 'DIV');
assert.ok(second.classList.contains('sub-tile'));
assert.ok(second.classList.contains('tile'));
assert.equal(second.textContent, 'Supertramp');
});
});
suite('events', function() {
var spy;
setup(function(done) {
this.tilesView.activate(() => done());
});
test('click tile', function() {
spy = this.sinon.spy(ModeManager, 'push');
var tile = document.querySelector('.tile > [role="button"]');
assert.ok(tile);
tile.click();
assert.ok(spy.called, 'ModeManager.push() should be called');
});
test('click search close', function() {
spy = this.sinon.spy(TilesView, 'hideSearch');
document.getElementById('views-tiles-search-close').click();
assert.ok(spy.called, 'TilesView.hideSearch() should have been called');
});
test('focus on search', function() {
// make sure this return the right value
this.sinon.stub(ModeManager, 'currentMode', () => MODE_TILES );
var spy = this.sinon.spy(ModeManager, 'start');
var input = document.getElementById('views-tiles-search-input');
input.dispatchEvent(new FocusEvent('focus'));
assert.ok(spy.called, 'ModeManager.start should have been called');
});
});
});