From 4cf837c4c88f0edc35a98af107ef31aa3be08a54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:54:31 +0000 Subject: [PATCH 01/23] chore(deps-dev): bump @storybook/addon-actions from 7.1.1 to 8.0.0 (#1841) --- package-lock.json | 100 +++++++++++++++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b9008f40..0b52cd2e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,7 +119,7 @@ "@cypress/webpack-preprocessor": "^5.17.0", "@jest/globals": "^27.0.3", "@mdx-js/react": "^2.3.0", - "@storybook/addon-actions": "^7.0.24", + "@storybook/addon-actions": "^8.0.0", "@storybook/addon-docs": "^7.0.24", "@storybook/addon-essentials": "^7.0.24", "@storybook/addon-links": "^7.0.24", @@ -8584,42 +8584,34 @@ } }, "node_modules/@storybook/addon-actions": { - "version": "7.1.1", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.0.0.tgz", + "integrity": "sha512-QXfnEWZt5k35cPYsLvxq505XrCgXujc4UEkky1lBtSMI9SLzlXZg3fC/lW0c0hiu2c0+zI+y4fj5vTE9AZJdjw==", "dev": true, - "license": "MIT", "dependencies": { - "@storybook/client-logger": "7.1.1", - "@storybook/components": "7.1.1", - "@storybook/core-events": "7.1.1", + "@storybook/core-events": "8.0.0", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.1.1", - "@storybook/preview-api": "7.1.1", - "@storybook/theming": "7.1.1", - "@storybook/types": "7.1.1", + "@types/uuid": "^9.0.1", "dequal": "^2.0.2", - "lodash": "^4.17.21", "polished": "^4.2.2", - "prop-types": "^15.7.2", - "react-inspector": "^6.0.0", - "telejson": "^7.0.3", - "ts-dedent": "^2.0.0", "uuid": "^9.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-actions/node_modules/@storybook/core-events": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.0.0.tgz", + "integrity": "sha512-kkabj4V99gOTBW+y3HM/LTCDekglqb+lslZMamM+Ytxv1lCqCEOIR/OGfnYOyEaK4BLcx61Zp+fO30FZxtoT1w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, "node_modules/@storybook/addon-actions/node_modules/uuid": { @@ -8773,6 +8765,59 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@storybook/addon-essentials/node_modules/@storybook/addon-actions": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.1.1.tgz", + "integrity": "sha512-IDxBmNnVgLFfQ407MxOUJmqjz0hgiZB9syi4sfp7BKp5MIPUDT1m+z603kGrvx0bk0W0DPqkp/H8ySEGEx0x6g==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.1.1", + "@storybook/components": "7.1.1", + "@storybook/core-events": "7.1.1", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.1.1", + "@storybook/preview-api": "7.1.1", + "@storybook/theming": "7.1.1", + "@storybook/types": "7.1.1", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "polished": "^4.2.2", + "prop-types": "^15.7.2", + "react-inspector": "^6.0.0", + "telejson": "^7.0.3", + "ts-dedent": "^2.0.0", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-essentials/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" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@storybook/addon-highlight": { "version": "7.1.1", "dev": true, @@ -33099,8 +33144,9 @@ }, "node_modules/react-inspector": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", + "integrity": "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==", "dev": true, - "license": "MIT", "peerDependencies": { "react": "^16.8.4 || ^17.0.0 || ^18.0.0" } diff --git a/package.json b/package.json index 51815de94..4f5032d1a 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "@cypress/webpack-preprocessor": "^5.17.0", "@jest/globals": "^27.0.3", "@mdx-js/react": "^2.3.0", - "@storybook/addon-actions": "^7.0.24", + "@storybook/addon-actions": "^8.0.0", "@storybook/addon-docs": "^7.0.24", "@storybook/addon-essentials": "^7.0.24", "@storybook/addon-links": "^7.0.24", From 65404c71aae8d2a4a931199c32815b2cff4df852 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:54:49 +0000 Subject: [PATCH 02/23] chore(deps): bump the npm_and_yarn group group with 2 updates (#1842) --- package-lock.json | 87 ++++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b52cd2e0..9455b31d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,7 +146,7 @@ "auto-changelog": "^2.4.0", "babel-jest": "^29.5.0", "chromatic": "^6.5.6", - "cypress": "^12.8.0", + "cypress": "^13.7.0", "cypress-pipe": "^2.0.0", "cz-conventional-changelog": "^3.3.0", "eslint": "^7.28.0", @@ -5053,9 +5053,9 @@ } }, "node_modules/@cypress/request": { - "version": "2.88.12", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz", - "integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", + "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -5071,7 +5071,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "~6.10.3", + "qs": "6.10.4", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -5083,8 +5083,9 @@ }, "node_modules/@cypress/request/node_modules/form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -5096,8 +5097,9 @@ }, "node_modules/@cypress/request/node_modules/qs": { "version": "6.10.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", + "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -5110,8 +5112,9 @@ }, "node_modules/@cypress/request/node_modules/uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -13984,16 +13987,18 @@ }, "node_modules/aws-sign2": { "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/aws4": { "version": "1.12.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "dev": true }, "node_modules/axe-core": { "version": "4.7.2", @@ -15350,8 +15355,9 @@ }, "node_modules/caseless": { "version": "0.12.0", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true }, "node_modules/ccount": { "version": "2.0.1", @@ -16942,20 +16948,20 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "12.17.2", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.0.tgz", + "integrity": "sha512-UimjRSJJYdTlvkChcdcfywKJ6tUYuwYuk/n1uMMglrvi+ZthNhoRYcxnWgTqUtkl17fXrPAsD5XT2rcQYN1xKA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { - "@cypress/request": "^2.88.11", + "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -16973,7 +16979,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -16982,6 +16988,7 @@ "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", "semver": "^7.5.3", @@ -16994,7 +17001,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, "node_modules/cypress-file-upload": { @@ -17012,11 +17019,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.54", - "dev": true, - "license": "MIT" - }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, @@ -19777,11 +19779,12 @@ }, "node_modules/extsprintf": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, "engines": [ "node >=0.6.0" - ], - "license": "MIT" + ] }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -20428,8 +20431,9 @@ }, "node_modules/forever-agent": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "*" } @@ -22037,8 +22041,9 @@ }, "node_modules/http-signature": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", @@ -23109,8 +23114,9 @@ }, "node_modules/isstream": { "version": "0.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -27213,8 +27219,9 @@ }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true }, "node_modules/json5": { "version": "2.2.3", @@ -27278,11 +27285,12 @@ }, "node_modules/jsprim": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "engines": [ "node >=0.6.0" ], - "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -38656,8 +38664,9 @@ }, "node_modules/tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -39462,11 +39471,12 @@ }, "node_modules/verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" ], - "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -39475,8 +39485,9 @@ }, "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true }, "node_modules/vfile": { "version": "5.3.7", diff --git a/package.json b/package.json index 4f5032d1a..d56fe39a1 100644 --- a/package.json +++ b/package.json @@ -193,7 +193,7 @@ "auto-changelog": "^2.4.0", "babel-jest": "^29.5.0", "chromatic": "^6.5.6", - "cypress": "^12.8.0", + "cypress": "^13.7.0", "cypress-pipe": "^2.0.0", "cz-conventional-changelog": "^3.3.0", "eslint": "^7.28.0", From 20621a608ae184e20acff3782d61e3658aea5f4e Mon Sep 17 00:00:00 2001 From: seaerchin <44049504+seaerchin@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:43:35 +0800 Subject: [PATCH 03/23] build(package-lock): bump pm to 2.0.8 to avoid legal (#1833) --- package-lock.json | 89 ++++------------------------------------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9455b31d0..ec41cdfe9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7767,34 +7767,6 @@ "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", "integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==" }, - "node_modules/@remirror/core-helpers": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@remirror/core-helpers/-/core-helpers-3.0.0.tgz", - "integrity": "sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==", - "dependencies": { - "@remirror/core-constants": "^2.0.2", - "@remirror/types": "^1.0.1", - "@types/object.omit": "^3.0.0", - "@types/object.pick": "^1.3.2", - "@types/throttle-debounce": "^2.1.0", - "case-anything": "^2.1.13", - "dash-get": "^1.0.2", - "deepmerge": "^4.3.1", - "fast-deep-equal": "^3.1.3", - "make-error": "^1.3.6", - "object.omit": "^3.0.0", - "object.pick": "^1.3.0", - "throttle-debounce": "^3.0.1" - } - }, - "node_modules/@remirror/types": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@remirror/types/-/types-1.0.1.tgz", - "integrity": "sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==", - "dependencies": { - "type-fest": "^2.19.0" - } - }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "dev": true, @@ -12451,16 +12423,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/object.omit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/object.omit/-/object.omit-3.0.2.tgz", - "integrity": "sha512-BxWU36cMP+FKD3OLFluQaj2cBev2sx2LJaHELuphHwnleq+xnEhTmuYYYx4pOT/1U/ZoR6B+RdvxWh2FD6lGGA==" - }, - "node_modules/@types/object.pick": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.3.tgz", - "integrity": "sha512-qZqHmdGEALeSATMB1djT1S5szv6Wtpb7DKpHrt2XG4iyKlV7C2Xk8GmDXr1KXakOqUfX6ohw7ceruYt4NVmB1Q==" - }, "node_modules/@types/parse-json": { "version": "4.0.0", "license": "MIT" @@ -12691,11 +12653,6 @@ "@types/jest": "*" } }, - "node_modules/@types/throttle-debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", - "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==" - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -15334,17 +15291,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/case-anything": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", - "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", "dev": true, @@ -17186,11 +17132,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/dash-get": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", - "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==" - }, "node_modules/dashdash": { "version": "1.14.1", "license": "MIT", @@ -22715,6 +22656,7 @@ }, "node_modules/is-extendable": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4" @@ -22725,6 +22667,7 @@ }, "node_modules/is-extendable/node_modules/is-plain-object": { "version": "2.0.4", + "dev": true, "license": "MIT", "dependencies": { "isobject": "^3.0.1" @@ -29699,19 +29642,9 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.omit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", - "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", - "dependencies": { - "is-extendable": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.pick": { "version": "1.3.0", + "dev": true, "license": "MIT", "dependencies": { "isobject": "^3.0.1" @@ -32081,12 +32014,11 @@ } }, "node_modules/prosemirror-trailing-node": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz", - "integrity": "sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.8.tgz", + "integrity": "sha512-ujRYhSuhQb1Jsarh1IHqb2KoSnRiD7wAMDGucP35DN7j5af6X7B18PfdPIrbwsPTqIAj0fyOvxbuPsWhNvylmA==", "dependencies": { "@remirror/core-constants": "^2.0.2", - "@remirror/core-helpers": "^3.0.0", "escape-string-regexp": "^4.0.0" }, "peerDependencies": { @@ -38107,14 +38039,6 @@ "dev": true, "license": "MIT" }, - "node_modules/throttle-debounce": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", - "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", - "engines": { - "node": ">=10" - } - }, "node_modules/throttleit": { "version": "1.0.0", "dev": true, @@ -38715,6 +38639,7 @@ }, "node_modules/type-fest": { "version": "2.19.0", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" From 97d343ab57ab7299d18ca4224568359a1f115ddc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:06:00 +0800 Subject: [PATCH 04/23] chore(deps): bump react-select from 5.7.4 to 5.8.0 (#1847) Bumps [react-select](https://github.com/JedWatson/react-select) from 5.7.4 to 5.8.0. - [Release notes](https://github.com/JedWatson/react-select/releases) - [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.7.4...react-select@5.8.0) --- updated-dependencies: - dependency-name: react-select dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec41cdfe9..6a136f098 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,7 +93,7 @@ "react-markdown": "^8.0.7", "react-query": "^3.34.16", "react-router-dom": "^5.1.2", - "react-select": "^5.4.0", + "react-select": "^5.8.0", "react-simplemde-editor": "^5.2.0", "react-virtuoso": "^2.19.0", "remixicon": "^3.5.0", @@ -34550,8 +34550,9 @@ } }, "node_modules/react-select": { - "version": "5.7.4", - "license": "MIT", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", diff --git a/package.json b/package.json index d56fe39a1..e6b4524ab 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "react-markdown": "^8.0.7", "react-query": "^3.34.16", "react-router-dom": "^5.1.2", - "react-select": "^5.4.0", + "react-select": "^5.8.0", "react-simplemde-editor": "^5.2.0", "react-virtuoso": "^2.19.0", "remixicon": "^3.5.0", From 8bb7772fe0f0a74fef7f2bd9396824f47f3cacb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:12:29 +0800 Subject: [PATCH 05/23] chore(deps): bump follow-redirects from 1.15.4 to 1.15.6 (#1849) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.15.4&new-version=1.15.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/isomerpages/isomercms-frontend/network/alerts).
--- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a136f098..bafa2b5b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20324,9 +20324,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", From ea960b3a16bd38dc4b248497f8f4d378abf8a68d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:38:05 +0800 Subject: [PATCH 06/23] chore(deps-dev): bump @adobe/css-tools from 4.2.0 to 4.3.3 (#1837) Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.2.0 to 4.3.3. - [Changelog](https://github.com/adobe/css-tools/blob/main/History.md) - [Commits](https://github.com/adobe/css-tools/commits) --- updated-dependencies: - dependency-name: "@adobe/css-tools" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bafa2b5b5..151fd6a93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -190,9 +190,10 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.2.0", - "dev": true, - "license": "MIT" + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", From 5e0fc135b6a8aaf8b7a15075aae16d92a58d6665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:38:24 +0800 Subject: [PATCH 07/23] chore(deps-dev): bump browserify-sign from 4.2.1 to 4.2.3 (#1836) Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.3. - [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md) - [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.3) --- updated-dependencies: - dependency-name: browserify-sign dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 85 ++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 151fd6a93..e29565414 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13722,20 +13722,21 @@ } }, "node_modules/asn1.js": { - "version": "5.4.1", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, - "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "minimalistic-assert": "^1.0.0" } }, "node_modules/asn1.js/node_modules/bn.js": { "version": "4.12.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, "node_modules/assert": { "version": "1.5.0", @@ -14964,32 +14965,37 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dev": true, - "license": "ISC", "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.5", + "hash-base": "~3.0", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" } }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.2", + "node_modules/browserify-sign/node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", "dev": true, - "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" }, "engines": { - "node": ">= 6" + "node": ">=4" } }, "node_modules/browserify-zlib": { @@ -18088,9 +18094,10 @@ "license": "ISC" }, "node_modules/elliptic": { - "version": "6.5.4", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", "dev": true, - "license": "MIT", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -30012,15 +30019,33 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.6", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", "dev": true, - "license": "ISC", "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-asn1/node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" } }, "node_modules/parse-github-url": { From 98e04d6c03e70647a6173a8fd5c52be81ff25655 Mon Sep 17 00:00:00 2001 From: seaerchin <44049504+seaerchin@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:55:15 +0800 Subject: [PATCH 08/23] feat(playwright): migrate dashboard spec (#1789) * feat(playwright): initial scaffold * feat(fixtures): add fixtures for playwright * build(package): update deps * feat(dashboard): add new spec in playwright * feat(tsconfig): add tsconfig * refactor(constants): removed extra file * refactor(const): update to export from hook * style(dashboard.spec): remove async * fix(tsconfig): use extends * fix(playwirght-script): reset e2e prior to run --- .github/workflows/playwright.yml | 27 ++ .gitignore | 6 +- cypress/e2e/dashboard.spec.ts | 110 ----- cypress/e2e/editPage.spec.ts | 3 +- cypress/e2e/folders.spec.ts | 3 +- cypress/e2e/resourceCategory.spec.ts | 3 +- cypress/e2e/workspace.spec.ts | 3 +- cypress/fixtures/messages.ts | 2 - e2e/dashboard.spec.ts | 143 +++++++ e2e/fixtures/constants.ts | 80 ++++ e2e/fixtures/users.ts | 9 + e2e/tsconfig.json | 10 + e2e/utils/session.ts | 58 +++ package-lock.json | 498 +++++++--------------- package.json | 3 + playwright.config.ts | 77 ++++ public/mockServiceWorker.js | 275 ++++++------ src/hooks/pageHooks/useUpdatePageHook.jsx | 4 +- 18 files changed, 686 insertions(+), 628 deletions(-) create mode 100644 .github/workflows/playwright.yml delete mode 100644 cypress/e2e/dashboard.spec.ts delete mode 100644 cypress/fixtures/messages.ts create mode 100644 e2e/dashboard.spec.ts create mode 100644 e2e/fixtures/constants.ts create mode 100644 e2e/fixtures/users.ts create mode 100644 e2e/tsconfig.json create mode 100644 e2e/utils/session.ts create mode 100644 playwright.config.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 000000000..90b6b700d --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 60ff6bb8c..76ec0182e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,8 @@ build-storybook.log storybook-static/ # cypress -cypress.env.json \ No newline at end of file +cypress.env.json +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/cypress/e2e/dashboard.spec.ts b/cypress/e2e/dashboard.spec.ts deleted file mode 100644 index 719757086..000000000 --- a/cypress/e2e/dashboard.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { closeReviewRequests } from "../api" -import { - E2E_EMAIL_REPO_MASTER_LINK, - E2E_EMAIL_REPO_STAGING_LINK, - E2E_EMAIL_TEST_SITE, - ISOMER_GUIDE_LINK, - TEST_REPO_NAME, -} from "../fixtures/constants" -import { getOpenStagingButton, visitE2eEmailTestRepo } from "../utils" - -const getReviewRequestButton = () => cy.contains("button", "Request a Review") - -const getOpenStagingDropdownButton = () => - getOpenStagingButton().siblings("button") - -const goToWorkspace = () => cy.contains("a", "Edit site").click() - -const REVIEW_MODAL_SUBTITLE = - "An Admin needs to review and approve your changes before they can be published" - -const REVIEW_REQUEST_ALERT_MESSAGE = - "There’s a Review request pending approval. Any changes you make now will be added to the existing Review request, and published with the changes in it." - -describe("dashboard flow", () => { - beforeEach(() => { - cy.setupDefaultInterceptors() - cy.setEmailSessionDefaults("Email admin") - visitE2eEmailTestRepo() - closeReviewRequests() - }) - - it('should open the staging site on click of the "Open staging" button', () => { - // Assert - getOpenStagingButton() - .should("have.attr", "href", E2E_EMAIL_REPO_STAGING_LINK) - .should("have.attr", "target", "_blank") - }) - - it('should open the "Request a Review" modal on click of the "Request a Review" button', () => { - // Act - getReviewRequestButton().click() - - // Assert - cy.contains(REVIEW_MODAL_SUBTITLE).should("be.visible") - }) - - it("should be able to navigate to the staging site using the dropdown button", () => { - // Act - getOpenStagingDropdownButton().click() - - // Assert - cy.contains("Open staging site") - .should("be.visible") - .should("have.attr", "href", E2E_EMAIL_REPO_STAGING_LINK) - .should("have.attr", "target", "_blank") - }) - - it("should be able to navigate to the production site using the dropdown button", () => { - // Act - getOpenStagingDropdownButton().click() - - // Assert - cy.contains("Visit live site") - .should("be.visible") - .should("have.attr", "href", E2E_EMAIL_REPO_MASTER_LINK) - .should("have.attr", "target", "_blank") - }) - - it('should navigate to the isomer guide on click of the "Get help" button', () => { - // Act - cy.contains("Get help") - .should("be.visible") - .should("have.attr", "href", ISOMER_GUIDE_LINK) - .should("have.attr", "target", "_blank") - }) - - it("should navigate to the settings page when manage site settings is clicked", () => { - // Act - cy.contains("Site settings") - .should("be.visible") - .parent() - .parent() - .siblings("a") - .should( - "have.attr", - "href", - `/sites/${E2E_EMAIL_TEST_SITE.repo}/settings` - ) - .click() - - // Assert - cy.contains("Site settings").should("be.visible") - }) - - it("should navigate to the workspace when edit site is clicked", () => { - // Act - cy.contains("Edit site") - .should("be.visible") - .should( - "have.attr", - "href", - `/sites/${E2E_EMAIL_TEST_SITE.repo}/workspace` - ) - .click() - - // Assert - cy.contains("My Workspace").should("be.visible") - cy.contains(REVIEW_REQUEST_ALERT_MESSAGE).should("not.exist") - }) -}) diff --git a/cypress/e2e/editPage.spec.ts b/cypress/e2e/editPage.spec.ts index ac563ab4e..74168e271 100644 --- a/cypress/e2e/editPage.spec.ts +++ b/cypress/e2e/editPage.spec.ts @@ -1,4 +1,6 @@ import "cypress-file-upload" +import { SUCCESSFUL_EDIT_PAGE_TOAST } from "hooks/pageHooks/useUpdatePageHook" + import { slugifyCategory, generateResourceFileName, @@ -10,7 +12,6 @@ import { Interceptors, TEST_REPO_NAME, } from "../fixtures/constants" -import { SUCCESSFUL_EDIT_PAGE_TOAST } from "../fixtures/messages" // Constants const PRIMARY_COLOUR = "rgb(255, 0, 0)" diff --git a/cypress/e2e/folders.spec.ts b/cypress/e2e/folders.spec.ts index 71bc96cd8..375c3bc77 100644 --- a/cypress/e2e/folders.spec.ts +++ b/cypress/e2e/folders.spec.ts @@ -1,5 +1,7 @@ import slugify from "slugify" +import { SUCCESSFUL_EDIT_PAGE_TOAST } from "hooks/pageHooks/useUpdatePageHook" + import { deslugifyDirectory } from "utils/deslugify" import { titleToPageFileName, pageFileNameToTitle } from "utils/fileNameUtils" @@ -8,7 +10,6 @@ import { Interceptors, TEST_REPO_NAME, } from "../fixtures/constants" -import { SUCCESSFUL_EDIT_PAGE_TOAST } from "../fixtures/messages" describe("Folders flow", () => { const DEFAULT_REPO_FOLDER_NAME = "default" diff --git a/cypress/e2e/resourceCategory.spec.ts b/cypress/e2e/resourceCategory.spec.ts index 21a495f59..2076c601a 100644 --- a/cypress/e2e/resourceCategory.spec.ts +++ b/cypress/e2e/resourceCategory.spec.ts @@ -1,4 +1,6 @@ import "cypress-file-upload" +import { SUCCESSFUL_EDIT_PAGE_TOAST } from "hooks/pageHooks/useUpdatePageHook" + import { generateResourceFileName, slugifyCategory } from "utils/fileNameUtils" import { @@ -6,7 +8,6 @@ import { TEST_REPO_NAME, Interceptors, } from "../fixtures/constants" -import { SUCCESSFUL_EDIT_PAGE_TOAST } from "../fixtures/messages" describe("Resource category page", () => { const TEST_RESOURCE_ROOM_NAME = "resources" diff --git a/cypress/e2e/workspace.spec.ts b/cypress/e2e/workspace.spec.ts index 1d399fc07..f1270bf17 100644 --- a/cypress/e2e/workspace.spec.ts +++ b/cypress/e2e/workspace.spec.ts @@ -1,3 +1,5 @@ +import { SUCCESSFUL_EDIT_PAGE_TOAST } from "hooks/pageHooks/useUpdatePageHook" + import { deslugifyDirectory } from "utils/deslugify" import { slugifyCategory, titleToPageFileName } from "utils/fileNameUtils" @@ -6,7 +8,6 @@ import { TEST_REPO_NAME, Interceptors, } from "../fixtures/constants" -import { SUCCESSFUL_EDIT_PAGE_TOAST } from "../fixtures/messages" const DATA_LOAD_ASSERTION_TEXT = "You can link folders to your navigation bar." diff --git a/cypress/fixtures/messages.ts b/cypress/fixtures/messages.ts deleted file mode 100644 index 116a1e8de..000000000 --- a/cypress/fixtures/messages.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const SUCCESSFUL_EDIT_PAGE_TOAST = - "Changes saved. See a preview on Staging, or request a Review for them to be published." diff --git a/e2e/dashboard.spec.ts b/e2e/dashboard.spec.ts new file mode 100644 index 000000000..e301ee468 --- /dev/null +++ b/e2e/dashboard.spec.ts @@ -0,0 +1,143 @@ +import { test, expect, Page } from "@playwright/test" + +import { + E2E_EMAIL_REPO_MASTER_LINK, + E2E_EMAIL_REPO_STAGING_LINK, + E2E_EMAIL_TEST_SITE, + ISOMER_GUIDE_LINK, +} from "./fixtures/constants" +import { setEmailSessionDefaults } from "./utils/session" + +const REVIEW_MODAL_SUBTITLE = + "An Admin needs to review and approve your changes before they can be published" + +const REVIEW_REQUEST_ALERT_MESSAGE = + "There’s a Review request pending approval. Any changes you make now will be added to the existing Review request, and published with the changes in it." + +const getReviewRequestButton = (page: Page) => { + const button = page.locator("button", { hasText: /Request a Review/ }).first() + + expect(button).toBeVisible() + + return button +} + +test.describe("dashboard flow", () => { + test.beforeEach(async ({ page, context }) => { + setEmailSessionDefaults(context, "Email admin") + await page.goto(`/sites/${E2E_EMAIL_TEST_SITE.repo}/dashboard`) + expect(page.getByText(E2E_EMAIL_TEST_SITE.repo)).toBeVisible() + // TODO: Close review requests + // closeReviewRequests() + }) + + test('should open the staging site on click of the "Open staging" button', async ({ + page, + }) => { + // Act + const link = page.getByRole("link", { + name: /Open staging/, + }) + + // Assert + await expect(link).toHaveAttribute("href", E2E_EMAIL_REPO_STAGING_LINK) + await expect(link).toHaveAttribute("target", "_blank") + }) + + test('should open the "Request a Review" modal on click of the "Request a Review" button', async ({ + page, + }) => { + // Act + const button = await getReviewRequestButton(page) + await button.click() + + // Assert + await expect(page.getByText(REVIEW_MODAL_SUBTITLE).first()).toBeVisible() + }) + + test("should be able to navigate to the staging site using the dropdown button", async ({ + page, + }) => { + // Act + const dropdown = page.getByLabel("Select options") + await dropdown.click() + const stagingLink = page.getByRole("menuitem", { + name: "Open staging site", + }) + + // Assert + await expect(stagingLink).toBeVisible() + await expect(stagingLink).toHaveAttribute( + "href", + E2E_EMAIL_REPO_STAGING_LINK + ) + await expect(stagingLink).toHaveAttribute("target", "_blank") + }) + + test("should be able to navigate to the production site using the dropdown button", async ({ + page, + }) => { + // Act + const dropdown = page.getByLabel("Select options") + await dropdown.click() + const prodLink = page.getByRole("menuitem", { + name: "Visit live site", + }) + + // Assert + await expect(prodLink).toBeVisible() + await expect(prodLink).toHaveAttribute("href", E2E_EMAIL_REPO_MASTER_LINK) + await expect(prodLink).toHaveAttribute("target", "_blank") + }) + + test('should navigate to the isomer guide on click of the "Get help" button', async ({ + page, + }) => { + // Act + const helpLink = page.getByRole("link", { name: "Get help" }) + + await expect(helpLink).toBeVisible() + await expect(helpLink).toHaveAttribute("href", ISOMER_GUIDE_LINK) + await expect(helpLink).toHaveAttribute("target", "_blank") + }) + + test("should navigate to the settings page when manage site settings is clicked", async ({ + page, + }) => { + // Act + // NOTE: Even though there are 2 visually identical links with the text "Manage", + // the first one is actually a button so this will work. + const settingsLink = await page.getByRole("link", { name: "Manage" }) + + await expect(settingsLink).toHaveAttribute( + "href", + `/sites/${E2E_EMAIL_TEST_SITE.repo}/settings` + ) + await settingsLink.click() + + // Assert + await expect( + page.getByRole("heading", { name: "Site settings" }) + ).toBeVisible() + }) + + test("should navigate to the workspace when edit site is clicked", async ({ + page, + }) => { + // Act + const editSiteButton = page.getByRole("link", { + name: "Edit site", + }) + await expect(editSiteButton).toBeVisible() + await editSiteButton.click() + + // Assert + await expect( + page.getByRole("heading", { name: "My Workspace" }) + ).toBeVisible() + // NOTE: check that the locator matches nothing + await expect( + await page.getByText(REVIEW_REQUEST_ALERT_MESSAGE).count() + ).toEqual(0) + }) +}) diff --git a/e2e/fixtures/constants.ts b/e2e/fixtures/constants.ts new file mode 100644 index 000000000..f18fc7912 --- /dev/null +++ b/e2e/fixtures/constants.ts @@ -0,0 +1,80 @@ +export const { + CYPRESS_TEST_REPO_NAME: TEST_REPO_NAME, + CYPRESS_CMS_BASEURL: CMS_BASEURL, + CYPRESS_BACKEND_URL: BACKEND_URL, + CYPRESS_COOKIE_NAME: COOKIE_NAME, + CYPRESS_COOKIE_VALUE: COOKIE_VALUE, +} = process.env as Record + +export const E2E_USER = { + userId: "test", + email: "test@open.gov.sg", + contactNumber: "99999999", +} + +export const E2E_EMAIL_ADMIN = { + email: "admin@e2e.gov.sg", +} as const + +export const E2E_EMAIL_ADMIN_2 = { + email: "twodmin@e2e.gov.sg", +} as const + +export const E2E_EMAIL_COLLAB = { + email: "collab@e2e.gov.sg", +} as const + +export const E2E_EMAIL_COLLAB_NON_GOV = { + email: "collab@e2etest.com", +} as const + +export const E2E_EMAIL_TEST_SITE = { + name: "e2e email test site", + repo: "e2e-email-test-repo", +} + +export const E2E_COOKIE = { + Auth: { + key: COOKIE_NAME, + value: COOKIE_VALUE, + }, + Site: { key: "site", value: E2E_EMAIL_TEST_SITE.name }, + EmailUserType: { key: "e2eUserType" }, + Email: { key: "email" }, +} + +export const E2E_USER_TYPE_COOKIE_KEY = "e2eUserType" +export const E2E_SITE_KEY = "site" +export const LOCAL_STORAGE_USERID_KEY = "userId" +export const LOCAL_STORAGE_USER_KEY = "user" +export const TEST_PRIMARY_COLOR = [255, 0, 0] + +export const BASE_SEO_LINK = "www.open.gov.sg" + +export const E2E_EMAIL_REPO_STAGING_LINK = + "https://staging.d2qim5mov3pptm.amplifyapp.com" + +export const E2E_EMAIL_REPO_MASTER_LINK = + "https://master.d2qim5mov3pptm.amplifyapp.com" + +export const ISOMER_GUIDE_LINK = "https://guide.isomer.gov.sg/" + +export const MOCK_REVIEW_TITLE = "Some interesting title" +export const MOCK_REVIEW_DESCRIPTION = "Some interesting description" + +export const FORBIDDEN_CHARACTERS = "!@#$%^&*()" +export const NON_ENGLISH_CHARACTERS = "文கி" + +export const timings = { + outOfTheWay: 0.2, + // greater than the out of the way time + // so that when the drop ends everything will + // have to be out of the way + minDropTime: 0.33, + maxDropTime: 0.55, +} + +export const keyCodes = { + space: 32, + arrowDown: 40, +} diff --git a/e2e/fixtures/users.ts b/e2e/fixtures/users.ts new file mode 100644 index 000000000..1a0b8578d --- /dev/null +++ b/e2e/fixtures/users.ts @@ -0,0 +1,9 @@ +export const USER_TYPES = { + Email: { + Admin: "Email admin", + Collaborator: "Email collaborator", + }, + Github: "Github user", +} as const + +export type EmailUserTypes = typeof USER_TYPES["Email"][keyof typeof USER_TYPES["Email"]] diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 000000000..3b241a7fb --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "../src", + "paths": { + "utils": ["../src/utils/index"] + } + }, + "include": ["**/*.ts"] +} diff --git a/e2e/utils/session.ts b/e2e/utils/session.ts new file mode 100644 index 000000000..dfaec0a2c --- /dev/null +++ b/e2e/utils/session.ts @@ -0,0 +1,58 @@ +import { APIRequestContext, BrowserContext } from "@playwright/test" + +import { + BACKEND_URL, + COOKIE_NAME, + COOKIE_VALUE, + E2E_COOKIE, + E2E_EMAIL_ADMIN, + E2E_EMAIL_COLLAB, + E2E_EMAIL_TEST_SITE, + E2E_SITE_KEY, + E2E_USER_TYPE_COOKIE_KEY, +} from "../fixtures/constants" +import { EmailUserTypes } from "../fixtures/users" + +const setCookieWithDomain = ( + context: BrowserContext, + name: string, + value: string +) => { + context.addCookies([ + { + name, + value, + url: BACKEND_URL, + }, + ]) +} + +export const setEmailSessionDefaults = ( + context: BrowserContext, + userType: EmailUserTypes +) => { + setCookieWithDomain(context, COOKIE_NAME, COOKIE_VALUE) + setCookieWithDomain(context, E2E_USER_TYPE_COOKIE_KEY, userType) + setCookieWithDomain(context, E2E_SITE_KEY, E2E_EMAIL_TEST_SITE.name) + setCookieWithDomain( + context, + E2E_COOKIE.Email.key, + userType === "Email admin" ? E2E_EMAIL_ADMIN.email : E2E_EMAIL_COLLAB.email + ) + setCookieWithDomain(context, E2E_COOKIE.Site.key, E2E_COOKIE.Site.value) +} + +export const actAsEmailUser = ( + context: BrowserContext, + apiContext: APIRequestContext, + email: string, + userType: EmailUserTypes, + site = "" +) => { + setCookieWithDomain(context, E2E_COOKIE.Site.key, site) + setCookieWithDomain(context, COOKIE_NAME, COOKIE_VALUE) + setCookieWithDomain(context, E2E_COOKIE.Auth.key, E2E_COOKIE.Auth.value) + setCookieWithDomain(context, E2E_COOKIE.EmailUserType.key, userType) + setCookieWithDomain(context, E2E_COOKIE.Email.key, email) + apiContext.get(`/auth/whoami`) +} diff --git a/package-lock.json b/package-lock.json index e29565414..9915dea54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,6 +119,7 @@ "@cypress/webpack-preprocessor": "^5.17.0", "@jest/globals": "^27.0.3", "@mdx-js/react": "^2.3.0", + "@playwright/test": "^1.41.1", "@storybook/addon-actions": "^8.0.0", "@storybook/addon-docs": "^7.0.24", "@storybook/addon-essentials": "^7.0.24", @@ -136,6 +137,7 @@ "@types/html": "^1.0.4", "@types/invariant": "^2.2.35", "@types/jest": "^29.4.4", + "@types/node": "^20.10.5", "@types/qrcode": "^1.5.4", "@types/react-color": "^3.0.6", "@types/react-router-dom": "^5.3.3", @@ -190,10 +192,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", - "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", - "dev": true + "version": "4.2.0", + "dev": true, + "license": "MIT" }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", @@ -1145,12 +1146,10 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.22.5", + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1211,12 +1210,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.22.9", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1319,22 +1317,20 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.22.5", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.22.5", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1482,9 +1478,8 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.22.5", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -1524,12 +1519,11 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.22.5", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "engines": { @@ -1537,10 +1531,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.22.7", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -3190,33 +3183,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.22.5", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.22.8", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -3225,12 +3216,11 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.22.5", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -5470,20 +5460,6 @@ "version": "0.3.1", "license": "MIT" }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -7644,6 +7620,21 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "dev": true, + "dependencies": { + "playwright": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.10", "dev": true, @@ -9085,21 +9076,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/darwin-x64": { - "version": "0.18.17", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@storybook/builder-manager/node_modules/esbuild": { "version": "0.18.17", "dev": true, @@ -9636,21 +9612,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/core-common/node_modules/@esbuild/darwin-x64": { - "version": "0.18.17", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@storybook/core-common/node_modules/@types/node": { "version": "16.18.39", "dev": true, @@ -10929,164 +10890,6 @@ "node": ">=10" } }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.73", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.73.tgz", - "integrity": "sha512-EmJALh7KUJhcdr7uUQg7wTpdcX5k1Xjspgy3QMg8j2dwb4DsnFgrnArsFNXHBB1Dj7LlQSoyxQ5mBcJtUtCb8A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.73.tgz", - "integrity": "sha512-RK6jTm8ppvglh42YOq/k2AqpHS9uYP5h5FNMmA9OI8lupCCS8HMtexbwqw+Xd0MGmSrsJiURw3Z6az8cEObrag==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.73.tgz", - "integrity": "sha512-hhS6yfgZLKPVAklGjKlbyf9InAhDGj3u+jbZkjStrOgtYNBCk5tbkROZP9ib5enN9m9Oosl5gM5v6oTw27TbUw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.95", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.95.tgz", - "integrity": "sha512-n9SuHEFtdfSJ+sHdNXNRuIOVprB8nbsz+08apKfdo4lEKq6IIPBBAk5kVhPhkjmg2dFVHVo4Tr/OHXM1tzWCCw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.73.tgz", - "integrity": "sha512-DMz2W0PnzMXAhbMPGArQUBVayyzzzuivvJyJkyFaMPiIwaI+QG+UvLgjSM7NmG/9Eq9hX2zZ1zdaalVKXyyCHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.73.tgz", - "integrity": "sha512-yHB1jG3c4/5An//nA9+War6oiNrM/NUz6ivDPbrBfbJHtU/iPfgdAvxfm5/xpOFx4U18JJHnOt853sDyXJwi/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.73.tgz", - "integrity": "sha512-cA61i4VPTrABAZ8LDvNVqwcO1VLEDO+71iWettvhyk7p6/H/lXG4VQVyHcncmfrAUzDQalXVbgZm6MA3hpqhFQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.73.tgz", - "integrity": "sha512-QwTO9IlIpEr2GsJvW8qNVvQXTzT1ASqf8C8aZDLtVwHKdreTMjlrNMRYw1883DVLRuHMs5RLP4IA2A47Oexp1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core/node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.73", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.73.tgz", - "integrity": "sha512-ZGcY63EtFW5OLz1tsKhqhymzvoto329c0oRS9ptzMO66eUrjsHxTt5uPixrI24F6y+bn+qFqsgIw3nwMV8jTPw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@tanstack/react-table": { "version": "8.9.3", "license": "MIT", @@ -12407,8 +12210,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.4.5", - "license": "MIT" + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-fetch": { "version": "2.6.4", @@ -13722,21 +13529,20 @@ } }, "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "version": "5.4.1", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, "node_modules/asn1.js/node_modules/bn.js": { "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/assert": { "version": "1.5.0", @@ -14965,37 +14771,32 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "version": "4.2.1", "dev": true, + "license": "ISC", "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", + "elliptic": "^6.5.3", "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" } }, - "node_modules/browserify-sign/node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.2", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, "node_modules/browserify-zlib": { @@ -15104,15 +14905,6 @@ "dev": true, "license": "MIT" }, - "node_modules/buildcheck": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", - "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/builtin-modules": { "version": "3.3.0", "dev": true, @@ -16294,20 +16086,6 @@ "version": "1.5.7", "license": "MIT" }, - "node_modules/cpu-features": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", - "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.17.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/create-ecdh": { "version": "4.0.4", "dev": true, @@ -17935,9 +17713,9 @@ "license": "BSD-2-Clause" }, "node_modules/dompurify": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", - "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.9.tgz", + "integrity": "sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==" }, "node_modules/domutils": { "version": "3.1.0", @@ -18094,10 +17872,9 @@ "license": "ISC" }, "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "version": "6.5.4", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -20332,9 +20109,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", @@ -29067,9 +28844,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", - "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.3.tgz", + "integrity": "sha512-CiPyRFiYJCXYyH/vwxT7m+sa4VZHuUH6cGwRBj0kaTjBGpsk4EnL47YzhoA859htVCF2vzqZuOsomIUlFqg9GQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -29104,7 +28881,7 @@ "url": "https://opencollective.com/mswjs" }, "peerDependencies": { - "typescript": ">= 4.4.x <= 5.2.x" + "typescript": ">= 4.4.x" }, "peerDependenciesMeta": { "typescript": { @@ -29210,12 +28987,6 @@ "thenify-all": "^1.0.0" } }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", - "optional": true - }, "node_modules/nano-time": { "version": "1.0.0", "license": "ISC", @@ -29871,9 +29642,9 @@ } }, "node_modules/outvariant": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", - "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", "dev": true }, "node_modules/p-limit": { @@ -30019,33 +29790,15 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", - "dev": true, - "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse-asn1/node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "version": "5.1.6", "dev": true, + "license": "ISC", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": ">=4" + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" } }, "node_modules/parse-github-url": { @@ -30381,6 +30134,36 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "dev": true, + "dependencies": { + "playwright-core": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/please-upgrade-node": { "version": "3.2.0", "dev": true, @@ -38249,9 +38032,9 @@ } }, "node_modules/tough-cookie/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -38830,6 +38613,11 @@ "undeclared-identifiers": "bin.js" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "dev": true, diff --git a/package.json b/package.json index e6b4524ab..8f952a2a8 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "build:netlify": "CI=false npm run build && npm run sourcemaps:upload", "release": "npm version $npm_config_isomer_update && git push --tags", "test": "craco test", + "test:playwright": "source .env && npm run reset-e2e && npx playwright test --ui", "test:ci": "node scripts/run-e2e.js run", "test:email-ci": "node scripts/run-e2e.js run-email", "test-e2e": "source .env && node scripts/run-e2e.js run", @@ -167,6 +168,7 @@ "@jest/globals": "^27.0.3", "@mdx-js/react": "^2.3.0", "@storybook/addon-actions": "^8.0.0", + "@playwright/test": "^1.41.1", "@storybook/addon-docs": "^7.0.24", "@storybook/addon-essentials": "^7.0.24", "@storybook/addon-links": "^7.0.24", @@ -183,6 +185,7 @@ "@types/html": "^1.0.4", "@types/invariant": "^2.2.35", "@types/jest": "^29.4.4", + "@types/node": "^20.10.5", "@types/qrcode": "^1.5.4", "@types/react-color": "^3.0.6", "@types/react-router-dom": "^5.3.3", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 000000000..28936f2fa --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from "@playwright/test" + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./e2e", + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.CYPRESS_BASEURL || "http://localhost:3000", // TODO: rename this to a framework agnostic name + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + + { + name: "firefox", + use: { ...devices["Desktop Firefox"] }, + }, + + { + name: "webkit", + use: { ...devices["Desktop Safari"] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}) diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index d7243e2bf..35b982861 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -2,22 +2,21 @@ /* tslint:disable */ /** - * Mock Service Worker (0.42.3). + * Mock Service Worker (1.3.3). * @see https://github.com/mswjs/msw * - Please do NOT modify this file. * - Please do NOT serve this file on production. */ -const INTEGRITY_CHECKSUM = "02f4ad4a2797f85668baf196e553d929" -const bypassHeaderName = "x-msw-bypass" +const INTEGRITY_CHECKSUM = "3d6b9f06410d179a7f7404d4bf4c3c70" const activeClientIds = new Set() self.addEventListener("install", function () { - return self.skipWaiting() + self.skipWaiting() }) -self.addEventListener("activate", async function (event) { - return self.clients.claim() +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()) }) self.addEventListener("message", async function (event) { @@ -33,7 +32,9 @@ self.addEventListener("message", async function (event) { return } - const allClients = await self.clients.matchAll() + const allClients = await self.clients.matchAll({ + type: "window", + }) switch (event.data) { case "KEEPALIVE_REQUEST": { @@ -83,30 +84,58 @@ self.addEventListener("message", async function (event) { } }) -// Resolve the "main" client for the given event. -// Client that issues a request doesn't necessarily equal the client -// that registered the worker. It's with the latter the worker should -// communicate with during the response resolving phase. -async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) +self.addEventListener("fetch", function (event) { + const { request } = event + const accept = request.headers.get("accept") || "" - if (client.frameType === "top-level") { - return client + // Bypass server-sent events. + if (accept.includes("text/event-stream")) { + return } - const allClients = await self.clients.matchAll() + // Bypass navigation requests. + if (request.mode === "navigate") { + return + } - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === "visible" - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === "only-if-cached" && request.mode !== "same-origin") { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === "NetworkError") { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}` + ) }) -} + ) +}) async function handleRequest(event, requestId) { const client = await resolveMainClient(event) @@ -128,7 +157,7 @@ async function handleRequest(event, requestId) { statusText: clonedResponse.statusText, body: clonedResponse.body === null ? null : await clonedResponse.text(), - headers: serializeHeaders(clonedResponse.headers), + headers: Object.fromEntries(clonedResponse.headers.entries()), redirected: clonedResponse.redirected, }, }) @@ -138,14 +167,54 @@ async function handleRequest(event, requestId) { return response } +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === "top-level") { + return client + } + + const allClients = await self.clients.matchAll({ + type: "window", + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === "visible" + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + async function getResponse(event, client, requestId) { const { request } = event - const requestClone = request.clone() - const getOriginalResponse = () => fetch(requestClone) + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers["x-msw-bypass"] - // Bypass mocking when the request client is not active. + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. if (!client) { - return getOriginalResponse() + return passthrough() } // Bypass initial page load requests (i.e. static assets). @@ -153,34 +222,23 @@ async function getResponse(event, client, requestId) { // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet // and is not ready to handle requests. if (!activeClientIds.has(client.id)) { - return await getOriginalResponse() + return passthrough() } - // Bypass requests with the explicit bypass header - if (requestClone.headers.get(bypassHeaderName) === "true") { - const cleanRequestHeaders = serializeHeaders(requestClone.headers) - - // Remove the bypass header to comply with the CORS preflight check. - delete cleanRequestHeaders[bypassHeaderName] - - const originalRequest = new Request(requestClone, { - headers: new Headers(cleanRequestHeaders), - }) - - return fetch(originalRequest) + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get("x-msw-bypass") === "true") { + return passthrough() } - // Send the request to the client-side MSW. - const reqHeaders = serializeHeaders(request.headers) - const body = await request.text() - + // Notify the client that a request has been intercepted. const clientMessage = await sendToClient(client, { type: "REQUEST", payload: { id: requestId, url: request.url, method: request.method, - headers: reqHeaders, + headers: Object.fromEntries(request.headers.entries()), cache: request.cache, mode: request.mode, credentials: request.credentials, @@ -189,115 +247,32 @@ async function getResponse(event, client, requestId) { redirect: request.redirect, referrer: request.referrer, referrerPolicy: request.referrerPolicy, - body, + body: await request.text(), bodyUsed: request.bodyUsed, keepalive: request.keepalive, }, }) switch (clientMessage.type) { - case "MOCK_SUCCESS": { - return delayPromise( - () => respondWithMock(clientMessage), - clientMessage.payload.delay - ) + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data) } case "MOCK_NOT_FOUND": { - return getOriginalResponse() + return passthrough() } case "NETWORK_ERROR": { - const { name, message } = clientMessage.payload + const { name, message } = clientMessage.data const networkError = new Error(message) networkError.name = name - // Rejecting a request Promise emulates a network error. + // Rejecting a "respondWith" promise emulates a network error. throw networkError } - - case "INTERNAL_ERROR": { - const parsedBody = JSON.parse(clientMessage.payload.body) - - console.error( - `\ -[MSW] Uncaught exception in the request handler for "%s %s": - -${parsedBody.location} - -This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ -`, - request.method, - request.url - ) - - return respondWithMock(clientMessage) - } - } - - return getOriginalResponse() -} - -self.addEventListener("fetch", function (event) { - const { request } = event - const accept = request.headers.get("accept") || "" - - // Bypass server-sent events. - if (accept.includes("text/event-stream")) { - return - } - - // Bypass navigation requests. - if (request.mode === "navigate") { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === "only-if-cached" && request.mode !== "same-origin") { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return } - const requestId = uuidv4() - - return event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === "NetworkError") { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url - ) - return - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ -[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}` - ) - }) - ) -}) - -function serializeHeaders(headers) { - const reqHeaders = {} - headers.forEach((value, name) => { - reqHeaders[name] = reqHeaders[name] - ? [].concat(reqHeaders[name]).concat(value) - : value - }) - return reqHeaders + return passthrough() } function sendToClient(client, message) { @@ -312,27 +287,17 @@ function sendToClient(client, message) { resolve(event.data) } - client.postMessage(JSON.stringify(message), [channel.port2]) + client.postMessage(message, [channel.port2]) }) } -function delayPromise(cb, duration) { +function sleep(timeMs) { return new Promise((resolve) => { - setTimeout(() => resolve(cb()), duration) + setTimeout(resolve, timeMs) }) } -function respondWithMock(clientMessage) { - return new Response(clientMessage.payload.body, { - ...clientMessage.payload, - headers: clientMessage.payload.headers, - }) -} - -function uuidv4() { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { - const r = (Math.random() * 16) | 0 - const v = c == "x" ? r : (r & 0x3) | 0x8 - return v.toString(16) - }) +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) } diff --git a/src/hooks/pageHooks/useUpdatePageHook.jsx b/src/hooks/pageHooks/useUpdatePageHook.jsx index 9d3142fec..0a64b023b 100644 --- a/src/hooks/pageHooks/useUpdatePageHook.jsx +++ b/src/hooks/pageHooks/useUpdatePageHook.jsx @@ -15,6 +15,8 @@ import { useSuccessToast, useErrorToast } from "utils/toasts" import { extractPageInfo } from "./utils" +export const SUCCESSFUL_EDIT_PAGE_TOAST = `Changes saved. See a preview on Staging, or request a Review for them to be published.` + export function useUpdatePageHook(params, queryParams) { const queryClient = useQueryClient() const { pageService } = useContext(ServicesContext) @@ -57,7 +59,7 @@ export function useUpdatePageHook(params, queryParams) { ]) // invalidates unlinked pages successToast({ id: "update-resource-room-name-success", - description: `Changes saved. See a preview on Staging, or request a Review for them to be published.`, + description: SUCCESSFUL_EDIT_PAGE_TOAST, }) if (queryParams && queryParams.onSuccess) queryParams.onSuccess(data) }, From 21cb23913776dc1e2e944f4436eec063792dbbe5 Mon Sep 17 00:00:00 2001 From: seaerchin <44049504+seaerchin@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:04:20 +0800 Subject: [PATCH 09/23] feat(e2e): migrate unlinked page tests to playwright (#1790) * feat(editpage): add spec for unlinked pages * refactor(editpage): remove cypress specs that got migrated * docs(editpage): reload + add TODO --- cypress/e2e/editPage.spec.ts | 214 -------------------- e2e/api/context.api.ts | 12 ++ e2e/editPage.spec.ts | 378 +++++++++++++++++++++++++++++++++++ e2e/fixtures/messages.ts | 1 + 4 files changed, 391 insertions(+), 214 deletions(-) create mode 100644 e2e/api/context.api.ts create mode 100644 e2e/editPage.spec.ts create mode 100644 e2e/fixtures/messages.ts diff --git a/cypress/e2e/editPage.spec.ts b/cypress/e2e/editPage.spec.ts index 74168e271..9ae92dbbe 100644 --- a/cypress/e2e/editPage.spec.ts +++ b/cypress/e2e/editPage.spec.ts @@ -23,220 +23,6 @@ describe("editPage.spec", () => { cy.setGithubSessionDefaults() }) - describe("Edit unlinked page", () => { - const TEST_PAGE_CONTENT = "lorem ipsum" - const TEST_INSTAGRAM_EMBED_SCRIPT = - '' - const TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT = - '' - const TEST_UNTRUSTED_SCRIPT = - '' - const TEST_INLINE_SCRIPT = '' - - const TEST_UNLINKED_PAGE_TITLE = "Test Unlinked Page" - const TEST_UNLINKED_PAGE_FILENAME = titleToPageFileName( - TEST_UNLINKED_PAGE_TITLE - ) - const TEST_PAGE_TITLE_ENCODED = encodeURIComponent( - TEST_UNLINKED_PAGE_FILENAME - ) - - const DEFAULT_IMAGE_TITLE = "isomer-logo.svg" - const ADDED_IMAGE_TITLE = "balloon" - const ADDED_IMAGE_PATH = "images/balloon.png" - - const ADDED_FILE_TITLE = "singapore-pages" - const ADDED_FILE_PATH = "files/singapore.pdf" - - const LINK_TITLE = "link" - const LINK_URL = "https://www.google.com" - - before(() => { - cy.setDefaultSettings() - - // NOTE: We need to repeat the interceptor here as - // cypress resolves by type before nesting level. - // Hence, the alias here will not be resolved as the `before` hook - // will be resolved before the outer `beforeEach` - cy.setupDefaultInterceptors() - - // Set up test resource categories - cy.visit(`/sites/${TEST_REPO_NAME}/workspace`) - cy.contains("a", "Create page").click({ force: true }) - cy.get("#title").clear().type(TEST_UNLINKED_PAGE_TITLE) - cy.contains("Save").click().wait(Interceptors.POST) - }) - - beforeEach(() => { - cy.visit(`/sites/${TEST_REPO_NAME}/editPage/${TEST_PAGE_TITLE_ENCODED}`) - cy.contains("verify").should("not.exist") - }) - - it("Edit page (unlinked) should have correct colour", () => { - cy.get("#display-header").should( - "have.css", - "background-color", - PRIMARY_COLOUR - ) - }) - - it("Edit page (unlinked) should have name of title", () => { - cy.contains(TEST_UNLINKED_PAGE_TITLE) - }) - - it("Edit page (unlinked) should provide a warning to users when navigating away", () => { - cy.get(".CodeMirror-scroll").type(TEST_PAGE_CONTENT) - cy.get('button[aria-label="Back to sites"]').click() - - cy.contains("Warning") - cy.contains(":button", "No").click() - - // Sanity check: still in unlinked pages and content still present - cy.url().should( - "include", - `${CMS_BASEURL}/sites/${TEST_REPO_NAME}/editPage/${TEST_PAGE_TITLE_ENCODED}` - ) - cy.contains(TEST_PAGE_CONTENT) - - cy.get('button[aria-label="Back to sites"]').click() - - cy.contains("Warning") - cy.contains(":button", "Yes").click() - - // Assert: in Workspace - cy.url().should( - "include", - `${CMS_BASEURL}/sites/${TEST_REPO_NAME}/workspace` - ) - }) - - it("Edit page (unlinked) should allow user to modify and save content", () => { - cy.get(".CodeMirror-scroll").type(TEST_PAGE_CONTENT) - cy.contains(":button", "Save").click() - - // Asserts - // 1. Toast - cy.contains(SUCCESSFUL_EDIT_PAGE_TOAST) - - // 2. Content is there even after refreshing - cy.reload() - cy.contains(TEST_PAGE_CONTENT).should("exist") - }) - - it("Edit page (unlinked) should allow user to add existing image", () => { - // NOTE: Multiple GET requests are fired off and hence, unable to use default GET to match - cy.intercept("**/images").as("getImages") - cy.get(".image").click().wait("@getImages") - cy.contains(DEFAULT_IMAGE_TITLE).should("exist").click() - cy.contains(":button", "Select").click() - - cy.get("#altText").clear().type("Hello World") - cy.contains(":button", "Save").click() - - cy.contains(`/images/${DEFAULT_IMAGE_TITLE}`) - }) - - it("Edit page (unlinked) should allow user to upload and add new image", () => { - cy.get(".image").click().wait(Interceptors.GET) - cy.contains(":button", "Add new").click() - - cy.get("#file-upload").attachFile(ADDED_IMAGE_PATH) - cy.get("#name").clear().type(ADDED_IMAGE_TITLE) - cy.get("button") - .contains(/^Upload$/) - .click() - .wait(Interceptors.POST) - - cy.get("#altText").clear().type("Hello World") - cy.contains(":button", "Save").click() - - cy.contains(`/images/${ADDED_IMAGE_TITLE}`) - }) - - it("Edit page (unlinked) should allow user to upload and add new file", () => { - cy.get(".file").click().wait(Interceptors.GET) - cy.contains(":button", "Add new").click() - - cy.get("#file-upload").attachFile(ADDED_FILE_PATH) - cy.get("#name").clear().type(ADDED_FILE_TITLE) - cy.get("button") - .contains(/^Upload$/) - .click() - .wait(Interceptors.POST) - - cy.get("#altText").clear().type("Hello World") - cy.contains(":button", "Save").click() - - cy.contains(`/files/${ADDED_FILE_TITLE}`) - }) - - it("Edit page (unlinked) should allow user to add existing file", () => { - cy.get(".file").click().wait(Interceptors.GET) - cy.contains(ADDED_FILE_TITLE).click() - cy.contains(":button", "Select").click() - - cy.get("#altText").clear().type("Hello World") - cy.contains(":button", "Save").click() - cy.contains(`/files/${ADDED_FILE_TITLE}`) - }) - - it("Edit page (unlinked) should allow user to add link", () => { - cy.get(".link").click() - - cy.get('input[id="text"]').type(LINK_TITLE) - cy.get('input[id="link"]').type(LINK_URL) - cy.contains(":button", "Save").click() - - cy.contains(`[${LINK_TITLE}](${LINK_URL})`) - }) - - it("Edit page (unlinked) should allow users to add Instagram embed script", () => { - cy.get(".CodeMirror-scroll").type(TEST_INSTAGRAM_EMBED_SCRIPT) - cy.contains(":button", "Save").click() - - // Asserts - // 1. Toast - cy.contains(SUCCESSFUL_EDIT_PAGE_TOAST) - - // 2. Content is there even after refreshing - cy.reload() - cy.contains(TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT).should("exist") - }) - - it("Edit page (unlinked) should not allow users to add untrusted external scripts", () => { - cy.get(".CodeMirror-scroll").type(TEST_UNTRUSTED_SCRIPT) - - // Asserts - // 1. Save button is disabled - cy.contains(":button", "Save").should("be.disabled") - - // 2. CSP warning appears - cy.contains( - "Intended ' + const TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT = + '' + const TEST_UNTRUSTED_SCRIPT = + '' + const TEST_INLINE_SCRIPT = '' + + const TEST_UNLINKED_PAGE_TITLE = "Test Unlinked Page" + const TEST_UNLINKED_PAGE_FILENAME = titleToPageFileName( + TEST_UNLINKED_PAGE_TITLE + ) + const TEST_PAGE_TITLE_ENCODED = encodeURIComponent( + TEST_UNLINKED_PAGE_FILENAME + ) + + const DEFAULT_IMAGE_TITLE = "isomer-logo.svg" + const ADDED_IMAGE_TITLE = "balloon" + const ADDED_IMAGE_PATH = "images/balloon.png" + + const ADDED_FILE_TITLE = "singapore-pages" + const ADDED_FILE_PATH = "files/singapore.pdf" + + const LINK_TITLE = "link" + const LINK_URL = "https://www.google.com" + + const UNLINKED_PAGE = { + frontMatter: { + title: TEST_UNLINKED_PAGE_TITLE, + permalink: "/permalink", + variant: "tiptap", + description: "", + }, + } + + const BASE_SETTINGS = { + title: "Title", + description: "An Isomer site of the Singapore Government", + favicon: "/images/favicon-isomer.ico", + shareicon: "/images/isomer-logo.svg", + google_analytics_ga4: "", + is_government: false, + contact_us: "/contact-us/", + feedback: "", + faq: "/faq/", + show_reach: true, + logo: "/images/isomer-logo.svg", + url: BASE_SEO_LINK, + social_media: { + facebook: "https://www.facebook.com/YourFBPage", + linkedin: "https://www.linkedin.com/company/YourAgency", + twitter: "https://www.twitter.com/YourTwitter", + youtube: "https://www.youtube.com/YourYoutube", + instagram: "https://www.instagram.com/your.insta/", + telegram: "", + tiktok: "", + }, + colors: { + "primary-color": "#ff0000", + "secondary-color": "#ff0000", + "media-colors": [ + { title: "media-color-one", color: "#ff0000" }, + { title: "media-color-two", color: "#ff0000" }, + { title: "media-color-three", color: "#ff0000" }, + { title: "media-color-four", color: "#ff0000" }, + { title: "media-color-five", color: "#ff0000" }, + ], + }, + "facebook-pixel": "", + "linkedin-insights": "", + } + + test.beforeEach(async ({ page, context }) => { + setEmailSessionDefaults(context, "Email admin") + const api = await getApi(await context.storageState()) + await api.post("v2/sites/e2e-email-test-repo/pages/pages", { + data: { + content: UNLINKED_PAGE, + newFileName: TEST_UNLINKED_PAGE_FILENAME, + }, + }) + await page.goto( + `/sites/${E2E_EMAIL_TEST_SITE.repo}/editPage/${TEST_PAGE_TITLE_ENCODED}` + ) + }) + + test("Edit page (unlinked) should have correct colour", async ({ + page, + context, + }) => { + // Arrange + const api = await getApi(await context.storageState()) + const header = page.locator("#display-header") + + // Act + await api.post("v2/sites/e2e-email-test-repo/settings", { + data: BASE_SETTINGS, + }) + page.reload() + + // Assert + await expect(header).toHaveCSS("background-color", PRIMARY_COLOUR) + }) + + test("Edit page (unlinked) should have name of title", async ({ page }) => { + // Act + const title = page.getByRole("heading", { + name: TEST_UNLINKED_PAGE_TITLE, + }) + + // Assert + // NOTE: At least 1 heading + await expect(await title.count()).toBeGreaterThan(0) + }) + + test("Edit page (unlinked) should provide a warning to users when navigating away", async ({ + page, + }) => { + // Arrange + await page.getByRole("textbox").fill(TEST_PAGE_CONTENT) + const backButton = page.getByRole("button", { name: "Back to sites" }) + await backButton.click() + + // Act + const warningModal = page.getByText("Warning") + await expect(warningModal).toBeVisible() + await page.getByRole("button", { name: "No" }).click() + + // Sanity check: still in unlinked pages and content still present + await expect(page.url()).toContain( + `${E2E_EMAIL_TEST_SITE.repo}/editPage/${TEST_PAGE_TITLE_ENCODED}` + ) + await expect(page.getByText(TEST_PAGE_CONTENT)).toBeVisible() + + await backButton.click() + await expect(warningModal).toBeVisible() + await page.getByRole("button", { name: "Yes" }).click() + + // Assert + await expect(page.url()).toContain( + `${E2E_EMAIL_TEST_SITE.repo}/workspace` + ) + }) + + test("Edit page (unlinked) should allow user to modify and save content", async ({ + page, + }) => { + // Arrange + await page.getByRole("textbox").fill(TEST_PAGE_CONTENT) + const saveButton = page.getByRole("button", { name: "Save" }) + + // Act + await saveButton.click() + + // Assert + // 1. Toast + await expect(page.getByText(SUCCESSFUL_EDIT_PAGE_TOAST)).toBeVisible() + + // 2. Content is there even after refreshing + await page.reload() + await expect(page.getByText(TEST_PAGE_CONTENT)).toBeVisible() + }) + + test("Edit page (unlinked) should allow user to add existing image", async ({ + page, + }) => { + // Arrange + const addImageButton = page.getByRole("button", { + name: "Insert Image", + }) + await addImageButton.click() + const defaultImageButton = page.getByRole("button").filter({ + hasText: DEFAULT_IMAGE_TITLE, + }) + await defaultImageButton.click() + + // Act + await page.getByRole("button", { name: "Add image to page" }).click() + await page.getByPlaceholder("Alt text").fill("Hello World") + await page.locator("form").getByRole("button", { name: "Save" }).click() + + await expect( + page.getByText(`/images/${DEFAULT_IMAGE_TITLE}`) + ).toBeVisible() + }) + + test("Edit page (unlinked) should allow user to upload and add new image", async ({ + page, + }) => { + // Act + const addImageButton = page.getByRole("button", { + name: "Insert Image", + }) + await addImageButton.click() + const uploadNewImageButton = page.getByRole("button", { + name: "Upload new image", + }) + await uploadNewImageButton.click() + const fileChooserPromise = page.waitForEvent("filechooser") + await page + .getByRole("button", { name: "Choose files or drag and drop" }) + .click() + const fileChooser = await fileChooserPromise + await fileChooser.setFiles(ADDED_IMAGE_PATH) + const fileUploadPromise = page.waitForResponse((res) => + // NOTE: Not using a string/path here cos it gets merged + // as we have provided a `baseURL` (our frontend URL) for playwright. + res.url().includes("media/images/pages") + ) + await page.getByRole("button", { name: "Upload" }).click() + await fileUploadPromise + + // Assert + await expect( + page.getByText("Successfully uploaded 1 image") + ).toBeVisible() + await expect(page.getByText(`/images/${ADDED_IMAGE_TITLE}`)).toBeVisible() + }) + + test("Edit page (unlinked) should allow user to upload and add new file", async ({ + page, + }) => { + // Act + const addFileButton = page.getByRole("button", { + name: "Insert File", + }) + await addFileButton.click() + const uploadNewFileButton = page.getByRole("button", { + name: "Upload new file", + }) + await uploadNewFileButton.click() + const fileChooserPromise = page.waitForEvent("filechooser") + await page + .getByRole("button", { name: "Choose files or drag and drop" }) + .click() + const fileChooser = await fileChooserPromise + await fileChooser.setFiles(ADDED_FILE_PATH) + const fileUploadPromise = page.waitForResponse((res) => + // NOTE: Not using a string/path here cos it gets merged + // as we have provided a `baseURL` (our frontend URL) for playwright. + res.url().includes("media/files/pages") + ) + await page.getByRole("button", { name: "Upload" }).click() + await fileUploadPromise + + // Assert + await expect(page.getByText("Successfully uploaded 1 file")).toBeVisible() + await expect(page.getByText(`/images/${ADDED_FILE_TITLE}`)).toBeVisible() + }) + + test("Edit page (unlinked) should allow user to add existing file", async ({ + page, + }) => { + // NOTE: This test has an implicit dependency on the previous test + // We might wish to squash the two tests together in the future. + // Arrange + const addFileButton = page.getByRole("button", { + name: "Insert File", + }) + await addFileButton.click() + const defaultFileButton = page.getByRole("button").filter({ + hasText: ADDED_FILE_TITLE, + }) + await defaultFileButton.click() + + // Act + await page.getByRole("button", { name: "Add file to page" }).click() + await page.getByPlaceholder("Alt text").fill("Hello World") + await page.locator("form").getByRole("button", { name: "Save" }).click() + + await expect(page.getByText(`/files/${ADDED_FILE_TITLE}`)).toBeVisible() + }) + + test("Edit page (unlinked) should allow user to add link", async ({ + page, + }) => { + // Act + await page.getByRole("button", { name: "Insert Link" }).click() + + await page.getByPlaceholder("Text").fill(LINK_TITLE) + await page.getByPlaceholder("Link").fill(LINK_URL) + + await page.getByRole("button", { name: "Save" }).click() + + // Assert + page.getByText(`[${LINK_TITLE}](${LINK_URL})`) + }) + + test("Edit page (unlinked) should allow users to add Instagram embed script", async ({ + page, + }) => { + // Arrange + await page.getByRole("textbox").fill(TEST_INSTAGRAM_EMBED_SCRIPT) + + // Act + await page.getByRole("button", { name: "Save" }).click() + + // Assert + // 1. Toast + await expect(page.getByText(SUCCESSFUL_EDIT_PAGE_TOAST)).toBeVisible() + + // 2. Content is there even after refreshing + await page.reload() + await expect( + page.getByText(TEST_SANITIZED_INSTAGRAM_EMBED_SCRIPT) + ).toBeVisible() + }) + + // TODO: Add functionality to prevent users from adding + // untrusted external scripts + test.skip("Edit page (unlinked) should not allow users to add untrusted external scripts", async ({ + page, + }) => { + // NOTE: This test will fail at present + // Arrange + const PAGE_VIOLATION_WARNING = + "Intended