diff --git a/e2e-tests/.ci/server.generate.sh b/e2e-tests/.ci/server.generate.sh index a6d365ac10..b64ee64e44 100755 --- a/e2e-tests/.ci/server.generate.sh +++ b/e2e-tests/.ci/server.generate.sh @@ -217,14 +217,13 @@ $(if mme2e_is_token_in_list "webhook-interactions" "$ENABLED_DOCKER_SERVICES"; t $(if mme2e_is_token_in_list "playwright" "$ENABLED_DOCKER_SERVICES"; then echo ' playwright: - image: mcr.microsoft.com/playwright:v1.43.0-jammy + image: mcr.microsoft.com/playwright:v1.46.1 entrypoint: ["/bin/bash", "-c"] command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"] env_file: - "./.env.playwright" environment: CI: "true" - NODE_OPTIONS: --no-experimental-fetch PW_BASE_URL: http://localhost:8065 PW_ADMIN_USERNAME: sysadmin PW_ADMIN_PASSWORD: Sys@dmin-sample1 diff --git a/e2e-tests/playwright/README.md b/e2e-tests/playwright/README.md index a894201b1d..bcafd87deb 100644 --- a/e2e-tests/playwright/README.md +++ b/e2e-tests/playwright/README.md @@ -15,8 +15,6 @@ cd server && make run-server #### 2. Install dependencies and run the test. -Note: If you're using Node.js version 18 and above, you may need to set `NODE_OPTIONS='--no-experimental-fetch'`. - ``` # Install npm packages npm i @@ -51,7 +49,6 @@ docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwr #### 2. Inside the docker container ``` -export NODE_OPTIONS='--no-experimental-fetch' export PW_BASE_URL=http://host.docker.internal:8065 export PW_HEADLESS=true cd mattermost/e2e-tests/playwright diff --git a/e2e-tests/playwright/global_setup.ts b/e2e-tests/playwright/global_setup.ts index 9be3a84001..3fa9e9f293 100644 --- a/e2e-tests/playwright/global_setup.ts +++ b/e2e-tests/playwright/global_setup.ts @@ -111,7 +111,7 @@ async function printClientInfo(client: Client) { - LogSettings.EnableDiagnostics = ${LogSettings?.EnableDiagnostics}`); } -async function ensurePluginsLoaded(client: Client) { +export async function ensurePluginsLoaded(client: Client) { const pluginStatus = await client.getPluginStatuses(); const plugins = await client.getPlugins(); diff --git a/e2e-tests/playwright/package-lock.json b/e2e-tests/playwright/package-lock.json index 238cec048e..4e7b8b0d8e 100644 --- a/e2e-tests/playwright/package-lock.json +++ b/e2e-tests/playwright/package-lock.json @@ -16,8 +16,8 @@ "chalk": "4.1.2", "deepmerge": "4.3.1", "dotenv": "16.4.5", - "form-data": "4.0.0", - "isomorphic-unfetch": "4.0.2", + "form-data-encoder": "4.0.2", + "formdata-node": "6.0.3", "uuid": "10.0.0" }, "devDependencies": { @@ -837,11 +837,6 @@ "npm": ">= 1.0.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, "node_modules/axe-core": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", @@ -968,17 +963,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1081,14 +1065,6 @@ "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1421,28 +1397,6 @@ "pend": "~1.2.0" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1502,28 +1456,22 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, + "node_modules/form-data-encoder": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", + "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 18" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, + "node_modules/formdata-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", + "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", + "license": "MIT", "engines": { - "node": ">=12.20.0" + "node": ">= 18" } }, "node_modules/fs.realpath": { @@ -1765,40 +1713,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "node_modules/isomorphic-unfetch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-4.0.2.tgz", - "integrity": "sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==", - "dependencies": { - "node-fetch": "^3.2.0", - "unfetch": "^5.0.0" - } - }, - "node_modules/isomorphic-unfetch/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/isomorphic-unfetch/node_modules/node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1962,24 +1876,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2454,11 +2350,6 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "optional": true }, - "node_modules/unfetch": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-5.0.0.tgz", - "integrity": "sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==" - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2480,14 +2371,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3137,11 +3020,6 @@ "resolved": "https://registry.npmjs.org/async-wait-until/-/async-wait-until-2.0.12.tgz", "integrity": "sha512-SXy/vDs6UPJMG6YeEYOQ4ilA/JnGxk187KPGqFx9O+qVxsjkSl+jH+3P50qSNyMpEmDgr8qOFGOKCJckWb1i7A==" }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, "axe-core": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", @@ -3237,14 +3115,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3307,11 +3177,6 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3570,15 +3435,6 @@ "pend": "~1.2.0" } }, - "fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "requires": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3623,23 +3479,15 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } + "form-data-encoder": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", + "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==" }, - "formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "requires": { - "fetch-blob": "^3.1.2" - } + "formdata-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", + "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==" }, "fs.realpath": { "version": "1.0.0", @@ -3812,32 +3660,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "isomorphic-unfetch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-4.0.2.tgz", - "integrity": "sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==", - "requires": { - "node-fetch": "^3.2.0", - "unfetch": "^5.0.0" - }, - "dependencies": { - "data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" - }, - "node-fetch": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", - "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", - "requires": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - } - } - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3968,11 +3790,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4267,11 +4084,6 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "optional": true }, - "unfetch": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-5.0.0.tgz", - "integrity": "sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==" - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4286,11 +4098,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==" }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/e2e-tests/playwright/package.json b/e2e-tests/playwright/package.json index 7ea7cc238a..af586d28d2 100644 --- a/e2e-tests/playwright/package.json +++ b/e2e-tests/playwright/package.json @@ -1,16 +1,16 @@ { "scripts": { - "test": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true playwright test", - "test:update-snapshots": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true playwright test --update-snapshots", - "percy": "cross-env NODE_OPTIONS='--no-experimental-fetch' PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=ipad", + "test": "cross-env PW_SNAPSHOT_ENABLE=true playwright test", + "test:update-snapshots": "cross-env PW_SNAPSHOT_ENABLE=true playwright test --update-snapshots", + "percy": "cross-env PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=ipad", "tsc": "tsc -b", "lint": "eslint . --ext .js,.ts", "prettier": "prettier . --check", "prettier:fix": "prettier --write .", "check": "npm run tsc && npm run lint && npm run prettier", "codegen": "cross-env playwright codegen $PW_BASE_URL", - "playwright-ui": "cross-env NODE_OPTIONS='--no-experimental-fetch' playwright test --ui", - "test-slomo": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test", + "playwright-ui": "cross-env playwright test --ui", + "test-slomo": "cross-env PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test", "show-report": "npx playwright show-report", "postinstall": "script/post_install.sh" }, @@ -24,8 +24,8 @@ "chalk": "4.1.2", "deepmerge": "4.3.1", "dotenv": "16.4.5", - "form-data": "4.0.0", - "isomorphic-unfetch": "4.0.2", + "form-data-encoder": "4.0.2", + "formdata-node": "6.0.3", "uuid": "10.0.0" }, "devDependencies": { diff --git a/e2e-tests/playwright/support/flag.ts b/e2e-tests/playwright/support/flag.ts index 44fd287e1f..1a214d3a4f 100644 --- a/e2e-tests/playwright/support/flag.ts +++ b/e2e-tests/playwright/support/flag.ts @@ -35,6 +35,41 @@ export async function shouldRunInLinux() { await expect(platform, 'Run in Linux or Playwright docker image only').toBe('linux'); } +export async function ensureLicense() { + const {adminClient} = await getAdminClient(); + let license = await adminClient.getClientLicenseOld(); + + if (license?.IsLicensed !== 'true') { + const config = await adminClient.getClientConfigOld(); + await expect( + config.ServiceEnvironment === 'dev', + 'The trial license request fails in the local development environment. Please manually upload the test license.', + ).toBeFalsy(); + + await requestTrialLicense(); + + license = await adminClient.getClientLicenseOld(); + } + + await expect(license?.IsLicensed === 'true', 'Ensure server has license').toBeTruthy(); +} + +export async function requestTrialLicense() { + const {adminClient} = await getAdminClient(); + try { + // @ts-expect-error This may fail requesting for trial license + await adminClient.requestTrialLicense({ + receive_emails_accepted: true, + terms_accepted: true, + users: 100, + }); + } catch (error) { + // eslint-disable-next-line no-console + expect(error, 'Failed to request trial license').toBeFalsy(); + throw error; + } +} + export async function skipIfNoLicense() { const {adminClient} = await getAdminClient(); const license = await adminClient.getClientLicenseOld(); diff --git a/e2e-tests/playwright/support/server/client.ts b/e2e-tests/playwright/support/server/client.ts index 947dd6f391..7d412849d8 100644 --- a/e2e-tests/playwright/support/server/client.ts +++ b/e2e-tests/playwright/support/server/client.ts @@ -6,9 +6,10 @@ import fs from 'node:fs'; import path from 'node:path'; +import stream from 'stream'; -import FormData from 'form-data'; -import 'isomorphic-unfetch'; +import {FormData} from 'formdata-node'; +import {FormDataEncoder} from 'form-data-encoder'; import testConfig from '@e2e-test.config'; import Client4 from '@mattermost/client/client4'; @@ -20,115 +21,89 @@ import {UserProfile} from '@mattermost/types/users'; export default class Client extends Client4 { getFormDataOptions = (formData: FormData): Options => { + const encoder = new FormDataEncoder(formData); + return { method: 'post', - body: formData, - headers: { - 'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`, - }, + body: stream.Readable.from(encoder.encode()), + headers: encoder.headers, + duplex: 'half', }; }; - uploadProfileImageX = (userId: string, filePath: string) => { + readFileAsFormData = (filePath: string, key: string, additionalFields: Record = {}) => { const fileData = fs.readFileSync(filePath); const formData = new FormData(); - formData.append('image', fileData, path.basename(filePath)); - const options = this.getFormDataOptions(formData); + formData.append(key, new Blob([fileData]), path.basename(filePath)); + // Append additional fields if provided + for (const [field, value] of Object.entries(additionalFields)) { + formData.append(field, value); + } + + return formData; + }; + + uploadProfileImageX = (userId: string, filePath: string) => { + const formData = this.readFileAsFormData(filePath, 'image'); + const options = this.getFormDataOptions(formData); return this.doFetch(`${this.getUserRoute(userId)}/image`, options); }; setTeamIconX = (teamId: string, filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('image', fileData, path.basename(filePath)); + const formData = this.readFileAsFormData(filePath, 'image'); const options = this.getFormDataOptions(formData); - return this.doFetch(`${this.getTeamRoute(teamId)}/image`, options); }; createCustomEmojiX = (emoji: CustomEmoji, filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('image', fileData, path.basename(filePath)); - formData.append('emoji', JSON.stringify(emoji)); + const formData = this.readFileAsFormData(filePath, 'image', {emoji: JSON.stringify(emoji)}); const options = this.getFormDataOptions(formData); - return this.doFetch(`${this.getEmojisRoute()}`, options); }; uploadBrandImageX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('image', fileData, path.basename(filePath)); + const formData = this.readFileAsFormData(filePath, 'image'); const options = this.getFormDataOptions(formData); - return this.doFetch(`${this.getBrandRoute()}/image`, options); }; - uploadPublicSamlCertificateX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('certificate', fileData, path.basename(filePath)); + uploadCertificateX = (filePath: string, route: string) => { + const formData = this.readFileAsFormData(filePath, 'certificate'); const options = this.getFormDataOptions(formData); + return this.doFetch(route, options); + }; - return this.doFetch(`${this.getBaseRoute()}/saml/certificate/public`, options); + uploadPublicSamlCertificateX = (filePath: string) => { + return this.uploadCertificateX(filePath, `${this.getBaseRoute()}/saml/certificate/public`); }; uploadPrivateSamlCertificateX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('certificate', fileData, path.basename(filePath)); - const options = this.getFormDataOptions(formData); - - return this.doFetch(`${this.getBaseRoute()}/saml/certificate/private`, options); + return this.uploadCertificateX(filePath, `${this.getBaseRoute()}/saml/certificate/private`); }; uploadPublicLdapCertificateX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('certificate', fileData, path.basename(filePath)); - const options = this.getFormDataOptions(formData); - - return this.doFetch(`${this.getBaseRoute()}/ldap/certificate/public`, options); + return this.uploadCertificateX(filePath, `${this.getBaseRoute()}/ldap/certificate/public`); }; uploadPrivateLdapCertificateX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('certificate', fileData, path.basename(filePath)); - const options = this.getFormDataOptions(formData); - - return this.doFetch(`${this.getBaseRoute()}/ldap/certificate/private`, options); + return this.uploadCertificateX(filePath, `${this.getBaseRoute()}/ldap/certificate/private`); }; uploadIdpSamlCertificateX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('certificate', fileData, path.basename(filePath)); - const options = this.getFormDataOptions(formData); - - return this.doFetch(`${this.getBaseRoute()}/saml/certificate/idp`, options); + return this.uploadCertificateX(filePath, `${this.getBaseRoute()}/saml/certificate/idp`); }; uploadLicenseX = (filePath: string) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - formData.append('license', fileData, path.basename(filePath)); + const formData = this.readFileAsFormData(filePath, 'license'); const options = this.getFormDataOptions(formData); - return this.doFetch(`${this.getBaseRoute()}/license`, options); }; uploadPluginX = async (filePath: string, force = false) => { - const fileData = fs.readFileSync(filePath); - const formData = new FormData(); - if (force) { - formData.append('force', 'true'); - } - formData.append('plugin', fileData, path.basename(filePath)); + const additionalFields = force ? {force: 'true'} : {}; + const formData = this.readFileAsFormData(filePath, 'plugin', additionalFields); const options = this.getFormDataOptions(formData); - return this.doFetch(this.getPluginsRoute(), options); }; } diff --git a/e2e-tests/playwright/support/server/init.ts b/e2e-tests/playwright/support/server/init.ts index 6e3ae38929..982d7cbbb1 100644 --- a/e2e-tests/playwright/support/server/init.ts +++ b/e2e-tests/playwright/support/server/init.ts @@ -3,9 +3,7 @@ import path from 'node:path'; import {expect} from '@playwright/test'; -import chalk from 'chalk'; -import {ClientError} from '@mattermost/client/client4'; import {PreferenceType} from '@mattermost/types/preferences'; import testConfig from '@e2e-test.config'; @@ -73,22 +71,8 @@ export async function initSetup({ townSquareUrl: getUrl(team.name, 'town-square'), }; } catch (error) { - // log an error for debugging - // eslint-disable-next-line no-console - const err = error as ClientError; - if (err.message === 'Could not parse multipart form.') { - // eslint-disable-next-line no-console - console.log(chalk.yellow(`node version: ${process.version}\nNODE_OPTIONS: ${process.env.NODE_OPTIONS}`)); - - // eslint-disable-next-line no-console - console.log( - chalk.green( - `This failed due to the experimental fetch support in Node.js starting v18.0.0.\nYou may set environment variable: "export NODE_OPTIONS='--no-experimental-fetch'", then try again.'`, - ), - ); - } - expect(err, 'Should not throw an error').toBeFalsy(); - throw err; + expect(error, 'Should not throw an error').toBeFalsy(); + throw error; } } diff --git a/e2e-tests/playwright/support/test_fixture.ts b/e2e-tests/playwright/support/test_fixture.ts index 2db66a8e97..2ad0ff6317 100644 --- a/e2e-tests/playwright/support/test_fixture.ts +++ b/e2e-tests/playwright/support/test_fixture.ts @@ -3,7 +3,7 @@ import {AxeResults} from 'axe-core'; import AxeBuilder from '@axe-core/playwright'; import {TestBrowser} from './browser_context'; -import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldRunInLinux, skipIfNoLicense} from './flag'; +import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldRunInLinux, ensureLicense, skipIfNoLicense} from './flag'; import {initSetup, getAdminClient} from './server'; import {hideDynamicChannelsContent, waitForAnimationEnd, waitUntil} from './test_action'; import {pages} from './ui/pages'; @@ -47,6 +47,7 @@ class PlaywrightExtended { readonly shouldHaveCallsEnabled; readonly shouldHaveFeatureFlag; readonly shouldRunInLinux; + readonly ensureLicense; readonly skipIfNoLicense; // ./server @@ -72,6 +73,7 @@ class PlaywrightExtended { this.shouldHaveCallsEnabled = shouldHaveCallsEnabled; this.shouldHaveFeatureFlag = shouldHaveFeatureFlag; this.shouldRunInLinux = shouldRunInLinux; + this.ensureLicense = ensureLicense; this.skipIfNoLicense = skipIfNoLicense; // ./server diff --git a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-chrome-linux.png b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-chrome-linux.png index c3829d3eb5..f2a170dd9f 100644 Binary files a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-chrome-linux.png and b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-chrome-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-firefox-linux.png b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-firefox-linux.png index 13eef56023..f735e6118a 100644 Binary files a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-firefox-linux.png and b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-firefox-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-ipad-linux.png b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-ipad-linux.png index 4dd3d8f2c2..b69af168d8 100644 Binary files a/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-ipad-linux.png and b/e2e-tests/playwright/tests/visual/common/landing_page.spec.ts-snapshots/landing-login-ipad-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-chrome-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-chrome-linux.png index c99ddac90a..f06a9908b0 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-chrome-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-chrome-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-chrome-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-chrome-linux.png index 1559290d13..3ac10df543 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-chrome-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-chrome-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-firefox-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-firefox-linux.png index d8902bc0c6..e7fd27354f 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-firefox-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-firefox-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-ipad-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-ipad-linux.png index 2d7fdc1d90..650eaf69af 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-ipad-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-error-ipad-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-firefox-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-firefox-linux.png index ee7503143c..66110bf15e 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-firefox-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-firefox-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-ipad-linux.png b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-ipad-linux.png index ff12316cc5..10abc1705e 100644 Binary files a/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-ipad-linux.png and b/e2e-tests/playwright/tests/visual/common/login.spec.ts-snapshots/login-ipad-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-firefox-linux.png b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-firefox-linux.png index 85b2b68c5b..4c12de3a1c 100644 Binary files a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-firefox-linux.png and b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-firefox-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-ipad-linux.png b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-ipad-linux.png index a751719131..a0de7df0b2 100644 Binary files a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-ipad-linux.png and b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-error-ipad-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-firefox-linux.png b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-firefox-linux.png index 6b418c51f3..2ae3568cb8 100644 Binary files a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-firefox-linux.png and b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-firefox-linux.png differ diff --git a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-ipad-linux.png b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-ipad-linux.png index 2e0c00388a..897a23fef0 100644 Binary files a/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-ipad-linux.png and b/e2e-tests/playwright/tests/visual/common/signup_email.spec.ts-snapshots/signup-email-ipad-linux.png differ diff --git a/webapp/platform/types/src/client4.ts b/webapp/platform/types/src/client4.ts index eb5840ec62..afe6734d17 100644 --- a/webapp/platform/types/src/client4.ts +++ b/webapp/platform/types/src/client4.ts @@ -22,6 +22,7 @@ export type Options = { body?: any; signal?: RequestInit['signal']; ignoreStatus?: boolean; /** If true, status codes > 300 are ignored and don't cause an error */ + duplex?: 'half'; /** Optional, but required for node clients. Must be 'half' for half-duplex fetch; 'full' is reserved for future use. See https://fetch.spec.whatwg.org/#dom-requestinit-duplex */ }; export type StatusOK = {