This commit is contained in:
Fredrik Wollsén 2018-02-15 18:05:06 +02:00
Родитель 445c8266a1
Коммит 16d823e68e
10 изменённых файлов: 228 добавлений и 160 удалений

Просмотреть файл

@ -7,18 +7,17 @@
*/
module.exports = {
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module",
"ecmaFeatures": {
"jsx": false,
"experimentalObjectRestSpread": true,
parserOptions: {
ecmaVersion: 8,
sourceType: "module",
ecmaFeatures: {
jsx: false,
experimentalObjectRestSpread: true,
},
},
env: {
"es6": true,
es6: true,
// 'browser-window': false
},
extends: [
"eslint:recommended",
@ -28,16 +27,13 @@ module.exports = {
"plugin:mozilla/recommended",
],
plugins: [
"json",
"mozilla",
],
plugins: ["json", "mozilla"],
rules: {
"babel/new-cap": "off",
"comma-dangle": ["error", "always-multiline"],
"eqeqeq": "error",
"indent": ["warn", 2, { SwitchCase: 1 }],
eqeqeq: "error",
indent: ["warn", 2, { SwitchCase: 1 }],
"mozilla/no-aArgs": "warn",
"mozilla/balanced-listeners": 0,
"no-console": "warn",
@ -45,6 +41,6 @@ module.exports = {
"no-unused-vars": "error",
"prefer-const": "warn",
"prefer-spread": "error",
"semi": ["error", "always"],
semi: ["error", "always"],
},
};

65
addon/bootstrap.js поставляемый
Просмотреть файл

@ -5,17 +5,29 @@
const { utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm",
);
const STUDY = "button-icon-preference";
XPCOMUtils.defineLazyModuleGetter(this, "config",
`resource://${STUDY}/Config.jsm`);
XPCOMUtils.defineLazyModuleGetter(this, "studyUtils",
`resource://${STUDY}/StudyUtils.jsm`);
XPCOMUtils.defineLazyModuleGetter(this, "Feature",
`resource://${STUDY}/lib/Feature.jsm`);
XPCOMUtils.defineLazyModuleGetter(
this,
"config",
`resource://${STUDY}/Config.jsm`,
);
XPCOMUtils.defineLazyModuleGetter(
this,
"studyUtils",
`resource://${STUDY}/StudyUtils.jsm`,
);
XPCOMUtils.defineLazyModuleGetter(
this,
"Feature",
`resource://${STUDY}/lib/Feature.jsm`,
);
/* Example addon-specific module imports. Remember to Unload during shutdown() below.
@ -32,7 +44,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Feature",
*/
this.Bootstrap = {
VARIATION_OVERRIDE_PREF: "extensions.button_icon_preference.variation",
/**
@ -41,7 +52,6 @@ this.Bootstrap = {
* @returns {Promise<void>}
*/
async startup(addonData, reason) {
this.REASONS = studyUtils.REASONS;
this.initLog();
@ -58,7 +68,10 @@ this.Bootstrap = {
// Check if the user is eligible to run this study using the |isEligible|
// function when the study is initialized (install or upgrade, the latter
// being interpreted as a new install).
if (reason === this.REASONS.ADDON_INSTALL || reason === this.REASONS.ADDON_UPGRADE) {
if (
reason === this.REASONS.ADDON_INSTALL ||
reason === this.REASONS.ADDON_UPGRADE
) {
// telemetry "enter" ONCE
studyUtils.firstSeen();
const eligible = await config.isEligible();
@ -82,7 +95,12 @@ this.Bootstrap = {
this.log.debug(`info ${JSON.stringify(studyUtils.info())}`);
// initiate the chrome-privileged part of the study add-on
this.feature = new Feature(variation, studyUtils, this.REASONS[reason], this.log);
this.feature = new Feature(
variation,
studyUtils,
this.REASONS[reason],
this.log,
);
// if you have code to handle expiration / long-timers, it could go here
/*
@ -101,14 +119,15 @@ this.Bootstrap = {
/** spec for messages intended for Shield =>
* {shield:true,msg=[info|endStudy|telemetry],data=data}
*/
browser.runtime.onMessage.addListener(studyUtils.respondToWebExtensionMessage);
browser.runtime.onMessage.addListener(
studyUtils.respondToWebExtensionMessage,
);
// other browser.runtime.onMessage handlers for your addon, if any
});
}
// start up the chrome-privileged part of the study
this.feature.start();
},
/*
@ -117,8 +136,8 @@ this.Bootstrap = {
*/
initLog() {
XPCOMUtils.defineLazyGetter(this, "log", () => {
const ConsoleAPI =
Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
const ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {})
.ConsoleAPI;
const consoleOptions = {
maxLogLevel: config.log.bootstrap.level,
prefix: "TPStudy",
@ -136,8 +155,9 @@ this.Bootstrap = {
// choose the variation for this particular user, then set it.
async selectVariation() {
const variation = this.getVariationFromPref(config.weightedVariations) ||
await studyUtils.deterministicVariation(config.weightedVariations);
const variation =
this.getVariationFromPref(config.weightedVariations) ||
(await studyUtils.deterministicVariation(config.weightedVariations));
studyUtils.setVariation(variation);
this.log.debug(`studyUtils has config and variation.name: ${variation.name}.
Ready to send telemetry`);
@ -150,7 +170,9 @@ this.Bootstrap = {
if (name !== "") {
const variation = weightedVariations.filter(x => x.name === name)[0];
if (!variation) {
throw new Error(`about:config => ${this.VARIATION_OVERRIDE_PREF} set to ${name},
throw new Error(`about:config => ${
this.VARIATION_OVERRIDE_PREF
} set to ${name},
but no variation with that name exists.`);
}
return variation;
@ -167,8 +189,9 @@ this.Bootstrap = {
async shutdown(addonData, reason) {
this.log.debug("shutdown", this.REASONS[reason] || reason);
const isUninstall = (reason === this.REASONS.ADDON_UNINSTALL
|| reason === this.REASONS.ADDON_DISABLE);
const isUninstall =
reason === this.REASONS.ADDON_UNINSTALL ||
reason === this.REASONS.ADDON_DISABLE;
if (isUninstall) {
this.log.debug("uninstall or disable");
}

Просмотреть файл

@ -126,7 +126,7 @@ class Feature {
});
feature.studyUtils.endStudy("introduction-leave-study");
},
}
},
],
// callback for nb events
null

Просмотреть файл

@ -1,13 +1,11 @@
{
"env": {
"browser": true,
"es6": true,
"webextensions": true
},
"extends": [
"eslint:recommended"
],
"rules": {
"no-console": "warn"
}
"env": {
"browser": true,
"es6": true,
"webextensions": true
},
"extends": ["eslint:recommended"],
"rules": {
"no-console": "warn"
}
}

Просмотреть файл

@ -13,7 +13,6 @@
* - Only the webExtension can initiate messages. see `msgStudyUtils("info")` below.
*/
/** Re-usable code for talking to `studyUtils` using `browser.runtime.sendMessage`
* - Host listens and responds at `bootstrap.js`:
*
@ -27,7 +26,8 @@
*/
async function msgStudyUtils(msg, data) {
const allowed = ["endStudy", "telemetry", "info"];
if (!allowed.includes(msg)) throw new Error(`shieldUtils doesn't know ${msg}, only knows ${allowed}`);
if (!allowed.includes(msg))
throw new Error(`shieldUtils doesn't know ${msg}, only knows ${allowed}`);
try {
// the "shield" key is how the Host listener knows it's for shield.
return await browser.runtime.sendMessage({ shield: true, msg, data });
@ -56,28 +56,31 @@ function telemetry(data) {
function throwIfInvalid(obj) {
// Check: all keys and values must be strings,
for (const k in obj) {
if (typeof k !== 'string') throw new Error(`key ${k} not a string`);
if (typeof obj[k] !== 'string') throw new Error(`value ${k} ${obj[k]} not a string`);
if (typeof k !== "string") throw new Error(`key ${k} not a string`);
if (typeof obj[k] !== "string")
throw new Error(`value ${k} ${obj[k]} not a string`);
}
return true
return true;
}
throwIfInvalid(data);
return msgStudyUtils("telemetry", data);
}
class BrowserActionButtonChoiceFeature {
/**
* - set image, text, click handler (telemetry)
* - tell Legacy Addon to send
*/
constructor(variation) {
console.log("initilizing BrowserActionButtonChoiceFeature:", variation.name);
console.log(
"initilizing BrowserActionButtonChoiceFeature:",
variation.name,
);
this.timesClickedInSession = 0;
// modify BrowserAction (button) ui for this particular {variation}
console.log("path:", `icons/${variation.name}.svg`)
console.log("path:", `icons/${variation.name}.svg`);
browser.browserAction.setIcon({ path: `icons/${variation.name}.svg` });
browser.browserAction.setTitle({ title: variation.name });
browser.browserAction.onClicked.addListener(() => this.handleButtonClick());
@ -92,15 +95,20 @@ class BrowserActionButtonChoiceFeature {
// note: doesn't persist across a session, unless you use localStorage or similar.
this.timesClickedInSession += 1;
console.log("got a click", this.timesClickedInSession);
browser.browserAction.setBadgeText({ text: this.timesClickedInSession.toString() });
browser.browserAction.setBadgeText({
text: this.timesClickedInSession.toString(),
});
// telemetry: FIRST CLICK
if (this.timesClickedInSession == 1) {
telemetry({ "event": "button-first-click-in-session" });
telemetry({ event: "button-first-click-in-session" });
}
// telemetry EVERY CLICK
telemetry({ "event": "button-click", timesClickedInSession: "" + this.timesClickedInSession });
telemetry({
event: "button-click",
timesClickedInSession: "" + this.timesClickedInSession,
});
// webExtension-initiated ending for "used-often"
//
@ -119,13 +127,15 @@ class BrowserActionButtonChoiceFeature {
* 3. initialize the feature, using our specific variation
*/
function runOnce() {
msgStudyUtils("info").then(
({ variation }) => new BrowserActionButtonChoiceFeature(variation)
).catch(function defaultSetup() {
// Errors here imply that this is NOT embedded.
console.log("you must be running as part of `web-ext`. You get 'corn dog'!");
new BrowserActionButtonChoiceFeature({ "name": "isolatedcorndog" })
});
msgStudyUtils("info")
.then(({ variation }) => new BrowserActionButtonChoiceFeature(variation))
.catch(function defaultSetup() {
// Errors here imply that this is NOT embedded.
console.log(
"you must be running as part of `web-ext`. You get 'corn dog'!",
);
new BrowserActionButtonChoiceFeature({ name: "isolatedcorndog" });
});
}
// actually start

Просмотреть файл

@ -68,7 +68,7 @@
"eslint": "eslint . --ext jsm --ext js --ext json",
"eslint-fix": "eslint . --ext jsm --ext js --ext json --fix",
"firefox": "export XPI=dist/linked-addon.xpi && npm run build && node run-firefox.js",
"format": "prettier '**/*.{css,js,json}' --trailing-comma=all --ignore-path=.eslintignore --print-width 160 --write",
"format": "prettier '**/*.{css,js,json}' --trailing-comma=all --ignore-path=.eslintignore --write && npm run eslint-fix",
"harness_test": "export XPI=dist/linked-addon.xpi && mocha test/functional_tests.js --retry 2 --reporter json",
"lint": "npm-run-all lint:*",
"lint-build:addons-linter": "# actually a post build test: bin/addonLintTest ' + require('./package.json').name",

Просмотреть файл

@ -24,7 +24,6 @@ const {
MODIFIER_KEY,
} = require("./test/utils");
const HELP = `
env vars:
@ -44,12 +43,11 @@ Future will clean up this interface a bit!
`;
const minimistHandler = {
boolean: [ "help" ],
boolean: ["help"],
alias: { h: "help", v: "version" },
"--": true,
};
(async() => {
const minimist = require("minimist");
const parsedArgs = minimist(process.argv.slice(2), minimistHandler);
@ -80,8 +78,9 @@ const minimistHandler = {
const openBrowserConsole = Key.chord(MODIFIER_KEY, Key.SHIFT, "j");
await urlBar.sendKeys(openBrowserConsole);
console.log("The addon should now be loaded and you should be able to interact with the addon in the newly opened Firefox instance.");
console.log(
"The addon should now be loaded and you should be able to interact with the addon in the newly opened Firefox instance.",
);
} catch (e) {
console.error(e);
}

Просмотреть файл

@ -17,14 +17,16 @@ const utils = require("./utils");
/* Part 1: Utilities */
async function getShieldPingsAfterTimestamp(driver, ts) {
return utils.getTelemetryPings(driver, { type: ["shield-study", "shield-study-addon"], timestamp: ts });
return utils.getTelemetryPings(driver, {
type: ["shield-study", "shield-study-addon"],
timestamp: ts,
});
}
function summarizePings(pings) {
return pings.map(p => [p.payload.type, p.payload.data]);
}
async function getNotification(driver) {
return utils.getChromeElementBy.tagName(driver, "notification");
}
@ -33,7 +35,6 @@ async function getFirstButton(driver) {
return utils.getChromeElementBy.className(driver, "notification-button");
}
/* Part 2: The Tests */
describe("basic functional tests", function() {
@ -58,17 +59,14 @@ describe("basic functional tests", function() {
// collect sent pings
pings = await getShieldPingsAfterTimestamp(driver, beginTime);
// console.log(pingsReport(pings).report);
});
after(async() => {
driver.quit();
});
beforeEach(async() => {
});
afterEach(async() => {
});
beforeEach(async() => {});
afterEach(async() => {});
/* Expected behaviour:
@ -85,17 +83,33 @@ describe("basic functional tests", function() {
});
it("at least one shield-study telemetry ping with study_state=installed", async() => {
const foundPings = utils.searchTelemetry([
ping => ping.type === "shield-study" && ping.payload.data.study_state === "installed",
], pings);
assert(foundPings.length > 0, "at least one shield-study telemetry ping with study_state=installed");
const foundPings = utils.searchTelemetry(
[
ping =>
ping.type === "shield-study" &&
ping.payload.data.study_state === "installed",
],
pings,
);
assert(
foundPings.length > 0,
"at least one shield-study telemetry ping with study_state=installed",
);
});
it("at least one shield-study telemetry ping with study_state=enter", async() => {
const foundPings = utils.searchTelemetry([
ping => ping.type === "shield-study" && ping.payload.data.study_state === "enter",
], pings);
assert(foundPings.length > 0, "at least one shield-study telemetry ping with study_state=enter");
const foundPings = utils.searchTelemetry(
[
ping =>
ping.type === "shield-study" &&
ping.payload.data.study_state === "enter",
],
pings,
);
assert(
foundPings.length > 0,
"at least one shield-study telemetry ping with study_state=enter",
);
});
it("telemetry: has entered, installed, etc", function() {
@ -105,21 +119,21 @@ describe("basic functional tests", function() {
[
"shield-study-addon",
{
"attributes": {
"event": "introduction-shown",
attributes: {
event: "introduction-shown",
},
},
],
[
"shield-study",
{
"study_state": "installed",
study_state: "installed",
},
],
[
"shield-study",
{
"study_state": "enter",
study_state: "enter",
},
],
];
@ -129,7 +143,9 @@ describe("basic functional tests", function() {
describe("introduction / orientation bar", function() {
it("exists, carries study config", async() => {
const notice = await getNotification(driver);
const noticeConfig = JSON.parse(await notice.getAttribute("data-study-config"));
const noticeConfig = JSON.parse(
await notice.getAttribute("data-study-config"),
);
assert(noticeConfig.name);
assert(noticeConfig.weight);
});
@ -153,15 +169,14 @@ describe("basic functional tests", function() {
[
"shield-study-addon",
{
"attributes": {
"event": "introduction-accept",
attributes: {
event: "introduction-accept",
},
},
],
];
// this would add new telemetry
assert.deepEqual(expected, observed, "telemetry pings do not match");
});
it("TBD click on NO uninstalls addon", async() => {

Просмотреть файл

@ -13,21 +13,21 @@ const { spawn } = require("child_process");
// Promise wrapper around childProcess.spawn()
function spawnProcess(command, args) {
return new Promise((resolve) => {
return new Promise(resolve => {
const childProcess = spawn(command, args);
const stderrArray = [];
const stdoutArray = [];
childProcess.stdout.on("data", (data) => {
childProcess.stdout.on("data", data => {
stdoutArray.push(data.toString()); // data is of type Buffer
});
childProcess.stderr.on("data", (data) => {
childProcess.stderr.on("data", data => {
// TODO reject upon error?
stderrArray.push(data.toString()); // data is of type Buffer
});
childProcess.on("close", (code) => {
childProcess.on("close", code => {
// TODO reject upon error?
console.log("Test suite completed.");
resolve({ code, stdoutArray, stderrArray });
@ -44,7 +44,9 @@ async function main() {
console.log(`Currently running test suite #${i}.`);
const childProcesses = [];
// NOTE Parallel tests seem to introduce more errors.
childProcesses.push(spawnProcess("npm", ["run", "--silent", "harness_test"]));
childProcesses.push(
spawnProcess("npm", ["run", "--silent", "harness_test"]),
);
// TODO Promise.all() will reject upon a single error, is this an issue?
try {
@ -57,11 +59,13 @@ async function main() {
const mochaOutput = JSON.parse(rawOutput.join(""));
for (const failedTest of mochaOutput.failures) {
console.log(failedTest.err);
if (!(failedTestCounts.has(failedTest.fullTitle))) {
if (!failedTestCounts.has(failedTest.fullTitle)) {
failedTestCounts.set(failedTest.fullTitle, 0);
}
failedTestCounts.set(failedTest.fullTitle,
failedTestCounts.get(failedTest.fullTitle) + 1);
failedTestCounts.set(
failedTest.fullTitle,
failedTestCounts.get(failedTest.fullTitle) + 1,
);
}
} catch (e) {
console.log(`JSON parsing error: ${e}`);
@ -74,12 +78,15 @@ async function main() {
}
console.log(failedTestCounts);
for (const pair of failedTestCounts) {
fs.appendFile(`test_harness_output_${new Date().toISOString()}.txt`, `${pair[0]}: ${pair[1]}\n`,
(err) => {
fs.appendFile(
`test_harness_output_${new Date().toISOString()}.txt`,
`${pair[0]}: ${pair[1]}\n`,
err => {
if (err) {
console.log(`fs.writeFile errror: ${err}`);
}
});
},
);
}
}

Просмотреть файл

@ -42,12 +42,12 @@ const FIREFOX_PREFERENCES = {
"extensions.button_icon_preference.variation": "kittens",
/** WARNING: gecko webdriver sets many additional prefs at:
* https://dxr.mozilla.org/mozilla-central/source/testing/geckodriver/src/prefs.rs
*
* In, particular, this DISABLES actual telemetry uploading
* ("toolkit.telemetry.server", Pref::new("https://%(server)s/dummy/telemetry/")),
*
*/
* https://dxr.mozilla.org/mozilla-central/source/testing/geckodriver/src/prefs.rs
*
* In, particular, this DISABLES actual telemetry uploading
* ("toolkit.telemetry.server", Pref::new("https://%(server)s/dummy/telemetry/")),
*
*/
};
// useful if we need to test on a specific version of Firefox
@ -66,14 +66,14 @@ async function promiseActualBinary(binary) {
}
/**
* Uses process.env.FIREFOX_BINARY
*
*/
* Uses process.env.FIREFOX_BINARY
*
*/
module.exports.promiseSetupDriver = async() => {
const profile = new firefox.Profile();
// TODO, allow 'actually send telemetry' here.
Object.keys(FIREFOX_PREFERENCES).forEach((key) => {
Object.keys(FIREFOX_PREFERENCES).forEach(key => {
profile.setPreference(key, FIREFOX_PREFERENCES[key]);
});
@ -85,7 +85,9 @@ module.exports.promiseSetupDriver = async() => {
.forBrowser("firefox")
.setFirefoxOptions(options);
const binaryLocation = await promiseActualBinary(process.env.FIREFOX_BINARY || "nightly");
const binaryLocation = await promiseActualBinary(
process.env.FIREFOX_BINARY || "nightly",
);
// console.log(binaryLocation);
await options.setBinary(new firefox.Binary(binaryLocation));
@ -96,25 +98,24 @@ module.exports.promiseSetupDriver = async() => {
return driver;
};
/* let's actually just make this a constant */
const MODIFIER_KEY = (function getModifierKey() {
const modifierKey = process.platform === "darwin" ?
webdriver.Key.COMMAND : webdriver.Key.CONTROL;
const modifierKey =
process.platform === "darwin"
? webdriver.Key.COMMAND
: webdriver.Key.CONTROL;
return modifierKey;
})();
module.exports.MODIFIER_KEY = MODIFIER_KEY;
// TODO glind general wrapper for 'async with callback'?
/* Firefox UI helper functions */
// such as: "social-share-button"
module.exports.addButtonFromCustomizePanel = async(driver, buttonId) =>
driver.executeAsyncScript((callback) => {
driver.executeAsyncScript(callback => {
// see https://dxr.mozilla.org/mozilla-central/rev/211d4dd61025c0a40caea7a54c9066e051bdde8c/browser/base/content/browser-social.js#193
Components.utils.import("resource:///modules/CustomizableUI.jsm");
CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_NAVBAR);
@ -123,7 +124,7 @@ module.exports.addButtonFromCustomizePanel = async(driver, buttonId) =>
module.exports.removeButtonFromNavbar = async(driver, buttonId) => {
try {
await driver.executeAsyncScript((callback) => {
await driver.executeAsyncScript(callback => {
Components.utils.import("resource:///modules/CustomizableUI.jsm");
CustomizableUI.removeWidgetFromArea(buttonId);
callback();
@ -136,7 +137,7 @@ module.exports.removeButtonFromNavbar = async(driver, buttonId) => {
if (e.name === "TimeoutError") {
return false;
}
throw (e);
throw e;
}
};
@ -147,17 +148,29 @@ module.exports.installAddon = async(driver, fileLocation) => {
fileLocation = fileLocation || path.join(process.cwd(), process.env.XPI);
const executor = driver.getExecutor();
executor.defineCommand("installAddon", "POST", "/session/:sessionId/moz/addon/install");
executor.defineCommand(
"installAddon",
"POST",
"/session/:sessionId/moz/addon/install",
);
const installCmd = new cmd.Command("installAddon");
const session = await driver.getSession();
installCmd.setParameters({ sessionId: session.getId(), path: fileLocation, temporary: true });
installCmd.setParameters({
sessionId: session.getId(),
path: fileLocation,
temporary: true,
});
return executor.execute(installCmd);
};
module.exports.uninstallAddon = async(driver, id) => {
const executor = driver.getExecutor();
executor.defineCommand("uninstallAddon", "POST", "/session/:sessionId/moz/addon/uninstall");
executor.defineCommand(
"uninstallAddon",
"POST",
"/session/:sessionId/moz/addon/uninstall",
);
const uninstallCmd = new cmd.Command("uninstallAddon");
const session = await driver.getSession();
@ -165,7 +178,6 @@ module.exports.uninstallAddon = async(driver, id) => {
await executor.execute(uninstallCmd);
};
/* this is NOT WORKING FOR UNKNOWN HARD TO EXLAIN REASONS
=> Uncaught WebDriverError: InternalError: too much recursion
module.exports.allAddons = async(driver) => {
@ -179,20 +191,20 @@ module.exports.allAddons = async(driver) => {
*/
/** Returns array of pings of type `type` in reverse sorted order by timestamp
* first element is most recent ping
*
* as seen in shield-study-addon-util's `utils.jsm`
* options
* - type: string or array of ping types
* - n: positive integer. at most n pings.
* - timestamp: only pings after this timestamp.
* - headersOnly: boolean, just the 'headers' for the pings, not the full bodies.
*/
* first element is most recent ping
*
* as seen in shield-study-addon-util's `utils.jsm`
* options
* - type: string or array of ping types
* - n: positive integer. at most n pings.
* - timestamp: only pings after this timestamp.
* - headersOnly: boolean, just the 'headers' for the pings, not the full bodies.
*/
module.exports.getTelemetryPings = async(driver, passedOptions) => {
// callback is how you get the return back from the script
return driver.executeAsyncScript(async(options, callback) => {
let {type} = options;
const { n, timestamp, headersOnly} = options;
let { type } = options;
const { n, timestamp, headersOnly } = options;
Components.utils.import("resource://gre/modules/TelemetryArchive.jsm");
// {type, id, timestampCreated}
let pings = await TelemetryArchive.promiseArchivedPingList();
@ -207,49 +219,57 @@ module.exports.getTelemetryPings = async(driver, passedOptions) => {
pings.sort((a, b) => b.timestampCreated - a.timestampCreated);
if (n) pings = pings.slice(0, n);
const pingData = headersOnly ? pings : pings.map(ping => TelemetryArchive.promiseArchivedPingById(ping.id));
const pingData = headersOnly
? pings
: pings.map(ping => TelemetryArchive.promiseArchivedPingById(ping.id));
callback(await Promise.all(pingData));
}, passedOptions);
};
// TODO glind, this interface feels janky
// this feels like it wants to be $ like.
// not obvious right now, moving on!
class getChromeElementBy {
static async _get1(driver, method, selector ) {
static async _get1(driver, method, selector) {
driver.setContext(Context.CHROME);
try {
return await driver.wait(until.elementLocated(
By[method](selector)), 1000);
return await driver.wait(
until.elementLocated(By[method](selector)),
1000,
);
} catch (e) {
// if there an error, the button was not found
console.error(e);
return null;
}
}
static async id(driver, id) { return this._get1(driver, "id", id); }
static async id(driver, id) {
return this._get1(driver, "id", id);
}
static async className(driver, className) { return this._get1(driver, "className", className); }
static async className(driver, className) {
return this._get1(driver, "className", className);
}
static async tagName(driver, tagName) { return this._get1(driver, "tagName", tagName); }
static async tagName(driver, tagName) {
return this._get1(driver, "tagName", tagName);
}
}
module.exports.getChromeElementBy = getChromeElementBy;
module.exports.promiseUrlBar = (driver) => {
module.exports.promiseUrlBar = driver => {
driver.setContext(Context.CHROME);
return driver.wait(until.elementLocated(
By.id("urlbar")), 1000);
return driver.wait(until.elementLocated(By.id("urlbar")), 1000);
};
module.exports.takeScreenshot = async(driver, filepath = "./screenshot.png") => {
module.exports.takeScreenshot = async(
driver,
filepath = "./screenshot.png",
) => {
try {
const data = await driver.takeScreenshot();
return await Fs.outputFile(filepath,
data, "base64");
return await Fs.outputFile(filepath, data, "base64");
} catch (screenshotError) {
throw screenshotError;
}
@ -275,7 +295,9 @@ module.exports.searchTelemetry = (conditionArray, telemetryArray) => {
const resultingPings = [];
for (const condition of conditionArray) {
const index = telemetryArray.findIndex(ping => condition(ping));
if (index === -1) { throw new SearchError(condition); }
if (index === -1) {
throw new SearchError(condition);
}
resultingPings.push(telemetryArray[index]);
}
return resultingPings;
@ -328,7 +350,6 @@ module.exports.searchTelemetry = (conditionArray, telemetryArray) => {
// }
// };
// module.exports.testPanel = async(driver, panelId) => {
// driver.setContext(Context.CHROME);
// try { // if we can't find the panel, return false
@ -351,7 +372,6 @@ module.exports.searchTelemetry = (conditionArray, telemetryArray) => {
// }
// };
// module.exports.closePanel = async(driver, target = null) => {
// if (target !== null) {
// target.sendKeys(webdriver.Key.ESCAPE);