summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_experiments.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_experiments.js451
1 files changed, 451 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js b/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
new file mode 100644
index 0000000000..cd2eb4dbb7
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
@@ -0,0 +1,451 @@
+"use strict";
+
+/* globals browser */
+const { AddonSettings } = ChromeUtils.importESModule(
+ "resource://gre/modules/addons/AddonSettings.sys.mjs"
+);
+
+AddonTestUtils.init(this);
+AddonTestUtils.overrideCertDB();
+AddonTestUtils.createAppInfo(
+ "xpcshell@tests.mozilla.org",
+ "XPCShell",
+ "1",
+ "42"
+);
+
+add_task(async function setup() {
+ AddonTestUtils.overrideCertDB();
+ await ExtensionTestUtils.startAddonManager();
+});
+
+let fooExperimentAPIs = {
+ foo: {
+ schema: "schema.json",
+ parent: {
+ scopes: ["addon_parent"],
+ script: "parent.js",
+ paths: [["experiments", "foo", "parent"]],
+ },
+ child: {
+ scopes: ["addon_child"],
+ script: "child.js",
+ paths: [
+ ["experiments", "foo", "child"],
+ ["experiments", "foo", "onChildEvent"],
+ ],
+ },
+ },
+};
+
+let fooExperimentFiles = {
+ "schema.json": JSON.stringify([
+ {
+ namespace: "experiments.foo",
+ types: [
+ {
+ id: "Meh",
+ type: "object",
+ properties: {},
+ },
+ ],
+ functions: [
+ {
+ name: "parent",
+ type: "function",
+ async: true,
+ parameters: [],
+ },
+ {
+ name: "child",
+ type: "function",
+ parameters: [],
+ returns: { type: "string" },
+ },
+ ],
+ events: [
+ {
+ name: "onChildEvent",
+ type: "function",
+ parameters: [],
+ },
+ ],
+ },
+ ]),
+
+ /* globals ExtensionAPI */
+ "parent.js": () => {
+ this.foo = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ experiments: {
+ foo: {
+ parent() {
+ return Promise.resolve("parent");
+ },
+ },
+ },
+ };
+ }
+ };
+ },
+
+ "child.js": () => {
+ this.foo = class extends ExtensionAPI {
+ getAPI(context) {
+ const EventManagerWithAssertions = class extends ExtensionCommon.EventManager {
+ constructor(...args) {
+ super(...args);
+ this.assertResetOnIdleOnEvent();
+ }
+
+ assertResetOnIdleOnEvent() {
+ const expectResetIdleOnEventFalse =
+ this.context.extension.persistentBackground;
+ if (expectResetIdleOnEventFalse && this.resetIdleOnEvent) {
+ const details = {
+ eventManagerName: this.name,
+ resetIdleOnEvent: this.resetIdleOnEvent,
+ envType: this.context.envType,
+ viewType: this.context.viewType,
+ isBackgroundContext: this.context.isBackgroundContext,
+ persistentBackground:
+ this.context.extension.persistentBackground,
+ };
+ throw new Error(
+ `EventManagerWithAssertions: resetIdleOnEvent should be forcefully set to false - ${JSON.stringify(
+ details
+ )}`
+ );
+ }
+ }
+ };
+ return {
+ experiments: {
+ foo: {
+ child() {
+ return "child";
+ },
+ onChildEvent: new EventManagerWithAssertions({
+ context,
+ name: `experiments.foo.onChildEvent`,
+ register: fire => {
+ return () => {};
+ },
+ }).api(),
+ },
+ },
+ };
+ }
+ };
+ },
+};
+
+async function testFooExperiment() {
+ browser.test.assertEq(
+ "object",
+ typeof browser.experiments,
+ "typeof browser.experiments"
+ );
+
+ browser.test.assertEq(
+ "object",
+ typeof browser.experiments.foo,
+ "typeof browser.experiments.foo"
+ );
+
+ browser.test.assertEq(
+ "function",
+ typeof browser.experiments.foo.child,
+ "typeof browser.experiments.foo.child"
+ );
+
+ browser.test.assertEq(
+ "function",
+ typeof browser.experiments.foo.parent,
+ "typeof browser.experiments.foo.parent"
+ );
+
+ browser.test.assertEq(
+ "child",
+ browser.experiments.foo.child(),
+ "foo.child()"
+ );
+
+ browser.test.assertEq(
+ "parent",
+ await browser.experiments.foo.parent(),
+ "await foo.parent()"
+ );
+}
+
+async function testFooFailExperiment() {
+ browser.test.assertEq(
+ "object",
+ typeof browser.experiments,
+ "typeof browser.experiments"
+ );
+
+ browser.test.assertEq(
+ "undefined",
+ typeof browser.experiments.foo,
+ "typeof browser.experiments.foo"
+ );
+}
+
+add_task(async function test_bundled_experiments() {
+ let testCases = [
+ { isSystem: true, temporarilyInstalled: true, shouldHaveExperiments: true },
+ {
+ isSystem: true,
+ temporarilyInstalled: false,
+ shouldHaveExperiments: true,
+ },
+ {
+ isPrivileged: true,
+ temporarilyInstalled: true,
+ shouldHaveExperiments: true,
+ },
+ {
+ isPrivileged: true,
+ temporarilyInstalled: false,
+ shouldHaveExperiments: true,
+ },
+ {
+ isPrivileged: false,
+ temporarilyInstalled: true,
+ shouldHaveExperiments: AddonSettings.EXPERIMENTS_ENABLED,
+ },
+ {
+ isPrivileged: false,
+ temporarilyInstalled: false,
+ shouldHaveExperiments: AppConstants.MOZ_APP_NAME == "thunderbird",
+ },
+ ];
+
+ async function background(shouldHaveExperiments) {
+ if (shouldHaveExperiments) {
+ await testFooExperiment();
+ } else {
+ await testFooFailExperiment();
+ }
+
+ browser.test.notifyPass("background.experiments.foo");
+ }
+
+ for (let testCase of testCases) {
+ let extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: testCase.isPrivileged,
+ isSystem: testCase.isSystem,
+ temporarilyInstalled: testCase.temporarilyInstalled,
+
+ manifest: {
+ experiment_apis: fooExperimentAPIs,
+ },
+
+ background: `
+ ${testFooExperiment}
+ ${testFooFailExperiment}
+ (${background})(${testCase.shouldHaveExperiments});
+ `,
+
+ files: fooExperimentFiles,
+ });
+
+ if (testCase.temporarilyInstalled && !testCase.shouldHaveExperiments) {
+ ExtensionTestUtils.failOnSchemaWarnings(false);
+ await Assert.rejects(
+ extension.startup(),
+ /Using 'experiment_apis' requires a privileged add-on/,
+ "startup failed without experimental api access"
+ );
+ ExtensionTestUtils.failOnSchemaWarnings(true);
+ } else {
+ await extension.startup();
+
+ await extension.awaitFinish("background.experiments.foo");
+
+ await extension.unload();
+ }
+ }
+});
+
+add_task(async function test_unbundled_experiments() {
+ async function background() {
+ await testFooExperiment();
+
+ browser.test.assertEq(
+ "object",
+ typeof browser.experiments.crunk,
+ "typeof browser.experiments.crunk"
+ );
+
+ browser.test.assertEq(
+ "function",
+ typeof browser.experiments.crunk.child,
+ "typeof browser.experiments.crunk.child"
+ );
+
+ browser.test.assertEq(
+ "function",
+ typeof browser.experiments.crunk.parent,
+ "typeof browser.experiments.crunk.parent"
+ );
+
+ browser.test.assertEq(
+ "crunk-child",
+ browser.experiments.crunk.child(),
+ "crunk.child()"
+ );
+
+ browser.test.assertEq(
+ "crunk-parent",
+ await browser.experiments.crunk.parent(),
+ "await crunk.parent()"
+ );
+
+ browser.test.notifyPass("background.experiments.crunk");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: true,
+
+ manifest: {
+ experiment_apis: fooExperimentAPIs,
+
+ permissions: ["experiments.crunk"],
+ },
+
+ background: `
+ ${testFooExperiment}
+ (${background})();
+ `,
+
+ files: fooExperimentFiles,
+ });
+
+ let apiExtension = ExtensionTestUtils.loadExtension({
+ isPrivileged: true,
+
+ manifest: {
+ browser_specific_settings: {
+ gecko: { id: "crunk@experiments.addons.mozilla.org" },
+ },
+
+ experiment_apis: {
+ crunk: {
+ schema: "schema.json",
+ parent: {
+ scopes: ["addon_parent"],
+ script: "parent.js",
+ paths: [["experiments", "crunk", "parent"]],
+ },
+ child: {
+ scopes: ["addon_child"],
+ script: "child.js",
+ paths: [["experiments", "crunk", "child"]],
+ },
+ },
+ },
+ },
+
+ files: {
+ "schema.json": JSON.stringify([
+ {
+ namespace: "experiments.crunk",
+ types: [
+ {
+ id: "Meh",
+ type: "object",
+ properties: {},
+ },
+ ],
+ functions: [
+ {
+ name: "parent",
+ type: "function",
+ async: true,
+ parameters: [],
+ },
+ {
+ name: "child",
+ type: "function",
+ parameters: [],
+ returns: { type: "string" },
+ },
+ ],
+ },
+ ]),
+
+ "parent.js": () => {
+ this.crunk = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ experiments: {
+ crunk: {
+ parent() {
+ return Promise.resolve("crunk-parent");
+ },
+ },
+ },
+ };
+ }
+ };
+ },
+
+ "child.js": () => {
+ this.crunk = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ experiments: {
+ crunk: {
+ child() {
+ return "crunk-child";
+ },
+ },
+ },
+ };
+ }
+ };
+ },
+ },
+ });
+
+ await apiExtension.startup();
+ await extension.startup();
+
+ await extension.awaitFinish("background.experiments.crunk");
+
+ await extension.unload();
+ await apiExtension.unload();
+});
+
+add_task(async function test_eventpage_with_experiments_resetOnIdleAssert() {
+ async function event_page() {
+ browser.test.log("EventPage startup");
+ // We expect EventManagerWithAssertions instance to throw
+ // here if the resetIdleOnEvent didn't got forcefully
+ // set to false for the EventManager instantiated in
+ // the child process.
+ browser.experiments.foo.onChildEvent.addListener(() => {});
+ browser.test.sendMessage("eventpage:ready");
+ }
+
+ const extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: true,
+ manifest: {
+ experiment_apis: fooExperimentAPIs,
+ background: { persistent: false },
+ },
+
+ background: event_page,
+
+ files: fooExperimentFiles,
+ });
+
+ await extension.startup();
+
+ await extension.awaitMessage("eventpage:ready");
+
+ await extension.unload();
+});