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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
54
gaia/apps/bookmark/js/activity_handler.js
Executable file
54
gaia/apps/bookmark/js/activity_handler.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
/* global utils, BookmarkEditor, BookmarkRemover */
|
||||
var ActivityHandler = {
|
||||
'save-bookmark': function ah_save(activity) {
|
||||
BookmarkEditor.init({
|
||||
data: activity.source.data,
|
||||
onsaved: function onsaved(saved) {
|
||||
window.addEventListener('status-hidden', function hidden() {
|
||||
window.removeEventListener('status-hidden', hidden);
|
||||
activity.postResult(saved ? 'saved' : 'updated');
|
||||
});
|
||||
|
||||
var msg = saved ?
|
||||
'added-to-home-screen-message' : 'updated-pinned-site';
|
||||
navigator.mozL10n.formatValue(msg)
|
||||
.then(msg => utils.status.show(msg));
|
||||
},
|
||||
oncancelled: function oncancelled() {
|
||||
activity.postError('cancelled');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'remove-bookmark': function ah_remove(activity) {
|
||||
BookmarkRemover.init({
|
||||
id: activity.source.data.url,
|
||||
onremoved: function onremoved() {
|
||||
activity.postResult('removed');
|
||||
},
|
||||
oncancelled: function oncancelled(e) {
|
||||
activity.postError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mozSetMessageHandler('activity', function onActivity(activity) {
|
||||
var name = activity.source.name;
|
||||
switch (name) {
|
||||
case 'save-bookmark':
|
||||
case 'remove-bookmark':
|
||||
if (activity.source.data.type === 'url') {
|
||||
ActivityHandler[name](activity);
|
||||
} else {
|
||||
activity.postError('type not supported');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
activity.postError('name not supported');
|
||||
}
|
||||
});
|
||||
175
gaia/apps/bookmark/js/bookmark_editor.js
Executable file
175
gaia/apps/bookmark/js/bookmark_editor.js
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
'use strict';
|
||||
|
||||
/* global UrlHelper, BookmarksDatabase, Icon */
|
||||
/* exported BookmarkEditor */
|
||||
|
||||
var BookmarkEditor = {
|
||||
BOOKMARK_ICON_SIZE: 60,
|
||||
APP_ICON_SIZE: 60,
|
||||
|
||||
init: function bookmarkEditor_show(options) {
|
||||
this.data = options.data;
|
||||
this.onsaved = options.onsaved;
|
||||
this.oncancelled = options.oncancelled;
|
||||
var mode = 'add';
|
||||
BookmarksDatabase.get(this.data.url).then((function got(bookmark) {
|
||||
if (bookmark) {
|
||||
this.data = bookmark;
|
||||
mode = 'put';
|
||||
}
|
||||
this._init(mode);
|
||||
}).bind(this), this._init.bind(this, mode));
|
||||
},
|
||||
|
||||
_init: function bookmarkEditor_init(mode) {
|
||||
this.mode = document.body.dataset.mode = mode;
|
||||
this.bookmarkTitle = document.getElementById('bookmark-title');
|
||||
this.bookmarkURL = document.getElementById('bookmark-url');
|
||||
this.bookmarkIcon = document.getElementById('bookmark-icon');
|
||||
this.header = document.getElementById('header');
|
||||
this.saveButton = document.getElementById('done-button');
|
||||
this.appInstallationSection = document.getElementById('app-installation');
|
||||
this.appIcon = document.getElementById('app-icon');
|
||||
this.appIconPlaceholder = document.getElementById('app-icon-placeholder');
|
||||
this.appNameText = document.getElementById('app-name');
|
||||
this.installAppButton = document.getElementById('install-app-button');
|
||||
|
||||
this.header.addEventListener('action', this.close.bind(this));
|
||||
this.saveListener = this.save.bind(this);
|
||||
this.saveButton.addEventListener('click', this.saveListener);
|
||||
|
||||
this.bookmarkTitle.value = this.data.name || '';
|
||||
|
||||
this.bookmarkURL.textContent = this.data.url;
|
||||
|
||||
this._renderIcon();
|
||||
|
||||
if (this.data.manifestURL) {
|
||||
this.manifestURL = this.data.manifestURL;
|
||||
this._fetchManifest(this.manifestURL);
|
||||
}
|
||||
|
||||
this._checkDoneButton();
|
||||
this.form = document.getElementById('bookmark-form');
|
||||
this.form.addEventListener('input', this._checkDoneButton.bind(this));
|
||||
this.form.addEventListener('submit', this._submit.bind(this));
|
||||
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
|
||||
this.clearButton = document.getElementById('bookmark-title-clear');
|
||||
this.clearButton.addEventListener(touchstart, this._clearTitle.bind(this));
|
||||
if (mode === 'put') {
|
||||
this._onEditMode();
|
||||
this.saveButton.setAttribute('data-l10n-id', 'done-action');
|
||||
} else {
|
||||
this.saveButton.setAttribute('data-l10n-id', 'add-action');
|
||||
}
|
||||
|
||||
// We're appending new elements to DOM so to make sure headers are
|
||||
// properly resized and centered, we emmit a lazyload event.
|
||||
// This will be removed when the gaia-header web component lands.
|
||||
window.dispatchEvent(new CustomEvent('lazyload', {
|
||||
detail: document.body
|
||||
}));
|
||||
},
|
||||
|
||||
_renderIcon: function renderIcon() {
|
||||
var icon = new Icon(this.bookmarkIcon, this.data.icon);
|
||||
icon.render({'size': this.BOOKMARK_ICON_SIZE});
|
||||
},
|
||||
|
||||
_renderAppIcon: function renderAppIcon(manifest, size) {
|
||||
// Parse icon URL from app manifest
|
||||
var iconURL = window.IconsHelper.getBestIconFromWebManifest(manifest, size);
|
||||
if (!iconURL) {
|
||||
return;
|
||||
}
|
||||
// Switch out placeholder for real icon when it loads
|
||||
this.appIconListener = this._handleAppIconLoad.bind(this);
|
||||
this.appIcon.addEventListener('load', this.appIconListener);
|
||||
this.appIcon.setAttribute('src', iconURL);
|
||||
},
|
||||
|
||||
_handleAppIconLoad: function _handleAppIconLoad() {
|
||||
this.appIconPlaceholder.classList.add('hidden');
|
||||
this.appIcon.classList.remove('hidden');
|
||||
this.appIcon.removeEventListener('load', this.appIconListener);
|
||||
},
|
||||
|
||||
_fetchManifest: function bookmarkEditor_fetchManifest(manifestURL) {
|
||||
var manifestPromise = window.WebManifestHelper.getManifest(manifestURL);
|
||||
|
||||
manifestPromise.then((function(manifestData) {
|
||||
if (manifestData) {
|
||||
this.installAppButtonListener = this._installApp.bind(this);
|
||||
this.installAppButton.addEventListener('click',
|
||||
this.installAppButtonListener);
|
||||
this.appNameText.textContent = manifestData.short_name ||
|
||||
manifestData.name;
|
||||
this.appInstallationSection.classList.remove('hidden');
|
||||
this._renderAppIcon(manifestData, manifestURL, this.APP_ICON_SIZE);
|
||||
}
|
||||
}).bind(this)).catch(function(error) {
|
||||
console.error('Unable to get web manifest: ' + error);
|
||||
});
|
||||
|
||||
return manifestPromise;
|
||||
},
|
||||
|
||||
_onEditMode: function bookmarkEditor_onEditMode() {
|
||||
// Done button will be disabled on edit mode once it is displayed
|
||||
this.saveButton.disabled = true;
|
||||
},
|
||||
|
||||
close: function bookmarkEditor_close() {
|
||||
this.oncancelled();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the submit case for the form when the user presses the enter key.
|
||||
* @param {Event} event The form submit event.
|
||||
*/
|
||||
_submit: function(event) {
|
||||
event.preventDefault();
|
||||
if (!this.saveButton.disabled) {
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
|
||||
_clearTitle: function bookmarkEditor_clearTitle(event) {
|
||||
event.preventDefault();
|
||||
this.bookmarkTitle.value = '';
|
||||
this._checkDoneButton();
|
||||
},
|
||||
|
||||
_checkDoneButton: function bookmarkEditor_checkDoneButton() {
|
||||
// If one of the fields is blank, the “Done” button should be dimmed and
|
||||
// inactive
|
||||
var title = this.bookmarkTitle.value.trim();
|
||||
this.saveButton.disabled = title === '';
|
||||
},
|
||||
|
||||
_installApp: function bookmarkEditor_installApp() {
|
||||
window.navigator.mozApps.install(this.manifestURL);
|
||||
},
|
||||
|
||||
save: function bookmarkEditor_save(evt) {
|
||||
this.saveButton.removeEventListener('click', this.saveListener);
|
||||
if (this.installAppButtonListener) {
|
||||
this.installAppButton.removeEventListener('click',
|
||||
this.installAppButtonListener);
|
||||
}
|
||||
|
||||
// Only allow urls to be bookmarked.
|
||||
// This is defensive check - callers should filter out non-URLs.
|
||||
var url = this.data.url.trim();
|
||||
if (UrlHelper.isNotURL(url)) {
|
||||
this.oncancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.name = this.bookmarkTitle.value;
|
||||
this.data.url = url;
|
||||
|
||||
BookmarksDatabase[this.mode](this.data).then(this.onsaved.bind(this),
|
||||
this.close.bind(this));
|
||||
}
|
||||
};
|
||||
39
gaia/apps/bookmark/js/bookmark_remover.js
Executable file
39
gaia/apps/bookmark/js/bookmark_remover.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
/* global BookmarksDatabase */
|
||||
/* exported BookmarkRemover */
|
||||
var BookmarkRemover = {
|
||||
init: function bookmarkRemover_init(data) {
|
||||
var id = data.id;
|
||||
BookmarksDatabase.get(id).then(function got(bookmark) {
|
||||
if (!bookmark) {
|
||||
data.oncancelled('bookmark does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
// Title and message
|
||||
var ttl = document.getElementById('title');
|
||||
var msg = document.getElementById('message');
|
||||
ttl.setAttribute('data-l10n-id', 'remove-title');
|
||||
ttl.setAttribute('data-l10n-args',
|
||||
JSON.stringify({ name: bookmark.name }));
|
||||
msg.setAttribute('data-l10n-id', 'remove-message');
|
||||
msg.setAttribute('data-l10n-args',
|
||||
JSON.stringify({ name: bookmark.name }));
|
||||
|
||||
// Action handlers
|
||||
document.getElementById('cancel-action').addEventListener('click',
|
||||
function onCancel() {
|
||||
data.oncancelled('cancelled');
|
||||
}
|
||||
);
|
||||
|
||||
document.getElementById('remove-action').addEventListener('click',
|
||||
function onConfirm(evt) {
|
||||
evt.target.removeEventListener('click', onConfirm);
|
||||
BookmarksDatabase.remove(id).then(data.onremoved, data.oncancelled);
|
||||
}
|
||||
);
|
||||
}, data.oncancelled);
|
||||
}
|
||||
};
|
||||
32
gaia/apps/bookmark/js/components/status-behavior.css
Executable file
32
gaia/apps/bookmark/js/components/status-behavior.css
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
section[role="status"] {
|
||||
animation-name: hide;
|
||||
animation-duration: .3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
section[role="status"].onviewport {
|
||||
animation-name: show;
|
||||
}
|
||||
|
||||
section[role="status"].hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
from {
|
||||
transform: translateY(100%)
|
||||
}
|
||||
to {
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hide {
|
||||
from {
|
||||
transform: translateY(0)
|
||||
}
|
||||
to {
|
||||
transform: translateY(100%)
|
||||
}
|
||||
}
|
||||
177
gaia/apps/bookmark/js/components/status.js
Executable file
177
gaia/apps/bookmark/js/components/status.js
Executable file
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
window.utils = window.utils || {};
|
||||
|
||||
window.utils.status = (function() {
|
||||
|
||||
// How many milliseconds is displayed the status component by default
|
||||
var DISPLAYED_TIME = 1500;
|
||||
|
||||
// References to the DOMElement(s) that renders the status UI component
|
||||
var section, content;
|
||||
|
||||
// The numerical ID of the timeout in order to hide UI component
|
||||
var timeoutID;
|
||||
|
||||
/*
|
||||
* Clears the callback in charge of hiding the component after timeout
|
||||
*/
|
||||
function clearHideTimeout() {
|
||||
if (timeoutID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.clearTimeout(timeoutID);
|
||||
timeoutID = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shows the status component
|
||||
*
|
||||
* @param{Object} Message. It could be a string or a DOMFragment that
|
||||
* represents the normal and strong strings
|
||||
*
|
||||
* @param{int} It defines the time that the status is displayed in ms. This
|
||||
* parameter is optional
|
||||
*
|
||||
*/
|
||||
function show(message, duration) {
|
||||
clearHideTimeout();
|
||||
content.innerHTML = '';
|
||||
|
||||
if (typeof message === 'string') {
|
||||
content.textContent = message;
|
||||
} else {
|
||||
try {
|
||||
// Here we should have a DOMFragment
|
||||
content.appendChild(message);
|
||||
} catch (ex) {
|
||||
console.error('DOMException: ' + ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
section.classList.remove('hidden');
|
||||
section.classList.add('onviewport');
|
||||
timeoutID = window.setTimeout(hide, duration || DISPLAYED_TIME);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is invoked when some animation is ended
|
||||
*/
|
||||
function animationEnd(evt) {
|
||||
var eventName = 'status-showed';
|
||||
|
||||
if (evt.animationName === 'hide') {
|
||||
clearHideTimeout();
|
||||
section.classList.add('hidden');
|
||||
eventName = 'status-hidden';
|
||||
}
|
||||
|
||||
window.dispatchEvent(new CustomEvent(eventName));
|
||||
}
|
||||
|
||||
/*
|
||||
* Hides the status component
|
||||
*/
|
||||
function hide() {
|
||||
section.classList.remove('onviewport');
|
||||
}
|
||||
|
||||
/*
|
||||
* Releases memory
|
||||
*/
|
||||
function destroy() {
|
||||
section.removeEventListener('animationend', animationEnd);
|
||||
document.body.removeChild(section);
|
||||
clearHideTimeout();
|
||||
section = content = null;
|
||||
}
|
||||
|
||||
function getPath() {
|
||||
return '/js/components/';
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the library. Basically it creates the markup:
|
||||
*
|
||||
* <section role="status">
|
||||
* <p>xxx</p>
|
||||
* </section>
|
||||
*/
|
||||
function initialize() {
|
||||
if (section) {
|
||||
return;
|
||||
}
|
||||
|
||||
section = document.createElement('section');
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = getPath() + 'status-behavior.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
section.setAttribute('role', 'status');
|
||||
section.classList.add('hidden');
|
||||
|
||||
content = document.createElement('p');
|
||||
|
||||
section.appendChild(content);
|
||||
section.addEventListener('animationend', animationEnd);
|
||||
|
||||
// to prevent flickering
|
||||
setTimeout(function append() {
|
||||
document.body.appendChild(section);
|
||||
});
|
||||
}
|
||||
|
||||
// Initializing the library
|
||||
if (document.readyState === 'complete') {
|
||||
initialize();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function loaded() {
|
||||
document.removeEventListener('DOMContentLoaded', loaded);
|
||||
initialize();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* The library is auto-initialized but it is for unit testing purposes
|
||||
*/
|
||||
init: initialize,
|
||||
|
||||
/*
|
||||
* Shows the status component
|
||||
*
|
||||
* @param{Object} Message. It could be a string or a DOMFragment that
|
||||
* represents the normal and strong strings
|
||||
*
|
||||
* @param{int} It defines the time that the status is displayed in ms
|
||||
*
|
||||
*/
|
||||
show: show,
|
||||
|
||||
/*
|
||||
* Hides the status component
|
||||
*/
|
||||
hide: hide,
|
||||
|
||||
/*
|
||||
* Releases memory
|
||||
*/
|
||||
destroy: destroy,
|
||||
|
||||
/*
|
||||
* Sets up the duration in milliseconds that a status is displayed
|
||||
*
|
||||
* @param{int} The time in milliseconds
|
||||
*
|
||||
*/
|
||||
setDuration: function setDuration(time) {
|
||||
DISPLAYED_TIME = time || DISPLAYED_TIME;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
18
gaia/apps/bookmark/jsdoc.json
Executable file
18
gaia/apps/bookmark/jsdoc.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "bookmark",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["apps/bookmark/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/bookmark"
|
||||
}
|
||||
}
|
||||
19
gaia/apps/bookmark/locales/bookmark.en-US.properties
Executable file
19
gaia/apps/bookmark/locales/bookmark.en-US.properties
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
# Add and update pinned websites UIs
|
||||
add-to-home-screen-title=Add to Home Screen
|
||||
edit-pinned-site-title=Edit Pinned Site
|
||||
|
||||
added-to-home-screen-message=Added to home screen
|
||||
updated-pinned-site=Updated pinned site
|
||||
|
||||
done-action=Done
|
||||
add-action=Add
|
||||
cancel-action=Cancel
|
||||
|
||||
add-application=Add Application
|
||||
add=Add
|
||||
application-available=An application is available for this web site.
|
||||
|
||||
# Remove pinned websites UI
|
||||
remove-title=Remove {{name}}
|
||||
remove-message=Remove {{name}} from system?
|
||||
remove-action=Remove
|
||||
59
gaia/apps/bookmark/manifest.webapp
Executable file
59
gaia/apps/bookmark/manifest.webapp
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"name": "Bookmark",
|
||||
"description": "Bookmark Manager",
|
||||
"type": "certified",
|
||||
"role": "system",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"permissions": {
|
||||
"themeable": {},
|
||||
"systemXHR": {},
|
||||
"moz-extremely-unstable-and-will-change-webcomponents": {}
|
||||
},
|
||||
"activities": {
|
||||
"save-bookmark": {
|
||||
"filters": {
|
||||
"type": "url",
|
||||
"url": {
|
||||
"required": true,
|
||||
"pattern": "(https?|rtsp|data):.{1,16384}"
|
||||
}
|
||||
},
|
||||
"disposition": "inline",
|
||||
"href": "/save.html",
|
||||
"returnValue": true
|
||||
},
|
||||
"remove-bookmark": {
|
||||
"filters": {
|
||||
"type": "url",
|
||||
"url": {
|
||||
"required": true,
|
||||
"pattern": "(https?|rtsp|data):.{1,16384}"
|
||||
}
|
||||
},
|
||||
"disposition": "inline",
|
||||
"href": "/remove.html",
|
||||
"returnValue": true
|
||||
}
|
||||
},
|
||||
"locales": {
|
||||
"en-US": {
|
||||
"name": "Bookmark",
|
||||
"description": "Bookmark Manager"
|
||||
},
|
||||
"fr": {
|
||||
"name": "Marque-pages",
|
||||
"description": "Gestionnaire de marque-pages"
|
||||
}
|
||||
},
|
||||
"default_locale": "en-US",
|
||||
"datastores-owned": {
|
||||
"bookmarks_store": {
|
||||
"access": "readwrite",
|
||||
"certified-only": false,
|
||||
"description": "Stores bookmarks"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
gaia/apps/bookmark/package.json
Executable file
10
gaia/apps/bookmark/package.json
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"webide": {
|
||||
"prepackage": {
|
||||
"command": "make",
|
||||
"env": ["APP=bookmark"],
|
||||
"cwd": "../.."
|
||||
},
|
||||
"packageDir": "../../build_stage/bookmark/"
|
||||
}
|
||||
}
|
||||
32
gaia/apps/bookmark/remove.html
Executable file
32
gaia/apps/bookmark/remove.html
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/confirm.css">
|
||||
|
||||
<script type="text/javascript" src="shared/js/l10n.js"></script>
|
||||
<script type="text/javascript" defer src="shared/js/bookmarks_database.js"></script>
|
||||
|
||||
<script type="text/javascript" defer src="js/bookmark_remover.js"></script>
|
||||
<script type="text/javascript" defer src="js/activity_handler.js"></script>
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/bookmark.{locale}.properties">
|
||||
</head>
|
||||
<body>
|
||||
<form role="dialog" data-type="confirm">
|
||||
<section>
|
||||
<h1 id="title"></h1>
|
||||
<p id="message"></p>
|
||||
</section>
|
||||
<menu>
|
||||
<button id="cancel-action" type="button" data-l10n-id="cancel-action"></button>
|
||||
<button id="remove-action" type="button" data-l10n-id="remove-action" class="danger"></button>
|
||||
</menu>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
64
gaia/apps/bookmark/save.html
Executable file
64
gaia/apps/bookmark/save.html
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/save.css">
|
||||
|
||||
<!-- Shared code -->
|
||||
<script type="text/javascript" src="shared/js/l10n.js"></script>
|
||||
<script type="text/javascript" defer src="shared/js/url_helper.js"></script>
|
||||
<script type="text/javascript" defer src="shared/js/bookmarks_database.js"></script>
|
||||
<script type="text/javascript" defer src="shared/js/web_manifest_helper.js"></script>
|
||||
<script type="text/javascript" defer src="shared/js/icons_helper.js"></script>
|
||||
<script type="text/javascript" defer src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/headers.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/status.css">
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css">
|
||||
|
||||
<!-- Link to component just to copy the default_icon here -->
|
||||
<!-- <script defer src="shared/elements/gaia_grid/script.js"></script> -->
|
||||
<script defer src="shared/elements/gaia_grid/js/grid_icon_renderer.js"></script>
|
||||
<script defer src="shared/js/homescreens/icon.js"></script>
|
||||
|
||||
<!-- Specific code -->
|
||||
<script type="text/javascript" defer src="js/bookmark_editor.js"></script>
|
||||
<script type="text/javascript" defer src="js/components/status.js"></script>
|
||||
<script type="text/javascript" defer src="js/activity_handler.js"></script>
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/bookmark.{locale}.properties">
|
||||
</head>
|
||||
<body class="theme-settings">
|
||||
<section id="bookmark-entry-sheet" role="region" class="skin-organic">
|
||||
<gaia-header id="header" action="close">
|
||||
<h1 id="add-bookmark-header" data-l10n-id="add-to-home-screen-title"></h1>
|
||||
<h1 id="edit-bookmark-header" data-l10n-id="edit-pinned-site-title"></h1>
|
||||
<button id="done-button" data-l10n-id="add-action"></button>
|
||||
</gaia-header>
|
||||
<form id="bookmark-form">
|
||||
<div id="bookmark-icon"></div>
|
||||
<p id="bookmark-url" dir="ltr"></p>
|
||||
<p>
|
||||
<input type="text" id="bookmark-title" dir="auto"></input>
|
||||
<button id="bookmark-title-clear" type="reset">Clear</button>
|
||||
</p>
|
||||
</form>
|
||||
<div id="app-installation" class="hidden">
|
||||
<h2><span data-l10n-id="add-application">Add Application</span></h2>
|
||||
<img id="app-icon-placeholder" src="../shared/elements/gaia_grid/images/default_icon.png">
|
||||
<img id="app-icon" class="hidden">
|
||||
<p id="app-name"></p>
|
||||
<button id="install-app-button" data-l10n-id="add">Add</button>
|
||||
<p data-l10n-id="application-available">An application is available for this web site.</p>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
8
gaia/apps/bookmark/style/common.css
Executable file
8
gaia/apps/bookmark/style/common.css
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
font-size: 10px;
|
||||
}
|
||||
118
gaia/apps/bookmark/style/save.css
Executable file
118
gaia/apps/bookmark/style/save.css
Executable file
|
|
@ -0,0 +1,118 @@
|
|||
html, body {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
#bookmark-entry-sheet {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#bookmark-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
#bookmark-form p {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add-bookmark-header, #edit-bookmark-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-mode="add"] #add-bookmark-header {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body[data-mode="put"] #edit-bookmark-header {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#bookmark-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url('../shared/elements/gaia_grid/images/default_icon.png');
|
||||
background-size: 8.4rem;
|
||||
display: block;
|
||||
margin-bottom: 2.4rem;
|
||||
}
|
||||
|
||||
#bookmark-url {
|
||||
font-size: 1.4rem;
|
||||
color: rgb(95, 95, 95);
|
||||
width: 100%;
|
||||
margin-bottom: 1.4rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 1.5rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
/* App Installation section */
|
||||
|
||||
#app-installation {
|
||||
padding: 1.5rem;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
#app-installation h2 {
|
||||
text-align: center;
|
||||
border-top: 0.2rem solid rgb(114, 114, 114);
|
||||
font-size: 1.4rem;
|
||||
color: rgb(114, 114, 114);
|
||||
margin: 0.8rem 0 -0.8rem 0;
|
||||
}
|
||||
|
||||
#app-installation h2 span {
|
||||
position: relative;
|
||||
top: -1rem;
|
||||
background-color: #f4f4f4;
|
||||
padding: 0 0.5rem;
|
||||
margin-bottom: 0;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#app-icon, #app-icon-placeholder {
|
||||
display: block;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin: 0.5rem auto 0 auto;
|
||||
}
|
||||
|
||||
#app-installation p {
|
||||
font-size: 1.4rem;
|
||||
color: rgb(80, 88, 89);
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
#app-installation #app-name {
|
||||
height: 2rem;
|
||||
font-size: 1.6rem;
|
||||
color: rgb(41, 41, 41);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#app-installation #install-app-button {
|
||||
display: block;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
padding: 0 5rem;
|
||||
}
|
||||
|
||||
#app-icon.hidden,
|
||||
#app-icon-placeholder.hidden,
|
||||
#app-installation.hidden {
|
||||
display: none;
|
||||
}
|
||||
27
gaia/apps/bookmark/test/build/integration/bookmark_test.js
Executable file
27
gaia/apps/bookmark/test/build/integration/bookmark_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Bookmark tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=bookmark make', function(done) {
|
||||
helper.exec('APP=bookmark make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/bookmark.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
7
gaia/apps/bookmark/test/marionette/client_options.js
Executable file
7
gaia/apps/bookmark/test/marionette/client_options.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* global module */
|
||||
module.exports = {
|
||||
settings: {
|
||||
'devtools.pseudolocalization.enabled': false,
|
||||
'language.current': 'en-US'
|
||||
}
|
||||
};
|
||||
12
gaia/apps/bookmark/test/marionette/fixtures/app.html
Executable file
12
gaia/apps/bookmark/test/marionette/fixtures/app.html
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>My App Title</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<header><h1>App</h1></header>
|
||||
<section>I am an app</section>
|
||||
</body>
|
||||
</html>
|
||||
BIN
gaia/apps/bookmark/test/marionette/fixtures/favicon.ico
Executable file
BIN
gaia/apps/bookmark/test/marionette/fixtures/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 2 KiB |
11
gaia/apps/bookmark/test/marionette/fixtures/manifest.json
Executable file
11
gaia/apps/bookmark/test/marionette/fixtures/manifest.json
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "My App Name",
|
||||
"short_name": "My App Shortname",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
43
gaia/apps/bookmark/test/marionette/install_app_test.js
Executable file
43
gaia/apps/bookmark/test/marionette/install_app_test.js
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
/* global __dirname, require, marionette, suiteSetup, suiteTeardown */
|
||||
/* global setup, test */
|
||||
|
||||
var Bookmark = require('../../../../apps/system/test/marionette/lib/bookmark');
|
||||
var Server = require('../../../../shared/test/integration/server');
|
||||
|
||||
marionette('Bookmark -', function() {
|
||||
var options = require(__dirname + '/client_options.js');
|
||||
options.settings['dev.gaia.pinning_the_web'] = false;
|
||||
var client = marionette.client({
|
||||
profile: options,
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var bookmark, server, system;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
Server.create(__dirname + '/fixtures/', function(err, _server) {
|
||||
server = _server;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
server.stop();
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
system = client.loader.getAppClass('system');
|
||||
system.waitForFullyLoaded();
|
||||
bookmark = new Bookmark(client, server);
|
||||
});
|
||||
|
||||
test('Install app from page', function() {
|
||||
|
||||
var url = server.url('app.html');
|
||||
|
||||
client.switchToFrame();
|
||||
bookmark.openAndInstall(url, 'My App Shortname', '/favicon.ico');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
197
gaia/apps/bookmark/test/unit/activity_handler_test.js
Executable file
197
gaia/apps/bookmark/test/unit/activity_handler_test.js
Executable file
|
|
@ -0,0 +1,197 @@
|
|||
'use strict';
|
||||
|
||||
/* global BookmarkEditor, msgHandler, realMozSetMessageHandler, utils,
|
||||
BookmarkRemover */
|
||||
/* global requireApp, require, suite, suiteTeardown, suiteSetup, test, assert,
|
||||
MocksHelper, MockL10n, sinon */
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
|
||||
requireApp('bookmark/test/unit/mock_mozsetmessagehandler.js');
|
||||
requireApp('bookmark/test/unit/mock_bookmark_editor.js');
|
||||
|
||||
requireApp('bookmark/js/bookmark_remover.js');
|
||||
requireApp('bookmark/js/components/status.js');
|
||||
requireApp('bookmark/js/activity_handler.js');
|
||||
|
||||
var mocksHelperForSaveBookmark = new MocksHelper([
|
||||
'BookmarkEditor'
|
||||
]);
|
||||
|
||||
mocksHelperForSaveBookmark.init();
|
||||
|
||||
suite('activity_handler.js >', function() {
|
||||
|
||||
mocksHelperForSaveBookmark.attachTestHelpers();
|
||||
var realMozL10n;
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozSetMessageHandler = realMozSetMessageHandler;
|
||||
navigator.mozL10n = realMozL10n;
|
||||
msgHandler.activity = null;
|
||||
});
|
||||
|
||||
function createSource(name, type) {
|
||||
return {
|
||||
name: name,
|
||||
data: {
|
||||
type: type
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
suite('Initialization >', function() {
|
||||
test('Bookmark app is listening right now ', function() {
|
||||
// The handler was defined
|
||||
assert.isFunction(msgHandler.activity);
|
||||
// The handler defines a parameter -> the activity
|
||||
assert.equal(msgHandler.activity.length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Adding and updating bookmarks >', function() {
|
||||
suiteSetup(function() {
|
||||
utils.status.init();
|
||||
// We change the duration of the status in order to avoid timeout
|
||||
utils.status.setDuration(400);
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
utils.status.destroy();
|
||||
});
|
||||
|
||||
test.skip('The new bookmark has been added correctly ', function(done) {
|
||||
msgHandler.activity({
|
||||
source: createSource('save-bookmark', 'url'),
|
||||
postResult: function(result) {
|
||||
assert.equal(result, 'saved');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// The user clicks on <save> button
|
||||
BookmarkEditor.save(true);
|
||||
});
|
||||
|
||||
test('The bookmark already exists and it is updated ', function(done) {
|
||||
msgHandler.activity({
|
||||
source: createSource('save-bookmark', 'url'),
|
||||
postResult: function(result) {
|
||||
assert.equal(result, 'updated');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// The user clicks on <save> button
|
||||
BookmarkEditor.save();
|
||||
});
|
||||
|
||||
test('Bookmarking failed - request cancelled by user ', function(done) {
|
||||
msgHandler.activity({
|
||||
source: createSource('save-bookmark', 'url'),
|
||||
postError: function(result) {
|
||||
assert.equal(result, 'cancelled');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// The user cancels the request
|
||||
BookmarkEditor.cancel();
|
||||
});
|
||||
|
||||
test('The status is displayed when a bookmark has been added correctly ',
|
||||
function(done) {
|
||||
msgHandler.activity({
|
||||
source: createSource('save-bookmark', 'url'),
|
||||
postResult: function() { }
|
||||
});
|
||||
|
||||
window.addEventListener('status-showed', function showed() {
|
||||
window.removeEventListener('status-showed', showed);
|
||||
assert.isTrue(document.querySelector('[role="status"] p').textContent.
|
||||
length > 0);
|
||||
});
|
||||
|
||||
window.addEventListener('status-hidden', function hidden() {
|
||||
window.removeEventListener('status-hidden', hidden);
|
||||
done();
|
||||
});
|
||||
|
||||
BookmarkEditor.save(true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Removing bookmarks >', function() {
|
||||
var stub = null,
|
||||
removed = true;
|
||||
|
||||
suiteSetup(function() {
|
||||
stub = sinon.stub(BookmarkRemover, 'init', function(options) {
|
||||
removed ? options.onremoved() : options.oncancelled();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test('The bookmark has been removed correctly ', function(done) {
|
||||
msgHandler.activity({
|
||||
source: createSource('remove-bookmark', 'url'),
|
||||
postResult: function(result) {
|
||||
assert.equal(result, 'removed');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Removing failed ', function(done) {
|
||||
removed = false;
|
||||
msgHandler.activity({
|
||||
source: createSource('remove-bookmark', 'url'),
|
||||
postError: done
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Common errors >', function() {
|
||||
test('Name not supported ', function(done) {
|
||||
// "save-watermelon" activities are not supported
|
||||
msgHandler.activity({
|
||||
source: createSource('save-watermelon', 'url'),
|
||||
postError: function(result) {
|
||||
assert.equal(result, 'name not supported');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Type not supported for save-bookmark', function(done) {
|
||||
// We don't support the type "cherry"
|
||||
msgHandler.activity({
|
||||
source: createSource('save-bookmark', 'cherry'),
|
||||
postError: function(result) {
|
||||
assert.equal(result, 'type not supported');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Type not supported for remove-bookmark', function(done) {
|
||||
// We don't support the type "lemon"
|
||||
msgHandler.activity({
|
||||
source: createSource('remove-bookmark', 'lemon'),
|
||||
postError: function(result) {
|
||||
assert.equal(result, 'type not supported');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
222
gaia/apps/bookmark/test/unit/bookmark_editor_test.js
Executable file
222
gaia/apps/bookmark/test/unit/bookmark_editor_test.js
Executable file
|
|
@ -0,0 +1,222 @@
|
|||
'use strict';
|
||||
|
||||
/* global loadBodyHTML, BookmarkEditor, BookmarksDatabase, Icon */
|
||||
/* global requireApp, require, suite, suiteTeardown, suiteSetup, test, assert,
|
||||
sinon, MocksHelper */
|
||||
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_web_manifest_helper.js');
|
||||
|
||||
require('/shared/test/unit/mocks/mock_icons_helper.js');
|
||||
require('/shared/js/homescreens/icon.js');
|
||||
requireApp('bookmark/js/bookmark_editor.js');
|
||||
require('/shared/js/bookmarks_database.js');
|
||||
require('/shared/js/url_helper.js');
|
||||
|
||||
var mocksForBookmarkEditor = new MocksHelper([
|
||||
'IconsHelper', 'WebManifestHelper'
|
||||
]).init();
|
||||
|
||||
suite('bookmark_editor.js >', function() {
|
||||
|
||||
var getStub, iconRenderStub, realL10n;
|
||||
mocksForBookmarkEditor.attachTestHelpers();
|
||||
|
||||
var name = 'Mozilla';
|
||||
var url = 'http://www.mozilla.org/es-ES/firefox/new/';
|
||||
|
||||
var bookmark = {
|
||||
name: name,
|
||||
url: url
|
||||
};
|
||||
|
||||
var databaseInError = false;
|
||||
|
||||
suiteSetup(function() {
|
||||
realL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = window.MockL10n;
|
||||
loadBodyHTML('/save.html');
|
||||
getStub = sinon.stub(BookmarksDatabase, 'get', function(purl) {
|
||||
return {
|
||||
then: function(resolve, refect) {
|
||||
databaseInError ? refect() : resolve (purl === url ? bookmark : null);
|
||||
}
|
||||
};
|
||||
});
|
||||
iconRenderStub = sinon.stub(Icon.prototype, 'render', function() {});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realL10n;
|
||||
document.body.innerHTML = '';
|
||||
databaseInError = false;
|
||||
getStub.restore();
|
||||
iconRenderStub.restore();
|
||||
});
|
||||
|
||||
function noop() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
function dispatchInputEvent() {
|
||||
BookmarkEditor.form.dispatchEvent(new CustomEvent('input'));
|
||||
}
|
||||
|
||||
suite('Add UI initialized correctly >', function() {
|
||||
var expectedName = 'Telefonica';
|
||||
|
||||
suiteSetup(function() {
|
||||
BookmarkEditor.init({
|
||||
data: {
|
||||
name: expectedName
|
||||
},
|
||||
oncancelled: noop
|
||||
});
|
||||
});
|
||||
|
||||
test('The title has to be defined from options.data.name >', function() {
|
||||
assert.equal(document.getElementById('bookmark-title').value,
|
||||
expectedName);
|
||||
});
|
||||
|
||||
test('Checking styles', function() {
|
||||
assert.equal(document.body.dataset.mode, 'add');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Edit UI initialized correctly >', function() {
|
||||
suiteSetup(function() {
|
||||
BookmarkEditor.init({
|
||||
data: bookmark,
|
||||
oncancelled: noop
|
||||
});
|
||||
});
|
||||
|
||||
test('The title has to be defined from datastore >', function() {
|
||||
assert.equal(document.getElementById('bookmark-title').value, name);
|
||||
});
|
||||
|
||||
test('Checking styles', function() {
|
||||
assert.equal(document.body.dataset.mode, 'put');
|
||||
});
|
||||
|
||||
test('Checking "done" button after writing', function() {
|
||||
document.getElementById('bookmark-form').
|
||||
dispatchEvent(new CustomEvent('input'));
|
||||
assert.isFalse(BookmarkEditor.saveButton.disabled);
|
||||
});
|
||||
});
|
||||
|
||||
suite('UI initialized correctly as "add" when database fails >', function() {
|
||||
var expectedName = 'Telefonica';
|
||||
var expectedURL = 'www.telefonica.es';
|
||||
|
||||
suiteSetup(function() {
|
||||
databaseInError = false;
|
||||
BookmarkEditor.init({
|
||||
data: {
|
||||
name: expectedName,
|
||||
url: expectedURL
|
||||
},
|
||||
oncancelled: noop
|
||||
});
|
||||
});
|
||||
|
||||
test('The title has to be defined from options.data.name >', function() {
|
||||
assert.equal(document.getElementById('bookmark-title').value,
|
||||
expectedName);
|
||||
});
|
||||
|
||||
test('Checking styles', function() {
|
||||
assert.equal(document.body.dataset.mode, 'add');
|
||||
});
|
||||
|
||||
test('Checking "add" button initially', function() {
|
||||
assert.isFalse(BookmarkEditor.saveButton.disabled);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Invalid website name >', function() {
|
||||
|
||||
suiteSetup(function() {
|
||||
BookmarkEditor.init({
|
||||
data: {
|
||||
name: '',
|
||||
url: url
|
||||
},
|
||||
oncancelled: noop
|
||||
});
|
||||
});
|
||||
|
||||
test('Bookmarks with blank title should not be saved >', function() {
|
||||
assert.isTrue(BookmarkEditor.saveButton.disabled,
|
||||
'Blank title, add button should be disabled');
|
||||
|
||||
var saveSpy = sinon.spy(BookmarkEditor, 'save');
|
||||
|
||||
BookmarkEditor._submit(new CustomEvent('submit'));
|
||||
assert.equal(saveSpy.callCount, 0);
|
||||
|
||||
BookmarkEditor.save.restore();
|
||||
});
|
||||
|
||||
test('Check save button typing website name ', function() {
|
||||
BookmarkEditor.bookmarkTitle.value = 'Telefonica';
|
||||
dispatchInputEvent();
|
||||
assert.isFalse(BookmarkEditor.saveButton.disabled);
|
||||
|
||||
BookmarkEditor.bookmarkTitle.value = '';
|
||||
dispatchInputEvent();
|
||||
assert.isTrue(BookmarkEditor.saveButton.disabled);
|
||||
|
||||
BookmarkEditor.bookmarkTitle.value = 'Mozilla';
|
||||
dispatchInputEvent();
|
||||
assert.isFalse(BookmarkEditor.saveButton.disabled);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Install App >', function() {
|
||||
suiteSetup(function() {
|
||||
BookmarkEditor.init({
|
||||
data: {
|
||||
name: 'My App',
|
||||
url: 'http://example.com'
|
||||
},
|
||||
oncancelled: noop
|
||||
});
|
||||
});
|
||||
|
||||
test('_fetchManifest()', function(done) {
|
||||
var stubRenderAppIcon = sinon.stub(BookmarkEditor, '_renderAppIcon',
|
||||
function(manifest, size) {}
|
||||
);
|
||||
BookmarkEditor._fetchManifest().then(
|
||||
function () {
|
||||
assert.isFalse(
|
||||
BookmarkEditor.appInstallationSection.classList.contains(
|
||||
'hidden'));
|
||||
assert.equal(BookmarkEditor.appNameText.textContent, 'App');
|
||||
done();
|
||||
stubRenderAppIcon.restore();
|
||||
},
|
||||
function (err) {
|
||||
done(err);
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('_renderAppIcon()', function() {
|
||||
this.sinon.stub(window.IconsHelper, 'getBestIconFromWebManifest',
|
||||
function() {
|
||||
return new URL('http://example.com/icon.png');
|
||||
});
|
||||
|
||||
BookmarkEditor._renderAppIcon({}, 60);
|
||||
assert.equal(BookmarkEditor.appIcon.getAttribute('src'),
|
||||
'http://example.com/icon.png');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
153
gaia/apps/bookmark/test/unit/bookmark_remover_test.js
Executable file
153
gaia/apps/bookmark/test/unit/bookmark_remover_test.js
Executable file
|
|
@ -0,0 +1,153 @@
|
|||
'use strict';
|
||||
|
||||
/* global BookmarkRemover, loadBodyHTML, BookmarksDatabase */
|
||||
/* global requireApp, require, suite, suiteTeardown, suiteSetup, test, assert,
|
||||
sinon, teardown, setup, MockL10n */
|
||||
|
||||
require('/shared/js/bookmarks_database.js');
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
requireApp('bookmark/js/bookmark_remover.js');
|
||||
|
||||
suite('bookmark_remover.js >', function() {
|
||||
|
||||
var getStub;
|
||||
var realMozL10n;
|
||||
|
||||
var name = 'Mozilla';
|
||||
var url = 'http://www.mozilla.org/es-ES/firefox/new/';
|
||||
|
||||
var bookmark = {
|
||||
name: name,
|
||||
url: url
|
||||
};
|
||||
|
||||
var databaseInError = false;
|
||||
|
||||
suiteSetup(function() {
|
||||
getStub = sinon.stub(BookmarksDatabase, 'get', function(purl) {
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
databaseInError ? reject('rejected') :
|
||||
resolve (purl === url ? bookmark : null);
|
||||
}
|
||||
};
|
||||
});
|
||||
realMozL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
getStub.restore();
|
||||
navigator.mozL10n = realMozL10n;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
loadBodyHTML('/remove.html');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.body.innerHTML = '';
|
||||
databaseInError = false;
|
||||
});
|
||||
|
||||
test('UI initialized correctly >', function() {
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url
|
||||
});
|
||||
assert.isTrue(document.getElementById('title').dataset.l10nArgs
|
||||
.contains(name));
|
||||
assert.isTrue(document.getElementById('message').dataset.l10nArgs
|
||||
.contains(name));
|
||||
});
|
||||
|
||||
test('Handles quotes in bookmark name >', function() {
|
||||
bookmark.name = name = 'Mozilla"\'';
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url
|
||||
});
|
||||
var titleArgs =
|
||||
JSON.parse(document.getElementById('title').dataset.l10nArgs);
|
||||
var messageArgs =
|
||||
JSON.parse(document.getElementById('message').dataset.l10nArgs);
|
||||
assert.equal(titleArgs.name, name);
|
||||
assert.equal(messageArgs.name, name);
|
||||
});
|
||||
|
||||
test('Bookmark does not exist >', function(done) {
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url + 'blabla',
|
||||
oncancelled: function(e) {
|
||||
assert.equal(e, 'bookmark does not exist');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Getting bookmark failed >', function(done) {
|
||||
databaseInError = true;
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url,
|
||||
oncancelled: function(e) {
|
||||
assert.equal(e, 'rejected');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('User cancels >', function(done) {
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url,
|
||||
oncancelled: function(e) {
|
||||
assert.equal(e, 'cancelled');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('cancel-action').click();
|
||||
});
|
||||
|
||||
test('User confirms and bookmark is removed >', function(done) {
|
||||
var stub = sinon.stub(BookmarksDatabase, 'remove', function(id) {
|
||||
assert.equal(id, bookmark.url);
|
||||
return {
|
||||
then: function(resolve) {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url,
|
||||
onremoved: function() {
|
||||
stub.restore();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('remove-action').click();
|
||||
});
|
||||
|
||||
test('User confirms but it fails >', function(done) {
|
||||
var stub = sinon.stub(BookmarksDatabase, 'remove', function(id) {
|
||||
assert.equal(id, bookmark.url);
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
reject('failed');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
BookmarkRemover.init({
|
||||
id: bookmark.url,
|
||||
oncancelled: function(e) {
|
||||
assert.equal(e, 'failed');
|
||||
stub.restore();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('remove-action').click();
|
||||
});
|
||||
|
||||
});
|
||||
311
gaia/apps/bookmark/test/unit/bookmarks_database_test.js
Executable file
311
gaia/apps/bookmark/test/unit/bookmarks_database_test.js
Executable file
|
|
@ -0,0 +1,311 @@
|
|||
'use strict';
|
||||
|
||||
/* global BookmarksDatabase, MockDatastore, MockNavigatorDatastore */
|
||||
/* global require, suite, suiteTeardown, suiteSetup, test, assert, teardown,
|
||||
setup */
|
||||
|
||||
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
|
||||
require('/shared/js/bookmarks_database.js');
|
||||
|
||||
suite('bookmarks_database.js >', function() {
|
||||
|
||||
var realDatastore = null,
|
||||
url = 'http://mozilla.org',
|
||||
name = 'mozilla',
|
||||
data = {
|
||||
url: url,
|
||||
name: name
|
||||
};
|
||||
|
||||
suiteSetup(function() {
|
||||
realDatastore = navigator.getDataStores;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.getDataStores = realDatastore;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockDatastore._inError = false;
|
||||
MockDatastore._records = Object.create(null);
|
||||
});
|
||||
|
||||
function assertEvent(type, event) {
|
||||
assert.equal(event.type, type);
|
||||
assert.equal(event.target.id, url);
|
||||
assert.equal(event.target.url, url);
|
||||
assert.equal(event.target.name, name);
|
||||
}
|
||||
|
||||
test('testing not getDataStores defined >', function(done) {
|
||||
navigator.getDataStores = undefined;
|
||||
BookmarksDatabase.getRevisionId().then(function(id) {
|
||||
// Do noting
|
||||
}, function(e) {
|
||||
assert.equal(e.name, 'NO_DATASTORE');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('testing no access to datastore >', function(done) {
|
||||
navigator.getDataStores = function() {
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
resolve({
|
||||
length: 0
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
BookmarksDatabase.getRevisionId().then(function(id) {
|
||||
// Do noting
|
||||
}, function(e) {
|
||||
assert.equal(e.name, 'NO_ACCESS_TO_DATASTORE');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to add method - added >', function(done) {
|
||||
BookmarksDatabase.add(data).then(function(value) {
|
||||
assert.isTrue(value);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to add method - updated >', function(done) {
|
||||
var expectedName = name + 'corporation';
|
||||
|
||||
BookmarksDatabase.add(data).then(function(value) {
|
||||
assert.isTrue(value);
|
||||
BookmarksDatabase.add({
|
||||
url: url,
|
||||
name: expectedName
|
||||
}).then(function(value) {
|
||||
assert.isUndefined(value);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to add method - failed >', function(done) {
|
||||
MockDatastore._inError = true;
|
||||
BookmarksDatabase.add(data).then(function() {
|
||||
// Do nothing here
|
||||
}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to put method - OK >', function(done) {
|
||||
var id = url;
|
||||
var expectedName = 'Telefonica';
|
||||
var expectedURL = 'http://www.telefonica.es';
|
||||
|
||||
MockDatastore._records[id] = {
|
||||
id: id,
|
||||
url: url,
|
||||
name: name
|
||||
};
|
||||
|
||||
BookmarksDatabase.put({
|
||||
id: id,
|
||||
url: expectedURL,
|
||||
name: expectedName
|
||||
}).then(function onSuccess() {
|
||||
BookmarksDatabase.get(id).then(function(bookmark) {
|
||||
assert.equal(bookmark.name, expectedName);
|
||||
assert.equal(bookmark.url, expectedURL);
|
||||
assert.equal(bookmark.id, id);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to put method - failed >', function(done) {
|
||||
MockDatastore._inError = true;
|
||||
BookmarksDatabase.put(data).then(function() {
|
||||
// Do nothing here
|
||||
}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to remove method - OK >', function(done) {
|
||||
var id = url;
|
||||
|
||||
MockDatastore._records[id] = {
|
||||
id: id,
|
||||
url: url,
|
||||
name: name
|
||||
};
|
||||
|
||||
BookmarksDatabase.remove(id).then(function(bookmark) {
|
||||
assert.equal(Object.keys(MockDatastore._records).length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to remove method - failed >', function(done) {
|
||||
MockDatastore._inError = true;
|
||||
BookmarksDatabase.remove(url).then(function() {
|
||||
// Do nothing here
|
||||
}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to get method - OK >', function(done) {
|
||||
BookmarksDatabase.add(data).then(function() {
|
||||
BookmarksDatabase.get(url).then(function(bookmark) {
|
||||
assert.equal(bookmark.name, name);
|
||||
assert.equal(bookmark.url, url);
|
||||
assert.equal(bookmark.id, url);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to get method - failed >', function(done) {
|
||||
MockDatastore._inError = true;
|
||||
BookmarksDatabase.get('www.telefonica.es').then(function() {
|
||||
// Do nothing here
|
||||
}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to getRevisionId method - OK >', function(done) {
|
||||
BookmarksDatabase.getRevisionId().then(function(id) {
|
||||
assert.equal(id, MockDatastore.revisionId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to getAll method - OK >', function(done) {
|
||||
MockDatastore.sync = function() {
|
||||
var cursor = {
|
||||
next: function() {
|
||||
cursor.next = function() {
|
||||
return new window.Promise(function(resolve, reject) {
|
||||
resolve({
|
||||
operation: 'done'
|
||||
});
|
||||
});
|
||||
};
|
||||
return new window.Promise(function(resolve, reject) {
|
||||
resolve({
|
||||
operation: 'add',
|
||||
data: {
|
||||
id: url,
|
||||
url: url,
|
||||
name: name
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return cursor;
|
||||
};
|
||||
|
||||
BookmarksDatabase.getAll().then(function(bookmarks) {
|
||||
assert.equal(Object.keys(bookmarks).length, 1);
|
||||
assert.equal(bookmarks[url].name, name);
|
||||
assert.equal(bookmarks[url].id, url);
|
||||
assert.equal(bookmarks[url].url, url);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('calling to getAll method - failed >', function(done) {
|
||||
MockDatastore.sync = function() {
|
||||
var cursor = {
|
||||
next: function() {
|
||||
return new window.Promise(function(resolve, reject) {
|
||||
reject();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return cursor;
|
||||
};
|
||||
|
||||
BookmarksDatabase.getAll().then(function(bookmarks) {
|
||||
// Do nothing
|
||||
}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('testing addEventListener for "added" event >', function(done) {
|
||||
BookmarksDatabase.addEventListener('added', function onAdded(event) {
|
||||
BookmarksDatabase.removeEventListener('added', onAdded);
|
||||
assertEvent('added', event);
|
||||
done();
|
||||
});
|
||||
|
||||
BookmarksDatabase.add(data);
|
||||
});
|
||||
|
||||
test('testing addEventListener for context >', function(done) {
|
||||
var myObject = {
|
||||
handleEvent: function(event) {
|
||||
BookmarksDatabase.removeEventListener('added', myObject);
|
||||
assert.equal(this, myObject);
|
||||
assertEvent('added', event);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
BookmarksDatabase.addEventListener('added', myObject);
|
||||
BookmarksDatabase.add(data);
|
||||
});
|
||||
|
||||
test('testing addEventListener for "updated" event >', function(done) {
|
||||
// Testing handleEvent instead of a callback directly
|
||||
var handler = {
|
||||
handleEvent: function(event) {
|
||||
BookmarksDatabase.removeEventListener('updated', handler);
|
||||
assertEvent('updated', event);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
BookmarksDatabase.addEventListener('updated', handler);
|
||||
BookmarksDatabase.add(data).then(function onAdded() {
|
||||
BookmarksDatabase.add(data);
|
||||
});
|
||||
});
|
||||
|
||||
test('testing removeEventListener >', function(done) {
|
||||
var onAdded = function() {
|
||||
done(new Error('The "added" listener should NOT be called!!!'));
|
||||
};
|
||||
|
||||
BookmarksDatabase.addEventListener('added', onAdded);
|
||||
var ret = BookmarksDatabase.removeEventListener('added', onAdded);
|
||||
assert.isTrue(ret);
|
||||
|
||||
BookmarksDatabase.add(data).then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('testing removeEventListener unknown type >', function() {
|
||||
assert.isFalse(BookmarksDatabase.removeEventListener('dog', function() {
|
||||
// Do nothing...
|
||||
}));
|
||||
});
|
||||
|
||||
test('testing removeEventListener unknown callback >', function() {
|
||||
assert.isFalse(BookmarksDatabase.removeEventListener('added', function() {
|
||||
// Do nothing...
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
22
gaia/apps/bookmark/test/unit/mock_bookmark_editor.js
Executable file
22
gaia/apps/bookmark/test/unit/mock_bookmark_editor.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
/* exported MockBookmarkEditor */
|
||||
var MockBookmarkEditor = {
|
||||
init: function mbe_init(options) {
|
||||
this.onsaved = options.onsaved;
|
||||
this.oncancelled = options.oncancelled;
|
||||
},
|
||||
|
||||
close: function mbe_close() {
|
||||
this.oncancelled();
|
||||
},
|
||||
|
||||
save: function mbe_save(value) {
|
||||
this.onsaved(value);
|
||||
},
|
||||
|
||||
cancel: function mbe_cancel() {
|
||||
this.oncancelled();
|
||||
}
|
||||
|
||||
};
|
||||
9
gaia/apps/bookmark/test/unit/mock_mozsetmessagehandler.js
Executable file
9
gaia/apps/bookmark/test/unit/mock_mozsetmessagehandler.js
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/* exported realMozSetMessageHandler, msgHandler */
|
||||
var realMozSetMessageHandler = navigator.mozSetMessageHandler;
|
||||
var msgHandler = {};
|
||||
|
||||
navigator.mozSetMessageHandler = function(type, handler) {
|
||||
msgHandler[type] = handler;
|
||||
};
|
||||
19
gaia/apps/bookmark/test/unit/mock_xml_http_request.js
Executable file
19
gaia/apps/bookmark/test/unit/mock_xml_http_request.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
/* exported MockXMLHttpRequest */
|
||||
var MockXMLHttpRequest = function() {
|
||||
this.response = 'foo';
|
||||
};
|
||||
|
||||
MockXMLHttpRequest.prototype.send = function() {
|
||||
this.status = '200';
|
||||
this.onload();
|
||||
};
|
||||
|
||||
MockXMLHttpRequest.prototype.setRequestHeader = function() {
|
||||
|
||||
};
|
||||
|
||||
MockXMLHttpRequest.prototype.open = function() {
|
||||
|
||||
};
|
||||
4
gaia/apps/bookmark/test/unit/setup.js
Executable file
4
gaia/apps/bookmark/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
/* global require */
|
||||
require('/shared/test/unit/mocks/mocks_helper.js');
|
||||
Loading…
Reference in a new issue