diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2077935d2bd..bfed7cc5341 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -28,8 +28,6 @@ updates: versions: [ ">=1.0.1" ] - dependency-name: "openpgp" update-types: ["version-update:semver-major"] - - dependency-name: "puppeteer" - update-types: ["version-update:semver-major"] - dependency-name: "web-ext" update-types: ["version-update:semver-major"] - dependency-name: "@openpgp/web-stream-tools" diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 0502a4d4b89..1fe5d5102e5 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -53,7 +53,7 @@ blocks: shopt -s nullglob for file in build/test/test/debugArtifacts/debugHtmlAttachment-*.html; do echo "Uploading debug file $file as job artifact..." - artifact push job "$file" + artifact push job "$file" --force done - name: Live UI Gmail tests @@ -89,7 +89,7 @@ blocks: shopt -s nullglob for file in build/test/test/debugArtifacts/debugHtmlAttachment-*.html; do echo "Uploading debug file $file as job artifact..." - artifact push job "$file" + artifact push job "$file" --force done - name: Other tests diff --git a/conf/tsconfig.content_scripts.json b/conf/tsconfig.content_scripts.json index f3fa6126898..ac1c3b0716c 100644 --- a/conf/tsconfig.content_scripts.json +++ b/conf/tsconfig.content_scripts.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "module": "es6", "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, diff --git a/conf/tsconfig.streams.json b/conf/tsconfig.streams.json index cf9706b7a8c..31a4401494a 100644 --- a/conf/tsconfig.streams.json +++ b/conf/tsconfig.streams.json @@ -1,10 +1,7 @@ { "compilerOptions": { - "target": "ES2020", - "lib": [ - "es6", - "dom" - ], + "target": "ES2022", + "lib": ["es6", "dom"], "allowJs": true, "alwaysStrict": true, "noImplicitAny": true, @@ -16,7 +13,5 @@ "outDir": "../build/streams", "skipLibCheck": true }, - "include": [ - "../node_modules/@openpgp/web-stream-tools/lib/*.js" - ] -} \ No newline at end of file + "include": ["../node_modules/@openpgp/web-stream-tools/lib/*.js"] +} diff --git a/conf/tsconfig.tooling.json b/conf/tsconfig.tooling.json index d33b68f5bd9..1ee5b546f38 100644 --- a/conf/tsconfig.tooling.json +++ b/conf/tsconfig.tooling.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "lib": ["es6", "dom"], "alwaysStrict": true, "noImplicitAny": true, diff --git a/extension/chrome/elements/compose-modules/compose-recipients-module.ts b/extension/chrome/elements/compose-modules/compose-recipients-module.ts index a926da26c9b..a91a77ca8cd 100644 --- a/extension/chrome/elements/compose-modules/compose-recipients-module.ts +++ b/extension/chrome/elements/compose-modules/compose-recipients-module.ts @@ -201,7 +201,7 @@ export class ComposeRecipientsModule extends ViewModule { this.view.errModule.debug(`parseRenderRecipients(force: ${force}).4`); } else { this.view.sizeModule.setInputTextHeightManuallyIfNeeded(); - $('body').attr('data-test-state', 'ready'); + document.querySelector('body')?.setAttribute('data-test-state', 'ready'); } } }; @@ -282,7 +282,7 @@ export class ComposeRecipientsModule extends ViewModule { public evaluateRecipients = async (recipientEls: ValidRecipientElement[], triggerCallback = true) => { this.view.errModule.debug(`evaluateRecipients`); - $('body').attr('data-test-state', 'working'); + document.querySelector('body')?.setAttribute('data-test-state', 'working'); for (const recipientEl of recipientEls) { this.view.S.now('send_btn_text').text(this.BTN_LOADING); this.view.sizeModule.setInputTextHeightManuallyIfNeeded(); @@ -307,7 +307,7 @@ export class ComposeRecipientsModule extends ViewModule { } } this.setEmailsPreview(); - $('body').attr('data-test-state', 'ready'); + document.querySelector('body')?.setAttribute('data-test-state', 'ready'); this.view.sizeModule.setInputTextHeightManuallyIfNeeded(); }; diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index cbbf83f4bfc..b8d056cd55f 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -193,7 +193,7 @@ export class ComposeRenderModule extends ViewModule { }; public closeMsg = () => { - $('body').attr('data-test-state', 'closed'); // used by automated tests + document.querySelector('body')?.setAttribute('data-test-state', 'closed'); // used by automated tests if (this.view.isReplyBox) { BrowserMsg.send.closeReplyMessage(this.view.parentTabId, { frameId: this.view.frameId }); } else { @@ -472,7 +472,7 @@ export class ComposeRenderModule extends ViewModule { .filter(r => r.evaluating) .map(r => r.evaluating) ); - $('body').attr('data-test-state', 'ready'); // set as ready so that automated tests can evaluate results + document.querySelector('body')?.setAttribute('data-test-state', 'ready'); // set as ready so that automated tests can evaluate results }; private renderReplySuccessAttachments = (attachments: Attachment[], msgId: string, isEncrypted: boolean) => { diff --git a/extension/chrome/elements/compose-modules/formatters/base-mail-formatter.ts b/extension/chrome/elements/compose-modules/formatters/base-mail-formatter.ts index da38c47393e..da40d28847b 100644 --- a/extension/chrome/elements/compose-modules/formatters/base-mail-formatter.ts +++ b/extension/chrome/elements/compose-modules/formatters/base-mail-formatter.ts @@ -15,7 +15,10 @@ export class BaseMailFormatter { protected richtext: boolean; protected acctEmail: string; - public constructor(view: ComposeView, protected isDraft = false) { + public constructor( + view: ComposeView, + protected isDraft = false + ) { this.view = view; this.richtext = view.sendBtnModule.popover.choices.richtext; this.acctEmail = this.view.acctEmail; diff --git a/extension/chrome/settings/modules/contacts.htm b/extension/chrome/settings/modules/contacts.htm index bedf31553e5..99ff8bc127b 100644 --- a/extension/chrome/settings/modules/contacts.htm +++ b/extension/chrome/settings/modules/contacts.htm @@ -53,7 +53,7 @@

proceeding.
- Select a file or paste public key(s) below: + Select a file or paste public key(s) below:
diff --git a/extension/css/fine-uploader-new.css b/extension/css/fine-uploader-new.css index 927755e9609..7c1c96df480 100644 --- a/extension/css/fine-uploader-new.css +++ b/extension/css/fine-uploader-new.css @@ -19,7 +19,10 @@ /* Buttons ------------------------------------------ */ .qq-btn { - box-shadow: 0 1px 1px rgba(255, 255, 255, 0.37) inset, 1px 0 1px rgba(255, 255, 255, 0.07) inset, 0 1px 0 rgba(0, 0, 0, 0.36), + box-shadow: + 0 1px 1px rgba(255, 255, 255, 0.37) inset, + 1px 0 1px rgba(255, 255, 255, 0.07) inset, + 0 1px 0 rgba(0, 0, 0, 0.36), 0 -2px 12px rgba(0, 0, 0, 0.08) inset; padding: 3px 4px; border: 1px solid #ccc; diff --git a/extension/css/open-sans.css b/extension/css/open-sans.css index e92e1faff43..cff9bace4f2 100644 --- a/extension/css/open-sans.css +++ b/extension/css/open-sans.css @@ -7,7 +7,10 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('/fonts/open-sans/open-sans-v15-light.woff2') format('woff2'), + src: + local('Open Sans Light'), + local('OpenSans-Light'), + url('/fonts/open-sans/open-sans-v15-light.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/fonts/open-sans/open-sans-v15-light.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ @@ -18,7 +21,10 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 400; - src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/open-sans/open-sans-v15-regular.woff2') format('woff2'), + src: + local('Open Sans Regular'), + local('OpenSans-Regular'), + url('/fonts/open-sans/open-sans-v15-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/fonts/open-sans/open-sans-v15-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ @@ -29,7 +35,10 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 600; - src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), url('/fonts/open-sans/open-sans-v15-semibold.woff2') format('woff2'), + src: + local('Open Sans SemiBold'), + local('OpenSans-SemiBold'), + url('/fonts/open-sans/open-sans-v15-semibold.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('/fonts/open-sans/open-sans-v15-semibold.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ diff --git a/extension/js/common/browser/ui.ts b/extension/js/common/browser/ui.ts index 185c3c2eec8..db7e7ae6de0 100644 --- a/extension/js/common/browser/ui.ts +++ b/extension/js/common/browser/ui.ts @@ -502,7 +502,7 @@ export class Ui { }; public static setTestState = (state: 'ready' | 'working' | 'waiting') => { - $('body').attr('data-test-state', state); // for automated tests + document.querySelector('body')?.setAttribute('data-test-state', state); // for automated tests }; public static buildJquerySels = (sels: Dict): SelCache => { diff --git a/extension/js/common/core/crypto/pgp/msg-util.ts b/extension/js/common/core/crypto/pgp/msg-util.ts index 087683af734..5647ce16d40 100644 --- a/extension/js/common/core/crypto/pgp/msg-util.ts +++ b/extension/js/common/core/crypto/pgp/msg-util.ts @@ -335,7 +335,7 @@ export class MsgUtil { // todo - the `ki.passphrase || ''` used to be `ki.passphrase!` which could have actually allowed an undefined to be passed // as fixed currently it appears better, but it may be best to instead check `ki.passphrase && await MsgUtil.decryptKeyFor(...)` // but that is a larger change that would require separate PR and testing - if ((await MsgUtil.isKeyDecryptedFor(parsed, matchingKeyids)) || await MsgUtil.decryptKeyFor(parsed, ki.passphrase || '', matchingKeyids)) { + if ((await MsgUtil.isKeyDecryptedFor(parsed, matchingKeyids)) || (await MsgUtil.decryptKeyFor(parsed, ki.passphrase || '', matchingKeyids))) { KeyCache.setDecrypted(parsed); keys.prvForDecryptDecrypted.push({ ki, decrypted: parsed }); } else { @@ -369,7 +369,7 @@ export class MsgUtil { continue; } const parsed = await KeyUtil.parse(ki.private); - if (parsed.fullyDecrypted || (ki.passphrase && await SmimeKey.decryptKey(parsed, ki.passphrase))) { + if (parsed.fullyDecrypted || (ki.passphrase && (await SmimeKey.decryptKey(parsed, ki.passphrase)))) { KeyCache.setDecrypted(parsed); keys.prvForDecryptDecrypted.push({ ki, decrypted: parsed }); } else { diff --git a/extension/js/common/ui/key-import-ui.ts b/extension/js/common/ui/key-import-ui.ts index b359e396988..45f192e4cda 100644 --- a/extension/js/common/ui/key-import-ui.ts +++ b/extension/js/common/ui/key-import-ui.ts @@ -231,11 +231,7 @@ export class KeyImportUi { return normalized; }; - public renderPassPhraseStrengthValidationInput = ( - input: JQuery, - submitButton?: JQuery, - type: 'passphrase' | 'pwd' = 'passphrase' - ) => { + public renderPassPhraseStrengthValidationInput = (input: JQuery, submitButton?: JQuery, type: 'passphrase' | 'pwd' = 'passphrase') => { const validationElements = this.getPPValidationElements(); const setBtnColor = (type: 'gray' | 'green') => { if (submitButton) { diff --git a/extension/js/common/view.ts b/extension/js/common/view.ts index 6d847deb1f6..aa2a08ca910 100644 --- a/extension/js/common/view.ts +++ b/extension/js/common/view.ts @@ -26,7 +26,7 @@ export abstract class View { } private static setTestViewStateLoaded = () => { - $('body').attr('data-test-view-state', 'loaded'); + document.querySelector('body')?.setAttribute('data-test-view-state', 'loaded'); }; private static reportAndRenderErr = (e: unknown) => { diff --git a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts index 15cac4985f0..713f0b07518 100644 --- a/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail/gmail-element-replacer.ts @@ -506,7 +506,7 @@ export class GmailElementReplacer extends WebmailElementReplacer { attachmentsContainerInner = $(attachmentsContainerInner); attachmentsContainerInner.parent().find(this.sel.numberOfAttachments).hide(); let nRenderedAttachments = attachments.length; - for (const a of attachments) { + for (const a of attachments.reverse()) { const attachmentSel = this.filterAttachments( attachmentsContainerInner.children().not('.attachment_processed'), new RegExp(`^${Str.regexEscape(a.name || 'noname')}$`) diff --git a/package-lock.json b/package-lock.json index b8c033309bb..fa38adc4304 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.15", + "eslint-plugin-jsdoc": "^48.4.0", "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-only-tests": "3.1.0", @@ -54,11 +54,11 @@ "openpgp": "5.11.2", "pdfjs-dist": "4.3.136", "prettier": "^3.3.2", - "puppeteer": "20.9.0", + "puppeteer": "22.12.0", "stylelint": "16.6.1", "stylelint-config-standard": "36.0.1", "typescript": "5.5.2", - "typescript-eslint": "^7.11.0", + "typescript-eslint": "^7.14.1", "undici-types": "^6.17.0", "web-ext": "7.12.0", "webpack-cli": "^5.1.1" @@ -709,6 +709,19 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -755,43 +768,26 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "yargs": "17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/@puppeteer/browsers/node_modules/debug": { @@ -812,21 +808,17 @@ } } }, - "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@puppeteer/browsers/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@puppeteer/browsers/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/@puppeteer/browsers/node_modules/ms": { @@ -836,51 +828,20 @@ "dev": true, "license": "MIT" }, - "node_modules/@puppeteer/browsers/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "lru-cache": "^6.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/@selderee/plugin-htmlparser2": { @@ -1072,9 +1033,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.7.tgz", - "integrity": "sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==", + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", "dev": true, "license": "MIT", "dependencies": { @@ -1113,17 +1074,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", - "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz", + "integrity": "sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/type-utils": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/type-utils": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1147,16 +1108,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", - "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.14.1.tgz", + "integrity": "sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "debug": "^4.3.4" }, "engines": { @@ -1176,14 +1137,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", + "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1194,14 +1155,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", - "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.14.1.tgz", + "integrity": "sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1222,9 +1183,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", + "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", "dev": true, "license": "MIT", "engines": { @@ -1236,14 +1197,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", + "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1332,16 +1293,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", - "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz", + "integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1355,13 +1316,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", - "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", + "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/types": "7.14.1", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1732,41 +1693,6 @@ "node": ">=16.0.0" } }, - "node_modules/addons-linter/node_modules/addons-scanner-utils": { - "version": "9.10.1", - "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.10.1.tgz", - "integrity": "sha512-Tz9OUQx9Ja0TyQ+H2GakB9KlJ50myI6ESBGRlA8N80nHBzMjjPRFGm0APADSaCd5NP74SrFtEvL4TRpDwZXETA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@types/yauzl": "2.10.3", - "common-tags": "1.8.2", - "first-chunk-stream": "3.0.0", - "strip-bom-stream": "4.0.0", - "upath": "2.0.1", - "yauzl": "2.10.0" - }, - "peerDependencies": { - "body-parser": "1.20.2", - "express": "4.18.3", - "node-fetch": "2.6.11", - "safe-compare": "1.1.4" - }, - "peerDependenciesMeta": { - "body-parser": { - "optional": true - }, - "express": { - "optional": true - }, - "node-fetch": { - "optional": true - }, - "safe-compare": { - "optional": true - } - } - }, "node_modules/addons-linter/node_modules/ajv": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", @@ -1934,36 +1860,48 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/addons-linter/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "node_modules/addons-moz-compare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz", + "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true, + "license": "MPL-2.0" + }, + "node_modules/addons-scanner-utils": { + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.10.1.tgz", + "integrity": "sha512-Tz9OUQx9Ja0TyQ+H2GakB9KlJ50myI6ESBGRlA8N80nHBzMjjPRFGm0APADSaCd5NP74SrFtEvL4TRpDwZXETA==", + "dev": true, + "license": "MPL-2.0", "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" + "@types/yauzl": "2.10.3", + "common-tags": "1.8.2", + "first-chunk-stream": "3.0.0", + "strip-bom-stream": "4.0.0", + "upath": "2.0.1", + "yauzl": "2.10.0" }, "peerDependencies": { - "encoding": "^0.1.0" + "body-parser": "1.20.2", + "express": "4.18.3", + "node-fetch": "2.6.11", + "safe-compare": "1.1.4" }, "peerDependenciesMeta": { - "encoding": { + "body-parser": { + "optional": true + }, + "express": { + "optional": true + }, + "node-fetch": { + "optional": true + }, + "safe-compare": { "optional": true } } }, - "node_modules/addons-moz-compare": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz", - "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", - "dev": true, - "license": "MPL-2.0" - }, "node_modules/adm-zip": { "version": "0.5.14", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz", @@ -2598,6 +2536,49 @@ "license": "Apache-2.0", "optional": true }, + "node_modules/bare-fs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", + "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", + "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", + "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.18.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3215,13 +3196,15 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" @@ -3828,32 +3811,30 @@ "license": "MIT" }, "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", "dependencies": { - "import-fresh": "^3.2.1", + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cross-spawn": { @@ -4299,9 +4280,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "version": "0.0.1299070", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==", "dev": true, "license": "BSD-3-Clause" }, @@ -4465,9 +4446,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.807", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz", - "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==", + "version": "1.4.811", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.811.tgz", + "integrity": "sha512-CDyzcJ5XW78SHzsIOdn27z8J4ist8eaFLhdto2hSMSJQgsiwvbv2fbizcKUICryw1Wii1TI/FEkvzvJsR3awrA==", "dev": true, "license": "ISC", "peer": true @@ -4657,12 +4638,11 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.0.0", @@ -4990,10 +4970,11 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.15", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.15.tgz", - "integrity": "sha512-ScHhf9thOoJQ4dXpS+nJXTfNfslG8NBtzQ165PiXWIyGlJcAmkiEeTIEAlIcLA7uztJF7zRiuiKIqOBX4JpVzw==", + "version": "48.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.4.0.tgz", + "integrity": "sha512-xBUxuAx03cKoEA7y+MYSUdwyN8AJyZHbAJ257sOFXgVgCScm574S4zEYJpBoARwaCu4chhCbvA+gdm+00whlxA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@es-joy/jsdoccomment": "~0.43.1", "are-docs-informative": "^0.0.2", @@ -5001,8 +4982,10 @@ "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", + "parse-imports": "^2.1.0", "semver": "^7.6.2", - "spdx-expression-parse": "^4.0.0" + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.0" }, "engines": { "node": ">=18" @@ -5015,7 +4998,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-3.0.2.tgz", "integrity": "sha512-IWME7GIYHXogTkFsToLdBCQVJ0U4kbSuVyDT+nKoR4UgtnVrrVeNWuAZkdEu1nxkvi9nsPccGehEEF6dgA28IQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint-plugin-no-null": { "version": "1.0.2", @@ -8101,9 +8085,9 @@ } }, "node_modules/listr2": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.2.tgz", - "integrity": "sha512-sy0dq+JPS+RAFiFk2K8Nbub7khNmeeoFALNUJ4Wzk34wZKAzaOhEXqGWs4RA5aui0RaM6Hgn7VEKhCj0mlKNLA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.3.tgz", + "integrity": "sha512-Lllokma2mtoniUOS94CcOErHWAug5iu7HOmDrvWgpw8jyQH2fomgB+7lZS4HWZxytUuQwkGOwe49FvwVaA85Xw==", "dev": true, "license": "MIT", "dependencies": { @@ -8669,9 +8653,9 @@ } }, "node_modules/mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true, "license": "MIT" }, @@ -8691,13 +8675,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT" - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -8894,9 +8871,9 @@ } }, "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dev": true, "license": "MIT", "dependencies": { @@ -9094,11 +9071,14 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9243,6 +9223,7 @@ "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.11.2.tgz", "integrity": "sha512-f8dJFVLwdkvPvW3VPFs6q9Vs2+HNhdvwls7a/MIFcQUB+XiQzRe7alfa3RtwfGJU7oUDDMAWPZ0nYsHa23Az+A==", "dev": true, + "license": "LGPL-3.0+", "dependencies": { "asn1.js": "^5.0.0" }, @@ -9616,6 +9597,20 @@ "node": ">=6" } }, + "node_modules/parse-imports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.1.0.tgz", + "integrity": "sha512-JQWgmK2o4w8leUkZeZPatWdAny6vXGU/3siIUvMF6J2rDCud9aTt8h/px9oZJ6U3EcfhngBJ635uPFI0q0VAeA==", + "dev": true, + "license": "Apache 2.0", + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -10332,20 +10327,20 @@ "license": "ISC" }, "node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", + "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" @@ -10413,73 +10408,42 @@ } }, "node_modules/puppeteer": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.9.0.tgz", - "integrity": "sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw==", - "deprecated": "< 22.6.4 is no longer supported", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "1.4.6", - "cosmiconfig": "8.2.0", - "puppeteer-core": "20.9.0" + "@puppeteer/browsers": "2.2.3", + "cosmiconfig": "9.0.0", + "devtools-protocol": "0.0.1299070", + "puppeteer-core": "22.12.0" + }, + "bin": { + "puppeteer": "lib/esm/puppeteer/node/cli.js" }, "engines": { - "node": ">=16.3.0" + "node": ">=18" } }, "node_modules/puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" + "@puppeteer/browsers": "2.2.3", + "chromium-bidi": "0.5.24", + "debug": "4.3.5", + "devtools-protocol": "0.0.1299070", + "ws": "8.17.1" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/puppeteer-core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/puppeteer-core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, "node_modules/qs": { "version": "6.12.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", @@ -11451,6 +11415,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "dev": true, + "license": "ISC" + }, "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -12069,6 +12040,7 @@ "url": "https://github.com/sponsors/stylelint" } ], + "license": "MIT", "dependencies": { "stylelint-config-recommended": "^14.0.1" }, @@ -12106,33 +12078,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stylelint/node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/stylelint/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -12370,11 +12315,29 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.0.tgz", "integrity": "sha512-Fe6sitTNReNdPF1q1w4uz1GAVv9acZff9Q7YILH5n6O/ti3MzwgfEA0aQ6tLjpy+O1NLXnZjUE//xrbluGXzJw==", + "license": "MIT", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" } }, + "node_modules/synckit": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.0.tgz", + "integrity": "sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/table": { "version": "6.8.2", "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", @@ -12659,15 +12622,18 @@ } }, "node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "dev": true, "license": "MIT", "dependencies": { - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/tar-stream": { @@ -13153,15 +13119,15 @@ } }, "node_modules/typescript-eslint": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.13.1.tgz", - "integrity": "sha512-pvLEuRs8iS9s3Cnp/Wt//hpK8nKc8hVa3cLljHqzaJJQYP8oys8GUyIFqtlev+2lT/fqMPcyQko+HJ6iYK3nFA==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.14.1.tgz", + "integrity": "sha512-Eo1X+Y0JgGPspcANKjeR6nIqXl4VL5ldXLc15k4m9upq+eY5fhU2IueiEZL6jmHrKH8aCfbIvM/v3IrX5Hg99w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "7.13.1", - "@typescript-eslint/parser": "7.13.1", - "@typescript-eslint/utils": "7.13.1" + "@typescript-eslint/eslint-plugin": "7.14.1", + "@typescript-eslint/parser": "7.14.1", + "@typescript-eslint/utils": "7.14.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -13335,6 +13301,13 @@ "dev": true, "license": "BSD" }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13609,6 +13582,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/web-ext/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/web-ext/node_modules/yargs": { "version": "17.7.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", @@ -14184,9 +14179,9 @@ } }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "license": "MIT", "engines": { @@ -14278,8 +14273,7 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/yaml": { "version": "2.4.5", @@ -14413,6 +14407,16 @@ "jszip": "^3.2.2" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zxcvbn": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", diff --git a/package.json b/package.json index 9ccc9f2d848..99c890960fd 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.15", + "eslint-plugin-jsdoc": "^48.4.0", "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-only-tests": "3.1.0", @@ -33,11 +33,11 @@ "openpgp": "5.11.2", "pdfjs-dist": "4.3.136", "prettier": "^3.3.2", - "puppeteer": "20.9.0", + "puppeteer": "22.12.0", "stylelint": "16.6.1", "stylelint-config-standard": "36.0.1", "typescript": "5.5.2", - "typescript-eslint": "^7.11.0", + "typescript-eslint": "^7.14.1", "undici-types": "^6.17.0", "web-ext": "7.12.0", "webpack-cli": "^5.1.1" @@ -71,7 +71,7 @@ "test_local_chrome_enterprise_mock": "npm run pretest && npm run test_ci_chrome_enterprise -- -- --pool-size=1 --debug", "test_local_chrome_consumer_live_gmail": "npm run pretest && npm run test_ci_chrome_consumer_live_gmail -- -- --pool-size=1 --debug", "test_local_chrome_consumer_mock_flaky": "npm run pretest && npm run test_ci_chrome_consumer_flaky -- -- --pool-size=1 --retry=false --debug", - "test_local_unit_consumer": "npm run pretest && npx ava --timeout=20m --verbose --tap --concurrency=10 build/test/test/source/test.js -- UNIT-TESTS --retry=false --debug CONSUMER-MOCK | npx tap-xunit > report.xml", + "test_local_unit_consumer": "npm run pretest && npx ava --timeout=20m --verbose --tap --concurrency=10 build/test/test/source/test.js -- UNIT-TESTS --retry=false --pool-size=1 --debug CONSUMER-MOCK | npx tap-xunit > report.xml", "test_local_unit_enterprise": "npm run pretest && npx ava --timeout=20m --verbose --tap --concurrency=10 build/test/test/source/test.js -- UNIT-TESTS --retry=false --pool-size=1 --debug ENTERPRISE-MOCK | npx tap-xunit > report.xml", "test_local_chrome_consumer_mock_headless": "xvfb-run npm run test_local_chrome_consumer_mock", "test_stylelint": "stylelint extension/css/cryptup.css extension/css/settings.css extension/css/webmail.css && stylelint extension/**/*.htm --custom-syntax postcss-html", diff --git a/test/source/browser/browser-handle.ts b/test/source/browser/browser-handle.ts index cf6716addb6..7effc970a79 100644 --- a/test/source/browser/browser-handle.ts +++ b/test/source/browser/browser-handle.ts @@ -1,6 +1,6 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ -import { Browser, EvaluateFunc, Page, Target } from 'puppeteer'; +import { Browser, EvaluateFunc, Page, Target, TargetType } from 'puppeteer'; import { Util } from '../util'; import { ControllablePage } from './controllable'; import { Semaphore } from './browser-pool'; @@ -28,6 +28,15 @@ export class BrowserHandle { const page = await this.browser.newPage(); if (extraHeaders !== undefined) { await page.setExtraHTTPHeaders(extraHeaders); + await page.setRequestInterception(true); + page.on('request', async request => { + const headers = request.headers(); + if (request.url().startsWith('https://flowcrypt.s3.amazonaws.com')) { + // S3 returns 400 error when request contains Authorization header + headers.Authorization = ''; + } + return await request.continue({ headers }); + }); } await page.setViewport(this.viewport); const controllablePage = new ControllablePage(t, page); @@ -137,9 +146,9 @@ export class BrowserHandle { setTimeout(() => reject(new Error('Action did not trigger a new page within timeout period')), TIMEOUT_ELEMENT_APPEAR * 1000); let resolved = 0; const listener = async (target: Target) => { - if (target.type() === 'page') { + if (target.type() === TargetType.PAGE) { if (!resolved++) { - this.browser.removeListener('targetcreated', listener); + this.browser.off('targetcreated', listener); target.page().then(resolve, reject); } } diff --git a/test/source/browser/controllable.ts b/test/source/browser/controllable.ts index 3c24ca6830f..3dcfd4f5ffd 100644 --- a/test/source/browser/controllable.ts +++ b/test/source/browser/controllable.ts @@ -64,7 +64,7 @@ abstract class ControllableBase { this.log(`wait_all:2:${selector}`); if (this.isXpath(selector)) { this.log(`wait_all:3:${selector}`); - await this.target.waitForXPath(selector, { timeout: timeout * 1000 }); + await this.target.waitForSelector(`xpath/.${selector}`, { timeout: timeout * 1000 }); this.log(`wait_all:4:${selector}`); } else { this.log(`wait_all:5:${selector}`); @@ -581,7 +581,7 @@ abstract class ControllableBase { protected firstElement = async (selector: string): Promise => { selector = this.selector(selector); if (this.isXpath(selector)) { - return (await this.target.$x(selector))[0] as ElementHandle; + return (await this.target.$$(`xpath/.${selector}`))[0]; } else { return await this.target.$(selector); } @@ -608,7 +608,7 @@ abstract class ControllableBase { protected elements = async (selector: string) => { selector = this.selector(selector); if (this.isXpath(selector)) { - return (await this.target.$x(selector)) as ElementHandle[]; + return await this.target.$$(`xpath/.${selector}`); } else { return await this.target.$$(selector); } @@ -624,11 +624,11 @@ abstract class ControllableBase { while (timeout-- > 0) { try { for (const selector of processedSelectors) { - const elements = await (this.isXpath(selector) ? this.target.$x(selector) : this.target.$$(selector)); + const elements = await (this.isXpath(selector) ? this.target.$$(`xpath/.${selector}`) : this.target.$$(selector)); for (const element of elements) { if (!visible || (await Util.isVisible(element))) { // element is visible - return element as ElementHandle; + return element; } } } diff --git a/test/source/browser/test-urls.ts b/test/source/browser/test-urls.ts index 14e235cee5a..4365caff04d 100644 --- a/test/source/browser/test-urls.ts +++ b/test/source/browser/test-urls.ts @@ -3,7 +3,10 @@ import { GmailCategory } from '../tests/gmail'; export class TestUrls { - public constructor(public extensionId: string, public port?: number) {} + public constructor( + public extensionId: string, + public port?: number + ) {} public static googleChat = (acctLoginIndex = 0) => { return `https://mail.google.com/chat/u/${acctLoginIndex}`; diff --git a/test/source/test.ts b/test/source/test.ts index 76f7abf93de..50717e7b453 100644 --- a/test/source/test.ts +++ b/test/source/test.ts @@ -169,7 +169,7 @@ test.after.always('evaluate Catch.reportErr errors', async t => { .filter(e => !e.trace.includes('-1 when GET-ing https://fes.example.com')) // todo - ideally mock tests would never call this. But we do tests with human@flowcrypt.com so it's calling here .filter(e => !e.trace.includes('-1 when GET-ing https://openpgpkey.flowcrypt.com')) - // below for "test allows to retry public key search when attester returns error" + // below for test "allows to retry public key search when attester returns error" .filter( e => !e.message.match(/Error: Internal Server Error: 500 when GET-ing https:\/\/localhost:\d+\/attester\/pub\/attester\.return\.error@flowcrypt\.test/) ); diff --git a/test/source/tests/browser-unit-tests/unit-Catch.js b/test/source/tests/browser-unit-tests/unit-Catch.js index 694029fdca3..78a4bf95245 100644 --- a/test/source/tests/browser-unit-tests/unit-Catch.js +++ b/test/source/tests/browser-unit-tests/unit-Catch.js @@ -62,10 +62,10 @@ BROWSER_UNIT_TEST_NAME(`Catcher does report sensitive infos`); BROWSER_UNIT_TEST_NAME(`Catcher does not include query string on report`); (async () => { - const formatted = Catch.formatExceptionForReport({'name': 'Error'}); + const formatted = Catch.formatExceptionForReport({ name: 'Error' }); const expectedUrl = 'chrome-extension://extension-id/chrome/dev/ci_unit_test.htm'; if (formatted.url.indexOf('?') !== -1) { - const url = formatted.url.replace(/(\w{32})/,'extension-id'); + const url = formatted.url.replace(/(\w{32})/, 'extension-id'); throw new Error(`The reported URL where the error occurred should not include query strings. Expecting ${expectedUrl} but got ${url}.`); } return 'pass'; diff --git a/test/source/tests/compose.ts b/test/source/tests/compose.ts index 1a12c9fbd9d..602b7f15754 100644 --- a/test/source/tests/compose.ts +++ b/test/source/tests/compose.ts @@ -1549,7 +1549,7 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te }); await BrowserRecipe.setupCommonAcctWithAttester(t, browser, 'ci.tests.gmail'); let composePage = await ComposePageRecipe.openStandalone(t, browser, 'compose'); - await composePage.waitAndClick('@action-show-container-cc-bcc-buttons'); + await composePage.waitAndClick('@action-show-cc'); await composePage.type('@input-to', 'contact'); if (testVariant === 'CONSUMER-MOCK') { // consumer does not get Contacts scope automatically (may scare users when they install) @@ -1563,7 +1563,7 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te await ComposePageRecipe.expectContactsResultEqual(composePage, ['contact.test@flowcrypt.com']); // re-load the compose window, expect that it remembers scope was connected, and remembers the contact composePage = await ComposePageRecipe.openStandalone(t, browser, 'compose'); - await composePage.waitAndClick('@action-show-container-cc-bcc-buttons'); + await composePage.waitAndClick('@action-show-cc'); await composePage.type('@input-to', 'contact'); await ComposePageRecipe.expectContactsResultEqual(composePage, ['contact.test@flowcrypt.com']); await composePage.notPresent('@action-auth-with-contacts-scope'); diff --git a/test/source/tests/flaky.ts b/test/source/tests/flaky.ts index affd9ca8d4c..68a36d957ef 100644 --- a/test/source/tests/flaky.ts +++ b/test/source/tests/flaky.ts @@ -24,6 +24,7 @@ import { singlePubKeyAttesterConfig, somePubkey } from '../mock/attester/atteste import { flowcryptCompatibilityAliasList } from '../mock/google/google-endpoints'; import { processMessageFromUser4 } from '../mock/fes/fes-constants'; import { standardSubDomainFesClientConfiguration } from '../mock/fes/customer-url-fes-endpoints'; +import { minutes } from './tooling'; // these tests are run serially, one after another, because they are somewhat more sensitive to parallel testing // eg if they are very cpu-sensitive (create key tests) @@ -267,120 +268,123 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test test( 'user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - some sends fail with BadRequest error', - testWithBrowser(async (t, browser) => { - t.context.mockApi!.configProvider = new ConfigurationProvider({ - attester: { - pubkeyLookup: { - 'flowcrypt.compatibility@gmail.com': { - pubkey: somePubkey, + testWithBrowser( + async (t, browser) => { + t.context.mockApi!.configProvider = new ConfigurationProvider({ + attester: { + pubkeyLookup: { + 'flowcrypt.compatibility@gmail.com': { + pubkey: somePubkey, + }, }, }, - }, - fes: { - messagePostValidator: processMessageFromUser4, - clientConfiguration: standardSubDomainFesClientConfiguration, - }, - }); - const port = t.context.urls?.port; - const acct = `user4@standardsubdomainfes.localhost:${port}`; // added port to trick extension into calling the mock - const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); - await SetupPageRecipe.manualEnter( - settingsPage, - 'flowcrypt.test.key.used.pgp', - { submitPubkey: false, usedPgpBefore: false }, - { isSavePassphraseChecked: false, isSavePassphraseHidden: false } - ); - // add a name to one of the contacts - const dbPage = await browser.newExtensionPage(t, 'chrome/dev/ci_unit_test.htm'); - await dbPage.page.evaluate(async () => { - /* eslint-disable @typescript-eslint/no-explicit-any */ - const db = await (window as any).ContactStore.dbOpen(); - await (window as any).ContactStore.update(db, 'cc@example.com', { name: 'Mr Cc' }); - /* eslint-enable @typescript-eslint/no-explicit-any */ - }); - await dbPage.close(); - const subject = 'PWD encrypted message with FES web portal - some sends fail with BadRequest error - ' + testVariant; - let expectedNumberOfPassedMessages = (await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length; - // 1. vague Gmail error with partial success - let composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'confirm', - 'cancel', - 'Messages to some recipients were sent successfully, while messages to flowcrypt.compatibility@gmail.com, Mr Cc ' + - 'encountered error(s) from Gmail. Please help us improve FlowCrypt by reporting the error to us.' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 2. vague Gmail error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'confirm', - 'cancel', - 'Google returned an error when sending message. ' + 'Please help us improve FlowCrypt by reporting the error to us.' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // 3. "invalid To" Gmail error with partial success - composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'to@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'error', - 'confirm', - 'Messages to some recipients were sent successfully, while messages to invalid@example.com ' + - 'encountered error(s) from Gmail: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 4. "invalid To" Gmail error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'cc@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'error', - 'confirm', - 'Error from google: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // 5. "RequestTimeout" error with partial success - composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'to@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'error', - 'confirm', - 'Messages to some recipients were sent successfully, while messages to timeout@example.com ' + - 'encountered network errors. Please check your internet connection and try again.' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 6. "RequestTimeout" error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); - await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'cc@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal( - 'error', - 'confirm', - 'Could not send message due to network error. Please check your internet connection and try again. ' + - '(This may also be caused by missing extension permissions).' - ); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // this test is using PwdEncryptedMessageWithFesReplyBadRequestTestStrategy to check sent result based on subject - // "PWD encrypted message with FES web portal - some sends fail with BadRequest error" - // also see '/api/v1/message' in customer-url-fes-endpoints.ts mock - }) + fes: { + messagePostValidator: processMessageFromUser4, + clientConfiguration: standardSubDomainFesClientConfiguration, + }, + }); + const port = t.context.urls?.port; + const acct = `user4@standardsubdomainfes.localhost:${port}`; // added port to trick extension into calling the mock + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); + await SetupPageRecipe.manualEnter( + settingsPage, + 'flowcrypt.test.key.used.pgp', + { submitPubkey: false, usedPgpBefore: false }, + { isSavePassphraseChecked: false, isSavePassphraseHidden: false } + ); + // add a name to one of the contacts + const dbPage = await browser.newExtensionPage(t, 'chrome/dev/ci_unit_test.htm'); + await dbPage.page.evaluate(async () => { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const db = await (window as any).ContactStore.dbOpen(); + await (window as any).ContactStore.update(db, 'cc@example.com', { name: 'Mr Cc' }); + /* eslint-enable @typescript-eslint/no-explicit-any */ + }); + await dbPage.close(); + const subject = 'PWD encrypted message with FES web portal - some sends fail with BadRequest error - ' + testVariant; + let expectedNumberOfPassedMessages = (await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length; + // 1. vague Gmail error with partial success + let composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'confirm', + 'cancel', + 'Messages to some recipients were sent successfully, while messages to flowcrypt.compatibility@gmail.com, Mr Cc ' + + 'encountered error(s) from Gmail. Please help us improve FlowCrypt by reporting the error to us.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 2. vague Gmail error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'confirm', + 'cancel', + 'Google returned an error when sending message. ' + 'Please help us improve FlowCrypt by reporting the error to us.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // 3. "invalid To" Gmail error with partial success + composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'to@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'error', + 'confirm', + 'Messages to some recipients were sent successfully, while messages to invalid@example.com ' + + 'encountered error(s) from Gmail: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 4. "invalid To" Gmail error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'cc@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'error', + 'confirm', + 'Error from google: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // 5. "RequestTimeout" error with partial success + composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'to@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'error', + 'confirm', + 'Messages to some recipients were sent successfully, while messages to timeout@example.com ' + + 'encountered network errors. Please check your internet connection and try again.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 6. "RequestTimeout" error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, `user4@standardsubdomainfes.localhost:${port}`); + await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'cc@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal( + 'error', + 'confirm', + 'Could not send message due to network error. Please check your internet connection and try again.' + ); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // this test is using PwdEncryptedMessageWithFesReplyBadRequestTestStrategy to check sent result based on subject + // "PWD encrypted message with FES web portal - some sends fail with BadRequest error" + // also see '/api/v1/message' in customer-url-fes-endpoints.ts mock + }, + undefined, + minutes(8) + ) ); test( @@ -592,43 +596,47 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test ); test( 'decrypt - entering pass phrase should unlock all keys that match the pass phrase', - testWithBrowser(async (t, browser) => { - const acctEmail = 'flowcrypt.compatibility@gmail.com'; - await BrowserRecipe.setupCommonAcctWithAttester(t, browser, 'compatibility'); - const passphrase = 'pa$$w0rd'; - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey17AD7D07, passphrase, {}, false); - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey0389D3A7, passphrase, {}, false); - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testKeyMultipleSmimeCEA2D53BB9D24871, passphrase, {}, false); - const inboxPage = await browser.newExtensionInboxPage(t, acctEmail); - await BrowserRecipe.finishSession(inboxPage); - await InboxPageRecipe.checkDecryptMsg(t, browser, { - acctEmail, - threadId: '17c0e50966d7877c', - content: ['1st key of of 2 keys with the same passphrase'], - enterPp: { - passphrase, - isForgetPpChecked: true, - isForgetPpHidden: false, - }, - }); - await InboxPageRecipe.checkDecryptMsg(t, browser, { - acctEmail, - threadId: '17c0e55caaa4abb3', - content: ['2nd key of of 2 keys with the same passphrase'], - // passphrase for the 2nd key should not be needed because it's the same as for the 1st key - }); - // as decrypted s/mime messages are not rendered yet (#4070), let's test signing instead - const composeFrame = await InboxPageRecipe.openAndGetComposeFrame(inboxPage); - await ComposePageRecipe.fillMsg(composeFrame, { to: 'smime@recipient.com' }, 'send signed and encrypted S/MIME without attachment'); - await ComposePageRecipe.pastePublicKeyManually( - composeFrame, - inboxPage, - 'smime@recipient.com', - testConstants.testCertificateMultipleSmimeCEA2D53BB9D24871 - ); - await composeFrame.waitAndClick('@action-send', { delay: 2 }); - await inboxPage.waitTillGone('@container-new-message'); - }) + testWithBrowser( + async (t, browser) => { + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + await BrowserRecipe.setupCommonAcctWithAttester(t, browser, 'compatibility'); + const passphrase = 'pa$$w0rd'; + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey17AD7D07, passphrase, {}, false); + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey0389D3A7, passphrase, {}, false); + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testKeyMultipleSmimeCEA2D53BB9D24871, passphrase, {}, false); + const inboxPage = await browser.newExtensionInboxPage(t, acctEmail); + await BrowserRecipe.finishSession(inboxPage); + await InboxPageRecipe.checkDecryptMsg(t, browser, { + acctEmail, + threadId: '17c0e50966d7877c', + content: ['1st key of of 2 keys with the same passphrase'], + enterPp: { + passphrase, + isForgetPpChecked: true, + isForgetPpHidden: false, + }, + }); + await InboxPageRecipe.checkDecryptMsg(t, browser, { + acctEmail, + threadId: '17c0e55caaa4abb3', + content: ['2nd key of of 2 keys with the same passphrase'], + // passphrase for the 2nd key should not be needed because it's the same as for the 1st key + }); + // as decrypted s/mime messages are not rendered yet (#4070), let's test signing instead + const composeFrame = await InboxPageRecipe.openAndGetComposeFrame(inboxPage); + await ComposePageRecipe.fillMsg(composeFrame, { to: 'smime@recipient.com' }, 'send signed and encrypted S/MIME without attachment'); + await ComposePageRecipe.pastePublicKeyManually( + composeFrame, + inboxPage, + 'smime@recipient.com', + testConstants.testCertificateMultipleSmimeCEA2D53BB9D24871 + ); + await composeFrame.waitAndClick('@action-send', { delay: 2 }); + await inboxPage.waitTillGone('@container-new-message'); + }, + undefined, + minutes(8) + ) ); test.skip( 'decrypt - benchmark decryption of 50 pgp messages', diff --git a/test/source/tests/page-recipe/settings-page-recipe.ts b/test/source/tests/page-recipe/settings-page-recipe.ts index 980f0fa8e14..b17bbea6f24 100644 --- a/test/source/tests/page-recipe/settings-page-recipe.ts +++ b/test/source/tests/page-recipe/settings-page-recipe.ts @@ -24,11 +24,11 @@ export class SettingsPageRecipe extends PageRecipe { public static toggleScreen = async (settingsPage: ControllablePage, to: 'basic' | 'additional') => { await SettingsPageRecipe.ready(settingsPage); - await Util.sleep(0.5); + await Util.sleep(1); await settingsPage.waitAndClick(to === 'basic' ? '@action-toggle-screen-basic' : '@action-toggle-screen-additional'); // switch - await Util.sleep(0.5); + await Util.sleep(1); await settingsPage.waitAll(to === 'basic' ? '@action-toggle-screen-additional' : '@action-toggle-screen-basic'); // wait for opposite button to show up - await Util.sleep(0.5); + await Util.sleep(1); }; public static closeDialog = async (settingsPage: ControllablePage) => { diff --git a/test/source/tests/settings.ts b/test/source/tests/settings.ts index 037d7bffca6..2013ad0b4e7 100644 --- a/test/source/tests/settings.ts +++ b/test/source/tests/settings.ts @@ -373,7 +373,7 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T await contactsFrame.waitAndClick('@action-show-import-public-keys-form', { confirmGone: true }); const [fileChooser] = await Promise.all([ settingsPage.page.waitForFileChooser(), - contactsFrame.waitAndClick('@action-attach-files', { retryErrs: true }), + contactsFrame.waitAndClick('@action-upload-key', { retryErrs: true }), ]); await fileChooser.accept(['test/samples/openpgp/pub_keys_5_good_1_unsupported.txt']); await Util.sleep(1); diff --git a/test/source/tests/setup.ts b/test/source/tests/setup.ts index 20567e984b5..4b4d0a0a89a 100644 --- a/test/source/tests/setup.ts +++ b/test/source/tests/setup.ts @@ -646,24 +646,26 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== await BrowserRecipe.setupCommonAcctWithAttester(t, browser, 'compatibility'); // Wipe google tokens to test re-auth popup await Util.wipeGoogleTokensUsingExperimentalSettingsPage(t, browser, acctEmail); - const gmailPage = await openMockGmailPage(t, browser, acctEmail); - await gmailPage.waitAndClick('@action-secure-compose'); + const gmailPage1 = await openMockGmailPage(t, browser, acctEmail); + await gmailPage1.waitAndClick('@action-secure-compose'); // Check reconnect auth notification - await gmailPage.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); - let oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-reconnect-account')); + await gmailPage1.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); + let oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage1.waitAndClick('@action-reconnect-account')); // mock api will return missing scopes await OauthPageRecipe.mock(t, oauthPopup, acctEmail, 'missing_permission'); // Check missing permission notification - await gmailPage.waitForContent('@webmail-notification-setup', 'Connection successful. Please also add missing permissions'); - oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-add-missing-permission')); + await gmailPage1.waitForContent('@webmail-notification-setup', 'Connection successful. Please also add missing permissions'); + oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage1.waitAndClick('@action-add-missing-permission')); await OauthPageRecipe.mock(t, oauthPopup, acctEmail, 'approve'); // after successful reauth, check if connection is successful - await gmailPage.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); + await gmailPage1.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); + await gmailPage1.close(); // reload and test that it has no more notifications - await gmailPage.page.reload(); - await gmailPage.waitAndClick('@action-secure-compose'); + const gmailPage2 = await openMockGmailPage(t, browser, acctEmail); + await gmailPage2.waitAndClick('@action-secure-compose'); await Util.sleep(2); - await gmailPage.notPresent(['@webmail-notification-setup']); + await gmailPage2.notPresent(['@webmail-notification-setup']); + await gmailPage2.close(); }) ); @@ -672,33 +674,36 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== testWithBrowser(async (t, browser) => { const acct = 'flowcrypt.compatibility@gmail.com'; await BrowserRecipe.setupCommonAcctWithAttester(t, browser, 'compatibility'); - const gmailPage = await openMockGmailPage(t, browser, acct); - await gmailPage.waitAll(['@webmail-notification-setup', '@notification-successfully-setup-action-close']); - await gmailPage.waitAndClick('@notification-successfully-setup-action-close', { confirmGone: true }); - await gmailPage.page.reload(); - await gmailPage.notPresent(['@webmail-notification-setup', '@notification-setup-action-close', '@notification-successfully-setup-action-close']); + const gmailPage1 = await openMockGmailPage(t, browser, acct); + await gmailPage1.waitAll(['@webmail-notification-setup', '@notification-successfully-setup-action-close']); + await gmailPage1.waitAndClick('@notification-successfully-setup-action-close', { confirmGone: true }); + await gmailPage1.page.reload(); + await gmailPage1.notPresent(['@webmail-notification-setup', '@notification-setup-action-close', '@notification-successfully-setup-action-close']); + await gmailPage1.close(); // below test that can re-auth after lost access (simulating situation when user changed password on google) await Util.wipeGoogleTokensUsingExperimentalSettingsPage(t, browser, acct); const settingsPage = await browser.newExtensionSettingsPage(t, acct); await settingsPage.waitAndRespondToModal('confirm', 'cancel', 'FlowCrypt must be re-connected to your Google account.'); + await settingsPage.close(); // *** these tests below are very flaky in CI environment, Google will want to re-authenticate the user for whatever reason // // opening secure compose should trigger an api call which causes a reconnect notification - await gmailPage.page.bringToFront(); - await gmailPage.page.reload(); - await gmailPage.waitAndClick('@action-secure-compose'); - await gmailPage.waitAll(['@webmail-notification-setup', '@action-reconnect-account']); + const gmailPage2 = await openMockGmailPage(t, browser, acct); + await gmailPage2.waitAndClick('@action-secure-compose'); + await gmailPage2.waitAll(['@webmail-notification-setup', '@action-reconnect-account']); await Util.sleep(1); - await gmailPage.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); - const oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-reconnect-account')); + await gmailPage2.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); + const oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage2.waitAndClick('@action-reconnect-account')); await OauthPageRecipe.google(t, oauthPopup, acct, 'approve'); - await gmailPage.waitAll(['@webmail-notification-setup']); + await gmailPage2.waitAll(['@webmail-notification-setup']); await Util.sleep(1); - await gmailPage.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); + await gmailPage2.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); + await gmailPage2.close(); // reload and test that it has no more notifications - await gmailPage.page.reload(); - await gmailPage.waitAndClick('@action-secure-compose'); + const gmailPage3 = await openMockGmailPage(t, browser, acct); + await gmailPage3.waitAndClick('@action-secure-compose'); await Util.sleep(1); - await gmailPage.notPresent(['@webmail-notification-setup']); + await gmailPage3.notPresent(['@webmail-notification-setup']); + await gmailPage3.close(); }) ); diff --git a/tooling/release-firefox-latest-update.json b/tooling/release-firefox-latest-update.json index 3385d0e4860..2579e0a725e 100644 --- a/tooling/release-firefox-latest-update.json +++ b/tooling/release-firefox-latest-update.json @@ -15,4 +15,4 @@ ] } } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 3fe1c226d81..ad00802a4bc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, "noImplicitReturns": true,