diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index ac0b1cad27e..6a79934cbdb 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -95,11 +95,10 @@ export class ComposeDraftModule extends ViewModule { await Ui.time.wait(() => !this.currentlySavingDraft ? true : undefined); if (this.view.draftId) { try { - if (this.isLocalDraftId(this.view.draftId)) { - await this.localDraftRemove(); - } else { + if (!this.isLocalDraftId(this.view.draftId)) { await this.view.emailProvider.draftDelete(this.view.draftId); } + await this.localDraftRemove(); this.view.draftId = ''; } catch (e) { if (ApiErr.isAuthErr(e)) { diff --git a/package-lock.json b/package-lock.json index dacbbdca86f..68fb0971365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "openpgp": "4.10.10", "postcss-html": "^1.2.0", "squire-rte": "1.11.3", - "sweetalert2": "^11.1.9", + "sweetalert2": "11.1.9", "zxcvbn": "4.4.2" }, "devDependencies": { @@ -47,7 +47,7 @@ "mkdirp": "1.0.4", "openpgp": "4.10.10", "pdfjs-dist": "2.10.377", - "puppeteer": "10.2.0", + "puppeteer": "11.0.0", "stylelint": "14.0.1", "stylelint-config-standard": "23.0.0", "tslint": "6.1.3", @@ -2468,9 +2468,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -5928,6 +5928,12 @@ "node": ">=10" } }, + "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 + }, "node_modules/moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -6110,10 +6116,13 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" } @@ -7089,36 +7098,48 @@ } }, "node_modules/puppeteer": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.2.0.tgz", - "integrity": "sha512-OR2CCHRashF+f30+LBOtAjK6sNtz2HEyTr5FqAvhf8lR/qB3uBRoIZOwQKgwoyZnMBsxX7ZdazlyBgGjpnkiMw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-11.0.0.tgz", + "integrity": "sha512-6rPFqN1ABjn4shgOICGDBITTRV09EjXVqhDERBDKwCLz0UyBxeeBH6Ay0vQUJ84VACmlxwzOIzVEJXThcF3aNg==", "dev": true, "hasInstallScript": true, "dependencies": { - "debug": "4.3.1", + "debug": "4.3.2", "devtools-protocol": "0.0.901419", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.0", - "node-fetch": "2.6.1", + "node-fetch": "2.6.5", "pkg-dir": "4.2.0", - "progress": "2.0.1", + "progress": "2.0.3", "proxy-from-env": "1.1.0", "rimraf": "3.0.2", - "tar-fs": "2.0.0", - "unbzip2-stream": "1.3.3", - "ws": "7.4.6" + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.2.3" }, "engines": { "node": ">=10.18.1" } }, - "node_modules/puppeteer/node_modules/progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", - "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", + "node_modules/puppeteer/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "dev": true, "engines": { - "node": ">=0.4.0" + "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/qs": { @@ -8221,23 +8242,6 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "node_modules/stylelint/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/stylelint/node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -8433,27 +8437,15 @@ } }, "node_modules/tar-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "dependencies": { "chownr": "^1.1.1", - "mkdirp": "^0.5.1", + "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "node_modules/tar-fs/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "tar-stream": "^2.1.4" } }, "node_modules/tar-stream": { @@ -8618,6 +8610,12 @@ "node": ">=0.8" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -8804,9 +8802,9 @@ "dev": true }, "node_modules/unbzip2-stream": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", - "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "dependencies": { "buffer": "^5.2.1", @@ -9354,6 +9352,12 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, "node_modules/well-known-symbols": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", @@ -9363,6 +9367,16 @@ "node": ">=6" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/when": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz", @@ -11509,9 +11523,9 @@ "dev": true }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -14218,6 +14232,12 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "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 + }, "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -14361,10 +14381,13 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } }, "node-forge": { "version": "0.10.0", @@ -15086,30 +15109,31 @@ } }, "puppeteer": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.2.0.tgz", - "integrity": "sha512-OR2CCHRashF+f30+LBOtAjK6sNtz2HEyTr5FqAvhf8lR/qB3uBRoIZOwQKgwoyZnMBsxX7ZdazlyBgGjpnkiMw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-11.0.0.tgz", + "integrity": "sha512-6rPFqN1ABjn4shgOICGDBITTRV09EjXVqhDERBDKwCLz0UyBxeeBH6Ay0vQUJ84VACmlxwzOIzVEJXThcF3aNg==", "dev": true, "requires": { - "debug": "4.3.1", + "debug": "4.3.2", "devtools-protocol": "0.0.901419", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.0", - "node-fetch": "2.6.1", + "node-fetch": "2.6.5", "pkg-dir": "4.2.0", - "progress": "2.0.1", + "progress": "2.0.3", "proxy-from-env": "1.1.0", "rimraf": "3.0.2", - "tar-fs": "2.0.0", - "unbzip2-stream": "1.3.3", - "ws": "7.4.6" + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.2.3" }, "dependencies": { - "progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", - "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", - "dev": true + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "requires": {} } } }, @@ -15972,15 +15996,6 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -16152,26 +16167,15 @@ } }, "tar-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "requires": { "chownr": "^1.1.1", - "mkdirp": "^0.5.1", + "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } + "tar-stream": "^2.1.4" } }, "tar-stream": { @@ -16299,6 +16303,12 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -16437,9 +16447,9 @@ "dev": true }, "unbzip2-stream": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", - "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "requires": { "buffer": "^5.2.1", @@ -16841,12 +16851,28 @@ } } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, "well-known-symbols": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", "dev": true }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "when": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz", diff --git a/package.json b/package.json index 52399116d65..f25d90be285 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "mkdirp": "1.0.4", "openpgp": "4.10.10", "pdfjs-dist": "2.10.377", - "puppeteer": "10.2.0", + "puppeteer": "11.0.0", "stylelint": "14.0.1", "stylelint-config-standard": "23.0.0", "tslint": "6.1.3", diff --git a/test/source/browser/controllable.ts b/test/source/browser/controllable.ts index 277a538c307..d89639973da 100644 --- a/test/source/browser/controllable.ts +++ b/test/source/browser/controllable.ts @@ -273,11 +273,11 @@ abstract class ControllableBase { await this.waitAndClick(`@ui-modal-${type}-${clickBtn}`); } - public waitAndClick = async (selector: string, { delay = 0.1, confirmGone = false, retryErrs = false, sleepWhenDone }: - { delay?: number, confirmGone?: boolean, retryErrs?: boolean, sleepWhenDone?: number } = {}) => { + public waitAndClick = async (selector: string, { delay = 0.1, timeout = TIMEOUT_ELEMENT_APPEAR, confirmGone = false, retryErrs = false, sleepWhenDone }: + { delay?: number, timeout?: number, confirmGone?: boolean, retryErrs?: boolean, sleepWhenDone?: number } = {}) => { for (const i of [1, 2, 3]) { this.log(`wait_and_click(i${i}):1:${selector}`); - await this.waitAll(selector); + await this.waitAll(selector, { timeout }); this.log(`wait_and_click(i${i}):2:${selector}`); await Util.sleep(delay); this.log(`wait_and_click(i${i}):3:${selector}`); @@ -288,7 +288,11 @@ abstract class ControllableBase { break; } catch (e) { this.log(`wait_and_click(i${i}):6:err(${String(e)}):${selector}`); - if (e.message === 'Node is either not visible or not an HTMLElement' || e.message === 'Node is detached from document') { + if ( + e.message === 'Node is either not visible or not an HTMLElement' || + e.message === 'Node is either not clickable or not an HTMLElement' || + e.message === 'Node is detached from document' + ) { // maybe the node just re-rendered? if (!retryErrs || i === 3) { e.stack = `[clicking(${selector}) failed because element quickly disappeared, consider adding retryErrs]\n` + e.stack; diff --git a/test/source/mock/google/live-gmail-test-emails/[ci.test] encrypted email for reply render (FMfcgzGkbDRNgcPktjdSxpJVhZlZqpTr).eml b/test/source/mock/google/live-gmail-test-emails/[ci.test] encrypted email for reply render (FMfcgzGlkjftvKTsGnTltMvmZdDzdPFB).eml similarity index 100% rename from test/source/mock/google/live-gmail-test-emails/[ci.test] encrypted email for reply render (FMfcgzGkbDRNgcPktjdSxpJVhZlZqpTr).eml rename to test/source/mock/google/live-gmail-test-emails/[ci.test] encrypted email for reply render (FMfcgzGlkjftvKTsGnTltMvmZdDzdPFB).eml diff --git a/test/source/tests/gmail.ts b/test/source/tests/gmail.ts index 75dbe76aa11..30140b1ddb3 100644 --- a/test/source/tests/gmail.ts +++ b/test/source/tests/gmail.ts @@ -1,9 +1,9 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ import * as ava from 'ava'; -import { Page } from 'puppeteer'; import { BrowserHandle, ControllablePage } from './../browser'; +import { Controllable } from './../browser/controllable'; import { TestVariant, Util } from './../util'; import { AvaContext } from './tooling'; import { BrowserRecipe } from './tooling/browser-recipe'; @@ -44,29 +44,35 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test }; const createSecureDraft = async (t: AvaContext, browser: BrowserHandle, gmailPage: ControllablePage, content: string, params: { offline: boolean } = { offline: false }) => { - const urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 1 }); - const composeBox = await browser.newPage(t, urls[0]); + let composeBox: Controllable | undefined; if (params.offline) { - await (composeBox.target as Page).setOfflineMode(true); // go offline mode + // TODO(@limonte): for some reason iframe is able to save the draft to the cloud even + // after gmailPage.page.setOfflineMode(true) is called. Probably, the puppeteer issue, revisit. + // const composeBoxFrame = await gmailPage.getFrame(['/chrome/elements/compose.htm']); + const urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 1 }); + composeBox = await browser.newPage(t, urls[0]); + await composeBox.page.setOfflineMode(true); // go offline mode + } else { + composeBox = await gmailPage.getFrame(['/chrome/elements/compose.htm']); } - await Util.sleep(4); // the draft isn't being saved if start typing without this delay + await Util.sleep(5); // the draft isn't being saved if start typing without this delay await composeBox.type('@input-body', content, true); if (params.offline) { await ComposePageRecipe.waitWhenDraftIsSavedLocally(composeBox); + await (composeBox as ControllablePage).close(); } else { await ComposePageRecipe.waitWhenDraftIsSaved(composeBox); } - await composeBox.close(); }; - const pageHasSecureDraft = async (t: AvaContext, browser: BrowserHandle, url: string, expectedContent?: string) => { - const secureDraft = await browser.newPage(t, url); + const pageHasSecureDraft = async (gmailPage: ControllablePage, expectedContent?: string) => { + const secureDraftFrame = await gmailPage.getFrame(['/chrome/elements/compose.htm', '&draftId=']); if (expectedContent) { - await secureDraft.waitForContent('@input-body', expectedContent); + await secureDraftFrame.waitForContent('@input-body', expectedContent); } else { - await secureDraft.waitAll('@input-body'); + await secureDraftFrame.waitAll('@input-body'); } - return secureDraft; + return secureDraftFrame; }; const pageDoesNotHaveSecureReplyContainer = async (gmailPage: ControllablePage) => { @@ -76,12 +82,7 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test const openGmailPage = async (t: AvaContext, browser: BrowserHandle, path: string): Promise => { const url = TestUrls.gmail(0, path); - const gmailPage = await browser.newPage(t, url); - await gmailPage.waitAll('@action-secure-compose'); - if (path) { // gmail does weird things with navigation sometimes, nudge it again - await gmailPage.goto(url); - } - return gmailPage; + return await browser.newPage(t, url); }; const gotoGmailPage = async (gmailPage: ControllablePage, path: string, category: GmailCategory = 'inbox') => { @@ -180,14 +181,10 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test await Util.sleep(2); // wait for search results await gmailPage.page.setOfflineMode(true); // go offline mode await gmailPage.press('Enter'); // open the message - // TODO(@limonte): use the commented line below instead of opening pgp block in a new tab - // once https://github.com/puppeteer/puppeteer/issues/2548 is resolved - // const pgpBlockPage = await gmailPage.getFrame(['pgp_block.htm']); - const urls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_block.htm'], { sleep: 1 }); - const pgpBlockPage = await browser.newPage(t); - await pgpBlockPage.page.setOfflineMode(true); // go offline mode - await pgpBlockPage.page.goto(urls[0]); - await pgpBlockPage.waitForContent('@pgp-block-content', 'this should decrypt even offline'); + const pgpBlockFrame = await gmailPage.getFrame(['pgp_block.htm']); + await gmailPage.page.setOfflineMode(true); // go offline mode + await pgpBlockFrame.frame.goto(await pgpBlockFrame.frame.url()); // reload the frame + await pgpBlockFrame.waitForContent('@pgp-block-content', 'this should decrypt even offline'); })); ava.default('mail.google.com - rendering attachmnents', testWithBrowser('ci.tests.gmail', async (t, browser) => { @@ -215,13 +212,9 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test const signature = ['Limon.Monte@Gmail.Com', 'matching signature']; await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { params: url, content: ['1234'], signature }); await pageHasSecureReplyContainer(t, browser, gmailPage); - // validate pgp_pubkey.htm is rendered - const pgpPubkeyUrls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_pubkey.htm'], { sleep: 10, appearIn: 20 }); - expect(pgpPubkeyUrls.length).to.equal(1); - await pageHasSecureReplyContainer(t, browser, gmailPage); await testMinimumElementHeight(gmailPage, '.pgp_block.signedMsg', 80); await testMinimumElementHeight(gmailPage, '.pgp_block.publicKey', 120); - const pubkeyPage = await browser.newPage(t, pgpPubkeyUrls[0]); + const pubkeyPage = await gmailPage.getFrame(['/chrome/elements/pgp_pubkey.htm']); await pubkeyPage.waitForContent('@container-pgp-pubkey', 'Fingerprint: 50B7 A032 B5E1 FBAB 24BA B205 B362 45FD AC2F BF3D'); })); @@ -236,11 +229,7 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test const signature = ['Limon.Monte@Gmail.Com', 'matching signature']; await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { params: url, content: ['1234'], signature }); await pageHasSecureReplyContainer(t, browser, gmailPage); - // validate pgp_pubkey.htm is rendered - const pgpPubkeyUrls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_pubkey.htm'], { sleep: 10, appearIn: 20 }); - expect(pgpPubkeyUrls.length).to.equal(1); - await pageHasSecureReplyContainer(t, browser, gmailPage); - const pubkeyPage = await browser.newPage(t, pgpPubkeyUrls[0]); + const pubkeyPage = await gmailPage.getFrame(['/chrome/elements/pgp_pubkey.htm']); await pubkeyPage.waitForContent('@container-pgp-pubkey', 'Fingerprint: 50B7 A032 B5E1 FBAB 24BA B205 B362 45FD AC2F BF3D'); })); @@ -249,10 +238,8 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test const pgpBlockUrls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_block.htm'], { sleep: 10, appearIn: 20 }); const url = pgpBlockUrls[0].split('/chrome/elements/pgp_block.htm')[1]; await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { params: url, content: ['Encrypted Subject: [ci.test] Thunderbird html signed + encrypted', '1234'] }); - const urls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_pubkey.htm'], { sleep: 10, appearIn: 20 }); - expect(urls.length).to.equal(1); await pageHasSecureReplyContainer(t, browser, gmailPage); - const pubkeyPage = await browser.newPage(t, urls[0]); + const pubkeyPage = await gmailPage.getFrame(['/chrome/elements/pgp_pubkey.htm']); await pubkeyPage.waitForContent('@container-pgp-pubkey', 'Fingerprint: 50B7 A032 B5E1 FBAB 24BA B205 B362 45FD AC2F BF3D'); })); @@ -268,25 +255,21 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test await gmailPage.waitForContent('#fc_offline_drafts', 'FlowCrypt offline drafts:'); await gmailPage.ensureElementsCount('#fc_offline_drafts a', 2); await gmailPage.waitAndClick('#fc_offline_drafts a'); - let urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 1 }); // compose draft 2 should be first in list as drafts are sorted by date descending - const draft = await pageHasSecureDraft(t, browser, urls[0], 'compose draft 2'); - await Util.sleep(4); // the draft isn't being saved if start typing without this delay + const draft = await pageHasSecureDraft(gmailPage, 'compose draft 2'); + await Util.sleep(5); // the draft isn't being saved if start typing without this delay await draft.type('@input-body', 'trigger saving a draft to the cloud', true); await ComposePageRecipe.waitWhenDraftIsSaved(draft); - await draft.close(); // after draft 2 is saved to the cloud, it should be removed from offline drafts await gmailPage.page.reload(); await gmailPage.waitForContent('#fc_offline_drafts', 'FlowCrypt offline drafts:'); await gmailPage.ensureElementsCount('#fc_offline_drafts a', 1); await gmailPage.waitAndClick('#fc_offline_drafts a'); - urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 1 }); - await pageHasSecureDraft(t, browser, urls[0], 'compose draft 1'); + await pageHasSecureDraft(gmailPage, 'compose draft 1'); })); ava.default('mail.google.com - secure reply btn, reply draft', testWithBrowser('ci.tests.gmail', async (t, browser) => { - const gmailPage = await openGmailPage(t, browser, '/'); - await gotoGmailPage(gmailPage, '/FMfcgzGkbDRNgcPktjdSxpJVhZlZqpTr'); // to go encrypted convo + const gmailPage = await openGmailPage(t, browser, '/FMfcgzGlkjftvKTsGnTltMvmZdDzdPFB'); // to go encrypted convo // Gmail has 100 emails per thread limit, so if there are 98 deleted messages + 1 initial message, // the draft number 100 won't be saved. Therefore, we need to delete forever trashed messages from this thread. if (await gmailPage.isElementPresent('//*[text()="delete forever"]')) { @@ -294,18 +277,14 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test } await gmailPage.waitAndClick('@secure-reply-button'); await createSecureDraft(t, browser, gmailPage, 'reply draft'); - await gmailPage.page.reload(); - const urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm']); - expect(urls.length).to.equal(1); - let replyBox = await pageHasSecureDraft(t, browser, urls[0], 'reply draft'); - await replyBox.close(); await createSecureDraft(t, browser, gmailPage, 'offline reply draft', { offline: true }); - await gmailPage.page.reload(); - replyBox = await pageHasSecureDraft(t, browser, urls[0], 'offline reply draft'); - await replyBox.waitAndClick('@action-send'); + await gmailPage.page.reload({ waitUntil: 'networkidle2' }); + const replyBox = await pageHasSecureDraft(gmailPage, 'offline reply draft'); + // await replyBox.waitAndClick('@action-send'); doesn't work for some reason, use keyboard instead + await gmailPage.page.keyboard.press('Tab'); + await gmailPage.page.keyboard.press('Enter'); await replyBox.waitTillGone('@action-send'); - await replyBox.close(); - await gmailPage.page.reload(); + await gmailPage.page.reload({ waitUntil: 'networkidle2' }); await gmailPage.waitAndClick('.h7:last-child .ajz', { delay: 1 }); // the small triangle which toggles the message details await gmailPage.waitForContent('.h7:last-child .ajA', 'Re: [ci.test] encrypted email for reply render'); // make sure that the subject of the sent draft is corrent await GmailPageRecipe.deleteLastReply(gmailPage); @@ -325,8 +304,7 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test // veryfy that there are two compose windows: new compose window and secure draft const urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 1 }); expect(urls.length).to.equal(2); - const composeDraft = await pageHasSecureDraft(t, browser, urls[1], 'compose draft'); - await composeDraft.close(); + await pageHasSecureDraft(gmailPage, 'compose draft'); // try to open 4 compose windows at the same time await gmailPage.waitAndClick('@action-secure-compose', { delay: 1 }); await gmailPage.waitAndClick('@action-secure-compose', { delay: 1 }); @@ -354,13 +332,12 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test ava.default('mail.google.com - plain reply draft', testWithBrowser('ci.tests.gmail', async (t, browser) => { const gmailPage = await openGmailPage(t, browser, '/FMfcgzGkbDRNgcPktjdSxpJVhZlZqpTr'); // encrypted convo await gmailPage.waitAndClick('[data-tooltip="Reply"]'); - await Util.sleep(5); + await gmailPage.waitTillFocusIsIn('div[aria-label="Message Body"]'); await gmailPage.type('div[aria-label="Message Body"]', 'plain reply', true); - await gotoGmailPage(gmailPage, '/'); // go to Inbox - await Util.sleep(1); - await gotoGmailPage(gmailPage, '/FMfcgzGkbDRNgcPktjdSxpJVhZlZqpTr'); // go back to encrypted convo with plain reply + await gmailPage.waitForContent('.oG.aOy', 'Draft saved'); + await gmailPage.page.reload({ waitUntil: 'networkidle2' }); await pageDoesNotHaveSecureReplyContainer(gmailPage); - await gmailPage.waitForContent('div[aria-label="Message Body"]', 'plain reply'); + await gmailPage.waitForContent('div[aria-label="Message Body"]', 'plain reply', 30); await gmailPage.click('[aria-label^="Discard draft"]'); })); diff --git a/test/source/tests/page-recipe/setup-page-recipe.ts b/test/source/tests/page-recipe/setup-page-recipe.ts index 3c663186c9b..c381001195c 100644 --- a/test/source/tests/page-recipe/setup-page-recipe.ts +++ b/test/source/tests/page-recipe/setup-page-recipe.ts @@ -108,9 +108,9 @@ export class SetupPageRecipe extends PageRecipe { ) { if (!noPrvCreateOrgRule) { if (usedPgpBefore) { - await settingsPage.waitAndClick('@action-step0foundkey-choose-manual-enter', { retryErrs: true }); + await settingsPage.waitAndClick('@action-step0foundkey-choose-manual-enter', { timeout: 30, retryErrs: true }); } else { - await settingsPage.waitAndClick('@action-step1easyormanual-choose-manual-enter', { retryErrs: true }); + await settingsPage.waitAndClick('@action-step1easyormanual-choose-manual-enter', { timeout: 30, retryErrs: true }); } } key = key || Config.key(keyTitle); @@ -186,7 +186,7 @@ export class SetupPageRecipe extends PageRecipe { } await settingsPage.waitAndClick('@input-step2bmanualenter-save', { delay: 1 }); if (fixKey) { - await settingsPage.waitAll('@input-compatibility-fix-expire-years'); + await settingsPage.waitAll('@input-compatibility-fix-expire-years', { timeout: 30 }); await settingsPage.selectOption('@input-compatibility-fix-expire-years', '1'); await settingsPage.waitAndClick('@action-fix-and-import-key'); } @@ -284,9 +284,9 @@ export class SetupPageRecipe extends PageRecipe { private static createBegin = async (settingsPage: ControllablePage, keyTitle: string, { key, usedPgpBefore = false }: { key?: { passphrase: string }, usedPgpBefore?: boolean } = {}) => { const k = key || Config.key(keyTitle); if (usedPgpBefore) { - await settingsPage.waitAndClick('@action-step0foundkey-choose-manual-create'); + await settingsPage.waitAndClick('@action-step0foundkey-choose-manual-create', { timeout: 30 }); } else { - await settingsPage.waitAndClick('@action-step1easyormanual-choose-manual-create', { retryErrs: true }); + await settingsPage.waitAndClick('@action-step1easyormanual-choose-manual-create', { timeout: 30, retryErrs: true }); } await settingsPage.waitAndType('@input-step2bmanualcreate-passphrase-1', k.passphrase); await settingsPage.waitAndType('@input-step2bmanualcreate-passphrase-2', k.passphrase);