Upgraded to shield utils 5.2.0 + ported over improvements from the jestr study

This commit is contained in:
Fredrik Wollsén 2019-01-31 00:13:16 +01:00
Родитель dc5e025631
Коммит ad1fad004b
27 изменённых файлов: 1998 добавлений и 2449 удалений

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

@ -1,10 +1,11 @@
# do not lint/format generated artifacts
test/results/
test/results/coverage/
dist/
package-lock.json
src/privileged/privacyContext/api.js
src/privileged/privacyContext/schema.json
src/privileged/privacyContext/stubApi.js
# do not lint/format bundled util libraries
src/privileged/prefs/api.js
src/privileged/prefs/schema.json
src/privileged/study/api.js
src/privileged/study/schema.json
# makes sure that eslintrc.js gets linted/formatted

1
.gitignore поставляемый
Просмотреть файл

@ -1,4 +1,5 @@
dist/*
node_modules/
.env
test/results/coverage
/firefox-*

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

@ -77,6 +77,10 @@ Telemetry pings are loaded into S3 and re:dash. Sample query:
* [All pings](https://sql.telemetry.mozilla.org/queries/{#your-id}/source#table)
(OR, if Pioneer, use the below instead)
Telemetry pings are loaded into the encrypted Pioneer pipeline.
## Improving this add-on
See [DEV.md](./docs/DEV.md) for more details on how to work with this add-on as a developer.

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

@ -50,7 +50,7 @@ npm run build
## Details
First, make sure you are on NPM 6+ installed:
First, make sure you are on NPM 8+ installed:
```shell
npm install -g npm

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

@ -27,9 +27,11 @@
* (Create profile: <https://developer.mozilla.org/Firefox/Multiple_profiles>, or via some other method)
* Navigate to _about:config_ and set the following preferences. (If a preference does not exist, create it be right-clicking in the white area and selecting New -> String)
* Set `shieldStudy.logLevel` to `All`. This permits shield-add-on log output in browser console.
* Set `shieldStudy.logLevel` to `info`. This permits shield-add-on log output in browser console.
* (If Pioneer study) Make sure that the [Firefox Pioneer Add-on](https://addons.mozilla.org/en-US/firefox/addon/firefox-pioneer/) is installed
* Set `extensions.button-icon-preference_shield_mozilla_org.test.variationName` to `kittens` (or any other study variation/branch to test specifically)
* Go to [this study's tracking bug](tbd: replace with your study's launch bug link in bugzilla) and install the latest add-on zip file
* (If you are installing an unsigned version of the add-on, you need to set `extensions.legacy.enabled` to `true` before installing the add-on)
## Expected User Experience / Functionality
@ -116,6 +118,8 @@ Any UI in a Shield study should be consistent with standard Firefox design speci
### Note: checking "sent Telemetry is correct"
* Open the Browser Console using Firefox's top menu at `Tools > Web Developer > Browser Console`. This will display Shield (loading/telemetry) log output from the add-on.
* To inspect the (unencrypted) contents individual telemetry packets, set `shieldStudy.logLevel` to `all`. This permits debug-level shield-add-on log output in the browser console. Note that this will negatively affect the performance of Firefox.
* To see the actual (encrypted if Pioneer study) payloads, go to `about:telemetry` -> Click `current ping` -> Select `Archived ping data` -> Ping Type `pioneer-study` -> Choose a payload -> Raw Payload
See [TELEMETRY.md](./TELEMETRY.md) for more details on what pings are sent by this add-on.

3905
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -7,7 +7,7 @@
"url": "https://github.com/mozilla/shield-studies-addon-template/issues"
},
"dependencies": {
"shield-studies-addon-utils": "github:mozilla/shield-studies-addon-utils#release/5.0.4"
"shield-studies-addon-utils": "5.2.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
@ -46,6 +46,7 @@
"sinon": "4.0.0",
"sinon-chrome": "2.2.1",
"web-ext": "^2.9.3",
"webext-experiment-utils": "github:mozilla/webext-experiment-utils#develop",
"yamljs": "^0.3.0"
},
"engines": {
@ -65,15 +66,17 @@
},
"scripts": {
"build": "web-ext build",
"bundle-utils": "copyStudyUtils src/privileged",
"build:use-if-pioneer-study": "web-ext build && importPioneerOptIn",
"bundle:studyUtils": "copyStudyUtils src/privileged",
"docformat": "doctoc --title '**Contents**' docs/*.md && prettier '**/*.md' --write",
"eslint": "eslint . --ext jsm,js,json",
"eslint-fix": "npm run eslint -- --fix",
"format": "prettier '**/*.{css,js,json,jsm,md}' --trailing-comma=all --ignore-path=.eslintignore --write",
"generate": "npm-run-all -s -n generate:schema:* generate:docs:* generate:stubApi:* format",
"generate:docs:testingOverrides": "node ./node_modules/shield-studies-addon-utils/weeUtils/documentSchema.js ./src/privileged/testingOverrides/schema.json > ./src/privileged/testingOverrides/api.md",
"generate:schema:testingOverrides": "cd ./src/privileged/testingOverrides && yaml2json schema.yaml -p > schema.json && node ../../../node_modules/shield-studies-addon-utils/weeUtils/verifyWeeSchema.js schema.json",
"generate:stubApi:testingOverrides": "cd ./src/privileged/testingOverrides && node ../../../node_modules/shield-studies-addon-utils/weeUtils/generateStubApi.js ./schema.json > stubApi.js",
"generate": "npm-run-all -s -n generate:generateSchema:* generate:verifyWeeSchema:* generate:documentSchema:* generate:generateStubApi:*",
"generate:documentSchema:privacyContext": "cd src/privileged/privacyContext && documentSchema schema.json > api.md",
"generate:generateSchema:privacyContext": "cd src/privileged/privacyContext && yaml2json schema.yaml -p > schema.json",
"generate:generateStubApi:privacyContext": "cd src/privileged/privacyContext && generateStubApi ./schema.json > stubApi.js",
"generate:verifyWeeSchema:privacyContext": "cd src/privileged/privacyContext && verifyWeeSchema schema.json",
"harness_test": "npm run test:func -- --retry 2 --reporter json",
"lint": "npm-run-all lint:*",
"lint:eslint": "npm run eslint",
@ -81,17 +84,21 @@
"lint:nsp": "nsp check",
"lint:web-ext-lint": "web-ext lint",
"postformat": "npm run eslint-fix && fixpack",
"prebuild": "npm run bundle-utils",
"prestart": "npm run bundle-utils",
"prebuild": "npm-run-all bundle:*",
"prestart": "npm run pretest",
"pretest": "node test/ensure_minimum_node_version.js && npm run build",
"prewatch": "npm run bundle-utils",
"prewatch": "npm-run-all bundle:*",
"sign": "echo 'TBD, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1407757'",
"start": "web-ext run --no-reload",
"test": "npm-run-all -n test:unit test:func",
"start:use-if-pioneer-study": "npm run test:manual",
"test": "npm-run-all -n test:unit # test:func",
"test:func": "npm-run-all -pr test:func:*",
"test:func:selenium-mocha": "FIREFOX_BINARY=${FIREFOX_BINARY:-nightly} ADDON_ZIP=./dist/button_icon_preference_-_shield_study_example-2.0.0.zip GECKODRIVER_URL=http://127.0.0.1:4444 mocha test/functional/ --bail --full-trace",
"test:func:start-geckodriver-server": "geckodriver -vv 1> test/results/logs/geckodriver.log 2> test/results/logs/geckodriver.errors.log",
"test:manual": "npm-run-all -pr test:func:start-geckodriver-server test:run-firefox",
"test:run-firefox": "FIREFOX_BINARY=${FIREFOX_BINARY:-nightly} ADDON_ZIP=./dist/button_icon_preference_-_shield_study_example-2.0.0.zip GECKODRIVER_URL=http://127.0.0.1:4444 node run-firefox.js",
"test:unit": "NODE_ENV=test karma start",
"watch": "web-ext run"
"watch": "web-ext run",
"watch:use-if-pioneer-study": "echo \"Watch is disabled since Pioneer Opt-in add-on needs to be available in the empty profile and web-ext thus is not an option\""
}
}

