forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			287 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";
 | 
						|
 | 
						|
const { ExperimentAPI, _ExperimentFeature: ExperimentFeature } =
 | 
						|
  ChromeUtils.importESModule("resource://nimbus/ExperimentAPI.sys.mjs");
 | 
						|
 | 
						|
const { JsonSchema } = ChromeUtils.importESModule(
 | 
						|
  "resource://gre/modules/JsonSchema.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
ChromeUtils.defineLazyGetter(this, "fetchSchema", () => {
 | 
						|
  return fetch("resource://nimbus/schemas/NimbusEnrollment.schema.json", {
 | 
						|
    credentials: "omit",
 | 
						|
  }).then(rsp => rsp.json());
 | 
						|
});
 | 
						|
 | 
						|
const NON_MATCHING_ROLLOUT = Object.freeze(
 | 
						|
  ExperimentFakes.rollout("non-matching-rollout", {
 | 
						|
    branch: {
 | 
						|
      slug: "slug",
 | 
						|
      features: [
 | 
						|
        {
 | 
						|
          featureId: "aboutwelcome",
 | 
						|
          value: { enabled: false },
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    },
 | 
						|
  })
 | 
						|
);
 | 
						|
const MATCHING_ROLLOUT = Object.freeze(
 | 
						|
  ExperimentFakes.rollout("matching-rollout", {
 | 
						|
    branch: {
 | 
						|
      slug: "slug",
 | 
						|
      features: [
 | 
						|
        {
 | 
						|
          featureId: "aboutwelcome",
 | 
						|
          value: { enabled: false },
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    },
 | 
						|
  })
 | 
						|
);
 | 
						|
 | 
						|
const AW_FAKE_MANIFEST = {
 | 
						|
  description: "Different manifest with a special test variable",
 | 
						|
  isEarlyStartup: true,
 | 
						|
  variables: {
 | 
						|
    remoteValue: {
 | 
						|
      type: "boolean",
 | 
						|
      description: "Test value",
 | 
						|
    },
 | 
						|
    mochitest: {
 | 
						|
      type: "boolean",
 | 
						|
    },
 | 
						|
    enabled: {
 | 
						|
      type: "boolean",
 | 
						|
    },
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
async function setupForExperimentFeature() {
 | 
						|
  const sandbox = sinon.createSandbox();
 | 
						|
  const manager = ExperimentFakes.manager();
 | 
						|
 | 
						|
  await manager.onStartup();
 | 
						|
 | 
						|
  sandbox.stub(ExperimentAPI, "_store").get(() => manager.store);
 | 
						|
 | 
						|
  return { sandbox, manager };
 | 
						|
}
 | 
						|
 | 
						|
add_task(async function validSchema() {
 | 
						|
  const validator = new JsonSchema.Validator(await fetchSchema, {
 | 
						|
    shortCircuit: false,
 | 
						|
  });
 | 
						|
 | 
						|
  {
 | 
						|
    const result = validator.validate(NON_MATCHING_ROLLOUT);
 | 
						|
    Assert.ok(result.valid, JSON.stringify(result.errors, undefined, 2));
 | 
						|
  }
 | 
						|
  {
 | 
						|
    const result = validator.validate(MATCHING_ROLLOUT);
 | 
						|
    Assert.ok(result.valid, JSON.stringify(result.errors, undefined, 2));
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function readyCallAfterStore_with_remote_value() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome");
 | 
						|
 | 
						|
  Assert.ok(feature.getVariable("enabled"), "Feature is true by default");
 | 
						|
 | 
						|
  await manager.store.addEnrollment(MATCHING_ROLLOUT);
 | 
						|
 | 
						|
  Assert.ok(!feature.getVariable("enabled"), "Loads value from store");
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function has_sync_value_before_ready() {
 | 
						|
  let { manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome", AW_FAKE_MANIFEST);
 | 
						|
 | 
						|
  Assert.equal(
 | 
						|
    feature.getVariable("remoteValue"),
 | 
						|
    undefined,
 | 
						|
    "Feature is true by default"
 | 
						|
  );
 | 
						|
 | 
						|
  Services.prefs.setStringPref(
 | 
						|
    "nimbus.syncdefaultsstore.aboutwelcome",
 | 
						|
    JSON.stringify({
 | 
						|
      ...MATCHING_ROLLOUT,
 | 
						|
      branch: { feature: MATCHING_ROLLOUT.branch.features[0] },
 | 
						|
    })
 | 
						|
  );
 | 
						|
 | 
						|
  Services.prefs.setBoolPref(
 | 
						|
    "nimbus.syncdefaultsstore.aboutwelcome.remoteValue",
 | 
						|
    true
 | 
						|
  );
 | 
						|
 | 
						|
  Assert.equal(feature.getVariable("remoteValue"), true, "Sync load from pref");
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function update_remote_defaults_onUpdate() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome");
 | 
						|
  let stub = sandbox.stub();
 | 
						|
 | 
						|
  feature.onUpdate(stub);
 | 
						|
 | 
						|
  await manager.store.addEnrollment(MATCHING_ROLLOUT);
 | 
						|
 | 
						|
  Assert.ok(stub.called, "update event called");
 | 
						|
  Assert.equal(stub.callCount, 1, "Called once for remote configs");
 | 
						|
  Assert.equal(stub.firstCall.args[1], "rollout-updated", "Correct reason");
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function test_features_over_feature() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome");
 | 
						|
  const rollout_features_and_feature = Object.freeze(
 | 
						|
    ExperimentFakes.rollout("matching-rollout", {
 | 
						|
      branch: {
 | 
						|
        slug: "slug",
 | 
						|
        feature: {
 | 
						|
          featureId: "aboutwelcome",
 | 
						|
          value: { enabled: false },
 | 
						|
        },
 | 
						|
        features: [
 | 
						|
          {
 | 
						|
            featureId: "aboutwelcome",
 | 
						|
            value: { enabled: true },
 | 
						|
          },
 | 
						|
        ],
 | 
						|
      },
 | 
						|
    })
 | 
						|
  );
 | 
						|
  const rollout_just_feature = Object.freeze(
 | 
						|
    ExperimentFakes.rollout("matching-rollout", {
 | 
						|
      branch: {
 | 
						|
        slug: "slug",
 | 
						|
        feature: {
 | 
						|
          featureId: "aboutwelcome",
 | 
						|
          value: { enabled: false },
 | 
						|
        },
 | 
						|
      },
 | 
						|
    })
 | 
						|
  );
 | 
						|
 | 
						|
  await manager.store.addEnrollment(rollout_features_and_feature);
 | 
						|
  Assert.ok(
 | 
						|
    feature.getVariable("enabled"),
 | 
						|
    "Should read from the features property over feature"
 | 
						|
  );
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  manager.store._deleteForTests("matching-rollout");
 | 
						|
 | 
						|
  await manager.store.addEnrollment(rollout_just_feature);
 | 
						|
  Assert.ok(
 | 
						|
    !feature.getVariable("enabled"),
 | 
						|
    "Should read from the feature property when features doesn't exist"
 | 
						|
  );
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  manager.store._deleteForTests("matching-rollout");
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function update_remote_defaults_readyPromise() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome");
 | 
						|
  let stub = sandbox.stub();
 | 
						|
 | 
						|
  feature.onUpdate(stub);
 | 
						|
 | 
						|
  await manager.store.addEnrollment(MATCHING_ROLLOUT);
 | 
						|
 | 
						|
  Assert.ok(stub.calledOnce, "Update called after enrollment processed.");
 | 
						|
  Assert.ok(
 | 
						|
    stub.calledWith("featureUpdate:aboutwelcome", "rollout-updated"),
 | 
						|
    "Update called after enrollment processed."
 | 
						|
  );
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function update_remote_defaults_enabled() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome");
 | 
						|
 | 
						|
  Assert.equal(
 | 
						|
    feature.getVariable("enabled"),
 | 
						|
    true,
 | 
						|
    "Feature is enabled by manifest.variables.enabled"
 | 
						|
  );
 | 
						|
 | 
						|
  await manager.store.addEnrollment(NON_MATCHING_ROLLOUT);
 | 
						|
 | 
						|
  Assert.ok(
 | 
						|
    !feature.getVariable("enabled"),
 | 
						|
    "Feature is disabled by remote configuration"
 | 
						|
  );
 | 
						|
 | 
						|
  manager.store._deleteForTests("aboutwelcome");
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
// If the branch data returned from the store is not modified
 | 
						|
// this test should not throw
 | 
						|
add_task(async function test_getVariable_no_mutation() {
 | 
						|
  let { sandbox, manager } = await setupForExperimentFeature();
 | 
						|
  sandbox.stub(manager.store, "getExperimentForFeature").returns(
 | 
						|
    Cu.cloneInto(
 | 
						|
      {
 | 
						|
        branch: {
 | 
						|
          features: [{ featureId: "aboutwelcome", value: { mochitest: true } }],
 | 
						|
        },
 | 
						|
      },
 | 
						|
      {},
 | 
						|
      { deepFreeze: true }
 | 
						|
    )
 | 
						|
  );
 | 
						|
  let feature = new ExperimentFeature("aboutwelcome", AW_FAKE_MANIFEST);
 | 
						|
 | 
						|
  Assert.ok(feature.getVariable("mochitest"), "Got back the expected feature");
 | 
						|
 | 
						|
  sandbox.restore();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function remote_isEarlyStartup_config() {
 | 
						|
  let { manager } = await setupForExperimentFeature();
 | 
						|
  let rollout = ExperimentFakes.rollout("password-autocomplete", {
 | 
						|
    branch: {
 | 
						|
      slug: "remote-config-isEarlyStartup",
 | 
						|
      features: [
 | 
						|
        {
 | 
						|
          featureId: "password-autocomplete",
 | 
						|
          enabled: true,
 | 
						|
          value: { remote: true },
 | 
						|
          isEarlyStartup: true,
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    },
 | 
						|
  });
 | 
						|
 | 
						|
  await manager.onStartup();
 | 
						|
  await manager.store.addEnrollment(rollout);
 | 
						|
 | 
						|
  Assert.ok(
 | 
						|
    Services.prefs.prefHasUserValue(
 | 
						|
      "nimbus.syncdefaultsstore.password-autocomplete"
 | 
						|
    ),
 | 
						|
    "Configuration is marked early startup"
 | 
						|
  );
 | 
						|
 | 
						|
  Services.prefs.clearUserPref(
 | 
						|
    "nimbus.syncdefaultsstore.password-autocomplete"
 | 
						|
  );
 | 
						|
});
 |