Bug 1866802 - Move ASRouterAdmin test to browser/components/asrouter. r=pdahiya

Differential Revision: https://phabricator.services.mozilla.com/D194812
This commit is contained in:
Mike Conley 2023-12-13 23:29:48 +00:00
parent 4b9f3545e2
commit a7ac21fdb8
7 changed files with 6197 additions and 97 deletions

View file

@ -19,32 +19,27 @@ module.exports = {
overrides: [ overrides: [
{ {
// Only mark the files as modules which are actually modules. // Only mark the files as modules which are actually modules.
// TODO: Add "tests/unit/**" to this list once we get our tests built. files: ["content-src/**", "tests/unit/**"],
files: ["content-src/**"],
parserOptions: { parserOptions: {
sourceType: "module", sourceType: "module",
}, },
}, },
{ {
// TODO: Add ./*.js and tests/unit/** to this list if necessary files: ["./*.js", "content-src/**", "tests/unit/**"],
files: ["./*.js", "content-src/**"],
env: { env: {
node: true, node: true,
}, },
}, },
/* TODO: Turn this rule on when I move the tests over.
{
// Use a configuration that's appropriate for modules, workers and
// non-production files.
files: ["modules/*.jsm", "tests/**"],
rules: {
"no-implicit-globals": "off",
},
},
*/
{ {
// TODO: Add "tests/unit/**" to this list once we get our tests built. // Use a configuration that's appropriate for modules, workers and
files: ["content-src/**"], // non-production files.
files: ["tests/**"],
rules: {
"no-implicit-globals": "off",
},
},
{
files: ["content-src/**", "tests/unit/**"],
rules: { rules: {
// Disallow commonjs in these directories. // Disallow commonjs in these directories.
"import/no-commonjs": 2, "import/no-commonjs": 2,
@ -52,28 +47,27 @@ module.exports = {
"react/jsx-no-bind": 0, "react/jsx-no-bind": 0,
}, },
}, },
/* TODO: Turn this rule on when I move the tests over. {
{ // These tests simulate the browser environment.
// These tests simulate the browser environment. files: "tests/unit/**",
files: "tests/unit/**", env: {
env: { browser: true,
browser: true, mocha: true,
mocha: true,
},
globals: {
assert: true,
chai: true,
sinon: true,
},
}, },
{ globals: {
files: "tests/**", assert: true,
rules: { chai: true,
"func-name-matching": 0, sinon: true,
"lines-between-class-members": 0, },
"require-await": 0, },
}, {
},*/ files: "tests/**",
rules: {
"func-name-matching": 0,
"lines-between-class-members": 0,
"require-await": 0,
},
},
], ],
rules: { rules: {
"fetch-options/no-fetch-credentials": "error", "fetch-options/no-fetch-credentials": "error",

View file

@ -0,0 +1,203 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = require("path");
const webpack = require("webpack");
const { ResourceUriPlugin } = require("../newtab/tools/resourceUriPlugin");
const PATHS = {
// Where is the entry point for the unit tests?
testEntryFile: path.resolve(__dirname, "./tests/unit/unit-entry.js"),
// A glob-style pattern matching all unit tests
testFilesPattern: "./tests/unit/unit-entry.js",
// The base directory of all source files (used for path resolution in webpack importing)
moduleResolveDirectory: __dirname,
newtabResolveDirectory: "../newtab",
// a RegEx matching all Cu.import statements of local files
resourcePathRegEx: /^resource:\/\/activity-stream\//,
coverageReportingPath: "logs/coverage/",
};
// When tweaking here, be sure to review the docs about the execution ordering
// semantics of the preprocessors array, as they are somewhat odd.
const preprocessors = {};
preprocessors[PATHS.testFilesPattern] = [
"webpack", // require("karma-webpack")
"sourcemap", // require("karma-sourcemap-loader")
];
module.exports = function (config) {
const isTDD = config.tdd;
const browsers = isTDD ? ["Firefox"] : ["FirefoxHeadless"]; // require("karma-firefox-launcher")
config.set({
singleRun: !isTDD,
browsers,
customLaunchers: {
FirefoxHeadless: {
base: "Firefox",
flags: ["--headless"],
},
},
frameworks: [
"chai", // require("chai") require("karma-chai")
"mocha", // require("mocha") require("karma-mocha")
"sinon", // require("sinon") require("karma-sinon")
],
reporters: [
"coverage-istanbul", // require("karma-coverage")
"mocha", // require("karma-mocha-reporter")
// for bin/try-runner.js to parse the output easily
"json", // require("karma-json-reporter")
],
jsonReporter: {
// So this doesn't get interleaved with other karma output
stdout: false,
outputFile: path.join("logs", "karma-run-results.json"),
},
coverageIstanbulReporter: {
reports: ["lcov", "text-summary"], // for some reason "lcov" reallys means "lcov" and "html"
"report-config": {
// so the full m-c path gets printed; needed for https://coverage.moz.tools/ integration
lcov: {
projectRoot: "../../..",
},
},
dir: PATHS.coverageReportingPath,
// This will make karma fail if coverage reporting is less than the minimums here
thresholds: !isTDD && {
each: {
statements: 100,
lines: 100,
functions: 100,
branches: 66,
overrides: {
"content-src/components/ASRouterAdmin/*.jsx": {
statements: 0,
lines: 0,
functions: 0,
branches: 0,
},
},
},
},
},
files: [PATHS.testEntryFile],
preprocessors,
webpack: {
mode: "none",
devtool: "inline-source-map",
// This loader allows us to override required files in tests
resolveLoader: {
alias: {
inject: path.join(__dirname, "../newtab/loaders/inject-loader"),
},
},
// This resolve config allows us to import with paths relative to the root directory, e.g. "lib/ActivityStream.jsm"
resolve: {
extensions: [".js", ".jsx"],
modules: [
PATHS.moduleResolveDirectory,
"node_modules",
PATHS.newtabResolveDirectory,
],
fallback: {
stream: require.resolve("stream-browserify"),
buffer: require.resolve("buffer"),
},
alias: {
newtab: path.join(__dirname, "../newtab"),
},
},
plugins: [
// The ResourceUriPlugin handles translating resource URIs in import
// statements in .mjs files, in a similar way to what
// babel-jsm-to-commonjs does for jsm files.
new ResourceUriPlugin({
resourcePathRegEx: PATHS.resourcePathRegEx,
}),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("development"),
}),
],
externals: {
// enzyme needs these for backwards compatibility with 0.13.
// see https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md#using-enzyme-with-webpack
"react/addons": true,
"react/lib/ReactContext": true,
"react/lib/ExecutionEnvironment": true,
},
module: {
rules: [
// This rule rewrites importing/exporting in .jsm files to be compatible with esmodules
{
test: /\.jsm$/,
exclude: [/node_modules/],
use: [
{
loader: "babel-loader", // require("babel-core")
options: {
plugins: [
// Converts .jsm files into common-js modules
[
"../newtab/tools/babel-jsm-to-commonjs.js",
{
basePath: PATHS.resourcePathRegEx,
removeOtherImports: true,
replace: true,
},
],
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-class-properties",
],
},
},
],
},
{
test: /\.js$/,
exclude: [/node_modules\/(?!@fluent\/).*/, /tests/],
loader: "babel-loader",
options: {
// This is a workaround for bug 1787278. It can be removed once
// that bug is fixed.
plugins: ["@babel/plugin-proposal-optional-chaining"],
},
},
{
test: /\.jsx$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"],
plugins: [
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-optional-chaining",
],
},
},
{
test: /\.md$/,
use: "raw-loader",
},
{
enforce: "post",
test: /\.js[mx]?$/,
loader: "@jsdevtools/coverage-istanbul-loader",
options: { esModules: true },
include: [path.resolve("content-src"), path.resolve("modules")],
exclude: [path.resolve("tests"), path.resolve("../newtab")],
},
],
},
},
// Silences some overly-verbose logging of individual module builds
webpackMiddleware: { noInfo: true },
});
};