65
run-firefox.js Normal file
Просмотреть файл

@ -0,0 +1,65 @@
/* eslint-env node */
/* global browser */
// for unhandled promise rejection debugging
process.on("unhandledRejection", r => console.error(r)); // eslint-disable-line no-console
const utils = require("./test/functional/utils");
const STUDY_TYPE = process.env.STUDY_TYPE || "shield";
const LOG_LEVEL = process.env.LOG_LEVEL || "info";
const EXPIRE_SECONDS = process.env.EXPIRE_SECONDS || false;
const EXPIRED = process.env.EXPIRED || false;
const run = async studyType => {
const driver = await utils.setupWebdriver.promiseSetupDriver(
utils.FIREFOX_PREFERENCES,
);
const widgetId = utils.ui.makeWidgetId(
"jestr-pioneer-shield-study@pioneer.mozilla.org",
);
/*
await utils.preferences.set(
driver,
`extensions.${widgetId}.test.studyType`,
STUDY_TYPE,
);
*/
if (EXPIRE_SECONDS > 0) {
// Set preference that simulates that the study will expire after EXPIRE_SECONDS seconds
const beginTime = Date.now();
const msInOneDay = 60 * 60 * 24 * 1000;
const expiresInDays = 7 * 5; // 5 weeks // Needs to be the same as in src/studySetup.js
const firstRunTimestamp =
beginTime - msInOneDay * expiresInDays + EXPIRE_SECONDS * 1000;
await utils.preferences.set(
driver,
`extensions.${widgetId}.test.firstRunTimestamp`,
String(firstRunTimestamp),
);
}
if (EXPIRED) {
// Set preference that simulates that the study has already expired before the study starts
await utils.preferences.set(
driver,
`extensions.${widgetId}.test.expired`,
true,
);
}
await utils.preferences.set(driver, `shieldStudy.logLevel`, LOG_LEVEL);
await utils.preferences.set(
driver,
`browser.ctrlTab.recentlyUsedOrder`,
false,
);
if (studyType === "pioneer") {
await utils.setupWebdriver.installPioneerOptInAddon(driver);
}
await utils.setupWebdriver.installAddon(driver);
await utils.ui.openBrowserConsole(driver);
await driver.sleep(1000 * 60 * 60 * 24);
driver.quit();
};
run(STUDY_TYPE);

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

