From 19b0674012d5de175daef18bd1242feff9eb6c12 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Fri, 3 Nov 2023 11:38:29 +0000 Subject: [PATCH] macos and windows --- build/app/public/css/android.css | 2 + build/app/public/css/base.css | 2 + build/app/public/css/popup.css | 2 + build/app/public/js/base.js | 1420 +++++++++++++++++++++++- build/app/public/js/polyfills.js | 1 + integration-tests/DashboardPage.js | 5 +- integration-tests/Mocks.js | 4 +- integration-tests/android.spec-int.js | 45 +- integration-tests/common-flows.js | 98 ++ integration-tests/macos.spec-int.js | 43 +- integration-tests/windows.spec-int.js | 25 +- shared/js/ui/templates/key-insights.js | 6 - shared/js/ui/templates/site.js | 28 +- 13 files changed, 1513 insertions(+), 168 deletions(-) create mode 100644 integration-tests/common-flows.js diff --git a/build/app/public/css/android.css b/build/app/public/css/android.css index 05346486e..7a234c723 100644 --- a/build/app/public/css/android.css +++ b/build/app/public/css/android.css @@ -936,3 +936,5 @@ .body--theme-dark .material-design-ripple.mdc-ripple-upgraded { --mdc-ripple-fg-opacity: var(--mdc-ripple-press-opacity, 0.3); } + +/*# sourceMappingURL=android.css.map */ diff --git a/build/app/public/css/base.css b/build/app/public/css/base.css index 396424d1e..9c25c2890 100644 --- a/build/app/public/css/base.css +++ b/build/app/public/css/base.css @@ -716,3 +716,5 @@ a.link-action--rounded { vertical-align: bottom; margin: 0 7px; } + +/*# sourceMappingURL=base.css.map */ diff --git a/build/app/public/css/popup.css b/build/app/public/css/popup.css index 25149fea6..1e50a793b 100644 --- a/build/app/public/css/popup.css +++ b/build/app/public/css/popup.css @@ -3538,3 +3538,5 @@ body.environment--macos, body.environment--browser, body.environment--windows, b padding: 16px 0; text-align: center; } + +/*# sourceMappingURL=popup.css.map */ diff --git a/build/app/public/js/base.js b/build/app/public/js/base.js index f07177848..19cb43399 100644 --- a/build/app/public/js/base.js +++ b/build/app/public/js/base.js @@ -12957,7 +12957,1385 @@ } }); + // 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((r3) => { + if ("blocked" in r3.state) { + return detectedRequestSchema.parse({ + ...r3, + state: { allowed: { reason: "protectionDisabled" } } + }); + } + if ("allowed" in r3.state) { + if (r3.state.allowed.reason === "otherThirdPartyRequest") { + return r3; + } + return detectedRequestSchema.parse({ + ...r3, + state: { allowed: { reason: "protectionDisabled" } } + }); + } + return r3; + }); + } + 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 + 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 _MockData { + /** + * @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] + * @param {import('../../../../../schema/__generated__/schema.types').RemoteFeatureSettings} [params.remoteFeatureSettings] + * @param {import('../../../../../schema/__generated__/schema.types').EmailProtectionUserData} [params.emailProtectionUserData] + */ + constructor(params) { + 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; + 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; + this.remoteFeatureSettings = params.remoteFeatureSettings; + this.emailProtectionUserData = params.emailProtectionUserData; + this.fireButtonOptions = 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.requests && (this.protections.allowlisted || this.contentBlockingException)) { + this.requests = protectionsOff(this.requests); + } + } + /** + * @param {Partial & {url: string}} mock + * @returns {MockData} + */ + static default(mock) { + return new _MockData({ + ...mock + }); + } + /** + * @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 + } + }; + } + /** + * @return {import('../../../../../schema/__generated__/schema.types').GetPrivacyDashboardData} + */ + toExtensionDashboardData() { + const output2 = { + 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) { + output2.tab.specialDomainName = "extensions"; + } + if (this.emailUser) { + output2.emailProtectionUserData = { + nextAlias: "123456_next" + }; + } + if (this.localeSettings) { + output2.tab.localeSettings = this.localeSettings; + } + return output2; + } + /** + * @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: 1e3, + pinnedTabs + } + } + ] + }; + } + }; + createDataStates = (google, cnn) => { + return { + "alternative-layout-exp-1": new MockData({ + url: "https://example.com", + requests: cnn.requests, + remoteFeatureSettings: { + primaryScreen: { + layout: "highlighted-protections-toggle" + } + } + }), + "alternative-layout-exp-1-protections-off": new MockData({ + url: "https://example.com", + requests: cnn.requests, + remoteFeatureSettings: { + primaryScreen: { + layout: "highlighted-protections-toggle" + } + }, + allowlisted: true + }), + "alternative-layout-exp-1-disabled": new MockData({ + url: "https://example.com", + requests: cnn.requests, + remoteFeatureSettings: { + primaryScreen: { + layout: "highlighted-protections-toggle" + } + }, + contentBlockingException: true + }), + "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: [] + }), + "email-user": new MockData({ + emailProtectionUserData: { + nextAlias: "abc" + } + }), + "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 } + }), + "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" }] + }) + }; + }; + } + }); + + // shared/js/ui/views/tests/states-with-fixtures.js + var states_with_fixtures_exports = {}; + __export(states_with_fixtures_exports, { + testDataStates: () => testDataStates + }); + 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/communication-mocks.mjs + async function mockDataProvider(params) { + const { state, platform: platform2, messages } = params; + Object.assign(window.__playwright.messages, messages); + if (platform2?.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; + } + if (platform2?.name === "browser") { + for (const listener of window.__playwright.listeners || []) { + listener({ updateTabData: true }, { id: "test" }); + } + return; + } + if (state.cookiePromptManagementStatus) { + window.onChangeConsentManaged(state.cookiePromptManagementStatus); + } + window.onChangeParentEntity(state.parentEntity); + window.onChangeProtectionStatus(state.protections); + window.onChangeUpgradedHttps(state.upgradedHttps); + window.onChangeCertificateData({ + secCertificateViewModels: state.certificate + }); + if (state.remoteFeatureSettings) { + window.onChangeFeatureSettings?.(state.remoteFeatureSettings); + } + window.onChangeLocale?.(state.localeSettings); + window.onChangeRequestData(state.url, { requests: state.requests || [] }); + } + function windowsMockApis() { + try { + if (!window.chrome) { + window.chrome = {}; + } + window.__playwright = { + listeners: [], + messages: {}, + mocks: { + outgoing: [], + incoming: [] + }, + calls: [] + }; + window.chrome.webview = { + // @ts-ignore + addEventListener: (messageName, listener) => { + window.__playwright.listeners?.push(listener); + }, + postMessage(arg) { + window.__playwright.mocks.outgoing.push([arg.Name, arg]); + } + }; + } catch (e3) { + console.error("\u274Ccouldn't set up mocks"); + console.error(e3); + } + } + 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 (e3) { + console.error("\u274Ccouldn't set up mocks"); + console.error(e3); + } + } + 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 (e3) { + console.error("\u274Ccouldn't set up mocks"); + console.error(e3); + } + } + function mockBrowserApis() { + const messages = { + submitBrokenSiteReport: {}, + setLists: {}, + search: {}, + openOptions: {}, + setBurnDefaultOption: {}, + doBurn: {} + }; + try { + if (!window.chrome) { + 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: [] + }; + window.chrome.runtime = { + id: "test", + async sendMessage(message, cb) { + function respond(fn, timeout = 100) { + setTimeout(() => { + fn(); + }, timeout); + } + const matchingMessage = window.__playwright.messages[message.messageType]; + if (matchingMessage) { + window.__playwright.mocks.outgoing.push([message.messageType, message]); + respond(() => cb(matchingMessage), 200); + } else { + setTimeout(() => { + const matchingMessage2 = window.__playwright.messages[message.messageType]; + if (matchingMessage2) { + window.__playwright.mocks.outgoing.push([message.messageType, message]); + respond(() => cb(matchingMessage2), 0); + } else { + console.trace(`\u274C [(mocks): window.chrome.runtime] Missing support for ${JSON.stringify(message)}`); + } + }, 200); + } + }, + // @ts-ignore + onMessage: { + addListener(listener) { + window.__playwright.listeners?.push(listener); + } + } + }; + } catch (e3) { + console.error("\u274Ccouldn't set up browser mocks"); + console.error(e3); + } + } + async function installMocks(platform2) { + if (window.__playwright) + return console.log("\u274C mocked already there"); + if (platform2.name === "windows") { + windowsMockApis(); + } else if (platform2.name === "ios" || platform2.name === "macos") { + webkitMockApis(); + } else if (platform2.name === "android") { + mockAndroidApis(); + } else if (platform2.name === "browser") { + mockBrowserApis(); + } + const { testDataStates: testDataStates2 } = await Promise.resolve().then(() => (init_states_with_fixtures(), states_with_fixtures_exports)); + const stateFromUrl = new URLSearchParams(window.location.search).get("state"); + let mock; + if (stateFromUrl && stateFromUrl in testDataStates2) { + mock = testDataStates2[stateFromUrl]; + } else { + mock = testDataStates2.protectionsOn_blocked; + console.warn("state not found, falling back to default. state: ", "protectionsOn_blocked", stateFromUrl); + } + console.groupCollapsed(`${platform2.name} open for more Dashboard States`); + const urls = Object.keys(testDataStates2).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 (platform2.name === "browser") { + messages["getBurnOptions"] = mock.toBurnOptions(); + messages["getPrivacyDashboardData"] = mock.toExtensionDashboardData(); + } + if (platform2.name === "windows") { + messages["windowsViewModel"] = mock.toWindowsViewModel(); + } + await mockDataProvider({ + state: mock, + platform: platform2, + messages + }); + } var init_communication_mocks = __esm({ "shared/js/browser/utils/communication-mocks.mjs"() { "use strict"; @@ -12997,6 +14375,8 @@ } if (!defaultComms) throw new Error("unsupported environment"); + $TEST: + typeof window.__ddg_integration_test === "undefined" && installMocks(platform).catch(console.error); defaultComms.setup(); communication_default = defaultComms; } @@ -21567,7 +22947,6 @@ } function renderKeyInsight() { const model = this.model; - const layout = this.model.featureSettings.primaryScreen.layout; const title = (text) => import_nanohtml10.default`

