diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 323c3b3d..00000000 --- a/.eslintrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "standard", - "rules": { - "indent": "off", - "import/order": "error", - "comma-dangle": "off", - "space-before-function-paren": "off" - }, - "env": { - "webextensions": true, - "browser": true, - "jasmine": true - } -} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..3a05e18f --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + root: true, + globals: { + it: true, + Proxy: true, + }, + env: { + browser: true, + webextensions: true, + node: true, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/triple-slash-reference': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, +} diff --git a/build/app/public/js/base.js b/build/app/public/js/base.js index cc2ea545..d42e959a 100644 --- a/build/app/public/js/base.js +++ b/build/app/public/js/base.js @@ -6,7 +6,6 @@ var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; - var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; @@ -34,10 +33,6 @@ mod2 )); var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2); - var __publicField = (obj, key, value) => { - __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); - return value; - }; // node_modules/jquery/dist/jquery.js var require_jquery = __commonJS({ @@ -3578,8 +3573,8 @@ return elem; } jQuery.extend({ - htmlPrefilter: function(html18) { - return html18; + htmlPrefilter: function(html19) { + return html19; }, clone: function(elem, dataAndEvents, deepDataAndEvents) { var i, l, srcElements, destElements, clone = elem.cloneNode(true), inPage = isAttached(elem); @@ -4143,18 +4138,18 @@ margin: "", padding: "", border: "Width" - }, function(prefix, suffix) { - jQuery.cssHooks[prefix + suffix] = { + }, function(prefix2, suffix) { + jQuery.cssHooks[prefix2 + suffix] = { expand: function(value) { var i = 0, expanded = {}, parts = typeof value === "string" ? value.split(" ") : [value]; for (; i < 4; i++) { - expanded[prefix + cssExpand[i] + suffix] = parts[i] || parts[i - 2] || parts[0]; + expanded[prefix2 + cssExpand[i] + suffix] = parts[i] || parts[i - 2] || parts[0]; } return expanded; } }; - if (prefix !== "margin") { - jQuery.cssHooks[prefix + suffix].set = setPositiveNumber; + if (prefix2 !== "margin") { + jQuery.cssHooks[prefix2 + suffix].set = setPositiveNumber; } }); jQuery.fn.extend({ @@ -5287,7 +5282,7 @@ }; }); } - var location = window2.location; + var location2 = window2.location; var nonce = { guid: Date.now() }; var rquery = /\?/; jQuery.parseXML = function(data) { @@ -5308,15 +5303,15 @@ return xml; }; var rbracket = /\[\]$/, rCRLF = /\r?\n/g, rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i; - function buildParams(prefix, obj, traditional, add2) { + function buildParams(prefix2, obj, traditional, add2) { var name; if (Array.isArray(obj)) { jQuery.each(obj, function(i, v) { - if (traditional || rbracket.test(prefix)) { - add2(prefix, v); + if (traditional || rbracket.test(prefix2)) { + add2(prefix2, v); } else { buildParams( - prefix + "[" + (typeof v === "object" && v != null ? i : "") + "]", + prefix2 + "[" + (typeof v === "object" && v != null ? i : "") + "]", v, traditional, add2 @@ -5325,14 +5320,14 @@ }); } else if (!traditional && toType(obj) === "object") { for (name in obj) { - buildParams(prefix + "[" + name + "]", obj[name], traditional, add2); + buildParams(prefix2 + "[" + name + "]", obj[name], traditional, add2); } } else { - add2(prefix, obj); + add2(prefix2, obj); } } jQuery.param = function(a, traditional) { - var prefix, s = [], add2 = function(key, valueOrFunction) { + var prefix2, s = [], add2 = function(key, valueOrFunction) { var value = isFunction(valueOrFunction) ? valueOrFunction() : valueOrFunction; s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value); }; @@ -5344,8 +5339,8 @@ add2(this.name, this.value); }); } else { - for (prefix in a) { - buildParams(prefix, a[prefix], traditional, add2); + for (prefix2 in a) { + buildParams(prefix2, a[prefix2], traditional, add2); } } return s.join("&"); @@ -5376,7 +5371,7 @@ } }); var r20 = /%20/g, rhash = /#.*$/, rantiCache = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, prefilters = {}, transports = {}, allTypes = "*/".concat("*"), originAnchor = document2.createElement("a"); - originAnchor.href = location.href; + originAnchor.href = location2.href; function addToPrefiltersOrTransports(structure) { return function(dataTypeExpression, func) { if (typeof dataTypeExpression !== "string") { @@ -5529,9 +5524,9 @@ lastModified: {}, etag: {}, ajaxSettings: { - url: location.href, + url: location2.href, type: "GET", - isLocal: rlocalProtocol.test(location.protocol), + isLocal: rlocalProtocol.test(location2.protocol), global: true, processData: true, async: true, @@ -5666,7 +5661,7 @@ } }; deferred.promise(jqXHR); - s.url = ((url || s.url || location.href) + "").replace(rprotocol, location.protocol + "//"); + s.url = ((url || s.url || location2.href) + "").replace(rprotocol, location2.protocol + "//"); s.type = options.method || options.type || s.method || s.type; s.dataTypes = (s.dataType || "*").toLowerCase().match(rnothtmlwhite) || [""]; if (s.crossDomain == null) { @@ -5887,13 +5882,13 @@ }); }; jQuery.fn.extend({ - wrapAll: function(html18) { + wrapAll: function(html19) { var wrap; if (this[0]) { - if (isFunction(html18)) { - html18 = html18.call(this[0]); + if (isFunction(html19)) { + html19 = html19.call(this[0]); } - wrap = jQuery(html18, this[0].ownerDocument).eq(0).clone(true); + wrap = jQuery(html19, this[0].ownerDocument).eq(0).clone(true); if (this[0].parentNode) { wrap.insertBefore(this[0]); } @@ -5907,25 +5902,25 @@ } return this; }, - wrapInner: function(html18) { - if (isFunction(html18)) { + wrapInner: function(html19) { + if (isFunction(html19)) { return this.each(function(i) { - jQuery(this).wrapInner(html18.call(this, i)); + jQuery(this).wrapInner(html19.call(this, i)); }); } return this.each(function() { var self = jQuery(this), contents = self.contents(); if (contents.length) { - contents.wrapAll(html18); + contents.wrapAll(html19); } else { - self.append(html18); + self.append(html19); } }); }, - wrap: function(html18) { - var htmlIsFunction = isFunction(html18); + wrap: function(html19) { + var htmlIsFunction = isFunction(html19); return this.each(function(i) { - jQuery(this).wrapAll(htmlIsFunction ? html18.call(this, i) : html18); + jQuery(this).wrapAll(htmlIsFunction ? html19.call(this, i) : html19); }); }, unwrap: function(selector) { @@ -7343,7 +7338,7 @@ }); // shared/js/ui/base/page.js - function BasePage(ops) { + function BasePage(_ops) { this.views = {}; this.store = store_exports; this.ready(); @@ -7366,15 +7361,9 @@ // shared/js/ui/environment-check.js function isEnvironment(platform2) { - if (platform2 === window.environmentOverride) { - return true; - } return document.body.classList.contains(`environment--${platform2}`); } function currentPlatform() { - const windowVar = window.environmentOverride; - if (windowVar && isValidPlatform(windowVar)) - return windowVar; const matchingClass = [...document.body.classList].find((x) => x.startsWith("environment--")); if (matchingClass) { const platform2 = matchingClass.slice(13); @@ -7387,7 +7376,7 @@ function isValidPlatform(name) { if (!name) throw new Error(`not a valid platform name ${name}`); - const names = ["ios", "android", "macos", "browser", "windows", "example"]; + const names = ["ios", "android", "macos", "browser", "windows"]; if (names.includes(name)) { return true; } @@ -7872,7 +7861,7 @@ }; }; EMPTY_PATH = []; - ParseStatus = class { + ParseStatus = class _ParseStatus { constructor() { this.value = "valid"; } @@ -7903,7 +7892,7 @@ value: await pair.value }); } - return ParseStatus.mergeObjectSync(status, syncPairs); + return _ParseStatus.mergeObjectSync(status, syncPairs); } static mergeObjectSync(status, pairs) { const finalObject = {}; @@ -8182,7 +8171,7 @@ cuidRegex = /^c[^\s-]{8,}$/i; uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i; emailRegex = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; - ZodString = class extends ZodType { + ZodString = class _ZodString extends ZodType { constructor() { super(...arguments); this._regex = (regex, validation, message) => this.refinement((data) => regex.test(data), { @@ -8191,7 +8180,7 @@ ...errorUtil.errToObj(message) }); this.nonempty = (message) => this.min(1, errorUtil.errToObj(message)); - this.trim = () => new ZodString({ + this.trim = () => new _ZodString({ ...this._def, checks: [...this._def.checks, { kind: "trim" }] }); @@ -8321,7 +8310,7 @@ return { status: status.value, value: input.data }; } _addCheck(check) { - return new ZodString({ + return new _ZodString({ ...this._def, checks: [...this._def.checks, check] }); @@ -8416,7 +8405,7 @@ ...processCreateParams(params) }); }; - ZodNumber = class extends ZodType { + ZodNumber = class _ZodNumber extends ZodType { constructor() { super(...arguments); this.min = this.gte; @@ -8503,7 +8492,7 @@ return this.setLimit("max", value, false, errorUtil.toString(message)); } setLimit(kind, value, inclusive, message) { - return new ZodNumber({ + return new _ZodNumber({ ...this._def, checks: [ ...this._def.checks, @@ -8517,7 +8506,7 @@ }); } _addCheck(check) { - return new ZodNumber({ + return new _ZodNumber({ ...this._def, checks: [...this._def.checks, check] }); @@ -8640,7 +8629,7 @@ ...processCreateParams(params) }); }; - ZodDate = class extends ZodType { + ZodDate = class _ZodDate extends ZodType { _parse(input) { const parsedType = this._getType(input); if (parsedType !== ZodParsedType.date) { @@ -8696,7 +8685,7 @@ }; } _addCheck(check) { - return new ZodDate({ + return new _ZodDate({ ...this._def, checks: [...this._def.checks, check] }); @@ -8853,7 +8842,7 @@ ...processCreateParams(params) }); }; - ZodArray = class extends ZodType { + ZodArray = class _ZodArray extends ZodType { _parse(input) { const { ctx, status } = this._processInputParams(input); const def = this._def; @@ -8905,13 +8894,13 @@ return this._def.type; } min(minLength, message) { - return new ZodArray({ + return new _ZodArray({ ...this._def, minLength: { value: minLength, message: errorUtil.toString(message) } }); } max(maxLength, message) { - return new ZodArray({ + return new _ZodArray({ ...this._def, maxLength: { value: maxLength, message: errorUtil.toString(message) } }); @@ -8949,7 +8938,7 @@ }) }); }; - ZodObject = class extends ZodType { + ZodObject = class _ZodObject extends ZodType { constructor() { super(...arguments); this._cached = null; @@ -9055,7 +9044,7 @@ } strict(message) { errorUtil.errToObj; - return new ZodObject({ + return new _ZodObject({ ...this._def, unknownKeys: "strict", ...message !== void 0 ? { @@ -9074,13 +9063,13 @@ }); } strip() { - return new ZodObject({ + return new _ZodObject({ ...this._def, unknownKeys: "strip" }); } passthrough() { - return new ZodObject({ + return new _ZodObject({ ...this._def, unknownKeys: "passthrough" }); @@ -9094,7 +9083,7 @@ * upgrade if you are experiencing issues. */ merge(merging) { - const merged = new ZodObject({ + const merged = new _ZodObject({ unknownKeys: merging._def.unknownKeys, catchall: merging._def.catchall, shape: () => objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), @@ -9103,7 +9092,7 @@ return merged; } catchall(index) { - return new ZodObject({ + return new _ZodObject({ ...this._def, catchall: index }); @@ -9114,7 +9103,7 @@ if (this.shape[key]) shape[key] = this.shape[key]; }); - return new ZodObject({ + return new _ZodObject({ ...this._def, shape: () => shape }); @@ -9126,7 +9115,7 @@ shape[key] = this.shape[key]; } }); - return new ZodObject({ + return new _ZodObject({ ...this._def, shape: () => shape }); @@ -9144,7 +9133,7 @@ newShape[key] = this.shape[key].optional(); } }); - return new ZodObject({ + return new _ZodObject({ ...this._def, shape: () => newShape }); @@ -9154,7 +9143,7 @@ newShape[key] = fieldSchema.optional(); } } - return new ZodObject({ + return new _ZodObject({ ...this._def, shape: () => newShape }); @@ -9169,7 +9158,7 @@ } newShape[key] = newField; } - return new ZodObject({ + return new _ZodObject({ ...this._def, shape: () => newShape }); @@ -9296,7 +9285,7 @@ ...processCreateParams(params) }); }; - ZodDiscriminatedUnion = class extends ZodType { + ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType { _parse(input) { const { ctx } = this._processInputParams(input); if (ctx.parsedType !== ZodParsedType.object) { @@ -9362,7 +9351,7 @@ if (options.size !== types.length) { throw new Error("Some of the discriminator values are not unique"); } - return new ZodDiscriminatedUnion({ + return new _ZodDiscriminatedUnion({ typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, discriminator, options, @@ -9423,7 +9412,7 @@ ...processCreateParams(params) }); }; - ZodTuple = class extends ZodType { + ZodTuple = class _ZodTuple extends ZodType { _parse(input) { const { status, ctx } = this._processInputParams(input); if (ctx.parsedType !== ZodParsedType.array) { @@ -9471,7 +9460,7 @@ return this._def.items; } rest(rest) { - return new ZodTuple({ + return new _ZodTuple({ ...this._def, rest }); @@ -9488,7 +9477,7 @@ ...processCreateParams(params) }); }; - ZodRecord = class extends ZodType { + ZodRecord = class _ZodRecord extends ZodType { get keySchema() { return this._def.keyType; } @@ -9525,14 +9514,14 @@ } static create(first, second, third) { if (second instanceof ZodType) { - return new ZodRecord({ + return new _ZodRecord({ keyType: first, valueType: second, typeName: ZodFirstPartyTypeKind.ZodRecord, ...processCreateParams(third) }); } - return new ZodRecord({ + return new _ZodRecord({ keyType: ZodString.create(), valueType: first, typeName: ZodFirstPartyTypeKind.ZodRecord, @@ -9600,7 +9589,7 @@ ...processCreateParams(params) }); }; - ZodSet = class extends ZodType { + ZodSet = class _ZodSet extends ZodType { _parse(input) { const { status, ctx } = this._processInputParams(input); if (ctx.parsedType !== ZodParsedType.set) { @@ -9656,13 +9645,13 @@ } } min(minSize, message) { - return new ZodSet({ + return new _ZodSet({ ...this._def, minSize: { value: minSize, message: errorUtil.toString(message) } }); } max(maxSize, message) { - return new ZodSet({ + return new _ZodSet({ ...this._def, maxSize: { value: maxSize, message: errorUtil.toString(message) } }); @@ -9683,7 +9672,7 @@ ...processCreateParams(params) }); }; - ZodFunction = class extends ZodType { + ZodFunction = class _ZodFunction extends ZodType { constructor() { super(...arguments); this.validate = this.implement; @@ -9768,13 +9757,13 @@ return this._def.returns; } args(...items) { - return new ZodFunction({ + return new _ZodFunction({ ...this._def, args: ZodTuple.create(items).rest(ZodUnknown.create()) }); } returns(returnType) { - return new ZodFunction({ + return new _ZodFunction({ ...this._def, returns: returnType }); @@ -9788,7 +9777,7 @@ return validatedFunc; } static create(args, returns, params) { - return new ZodFunction({ + return new _ZodFunction({ args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()), returns: returns || ZodUnknown.create(), typeName: ZodFirstPartyTypeKind.ZodFunction, @@ -10705,7 +10694,7 @@ var init_protections = __esm({ "shared/js/browser/utils/protections.mjs"() { "use strict"; - Protections = class { + Protections = class _Protections { /** * @param {boolean} unprotectedTemporary * @param {string[]} enabledFeatures @@ -10719,7 +10708,7 @@ this.denylisted = denylisted; } static default() { - return new Protections(false, ["contentBlocking"], false, false); + return new _Protections(false, ["contentBlocking"], false, false); } }; } @@ -10779,14 +10768,12 @@ }; }; AggregatedCompanyResponseData = class { - constructor() { - /** @type {number} */ - __publicField(this, "entitiesCount", 0); - /** @type {number} */ - __publicField(this, "requestCount", 0); - /** @type {Record} */ - __publicField(this, "entities", {}); - } + /** @type {number} */ + entitiesCount = 0; + /** @type {number} */ + requestCount = 0; + /** @type {Record} */ + entities = {}; /** * @param {import('../../../../schema/__generated__/schema.types.js').DetectedRequest} request */ @@ -10851,20 +10838,20 @@ protectionsOff_allowedTrackers_allowedNonTrackers: "protectionsOff_allowedTrackers_allowedNonTrackers" }; RequestDetails = class { + surrogates; + all = new AggregatedCompanyResponseData(); + blocked = new AggregatedCompanyResponseData(); + allowed = { + adClickAttribution: new AggregatedCompanyResponseData(), + ownedByFirstParty: new AggregatedCompanyResponseData(), + ruleException: new AggregatedCompanyResponseData(), + protectionDisabled: new AggregatedCompanyResponseData(), + otherThirdPartyRequest: new AggregatedCompanyResponseData() + }; /** * @param {string[]} surrogates - any installed surrogates, just the domains */ constructor(surrogates) { - __publicField(this, "surrogates"); - __publicField(this, "all", new AggregatedCompanyResponseData()); - __publicField(this, "blocked", new AggregatedCompanyResponseData()); - __publicField(this, "allowed", { - adClickAttribution: new AggregatedCompanyResponseData(), - ownedByFirstParty: new AggregatedCompanyResponseData(), - ruleException: new AggregatedCompanyResponseData(), - protectionDisabled: new AggregatedCompanyResponseData(), - otherThirdPartyRequest: new AggregatedCompanyResponseData() - }); this.surrogates = surrogates; } /** @@ -11172,6 +11159,26 @@ } }); + // node_modules/tiny-invariant/dist/esm/tiny-invariant.js + function invariant(condition, message) { + if (condition) { + return; + } + if (isProduction) { + throw new Error(prefix); + } + var provided = typeof message === "function" ? message() : message; + var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix; + throw new Error(value); + } + var isProduction, prefix; + var init_tiny_invariant = __esm({ + "node_modules/tiny-invariant/dist/esm/tiny-invariant.js"() { + isProduction = false; + prefix = "Invariant failed"; + } + }); + // node_modules/@material/ripple/util.js function supportsCssVariables(windowObj, forceRefresh) { if (forceRefresh === void 0) { @@ -12694,6 +12701,7 @@ continue; } const isProtected = value === false; + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardSetProtection.postMessage(isProtected); } } @@ -12704,6 +12712,7 @@ return; } if (message instanceof UpdatePermissionMessage) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardSetPermission.postMessage({ permission: message.id, value: message.value @@ -12711,16 +12720,19 @@ } } function privacyDashboardOpenUrlInNewTab(args) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardOpenUrlInNewTab.postMessage({ url: args.url }); } function privacyDashboardOpenSettings(args) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardOpenSettings.postMessage({ target: args.target }); } function privacyDashboardSubmitBrokenSiteReport(report) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardSubmitBrokenSiteReport.postMessage({ category: report.category, description: report.description @@ -12728,6 +12740,7 @@ } function privacyDashboardSetSize(payload) { if (!isIOS()) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardSetSize.postMessage(payload); } } @@ -12781,6 +12794,7 @@ var init_macos_communication = __esm({ "shared/js/browser/macos-communication.js"() { "use strict"; + init_tiny_invariant(); init_schema_parsers(); init_environment_check(); init_utils(); @@ -12849,9 +12863,11 @@ setupShared(); } function privacyDashboardClose(args) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardClose.postMessage(args); } function privacyDashboardShowReportBrokenSite(args) { + invariant(window.webkit?.messageHandlers, "webkit.messageHandlers required"); window.webkit.messageHandlers.privacyDashboardShowReportBrokenSite.postMessage(args); } async function fetch3(message) { @@ -12868,6 +12884,7 @@ var init_ios_communication = __esm({ "shared/js/browser/ios-communication.js"() { "use strict"; + init_tiny_invariant(); init_common(); init_macos_communication(); } @@ -13303,1240 +13320,15 @@ } }); - // schema/__fixtures__/request-data-google.json - var request_data_google_default; - var init_request_data_google = __esm({ - "schema/__fixtures__/request-data-google.json"() { - request_data_google_default = { - requests: [ - { - category: "Advertising", - url: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_160x56dp.png", - pageUrl: "https://www.google.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ownedByFirstParty" - } - }, - prevalence: 80.1 - }, - { - category: "Advertising", - url: "https://apis.google.com/_/scs/abc-static/_/js/k=gapi.gapi.en.t9z7VPsEMFg.O/m=gapi_iframes,googleapis_client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo8oD_5FQW3kT3ksWwmXIWvhhqbKdw/cb=gapi.loaded_0", - pageUrl: "https://www.google.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ownedByFirstParty" - } - }, - prevalence: 80.1 - }, - { - category: "Content Delivery", - url: "https://fonts.gstatic.com/s/i/productlogos/googleg/v6/24px.svg", - pageUrl: "https://www.google.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ownedByFirstParty" - } - }, - prevalence: 80.1 - }, - { - category: "Content Delivery", - url: "https://www.gstatic.com/og/_/js/k=og.qtm.en_US.asUsweLQqwk.O/rt=j/m=qabr,q_dnp,qcwid,qapid/exm=qaaw,qadd,qaid,qein,qhaw,qhbr,qhch,qhga,qhid,qhin,qhpr/d=1/ed=1/rs=AA2YrTvH37iHjvnJ7NPFbMaGY1OZ0tqdnw", - pageUrl: "https://www.google.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ownedByFirstParty" - } - }, - prevalence: 80.1 - } - ] - }; - } - }); - - // schema/__fixtures__/request-data-cnn.json - var request_data_cnn_default; - var init_request_data_cnn = __esm({ - "schema/__fixtures__/request-data-cnn.json"() { - request_data_cnn_default = { - installedSurrogates: ["widgets.outbrain.com", "www.googletagservices.com", "sb.scorecardresearch.com"], - requests: [ - { - category: "Advertising", - url: "https://cdn.krxd.net/", - eTLDplus1: "krxd.net", - pageUrl: "https://edition.cnn.com/", - ownerName: "Salesforce.com, Inc.", - entityName: "Salesforce.com", - state: { - blocked: {} - }, - prevalence: 9.23 - }, - { - category: "Advertising", - url: "https://www.google.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ruleException" - } - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://vrt.outbrain.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Outbrain", - entityName: "Outbrain", - state: { - blocked: {} - }, - prevalence: 12.4 - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "ownedByFirstParty" - } - }, - prevalence: 21.6, - url: "https://www.ugdturner.com/", - ownerName: "WarnerMedia, LLC", - entityName: "WarnerMedia" - }, - { - category: "Advertising", - url: "https://js-sec.indexww.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Index Exchange, Inc.", - entityName: "Index Exchange", - state: { - blocked: {} - }, - prevalence: 17.3 - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - blocked: {} - }, - prevalence: 0.854, - url: "https://consent.truste.com/", - ownerName: "TrustArc Inc.", - entityName: "TrustArc" - }, - { - category: "Advertising", - url: "https://as.casalemedia.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Index Exchange, Inc.", - entityName: "Index Exchange", - state: { - blocked: {} - }, - prevalence: 17.3 - }, - { - category: "Advertising", - url: "https://c.amazon-adsystem.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Amazon Technologies, Inc.", - entityName: "Amazon.com", - state: { - blocked: {} - }, - prevalence: 21.4 - }, - { - category: "Advertising", - url: "https://as-sec.casalemedia.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Index Exchange, Inc.", - entityName: "Index Exchange", - state: { - blocked: {} - }, - prevalence: 17.3 - }, - { - category: "Advertising", - url: "https://ads.rubiconproject.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Magnite, Inc.", - entityName: "Magnite", - state: { - blocked: {} - }, - prevalence: 18.3 - }, - { - category: "Advertising", - url: "https://aax.amazon-adsystem.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Amazon Technologies, Inc.", - entityName: "Amazon.com", - state: { - blocked: {} - }, - prevalence: 21.4 - }, - { - category: "Advertising", - url: "https://dsum-sec.casalemedia.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Index Exchange, Inc.", - entityName: "Index Exchange", - state: { - blocked: {} - }, - prevalence: 17.3 - }, - { - category: "Advertising", - url: "https://plus.google.com/+cnn/posts", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - allowed: { - reason: "ruleException" - } - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://tpc.googlesyndication.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - blocked: {} - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://fastlane.rubiconproject.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Magnite, Inc.", - entityName: "Magnite", - state: { - blocked: {} - }, - prevalence: 18.3 - }, - { - category: "Advertising", - url: "https://partner.googleadservices.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - blocked: {} - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://pagead2.googlesyndication.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - blocked: {} - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://amplify.outbrain.com/cp/obtp.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Outbrain", - entityName: "Outbrain", - state: { - blocked: {} - }, - prevalence: 12.4 - }, - { - category: "Advertising", - url: "https://tag.bounceexchange.com/340/i.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Bounce Exchange", - entityName: "Bounce Exchange", - state: { - blocked: {} - }, - prevalence: 0.582 - }, - { - category: "Advertising", - url: "https://widgets.outbrain.com/outbrain.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Outbrain", - entityName: "Outbrain", - state: { - blocked: {} - }, - prevalence: 12.4 - }, - { - category: "Advertising", - url: "https://fastlane-adv.rubiconproject.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Magnite, Inc.", - entityName: "Magnite", - state: { - blocked: {} - }, - prevalence: 18.3 - }, - { - category: "Advertising", - url: "https://optimized-by.rubiconproject.com/", - pageUrl: "https://edition.cnn.com/", - ownerName: "Magnite, Inc.", - entityName: "Magnite", - state: { - blocked: {} - }, - prevalence: 18.3 - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "ruleException" - } - }, - prevalence: 0.0468, - url: "https://www.dianomi.com/js/contextfeed.js", - ownerName: "Dianomi Ltd", - entityName: "Dianomi" - }, - { - category: "Analytics", - url: "https://sb.scorecardresearch.com/beacon.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "comScore, Inc", - entityName: "comScore", - state: { - blocked: {} - }, - prevalence: 9.99 - }, - { - category: "Advertising", - url: "https://c.amazon-adsystem.com/aax2/apstag.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Amazon Technologies, Inc.", - entityName: "Amazon.com", - state: { - allowed: { - reason: "ruleException" - } - }, - prevalence: 21.4 - }, - { - category: "Advertising", - url: "https://www.googletagservices.com/tag/js/gpt.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Google LLC", - entityName: "Google", - state: { - blocked: {} - }, - prevalence: 79.9 - }, - { - category: "Advertising", - url: "https://get.s-onetag.com/c15ddde9-ec7d-4a49-b8ca-7a21bc4b943b/tag.min.js", - pageUrl: "https://edition.cnn.com/", - ownerName: "Sovrn Holdings", - entityName: "Sovrn Holdings", - state: { - blocked: {} - }, - prevalence: 10.5 - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://data.api.cnn.io/", - entityName: "WarnerMedia" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://pmd.cdn.turner.com/", - entityName: "WarnerMedia" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://amd.cdn.turner.com/", - entityName: "WarnerMedia" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://registry.api.cnn.io/bundles/fave/latest-4.x/js", - entityName: "WarnerMedia" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - url: "android-app://com.cnn.mobile.android.phone/http/edition.cnn.com", - entityName: "android.phone" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - url: "https://cdn.cookielaw.org/scripttemplates/otSDKStub.js", - entityName: "cookielaw.org" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 9.99, - url: "https://segment-data-us-east.zqtk.net/turner-47fcf6", - entityName: "comScore" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.4, - url: "https://d2uap9jskdzp2.cloudfront.net/script.js", - entityName: "Amazon.com" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://ht.cdn.turner.com/", - entityName: "WarnerMedia" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - url: "https://w.usabilla.com/", - entityName: "usabilla.com" - }, - { - pageUrl: "https://edition.cnn.com/", - state: { - allowed: { - reason: "otherThirdPartyRequest" - } - }, - prevalence: 21.6, - url: "https://s.cdn.turner.com/analytics/comscore/streamsense.5.2.0.160629.min.js", - entityName: "WarnerMedia" - } - ] - }; - } - }); - - // shared/js/ui/views/tests/toggle-protections.mjs - function protectionsOff(requests) { - return requests.map((r) => { - if ("blocked" in r.state) { - return detectedRequestSchema.parse({ - ...r, - state: { allowed: { reason: "protectionDisabled" } } - }); - } - if ("allowed" in r.state) { - if (r.state.allowed.reason === "otherThirdPartyRequest") { - return r; - } - return detectedRequestSchema.parse({ - ...r, - state: { allowed: { reason: "protectionDisabled" } } - }); - } - return r; - }); - } - var init_toggle_protections = __esm({ - "shared/js/ui/views/tests/toggle-protections.mjs"() { - "use strict"; - init_schema_parsers(); - } - }); - - // shared/js/ui/views/tests/generate-data.mjs - function mockBurnOptions({ clearHistory, tabClearEnabled, pinnedTabs }) { - return { - options: [ - { - name: "CurrentSite", - options: { - origins: ["https://example.com/"] - }, - descriptionStats: { - clearHistory, - site: "example.com", - duration: "all", - openTabs: tabClearEnabled ? 1 : 0, - cookies: 1, - pinnedTabs - } - }, - { - name: "LastHour", - options: { - since: Date.now() - }, - descriptionStats: { - clearHistory, - duration: "hour", - openTabs: tabClearEnabled ? 5 : 0, - cookies: 23, - pinnedTabs - } - }, - { - name: "AllTime", - options: {}, - descriptionStats: { - clearHistory, - duration: "all", - openTabs: tabClearEnabled ? 5 : 0, - cookies: 1e3, - pinnedTabs - } - } - ] - }; - } - var allowedTracker, allowedTrackerRule, allowedThirdParty, allowedAdClickAttribution, blocked1, defaultCertificates, MockData, createDataStates; - var init_generate_data = __esm({ - "shared/js/ui/views/tests/generate-data.mjs"() { - "use strict"; - init_protections(); - init_toggle_protections(); - allowedTracker = { - entityName: "example.com", - prevalence: 82.6, - url: "https://example.com/a.js", - pageUrl: "https://example.com", - state: { allowed: { reason: "ownedByFirstParty" } } - }; - allowedTrackerRule = { - entityName: "example.com", - prevalence: 82.6, - url: "https://example.com/a.js", - pageUrl: "https://example.com", - state: { allowed: { reason: "ruleException" } } - }; - allowedThirdParty = { - entityName: "Index Exchange", - prevalence: 12.7, - url: "indexww.com", - pageUrl: "https://example.com", - category: "Advertising", - state: { allowed: { reason: "otherThirdPartyRequest" } } - }; - allowedAdClickAttribution = { - entityName: "Index Exchange", - prevalence: 12.7, - url: "https://bat.bing.com/1.js", - pageUrl: "https://example.com", - category: "Advertising", - state: { allowed: { reason: "adClickAttribution" } } - }; - blocked1 = { - entityName: "Google", - prevalence: 82.6, - url: "securepubads.g.doubleclick.net", - pageUrl: "https://example.com", - category: "Advertising", - state: { blocked: {} } - }; - defaultCertificates = [ - { - commonName: "sni.cloudflaressl.com", - publicKey: { - blockSize: 72, - canEncrypt: true, - bitSize: 256, - canSign: false, - canDerive: true, - canUnwrap: false, - canWrap: false, - canDecrypt: false, - effectiveSize: 256, - isPermanent: false, - type: "Elliptic Curve", - externalRepresentation: "BEO3YVjG8jpNVRlh9G10paEfrx9XnVG9GvNtOAYkZvuytfhKTZ9sW+MhQaFDAgKveZUDIMg7WvG8QXZGPNTWCKg=", - canVerify: true, - keyId: "Xbo6o2j/lA8zNZ/axcChz8ID2MM=" - }, - emails: [], - summary: "sni.cloudflaressl.com" - }, - { - commonName: "Cloudflare Inc ECC CA-3", - publicKey: { - blockSize: 72, - canEncrypt: true, - bitSize: 256, - canSign: false, - canDerive: true, - canUnwrap: false, - canWrap: false, - canDecrypt: false, - effectiveSize: 256, - isPermanent: false, - type: "Elliptic Curve", - externalRepresentation: "BLmtTWaZFAtG7B+B0SpQHp0DFS80En0tlriIOJuFX4+/u03vYUbEyXPUJE/g7hzObLNRcS9q7kwFCXfTcmKkm9c=", - canVerify: true, - keyId: "pc436uuwdQ6UZ4i0RfrZJBCHlh8=" - }, - emails: [], - summary: "Cloudflare Inc ECC CA-3" - }, - { - commonName: "Baltimore CyberTrust Root", - publicKey: { - blockSize: 256, - canEncrypt: false, - bitSize: 2048, - canSign: false, - canDerive: false, - canUnwrap: false, - canWrap: false, - canDecrypt: false, - effectiveSize: 2048, - isPermanent: false, - type: "RSA", - externalRepresentation: "MIIBCgKCAQEAowS7IquYPVfoJnKatXnUKeLh6JWAsbDjW44rKZpk36Fd7bAJBW3bKC7OYqJi/rSI2hLrOOshncBBKwFSe4h30xyPx7q5iLVqCedz6BFAp9HMymKNLeWPC6ZQ0qhQwyjq9aslh4qalhypZ7g/DNX3+VITL8Ib1XBw8I/AEsoGy5rh2cozenfW+Oy58WhEQkgT0sDCpK5eYP62pgX8tN0HWQLUWRiYY/WlY+CQDH1dsgZ684Xq69QDrl6EPl//Fe1pvPk5NnJ1z3dSTfPJkCy5PeXJI1M/HySYIVwHmSm9xjrs526GOmuXdGMzvWgYMfB4jXa//J6OXSqGp02Q3CcaOQIDAQAB", - canVerify: true, - keyId: "5Z1ZMIJHWMys+ghUNoZ7OrUETfA=" - }, - emails: [], - summary: "Baltimore CyberTrust Root" - } - ]; - MockData = class { - /** - * @param {object} params - * @param {string} [params.state] - any string identifier for this mock state - * @param {string} params.url - * @param {{locale: string}} [params.localeSettings] - * @param {DetectedRequest[]} [params.requests] - * @param {any[]} [params.certificate] - * @param {ParentEntity} [params.parentEntity] - * @param {boolean} [params.upgradedHttps] - * @param {boolean} [params.contentBlockingException] - * @param {boolean} [params.allowlisted] - * @param {boolean} [params.denylisted] - * @param {any[]} [params.permissions] - * @param {boolean} [params.specialDomainName] - * @param {boolean} [params.emailUser] - * @param {boolean} [params.fireButtonEnabled] - * @param {BurnConfig} [params.fireButtonOptions] - * @param {import('../../../../../schema/__generated__/schema.types').CookiePromptManagementStatus} [params.cookiePromptManagementStatus] - */ - constructor(params) { - this.url = params.url; - this.requests = params.requests || []; - this.state = params.state; - this.localeSettings = params.localeSettings || { locale: "en" }; - this.certificate = params.certificate || defaultCertificates; - this.upgradedHttps = params.upgradedHttps ?? false; - this.contentBlockingException = params.contentBlockingException; - this.parentEntity = params.parentEntity; - this.permissions = params.permissions; - this.allowlisted = params.allowlisted; - this.denylisted = params.denylisted; - this.specialDomainName = params.specialDomainName; - this.emailUser = params.emailUser; - this.cookiePromptManagementStatus = params.cookiePromptManagementStatus; - this.fireButtonEnabled = params.fireButtonEnabled || false; - if (params.fireButtonOptions) { - this.getBurnOptions = mockBurnOptions(params.fireButtonOptions); - } - this.protections = Protections.default(); - if (this.allowlisted) { - this.protections.allowlisted = true; - } - if (this.denylisted) { - this.protections.denylisted = true; - this.contentBlockingException = true; - } - if (this.contentBlockingException) { - this.protections.enabledFeatures = []; - } - if (this.protections.allowlisted || this.contentBlockingException) { - this.requests = protectionsOff(this.requests); - } - } - /** - * @param {Partial & {url: string}} mock - * @returns {MockData} - */ - static default(mock) { - return new MockData({ - ...mock - }); - } - }; - createDataStates = (google, cnn) => { - return { - "consent-managed": new MockData({ - url: "https://example.com", - requests: [], - cookiePromptManagementStatus: { - consentManaged: true - } - }), - "consent-managed-configurable": new MockData({ - url: "https://example.com", - requests: [], - cookiePromptManagementStatus: { - consentManaged: true, - configurable: true - } - }), - "consent-managed-configurable-cosmetic": new MockData({ - url: "https://example.com", - requests: [], - cookiePromptManagementStatus: { - consentManaged: true, - configurable: true, - cosmetic: true - } - }), - "locale-pl": new MockData({ - localeSettings: { - locale: "pl" - }, - url: "https://example.com", - requests: [] - }), - "locale-fr": new MockData({ - localeSettings: { - locale: "fr" - }, - url: "https://example.com", - requests: [] - }), - "ad-attribution": new MockData({ - url: "https://example.com", - requests: [blocked1, allowedAdClickAttribution], - certificate: [] - }), - "without-certificate": new MockData({ - url: "https://example.com", - requests: [], - certificate: [], - localeSettings: void 0, - parentEntity: void 0, - upgradedHttps: false - }), - insecure: new MockData({ - url: "http://example.com", - requests: [], - certificate: [], - localeSettings: void 0, - parentEntity: void 0 - }), - upgraded: new MockData({ - url: "https://example.com", - upgradedHttps: true, - requests: [], - localeSettings: void 0, - parentEntity: void 0 - }), - google: new MockData({ - requests: google.requests, - url: "https://google.com", - parentEntity: { - displayName: "Google", - prevalence: 80.1 - } - }), - "google-off": new MockData({ - requests: protectionsOff(google.requests), - contentBlockingException: true, - url: "https://google.com", - parentEntity: { - displayName: "Google", - prevalence: 80.1 - } - }), - "google-with-blocked": new MockData({ - requests: google.requests.concat(blocked1), - url: "https://google.com", - parentEntity: { - displayName: "Google", - prevalence: 80.1 - } - }), - "upgraded+secure": new MockData({ - requests: [], - url: "https://example.com", - upgradedHttps: true, - certificate: defaultCertificates - }), - cnn: new MockData({ - url: "https://edition.cnn.com", - requests: cnn.requests, - parentEntity: { - displayName: "WarnerMedia, LLC", - prevalence: 0.401 - } - }), - protectionsOn: new MockData({ - url: "https://example.com", - requests: [] - }), - protectionsOn_blocked: new MockData({ - url: "https://example.com", - requests: [blocked1] - }), - protectionsOn_blocked_allowedTrackers: new MockData({ - url: "https://example.com", - requests: [blocked1, allowedTracker] - }), - protectionsOn_blocked_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [blocked1, allowedThirdParty] - }), - protectionsOn_blocked_allowedTrackers_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [blocked1, allowedThirdParty, allowedTracker] - }), - protectionsOn_allowedTrackers: new MockData({ - url: "https://example.com", - requests: [ - allowedTracker, - allowedTrackerRule, - allowedAdClickAttribution, - { - ...allowedTracker, - state: { allowed: { reason: "protectionDisabled" } } - } - ] - }), - protectionsOn_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [allowedThirdParty] - }), - protectionsOn_allowedTrackers_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [allowedTracker, allowedThirdParty] - }), - protectionsOff: new MockData({ - url: "https://example.com", - requests: [], - contentBlockingException: true - }), - protectionsOff_allowedTrackers: new MockData({ - url: "https://example.com", - requests: [allowedTracker], - contentBlockingException: true - }), - protectionsOff_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [allowedThirdParty], - contentBlockingException: true - }), - protectionsOff_allowedTrackers_allowedNonTrackers: new MockData({ - url: "https://example.com", - requests: [allowedThirdParty, allowedTracker], - contentBlockingException: true - }), - allowlisted: new MockData({ - url: "https://example.com", - requests: [allowedThirdParty, allowedTracker], - allowlisted: true - }), - denylisted: new MockData({ - url: "https://example.com", - requests: [allowedThirdParty, allowedTracker], - denylisted: true - }), - "remote-disabled": new MockData({ - url: "https://example.com", - requests: [allowedThirdParty, allowedTracker], - contentBlockingException: true - }), - "new-entities": new MockData({ - url: "https://m.youtube.com", - requests: [ - { - eTLDplus1: "ytimg.com", - entityName: "Youtube (Google)", - ownerName: "Youtube", - pageUrl: "https://m.youtube.com/", - prevalence: 0.5, - state: { blocked: {} }, - url: "https://i.ytimg.com/vi/AD6OPCFxmJM/hq720_2.jpg?sqp=-oaymwEdCJUDENAFSEbyq4qpAw8IARUAAIhCcAHAAQbQAQE=&rs=AOn4CLBsqqvey-tZ8K3peu7cavrfnR0zDA" - }, - { - category: "Advertising", - eTLDplus1: "doubleclick.net", - entityName: "Google Ads (Google)", - ownerName: "Google Ads", - pageUrl: "https://m.youtube.com/", - prevalence: 0.5, - state: { blocked: {} }, - url: "https://googleads.g.doubleclick.net/pagead/id" - }, - { - category: "Advertising", - eTLDplus1: "doubleclick.net", - entityName: "Google Analytics (Google)", - ownerName: "Google Ads", - pageUrl: "https://m.youtube.com/", - prevalence: 0.5, - state: { blocked: {} }, - url: "https://static.doubleclick.net/instream/ad_status.js" - }, - { - category: "Advertising", - eTLDplus1: "google.com", - entityName: "Instagram (Facebook)", - ownerName: "Google LLC", - pageUrl: "https://m.youtube.com/", - prevalence: 80.5, - state: { blocked: {} }, - url: "https://www.google.com/js/th/EWuoZ_9LU3hL76PT3YFLg_EjKJdTpZ6rgtgTJA98OBY.js" - } - ] - }), - "fire-button": new MockData({ - requests: google.requests, - url: "https://google.com", - parentEntity: { - displayName: "Google", - prevalence: 80.1 - }, - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 2 } - }) - }; - }; - } - }); - - // shared/js/ui/views/tests/states-with-fixtures.js - var testDataStates; - var init_states_with_fixtures = __esm({ - "shared/js/ui/views/tests/states-with-fixtures.js"() { - "use strict"; - init_request_data_google(); - init_request_data_cnn(); - init_generate_data(); - testDataStates = createDataStates(request_data_google_default, request_data_cnn_default); - } - }); - - // shared/js/browser/utils/overrides.js - function getOverrides(searchString) { - const overrides3 = { - requests: [], - tab: createTabData("https://example.com", false, Protections.default(), { requests: [] }), - platform: "example", - emailProtectionUserData: void 0, - theme: void 0, - fireButtonEnabled: false - }; - const params = new URLSearchParams(searchString); - const stateKey = params.get("state"); - if (stateKey) { - const match = testDataStates[stateKey]; - if (match) { - overrides3.requests = match.requests; - overrides3.tab.requestDetails = createRequestDetails(match.requests, []); - overrides3.tab.parentEntity = match.parentEntity; - overrides3.tab.url = match.url; - overrides3.tab.upgradedHttps = match.upgradedHttps; - overrides3.tab.certificate = match.certificate; - overrides3.tab.cookiePromptManagementStatus = match.cookiePromptManagementStatus; - overrides3.tab.locale = match.localeSettings.locale; - if (match.allowlisted) { - overrides3.requests = protectionsOff(overrides3.requests); - overrides3.tab.requestDetails = createRequestDetails(overrides3.requests, []); - overrides3.tab.protections = new Protections(false, ["contentBlocking"], true, false); - } - if (match.contentBlockingException) { - overrides3.requests = protectionsOff(overrides3.requests); - overrides3.tab.requestDetails = createRequestDetails(overrides3.requests, []); - overrides3.tab.protections = new Protections(false, [], false, false); - } - if (match.denylisted) { - overrides3.requests = protectionsOff(overrides3.requests); - overrides3.tab.requestDetails = createRequestDetails(overrides3.requests, []); - overrides3.tab.protections = new Protections(false, [], false, true); - } - } - } - const platformParam = params.get("platform"); - if (platformParam && isValidPlatform(platformParam)) { - overrides3.platform = platformParam; - document.body.classList.remove("environment--example"); - document.body.classList.add(`environment--${overrides3.platform}`); - window.environmentOverride = overrides3.platform; - } - if (params.has("theme")) { - if (params.get("theme") === "light") { - overrides3.theme = "light"; - } else if (params.get("theme") === "dark") { - overrides3.theme = "dark"; - } - if (overrides3.theme?.toLowerCase() === "dark") { - document.body.classList.add("body--theme-dark"); - } else { - document.body.classList.remove("body--theme-dark"); - } - } - if (params.get("specialDomainName") || params.get("specialDomain")) { - overrides3.tab.specialDomainName = "extensions"; - } - if (params.get("locale")) { - overrides3.tab.locale = params.get("locale"); - } - if (params.get("consentManaged")) { - overrides3.tab.cookiePromptManagementStatus = { - consentManaged: true, - cosmetic: false, - optoutFailed: false, - configurable: false - }; - if (params.get("consentConfigurable")) { - overrides3.tab.cookiePromptManagementStatus.configurable = true; - } - if (params.get("consentCosmetic")) { - overrides3.tab.cookiePromptManagementStatus.cosmetic = true; - } - } - if (overrides3.platform === "browser") { - overrides3.tab.emailProtection = {}; - overrides3.tab.search = {}; - overrides3.tab.ctaScreens = {}; - overrides3.tab.permissions = []; - if (params.get("emailUser") === "true") { - overrides3.emailProtectionUserData = { - nextAlias: "123456_next" - }; - } - if (params.get("fireButton") === "true") { - overrides3.fireButtonEnabled = true; - } - } - if (overrides3.platform === "ios" || overrides3.platform === "macos") { - overrides3.tab.platformLimitations = true; - } - if (overrides3.platform === "ios" || overrides3.platform === "android") { - overrides3.tab.permissions = []; - } - return overrides3; - } - var init_overrides = __esm({ - "shared/js/browser/utils/overrides.js"() { - "use strict"; - init_environment_check(); - init_states_with_fixtures(); - init_toggle_protections(); - init_request_details(); - init_protections(); - } - }); - - // shared/js/browser/example-communication.js - var example_communication_exports = {}; - __export(example_communication_exports, { - backgroundMessage: () => backgroundMessage5, - fetch: () => fetch6, - getBackgroundTabData: () => getBackgroundTabData5, - openOptionsPage: () => openOptionsPage, - search: () => search2, - setup: () => setup6 - }); - async function fetch6(message) { - if (message instanceof SetListsMessage) { - console.warn("doing nothing by default with `setList`"); - } - if (message instanceof RefreshEmailAliasMessage) { - if (overrides.platform === "browser") { - return Promise.resolve({ - privateAddress: "dax123456" - }); - } - } - if (message instanceof CheckBrokenSiteReportHandledMessage) { - if (overrides.platform === "ios" || overrides.platform === "android") { - return true; - } else { - return false; - } - } - if (message instanceof OpenSettingsMessages) { - if (overrides.platform === "ios" || overrides.platform === "android") { - return true; - } else { - return false; - } - } - if (message instanceof FetchBurnOptions) { - const clearHistory = true; - const tabClearEnabled = true; - return Promise.resolve({ - options: [ - { - name: "CurrentSite", - options: { - origins: ["https://example.com/"] - }, - descriptionStats: { - clearHistory, - site: "example.com", - openTabs: tabClearEnabled ? 1 : void 0, - cookies: 1, - pinnedTabs: 1 - } - }, - { - name: "LastHour", - options: { - since: Date.now() - }, - descriptionStats: { - clearHistory, - duration: "hour", - openTabs: tabClearEnabled ? 5 : void 0, - cookies: 23, - pinnedTabs: 1 - } - }, - { - name: "AllTime", - options: {}, - descriptionStats: { - clearHistory, - duration: "all", - openTabs: tabClearEnabled ? 5 : void 0, - cookies: 1e3, - pinnedTabs: 1 - } - } - ] - }); - } - console.log("fetch - Not implemented", message); - } - function backgroundMessage5(backgroundModel) { - console.log("backgroundMessage - setting local channel"); - channel5 = backgroundModel; - } - async function getBackgroundTabData5() { - return { - tab: overrides.tab, - emailProtectionUserData: overrides.emailProtectionUserData, - fireButton: { - enabled: overrides.fireButtonEnabled - } - }; - } - function setup6() { - const setColorScheme = setupColorScheme(); - setColorScheme(overrides.theme); - setupMutationObserver((height) => { - console.log("Window height change:", height); - }); - } - function openOptionsPage() { - console.warn("should open options page here"); - } - function search2(query) { - console.warn("should open search for ", JSON.stringify(query)); - } - var overrides, channel5; - var init_example_communication = __esm({ - "shared/js/browser/example-communication.js"() { + // shared/js/browser/utils/communication-mocks.mjs + var init_communication_mocks = __esm({ + "shared/js/browser/utils/communication-mocks.mjs"() { "use strict"; - init_common(); - init_overrides(); - overrides = getOverrides(window.location.search); - channel5 = null; - if (new URLSearchParams(window.location.search).has("continuous")) { - setInterval(() => { - channel5?.send("updateTabData"); - }, 200); - } } }); // shared/js/browser/communication.js - var defaultComms, platform, overrides2, communication_default; + var defaultComms, platform, communication_default; var init_communication = __esm({ "shared/js/browser/communication.js"() { "use strict"; @@ -14546,15 +13338,11 @@ init_android_communication(); init_windows_communication(); init_macos_communication(); - init_example_communication(); - init_overrides(); - platform = { name: "example" }; - overrides2 = getOverrides(window.location.search); - if (overrides2.platform && overrides2.platform !== "example") { - window.environmentOverride = overrides2.platform; - defaultComms = example_communication_exports; - platform.name = overrides2.platform; - } else if (isIOS()) { + init_communication_mocks(); + platform = { + name: "browser" + }; + if (isIOS()) { defaultComms = ios_communication_exports; platform.name = "ios"; } else if (isBrowser()) { @@ -14569,8 +13357,6 @@ } else if (isMacos()) { defaultComms = macos_communication_exports; platform.name = "macos"; - } else { - defaultComms = example_communication_exports; } if (!defaultComms) throw new Error("unsupported environment"); @@ -16447,11 +15233,11 @@ } }, { key: "forward", - value: function forward(args, lvl, prefix, debugOnly) { + value: function forward(args, lvl, prefix2, debugOnly) { if (debugOnly && !this.debug) return null; if (typeof args[0] === "string") - args[0] = "".concat(prefix).concat(this.prefix, " ").concat(args[0]); + args[0] = "".concat(prefix2).concat(this.prefix, " ").concat(args[0]); return this.logger[lvl](args); } }, { @@ -17073,9 +15859,9 @@ }], [{ key: "hasDefaultValue", value: function hasDefaultValue(options) { - var prefix = "defaultValue"; + var prefix2 = "defaultValue"; for (var option in options) { - if (Object.prototype.hasOwnProperty.call(options, option) && prefix === option.substring(0, prefix.length) && void 0 !== options[option]) { + if (Object.prototype.hasOwnProperty.call(options, option) && prefix2 === option.substring(0, prefix2.length) && void 0 !== options[option]) { return true; } } @@ -17986,15 +16772,15 @@ key: "loadOne", value: function loadOne(name) { var _this5 = this; - var prefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : ""; + var prefix2 = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : ""; var s = name.split("|"); var lng = s[0]; var ns2 = s[1]; this.read(lng, ns2, "read", void 0, void 0, function(err, data) { if (err) - _this5.logger.warn("".concat(prefix, "loading namespace ").concat(ns2, " for language ").concat(lng, " failed"), err); + _this5.logger.warn("".concat(prefix2, "loading namespace ").concat(ns2, " for language ").concat(lng, " failed"), err); if (!err && data) - _this5.logger.log("".concat(prefix, "loaded namespace ").concat(ns2, " for language ").concat(lng), data); + _this5.logger.log("".concat(prefix2, "loaded namespace ").concat(ns2, " for language ").concat(lng), data); _this5.loaded(name, err, data); }); } @@ -20575,13 +19361,13 @@ } startsWith = hasNativeStartsWith ? ( // Native - function startsWith2(s, search3, position) { - return s.startsWith(search3, position); + function startsWith2(s, search2, position) { + return s.startsWith(search2, position); } ) : ( // For IE11 - function startsWith3(s, search3, position) { - return s.slice(position, position + search3.length) === search3; + function startsWith3(s, search2, position) { + return s.slice(position, position + search2.length) === search2; } ); fromCodePoint = hasNativeFromCodePoint ? String.fromCodePoint : ( @@ -20816,9 +19602,9 @@ } break; } - var location = createLocation(start, this.clonePosition()); + var location2 = createLocation(start, this.clonePosition()); return { - val: { type: TYPE.literal, value, location }, + val: { type: TYPE.literal, value, location: location2 }, err: null }; }; @@ -20935,8 +19721,8 @@ var endOffset = startOffset + value.length; this.bumpTo(endOffset); var endPosition = this.clonePosition(); - var location = createLocation(startingPosition, endPosition); - return { value, location }; + var location2 = createLocation(startingPosition, endPosition); + return { value, location: location2 }; }; Parser2.prototype.parseArgumentOptions = function(nestingLevel, expectingCloseTag, value, openingBracePosition) { var _a2; @@ -21121,18 +19907,18 @@ err: null }; }; - Parser2.prototype.parseNumberSkeletonFromString = function(skeleton, location) { + Parser2.prototype.parseNumberSkeletonFromString = function(skeleton, location2) { var tokens = []; try { tokens = parseNumberSkeletonFromString(skeleton); } catch (e) { - return this.error(ErrorKind.INVALID_NUMBER_SKELETON, location); + return this.error(ErrorKind.INVALID_NUMBER_SKELETON, location2); } return { val: { type: SKELETON_TYPE.number, tokens, - location, + location: location2, parsedOptions: this.shouldParseSkeletons ? parseNumberSkeleton(tokens) : {} }, err: null @@ -21215,13 +20001,13 @@ break; } } - var location = createLocation(startingPosition, this.clonePosition()); + var location2 = createLocation(startingPosition, this.clonePosition()); if (!hasDigits) { - return this.error(expectNumberError, location); + return this.error(expectNumberError, location2); } decimal *= sign; if (!isSafeInteger(decimal)) { - return this.error(invalidNumberError, location); + return this.error(invalidNumberError, location2); } return { val: decimal, err: null }; }; @@ -21249,13 +20035,13 @@ } return code; }; - Parser2.prototype.error = function(kind, location) { + Parser2.prototype.error = function(kind, location2) { return { val: null, err: { kind, message: this.message, - location + location: location2 } }; }; @@ -21273,9 +20059,9 @@ this.position.offset += code < 65536 ? 1 : 2; } }; - Parser2.prototype.bumpIf = function(prefix) { - if (startsWith(this.message, prefix, this.offset())) { - for (var i = 0; i < prefix.length; i++) { + Parser2.prototype.bumpIf = function(prefix2) { + if (startsWith(this.message, prefix2, this.offset())) { + for (var i = 0; i < prefix2.length; i++) { this.bump(); } return true; @@ -22403,9 +21189,9 @@ * @param {Array} elems * @this {any} */ - _cacheElems: function(prefix, elems) { + _cacheElems: function(prefix2, elems) { for (let i = 0; i < elems.length; i++) { - const selector = prefix + "-" + elems[i]; + const selector = prefix2 + "-" + elems[i]; const id = "$" + elems[i].replace(/-/g, ""); this[id] = this.$(selector); } @@ -23200,8 +21986,8 @@ ${description( (0, import_raw2.default)( i18n.t("site:majorTrackingNetworkDesc.title", { - companyDisplayName: company.displayName, - companyPrevalence: Math.round(company.prevalence), + companyDisplayName: company?.displayName, + companyPrevalence: Math.round(company?.prevalence ?? 0), blocked: model.tab.requestDetails.blocked.entitiesCount > 0 }) ) @@ -23498,9 +22284,9 @@ } }); - // shared/js/ui/platform-features.js + // shared/js/ui/platform-features.mjs function createPlatformFeatures(platform2) { - const desktop = ["windows", "macos", "browser", "example"]; + const desktop = ["windows", "macos", "browser"]; return new PlatformFeatures({ spinnerFollowingProtectionsToggle: platform2.name !== "android" && platform2.name !== "windows", supportsHover: desktop.includes(platform2.name) @@ -23508,7 +22294,7 @@ } var PlatformFeatures; var init_platform_features = __esm({ - "shared/js/ui/platform-features.js"() { + "shared/js/ui/platform-features.mjs"() { "use strict"; PlatformFeatures = class { /** @@ -23538,7 +22324,7 @@ init_common(); CookiePromptModel.prototype = import_jquery9.default.extend({}, model_default.prototype, { modelName: "cookiePrompt", - openSettings: function(category) { + openSettings: function(_category) { this.fetch( new OpenSettingsMessages({ target: "cpm" @@ -23584,7 +22370,7 @@ if (this.popstateHandler) { window.removeEventListener("popstate", this.popstateHandler); } - this.popstateHandler = (e) => { + this.popstateHandler = () => { this._destroy(null, { fromNavigation: true }); }; window.addEventListener("popstate", this.popstateHandler); @@ -23941,10 +22727,10 @@ this.$go.removeClass(FOCUS_CLASS); } }, - _handleBlur: function(e) { + _handleBlur: function(_e) { this._removeHoverEffect(); }, - _handleInput: function(e) { + _handleInput: function(_e) { const searchText = this.$input.val(); this.model.set("searchText", searchText); if (searchText.length > 0) { @@ -24007,7 +22793,7 @@ modelName: "siteCompanyList", /** @this {any} */ fetchAsyncData: function() { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { communication_default.getBackgroundTabData().then(({ tab }) => { if (tab) { this.tab = tab; @@ -24152,7 +22938,7 @@ }, // calls `this.set()` to trigger view re-rendering /** @this {{tab: import('../../browser/utils/request-details.mjs').TabData} & Record} */ - update: function(ops) { + update: function() { if (!this.acceptingUpdates) { console.log("not updating because acceptingUpdates was false"); return; @@ -24399,19 +23185,19 @@ this.features = createPlatformFeatures(platform); this.cleanups = []; this.nav = { - connection: (e) => { + connection: () => { this.model.send("navigate", { target: "connection" }); }, - trackers: (e) => { + trackers: () => { this.model.send("navigate", { target: "trackers" }); }, - nonTrackers: (e) => { + nonTrackers: () => { this.model.send("navigate", { target: "nonTrackers" }); }, - consentManaged: (e) => { + consentManaged: () => { this.model.send("navigate", { target: "consentManaged" }); }, - cookieHidden: (e) => { + cookieHidden: () => { this.model.send("navigate", { target: "cookieHidden" }); } }; @@ -24739,7 +23525,7 @@ _close: function() { document.getElementById("fire-button-container")?.remove(); }, - _updateSummary: function(ev) { + _updateSummary: function() { const selectedOption = this.$opts[0].selectedIndex; const opts = this.model.fireOptions[selectedOption]; this.model.fetch(new SetBurnDefaultOption(opts.name)); @@ -24807,10 +23593,10 @@ Site2.prototype = import_jquery21.default.extend({}, view_default.prototype, { /** * @this {Site & Record} - * @param e + * @param _e * @private */ - _onAllowlistClick: function(e) { + _onAllowlistClick: function(_e) { if (this.$body.hasClass("is-disabled")) return; if (this.updateInProgress) @@ -24881,7 +23667,7 @@ this.showBreakageForm("reportBrokenSite"); } }).catch((e2) => { - console.error("could not check"); + console.error("could not check", e2); }); }, // pass clickSource to specify whether page should reload @@ -25013,9 +23799,9 @@
${renderSearchWrapper(this.model)}
-
+

${errorText}

-
+
@@ -25030,30 +23816,23 @@
-
- ${renderEmailWrapper(this.model)} -
+
${renderEmailWrapper(this.model)}
`; } const permissions = localizePermissions(this.model.permissions); - return import_nanohtml18.default` -
- ${renderSearchWrapper(this.model)} - ${topNav({ view: "primary" })} -
0}> -
-
+ return import_nanohtml18.default`
+ ${renderSearchWrapper(this.model)} ${topNav({ view: "primary" })} +
0}> +
+
-
- +
+ ${protectionToggle(this.model)}
-
- ${renderEmailWrapper(this.model)} - ${renderReportButton()} -
+
${renderEmailWrapper(this.model)} ${renderReportButton()}
${permissions.length ? outer({ children: renderManagePermissions(this.model) }) : null}
`; @@ -25076,35 +23855,34 @@ return ""; } const localizedPerms = localizePermissions(model.permissions); - return import_nanohtml18.default` -
    -
  • - ${localizedPerms.map(({ key: permissionId, title, permission, options }, index) => { + return import_nanohtml18.default`
      +
    • + ${localizedPerms.map(({ key: permissionId, title, permission, options }) => { if (!model.permissions) return ""; return import_nanohtml18.default`
      - -
      `; + + +
`; })} - - `; + + `; } function renderReportButton() { return import_nanohtml18.default``; + + ${i18n.t("site:websiteNotWorkingQ.title")} + +
`; } function localizePermissions(permissions) { if (!Array.isArray(permissions) || permissions.length === 0) { diff --git a/integration-tests/DashboardPage.js b/integration-tests/DashboardPage.js index b9395eb5..c4c71410 100644 --- a/integration-tests/DashboardPage.js +++ b/integration-tests/DashboardPage.js @@ -19,8 +19,6 @@ export class DashboardPage { return '/html/macos.html' case 'windows': return '/html/windows.html' - case 'example': - return '/html/example.html' case 'browser': return '/html/browser.html' default: { @@ -36,7 +34,7 @@ export class DashboardPage { mocks /** * @param {import("@playwright/test").Page} page - * @param {import("../shared/js/ui/platform-features.js").Platform} platform + * @param {import("../shared/js/ui/platform-features.mjs").Platform} platform */ constructor(page, platform) { this.platform = platform @@ -59,7 +57,7 @@ export class DashboardPage { async screenshotPrimary(name, state) { await this.page.emulateMedia({ reducedMotion: 'reduce' }) - await this.addStates([state]) + await this.addState([state]) await this.showsPrimaryScreen() return this.page.screenshot({ path: `screenshots/primary-${name}.png` }) } @@ -70,7 +68,7 @@ export class DashboardPage { */ async screenshotEachScreenForState(name, state) { await this.page.emulateMedia({ reducedMotion: 'reduce' }) - await this.addStates([state]) + await this.addState([state]) await this.showsPrimaryScreen() await this.screenshot(name + '-state-primary.png') await this.viewConnection() @@ -156,18 +154,11 @@ export class DashboardPage { /** * @param {import("@playwright/test").Page} page - * @param {{ - * getPrivacyDashboardData?: import('../schema/__generated__/schema.types').GetPrivacyDashboardData, - * getBurnOptions?: import('../schema/__generated__/schema.types').FireButtonData - * }} messages * @returns {Promise} */ - static async browser(page, messages) { + static async browser(page) { const dash = new DashboardPage(page, { name: 'browser' }) await dash.withMocks() - await page.addInitScript((messages) => { - window.__playwright.messages = Object.assign(window.__playwright.messages, messages) - }, messages) await dash.loadPage() await page.waitForFunction(() => typeof window.__playwright !== 'undefined') return dash @@ -185,15 +176,22 @@ export class DashboardPage { * @returns {Promise} */ async withMocks() { + await this.page.addInitScript(() => (window.__ddg_integration_test = true)) await this.mocks.install() return this } /** * @param {import("../shared/js/ui/views/tests/generate-data.mjs").MockData[]} states + * @returns {Promise[]>} */ - async addStates(states) { - await playTimeline(this.page, states, this.platform) + async addState(states) { + /** @type {Record[]} */ + const results = [] + for (const state of states) { + results.push(await playTimeline(this.page, state, this.platform)) + } + return results } async clickReportBreakage() { @@ -301,7 +299,7 @@ export class DashboardPage { await expect(this.page.locator('[placeholder="Search DuckDuckGo"]')).toHaveValue(text) } - async submitSearch(text) { + async submitSearch() { await this.page.locator('[type="submit"]').click() } diff --git a/integration-tests/Mocks.js b/integration-tests/Mocks.js index b1ec05fd..7f6168fa 100644 --- a/integration-tests/Mocks.js +++ b/integration-tests/Mocks.js @@ -6,7 +6,7 @@ export class Mocks { platform /** * @param {import("@playwright/test").Page} page - * @param {import("../shared/js/ui/platform-features.js").Platform} platform + * @param {import("../shared/js/ui/platform-features.mjs").Platform} platform */ constructor(page, platform) { this.page = page @@ -25,8 +25,6 @@ export class Mocks { return installWebkitMocks(this.page) case 'windows': return installWindowsMocks(this.page) - case 'example': - return '/build/app/html/example.html' case 'browser': return installBrowserMocks(this.page) default: { @@ -49,7 +47,7 @@ export class Mocks { return result } - async calledForShowBreakageForm(s) { + async calledForShowBreakageForm() { const calls = await this.outgoing() if (this.platform.name === 'android') { expect(calls).toMatchObject([['showBreakageForm', undefined]]) @@ -274,7 +272,7 @@ export class Mocks { async calledForOpenSettings() { /** - * @type {Record} + * @type {Record} */ const implementations = { windows: async () => { @@ -303,7 +301,6 @@ export class Mocks { expect(calls).toMatchObject([['openSettings', JSON.stringify({ target: 'cpm' })]]) }, browser: undefined, - example: undefined, } const impl = implementations[this.platform.name] if (!impl) throw new Error('calledForOpenSettings not implemented for ' + this.platform.name) diff --git a/integration-tests/android.spec-int.js b/integration-tests/android.spec-int.js index ff9dcbe7..5a94ba03 100644 --- a/integration-tests/android.spec-int.js +++ b/integration-tests/android.spec-int.js @@ -5,7 +5,7 @@ import { DashboardPage } from './DashboardPage' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.showsPrimaryScreen() }) }) @@ -13,13 +13,13 @@ test.describe('initial page data', () => { test.describe('page data (with trackers)', () => { test('should display correct primary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.showsPrimaryScreen() await dash.screenshot('primary-cnn.png') }) test('should display correct tracker screen + ripple effect on about link', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.viewTrackerCompanies() await dash.aboutLinkHasRipple() }) @@ -28,7 +28,7 @@ test.describe('page data (with trackers)', () => { test.describe('breakage form', () => { test('should call android interface', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.clickReportBreakage() await dash.mocks.calledForShowBreakageForm() }) @@ -37,7 +37,7 @@ test.describe('breakage form', () => { test.describe('open external links', () => { test('should call android interface for links', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewTrackerCompanies() await dash.clickAboutLink() await dash.mocks.calledForAboutLink() @@ -47,12 +47,12 @@ test.describe('open external links', () => { test.describe('localization', () => { test('should load with `pl` locale', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['locale-pl']]) + await dash.addState([testDataStates['locale-pl']]) await dash.hasPolishLinkTextForConnectionInfo() }) test('should load with `fr` locale', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['locale-fr']]) + await dash.addState([testDataStates['locale-fr']]) await dash.hasFrenchLinkTextForConnectionInfo() }) }) @@ -60,7 +60,7 @@ test.describe('localization', () => { test.describe('Protections toggle', () => { test('pressing toggle should disable protections', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.toggleProtectionsOff() await page.waitForTimeout(500) // todo(Shane): remove this await dash.mocks.calledForToggleAllowList() @@ -70,7 +70,7 @@ test.describe('Protections toggle', () => { test.describe('Close', () => { test('pressing close should call native API', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.clickClose() await dash.mocks.calledForClose() }) @@ -80,7 +80,7 @@ test.describe('cookie prompt management', () => { test.describe('none-configurable', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() }) }) @@ -88,12 +88,12 @@ test.describe('cookie prompt management', () => { test.describe('non-cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -102,12 +102,12 @@ test.describe('cookie prompt management', () => { test.describe('cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.indicatesCookiesWereHidden() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -117,14 +117,14 @@ test.describe('cookie prompt management', () => { }) if (!process.env.CI) { - const states = [ - { name: 'ad-attribution', state: testDataStates['ad-attribution'] }, - { name: 'new-entities', state: testDataStates['new-entities'] }, - { name: 'upgraded+secure', state: testDataStates['upgraded+secure'] }, - { name: 'google-off', state: testDataStates['google-off'] }, - { name: 'cnn', state: testDataStates.cnn }, - ] test.describe('screenshots', () => { + const states = [ + { name: 'ad-attribution', state: testDataStates['ad-attribution'] }, + { name: 'new-entities', state: testDataStates['new-entities'] }, + { name: 'upgraded+secure', state: testDataStates['upgraded+secure'] }, + { name: 'google-off', state: testDataStates['google-off'] }, + { name: 'cnn', state: testDataStates.cnn }, + ] for (const { name, state } of states) { test(name, async ({ page }) => { const dash = await DashboardPage.android(page) @@ -135,7 +135,7 @@ if (!process.env.CI) { test.describe('screenshots for cookies (non-configurable)', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.android(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() await dash.screenshot('consent-managed.png') }) @@ -144,13 +144,13 @@ if (!process.env.CI) { test.describe('non-cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() await dash.screenshot('consent-managed-configurable.png') }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.screenshot('consent-managed-configurable-secondary.png') await dash.disableCookiesInSettings() @@ -160,13 +160,13 @@ if (!process.env.CI) { test.describe('cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.indicatesCookiesWereHidden() await dash.screenshot('consent-managed-configurable-primary-cosmetic.png') }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.viewCookiePromptManagement() await dash.screenshot('consent-managed-configurable-secondary-cosmetic.png') await dash.disableCookiesInSettings() diff --git a/integration-tests/browser.spec-int.js b/integration-tests/browser.spec-int.js index c84d4c96..5701e02e 100644 --- a/integration-tests/browser.spec-int.js +++ b/integration-tests/browser.spec-int.js @@ -1,13 +1,11 @@ import { test } from '@playwright/test' -import { MockData, mockBurnOptions, mockToExtensionDashboardMessage } from '../shared/js/ui/views/tests/generate-data.mjs' import { testDataStates } from '../shared/js/ui/views/tests/states-with-fixtures' import { DashboardPage } from './DashboardPage' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) await dash.showsPrimaryScreen() await dash.mocks.calledForInitialExtensionMessage() }) @@ -15,10 +13,8 @@ test.describe('initial page data', () => { test.describe('breakage form', () => { test('should submit with no values', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) await dash.clickReportBreakage() await dash.screenshot('breakage-form.png') await dash.submitBreakageForm() @@ -26,10 +22,8 @@ test.describe('breakage form', () => { await dash.screenshot('breakage-form-message.png') }) test('should submit with description', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) await dash.clickReportBreakage() await dash.enterBreakageSubscription('Video not playing') await dash.submitBreakageForm() @@ -39,10 +33,8 @@ test.describe('breakage form', () => { }) }) test('should submit with category', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) const optionToSelect = "Video didn't play" await dash.clickReportBreakage() await dash.selectBreakageCategory(optionToSelect) @@ -57,10 +49,8 @@ test.describe('breakage form', () => { test.describe('Protections toggle', () => { test.describe('when a site is NOT allowlisted', () => { test('then pressing toggle should disable protections', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) await dash.toggleProtectionsOff() await page.waitForTimeout(500) // todo(Shane): remove this await dash.mocks.calledForToggleAllowList('protections-off') @@ -68,10 +58,8 @@ test.describe('Protections toggle', () => { }) test.describe('When the site is already allowlisted', () => { test('then pressing the toggle re-enables protections', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', allowlisted: true }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.allowlisted]) await dash.toggleProtectionsOn() await page.waitForTimeout(500) // todo(Shane): remove this await dash.mocks.calledForToggleAllowList('protections-on') @@ -79,10 +67,8 @@ test.describe('Protections toggle', () => { }) test.describe('When the site has content blocking disabled', () => { test('then pressing the toggle re-enables protections (overriding our decision)', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', contentBlockingException: true }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) - await dash.addStates([testDataStates.protectionsOn]) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOff]) await dash.toggleProtectionsOn() await page.waitForTimeout(500) // todo(Shane): remove this await dash.mocks.calledForToggleAllowList('protections-on-override') @@ -92,21 +78,19 @@ test.describe('Protections toggle', () => { test.describe('special page (cta)', () => { test('should render correctly', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', specialDomainName: true, emailUser: true }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['special-page']]) await dash.showingCTA() }) }) test.describe('search', () => { test('should not lose text when re-render occurs', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) const term = 'nike' await dash.enterSearchText(term) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn_blocked]) await dash.searchContainsText(term) await dash.submitSearch() await dash.mocks.calledForSearch(term) @@ -115,9 +99,8 @@ test.describe('search', () => { test.describe('options', () => { test('should open options page', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com' }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates.protectionsOn]) await dash.selectOptionsCog() await dash.mocks.calledForOptions() }) @@ -125,100 +108,69 @@ test.describe('options', () => { test.describe('tab data error', () => { test('should show an error screen', async ({ page }) => { - // @ts-expect-error - this SHOULD error, that's the test - const mock = new MockData({ url: 'https://example.com', requests: [{ foo: 'bar' }] }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['invalid-data']]) await dash.displayingError() }) }) test.describe('localization', () => { test('should load with `pl` locale', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', localeSettings: { locale: 'pl' } }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['locale-pl']]) await dash.hasPolishLinkTextForConnectionInfo() }) }) test.describe('fire button', () => { test('by default no fire button is displayed', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', fireButtonEnabled: false }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['fire-button-disabled']]) await dash.showsPrimaryScreen() await dash.fireButtonDoesntShow() }) test('adding firebutton option to dashboard message shows fire button', async ({ page }) => { - const mock = new MockData({ url: 'https://example.com', fireButtonEnabled: true }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['fire-button-enabled']]) await dash.fireButtonShows() }) test('fire button menu: open and close', async ({ page }) => { - const mock = new MockData({ - url: 'https://example.com', - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 0 }, - }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['fire-button-no-pinned']]) await dash.clickFireButton() await dash.fireButtonCancelClosesDialog() }) - test('fire button menu: history and tab clearing enabled', async ({ page }) => { - const mock = new MockData({ - url: 'https://example.com', - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 0 }, - }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + const mock = testDataStates['fire-button-no-pinned'] + await dash.addState([mock]) await dash.clickFireButton() - await dash.fireDialogIsPopulatedFromOptions(mock.getBurnOptions) + await dash.fireDialogIsPopulatedFromOptions(mock.toBurnOptions()) }) - test('fire button menu: history clearing enabled', async ({ page }) => { - const mock = new MockData({ - url: 'https://example.com', - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: false, pinnedTabs: 0 }, - }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([testDataStates['fire-button-tab-clear-disabled']]) await dash.clickFireButton() await dash.fireDialogHistoryDisabled() }) - test('fire button menu: sends option parameters with burn message', async ({ page }) => { - const mock = new MockData({ - url: 'https://example.com', - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 0 }, - }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + const mock = testDataStates['fire-button-no-pinned'] + await dash.addState([mock]) await dash.clickFireButton() await dash.clickFireButtonBurn() - await dash.sendsOptionsWithBurnMessage(mock.getBurnOptions?.options[0].options) + await dash.sendsOptionsWithBurnMessage(mock.toBurnOptions()?.options[0].options) }) - test('fire button menu: sends option parameters of selected burn option', async ({ page }) => { - const mock = new MockData({ - url: 'https://example.com', - fireButtonEnabled: true, - fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 0 }, - }) - const messages = mockToExtensionDashboardMessage(mock) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + const mock = testDataStates['fire-button-no-pinned'] + const [messages] = await dash.addState([mock]) await dash.clickFireButton() await dash.chooseBurnOption(1) await dash.clickFireButtonBurn() - await dash.sendsOptionsWithBurnMessage(mock.getBurnOptions?.options[1].options) + await dash.sendsOptionsWithBurnMessage(messages.getBurnOptions.options[1].options) }) }) @@ -232,14 +184,13 @@ if (!process.env.CI) { { name: 'fire-button', state: testDataStates['fire-button'], - burnOptions: mockBurnOptions({ clearHistory: true, tabClearEnabled: true, pinnedTabs: 2 }), }, ] test.describe('screenshots', () => { for (const { name, state } of states) { test(name, async ({ page }) => { - const messages = mockToExtensionDashboardMessage(state) - const dash = await DashboardPage.browser(page, messages) + const dash = await DashboardPage.browser(page) + await dash.addState([state]) await dash.screenshotEachScreenForState(name, state) }) } diff --git a/integration-tests/helpers.js b/integration-tests/helpers.js index ceb74311..d2056176 100644 --- a/integration-tests/helpers.js +++ b/integration-tests/helpers.js @@ -1,8 +1,16 @@ +import { + mockAndroidApis, + mockBrowserApis, + mockDataProvider, + webkitMockApis, + windowsMockApis, +} from '../shared/js/browser/utils/communication-mocks.mjs' + /** * @param {import('@playwright/test').Page} page */ export function forwardConsole(page) { - page.on('console', (msg, other) => { + page.on('console', (msg) => { const replaced = msg.text().replace(/http:\/\/localhost:3210/g, './build/app') console.log('->', msg.type(), replaced) }) @@ -10,123 +18,27 @@ export function forwardConsole(page) { /** * @param {import('@playwright/test').Page} page - * @param {import("../shared/js/ui/views/tests/generate-data.mjs").MockData[]} states - * @param {import('../shared/js/ui/platform-features').Platform} [platform] - * @returns {Promise} + * @param {import("../shared/js/ui/views/tests/generate-data.mjs").MockData} state + * @param {import('../shared/js/ui/platform-features.mjs').Platform} [platform] + * @returns {Promise>} */ -export async function playTimeline(page, states, platform) { +export async function playTimeline(page, state, platform) { platform = platform || { name: 'ios' } - await page.evaluate( - async (params) => { - const { states, platform } = params - for (const state of states) { - const nextParentEntity = state.parentEntity - const nextUpgradedHttps = state.upgradedHttps - const nextCerts = state.certificate - const nextProtections = state.protections - const nextLocale = state.localeSettings - const nextPermissions = state.permissions - const nextCookiePromptManagementStatus = state.cookiePromptManagementStatus - const nextRequestData = { requests: state.requests } - - if (platform?.name === 'windows') { - for (const listener of window.__playwright.listeners || []) { - // todo(Shane): Centralise this so that it's shared in the comms file - listener({ - /** @type {import('../schema/__generated__/schema.types').WindowsIncomingViewModel} */ - data: { - Feature: 'PrivacyDashboard', - Name: 'ViewModelUpdated', - Data: { - protections: nextProtections, - rawRequestData: nextRequestData, - tabUrl: state.url, - upgradedHttps: nextUpgradedHttps, - parentEntity: nextParentEntity, - permissions: nextPermissions, - certificates: nextCerts, - cookiePromptManagementStatus: nextCookiePromptManagementStatus, - }, - }, - }) - } - return - } - if (platform?.name === 'browser') { - window.__playwright.messages = Object.assign(window.__playwright.messages, { - getPrivacyDashboardData: { - tab: { - id: 1533, - url: state.url, - upgradedHttps: nextUpgradedHttps, - protections: nextProtections, - parentEntity: nextParentEntity, - }, - requestData: nextRequestData, - emailProtectionUserData: { - cohort: 'private_beta_dax', - nextAlias: '123456_next', - token: '123456', - userName: 'daxtheduck', - }, - }, - }) - for (const listener of window.__playwright.listeners || []) { - listener({ updateTabData: true }, { id: 'test' }) - } - return - } - if (nextCookiePromptManagementStatus) { - window.onChangeConsentManaged(nextCookiePromptManagementStatus) - } - window.onChangeParentEntity(nextParentEntity) - window.onChangeProtectionStatus(nextProtections) - window.onChangeUpgradedHttps(nextUpgradedHttps) - window.onChangeCertificateData({ - secCertificateViewModels: nextCerts, - }) - window.onChangeLocale(nextLocale) - window.onChangeRequestData(state.url, nextRequestData) - } - }, - { states, platform } - ) + const messages = {} + if (platform.name === 'browser') { + messages.getBurnOptions = state.toBurnOptions() + messages.getPrivacyDashboardData = state.toExtensionDashboardData() + } + if (platform.name === 'windows') { + messages.windowsViewModel = state.toWindowsViewModel() + } + await page.evaluate(mockDataProvider, { state, platform, messages }) + return messages } export async function installAndroidMocks(page) { await page.waitForFunction(() => typeof window.onChangeRequestData === 'function') - return page.evaluate(() => { - try { - window.__playwright = { - messages: {}, - mocks: { - outgoing: [], - incoming: [], - }, - calls: [], - } - window.PrivacyDashboard = { - showBreakageForm(arg) { - window.__playwright.mocks.outgoing.push(['showBreakageForm', arg]) - }, - openInNewTab(arg) { - window.__playwright.mocks.outgoing.push(['openInNewTab', arg]) - }, - openSettings(arg) { - window.__playwright.mocks.outgoing.push(['openSettings', arg]) - }, - close(arg) { - window.__playwright.mocks.outgoing.push(['close', arg]) - }, - toggleAllowlist(arg) { - window.__playwright.mocks.outgoing.push(['toggleAllowlist', arg]) - }, - } - } catch (e) { - console.error("❌couldn't set up mocks") - console.error(e) - } - }) + return page.evaluate(mockAndroidApis) } /** @@ -134,37 +46,7 @@ export async function installAndroidMocks(page) { * @returns {Promise} */ export function installWindowsMocks(page) { - return page.addInitScript(() => { - try { - if (!window.chrome) { - // @ts-ignore - window.chrome = {} - } - window.__playwright = { - listeners: [], - messages: {}, - mocks: { - outgoing: [], - incoming: [], - }, - calls: [], - } - // override some methods on window.chrome.runtime to fake the incoming/outgoing messages - window.chrome.webview = { - // @ts-ignore - addEventListener: (messageName, listener) => { - window.__playwright.listeners?.push(listener) - }, - postMessage(arg) { - window.__playwright.mocks.outgoing.push([arg.Name, arg]) - }, - } - console.log('window.chrome.webview', window.chrome.webview) - } catch (e) { - console.error("❌couldn't set up mocks") - console.error(e) - } - }) + return page.addInitScript(windowsMockApis) } /** @@ -173,55 +55,7 @@ export function installWindowsMocks(page) { */ export async function installWebkitMocks(page) { await page.waitForFunction(() => typeof window.onChangeRequestData === 'function') - return page.evaluate(() => { - try { - window.__playwright = { - messages: {}, - mocks: { - outgoing: [], - incoming: [], - }, - calls: [], - } - window.webkit = { - messageHandlers: { - privacyDashboardShowReportBrokenSite: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardShowReportBrokenSite', arg]) - }, - }, - privacyDashboardOpenUrlInNewTab: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardOpenUrlInNewTab', arg]) - }, - }, - privacyDashboardOpenSettings: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardOpenSettings', arg]) - }, - }, - privacyDashboardSubmitBrokenSiteReport: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardSubmitBrokenSiteReport', arg]) - }, - }, - privacyDashboardSetSize: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardSetSize', arg]) - }, - }, - privacyDashboardClose: { - postMessage: (arg) => { - window.__playwright.mocks.outgoing.push(['privacyDashboardClose', arg]) - }, - }, - }, - } - } catch (e) { - console.error("❌couldn't set up mocks") - console.error(e) - } - }) + return page.evaluate(webkitMockApis) } /** @@ -229,67 +63,5 @@ export async function installWebkitMocks(page) { * @returns {Promise} */ export async function installBrowserMocks(page) { - return page.addInitScript(() => { - const messages = { - submitBrokenSiteReport: {}, - setLists: {}, - search: {}, - openOptions: {}, - setBurnDefaultOption: {}, - doBurn: {}, - } - try { - if (!window.chrome) { - // @ts-ignore - window.chrome = { - // @ts-ignore - permissions: { - // eslint-disable-next-line n/no-callback-literal - request: (permissions, cb) => cb && cb(true), - }, - } - } - window.__playwright = { - messages, - mocks: { - outgoing: [], - incoming: [], - }, - calls: [], - listeners: [], - } - - // override some methods on window.chrome.runtime to fake the incoming/outgoing messages - window.chrome.runtime = { - id: 'test', - async sendMessage(message, cb) { - console.log('message', message) - function respond(fn, timeout = 100) { - setTimeout(() => { - fn() - }, timeout) - } - - // does the incoming message match one that's been mocked here? - const matchingMessage = window.__playwright.messages[message.messageType] - if (matchingMessage) { - window.__playwright.mocks.outgoing.push([message.messageType, message]) - respond(() => cb(matchingMessage), 200) - } else { - console.log(`❌ [(mocks): window.chrome.runtime] Missing support for ${JSON.stringify(message)}`) - } - }, - // @ts-ignore - onMessage: { - addListener(listener) { - console.log('got listener...') - window.__playwright.listeners?.push(listener) - }, - }, - } - } catch (e) { - console.error("❌couldn't set up browser mocks") - console.error(e) - } - }) + return page.addInitScript(mockBrowserApis) } diff --git a/integration-tests/ios.spec-int.js b/integration-tests/ios.spec-int.js index aa8efd3e..7baad0af 100644 --- a/integration-tests/ios.spec-int.js +++ b/integration-tests/ios.spec-int.js @@ -5,33 +5,33 @@ import { DashboardPage } from './DashboardPage' test.describe('page data (no trackers)', () => { test('should fetch initial data', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.showsPrimaryScreen() }) test('should accept updates when on trackers list screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewTrackerCompanies() await dash.screenshot('tracker-list-before.png') - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.waitForCompanyName('Google LLC') await dash.screenshot('tracker-list-after.png') }) test('should accept updates when on non-trackers list screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewThirdParties() await dash.screenshot('non-tracker-list-before.png') - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.waitForCompanyName('Google LLC') await dash.screenshot('non-tracker-list-after.png') }) test('does not alter the appearance of connection panel', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewConnection() await dash.screenshot('connection-before.png') - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.screenshot('connection-before.png') }) }) @@ -39,7 +39,7 @@ test.describe('page data (no trackers)', () => { test.describe('page data (with trackers)', () => { test('should display correct primary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.showsPrimaryScreen() await dash.screenshot('primary-screen.png') }) @@ -48,7 +48,7 @@ test.describe('page data (with trackers)', () => { test.describe('breakage form', () => { test('should call webkit interface and not use HTML form', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.cnn]) + await dash.addState([testDataStates.cnn]) await dash.clickReportBreakage() await dash.mocks.calledForShowBreakageForm() }) @@ -57,7 +57,7 @@ test.describe('breakage form', () => { test.describe('open external links', () => { test('should call ios interface for links', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewTrackerCompanies() await dash.clickAboutLink() await dash.mocks.calledForAboutLink() @@ -67,12 +67,12 @@ test.describe('open external links', () => { test.describe('localization', () => { test('should load with `pl` locale', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['locale-pl']]) + await dash.addState([testDataStates['locale-pl']]) await dash.hasPolishLinkTextForConnectionInfo() }) test('should load with `fr` locale', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['locale-fr']]) + await dash.addState([testDataStates['locale-fr']]) await dash.hasFrenchLinkTextForConnectionInfo() }) }) @@ -80,7 +80,7 @@ test.describe('localization', () => { test.describe('Close', () => { test('pressing close should call native API on iOS', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.clickClose() await dash.mocks.calledForClose() }) @@ -90,7 +90,7 @@ test.describe('cookie prompt management', () => { test.describe('none-configurable', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() }) }) @@ -98,12 +98,12 @@ test.describe('cookie prompt management', () => { test.describe('non-cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -115,12 +115,12 @@ test.describe('cookie prompt management', () => { test.describe('cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.indicatesCookiesWereHidden() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.ios(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -197,6 +197,7 @@ if (!process.env.CI) { const dash = await DashboardPage.ios(page) const screen = await dash.screenshotPrimary(name, state) await page.pause() + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { certificate, ...rest } = state await testInfo.attach(name, { body: JSON.stringify(rest, null, 2), diff --git a/integration-tests/macos.spec-int.js b/integration-tests/macos.spec-int.js index e7468da1..f7dff87d 100644 --- a/integration-tests/macos.spec-int.js +++ b/integration-tests/macos.spec-int.js @@ -5,7 +5,7 @@ import { DashboardPage } from './DashboardPage' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.showsPrimaryScreen() }) }) @@ -13,7 +13,7 @@ test.describe('initial page data', () => { test.describe('breakage form', () => { test('should show HTML breakage form and submit fields', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.clickReportBreakage() await dash.screenshot('breakage-form.png') await dash.submitBreakageForm() @@ -25,7 +25,7 @@ test.describe('breakage form', () => { test.describe('open external links', () => { test('should call webkit interface for external links', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.viewTrackerCompanies() await dash.clickAboutLink() await dash.mocks.calledForAboutLink() @@ -35,7 +35,7 @@ test.describe('open external links', () => { test.describe('setting the height', () => { test('should send the initial height to native', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.mocks.calledForInitialHeight() }) }) @@ -44,19 +44,19 @@ test.describe('cookie prompt management', () => { test.describe('none-configurable', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() }) }) test.describe('configurable', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -83,7 +83,7 @@ if (!process.env.CI) { test.describe('screenshots for cookies (none-configurable)', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() await dash.screenshot('consent-managed.png') }) @@ -92,13 +92,13 @@ if (!process.env.CI) { test.describe('non-cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() await dash.screenshot('consent-managed-configurable.png') }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.screenshot('consent-managed-configurable-secondary.png') await dash.disableCookiesInSettings() @@ -108,13 +108,13 @@ if (!process.env.CI) { test.describe('cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.indicatesCookiesWereHidden() await dash.screenshot('consent-managed-configurable-primary-cosmetic.png') }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.macos(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.viewCookiePromptManagement() await dash.screenshot('consent-managed-configurable-secondary-cosmetic.png') await dash.disableCookiesInSettings() diff --git a/integration-tests/windows.spec-int.js b/integration-tests/windows.spec-int.js index b6c63e41..59eb267f 100644 --- a/integration-tests/windows.spec-int.js +++ b/integration-tests/windows.spec-int.js @@ -5,7 +5,7 @@ import { DashboardPage } from './DashboardPage' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.showsPrimaryScreen() }) }) @@ -13,7 +13,7 @@ test.describe('initial page data', () => { test.describe('breakage form', () => { test('should submit with no values', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.clickReportBreakage() await dash.screenshot('breakage-form.png') await dash.submitBreakageForm() @@ -25,7 +25,7 @@ test.describe('breakage form', () => { test.describe('setting the height', () => { test('should send the initial height to native', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.mocks.calledForInitialHeight() }) }) @@ -33,7 +33,7 @@ test.describe('setting the height', () => { test.describe('Protections toggle', () => { test('pressing toggle should disable protections', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates.protectionsOn]) + await dash.addState([testDataStates.protectionsOn]) await dash.toggleProtectionsOff() await page.waitForTimeout(500) // todo(Shane): remove this await dash.mocks.calledForToggleAllowList() @@ -44,7 +44,7 @@ test.describe('cookie prompt management', () => { test.describe('none-configurable', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates['consent-managed']]) + await dash.addState([testDataStates['consent-managed']]) await dash.indicatesCookiesWereManaged() }) }) @@ -52,12 +52,12 @@ test.describe('cookie prompt management', () => { test.describe('non-cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.indicatesCookiesWereManaged() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates['consent-managed-configurable']]) + await dash.addState([testDataStates['consent-managed-configurable']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() @@ -66,12 +66,12 @@ test.describe('cookie prompt management', () => { test.describe('cosmetic', () => { test('primary screen', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.indicatesCookiesWereHidden() }) test('secondary screen', async ({ page }) => { const dash = await DashboardPage.windows(page) - await dash.addStates([testDataStates['consent-managed-configurable-cosmetic']]) + await dash.addState([testDataStates['consent-managed-configurable-cosmetic']]) await dash.viewCookiePromptManagement() await dash.disableCookiesInSettings() await dash.mocks.calledForOpenSettings() diff --git a/package-lock.json b/package-lock.json index f36949e4..3f9d8e0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,18 +6,18 @@ "packages": { "": { "name": "@duckduckgo/privacy-dashboard", - "version": "13", "devDependencies": { "@formatjs/intl-locale": "^3.0.7", "@material/ripple": "^14.0.0", "@material/switch": "^14.0.0", - "@playwright/test": "^1.34.3", + "@playwright/test": "^1.38.1", "@types/chrome": "^0.0.203", "@types/node": "18.16.16", + "@typescript-eslint/eslint-plugin": "^6.7.4", "chokidar-cli": "^3.0.0", "deep-freeze": "0.0.1", "duckduckgo-colors": "0.0.1", - "esbuild": "^0.17.19", + "esbuild": "^0.19.4", "eslint": "^8.41.0", "eslint-config-standard": "^17.1.0", "eventemitter2": "4.1.0", @@ -32,6 +32,7 @@ "prettier": "^2.7.1", "sass": "^1.32.4", "standard": "^17.1.0", + "tiny-invariant": "^1.3.1", "ts-to-zod": "^1.13.1", "typedoc": "^0.23.21", "typescript": "^4.9.3", @@ -85,9 +86,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.4.tgz", + "integrity": "sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==", "cpu": [ "arm" ], @@ -101,9 +102,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.4.tgz", + "integrity": "sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==", "cpu": [ "arm64" ], @@ -117,9 +118,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.4.tgz", + "integrity": "sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==", "cpu": [ "x64" ], @@ -133,9 +134,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.4.tgz", + "integrity": "sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==", "cpu": [ "arm64" ], @@ -149,9 +150,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.4.tgz", + "integrity": "sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==", "cpu": [ "x64" ], @@ -165,9 +166,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.4.tgz", + "integrity": "sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==", "cpu": [ "arm64" ], @@ -181,9 +182,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.4.tgz", + "integrity": "sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==", "cpu": [ "x64" ], @@ -197,9 +198,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.4.tgz", + "integrity": "sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==", "cpu": [ "arm" ], @@ -213,9 +214,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.4.tgz", + "integrity": "sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==", "cpu": [ "arm64" ], @@ -229,9 +230,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.4.tgz", + "integrity": "sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==", "cpu": [ "ia32" ], @@ -245,9 +246,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.4.tgz", + "integrity": "sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==", "cpu": [ "loong64" ], @@ -261,9 +262,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.4.tgz", + "integrity": "sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==", "cpu": [ "mips64el" ], @@ -277,9 +278,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.4.tgz", + "integrity": "sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==", "cpu": [ "ppc64" ], @@ -293,9 +294,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.4.tgz", + "integrity": "sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==", "cpu": [ "riscv64" ], @@ -309,9 +310,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.4.tgz", + "integrity": "sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==", "cpu": [ "s390x" ], @@ -325,9 +326,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.4.tgz", + "integrity": "sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==", "cpu": [ "x64" ], @@ -341,9 +342,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.4.tgz", + "integrity": "sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==", "cpu": [ "x64" ], @@ -357,9 +358,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.4.tgz", + "integrity": "sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==", "cpu": [ "x64" ], @@ -373,9 +374,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.4.tgz", + "integrity": "sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==", "cpu": [ "x64" ], @@ -389,9 +390,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.4.tgz", + "integrity": "sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==", "cpu": [ "arm64" ], @@ -405,9 +406,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.4.tgz", + "integrity": "sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==", "cpu": [ "ia32" ], @@ -421,9 +422,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.4.tgz", + "integrity": "sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==", "cpu": [ "x64" ], @@ -1318,22 +1319,18 @@ } }, "node_modules/@playwright/test": { - "version": "1.34.3", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.34.3.tgz", - "integrity": "sha512-zPLef6w9P6T/iT6XDYG3mvGOqOyb6eHaV9XtkunYs0+OzxBtrPAAaHotc0X+PJ00WPPnLfFBTl7mf45Mn8DBmw==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", "dev": true, "dependencies": { - "@types/node": "*", - "playwright-core": "1.34.3" + "playwright": "1.38.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "node": ">=16" } }, "node_modules/@types/chrome": { @@ -1378,9 +1375,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/json5": { @@ -1413,6 +1410,259 @@ "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==", "dev": true }, + "node_modules/@types/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", + "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/type-utils": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", + "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", + "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", + "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", + "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", + "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", + "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", + "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.4", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript/vfs": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz", @@ -2418,9 +2668,9 @@ } }, "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.4.tgz", + "integrity": "sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==", "dev": true, "hasInstallScript": true, "bin": { @@ -2430,28 +2680,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" + "@esbuild/android-arm": "0.19.4", + "@esbuild/android-arm64": "0.19.4", + "@esbuild/android-x64": "0.19.4", + "@esbuild/darwin-arm64": "0.19.4", + "@esbuild/darwin-x64": "0.19.4", + "@esbuild/freebsd-arm64": "0.19.4", + "@esbuild/freebsd-x64": "0.19.4", + "@esbuild/linux-arm": "0.19.4", + "@esbuild/linux-arm64": "0.19.4", + "@esbuild/linux-ia32": "0.19.4", + "@esbuild/linux-loong64": "0.19.4", + "@esbuild/linux-mips64el": "0.19.4", + "@esbuild/linux-ppc64": "0.19.4", + "@esbuild/linux-riscv64": "0.19.4", + "@esbuild/linux-s390x": "0.19.4", + "@esbuild/linux-x64": "0.19.4", + "@esbuild/netbsd-x64": "0.19.4", + "@esbuild/openbsd-x64": "0.19.4", + "@esbuild/sunos-x64": "0.19.4", + "@esbuild/win32-arm64": "0.19.4", + "@esbuild/win32-ia32": "0.19.4", + "@esbuild/win32-x64": "0.19.4" } }, "node_modules/escape-string-regexp": { @@ -5324,16 +5574,34 @@ "node": ">=6" } }, + "node_modules/playwright": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "dev": true, + "dependencies": { + "playwright-core": "1.38.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, "node_modules/playwright-core": { - "version": "1.34.3", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.3.tgz", - "integrity": "sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/portfinder": { @@ -6153,6 +6421,12 @@ "next-tick": "1" } }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "dev": true + }, "node_modules/tiny-worker": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", @@ -6202,6 +6476,18 @@ "nanobench": "^2.1.1" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-to-zod": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/ts-to-zod/-/ts-to-zod-1.13.1.tgz", @@ -6892,156 +7178,156 @@ } }, "@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.4.tgz", + "integrity": "sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.4.tgz", + "integrity": "sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.4.tgz", + "integrity": "sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.4.tgz", + "integrity": "sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.4.tgz", + "integrity": "sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.4.tgz", + "integrity": "sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.4.tgz", + "integrity": "sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.4.tgz", + "integrity": "sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.4.tgz", + "integrity": "sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.4.tgz", + "integrity": "sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.4.tgz", + "integrity": "sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.4.tgz", + "integrity": "sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.4.tgz", + "integrity": "sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.4.tgz", + "integrity": "sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.4.tgz", + "integrity": "sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.4.tgz", + "integrity": "sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.4.tgz", + "integrity": "sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.4.tgz", + "integrity": "sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.4.tgz", + "integrity": "sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.4.tgz", + "integrity": "sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.4.tgz", + "integrity": "sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.4.tgz", + "integrity": "sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==", "dev": true, "optional": true }, @@ -7768,14 +8054,12 @@ } }, "@playwright/test": { - "version": "1.34.3", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.34.3.tgz", - "integrity": "sha512-zPLef6w9P6T/iT6XDYG3mvGOqOyb6eHaV9XtkunYs0+OzxBtrPAAaHotc0X+PJ00WPPnLfFBTl7mf45Mn8DBmw==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", "dev": true, "requires": { - "@types/node": "*", - "fsevents": "2.3.2", - "playwright-core": "1.34.3" + "playwright": "1.38.1" } }, "@types/chrome": { @@ -7820,9 +8104,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "@types/json5": { @@ -7855,6 +8139,154 @@ "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==", "dev": true }, + "@types/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", + "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/type-utils": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", + "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", + "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", + "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/utils": "6.7.4", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", + "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", + "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/visitor-keys": "6.7.4", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", + "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.4", + "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/typescript-estree": "6.7.4", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", + "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.7.4", + "eslint-visitor-keys": "^3.4.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } + } + }, "@typescript/vfs": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.5.tgz", @@ -8642,33 +9074,33 @@ } }, "esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.4.tgz", + "integrity": "sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.19.4", + "@esbuild/android-arm64": "0.19.4", + "@esbuild/android-x64": "0.19.4", + "@esbuild/darwin-arm64": "0.19.4", + "@esbuild/darwin-x64": "0.19.4", + "@esbuild/freebsd-arm64": "0.19.4", + "@esbuild/freebsd-x64": "0.19.4", + "@esbuild/linux-arm": "0.19.4", + "@esbuild/linux-arm64": "0.19.4", + "@esbuild/linux-ia32": "0.19.4", + "@esbuild/linux-loong64": "0.19.4", + "@esbuild/linux-mips64el": "0.19.4", + "@esbuild/linux-ppc64": "0.19.4", + "@esbuild/linux-riscv64": "0.19.4", + "@esbuild/linux-s390x": "0.19.4", + "@esbuild/linux-x64": "0.19.4", + "@esbuild/netbsd-x64": "0.19.4", + "@esbuild/openbsd-x64": "0.19.4", + "@esbuild/sunos-x64": "0.19.4", + "@esbuild/win32-arm64": "0.19.4", + "@esbuild/win32-ia32": "0.19.4", + "@esbuild/win32-x64": "0.19.4" } }, "escape-string-regexp": { @@ -10752,10 +11184,20 @@ "load-json-file": "^5.2.0" } }, + "playwright": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.38.1" + } + }, "playwright-core": { - "version": "1.34.3", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.3.tgz", - "integrity": "sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", "dev": true }, "portfinder": { @@ -11334,6 +11776,12 @@ "next-tick": "1" } }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "dev": true + }, "tiny-worker": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", @@ -11377,6 +11825,13 @@ "nanobench": "^2.1.1" } }, + "ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "requires": {} + }, "ts-to-zod": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/ts-to-zod/-/ts-to-zod-1.13.1.tgz", diff --git a/package.json b/package.json index 872a20a8..234d6e33 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "scripts": { "build": "node scripts/build.js", - "build.debug": "node scripts/build.js -- --debug", + "build.debug": "node scripts/build.js --debug", "build.watch": "chokidar \"shared\" \"schema/*.json\" -c \"npm run build.debug\" --initial \"npm run build.debug\"", "docs": "typedoc", "docs.watch": "npm run docs -- --watch", @@ -22,28 +22,29 @@ "tsc": "tsc", "tsc.watch": "tsc --watch", "test": "npm run verify.local", - "test.unit": "node schema/schema.test.mjs && node shared/js/browser/utils/request-details.test.mjs && node shared/locales/validate-locales.test.mjs", + "test.unit": "node schema/schema.test.mjs && node shared/js/browser/utils/request-details.test.mjs && node shared/locales/validate-locales.test.mjs && node scripts/verify-artifacts.test.mjs", "test.int": "playwright test", "test.int.ui": "playwright test --ui", "test.int.headed": "playwright test --headed", "test.int.update-screenshots": "npm run test.int -- --update-snapshots", "test.int-debug": "playwright test --headed --debug", "test.clean-tree": "npm run build && sh scripts/check-for-changes.sh", - "verify.local": "npm run lint.fix; npm run prettier.fix; npm run test.unit; npm run build; npm run docs; npm run test.int", + "verify.artifacts": "node scripts/verify-artifacts.mjs", + "verify.local": "npm run lint.fix && npm run prettier.fix && npm run build && npm run test.unit && npm run docs && npm run test.int", "fetch-fonts": "mkdir -p build/app/public/font && curl -o build/app/public/font/ProximaNova-Reg-webfont.woff https://duckduckgo.com/font/ProximaNova-Reg-webfont.woff && curl -o build/app/public/font/ProximaNova-Sbold-webfont.woff https://duckduckgo.com/font/ProximaNova-Sbold-webfont.woff" }, "devDependencies": { "@formatjs/intl-locale": "^3.0.7", "@material/ripple": "^14.0.0", "@material/switch": "^14.0.0", - "@playwright/test": "^1.34.3", + "@playwright/test": "^1.38.1", "@types/chrome": "^0.0.203", "@types/node": "18.16.16", - "nanohtml": "^1.10.0", + "@typescript-eslint/eslint-plugin": "^6.7.4", "chokidar-cli": "^3.0.0", "deep-freeze": "0.0.1", "duckduckgo-colors": "0.0.1", - "esbuild": "^0.17.19", + "esbuild": "^0.19.4", "eslint": "^8.41.0", "eslint-config-standard": "^17.1.0", "eventemitter2": "4.1.0", @@ -53,10 +54,12 @@ "is-plain-object": "5.0.0", "jquery": "3.6.2", "json-schema-to-typescript": "^11.0.2", + "nanohtml": "^1.10.0", "normalize.scss": "0.1.0", "prettier": "^2.7.1", "sass": "^1.32.4", "standard": "^17.1.0", + "tiny-invariant": "^1.3.1", "ts-to-zod": "^1.13.1", "typedoc": "^0.23.21", "typescript": "^4.9.3", diff --git a/scripts/build.js b/scripts/build.js index d369d4f6..6fd60a89 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -29,6 +29,7 @@ const env = { const name = basename(job, '.scss') const outfile = join(output, name + '.css') const args = ['sass', job, outfile, '--load-path node_modules'] + if (debug) args.push('--embed-sources') if (!debug) args.push('--no-source-map') return exec(args.join(' '), { cwd: BASE, diff --git a/scripts/bundle.mjs b/scripts/bundle.mjs index b49773aa..9cbf0034 100644 --- a/scripts/bundle.mjs +++ b/scripts/bundle.mjs @@ -5,6 +5,8 @@ import { cwd, debug } from './utils.mjs' const CWD = cwd(import.meta.url) const BASE = join(CWD, '..') const LOCALES_BASE = join(BASE, 'shared/locales') +const IS_PROD = process.env.NODE_ENV === 'production' +console.log({ IS_PROD }) /** * Bundle the base and polyfill files. @@ -27,6 +29,10 @@ async function init() { bundle: true, outfile: manifest.base.output, sourcemap: debug ? 'linked' : undefined, + dropLabels: IS_PROD ? ['$TEST'] : [], + loader: { + '.js': 'jsx', + }, plugins: [ { name: 'require-globify-shim', diff --git a/scripts/duplicate-html.js b/scripts/duplicate-html.js index 4a3ed5f8..2dbfc659 100644 --- a/scripts/duplicate-html.js +++ b/scripts/duplicate-html.js @@ -5,6 +5,7 @@ const CWD = new URL('..', import.meta.url).pathname const base = join(CWD, 'build/app/html/popup.html') const html = readFileSync(base, 'utf8') const platforms = ['ios', 'macos', 'browser', 'android', 'windows'] + for (const platform of platforms) { const relative = join('build/app/html', platform + '.html') const outFile = join(CWD, relative) diff --git a/scripts/verify-artifacts.test.mjs b/scripts/verify-artifacts.test.mjs new file mode 100644 index 00000000..e5a4933a --- /dev/null +++ b/scripts/verify-artifacts.test.mjs @@ -0,0 +1,12 @@ +import { it } from 'node:test' +import assert from 'node:assert' +import { fileURLToPath } from 'node:url' +import { join, dirname } from 'node:path' +import { readFileSync } from 'node:fs' + +const __dirname = dirname(fileURLToPath(import.meta.url)) + +it('removes mock data for production build', () => { + const contents = readFileSync(join(__dirname, '..', 'build/app/public/js/base.js'), 'utf8') + assert.ok(!contents.includes('mockDataProvider'), 'artifact must not contain mockDataProvider. Please run `npm run build`') +}) diff --git a/shared/html/iframe.html b/shared/html/iframe.html index aa2cc96a..c7880d50 100644 --- a/shared/html/iframe.html +++ b/shared/html/iframe.html @@ -53,11 +53,17 @@
diff --git a/shared/js/browser/android-communication.js b/shared/js/browser/android-communication.js index 305578c1..b946f625 100644 --- a/shared/js/browser/android-communication.js +++ b/shared/js/browser/android-communication.js @@ -23,7 +23,6 @@ let channel = null const backgroundMessage = (backgroundModel) => { channel = backgroundModel } - const getBackgroundTabDataPromises = [] let trackerBlockingData let permissionsData diff --git a/shared/js/browser/common.js b/shared/js/browser/common.js index f01ba92d..44b085a9 100644 --- a/shared/js/browser/common.js +++ b/shared/js/browser/common.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /** * @module common */ diff --git a/shared/js/browser/communication.js b/shared/js/browser/communication.js index a7bcedd4..5f0c3f94 100644 --- a/shared/js/browser/communication.js +++ b/shared/js/browser/communication.js @@ -4,20 +4,16 @@ import * as iosComms from './ios-communication.js' import * as androidComms from './android-communication.js' import * as windowsComms from './windows-communication.js' import * as macosComms from './macos-communication.js' -import * as exampleComms from './example-communication.js' -import { getOverrides } from './utils/overrides' +import { installMocks } from './utils/communication-mocks.mjs' let defaultComms -/** @type {import('../ui/platform-features').Platform} */ -const platform = { name: 'example' } +/** @type {import('../ui/platform-features.mjs').Platform} */ +const platform = { + name: 'browser', +} -const overrides = getOverrides(window.location.search) -if (overrides.platform && overrides.platform !== 'example') { - window.environmentOverride = overrides.platform - defaultComms = exampleComms - platform.name = overrides.platform -} else if (isIOS()) { +if (isIOS()) { defaultComms = iosComms platform.name = 'ios' } else if (isBrowser()) { @@ -32,13 +28,14 @@ if (overrides.platform && overrides.platform !== 'example') { } else if (isMacos()) { defaultComms = macosComms platform.name = 'macos' -} else { - defaultComms = exampleComms } if (!defaultComms) throw new Error('unsupported environment') -// @ts-ignore +// in test environments, install mocks and deliver initial data +// eslint-disable-next-line no-unused-labels +$TEST: typeof window.__ddg_integration_test === 'undefined' && installMocks(platform).catch(console.error) + defaultComms.setup() export { platform } diff --git a/shared/js/browser/example-communication.js b/shared/js/browser/example-communication.js deleted file mode 100644 index aeb68a21..00000000 --- a/shared/js/browser/example-communication.js +++ /dev/null @@ -1,142 +0,0 @@ -import { - CheckBrokenSiteReportHandledMessage, - FetchBurnOptions, - OpenSettingsMessages, - RefreshEmailAliasMessage, - SetListsMessage, - setupColorScheme, - setupMutationObserver, -} from './common.js' -import { getOverrides } from './utils/overrides' - -// Overrides based on URL params -const overrides = getOverrides(window.location.search) - -let channel = null - -/** - * @type {import("./common.js").fetcher} - */ -export async function fetch(message) { - if (message instanceof SetListsMessage) { - console.warn('doing nothing by default with `setList`') - } - - if (message instanceof RefreshEmailAliasMessage) { - if (overrides.platform === 'browser') { - return Promise.resolve({ - privateAddress: 'dax123456', - }) - } - } - - if (message instanceof CheckBrokenSiteReportHandledMessage) { - if (overrides.platform === 'ios' || overrides.platform === 'android') { - return true - } else { - return false - } - } - - if (message instanceof OpenSettingsMessages) { - if (overrides.platform === 'ios' || overrides.platform === 'android') { - return true - } else { - return false - } - } - - if (message instanceof FetchBurnOptions) { - const clearHistory = true - const tabClearEnabled = true - return Promise.resolve({ - options: [ - { - name: 'CurrentSite', - options: { - origins: ['https://example.com/'], - }, - descriptionStats: { - clearHistory, - site: 'example.com', - openTabs: tabClearEnabled ? 1 : undefined, - cookies: 1, - pinnedTabs: 1, - }, - }, - { - name: 'LastHour', - options: { - since: Date.now(), - }, - descriptionStats: { - clearHistory, - duration: 'hour', - openTabs: tabClearEnabled ? 5 : undefined, - cookies: 23, - pinnedTabs: 1, - }, - }, - { - name: 'AllTime', - options: {}, - descriptionStats: { - clearHistory, - duration: 'all', - openTabs: tabClearEnabled ? 5 : undefined, - cookies: 1000, - pinnedTabs: 1, - }, - }, - ], - }) - } - - console.log('fetch - Not implemented', message) -} - -export function backgroundMessage(backgroundModel) { - console.log('backgroundMessage - setting local channel') - channel = backgroundModel -} - -/** - * @returns {Promise<{ - * tab: import('./utils/request-details.mjs').TabData, - * emailProtectionUserData: import('../../../schema/__generated__/schema.types').EmailProtectionUserData | undefined, - * fireButton: { enabled: boolean } - * }>} - */ -export async function getBackgroundTabData() { - return { - tab: overrides.tab, - emailProtectionUserData: overrides.emailProtectionUserData, - fireButton: { - enabled: overrides.fireButtonEnabled, - }, - } -} - -export function setup() { - // set initial colour scheme - const setColorScheme = setupColorScheme() - setColorScheme(overrides.theme) - - setupMutationObserver((height) => { - console.log('Window height change:', height) - }) -} - -export function openOptionsPage() { - console.warn('should open options page here') -} - -export function search(query) { - console.warn('should open search for ', JSON.stringify(query)) -} - -if (new URLSearchParams(window.location.search).has('continuous')) { - setInterval(() => { - channel?.send('updateTabData') - }, 200) -} diff --git a/shared/js/browser/ios-communication.js b/shared/js/browser/ios-communication.js index 78668abf..9be16306 100644 --- a/shared/js/browser/ios-communication.js +++ b/shared/js/browser/ios-communication.js @@ -21,6 +21,7 @@ * * @category integrations */ +import invariant from 'tiny-invariant' import { CheckBrokenSiteReportHandledMessage, CloseMessage, setupColorScheme } from './common.js' import { backgroundMessage, getBackgroundTabData, fetch as macosFetch, setupShared } from './macos-communication.js' @@ -46,6 +47,7 @@ export function setup() { * ``` */ export function privacyDashboardClose(args) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardClose.postMessage(args) } @@ -61,6 +63,7 @@ export function privacyDashboardClose(args) { * ``` */ export function privacyDashboardShowReportBrokenSite(args) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardShowReportBrokenSite.postMessage(args) } diff --git a/shared/js/browser/macos-communication.js b/shared/js/browser/macos-communication.js index 1b452884..9ee0d9b7 100644 --- a/shared/js/browser/macos-communication.js +++ b/shared/js/browser/macos-communication.js @@ -15,6 +15,7 @@ * @category integrations */ +import invariant from 'tiny-invariant' import { cookiePromptManagementStatusSchema, localeSettingsSchema, @@ -210,6 +211,7 @@ async function fetch(message) { // `allowlisted: true` means the user disabled protections. // so `isProtected` is the opposite of `allowlisted`. const isProtected = value === false + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardSetProtection.postMessage(isProtected) } } @@ -221,6 +223,7 @@ async function fetch(message) { } if (message instanceof UpdatePermissionMessage) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardSetPermission.postMessage({ permission: message.id, value: message.value, @@ -249,6 +252,7 @@ const getBackgroundTabData = () => { * @category Webkit Message Handlers */ export function privacyDashboardOpenUrlInNewTab(args) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardOpenUrlInNewTab.postMessage({ url: args.url, }) @@ -260,6 +264,7 @@ export function privacyDashboardOpenUrlInNewTab(args) { * @category Webkit Message Handlers */ export function privacyDashboardOpenSettings(args) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardOpenSettings.postMessage({ target: args.target, }) @@ -271,6 +276,7 @@ export function privacyDashboardOpenSettings(args) { * @category Webkit Message Handlers */ export function privacyDashboardSubmitBrokenSiteReport(report) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardSubmitBrokenSiteReport.postMessage({ category: report.category, description: report.description, @@ -284,6 +290,7 @@ export function privacyDashboardSubmitBrokenSiteReport(report) { */ export function privacyDashboardSetSize(payload) { if (!isIOS()) { + invariant(window.webkit?.messageHandlers, 'webkit.messageHandlers required') window.webkit.messageHandlers.privacyDashboardSetSize.postMessage(payload) } } diff --git a/shared/js/browser/utils/communication-mocks.mjs b/shared/js/browser/utils/communication-mocks.mjs new file mode 100644 index 00000000..c3a9c720 --- /dev/null +++ b/shared/js/browser/utils/communication-mocks.mjs @@ -0,0 +1,289 @@ +/** + * @param {object} params + * @param {import("../../ui/views/tests/generate-data.mjs").MockData} params.state + * @param {import('../../ui/platform-features.mjs').Platform} params.platform + * @param {Record} params.messages + */ +export async function mockDataProvider(params) { + const { state, platform, messages } = params + + // ensure certain globals are assigned. This is how the browser/extension mocks work + Object.assign(window.__playwright.messages, messages) + + // on windows, all data is delivered through a single API call. + if (platform?.name === 'windows') { + if (!window.__playwright.messages.windowsViewModel) throw new Error('missing `windowsViewModel` on messages') + for (const listener of window.__playwright.listeners || []) { + listener({ + data: window.__playwright.messages.windowsViewModel, + }) + } + return + } + + // in the 'browser/extension' scenario, we trigger the `updateTabData` event, and the Object.assign + // above takes care of ensuring the correct data is available on `window.__playwright.messages.x` + if (platform?.name === 'browser') { + for (const listener of window.__playwright.listeners || []) { + listener({ updateTabData: true }, { id: 'test' }) + } + return + } + + // If we get here, we're on ios/android or macOS - where everything is delivered through window methods + if (state.cookiePromptManagementStatus) { + window.onChangeConsentManaged(state.cookiePromptManagementStatus) + } + window.onChangeParentEntity(state.parentEntity) + window.onChangeProtectionStatus(state.protections) + window.onChangeUpgradedHttps(state.upgradedHttps) + window.onChangeCertificateData({ + secCertificateViewModels: state.certificate, + }) + window.onChangeLocale?.(state.localeSettings) + window.onChangeRequestData(state.url, { requests: state.requests || [] }) +} + +export function windowsMockApis() { + try { + if (!window.chrome) { + // @ts-ignore + window.chrome = {} + } + window.__playwright = { + listeners: [], + messages: {}, + mocks: { + outgoing: [], + incoming: [], + }, + calls: [], + } + // override some methods on window.chrome.runtime to fake the incoming/outgoing messages + window.chrome.webview = { + // @ts-ignore + addEventListener: (messageName, listener) => { + window.__playwright.listeners?.push(listener) + }, + postMessage(arg) { + window.__playwright.mocks.outgoing.push([arg.Name, arg]) + }, + } + // console.log('window.chrome.webview', window.chrome.webview) + } catch (e) { + console.error("❌couldn't set up mocks") + console.error(e) + } +} + +export function webkitMockApis() { + try { + window.__playwright = { + messages: {}, + mocks: { + outgoing: [], + incoming: [], + }, + calls: [], + } + window.webkit = { + messageHandlers: { + privacyDashboardShowReportBrokenSite: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardShowReportBrokenSite', arg]) + }, + }, + privacyDashboardOpenUrlInNewTab: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardOpenUrlInNewTab', arg]) + }, + }, + privacyDashboardOpenSettings: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardOpenSettings', arg]) + }, + }, + privacyDashboardSubmitBrokenSiteReport: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardSubmitBrokenSiteReport', arg]) + }, + }, + privacyDashboardSetSize: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardSetSize', arg]) + }, + }, + privacyDashboardClose: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardClose', arg]) + }, + }, + privacyDashboardSetProtection: { + postMessage: (arg) => { + window.__playwright.mocks.outgoing.push(['privacyDashboardSetProtection', arg]) + }, + }, + }, + } + } catch (e) { + console.error("❌couldn't set up mocks") + console.error(e) + } +} + +export function mockAndroidApis() { + try { + window.__playwright = { + messages: {}, + mocks: { + outgoing: [], + incoming: [], + }, + calls: [], + } + window.PrivacyDashboard = { + showBreakageForm(arg) { + window.__playwright.mocks.outgoing.push(['showBreakageForm', arg]) + }, + openInNewTab(arg) { + window.__playwright.mocks.outgoing.push(['openInNewTab', arg]) + }, + openSettings(arg) { + window.__playwright.mocks.outgoing.push(['openSettings', arg]) + }, + close(arg) { + window.__playwright.mocks.outgoing.push(['close', arg]) + }, + toggleAllowlist(arg) { + window.__playwright.mocks.outgoing.push(['toggleAllowlist', arg]) + }, + } + } catch (e) { + console.error("❌couldn't set up mocks") + console.error(e) + } +} + +export function mockBrowserApis() { + const messages = { + submitBrokenSiteReport: {}, + setLists: {}, + search: {}, + openOptions: {}, + setBurnDefaultOption: {}, + doBurn: {}, + } + try { + if (!window.chrome) { + // @ts-ignore + window.chrome = { + // @ts-ignore + permissions: { + // eslint-disable-next-line n/no-callback-literal + request: (permissions, cb) => cb && cb(true), + }, + } + } + window.__playwright = { + messages, + mocks: { + outgoing: [], + incoming: [], + }, + calls: [], + listeners: [], + } + + // override some methods on window.chrome.runtime to fake the incoming/outgoing messages + window.chrome.runtime = { + id: 'test', + async sendMessage(message, cb) { + function respond(fn, timeout = 100) { + setTimeout(() => { + fn() + }, timeout) + } + + // does the incoming message match one that's been mocked here? + const matchingMessage = window.__playwright.messages[message.messageType] + if (matchingMessage) { + window.__playwright.mocks.outgoing.push([message.messageType, message]) + respond(() => cb(matchingMessage), 200) + } else { + setTimeout(() => { + const matchingMessage = window.__playwright.messages[message.messageType] + if (matchingMessage) { + window.__playwright.mocks.outgoing.push([message.messageType, message]) + respond(() => cb(matchingMessage), 0) + } else { + console.trace(`❌ [(mocks): window.chrome.runtime] Missing support for ${JSON.stringify(message)}`) + } + }, 200) + } + }, + // @ts-ignore + onMessage: { + addListener(listener) { + window.__playwright.listeners?.push(listener) + }, + }, + } + } catch (e) { + console.error("❌couldn't set up browser mocks") + console.error(e) + } +} + +/** + * @param {import("../../ui/platform-features.mjs").Platform} platform + * @return {Promise} + */ +export async function installMocks(platform) { + if (window.__playwright) console.log('❌ mocked already there') + if (platform.name === 'windows') { + windowsMockApis() + } else if (platform.name === 'ios' || platform.name === 'macos') { + webkitMockApis() + } else if (platform.name === 'android') { + mockAndroidApis() + } else if (platform.name === 'browser') { + mockBrowserApis() + } + + const { testDataStates } = await import('../../ui/views/tests/states-with-fixtures') + const stateFromUrl = new URLSearchParams(window.location.search).get('state') + + let mock + if (stateFromUrl && stateFromUrl in testDataStates) { + mock = testDataStates[stateFromUrl] + } else { + mock = testDataStates.protectionsOn_blocked + console.warn('state not found, falling back to default. state: ', 'protectionsOn_blocked') + } + + console.groupCollapsed(`${platform.name} open for more Dashboard States`) + const urls = Object.keys(testDataStates).map((key) => { + const clone = new URL(location.href) + clone.searchParams.set('state', key) + return clone.href + }) + for (let url of urls) { + console.log(url) + } + console.groupEnd() + + let messages = {} + if (platform.name === 'browser') { + messages['getBurnOptions'] = mock.toBurnOptions() + messages['getPrivacyDashboardData'] = mock.toExtensionDashboardData() + } + if (platform.name === 'windows') { + messages['windowsViewModel'] = mock.toWindowsViewModel() + } + + await mockDataProvider({ + state: mock, + platform: platform, + messages, + }) +} diff --git a/shared/js/browser/utils/overrides.js b/shared/js/browser/utils/overrides.js deleted file mode 100644 index 6f981bd0..00000000 --- a/shared/js/browser/utils/overrides.js +++ /dev/null @@ -1,144 +0,0 @@ -import { isValidPlatform } from '../../ui/environment-check' -import { testDataStates } from '../../ui/views/tests/states-with-fixtures' -import { protectionsOff } from '../../ui/views/tests/toggle-protections.mjs' -import { createRequestDetails, createTabData } from './request-details.mjs' -import { Protections } from './protections.mjs' - -/** - * The purpose of this function is to allow URL parameters to override - * test data when viewing the 'example' build. - * - * Eg: after running 'npm run preview.example', you could then set the theme - * by providing '?theme=dark' as a url parameter - * - * See the 'Emulating different platforms/scenarios' section in the README.md file - */ -/** - * @typedef Overrides - * @property {import('../../ui/views/tests/generate-data.mjs').TabData} tab - * @property {import('../../../../schema/__generated__/schema.types').DetectedRequest[]} requests - * @property {import('../../ui/platform-features').Platform["name"]} platform - * @property {import('../../../../schema/__generated__/schema.types').EmailProtectionUserData | undefined} emailProtectionUserData - * @property {("dark" | "light") | undefined} theme - * @property {boolean} fireButtonEnabled - * @param {string} searchString - * @returns {Overrides} - */ -export function getOverrides(searchString) { - /** @type {Overrides} */ - const overrides = { - requests: [], - tab: createTabData('https://example.com', false, Protections.default(), { requests: [] }), - platform: 'example', - emailProtectionUserData: undefined, - theme: undefined, - fireButtonEnabled: false, - } - - const params = new URLSearchParams(searchString) - - const stateKey = params.get('state') - if (stateKey) { - const match = testDataStates[stateKey] - if (match) { - overrides.requests = match.requests - overrides.tab.requestDetails = createRequestDetails(match.requests, []) - overrides.tab.parentEntity = match.parentEntity - overrides.tab.url = match.url - overrides.tab.upgradedHttps = match.upgradedHttps - overrides.tab.certificate = match.certificate - overrides.tab.cookiePromptManagementStatus = match.cookiePromptManagementStatus - overrides.tab.locale = match.localeSettings.locale - if (match.allowlisted) { - overrides.requests = protectionsOff(overrides.requests) - overrides.tab.requestDetails = createRequestDetails(overrides.requests, []) - overrides.tab.protections = new Protections(false, ['contentBlocking'], true, false) - } - if (match.contentBlockingException) { - overrides.requests = protectionsOff(overrides.requests) - overrides.tab.requestDetails = createRequestDetails(overrides.requests, []) - overrides.tab.protections = new Protections(false, [], false, false) - } - if (match.denylisted) { - overrides.requests = protectionsOff(overrides.requests) - overrides.tab.requestDetails = createRequestDetails(overrides.requests, []) - overrides.tab.protections = new Protections(false, [], false, true) - } - } - } - const platformParam = params.get('platform') - if (platformParam && isValidPlatform(platformParam)) { - overrides.platform = platformParam - document.body.classList.remove('environment--example') - document.body.classList.add(`environment--${overrides.platform}`) - window.environmentOverride = overrides.platform - } - - if (params.has('theme')) { - if (params.get('theme') === 'light') { - overrides.theme = 'light' - } else if (params.get('theme') === 'dark') { - overrides.theme = 'dark' - } - if (overrides.theme?.toLowerCase() === 'dark') { - document.body.classList.add('body--theme-dark') - } else { - document.body.classList.remove('body--theme-dark') - } - } - - if (params.get('specialDomainName') || params.get('specialDomain')) { - overrides.tab.specialDomainName = 'extensions' - } - - if (params.get('locale')) { - overrides.tab.locale = params.get('locale') - } - - if (params.get('consentManaged')) { - overrides.tab.cookiePromptManagementStatus = { - consentManaged: true, - cosmetic: false, - optoutFailed: false, - configurable: false, - } - if (params.get('consentConfigurable')) { - overrides.tab.cookiePromptManagementStatus.configurable = true - } - if (params.get('consentCosmetic')) { - overrides.tab.cookiePromptManagementStatus.cosmetic = true - } - } - - // browser-specific overrides - if (overrides.platform === 'browser') { - // supports email - overrides.tab.emailProtection = {} - // supports search - overrides.tab.search = {} - // supports CTA screens - overrides.tab.ctaScreens = {} - // extensions can't handle 'permissions' - overrides.tab.permissions = [] - - // emulate a user being signed in to email protection - if (params.get('emailUser') === 'true') { - overrides.emailProtectionUserData = { - nextAlias: '123456_next', - } - } - if (params.get('fireButton') === 'true') { - overrides.fireButtonEnabled = true - } - } - - if (overrides.platform === 'ios' || overrides.platform === 'macos') { - overrides.tab.platformLimitations = true - } - - if (overrides.platform === 'ios' || overrides.platform === 'android') { - overrides.tab.permissions = [] - } - - return overrides -} diff --git a/shared/js/ui/base/index.js b/shared/js/ui/base/index.js index 2efe4a53..25317598 100644 --- a/shared/js/ui/base/index.js +++ b/shared/js/ui/base/index.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ window.onunhandledrejection = (event) => { console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`) } diff --git a/shared/js/ui/base/localize.js b/shared/js/ui/base/localize.js index 01640f23..1d880570 100644 --- a/shared/js/ui/base/localize.js +++ b/shared/js/ui/base/localize.js @@ -1,6 +1,6 @@ import i18next from 'i18next' import ICU from 'i18next-icu' -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars import siteTranslations from '../../../locales/en/site.json' // @ts-ignore diff --git a/shared/js/ui/base/page.js b/shared/js/ui/base/page.js index 4186f3a7..e286f98e 100644 --- a/shared/js/ui/base/page.js +++ b/shared/js/ui/base/page.js @@ -3,10 +3,10 @@ import * as mixins from './mixins/index.js' import * as store from './store.js' /** - * @param ops + * @param _ops * @constructor */ -function BasePage(ops) { +function BasePage(_ops) { this.views = {} this.store = store // @ts-ignore diff --git a/shared/js/ui/environment-check.js b/shared/js/ui/environment-check.js index 4ed5f2dd..4c15518b 100644 --- a/shared/js/ui/environment-check.js +++ b/shared/js/ui/environment-check.js @@ -3,9 +3,6 @@ * @returns {boolean} */ export function isEnvironment(platform) { - if (platform === window.environmentOverride) { - return true - } return document.body.classList.contains(`environment--${platform}`) } export const isIOS = () => isEnvironment('ios') @@ -15,11 +12,9 @@ export const isWindows = () => isEnvironment('windows') export const isMacos = () => isEnvironment('macos') /** - * @returns {import("./platform-features").Platform["name"] | null} + * @returns {import("./platform-features.mjs").Platform["name"] | null} */ function currentPlatform() { - const windowVar = window.environmentOverride - if (windowVar && isValidPlatform(windowVar)) return windowVar const matchingClass = [...document.body.classList].find((x) => x.startsWith('environment--')) if (matchingClass) { const platform = matchingClass.slice(13) @@ -30,13 +25,13 @@ function currentPlatform() { return null } /** - * @param {import("./platform-features").Platform["name"] | string | null | undefined} name - * @returns {name is import("./platform-features").Platform["name"]} + * @param {import("./platform-features.mjs").Platform["name"] | string | null | undefined} name + * @returns {name is import("./platform-features.mjs").Platform["name"]} */ export function isValidPlatform(name) { if (!name) throw new Error(`not a valid platform name ${name}`) - /** @type {import("./platform-features").Platform["name"][]} */ - const names = ['ios', 'android', 'macos', 'browser', 'windows', 'example'] + /** @type {import("./platform-features.mjs").Platform["name"][]} */ + const names = ['ios', 'android', 'macos', 'browser', 'windows'] // @ts-ignore if (names.includes(name)) { return true @@ -44,7 +39,7 @@ export function isValidPlatform(name) { throw new Error('nope!') } -/** @type {import("./platform-features").Platform["name"] | undefined | null} */ +/** @type {import("./platform-features.mjs").Platform["name"] | undefined | null} */ let lastKnownPlatformName /** diff --git a/shared/js/ui/models/background-message.js b/shared/js/ui/models/background-message.js index a602feba..5e7bafd3 100644 --- a/shared/js/ui/models/background-message.js +++ b/shared/js/ui/models/background-message.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ import $ from 'jquery' import Parent from '../base/model.js' import browserUIWrapper from '../../browser/communication.js' diff --git a/shared/js/ui/models/cookie-prompt.js b/shared/js/ui/models/cookie-prompt.js index e1a10247..3d079aff 100644 --- a/shared/js/ui/models/cookie-prompt.js +++ b/shared/js/ui/models/cookie-prompt.js @@ -10,7 +10,7 @@ export function CookiePromptModel(attrs) { CookiePromptModel.prototype = $.extend({}, Parent.prototype, { modelName: 'cookiePrompt', - openSettings: function (category) { + openSettings: function (_category) { this.fetch( new OpenSettingsMessages({ target: 'cpm', diff --git a/shared/js/ui/models/site-company-list.js b/shared/js/ui/models/site-company-list.js index d63715a6..f4525b15 100644 --- a/shared/js/ui/models/site-company-list.js +++ b/shared/js/ui/models/site-company-list.js @@ -16,7 +16,7 @@ SiteCompanyList.prototype = $.extend({}, Parent.prototype, normalizeCompanyName, /** @this {any} */ fetchAsyncData: function () { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { browserUIWrapper.getBackgroundTabData().then(({ tab }) => { if (tab) { this.tab = tab diff --git a/shared/js/ui/models/site.js b/shared/js/ui/models/site.js index c7ae8ded..d4208b67 100644 --- a/shared/js/ui/models/site.js +++ b/shared/js/ui/models/site.js @@ -3,7 +3,7 @@ import Parent from '../base/model.js' import { httpsMessages } from '../../../data/constants' import browserUIWrapper, { platform } from '../../browser/communication.js' import { i18n } from '../base/localize.js' -import { createPlatformFeatures } from '../platform-features' +import { createPlatformFeatures } from '../platform-features.mjs' import { CheckBrokenSiteReportHandledMessage, CloseMessage, SetListsMessage, UpdatePermissionMessage } from '../../browser/common.js' // We consider major tracker networks as those found on this percentage of sites @@ -166,7 +166,7 @@ Site.prototype = $.extend({}, Parent.prototype, { // calls `this.set()` to trigger view re-rendering /** @this {{tab: import('../../browser/utils/request-details.mjs').TabData} & Record} */ - update: function (ops) { + update: function () { if (!this.acceptingUpdates) { console.log('not updating because acceptingUpdates was false') return diff --git a/shared/js/ui/platform-features.js b/shared/js/ui/platform-features.mjs similarity index 93% rename from shared/js/ui/platform-features.js rename to shared/js/ui/platform-features.mjs index 4c8a607c..0a23a503 100644 --- a/shared/js/ui/platform-features.js +++ b/shared/js/ui/platform-features.mjs @@ -4,7 +4,7 @@ /** * @typedef Platform - * @property {"ios" | "android" | "macos" | "browser" | "windows" | "example"} name + * @property {"ios" | "android" | "macos" | "browser" | "windows"} name */ /** @@ -13,7 +13,7 @@ */ export function createPlatformFeatures(platform) { /** @type {Platform["name"][]} */ - const desktop = ['windows', 'macos', 'browser', 'example'] + const desktop = ['windows', 'macos', 'browser'] return new PlatformFeatures({ spinnerFollowingProtectionsToggle: platform.name !== 'android' && platform.name !== 'windows', supportsHover: desktop.includes(platform.name), diff --git a/shared/js/ui/templates/key-insights.js b/shared/js/ui/templates/key-insights.js index 209321cd..06a78efc 100644 --- a/shared/js/ui/templates/key-insights.js +++ b/shared/js/ui/templates/key-insights.js @@ -8,7 +8,7 @@ import { getColorId } from './shared/utils.js' /** * @param {object} ops - * @param {import("../models/site.js").default} ops.model + * @param {import("../models/site.js").PublicSiteModel} ops.model * @param {import("jquery")} ops.appendTo * @param {any} ops.store * @constructor @@ -87,6 +87,7 @@ export function renderKeyInsight() { if (model.isDenylisted) { text = i18n.t('site:protectionsDisabledRemoteOverride.title') } + // prettier-ignore return html`
@@ -112,8 +113,8 @@ export function renderKeyInsight() { ${description( raw( i18n.t('site:majorTrackingNetworkDesc.title', { - companyDisplayName: company.displayName, - companyPrevalence: Math.round(company.prevalence), + companyDisplayName: company?.displayName, + companyPrevalence: Math.round(company?.prevalence ?? 0), blocked: model.tab.requestDetails.blocked.entitiesCount > 0, }) ) diff --git a/shared/js/ui/templates/shared/status-list.js b/shared/js/ui/templates/shared/status-list.js deleted file mode 100644 index 0d012047..00000000 --- a/shared/js/ui/templates/shared/status-list.js +++ /dev/null @@ -1,18 +0,0 @@ -import html from 'nanohtml' - -export default function (items, extraClasses) { - extraClasses = extraClasses || '' - - return html`
    - ${items.map(renderItem)} -
` -} - -function renderItem(item) { - return html`
  • - ${item.msg} -
  • ` -} diff --git a/shared/js/ui/templates/site.js b/shared/js/ui/templates/site.js index bb6e54e2..e5ee2d6c 100644 --- a/shared/js/ui/templates/site.js +++ b/shared/js/ui/templates/site.js @@ -1,4 +1,4 @@ -import bel from 'nanohtml' +import html from 'nanohtml' import { i18n } from '../base/localize.js' import { protectionToggle } from './shared/protection-toggle' import { topNav } from './shared/top-nav' @@ -9,14 +9,14 @@ export default function () { const supportsCtaScreens = Boolean(this.model.tab?.ctaScreens) if (this.model.tab.error) { const errorText = i18n.t('site:errorMessage.title') - return bel` + return html`
    ${renderSearchWrapper(this.model)}
    -
    +

    ${errorText}

    -
    +
    @@ -24,45 +24,38 @@ export default function () { ` } if (this.model.disabled && supportsCtaScreens) { - return bel` + return html`
    ${renderSearchWrapper(this.model)}
    -
    - ${renderEmailWrapper(this.model)} -
    +
    ${renderEmailWrapper(this.model)}
    ` } const permissions = localizePermissions(this.model.permissions) - return bel` -
    - ${renderSearchWrapper(this.model)} - ${topNav({ view: 'primary' })} -
    0}> -
    -
    + return html`
    + ${renderSearchWrapper(this.model)} ${topNav({ view: 'primary' })} +
    0}> +
    +
    -
    - +
    + ${protectionToggle(this.model)}
    -
    - ${renderEmailWrapper(this.model)} - ${renderReportButton()} -
    +
    ${renderEmailWrapper(this.model)} ${renderReportButton()}
    ${permissions.length ? outer({ children: renderManagePermissions(this.model) }) : null}
    ` } function outer(props) { - return bel`
    ${props.children}
    ` + return html`
    ${props.children}
    ` } /** @@ -70,7 +63,7 @@ function outer(props) { */ function renderSearchWrapper(model) { if (model.tab?.search) { - return bel`
    ` + return html`
    ` } } @@ -79,7 +72,7 @@ function renderSearchWrapper(model) { */ function renderEmailWrapper(model) { if (model.tab?.emailProtection) { - return bel`
    ` + return html`
    ` } } @@ -93,35 +86,34 @@ function renderManagePermissions(model) { const localizedPerms = localizePermissions(model.permissions) - return bel` -
      -
    • - ${localizedPerms.map(({ key: permissionId, title, permission, options }, index) => { - if (!model.permissions) return '' // todo(Shane): typescript issue - return bel`
      - -
      ` - })} -
    • -
    ` + return html`
      +
    • + ${localizedPerms.map(({ key: permissionId, title, permission, options }) => { + if (!model.permissions) return '' // todo(Shane): typescript issue + return html`
      + +
      ` + })} +
    • +
    ` } function renderReportButton() { - return bel`` + return html`` } /** diff --git a/shared/js/ui/views/failover.js b/shared/js/ui/views/failover.js deleted file mode 100644 index d10a6694..00000000 --- a/shared/js/ui/views/failover.js +++ /dev/null @@ -1,12 +0,0 @@ -import $ from 'jquery' -import Parent from '../base/view.js' - -function Failover(ops) { - this.template = ops.template - this.message = ops.message - Parent.call(this, ops) -} - -Failover.prototype = $.extend({}, Parent.prototype, {}) - -export default Failover diff --git a/shared/js/ui/views/fire-dialog.js b/shared/js/ui/views/fire-dialog.js index 28685862..562abb9b 100644 --- a/shared/js/ui/views/fire-dialog.js +++ b/shared/js/ui/views/fire-dialog.js @@ -45,7 +45,7 @@ FireDialog.prototype = $.extend({}, Parent.prototype, { document.getElementById('fire-button-container')?.remove() }, - _updateSummary: function (ev) { + _updateSummary: function () { const selectedOption = this.$opts[0].selectedIndex const opts = this.model.fireOptions[selectedOption] this.model.fetch(new SetBurnDefaultOption(opts.name)) diff --git a/shared/js/ui/views/main-nav.js b/shared/js/ui/views/main-nav.js index 6aac6cb7..9b569edc 100644 --- a/shared/js/ui/views/main-nav.js +++ b/shared/js/ui/views/main-nav.js @@ -4,7 +4,7 @@ import Parent from '../base/view.js' import { trackerNetworksText } from '../templates/shared/tracker-networks-text.js' import { thirdpartyText } from '../templates/shared/thirdparty-text.js' import { i18n } from '../base/localize.js' -import { createPlatformFeatures } from '../platform-features' +import { createPlatformFeatures } from '../platform-features.mjs' import { platform } from '../../browser/communication.js' import { isAndroid } from '../environment-check' import { setupMaterialDesignRipple } from './utils/utils' @@ -23,19 +23,19 @@ export function MainNavView(ops) { this.features = createPlatformFeatures(platform) this.cleanups = [] this.nav = { - connection: (e) => { + connection: () => { this.model.send('navigate', { target: 'connection' }) }, - trackers: (e) => { + trackers: () => { this.model.send('navigate', { target: 'trackers' }) }, - nonTrackers: (e) => { + nonTrackers: () => { this.model.send('navigate', { target: 'nonTrackers' }) }, - consentManaged: (e) => { + consentManaged: () => { this.model.send('navigate', { target: 'consentManaged' }) }, - cookieHidden: (e) => { + cookieHidden: () => { this.model.send('navigate', { target: 'cookieHidden' }) }, } diff --git a/shared/js/ui/views/search.js b/shared/js/ui/views/search.js index 783771b9..ceb8424c 100644 --- a/shared/js/ui/views/search.js +++ b/shared/js/ui/views/search.js @@ -35,11 +35,11 @@ Search.prototype = $.extend({}, Parent.prototype, { } }, - _handleBlur: function (e) { + _handleBlur: function (_e) { this._removeHoverEffect() }, - _handleInput: function (e) { + _handleInput: function (_e) { const searchText = this.$input.val() this.model.set('searchText', searchText) diff --git a/shared/js/ui/views/site.js b/shared/js/ui/views/site.js index 939067d1..a050339d 100644 --- a/shared/js/ui/views/site.js +++ b/shared/js/ui/views/site.js @@ -15,7 +15,7 @@ import { heroFromTabNonTrackers, heroFromTabTrackers } from '../templates/shared import { KeyInsightView } from '../templates/key-insights' import { BreakageFormModel } from '../models/breakage-form.js' import { renderUpdatingSpinner } from '../templates/shared/protection-toggle' -import { createPlatformFeatures } from '../platform-features' +import { createPlatformFeatures } from '../platform-features.mjs' import { CookiePromptModel } from '../models/cookie-prompt.js' import { setupMaterialDesignRipple, setupSwitch } from './utils/utils.js' import BreakageFormView from './../views/breakage-form.js' @@ -59,10 +59,10 @@ function Site(ops) { Site.prototype = $.extend({}, Parent.prototype, { /** * @this {Site & Record} - * @param e + * @param _e * @private */ - _onAllowlistClick: function (e) { + _onAllowlistClick: function (_e) { if (this.$body.hasClass('is-disabled')) return // this can only ever be interacted with once @@ -152,7 +152,7 @@ Site.prototype = $.extend({}, Parent.prototype, { } }) .catch((e) => { - console.error('could not check') + console.error('could not check', e) }) }, diff --git a/shared/js/ui/views/sliding-subview.js b/shared/js/ui/views/sliding-subview.js index 7601ca24..4ef8612d 100644 --- a/shared/js/ui/views/sliding-subview.js +++ b/shared/js/ui/views/sliding-subview.js @@ -39,7 +39,7 @@ SlidingSubview.prototype = $.extend({}, Parent.prototype, { if (this.popstateHandler) { window.removeEventListener('popstate', this.popstateHandler) } - this.popstateHandler = (e) => { + this.popstateHandler = () => { // @ts-ignore this._destroy(null, { fromNavigation: true }) } diff --git a/shared/js/ui/views/tests/generate-data.mjs b/shared/js/ui/views/tests/generate-data.mjs index b3acd51d..79655e67 100644 --- a/shared/js/ui/views/tests/generate-data.mjs +++ b/shared/js/ui/views/tests/generate-data.mjs @@ -207,18 +207,21 @@ export const permissions = [ ] /** - * These can be used when previewing the dashboard by adding the 'state' parameter, such as - * - http://localhost:8080/html/popup.html?state=01 - * - http://localhost:8080/html/popup.html?state=02 - * - http://localhost:8080/html/popup.html?state=google - * - http://localhost:8080/html/popup.html?state=cnn + * @typedef {{ clearHistory: boolean, tabClearEnabled: boolean, pinnedTabs: number }} BurnConfig */ +/** + * These can be used when previewing the dashboard by adding the 'state' parameter, such as + * - http://localhost:8080/html/ios.html?state=01 + * - http://localhost:8080/html/ios.html?state=02 + * - http://localhost:8080/html/ios.html?state=google + * - http://localhost:8080/html/ios.html?state=cnn + */ export class MockData { /** * @param {object} params * @param {string} [params.state] - any string identifier for this mock state - * @param {string} params.url + * @param {string} [params.url] * @param {{locale: string}} [params.localeSettings] * @param {DetectedRequest[]} [params.requests] * @param {any[]} [params.certificate] @@ -233,10 +236,11 @@ export class MockData { * @param {boolean} [params.fireButtonEnabled] * @param {BurnConfig} [params.fireButtonOptions] * @param {import('../../../../../schema/__generated__/schema.types').CookiePromptManagementStatus} [params.cookiePromptManagementStatus] + * @param {import('../../../../../schema/__generated__/schema.types').EmailProtectionUserData} [params.emailProtectionUserData] */ constructor(params) { - this.url = params.url - this.requests = params.requests || [] + this.url = params.url || 'https://example.com' + this.requests = params.requests this.state = params.state this.localeSettings = params.localeSettings || { locale: 'en' } this.certificate = params.certificate || defaultCertificates @@ -250,9 +254,8 @@ export class MockData { this.emailUser = params.emailUser this.cookiePromptManagementStatus = params.cookiePromptManagementStatus this.fireButtonEnabled = params.fireButtonEnabled || false - if (params.fireButtonOptions) { - this.getBurnOptions = mockBurnOptions(params.fireButtonOptions) - } + this.emailProtectionUserData = params.emailProtectionUserData + this.fireButtonOptions = params.fireButtonOptions /** @type {Protections} */ this.protections = Protections.default() @@ -267,7 +270,7 @@ export class MockData { if (this.contentBlockingException) { this.protections.enabledFeatures = [] } - if (this.protections.allowlisted || this.contentBlockingException) { + if (this.requests && (this.protections.allowlisted || this.contentBlockingException)) { this.requests = protectionsOff(this.requests) } } @@ -281,46 +284,112 @@ export class MockData { ...mock, }) } -} -/** - * @param {MockData} mock - * @returns {{ - * getPrivacyDashboardData: import('../../../../../schema/__generated__/schema.types').GetPrivacyDashboardData, - * getBurnOptions?: import('../../../../../schema/__generated__/schema.types.js').FireButtonData - * }} - */ -export function mockToExtensionDashboardMessage(mock) { - /** @type {import('../../../../../schema/__generated__/schema.types').GetPrivacyDashboardData} */ - const msg = { - tab: { - id: 123, - url: mock.url, - protections: mock.protections, - upgradedHttps: mock.upgradedHttps, - localeSettings: mock.localeSettings, - parentEntity: mock.parentEntity, - }, - requestData: { requests: mock.requests }, - emailProtectionUserData: undefined, - fireButton: { - enabled: mock.fireButtonEnabled, - }, - } - if (mock.specialDomainName) { - msg.tab.specialDomainName = 'extensions' - } - if (mock.emailUser) { - msg.emailProtectionUserData = { - nextAlias: '123456_next', + /** + * @return {import('../../../../../schema/__generated__/schema.types').WindowsIncomingViewModel} + */ + toWindowsViewModel() { + return { + Feature: 'PrivacyDashboard', + Name: 'ViewModelUpdated', + Data: { + rawRequestData: { + requests: this.requests || [], + }, + protections: this.protections, + tabUrl: this.url, + upgradedHttps: this.upgradedHttps, + parentEntity: this.parentEntity, + permissions: this.permissions, + certificates: this.certificate, + cookiePromptManagementStatus: this.cookiePromptManagementStatus, + }, } } - if (mock.localeSettings) { - msg.tab.localeSettings = mock.localeSettings + + /** + * @return {import('../../../../../schema/__generated__/schema.types').GetPrivacyDashboardData} + */ + toExtensionDashboardData() { + /** @type {import('../../../../../schema/__generated__/schema.types').GetPrivacyDashboardData} */ + const output = { + tab: { + id: 1533, + url: this.url || 'https://example.com', + upgradedHttps: this.upgradedHttps, + protections: this.protections, + parentEntity: this.parentEntity, + }, + fireButton: { enabled: this.fireButtonEnabled }, + requestData: { + requests: this.requests || [], + }, + emailProtectionUserData: this.emailProtectionUserData, + } + + if (this.specialDomainName) { + output.tab.specialDomainName = 'extensions' + } + if (this.emailUser) { + output.emailProtectionUserData = { + nextAlias: '123456_next', + } + } + if (this.localeSettings) { + output.tab.localeSettings = this.localeSettings + } + + return output } - return { - getPrivacyDashboardData: msg, - getBurnOptions: mock.getBurnOptions || undefined, + + /** + * @return {import('../../../../../schema/__generated__/schema.types.js').FireButtonData} + */ + toBurnOptions() { + const burnConfig = this.fireButtonOptions || { clearHistory: true, tabClearEnabled: true, pinnedTabs: 2 } + const { clearHistory, pinnedTabs, tabClearEnabled } = burnConfig + return { + options: [ + { + name: 'CurrentSite', + options: { + origins: ['https://example.com/'], + }, + descriptionStats: { + clearHistory, + site: 'example.com', + duration: 'all', + openTabs: tabClearEnabled ? 1 : 0, + cookies: 1, + pinnedTabs, + }, + }, + { + name: 'LastHour', + options: { + since: Date.now(), + }, + descriptionStats: { + clearHistory, + duration: 'hour', + openTabs: tabClearEnabled ? 5 : 0, + cookies: 23, + pinnedTabs, + }, + }, + { + name: 'AllTime', + options: {}, + descriptionStats: { + clearHistory, + duration: 'all', + openTabs: tabClearEnabled ? 5 : 0, + cookies: 1000, + pinnedTabs, + }, + }, + ], + } } } @@ -368,6 +437,11 @@ export const createDataStates = (google, cnn) => { url: 'https://example.com', requests: [], }), + 'email-user': new MockData({ + emailProtectionUserData: { + nextAlias: 'abc', + }, + }), 'ad-attribution': new MockData({ url: 'https://example.com', requests: [blocked1, allowedAdClickAttribution], @@ -563,57 +637,33 @@ export const createDataStates = (google, cnn) => { fireButtonEnabled: true, fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 2 }, }), - } -} - -/** - * @typedef {{ clearHistory: boolean, tabClearEnabled: boolean, pinnedTabs: number }} BurnConfig - */ -/** - * @param {BurnConfig} options - * @returns {import('../../../../../schema/__generated__/schema.types.js').FireButtonData} - */ -export function mockBurnOptions({ clearHistory, tabClearEnabled, pinnedTabs }) { - return { - options: [ - { - name: 'CurrentSite', - options: { - origins: ['https://example.com/'], - }, - descriptionStats: { - clearHistory, - site: 'example.com', - duration: 'all', - openTabs: tabClearEnabled ? 1 : 0, - cookies: 1, - pinnedTabs, - }, - }, - { - name: 'LastHour', - options: { - since: Date.now(), - }, - descriptionStats: { - clearHistory, - duration: 'hour', - openTabs: tabClearEnabled ? 5 : 0, - cookies: 23, - pinnedTabs, - }, - }, - { - name: 'AllTime', - options: {}, - descriptionStats: { - clearHistory, - duration: 'all', - openTabs: tabClearEnabled ? 5 : 0, - cookies: 1000, - pinnedTabs, - }, - }, - ], + 'fire-button-enabled': new MockData({ + url: 'https://example.com', + fireButtonEnabled: true, + }), + 'fire-button-disabled': new MockData({ + url: 'https://example.com', + fireButtonEnabled: false, + }), + 'fire-button-no-pinned': new MockData({ + url: 'https://example.com', + fireButtonEnabled: true, + fireButtonOptions: { clearHistory: true, tabClearEnabled: true, pinnedTabs: 0 }, + }), + 'fire-button-tab-clear-disabled': new MockData({ + url: 'https://example.com', + fireButtonEnabled: true, + fireButtonOptions: { clearHistory: true, tabClearEnabled: false, pinnedTabs: 0 }, + }), + 'special-page': new MockData({ + url: 'https://example.com', + specialDomainName: true, + emailUser: true, + }), + 'invalid-data': new MockData({ + url: 'https://example.com', + // @ts-expect-error - this SHOULD error, that's the test + requests: [{ foo: 'bar' }], + }), } } diff --git a/shared/js/ui/views/user-data.js b/shared/js/ui/views/user-data.js deleted file mode 100644 index b7143eb4..00000000 --- a/shared/js/ui/views/user-data.js +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery' -import Parent from '../base/view.js' - -function UserData(ops) { - this.model = ops.model - this.pageView = ops.pageView - this.template = ops.template - - Parent.call(this, ops) - - // bind events - // @ts-ignore - this.setup() -} - -UserData.prototype = $.extend({}, Parent.prototype, { - _logout: function (e) { - e.preventDefault() - this.model.logout() - }, - - setup: function () { - this._cacheElems('.js-userdata', ['logout']) - - this.bindEvents([ - [this.$logout, 'click', this._logout], - // listen for changes to the userData model - [this.store.subscribe, 'change:userData', this.rerender], - ]) - }, - - rerender: function () { - this.unbindEvents() - this._rerender() - this.setup() - }, -}) - -export default UserData diff --git a/tsconfig.json b/tsconfig.json index 1f013678..a057acc7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,10 @@ "noImplicitAny": false, "strictNullChecks": true, "maxNodeModuleJsDepth": 0, - "skipLibCheck": true + "skipLibCheck": true, + "jsx": "react", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment" }, - "include": ["types.d.ts", "schema", "e2e", "scripts", "shared/js", "integration-tests"] + "include": ["types.ts", "schema", "e2e", "scripts", "shared/js", "integration-tests"] } diff --git a/typedoc.json b/typedoc.json index e8ee00f6..f7731d63 100644 --- a/typedoc.json +++ b/typedoc.json @@ -9,7 +9,7 @@ "shared/js/browser/common.js", "shared/js/browser/utils/request-details.mjs", "shared/js/browser/utils/protections.mjs", - "shared/js/ui/platform-features.js", + "shared/js/ui/platform-features.mjs", "shared/js/features.doc.js" ], "out": "build/docs", diff --git a/types.d.ts b/types.ts similarity index 70% rename from types.d.ts rename to types.ts index d812e714..482b5c69 100644 --- a/types.d.ts +++ b/types.ts @@ -1,19 +1,19 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars interface Window { __DDG_TEST_DATA: any DDG: any - webkit: any - onChangeConsentManaged: any + onChangeConsentManaged: (payload: import('./schema/__generated__/schema.types').CookiePromptManagementStatus) => void onChangeParentEntity: any /** * @deprecated use window.onChangeRequestData */ onChangeTrackerBlockingData: any onChangeTheme: any - onChangeRequestData: any - onChangeLocale: any + onChangeRequestData: (url: string, rawRequestData: import('./schema/__generated__/schema.types').RequestData) => void + onChangeLocale: (payload: import('./schema/__generated__/schema.types').LocaleSettings) => void onChangeAllowedPermissions: any onChangeUpgradedHttps: any - onChangeProtectionStatus: (protections: import('./shared/js/browser/utils/request-details.mjs').Protections) => void + onChangeProtectionStatus: (protections: import('./shared/js/browser/utils/protections.mjs').Protections) => void onChangeCertificateData: any onIsPendingUpdates: any @@ -31,8 +31,11 @@ interface Window { /** * Overrides */ - environmentOverride?: string $: import('jquery') + /** + * This is set in Playwright tests + */ + __ddg_integration_test?: boolean __playwright: { listeners?: any[] calls: any[] @@ -49,12 +52,11 @@ interface Window { privacyDashboardSetSize?: any privacyDashboardShowReportBrokenSite?: any privacyDashboardClose?: any + privacyDashboardSetProtection?: any privacyDashboardOpenSettings?: any + privacyDashboardSetPermission?: any } } -} - -interface Window { chrome: { webview?: { postMessage?: Window['postMessage'] @@ -63,7 +65,3 @@ interface Window { } } } - -interface Navigator { - brave?: any -}