@ -48,6 +48,7 @@ class StudyLifeCycleHandler {
*/
browser.study.onEndStudy.addListener(this.handleStudyEnding.bind(this));
browser.study.onReady.addListener(this.enableFeature.bind(this));
this.expirationAlarmName = `${browser.runtime.id}:studyExpiration`;
}
/**
@ -61,6 +62,7 @@ class StudyLifeCycleHandler {
*/
async cleanup() {
await browser.storage.local.clear();
await browser.alarms.clear(this.expirationAlarmName);
await feature.cleanup();
}
@ -74,11 +76,11 @@ class StudyLifeCycleHandler {
*
* @returns {undefined}
*/
enableFeature(studyInfo) {
console.log("Enabling experiment", studyInfo);
async enableFeature(studyInfo) {
await browser.study.logger.log(["Enabling experiment", studyInfo]);
const { delayInMinutes } = studyInfo;
if (delayInMinutes !== undefined) {
const alarmName = `${browser.runtime.id}:studyExpiration`;
const alarmName = this.expirationAlarmName;
const alarmListener = async alarm => {
if (alarm.name === alarmName) {
browser.alarms.onAlarm.removeListener(alarmListener);
@ -90,7 +92,7 @@ class StudyLifeCycleHandler {
delayInMinutes,
});
}
feature.configure(studyInfo);
return feature.configure(studyInfo);
}
/** handles `study:end` signals
@ -103,19 +105,19 @@ class StudyLifeCycleHandler {
* @returns {undefined}
*/
async handleStudyEnding(ending) {
console.log(`Study wants to end:`, ending);
await browser.study.logger.log([`Study wants to end:`, ending]);
for (const url of ending.urls) {
await browser.tabs.create({ url });
}
switch (ending.endingName) {
// could have different actions depending on positive / ending names
default:
console.log(`The ending: ${ending.endingName}`);
await browser.study.logger.log(`The ending: ${ending.endingName}`);
await this.cleanup();
break;
}
// actually remove the addon.
console.log("About to actually uninstall");
await browser.study.logger.log("About to actually uninstall");
return browser.management.uninstallSelf();
}
}
@ -129,7 +131,7 @@ async function onEveryExtensionLoad() {
new StudyLifeCycleHandler();
const studySetup = await getStudySetup();
console.log(`Study setup: `, studySetup);
await browser.study.logger.log([`Study setup: `, studySetup]);
await browser.study.setup(studySetup);
}
onEveryExtensionLoad();

Двоичные данные
src/icons/shield-icon.256.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 24 KiB

Двоичные данные
src/icons/shield-icon.48.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.6 KiB

Двоичные данные
src/icons/shield-icon.98.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 9.5 KiB

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

@ -12,7 +12,6 @@
xmlns:ns1="http://sozi.baierouge.fr"
id="svg2"
sodipodi:docname="Shield Icon.svg"
inkscape:export-filename="C:\Documents and Settings\Marrick\Desktop\Inkscape\Shield Icon.png"
viewBox="0 0 256 256"
inkscape:export-xdpi="90"
version="1.1"

До

Ширина:  |  Высота:  |  Размер: 9.7 KiB

После

Ширина:  |  Высота:  |  Размер: 9.6 KiB

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

@ -19,6 +19,14 @@
"paths": [["introductionNotificationBar"]]
}
},
"privacyContext": {
"schema": "./privileged/privacyContext/schema.json",
"parent": {
"scopes": ["addon_parent"],
"script": "./privileged/privacyContext/api.js",
"paths": [["privacyContext"]]
}
},
"study": {
"schema": "./privileged/study/schema.json",
"parent": {
@ -26,14 +34,6 @@
"script": "./privileged/study/api.js",
"paths": [["study"]]
}
},
"testingOverrides": {
"schema": "./privileged/testingOverrides/schema.json",
"parent": {
"scopes": ["addon_parent"],
"script": "./privileged/testingOverrides/api.js",
"paths": [["testingOverrides"]]
}
}
},
"permissions": ["management", "storage", "alarms"],
@ -41,7 +41,8 @@
"scripts": ["studySetup.js", "feature.js", "background.js"]
},
"icons": {
"48": "icons/shield-icon.svg"
"48": "icons/shield-icon.48.png",
"96": "icons/shield-icon.98.png"
},
"browser_action": {
"browser_style": true,

2
src/privileged/.gitignore поставляемый
Просмотреть файл

@ -1,3 +1,3 @@
# ignore APIs that are bundled from shield-studies-addon-utils
# ignore APIs that are maintained elsewhere
study/

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

@ -0,0 +1,51 @@
"use strict";
/* global ExtensionAPI */
ChromeUtils.import("resource://gre/modules/Console.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
/*
const {ExtensionCommon} = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
const {ExtensionUtils} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
const { EventManager } = ExtensionCommon;
const { EventEmitter } = ExtensionUtils;
*/
const { PrivateBrowsingUtils } = ChromeUtils.import(
"resource://gre/modules/PrivateBrowsingUtils.jsm",
);
this.privacyContext = class extends ExtensionAPI {
getAPI(context) {
return {
privacyContext: {
permanentPrivateBrowsing: async function permanentPrivateBrowsing() {
return PrivateBrowsingUtils.permanentPrivateBrowsing;
},
/*
privateBrowsingAutostartEnabled: async function privateBrowsingAutostartEnabled() {
const privateBrowsingAutostart = Preferences.get(
"browser.privatebrowsing.autostart",
);
return privateBrowsingAutostart !== false;
},
*/
aPrivateBrowserWindowIsOpen: async function aPrivateBrowserWindowIsOpen() {
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
return true;
}
const windowList = Services.wm.getEnumerator("navigator:browser");
while (windowList.hasMoreElements()) {
const nextWin = windowList.getNext();
if (PrivateBrowsingUtils.isWindowPrivate(nextWin)) {
return true;
}
}
return false;
},
},
};
}
};

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

@ -0,0 +1,23 @@
# Namespace: `browser.privacyContext`
Accesses privacy-related context / settings which can affect study/experiment behavior
## Functions
### `browser.privacyContext.permanentPrivateBrowsing( )`
**Parameters**
### `browser.privacyContext.aPrivateBrowserWindowIsOpen( )`
**Parameters**
## Events
(None)
## Properties TBD
## Data Types
(None)

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

@ -0,0 +1,22 @@
[
{
"namespace": "privacyContext",
"description": "Accesses privacy-related context / settings which can affect study/experiment behavior",
"functions": [
{
"name": "permanentPrivateBrowsing",
"type": "function",
"async": true,
"description": "",
"parameters": []
},
{
"name": "aPrivateBrowserWindowIsOpen",
"type": "function",
"async": true,
"description": "",
"parameters": []
}
]
}
]

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

@ -13,26 +13,16 @@
#
---
- namespace: testingOverrides
description: Accesses preferences used to set studySetup testing flags
- namespace: privacyContext
description: Accesses privacy-related context / settings which can affect study/experiment behavior
functions:
- name: getVariationNameOverride
- name: permanentPrivateBrowsing
type: 'function'
async: true
description: ''
parameters: []
- name: getFirstRunTimestampOverride
type: 'function'
async: true
description: ''
parameters: []
- name: getExpiredOverride
type: 'function'
async: true
description: ''
parameters: []
- name: listPreferences
- name: aPrivateBrowserWindowIsOpen
type: 'function'
async: true
description: ''

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

@ -0,0 +1,33 @@
/* eslint-env commonjs */
/* eslint no-logger: off */
/* eslint no-unused-vars: off */
/* global ExtensionAPI */
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
/* eslint-disable no-undef */
const { EventManager } = ExtensionCommon;
const EventEmitter =
ExtensionCommon.EventEmitter || ExtensionUtils.EventEmitter;
this.privacyContext = class extends ExtensionAPI {
getAPI(context) {
const apiEventEmitter = new EventEmitter();
return {
privacyContext: {
/* @TODO no description given */
permanentPrivateBrowsing: async function permanentPrivateBrowsing() {
console.log("Called permanentPrivateBrowsing()");
return undefined;
},
/* @TODO no description given */
aPrivateBrowserWindowIsOpen: async function aPrivateBrowserWindowIsOpen() {
console.log("Called aPrivateBrowserWindowIsOpen()");
return undefined;
},
},
};
}
};

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

@ -1,54 +0,0 @@
"use strict";
/* global ExtensionAPI, Preferences */
ChromeUtils.import("resource://gre/modules/Console.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Preferences.jsm");
this.testingOverrides = class extends ExtensionAPI {
getAPI(context) {
const { extension } = this;
// Copied here from tree
function makeWidgetId(id) {
id = id.toLowerCase();
return id.replace(/[^a-z0-9_-]/g, "_");
}
const widgetId = makeWidgetId(extension.manifest.applications.gecko.id);
function convertToNumberIfNotNull(value) {
return value !== null ? Number(value) : null;
}
return {
testingOverrides: {
getVariationNameOverride: async function getVariationNameOverride() {
return Preferences.get(
`extensions.${widgetId}.test.variationName`,
null,
);
},
getFirstRunTimestampOverride: async function getFirstRunTimestampOverride() {
return convertToNumberIfNotNull(
Preferences.get(
`extensions.${widgetId}.test.firstRunTimestamp`,
null,
),
);
},
getExpiredOverride: async function getExpiredOverride() {
return Preferences.get(`extensions.${widgetId}.test.expired`, null);
},
listPreferences: async function listPreferences() {
return [
`extensions.${widgetId}.test.variationName`,
`extensions.${widgetId}.test.firstRunTimestamp`,
`extensions.${widgetId}.test.expired`,
];
},
},
};
}
};

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

@ -1,31 +0,0 @@
# Namespace: `browser.testingOverrides`
Accesses preferences used to set studySetup testing flags
## Functions
### `browser.testingOverrides.getVariationNameOverride( )`
**Parameters**
### `browser.testingOverrides.getFirstRunTimestampOverride( )`
**Parameters**
### `browser.testingOverrides.getExpiredOverride( )`
**Parameters**
### `browser.testingOverrides.listPreferences( )`
**Parameters**
## Events
(None)
## Properties TBD
## Data Types
(None)

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

@ -1,36 +0,0 @@
[
{
"namespace": "testingOverrides",
"description": "Accesses preferences used to set studySetup testing flags",
"functions": [
{
"name": "getVariationNameOverride",
"type": "function",
"async": true,
"description": "",
"parameters": []
},
{
"name": "getFirstRunTimestampOverride",
"type": "function",
"async": true,
"description": "",
"parameters": []
},
{
"name": "getExpiredOverride",
"type": "function",
"async": true,
"description": "",
"parameters": []
},
{
"name": "listPreferences",
"type": "function",
"async": true,
"description": "",
"parameters": []
}
]
}
]

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

@ -1,41 +0,0 @@
/* eslint-disable */
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
// eslint-disable-next-line no-undef
const { EventManager } = ExtensionCommon;
// eslint-disable-next-line no-undef
const { EventEmitter } = ExtensionUtils;
this.testingOverrides = class extends ExtensionAPI {
getAPI(context) {
return {
testingOverrides: {
/* @TODO no description given */
getVariationNameOverride: async function getVariationNameOverride() {
console.log("called getVariationNameOverride ");
return undefined;
},
/* @TODO no description given */
getFirstRunTimestampOverride: async function getFirstRunTimestampOverride() {
console.log("called getFirstRunTimestampOverride ");
return undefined;
},
/* @TODO no description given */
getExpiredOverride: async function getExpiredOverride() {
console.log("called getExpiredOverride ");
return undefined;
},
/* @TODO no description given */
listPreferences: async function listPreferences() {
console.log("called listPreferences ");
return undefined;
},
},
};
}
};

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

@ -22,15 +22,17 @@ const baseStudySetup = {
// used for activeExperiments tagging (telemetryEnvironment.setActiveExperiment)
activeExperimentName: browser.runtime.id,
// uses shield sampling and telemetry semantics. Future: will support "pioneer"
// use either "shield" or "pioneer" telemetry semantics and data pipelines
studyType: "shield",
// telemetry
telemetry: {
// default false. Actually send pings.
// Actually submit the pings to Telemetry. [default if omitted: false]
send: true,
// Marks pings with testing=true. Set flag to `true` before final release
removeTestingFlag: false,
// Marks pings with testing=true. Set flag to `true` for pings are meant to be seen by analysts [default if omitted: false]
removeTestingFlag: true,
// Keep an internal telemetry archive. Useful for verifying payloads of Pioneer studies without risking actually sending any unencrypted payloads [default if omitted: false]
internalTelemetryArchive: false,
},
// endings with urls
@ -95,6 +97,24 @@ const baseStudySetup = {
},
};
async function isCurrentlyEligible(studySetup) {
const dataPermissions = await browser.study.getDataPermissions();
if (studySetup.studyType === "shield") {
allowed = dataPermissions.shield;
}
if (studySetup.studyType === "pioneer") {
allowed = dataPermissions.pioneer;
}
// Users with private browsing on autostart are not eligible
if (await browser.privacyContext.permanentPrivateBrowsing()) {
await browser.study.logger.log(
"Permanent private browsing, exiting study",
);
allowed = false;
}
return allowed;
}
/**
* Determine, based on common and study-specific criteria, if enroll (first run)
* should proceed.
@ -107,24 +127,23 @@ const baseStudySetup = {
*
* This implementation caches in local storage to speed up second run.
*
* @param {object} studySetup A complete study setup object
* @returns {Promise<boolean>} answer An boolean answer about whether the user should be
* allowed to enroll in the study
*/
async function cachingFirstRunShouldAllowEnroll() {
async function wasEligibleAtFirstRun(studySetup) {
// Cached answer. Used on 2nd run
let allowed = await browser.storage.local.get("allowEnroll");
if (allowed) return true;
const localStorageResult = await browser.storage.local.get(
"allowedEnrollOnFirstRun",
);
if (localStorageResult.allowedEnrollOnFirstRun === true) return true;
/*
First run, we must calculate the answer.
If false, the study will endStudy with 'ineligible' during `setup`
*/
// could have other reasons to be eligible, such add-ons, prefs
allowed = true;
// First run, we must calculate the answer.
// If false, the study will endStudy with 'ineligible' during `setup`
const allowed = await isCurrentlyEligible(studySetup);
// cache the answer
await browser.storage.local.set({ allowEnroll: allowed });
await browser.storage.local.set({ allowedEnrollOnFirstRun: allowed });
return allowed;
}
@ -137,17 +156,20 @@ async function getStudySetup() {
// shallow copy
const studySetup = Object.assign({}, baseStudySetup);
studySetup.allowEnroll = await cachingFirstRunShouldAllowEnroll();
studySetup.allowEnroll = await wasEligibleAtFirstRun(studySetup);
const testingPreferences = await browser.testingOverrides.listPreferences();
console.log(
"The preferences that can be used to override testing flags: ",
testingPreferences,
);
// If the eligibility criterias are not dependent on the state of the first run only
// but rather should be checked on every browser launch, skip the use
// of wasEligibleAtFirstRun and instead use the below:
// studySetup.allowEnroll = await wasEligibleAtFirstRun(studySetup);
const testingOverrides = await browser.study.getTestingOverrides();
studySetup.testing = {
variationName: await browser.testingOverrides.getVariationNameOverride(),
firstRunTimestamp: await browser.testingOverrides.getFirstRunTimestampOverride(),
expired: await browser.testingOverrides.getExpiredOverride(),
variationName: testingOverrides.variationName,
firstRunTimestamp: testingOverrides.firstRunTimestamp,
expired: testingOverrides.expired,
};
// TODO: Possible add testing override for studySetup.telemetry.internalTelemetryArchive
return studySetup;
}

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

@ -38,6 +38,9 @@ const FIREFOX_PREFERENCES = {
// Re-usable test methods from shield-studies-addon-utils
const { executeJs } = require("shield-studies-addon-utils/testUtils/executeJs");
const { nav } = require("shield-studies-addon-utils/testUtils/nav");
const {
preferences,
} = require("shield-studies-addon-utils/testUtils/preferences");
const {
setupWebdriver,
} = require("shield-studies-addon-utils/testUtils/setupWebdriver");
@ -49,6 +52,7 @@ module.exports = {
FIREFOX_PREFERENCES,
executeJs,
nav,
preferences,
setupWebdriver,
telemetry,
ui,

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

@ -13,7 +13,10 @@ const defaultConfig = {
firefox: process.env.FIREFOX_BINARY || "nightly",
browserConsole: true,
startUrl: ["about:debugging"],
pref: ["shieldStudy.logLevel=All"],
pref: [
"shieldStudy.logLevel=All",
"browser.ctrlTab.recentlyUsedOrder=false",
],
},
};