File diff suppressed because it is too large Load diff

View file

@ -12,13 +12,32 @@
"redux": "4.1.2" "redux": "4.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-class-properties": "7.16.0",
"@babel/plugin-proposal-optional-chaining": "7.16.0", "@babel/plugin-proposal-optional-chaining": "7.16.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.0",
"@babel/preset-react": "7.16.0", "@babel/preset-react": "7.16.0",
"@jsdevtools/coverage-istanbul-loader": "^3.0.5",
"babel-loader": "8.2.3", "babel-loader": "8.2.3",
"babel-plugin-jsm-to-esmodules": "0.6.0",
"chai": "4.3.4",
"chai-json-schema": "1.5.1",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.6",
"karma": "6.3.8",
"karma-chai": "0.1.0",
"karma-coverage-istanbul-reporter": "3.0.3",
"karma-firefox-launcher": "2.1.2",
"karma-json-reporter": "1.2.1",
"karma-mocha": "2.0.1",
"karma-mocha-reporter": "2.2.5",
"karma-sinon": "1.0.5",
"karma-sourcemap-loader": "0.3.8",
"karma-webpack": "5.0.0",
"mocha": "9.1.3", "mocha": "9.1.3",
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"sass": "1.43.4", "sass": "1.43.4",
"sinon": "12.0.1", "sinon": "12.0.1",
"stream-browserify": "3.0.0",
"webpack": "5.56.0", "webpack": "5.56.0",
"webpack-cli": "4.9.1", "webpack-cli": "4.9.1",
"yamscripts": "0.1.0" "yamscripts": "0.1.0"

