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,10 @@
{
"extends": "../../../../../.jshintrc",
"node": true,
"predef": [
"test",
"setup",
"marionette",
"client"
]
}

View file

@ -0,0 +1,34 @@
'use strict';
function Base(client, origin) {
this.client = client;
this.origin = origin;
this.elements = {};
}
Base.prototype = {
launch: function() {
this.client.apps.launch(this.origin);
this.switchTo();
},
switchTo: function() {
this.client.switchToFrame();
this.client.apps.switchToApp(this.origin);
this.waitForReady();
},
close: function() {
this.client.apps.close(this.origin);
},
waitForReady: function() {
this.client.helper.waitForElement('body');
}
};
module.exports = Base;

View file

@ -0,0 +1,48 @@
'use strict';
/*
* A helper module for the testing 3rd-party IME app.
*/
var Base = require('./base');
function ImeTestApp(client) {
Base.call(this, client, ImeTestApp.ORIGIN);
}
module.exports = ImeTestApp;
ImeTestApp.ORIGIN = 'app://imetestapp.gaiamobile.org';
ImeTestApp.MANIFEST_URL = 'app://imetestapp.gaiamobile.org/manifest.webapp';
ImeTestApp.Selectors = Object.freeze({
sendKeyButton: '#sendKey',
switchLayoutButton: '#switchLayout'
});
ImeTestApp.prototype = {
__proto__: Base.prototype,
get sendKeyButton() {
return this.client.helper.waitForElement(
ImeTestApp.Selectors.sendKeyButton);
},
get switchLayoutButton() {
return this.client.helper.waitForElement(
ImeTestApp.Selectors.switchLayoutButton);
},
switchTo: function() {
var systemInputMgmt = this.client.loader.getAppClass(
'system', 'input_management');
var imeKeyboard;
systemInputMgmt.waitForKeyboardFrameDisplayed();
systemInputMgmt.switchToActiveKeyboardFrame();
imeKeyboard = this.client.helper.waitForElement('#keyboard.ime-test-app');
this.client.waitFor(() => {
return imeKeyboard.displayed();
});
}
};

View file

