Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
import { IPPAuthProvider } from "moz-src:///toolkit/components/ipprotection/IPPAuthProvider.sys.mjs";
const BANDWIDTH_USAGE_ENABLED_PREF = "browser.ipProtection.bandwidth.enabled";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
IPProtectionService:
"moz-src:///toolkit/components/ipprotection/IPProtectionService.sys.mjs",
});
/**
* Test-only IPPAuthProvider implementation.
*
* State that mirrors a real auth provider (signed-in status, current
* entitlement, in-progress enrollment) is stored in private fields and
* manipulated via `simulateSignIn`, `setEntitlement`, `resetEntitlement`
* and `enroll`.
*
* Responses for the outbound/network-shaped methods (`enrollAndEntitle`,
* `getEntitlement`, `fetchProxyPass`, `fetchProxyUsage`) can be overridden
* via the matching `set*Response` setters so tests can drive them without
* wrapping the dummy in sinon.
*/
class IPPDummyAuthProviderSingleton extends IPPAuthProvider {
#entitlement = null;
#isEnrolling = false;
#signedIn = true;
#enrollResponse = null;
#getEntitlementResponse = null;
#proxyPassResponse = null;
#proxyPassError = null;
#proxyUsageResponse = null;
get helpers() {
return [this];
}
#setEntitlement(entitlement) {
this.#entitlement = entitlement;
Services.prefs.setBoolPref(
BANDWIDTH_USAGE_ENABLED_PREF,
entitlement?.limitedBandwidth ?? true
);
}
init() {}
initOnStartupCompleted() {
if (!this.#signedIn) {
return;
}
this.updateEntitlement();
}
uninit() {
this.#entitlement = null;
this.#signedIn = true;
this.#isEnrolling = false;
// Test-configuration responses (#enrollResponse, #getEntitlementResponse,
// #proxyPass*Response, #proxyPassError) are deliberately *not* cleared:
// they are seeded once by setupStubs at suite setup, and tests rely on
// them surviving init/uninit cycles triggered by pref toggles.
}
get isReady() {
return this.#signedIn && !!this.entitlement;
}
get hasUpgraded() {
return this.entitlement?.subscribed ?? false;
}
get isEnrolling() {
return this.#isEnrolling;
}
get maxBytes() {
return this.entitlement?.maxBytes ?? null;
}
get entitlement() {
return this.#entitlement;
}
/**
* Test API: set the dummy's sign-in state. Clears the entitlement on
* sign-out (mirroring the real provider's behavior on a sign-out
* transition). Does *not* call IPProtectionService.updateState() — tests
* that change sign-in state after init should call updateState themselves.
*
* @param {boolean} value
*/
simulateSignIn(value) {
this.#signedIn = value;
if (!value) {
this.#setEntitlement(null);
}
}
async enroll() {
this.#isEnrolling = true;
this.dispatchEvent(new CustomEvent("IPPAuthProvider:StateChanged"));
try {
const response = await this.enrollAndEntitle();
if (response?.isEnrolledAndEntitled && response.entitlement) {
this.#setEntitlement(response.entitlement);
}
return {
isEnrolledAndEntitled: !!response?.isEnrolledAndEntitled,
error: response?.error ?? null,
};
} finally {
this.#isEnrolling = false;
lazy.IPProtectionService.updateState();
this.dispatchEvent(new CustomEvent("IPPAuthProvider:StateChanged"));
}
}
async enrollAndEntitle() {
return this.#enrollResponse;
}
async aboutToStart() {
return null;
}
async checkForUpgrade() {}
async getEntitlement() {
return this.#getEntitlementResponse;
}
async fetchProxyPass() {
if (this.#proxyPassError) {
throw this.#proxyPassError;
}
return (
this.#proxyPassResponse ?? {
status: 200,
error: undefined,
pass: null,
usage: null,
}
);
}
async fetchProxyUsage() {
return this.#proxyUsageResponse;
}
/**
* Test API: override what enrollAndEntitle() resolves to.
*
* @param {object|null} response - {isEnrolledAndEntitled, entitlement, error}
* or null to make enrollAndEntitle() resolve to null.
*/
setEnrollResponse(response) {
this.#enrollResponse = response;
}
/**
* Test API: override what getEntitlement() resolves to.
*
* @param {object|null} response - {entitlement, error?} or null to make
* getEntitlement() resolve to null.
*/
setGetEntitlementResponse(response) {
this.#getEntitlementResponse = response;
}
/**
* Test API: override what fetchProxyPass() resolves to.
*
* @param {object|null} response
*/
setProxyPass(response) {
this.#proxyPassResponse = response;
}
/**
* Test API: make fetchProxyPass() throw the given error. Pass `null` to
* restore the normal response path.
*
* @param {Error|null} error
*/
setProxyPassError(error) {
this.#proxyPassError = error;
}
/**
* Test API: override what fetchProxyUsage() resolves to.
*
* @param {object|null} response
*/
setProxyUsage(response) {
this.#proxyUsageResponse = response;
}
async updateEntitlement() {
const response = await this.getEntitlement();
if (response?.entitlement) {
this.#setEntitlement(response.entitlement);
}
lazy.IPProtectionService.updateState();
this.dispatchEvent(new CustomEvent("IPPAuthProvider:StateChanged"));
return { isEntitled: !!this.#entitlement, error: null };
}
resetEntitlement() {
this.#setEntitlement(null);
lazy.IPProtectionService.updateState();
this.dispatchEvent(new CustomEvent("IPPAuthProvider:StateChanged"));
}
/**
* Test API: set the current entitlement.
*
* By default this mirrors the real provider's runtime behaviour and
* notifies the service (so any state transition propagates to listeners).
* Pass `{ silent: true }` during test setup, when you only want to seed
* the initial value without triggering a StateChanged event the test
* isn't ready to observe yet.
*
* @param {object|null} entitlement
* @param {object} [options]
* @param {boolean} [options.silent=false]
*/
setEntitlement(entitlement, { silent = false } = {}) {
this.#setEntitlement(entitlement);
if (silent) {
return;
}
lazy.IPProtectionService.updateState();
this.dispatchEvent(new CustomEvent("IPPAuthProvider:StateChanged"));
}
get excludedUrlPrefs() {
return [];
}
}
const IPPDummyAuthProvider = new IPPDummyAuthProviderSingleton();
export { IPPDummyAuthProvider, IPPDummyAuthProviderSingleton };