View file

@ -0,0 +1,262 @@
import { ASRouterAdminInner } from "content-src/components/ASRouterAdmin/ASRouterAdmin";
import { ASRouterUtils } from "content-src/asrouter/asrouter-utils";
import { GlobalOverrider } from "test/unit/utils";
import React from "react";
import { shallow } from "enzyme";
describe("ASRouterAdmin", () => {
let globalOverrider;
let sandbox;
let wrapper;
let globals;
let FAKE_PROVIDER_PREF = [
{
enabled: true,
id: "local_testing",
localProvider: "TestProvider",
type: "local",
},
];
let FAKE_PROVIDER = [
{
enabled: true,
id: "local_testing",
localProvider: "TestProvider",
messages: [],
type: "local",
},
];
beforeEach(() => {
globalOverrider = new GlobalOverrider();
sandbox = sinon.createSandbox();
sandbox.stub(ASRouterUtils, "getPreviewEndpoint").returns("foo");
globals = {
ASRouterMessage: sandbox.stub().resolves(),
ASRouterAddParentListener: sandbox.stub(),
ASRouterRemoveParentListener: sandbox.stub(),
};
globalOverrider.set(globals);
wrapper = shallow(<ASRouterAdminInner location={{ routes: [""] }} />);
wrapper.setState({ devtoolsEnabled: true });
});
afterEach(() => {
sandbox.restore();
globalOverrider.restore();
});
it("should render ASRouterAdmin component", () => {
assert.ok(wrapper.exists());
});
it("should send ADMIN_CONNECT_STATE on mount", () => {
assert.calledOnce(globals.ASRouterMessage);
assert.calledWith(globals.ASRouterMessage, {
type: "ADMIN_CONNECT_STATE",
data: { endpoint: "foo" },
});
});
describe("#getSection", () => {
it("should render a message provider section by default", () => {
assert.equal(wrapper.find("h2").at(1).text(), "Messages");
});
it("should render a targeting section for targeting route", () => {
wrapper = shallow(
<ASRouterAdminInner location={{ routes: ["targeting"] }} />
);
wrapper.setState({ devtoolsEnabled: true });
assert.equal(wrapper.find("h2").at(0).text(), "Targeting Utilities");
});
it("should render two error messages", () => {
wrapper = shallow(
<ASRouterAdminInner location={{ routes: ["errors"] }} Sections={[]} />
);
wrapper.setState({ devtoolsEnabled: true });
const firstError = {
timestamp: Date.now() + 100,
error: { message: "first" },
};
const secondError = {
timestamp: Date.now(),
error: { message: "second" },
};
wrapper.setState({
providers: [{ id: "foo", errors: [firstError, secondError] }],
});
assert.equal(
wrapper.find("tbody tr").at(0).find("td").at(0).text(),
"foo"
);
assert.lengthOf(wrapper.find("tbody tr"), 2);
assert.equal(
wrapper.find("tbody tr").at(0).find("td").at(1).text(),
secondError.error.message
);
});
});
describe("#render", () => {
beforeEach(() => {
wrapper.setState({
providerPrefs: [],
providers: [],
userPrefs: {},
});
});
describe("#renderProviders", () => {
it("should render the provider", () => {
wrapper.setState({
providerPrefs: FAKE_PROVIDER_PREF,
providers: FAKE_PROVIDER,
});
// Header + 1 item
assert.lengthOf(wrapper.find(".message-item"), 2);
});
});
describe("#renderMessages", () => {
beforeEach(() => {
sandbox.stub(ASRouterUtils, "blockById").resolves();
sandbox.stub(ASRouterUtils, "unblockById").resolves();
sandbox.stub(ASRouterUtils, "overrideMessage").resolves({ foo: "bar" });
sandbox.stub(ASRouterUtils, "sendMessage").resolves();
wrapper.setState({
messageFilter: "all",
messageBlockList: [],
messageImpressions: { foo: 2 },
groups: [{ id: "messageProvider", enabled: true }],
providers: [{ id: "messageProvider", enabled: true }],
});
});
it("should render a message when no filtering is applied", () => {
wrapper.setState({
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".message-id"), 1);
wrapper.find(".message-item button.primary").simulate("click");
assert.calledOnce(ASRouterUtils.blockById);
assert.calledWith(ASRouterUtils.blockById, "foo");
});
it("should render a blocked message", () => {
wrapper.setState({
messages: [
{
id: "foo",
groups: ["messageProvider"],
provider: "messageProvider",
},
],
messageBlockList: ["foo"],
});
assert.lengthOf(wrapper.find(".message-item.blocked"), 1);
wrapper.find(".message-item.blocked button").simulate("click");
assert.calledOnce(ASRouterUtils.unblockById);
assert.calledWith(ASRouterUtils.unblockById, "foo");
});
it("should render a message if provider matches filter", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".message-id"), 1);
});
it("should override with the selected message", async () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".message-id"), 1);
wrapper.find(".message-item button.show").simulate("click");
assert.calledOnce(ASRouterUtils.overrideMessage);
assert.calledWith(ASRouterUtils.overrideMessage, "foo");
await ASRouterUtils.overrideMessage();
assert.equal(wrapper.state().foo, "bar");
});
it("should hide message if provider filter changes", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".message-id"), 1);
wrapper.find("select").simulate("change", { target: { value: "bar" } });
assert.lengthOf(wrapper.find(".message-id"), 0);
});
it("should not display Reset All button if provider filter value is set to all or test providers", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".messages-reset"), 1);
wrapper.find("select").simulate("change", { target: { value: "all" } });
assert.lengthOf(wrapper.find(".messages-reset"), 0);
wrapper
.find("select")
.simulate("change", { target: { value: "test_local_testing" } });
assert.lengthOf(wrapper.find(".messages-reset"), 0);
});
it("should trigger disable and enable provider on Reset All button click", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
providerPrefs: [
{
id: "messageProvider",
},
],
});
wrapper.find(".messages-reset").simulate("click");
assert.calledTwice(ASRouterUtils.sendMessage);
assert.calledWith(ASRouterUtils.sendMessage, {
type: "DISABLE_PROVIDER",
data: "messageProvider",
});
assert.calledWith(ASRouterUtils.sendMessage, {
type: "ENABLE_PROVIDER",
data: "messageProvider",
});
});
});
});
});