@ -0,0 +1,340 @@
'use strict';
var Base = require('./base');
var Marionette = require('marionette-client');
function Keyboard(client) {
Base.call(this, client);
this.actions = new Marionette.Actions(client);
this.currentLayout = null;
}
module.exports = Keyboard;
Keyboard.Selectors = Object.freeze({
currentPanel:
'.keyboard-type-container[data-active]',
imeSwitchingKey:
'.keyboard-type-container[data-active] ' +
'.keyboard-key[data-keycode="-3"]',
backspaceKey:
'.keyboard-type-container[data-active] ' +
'.keyboard-key[data-keycode="8"]',
returnKey:
'.keyboard-type-container[data-active] ' +
'.keyboard-key[data-l10n-id="returnKey2"]',
spaceBarKey: '.keyboard-type-container[data-active]' +
' .keyboard-key[data-keycode="32"]',
dismissSuggestionsKey:
'.keyboard-type-container[data-active] ' +
'.dismiss-suggestions-button',
shiftKey:
'.keyboard-type-container[data-active] ' +
'button.keyboard-key[data-l10n-id="upperCaseKey2"]',
autoCorrectWord:
'.keyboard-type-container[data-active] ' +
'.autocorrect',
});
Keyboard.Templates = Object.freeze({
key:
'.keyboard-type-container[data-active] ' +
'button.keyboard-key[data-keycode="%s"], ' +
'.keyboard-type-container[data-active] ' +
'button.keyboard-key[data-keycode-upper="%s"]',
upperCaseKey:
'.keyboard-type-container[data-active] ' +
'button.keyboard-key[data-keycode-upper="%s"]',
pageSwitchingKey:
'.keyboard-type-container[data-active] ' +
'button.keyboard-key[data-target-page="%s"]',
suggestionKey:
'.keyboard-type-container[data-active] ' +
'.suggestions-container span[data-data="%s"]',
});
Keyboard.LONGPRESS_CHARS = Object.freeze({
'0': 'º',
'?': '¿',
'$': '€£¥',
'!': '¡',
'a': 'áàâäåãāæ',
'c': 'çćč',
'e': 'éèêëēę€ɛ',
'i': 'įīîìíï',
'l': '£ł',
'n': 'ńñ',
'o': 'ɵøœōôòóö',
's': 'ßśš$',
'u': 'ūûùúü',
'y': '¥ÿ',
'z': 'žźż',
'A': 'ÁÀÂÄÅÃĀÆ',
'C': 'ÇĆČ',
'E': 'ÉÈÊËĒĘ€Ɛ',
'I': 'ĮĪÎÌÍÏ',
'L': '£Ł',
'N': 'ŃÑ',
'O': 'ƟØŒŌÔÒÓÖ',
'S': 'ŚŠŞ',
'U': 'ŪÛÙÚÜ',
'Y': '¥Ÿ',
'Z': 'ŽŹŻ'
});
Keyboard.TypeGroupMap = Object.freeze({
'text': 'text',
'textarea': 'text',
'url': 'url',
'email': 'email',
'password': 'password',
'search': 'text',
'number': 'number',
'tel': 'number'
});
Keyboard.prototype = {
__proto__: Base.prototype,
get currentPanel() {
return this.client.findElement(Keyboard.Selectors.currentPanel);
},
get imeSwitchingKey() {
return this.client.findElement(Keyboard.Selectors.imeSwitchingKey);
},
get backspaceKey() {
return this.client.findElement(Keyboard.Selectors.backspaceKey);
},
get returnKey() {
return this.client.findElement(Keyboard.Selectors.returnKey);
},
get spaceBarKey() {
return this.client.findElement(Keyboard.Selectors.spaceBarKey);
},
get shiftKey() {
return this.client.findElement(Keyboard.Selectors.shiftKey);
},
get dismissSuggestionsKey() {
return this.client.findElement(Keyboard.Selectors.dismissSuggestionsKey);
},
get autoCorrectWord() {
return this.client.findElement(Keyboard.Selectors.autoCorrectWord);
},
getKey: function(char) {
var selector = Keyboard.Templates.key.replace(/%s/g, char.charCodeAt(0));
return this.client.findElement(selector);
},
getPageSwitchingKey: function(index) {
var selector = Keyboard.Templates.pageSwitchingKey.replace(/%s/g, index);
return this.client.findElement(selector);
},
getSuggestionKey: function(word) {
var selector = Keyboard.Templates.suggestionKey.replace(/%s/, word);
return this.client.findElement(selector);
},
getLongPressCharMiddleKey: function(char) {
var chars = Keyboard.LONGPRESS_CHARS;
for (var middleChar in chars) {
if (chars[middleChar].indexOf(char) !== -1) {
return middleChar;
}
}
return undefined;
},
getCurrentInputType: function() {
return this.client.executeScript(
'return window.wrappedJSObject.app.getBasicInputType();');
},
getCurrentInputMode: function() {
return this.client.executeScript(
'return window.wrappedJSObject.app.inputContext.inputMode;');
},
getCurrentPageIndex: function() {
return this.client.executeScript(
'return window.wrappedJSObject.app.layoutManager.currentPageIndex;');
},
getCurrentKeyboardLayout: function() {
var keyboardFrame;
this.client.switchToFrame();
keyboardFrame = this.client.findElement(
'#keyboards .inputWindow.active iframe');
this.currentLayout = keyboardFrame.getAttribute('data-frame-name');
this.switchTo();
return this.currentLayout;
},
getSymbolPageIndex: function(key) {
this.switchToPage(1);
if (this.isKeyPresent(key)) {
return 1;
}
return 2;
},
tapBackspaceKey: function(word) {
this.backspaceKey.tap();
},
tapAutoCorrectWord: function() {
this.client.waitFor(() => {
return this.autoCorrectWord.displayed();
});
this.autoCorrectWord.tap();
},
tapSuggestionWord: function(word) {
this.getSuggestionKey(word).tap();
},
isUpperCase: function() {
return this.client.executeScript(
'return ' +
'window.wrappedJSObject.app.upperCaseStateManager.isUpperCase;');
},
isElementPresent: function(selector) {
var pollClient = this.client.scope({ searchTimeout: 50 });
try {
pollClient.findElement(selector);
} catch (e) {
return false;
}
return true;
},
isKeyPresent: function(key) {
var keyTemplate = Keyboard.Templates.key;
return this.isElementPresent(keyTemplate.replace(/%s/g, key.charCodeAt(0)));
},
switchCase: function(isUpperCase) {
if (isUpperCase === undefined || this.isUpperCase() === isUpperCase) {
return;
}
this.shiftKey.tap();
this.client.waitFor(() => {
var expected = isUpperCase ? 'true' : 'false';
return (this.shiftKey.getAttribute('aria-pressed') === expected);
});
},
switchToPage: function(index) {
var pageIndex = this.getCurrentPageIndex();
if (index === undefined || pageIndex === index) {
return;
}
this.getPageSwitchingKey(index).tap();
this.client.waitFor(() => {
return (this.getCurrentPageIndex() === index);
});
this.waitForPageReady();
},
switchToKeyLayout: function(key) {
var page;
var upperCase;
var currentLayout = this.currentLayout || this.getCurrentKeyboardLayout();
if (['number'].indexOf(currentLayout) !== -1) {
return;
}
if (key >= '0' && key <= '9') {
page = 1;
} else if (key >= 'A' && key <='Z') {
page = 0;
upperCase = true;
} else if (key >= 'a' && key <= 'z') {
page = 0;
upperCase = false;
} else if (key === ' ' || key === '\u0008' || key === '\u000d') {
} else {
page = this.getSymbolPageIndex(key);
}
this.switchToPage(page);
this.switchCase(upperCase);
},
type: function(string) {
string.split('').forEach((char) => {
var middleChar = this.getLongPressCharMiddleKey(char);
var lookingForKey = middleChar || char;
var keyElement;
this.switchToKeyLayout(lookingForKey);
if (!middleChar) {
keyElement = this.getKey(char);
this.actions.wait(0.1).tap(keyElement).perform();
} else {
keyElement = this.getKey(middleChar);
this.actions
.press(keyElement).wait(1.5).perform()
.move(this.getKey(char)).wait(0.2).release().perform();
}
});
},
longPressSpaceBar: function(time) {
this.actions.longPress(this.spaceBarKey, time).perform();
},
switchTo: function() {
var systemInputMgmt = this.client.loader.getAppClass(
'system', 'input_management');
systemInputMgmt.waitForKeyboardFrameDisplayed();
systemInputMgmt.switchToActiveKeyboardFrame();
this.waitForPageReady();
},
waitForPageReady: function() {
this.client.waitFor(() => {
return this.currentPanel.displayed();
});
},
waitForKeyboardReady: function() {
this.client.switchToFrame();
this.client.waitFor(() => {
return this.client.findElement(
'#keyboards .inputWindow .browser-container');
});
}
};

