From 7b69d61540879ea37ee9c908e515485da8f9e00f Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 18 Mar 2025 10:56:36 +0000 Subject: [PATCH] [saas-e2e] initial revision of SaaS E2E tests running in the dev-env (#24311) * [saas-e2e] initial revision of SaaS E2E tests running in the dev-env * [v1] make rubocop happy * [v1] make rubocop happy * [saas-e2e] more retries for webpack startup check Co-authored-by: Alf Eaton * [web] restrict e2e_test_setup.mjs to running in the dev-env Co-authored-by: Mathias Jakobsen * [saas-e2e] import latest split-tests from production --------- Co-authored-by: Alf Eaton Co-authored-by: Mathias Jakobsen GitOrigin-RevId: 034343ee56b8d059090d8a5de74eaac24429b527 --- package-lock.json | 317 +++++++++++++++++- package.json | 3 +- .../js/features/form-helpers/hydrate-form.js | 6 +- .../js/shared/components/recaptcha-2.tsx | 3 + services/web/scripts/e2e_test_setup.mjs | 169 ++++++++++ 5 files changed, 492 insertions(+), 6 deletions(-) create mode 100644 services/web/scripts/e2e_test_setup.mjs diff --git a/package-lock.json b/package-lock.json index 6335b85427..a26154b269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,8 @@ "services/third-party-datastore", "services/third-party-references", "services/tpdsworker", - "services/web" + "services/web", + "tools/saas-e2e" ], "dependencies": { "patch-package": "^8.0.0" @@ -6441,6 +6442,29 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isomorphic-git/idb-keyval": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz", + "integrity": "sha512-r8/AdpiS0/WJCNR/t/gsgL+M8NMVj/ek7s60uz3LmpCaTF2mEVlZJlB01ZzalgYzRLXwSPC92o+pdzjM7PN/pA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@isomorphic-git/lightning-fs": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@isomorphic-git/lightning-fs/-/lightning-fs-4.6.0.tgz", + "integrity": "sha512-tfon8f1h6LawjFI/d8lZPWRPTxmdvyTMbkT/j5yo6dB0hALhKw5D9JsdCcUu/D1pAcMMiU7GZFDsDGqylerr7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isomorphic-git/idb-keyval": "3.3.2", + "isomorphic-textencoder": "1.0.1", + "just-debounce-it": "1.1.0", + "just-once": "1.1.0" + }, + "bin": { + "superblocktxt": "src/superblocktxt.js" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -8810,6 +8834,10 @@ "resolved": "services/references", "link": true }, + "node_modules/@overleaf/saas-e2e": { + "resolved": "tools/saas-e2e", + "link": true + }, "node_modules/@overleaf/settings": { "resolved": "libraries/settings", "link": true @@ -11901,6 +11929,16 @@ "@types/node": "*" } }, + "node_modules/@types/adm-zip": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", + "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/aria-query": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", @@ -12483,6 +12521,13 @@ "integrity": "sha512-ZkC5IUqqIFPXx3ASTTybTzmQdwHwe2C0u3eL75ldQ6T9E9IWFJodn6hIfbZGab73DfyiHN4Xw15gNxUq2FbvBA==", "dev": true }, + "node_modules/@types/pdf-parse": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", + "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/pg": { "version": "8.6.1", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", @@ -14573,6 +14618,16 @@ "node": ">=8.9" } }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -15225,6 +15280,13 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -17215,6 +17277,13 @@ "node": ">= 10.0" } }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/clean-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", @@ -19300,6 +19369,22 @@ "node": ">=0.10" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -19605,6 +19690,13 @@ "integrity": "sha512-UfwfKGxT/Wm2KaxFSZsp7/+YOnAgIzxQXs86zu1IFMLU/+3ouxnEvXqPDn3yxSxlsO4r1B+I2GGQHsIthp6/7Q==", "license": "Apache-2.0" }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "dev": true, + "license": "MIT" + }, "node_modules/dijkstrajs": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", @@ -26461,6 +26553,79 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-git": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.29.0.tgz", + "integrity": "sha512-zWGqk8901cicvVEhVpN76AwKrS/TzHak2NQCtNXIAavpMIy/yqh+d/JtC9A8AUKZAauUdOyEWKI29tuCLAL+Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "path-browserify": "^1.0.1", + "pify": "^4.0.1", + "readable-stream": "^3.4.0", + "sha.js": "^2.4.9", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/isomorphic-git/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/isomorphic-git/node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/isomorphic-textencoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-textencoder/-/isomorphic-textencoder-1.0.1.tgz", + "integrity": "sha512-676hESgHullDdHDsj469hr+7t3i/neBKU9J7q1T4RHaWwLAsaQnywC0D1dIUId0YZ+JtVrShzuBk1soo0+GVcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-text-encoding": "^1.0.0" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -27367,12 +27532,26 @@ "node": ">=4.0" } }, + "node_modules/just-debounce-it": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce-it/-/just-debounce-it-1.1.0.tgz", + "integrity": "sha512-87Nnc0qZKgBZuhFZjYVjSraic0x7zwjhaTMrCKlj0QYKH6lh0KbFzVnfu6LHan03NO7J8ygjeBeD0epejn5Zcg==", + "dev": true, + "license": "MIT" + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "node_modules/just-once": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-once/-/just-once-1.1.0.tgz", + "integrity": "sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==", + "dev": true, + "license": "MIT" + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -28517,6 +28696,32 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/mailtrap": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/mailtrap/-/mailtrap-3.4.0.tgz", + "integrity": "sha512-gegg90/gMY8hvfxB+WMtE8RRZyhQr90jUw00QOLApIAomItumqFBCpZv5IfG51EUKThu9+p7X4QdNA4buryenw==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": ">=0.27" + }, + "engines": { + "node": ">=16.20.1", + "yarn": ">=1.22.17" + }, + "peerDependencies": { + "@types/nodemailer": "^6.4.9", + "nodemailer": "^6.9.4" + }, + "peerDependenciesMeta": { + "@types/nodemailer": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -29316,6 +29521,19 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -29431,6 +29649,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, "node_modules/minipass": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", @@ -30242,6 +30470,13 @@ "node": ">=10.5.0" } }, + "node_modules/node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -31191,6 +31426,13 @@ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -31660,6 +31902,30 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, + "node_modules/pdf-parse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz", + "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "node-ensure": "^0.0.0" + }, + "engines": { + "node": ">=6.8.1" + } + }, + "node_modules/pdf-parse/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/pdfjs-dist": { "version": "4.10.38", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz", @@ -36376,6 +36642,20 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -36478,9 +36758,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true, - "peer": true + ] }, "node_modules/simple-get": { "version": "3.1.1", @@ -45445,6 +45723,37 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "tools/saas-e2e": { + "name": "@overleaf/saas-e2e", + "devDependencies": { + "@isomorphic-git/lightning-fs": "^4.6.0", + "@testing-library/cypress": "^10.0.1", + "@types/adm-zip": "^0.5.5", + "@types/pdf-parse": "^1.1.4", + "@types/uuid": "^9.0.8", + "adm-zip": "^0.5.12", + "cypress": "13.13.2", + "isomorphic-git": "^1.25.10", + "mailtrap": "^3.4.0", + "pdf-parse": "^1.1.1", + "typescript": "^5.0.4", + "uuid": "^9.0.1" + } + }, + "tools/saas-e2e/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } } } } diff --git a/package.json b/package.json index a18f518f8e..ae25e19029 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "services/third-party-datastore", "services/third-party-references", "services/tpdsworker", - "services/web" + "services/web", + "tools/saas-e2e" ] } diff --git a/services/web/frontend/js/features/form-helpers/hydrate-form.js b/services/web/frontend/js/features/form-helpers/hydrate-form.js index a3eeb8c159..9c00dd43ae 100644 --- a/services/web/frontend/js/features/form-helpers/hydrate-form.js +++ b/services/web/frontend/js/features/form-helpers/hydrate-form.js @@ -90,7 +90,11 @@ function formSubmitHelper(formEl) { async function validateCaptcha(formEl) { let captchaResponse - if (formEl.hasAttribute('captcha')) { + if ( + formEl.hasAttribute('captcha') && + // Disable captcha for E2E tests in dev-env. + !(process.env.NODE_ENV === 'development' && window.Cypress) + ) { if ( formEl.getAttribute('action') === '/login' && (await canSkipCaptcha(new FormData(formEl).get('email'))) diff --git a/services/web/frontend/js/shared/components/recaptcha-2.tsx b/services/web/frontend/js/shared/components/recaptcha-2.tsx index a587b88cad..edca1532fd 100644 --- a/services/web/frontend/js/shared/components/recaptcha-2.tsx +++ b/services/web/frontend/js/shared/components/recaptcha-2.tsx @@ -21,6 +21,9 @@ export function ReCaptcha2({ if (site && recaptchaDisabled[site]) { return null } + if (process.env.NODE_ENV === 'development' && window.Cypress) { + return null // Disable captcha for E2E tests in dev-env. + } return ( } + */ +async function createUser(email) { + const user = await UserRegistrationHandler.promises.registerNewUser({ + email, + password: process.env.CYPRESS_DEFAULT_PASSWORD, + }) + const features = email.startsWith('free@') + ? Settings.defaultFeatures + : Settings.features.professional + await db.users.updateOne( + { _id: user._id }, + { + $set: { + // Set admin flag. + isAdmin: email.startsWith('admin@'), + // Disable spell-checking for performance and flakiness reasons. + 'ace.spellCheckLanguage': '', + // Override features. + features, + featuresOverrides: [{ features }], + }, + } + ) +} + +/** + * @param {string} email + * @return {Promise} + */ +async function deleteUser(email) { + const user = await db.users.findOne({ email }) + if (!user) return + // Soft delete the user. + await UserDeleter.promises.deleteUser(user._id, { + force: true, + ipAddress: '0.0.0.0', + }) + // Hard-delete the users projects. + const projects = await db.deletedProjects + .find( + { deletedProjectOwnerId: user._id }, + { projection: { deletedProjectId: 1 } } + ) + .toArray() + await promiseMapWithLimit( + 10, + projects.map(p => p.deletedProjectId), + ProjectDeleter.promises.expireDeletedProject + ) + // Hard-delete the user. + await UserDeleter.promises.expireDeletedUser(user._id) +} + +/** + * @param {string} email + * @return {Promise} + */ +async function provisionUser(email) { + await deleteUser(email) + await createUser(email) +} + +async function provisionUsers() { + const emails = Settings.recaptcha.trustedUsers + console.log(`> Provisioning ${emails.length} E2E users.`) + await promiseMapWithLimit(3, emails, provisionUser) +} + +async function purgeNewUsers() { + const users = await db.users + .find( + { email: Settings.recaptcha.trustedUsersRegex }, + { projection: { email: 1 } } + ) + .toArray() + console.log(`> Deleting ${users.length} newly created E2E users.`) + await promiseMapWithLimit( + 3, + users.map(user => user.email), + deleteUser + ) +} + +const SPLIT_TEST_OVERRIDES = [ + // disable writefull, oauth registration does not work in dev-env and their banners hide our buttons. + { + name: 'writefull-auto-account-creation', + versions: [ + { + versionNumber: 1, + phase: 'release', + active: true, + analyticsEnabled: false, + variants: [{ name: 'enabled', rolloutPercent: 0, rolloutStripes: [] }], + createdAt: new Date(), + }, + ], + }, +] + +async function provisionSplitTests() { + const backup = Path.join( + MONOREPO, + 'backup', + 'split-tests', + new Date().toISOString() + '.json' + ) + console.log( + `> Backing up previous split-tests into ${backup}. You can import them again on https://www.dev-overleaf.com/admin/split-test via the [Import] button.` + ) + const splitTests = await SplitTestManager.getRuntimeTests() + await fs.promises.mkdir(Path.dirname(backup), { recursive: true }) + await fs.promises.writeFile( + backup, + JSON.stringify(splitTests.sort((a, b) => (a.name > b.name ? 1 : -1))) + ) + + // Imported from production via https://www.overleaf.com/admin/split-test -> "Copy all split tests" -> "Copy for E2E test setup" + const SPLIT_TESTS = JSON.parse( + await fs.promises.readFile( + Path.join(MONOREPO, 'tools/saas-e2e/split-tests.json') + ) + ) + console.log(`> Importing ${SPLIT_TESTS.length} split-tests from production.`) + await SplitTestManager.replaceSplitTests(SPLIT_TESTS) + console.log( + `> Importing ${SPLIT_TEST_OVERRIDES.length} split-tests for test compatibility.` + ) + await SplitTestManager.mergeSplitTests(SPLIT_TEST_OVERRIDES, true) +} + +async function main() { + if (process.env.NODE_ENV !== 'development') { + throw new Error('only available in dev-env') + } + + await purgeNewUsers() + await provisionUsers() + await provisionSplitTests() +} + +await main() +await GracefulShutdown.gracefulShutdown( + { + close(cb) { + cb() + }, + }, + 'SIGTERM' +)