View file

@ -0,0 +1,720 @@
import {
EventEmitter,
FakePrefs,
FakensIPrefService,
GlobalOverrider,
FakeConsoleAPI,
FakeLogger,
} from "newtab/test/unit/utils";
import Adapter from "enzyme-adapter-react-16";
import { chaiAssertions } from "newtab/test/schemas/pings";
import chaiJsonSchema from "chai-json-schema";
import enzyme from "enzyme";
import FxMSCommonSchema from "newtab/content-src/asrouter/schemas/FxMSCommon.schema.json";
enzyme.configure({ adapter: new Adapter() });
// Cause React warnings to make tests that trigger them fail
const origConsoleError = console.error;
console.error = function (msg, ...args) {
origConsoleError.apply(console, [msg, ...args]);
if (
/(Invalid prop|Failed prop type|Check the render method|React Intl)/.test(
msg
)
) {
throw new Error(msg);
}
};
const req = require.context(".", true, /\.test\.jsx?$/);
const files = req.keys();
// This exposes sinon assertions to chai.assert
sinon.assert.expose(assert, { prefix: "" });
chai.use(chaiAssertions);
chai.use(chaiJsonSchema);
chai.tv4.addSchema("file:///FxMSCommon.schema.json", FxMSCommonSchema);
const overrider = new GlobalOverrider();
const RemoteSettings = name => ({
get: () => {
if (name === "attachment") {
return Promise.resolve([{ attachment: {} }]);
}
return Promise.resolve([]);
},
on: () => {},
off: () => {},
});
RemoteSettings.pollChanges = () => {};
class JSWindowActorParent {
sendAsyncMessage(name, data) {
return { name, data };
}
}
class JSWindowActorChild {
sendAsyncMessage(name, data) {
return { name, data };
}
sendQuery(name, data) {
return Promise.resolve({ name, data });
}
get contentWindow() {
return {
Promise,
};
}
}
// Detect plain object passed to lazy getter APIs, and set its prototype to
// global object, and return the global object for further modification.
// Returns the object if it's not plain object.
//
// This is a workaround to make the existing testharness and testcase keep
// working even after lazy getters are moved to plain `lazy` object.
const cachedPlainObject = new Set();
function updateGlobalOrObject(object) {
// Given this function modifies the prototype, and the following
// condition doesn't meet on the second call, cache the result.
if (cachedPlainObject.has(object)) {
return global;
}
if (Object.getPrototypeOf(object).constructor.name !== "Object") {
return object;
}
cachedPlainObject.add(object);
Object.setPrototypeOf(object, global);
return global;
}
const TEST_GLOBAL = {
JSWindowActorParent,
JSWindowActorChild,
AboutReaderParent: {
addMessageListener: (messageName, listener) => {},
removeMessageListener: (messageName, listener) => {},
},
AboutWelcomeTelemetry: class {
submitGleanPingForPing() {}
},
AddonManager: {
getActiveAddons() {
return Promise.resolve({ addons: [], fullData: false });
},
},
AppConstants: {
MOZILLA_OFFICIAL: true,
MOZ_APP_VERSION: "69.0a1",
isChinaRepack() {
return false;
},
isPlatformAndVersionAtMost() {
return false;
},
platform: "win",
},
ASRouterPreferences: {
console: new FakeConsoleAPI({
maxLogLevel: "off", // set this to "debug" or "all" to get more ASRouter logging in tests
prefix: "ASRouter",
}),
},
AWScreenUtils: {
evaluateTargetingAndRemoveScreens() {
return true;
},
async removeScreens() {
return true;
},
evaluateScreenTargeting() {
return true;
},
},
BrowserUtils: {
sendToDeviceEmailsSupported() {
return true;
},
},
UpdateUtils: { getUpdateChannel() {} },
BasePromiseWorker: class {
constructor() {
this.ExceptionHandlers = [];
}
post() {}
},
browserSearchRegion: "US",
BrowserWindowTracker: { getTopWindow() {} },
ChromeUtils: {
defineModuleGetter: updateGlobalOrObject,
defineESModuleGetters: updateGlobalOrObject,
generateQI() {
return {};
},
import() {
return global;
},
importESModule() {
return global;
},
},
ClientEnvironment: {
get userId() {
return "foo123";
},
},
Components: {
Constructor(classId) {
switch (classId) {
case "@mozilla.org/referrer-info;1":
return function (referrerPolicy, sendReferrer, originalReferrer) {
this.referrerPolicy = referrerPolicy;
this.sendReferrer = sendReferrer;
this.originalReferrer = originalReferrer;
};
}
return function () {};
},
isSuccessCode: () => true,
},
ConsoleAPI: FakeConsoleAPI,
// NB: These are functions/constructors
// eslint-disable-next-line object-shorthand
ContentSearchUIController: function () {},
// eslint-disable-next-line object-shorthand
ContentSearchHandoffUIController: function () {},
Cc: {
"@mozilla.org/browser/nav-bookmarks-service;1": {
addObserver() {},
getService() {
return this;
},
removeObserver() {},
SOURCES: {},
TYPE_BOOKMARK: {},
},
"@mozilla.org/browser/nav-history-service;1": {
addObserver() {},
executeQuery() {},
getNewQuery() {},
getNewQueryOptions() {},
getService() {
return this;
},
insert() {},
markPageAsTyped() {},
removeObserver() {},
},
"@mozilla.org/io/string-input-stream;1": {
createInstance() {
return {};
},
},
"@mozilla.org/security/hash;1": {
createInstance() {
return {
init() {},
updateFromStream() {},
finish() {
return "0";
},
};
},
},
"@mozilla.org/updates/update-checker;1": { createInstance() {} },
"@mozilla.org/widget/useridleservice;1": {
getService() {
return {
idleTime: 0,
addIdleObserver() {},
removeIdleObserver() {},
};
},
},
"@mozilla.org/streamConverters;1": {
getService() {
return this;
},
},
"@mozilla.org/network/stream-loader;1": {
createInstance() {
return {};
},
},
},
Ci: {
nsICryptoHash: {},
nsIReferrerInfo: { UNSAFE_URL: 5 },
nsITimer: { TYPE_ONE_SHOT: 1 },
nsIWebProgressListener: { LOCATION_CHANGE_SAME_DOCUMENT: 1 },
nsIDOMWindow: Object,
nsITrackingDBService: {
TRACKERS_ID: 1,
TRACKING_COOKIES_ID: 2,
CRYPTOMINERS_ID: 3,
FINGERPRINTERS_ID: 4,
SOCIAL_ID: 5,
},
nsICookieBannerService: {
MODE_DISABLED: 0,
MODE_REJECT: 1,
MODE_REJECT_OR_ACCEPT: 2,
MODE_UNSET: 3,
},
},
Cu: {
importGlobalProperties() {},
now: () => window.performance.now(),
cloneInto: o => JSON.parse(JSON.stringify(o)),
},
console: {
...console,
error() {},
},
dump() {},
EveryWindow: {
registerCallback: (id, init, uninit) => {},
unregisterCallback: id => {},
},
setTimeout: window.setTimeout.bind(window),
clearTimeout: window.clearTimeout.bind(window),
fetch() {},
// eslint-disable-next-line object-shorthand
Image: function () {}, // NB: This is a function/constructor
IOUtils: {
writeJSON() {
return Promise.resolve(0);
},
readJSON() {
return Promise.resolve({});
},
read() {
return Promise.resolve(new Uint8Array());
},
makeDirectory() {
return Promise.resolve(0);
},
write() {
return Promise.resolve(0);
},
exists() {
return Promise.resolve(0);
},
remove() {
return Promise.resolve(0);
},
stat() {
return Promise.resolve(0);
},
},
NewTabUtils: {
activityStreamProvider: {
getTopFrecentSites: () => [],
executePlacesQuery: async (sql, options) => ({ sql, options }),
},
},
OS: {
File: {
writeAtomic() {},
makeDir() {},
stat() {},
Error: {},
read() {},
exists() {},
remove() {},
removeEmptyDir() {},
},
Path: {
join() {
return "/";
},
},
Constants: {
Path: {
localProfileDir: "/",
},
},
},
PathUtils: {
join(...parts) {
return parts[parts.length - 1];
},
joinRelative(...parts) {
return parts[parts.length - 1];
},
getProfileDir() {
return Promise.resolve("/");
},
getLocalProfileDir() {
return Promise.resolve("/");
},
},
PlacesUtils: {
get bookmarks() {
return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-bookmarks-service;1"];
},
get history() {
return TEST_GLOBAL.Cc["@mozilla.org/browser/nav-history-service;1"];
},
observers: {
addListener() {},
removeListener() {},
},
},
Preferences: FakePrefs,
PrivateBrowsingUtils: {
isBrowserPrivate: () => false,
isWindowPrivate: () => false,
permanentPrivateBrowsing: false,
},
DownloadsViewUI: {
getDisplayName: () => "filename.ext",
getSizeWithUnits: () => "1.5 MB",
},
FileUtils: {
// eslint-disable-next-line object-shorthand
File: function () {}, // NB: This is a function/constructor
},
Region: {
home: "US",
REGION_TOPIC: "browser-region-updated",
},
Services: {
dirsvc: {
get: () => ({ parent: { parent: { path: "appPath" } } }),
},
env: {
set: () => undefined,
},
locale: {
get appLocaleAsBCP47() {
return "en-US";
},
negotiateLanguages() {},
},
urlFormatter: { formatURL: str => str, formatURLPref: str => str },
mm: {
addMessageListener: (msg, cb) => this.receiveMessage(),
removeMessageListener() {},
},
obs: {
addObserver() {},
removeObserver() {},
notifyObservers() {},
},
telemetry: {
setEventRecordingEnabled: () => {},
recordEvent: eventDetails => {},
scalarSet: () => {},
keyedScalarAdd: () => {},
},
uuid: {
generateUUID() {
return "{foo-123-foo}";
},
},
console: { logStringMessage: () => {} },
prefs: new FakensIPrefService(),
tm: {
dispatchToMainThread: cb => cb(),
idleDispatchToMainThread: cb => cb(),
},
eTLD: {
getBaseDomain({ spec }) {
return spec.match(/\/([^/]+)/)[1];
},
getBaseDomainFromHost(host) {
return host.match(/.*?(\w+\.\w+)$/)[1];
},
getPublicSuffix() {},
},
io: {
newURI: spec => ({
mutate: () => ({
setRef: ref => ({
finalize: () => ({
ref,
spec,
}),
}),
}),
spec,
}),
},
search: {
init() {
return Promise.resolve();
},
getVisibleEngines: () =>
Promise.resolve([{ identifier: "google" }, { identifier: "bing" }]),
defaultEngine: {
identifier: "google",
searchForm:
"https://www.google.com/search?q=&ie=utf-8&oe=utf-8&client=firefox-b",
aliases: ["@google"],
},
defaultPrivateEngine: {
identifier: "bing",
searchForm: "https://www.bing.com",
aliases: ["@bing"],
},
getEngineByAlias: async () => null,
},
scriptSecurityManager: {
createNullPrincipal() {},
getSystemPrincipal() {},
},
wm: {
getMostRecentWindow: () => window,
getMostRecentBrowserWindow: () => window,
getEnumerator: () => [],
},
ww: { registerNotification() {}, unregisterNotification() {} },
appinfo: { appBuildID: "20180710100040", version: "69.0a1" },
scriptloader: { loadSubScript: () => {} },
startup: {
getStartupInfo() {
return {
process: {
getTime() {
return 1588010448000;
},
},
};
},
},
},
XPCOMUtils: {
defineLazyGetter(object, name, f) {
updateGlobalOrObject(object)[name] = f();
},
defineLazyGlobalGetters: updateGlobalOrObject,
defineLazyModuleGetters: updateGlobalOrObject,
defineLazyServiceGetter: updateGlobalOrObject,
defineLazyServiceGetters: updateGlobalOrObject,
defineLazyPreferenceGetter(object, name) {
updateGlobalOrObject(object)[name] = "";
},
generateQI() {
return {};
},
},
EventEmitter,
ShellService: {
doesAppNeedPin: () => false,
isDefaultBrowser: () => true,
},
FilterExpressions: {
eval() {
return Promise.resolve(false);
},
},
RemoteSettings,
Localization: class {
async formatMessages(stringsIds) {
return Promise.resolve(
stringsIds.map(({ id, args }) => ({ value: { string_id: id, args } }))
);
}
async formatValue(stringId) {
return Promise.resolve(stringId);
}
},
FxAccountsConfig: {
promiseConnectAccountURI(id) {
return Promise.resolve(id);
},
},
FX_MONITOR_OAUTH_CLIENT_ID: "fake_client_id",
ExperimentAPI: {
getExperiment() {},
getExperimentMetaData() {},
getRolloutMetaData() {},
},
NimbusFeatures: {
glean: {
getVariable() {},
},
newtab: {
getVariable() {},
getAllVariables() {},
onUpdate() {},
offUpdate() {},
},
pocketNewtab: {
getVariable() {},
getAllVariables() {},
onUpdate() {},
offUpdate() {},
},
cookieBannerHandling: {
getVariable() {},
},
},
TelemetryEnvironment: {
setExperimentActive() {},
currentEnvironment: {
profile: {
creationDate: 16587,
},
settings: {},
},
},
TelemetryStopwatch: {
start: () => {},
finish: () => {},
},
Sampling: {
ratioSample(seed, ratios) {
return Promise.resolve(0);
},
},
BrowserHandler: {
get kiosk() {
return false;
},
},
TelemetrySession: {
getMetadata(reason) {
return {
reason,
sessionId: "fake_session_id",
};
},
},
PageThumbs: {
addExpirationFilter() {},
removeExpirationFilter() {},
},
Logger: FakeLogger,
getFxAccountsSingleton() {},
AboutNewTab: {},
Glean: {
newtab: {
opened: {
record() {},
},
closed: {
record() {},
},
locale: {
set() {},
},
newtabCategory: {
set() {},
},
homepageCategory: {
set() {},
},
blockedSponsors: {
set() {},
},
sovAllocation: {
set() {},
},
},
newtabSearch: {
enabled: {
set() {},
},
},
pocket: {
enabled: {
set() {},
},
impression: {
record() {},
},
isSignedIn: {
set() {},
},
sponsoredStoriesEnabled: {
set() {},
},
click: {
record() {},
},
save: {
record() {},
},
topicClick: {
record() {},
},
},
topsites: {
enabled: {
set() {},
},
sponsoredEnabled: {
set() {},
},
impression: {
record() {},
},
click: {
record() {},
},
rows: {
set() {},
},
showPrivacyClick: {
record() {},
},
dismiss: {
record() {},
},
prefChanged: {
record() {},
},
},
topSites: {
pingType: {
set() {},
},
position: {
set() {},
},
source: {
set() {},
},
tileId: {
set() {},
},
reportingUrl: {
set() {},
},
advertiser: {
set() {},
},
contextId: {
set() {},
},
},
},
GleanPings: {
newtab: {
submit() {},
},
topSites: {
submit() {},
},
},
Utils: {
SERVER_URL: "bogus://foo",
},
};
overrider.set(TEST_GLOBAL);
describe("asrouter", () => {
after(() => overrider.restore());
files.forEach(file => req(file));
});