View file

@ -0,0 +1,50 @@
'use strict';
var Base = require('./base.js');
function KeyboardSettings(client) {
this.client = client.scope({ searchTimeout: 500 });
Base.call(this, client, KeyboardSettings.ORIGIN);
}
module.exports = KeyboardSettings;
KeyboardSettings.ORIGIN = 'app://keyboard.gaiamobile.org';
KeyboardSettings.Selectors = Object.freeze({
autoCorrectCheckBox: '#cb-autoCorrect'
});
KeyboardSettings.prototype = {
__proto__: Base.prototype,
get autoCorrectCheckBox() {
return this.client.findElement(
KeyboardSettings.Selectors.autoCorrectCheckBox);
},
get autoCorrect() {
return this.autoCorrectCheckBox.scriptWith(function(ele) {
return ele.wrappedJSObject.checked;
});
},
clickAutoCorrectOption: function() {
var currentSwitch = this.client.settings.get('keyboard.autocorrect');
this.autoCorrectCheckBox.click();
this.client.waitFor(() => {
return (this.autoCorrect !== currentSwitch);
});
},
waitForReady: function() {
this.client.waitFor(() => {
return this.autoCorrectCheckBox.displayed();
});
}
};

View file

@ -0,0 +1,97 @@
'use strict';
var Base = require('./base');
function KeyboardTests(client) {
Base.call(this, client, KeyboardTests.ORIGIN);
}
module.exports = KeyboardTests;
KeyboardTests.ORIGIN = 'app://keyboardtestapp.gaiamobile.org';
KeyboardTests.Selector = Object.freeze({
textInput: '#textInput',
textareaInput: '#textareaInput',
urlInput: '#urlInput',
emailInput: '#emailInput',
passwordInput: '#passwordInput',
searchInput: '#searchInput',
numberInput: '#numberInput',
telInput: '#telInput',
nonInputArea: '#nonInput',
triggerPromptButton: '#triggerPromptButton',
promptResult: '#promptResult'
});
KeyboardTests.prototype = {
__proto__: Base.prototype,
get textInput() {
return this.client.findElement(KeyboardTests.Selector.textInput);
},
get textareaInput() {
return this.client.findElement(KeyboardTests.Selector.textareaInput);
},
get urlInput() {
return this.client.findElement(KeyboardTests.Selector.urlInput);
},
get emailInput() {
return this.client.findElement(KeyboardTests.Selector.emailInput);
},
get passwordInput() {
return this.client.findElement(KeyboardTests.Selector.passwordInput);
},
get searchInput() {
return this.client.findElement(KeyboardTests.Selector.searchInput);
},
get numberInput() {
return this.client.findElement(KeyboardTests.Selector.numberInput);
},
get telInput() {
return this.client.findElement(KeyboardTests.Selector.telInput);
},
get nonInputArea() {
return this.client.findElement(KeyboardTests.Selector.nonInputArea);
},
get triggerPromptButton() {
return this.client.findElement(KeyboardTests.Selector.triggerPromptButton);
},
get promptResult() {
return this.client.findElement(KeyboardTests.Selector.promptResult);
},
triggerPromptModalDialog: function() {
this.triggerPromptButton.tap();
},
waitForReady: function() {
var inputs = [
this.textInput,
this.textareaInput,
this.urlInput,
this.emailInput,
this.passwordInput,
this.searchInput,
this.numberInput,
this.telInput
];
this.client.waitFor(() => {
return inputs.every((ele) => {
return ele.displayed();
});
});
}
};