${text}

`; const description = (text) => import_nanohtml10.default`
${text}
`; const state = (() => { @@ -21600,15 +22979,10 @@ `; }, broken: () => { - let text = i18n.t("site:protectionsDisabledRemote.title"); - if (model.isDenylisted) { - text = i18n.t("site:protectionsDisabledRemoteOverride.title"); - } return import_nanohtml10.default`
${title(model.tab.domain)} - ${layout === "default" ? description(import_nanohtml10.default`

${text}

`) : null}
`; }, @@ -23212,19 +24586,6 @@ ); return root; } - function protectionDefault(model) { - const root = import_nanohtml12.default`
`; - const migrationModel = { - protectionsEnabled: model.protectionsEnabled, - isAllowlisted: model.isAllowlisted, - isDenylisted: model.isDenylisted, - platformFeatures: model.features, - isBroken: model.isBroken, - toggleAllowlist: () => model.toggleAllowlist({ screen: "primaryScreen" }) - }; - B(/* @__PURE__ */ y(ProtectionToggle, { model: migrationModel }), root); - return root; - } function ProtectionHeader(props) { let initial; if (props.initialState) { @@ -24540,24 +25901,19 @@ `; } const permissions = localizePermissions(this.model.permissions); - const layout = this.model.featureSettings.primaryScreen.layout; return import_nanohtml17.default`
${renderSearchWrapper(this.model)} ${topNav({ view: "primary" })}
0}> - ${layout === "highlighted-protections-toggle" ? import_nanohtml17.default` -
${protectionHeader(this.model)}
-
- ` : null} +
${protectionHeader(this.model)}
+
- ${layout === "default" ? protectionDefault(this.model) : null}
${renderEmailWrapper(this.model)} - ${layout === "default" ? renderReportButton() : null}
${permissions.length ? outer({ children: renderManagePermissions(this.model) }) : null} @@ -24603,15 +25959,6 @@ `; } - function renderReportButton() { - function onClickTextLink(e3) { - e3.preventDefault(); - window.dispatchEvent(new CustomEvent("open-feedback")); - } - let root = import_nanohtml17.default`
`; - B(/* @__PURE__ */ y(TextLink, { onClick: onClickTextLink }, ns.site("websiteNotWorkingQ.title")), root); - return root; - } function localizePermissions(permissions) { if (!Array.isArray(permissions) || permissions.length === 0) { return []; @@ -24640,8 +25987,6 @@ init_localize(); init_top_nav(); init_protection_header(); - init_preact_module(); - init_text_link(); } }); @@ -25175,3 +26520,4 @@ eventemitter2/lib/eventemitter2.js: * THE SOFTWARE. *) */ +//# sourceMappingURL=base.js.map diff --git a/build/app/public/js/polyfills.js b/build/app/public/js/polyfills.js index 363804777..283b94785 100644 --- a/build/app/public/js/polyfills.js +++ b/build/app/public/js/polyfills.js @@ -6799,3 +6799,4 @@ // shared/js/polyfill.js var import_polyfill = __toESM(require_polyfill()); })(); +//# sourceMappingURL=polyfills.js.map diff --git a/integration-tests/DashboardPage.js b/integration-tests/DashboardPage.js index 77ebaf1e4..0ab41c918 100644 --- a/integration-tests/DashboardPage.js +++ b/integration-tests/DashboardPage.js @@ -61,7 +61,7 @@ export class DashboardPage { } async screenshot(name) { - if (!process.env.CI) { + if (false) { // console.log('🚧 skipping screenshot 🚧', name) await expect(this.page).toHaveScreenshot(name, { maxDiffPixelRatio: 0.025 }) } @@ -220,6 +220,9 @@ export class DashboardPage { return results } + /** + * @deprecated + */ async clickReportBreakage() { await this.page.getByRole('link', { name: 'Website not working as expected?' }).click() } diff --git a/integration-tests/Mocks.js b/integration-tests/Mocks.js index 7aa5444f6..e92436f50 100644 --- a/integration-tests/Mocks.js +++ b/integration-tests/Mocks.js @@ -65,6 +65,9 @@ export class Mocks { } async calledForShowBreakageForm() { + // only on ios/android + if (!['android', 'ios'].includes(this.platform.name)) return + const calls = await this.outgoing() if (this.platform.name === 'android') { expect(calls).toMatchObject([['showBreakageForm', undefined]]) @@ -74,7 +77,6 @@ export class Mocks { expect(calls).toMatchObject([['privacyDashboardShowReportBrokenSite', {}]]) return } - throw new Error('unreachable. mockCalledForShowBreakageForm must be handled') } async calledForSubmitBreakageForm(opts = {}) { diff --git a/integration-tests/android.spec-int.js b/integration-tests/android.spec-int.js index 0ce3efc5d..c408bafaa 100644 --- a/integration-tests/android.spec-int.js +++ b/integration-tests/android.spec-int.js @@ -1,6 +1,7 @@ import { test } from '@playwright/test' import { testDataStates } from '../shared/js/ui/views/tests/states-with-fixtures' import { DashboardPage } from './DashboardPage' +import { toggleFlows } from './common-flows' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { @@ -58,49 +59,7 @@ test.describe('localization', () => { }) test.describe('Protections toggle', () => { - test('pressing toggle should disable protections', async ({ page }) => { - const dash = await DashboardPage.android(page) - await dash.reducedMotion() - await dash.addState([testDataStates.protectionsOn]) - await dash.toggleProtectionsOff() - await dash.mocks.calledForToggleAllowList() - }) - test('with alternative primary screen - toggling protections', async ({ page }) => { - const dash = await DashboardPage.android(page) - await dash.reducedMotion() - await dash.addState([testDataStates['alternative-layout-exp-1']]) - await page.pause() - await dash.showsAlternativeLayout() - await dash.toggleProtectionsOff() - await dash.mocks.calledForToggleAllowList() - }) - test('with alternative primary screen - alternative-layout-exp-1', async ({ page }) => { - const dash = await DashboardPage.android(page) - await dash.reducedMotion() - await dash.addState([testDataStates['alternative-layout-exp-1']]) - await dash.showsAlternativeLayout() - await dash.clicksWebsiteNotWorking() - await dash.helpIsShown() - await dash.clicksReportBroken() - await dash.mocks.calledForShowBreakageForm() - }) - test('with alternative primary screen - alternative-layout-exp-1 protections off (allowlisted)', async ({ page }) => { - const dash = await DashboardPage.android(page) - await dash.reducedMotion() - await dash.addState([testDataStates['alternative-layout-exp-1-protections-off']]) - await dash.showsAlternativeLayout() - await dash.showsToggleFeedbackPrompt() - }) - test('with alternative primary screen - alternative-layout-exp-1 remote disabled', async ({ page }) => { - const dash = await DashboardPage.android(page) - await dash.reducedMotion() - await dash.addState([testDataStates['alternative-layout-exp-1-disabled']]) - await dash.showsAlternativeLayout() - await dash.showRemoteDisabled() - await dash.showsToggleFeedbackPrompt() - await dash.clicksReportBroken() - await dash.mocks.calledForShowBreakageForm() - }) + toggleFlows((page) => DashboardPage.android(page)) }) test.describe('Close', () => { diff --git a/integration-tests/common-flows.js b/integration-tests/common-flows.js new file mode 100644 index 000000000..1470f2ecd --- /dev/null +++ b/integration-tests/common-flows.js @@ -0,0 +1,98 @@ +import { test } from '@playwright/test' +import { testDataStates } from '../shared/js/ui/views/tests/states-with-fixtures' +import { DashboardPage } from './DashboardPage' + +/** + * @param {(page: import("@playwright/test").Page) => Promise} dashboardFactory + */ +export function toggleFlows(dashboardFactory) { + test('pressing toggle should disable protections', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates.protectionsOn]) + await dash.toggleProtectionsOff() + await dash.mocks.calledForToggleAllowList() + }) + test('with alternative primary screen - toggling protections', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates['alternative-layout-exp-1']]) + await dash.showsAlternativeLayout() + await dash.toggleProtectionsOff() + await dash.mocks.calledForToggleAllowList() + }) + test('with alternative primary screen - alternative-layout-exp-1', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates['alternative-layout-exp-1']]) + await dash.showsAlternativeLayout() + await dash.clicksWebsiteNotWorking() + await dash.helpIsShown() + await dash.clicksReportBroken() + await dash.mocks.calledForShowBreakageForm() + }) + test('with alternative primary screen - alternative-layout-exp-1 protections off (allowlisted)', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates['alternative-layout-exp-1-protections-off']]) + await dash.showsAlternativeLayout() + await dash.showsToggleFeedbackPrompt() + }) + test('with alternative primary screen - alternative-layout-exp-1 remote disabled', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates['alternative-layout-exp-1-disabled']]) + await dash.showsAlternativeLayout() + await dash.showRemoteDisabled() + await dash.showsToggleFeedbackPrompt() + await dash.clicksReportBroken() + await dash.mocks.calledForShowBreakageForm() + }) +} + +/** + * @param {(page: import("@playwright/test").Page) => Promise} dashboardFactory + */ +export function desktopBreakageForm(dashboardFactory) { + test('should show HTML breakage form and submit fields', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates.protectionsOn]) + await dash.showsAlternativeLayout() + await dash.clicksWebsiteNotWorking() + await dash.helpIsShown() + await dash.clicksReportBroken() + await dash.screenshot('breakage-form.png') + await dash.submitBreakageForm() + await dash.screenshot('breakage-form-message.png') + }) + test('toggling protections off from breakage form', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates.protectionsOn]) + await dash.clicksWebsiteNotWorking() + await dash.helpIsShown() + await dash.clicksReportBroken() + /** @type {import('../schema/__generated__/schema.types').EventOrigin} */ + const eventOrigin = { screen: 'breakageForm' } + await dash.toggleProtectionsOff(eventOrigin) + await dash.mocks.calledForToggleAllowList('protections-off', eventOrigin) + }) + test('toggling protections back on, from breakage form', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates.allowlisted]) + await dash.clicksReportBroken() + /** @type {import('../schema/__generated__/schema.types').EventOrigin} */ + const eventOrigin = { screen: 'breakageForm' } + await dash.screenshot('breakage-form-allowlisted.png') + await dash.toggleProtectionsOn(eventOrigin) + }) + test('broken (remote disabled) breakage form', async ({ page }) => { + const dash = await dashboardFactory(page) + await dash.reducedMotion() + await dash.addState([testDataStates.protectionsOff]) + await dash.clicksReportBroken() + await dash.screenshot('breakage-form-broken.png') + }) +} diff --git a/integration-tests/macos.spec-int.js b/integration-tests/macos.spec-int.js index 5391a3db4..f30d41579 100644 --- a/integration-tests/macos.spec-int.js +++ b/integration-tests/macos.spec-int.js @@ -1,6 +1,7 @@ import { test } from '@playwright/test' import { testDataStates } from '../shared/js/ui/views/tests/states-with-fixtures' import { DashboardPage } from './DashboardPage' +import { desktopBreakageForm, toggleFlows } from './common-flows' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { @@ -11,43 +12,11 @@ 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.reducedMotion() - await dash.addState([testDataStates.protectionsOn]) - await dash.clickReportBreakage() - await dash.screenshot('breakage-form.png') - await dash.submitBreakageForm() - await dash.mocks.calledForSubmitBreakageForm() - await dash.screenshot('breakage-form-message.png') - }) - test('toggling protections off from breakage form', async ({ page }) => { - const dash = await DashboardPage.macos(page) - await dash.reducedMotion() - await dash.addState([testDataStates.protectionsOn]) - await dash.clickReportBreakage() - /** @type {import('../schema/__generated__/schema.types').EventOrigin} */ - const eventOrigin = { screen: 'breakageForm' } - await dash.toggleProtectionsOff(eventOrigin) - await dash.mocks.calledForToggleAllowList('protections-off', eventOrigin) - }) - test('toggling protections back on, from breakage form', async ({ page }) => { - const dash = await DashboardPage.macos(page) - await dash.reducedMotion() - await dash.addState([testDataStates.allowlisted]) - await dash.clickReportBreakage() - /** @type {import('../schema/__generated__/schema.types').EventOrigin} */ - const eventOrigin = { screen: 'breakageForm' } - await dash.screenshot('breakage-form-allowlisted.png') - await dash.toggleProtectionsOn(eventOrigin) - }) - test('broken (remote disabled) breakage form', async ({ page }) => { - const dash = await DashboardPage.macos(page) - await dash.reducedMotion() - await dash.addState([testDataStates.protectionsOff]) - await dash.clickReportBreakage() - await dash.screenshot('breakage-form-broken.png') - }) + desktopBreakageForm((page) => DashboardPage.macos(page)) +}) + +test.describe('Protections toggle', () => { + toggleFlows((page) => DashboardPage.macos(page)) }) test.describe('open external links', () => { diff --git a/integration-tests/windows.spec-int.js b/integration-tests/windows.spec-int.js index d68af974e..79a32434e 100644 --- a/integration-tests/windows.spec-int.js +++ b/integration-tests/windows.spec-int.js @@ -1,6 +1,7 @@ import { test } from '@playwright/test' import { testDataStates } from '../shared/js/ui/views/tests/states-with-fixtures' import { DashboardPage } from './DashboardPage' +import { desktopBreakageForm, toggleFlows } from './common-flows' test.describe('initial page data', () => { test('should fetch initial data', async ({ page }) => { @@ -11,15 +12,11 @@ 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.addState([testDataStates.protectionsOn]) - await dash.clickReportBreakage() - await dash.screenshot('breakage-form.png') - await dash.submitBreakageForm() - await dash.mocks.calledForSubmitBreakageForm() - await dash.screenshot('breakage-form-message.png') - }) + desktopBreakageForm((page) => DashboardPage.windows(page)) +}) + +test.describe('Protections toggle', () => { + toggleFlows((page) => DashboardPage.windows(page)) }) test.describe('setting the height', () => { @@ -30,16 +27,6 @@ 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.addState([testDataStates.protectionsOn]) - await dash.reducedMotion() - await dash.toggleProtectionsOff() - await dash.mocks.calledForToggleAllowList() - }) -}) - test.describe('cookie prompt management', () => { test.describe('none-configurable', () => { test('primary screen', async ({ page }) => { diff --git a/shared/js/ui/templates/key-insights.js b/shared/js/ui/templates/key-insights.js index 1ae71b4e1..7e0031961 100644 --- a/shared/js/ui/templates/key-insights.js +++ b/shared/js/ui/templates/key-insights.js @@ -52,7 +52,6 @@ const keyInsightsState = /** @type {const} */ ({ */ export function renderKeyInsight() { const model = this.model - const layout = this.model.featureSettings.primaryScreen.layout const title = (text) => html`

${text}

` const description = (text) => html`
${text}
` @@ -84,16 +83,11 @@ export function renderKeyInsight() { ` }, broken: () => { - let text = i18n.t('site:protectionsDisabledRemote.title') - if (model.isDenylisted) { - text = i18n.t('site:protectionsDisabledRemoteOverride.title') - } // prettier-ignore return html`
${title(model.tab.domain)} - ${layout === 'default' ? description(html`

${text}

`) : null}
` }, diff --git a/shared/js/ui/templates/site.js b/shared/js/ui/templates/site.js index 7c834839d..3b03e287a 100644 --- a/shared/js/ui/templates/site.js +++ b/shared/js/ui/templates/site.js @@ -1,10 +1,8 @@ import html from 'nanohtml' -import { i18n, ns } from '../base/localize.js' +import { i18n } from '../base/localize.js' import { topNav } from './shared/top-nav' -import { protectionDefault, protectionHeader } from './protection-header' +import { protectionHeader } from './protection-header' // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { h, render } from 'preact' -import { TextLink } from '../components/text-link' /** @this {{model: import('../models/site.js').PublicSiteModel}} */ export default function () { @@ -41,28 +39,20 @@ export default function () { } const permissions = localizePermissions(this.model.permissions) - const layout = this.model.featureSettings.primaryScreen.layout - // prettier-ignore return html`
${renderSearchWrapper(this.model)} ${topNav({ view: 'primary' })}
0}> - ${layout === 'highlighted-protections-toggle' - ? html` -
${protectionHeader(this.model)}
-
- ` - : null} +
${protectionHeader(this.model)}
+
- ${layout === 'default' ? protectionDefault(this.model) : null}
${renderEmailWrapper(this.model)} - ${layout === 'default' ? renderReportButton() : null}
${permissions.length ? outer({ children: renderManagePermissions(this.model) }) : null} @@ -123,16 +113,6 @@ function renderManagePermissions(model) { ` } -function renderReportButton() { - function onClickTextLink(e) { - e.preventDefault() - window.dispatchEvent(new CustomEvent('open-feedback')) - } - let root = html`
` - render({ns.site('websiteNotWorkingQ.title')}, root) - return root -} - /** * @param permissions * @returns {any[]}