diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 618d10f7db2b93..f8698b2d55b9d1 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -46,8 +46,8 @@ jobs: - name: Running the tests run: | - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == ${{ matrix.part }} - 1' < ~/.jest-e2e-tests ) + $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.playwright.config.js --listTests > ~/.jest-e2e-tests + $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.playwright.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == ${{ matrix.part }} - 1' < ~/.jest-e2e-tests ) - name: Archive debug artifacts (screenshots, HTML snapshots) uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 diff --git a/package-lock.json b/package-lock.json index 297e1c5bcb8923..4d30627cda8959 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16881,6 +16881,15 @@ "@types/unist": "*" } }, + "@types/wait-on": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@types/wait-on/-/wait-on-5.3.1.tgz", + "integrity": "sha512-2FFOKCF/YydrMUaqg+fkk49qf0e5rDgwt6aQsMzFQzbS419h2gNOXyiwp/o2yYy27bi/C1z+HgfncryjGzlvgQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/webpack": { "version": "4.41.16", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.16.tgz", @@ -19962,6 +19971,15 @@ "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.4.tgz", "integrity": "sha512-WO5StDORR6JF/xYnXk/Fm0yu+iULaV5ULKuUw0Tu+jbgiTlSquaWBCgbpnsHLMXldf+fM3Gxn5p7vjond7He6w==" }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, "appium": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/appium/-/appium-1.20.2.tgz", @@ -27338,6 +27356,12 @@ } } }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", @@ -29864,6 +29888,32 @@ } } }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, "call-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", @@ -32711,6 +32761,23 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -34054,6 +34121,12 @@ "integrity": "sha512-FYpuxEjMeDvU4rulKqFdukQyZSTpzhg4ScQHrAosrlVpR6GFyaw14f74yn2+4BugniIS0Frpg7TvwZocU4ZMTw==", "dev": true }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -35835,6 +35908,12 @@ } } }, + "expect-playwright": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/expect-playwright/-/expect-playwright-0.7.2.tgz", + "integrity": "sha512-5o9si+8SUi68QVI0CRVv8tvTjZinpJWRSfQ3GP6v0DvlK55lDgFvD79r6A/NU+EUawrBc62qP30MxzOUnXNJZQ==", + "dev": true + }, "expect-puppeteer": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/expect-puppeteer/-/expect-puppeteer-4.4.0.tgz", @@ -36958,6 +37037,12 @@ "readable-stream": "^2.0.0" } }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -37346,6 +37431,12 @@ "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-pkg-repo": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", @@ -38277,6 +38368,30 @@ "minimalistic-assert": "^1.0.1" } }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "hast-to-hyperscript": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", @@ -39947,6 +40062,15 @@ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, "istanbul-lib-instrument": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", @@ -39970,6 +40094,70 @@ } } }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "istanbul-lib-report": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", @@ -42482,12 +42670,289 @@ } } }, + "jest-playwright-preset": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/jest-playwright-preset/-/jest-playwright-preset-1.7.0.tgz", + "integrity": "sha512-G25Nik+By0SNniMDdkouDL/yA1LdqjzsXNSVU4xnRX1typjXRmzRE0aSgqxas2sRi8cwG3M1ioHdkLLsp6sang==", + "dev": true, + "requires": { + "expect-playwright": "^0.7.0", + "jest-process-manager": "^0.3.1", + "nyc": "^15.1.0", + "playwright-core": ">=1.2.0", + "rimraf": "^3.0.2", + "uuid": "^8.3.2" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "playwright-core": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.14.1.tgz", + "integrity": "sha512-G4fO98T/DOTj3uabIlhkWrWzhXGXx8y8rVzBgR6DKPqEz8NfTxuQ3NFGWA1u+sfD1CtAXRxBisaUvlmnD2jRYw==", + "dev": true, + "requires": { + "commander": "^6.1.0", + "debug": "^4.1.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", + "jpeg-js": "^0.4.2", + "mime": "^2.4.6", + "pngjs": "^5.0.0", + "progress": "^2.0.3", + "proper-lockfile": "^4.1.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "stack-utils": "^2.0.3", + "ws": "^7.4.6", + "yazl": "^2.5.1" + } + }, + "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 + }, + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } + }, "jest-pnp-resolver": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, + "jest-process-manager": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/jest-process-manager/-/jest-process-manager-0.3.1.tgz", + "integrity": "sha512-x9W54UgZ7IkzUHgXtnI1x4GKOVjxtwW0CA/7yGbTHtT/YhENO0Lic2yfVyC/gekn7OIEMcQmy0L1r9WLQABfqw==", + "dev": true, + "requires": { + "@types/wait-on": "^5.2.0", + "chalk": "^4.1.0", + "cwd": "^0.10.0", + "exit": "^0.1.2", + "find-process": "^1.4.4", + "prompts": "^2.4.1", + "signal-exit": "^3.0.3", + "spawnd": "^5.0.0", + "tree-kill": "^1.2.2", + "wait-on": "^5.3.0" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "find-process": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.4.tgz", + "integrity": "sha512-rRSuT1LE4b+BFK588D2V8/VG9liW0Ark1XJgroxZXI0LtwmQJOb490DvDYvbm+Hek9ETFzTutGfJ90gumITPhQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "commander": "^5.1.0", + "debug": "^4.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "spawnd": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-5.0.0.tgz", + "integrity": "sha512-28+AJr82moMVWolQvlAIv3JcYDkjkFTEmfDc503wxrF5l2rQ3dFz6DpbXp3kD4zmgGGldfM4xM4v1sFj/ZaIOA==", + "dev": true, + "requires": { + "exit": "^0.1.2", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "wait-port": "^0.2.9" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "wait-on": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz", + "integrity": "sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==", + "dev": true, + "requires": { + "axios": "^0.21.1", + "joi": "^17.3.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^6.6.3" + } + }, + "wait-port": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-0.2.9.tgz", + "integrity": "sha512-hQ/cVKsNqGZ/UbZB/oakOGFqic00YAMM5/PEj3Bt4vKarv2jWIWzDbqlwT94qMs/exAQAsvMOq99sZblV92zxQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "commander": "^3.0.2", + "debug": "^4.1.1" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "dev": true + } + } + } + } + }, "jest-regex-util": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", @@ -44490,6 +44955,12 @@ } } }, + "jpeg-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz", + "integrity": "sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==", + "dev": true + }, "js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -48443,6 +48914,15 @@ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, "node-releases": { "version": "1.1.71", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", @@ -49226,6 +49706,231 @@ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -49956,6 +50661,26 @@ "p-reduce": "^1.0.0" } }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } + } + }, "pako": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", @@ -50381,6 +51106,220 @@ "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==", "dev": true }, + "playwright": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.14.1.tgz", + "integrity": "sha512-JYNjhwWcfsBkg0FMGLbFO9e58FVdmICE4k97/glIQV7cBULL7oxNjRQC7Ffe+Y70XVNnP0HSJLaA0W5SukyftQ==", + "dev": true, + "requires": { + "commander": "^6.1.0", + "debug": "^4.1.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", + "jpeg-js": "^0.4.2", + "mime": "^2.4.6", + "pngjs": "^5.0.0", + "progress": "^2.0.3", + "proper-lockfile": "^4.1.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "stack-utils": "^2.0.3", + "ws": "^7.4.6", + "yazl": "^2.5.1" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } + }, + "playwright-chromium": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.14.1.tgz", + "integrity": "sha512-gAHDcrBKrl1Az6TuzC4T013Nl+qKVZeblc2VkElHqEuNQHPKW7840cQBZavFz38xJshC993iClVc6Y+bLgF8FA==", + "dev": true, + "requires": { + "commander": "^6.1.0", + "debug": "^4.1.1", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", + "jpeg-js": "^0.4.2", + "mime": "^2.4.6", + "pngjs": "^5.0.0", + "progress": "^2.0.3", + "proper-lockfile": "^4.1.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "stack-utils": "^2.0.3", + "ws": "^7.4.6", + "yazl": "^2.5.1" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } + }, "please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -50416,6 +51355,12 @@ "irregular-plurals": "^3.2.0" } }, + "pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "dev": true + }, "pnp-webpack-plugin": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", @@ -51761,6 +52706,15 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -52165,6 +53119,31 @@ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, + "proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + } + } + }, "property-information": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", @@ -54621,6 +55600,15 @@ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", "dev": true }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, "remark": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz", @@ -56733,6 +57721,31 @@ "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", "dev": true }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "spawnd": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-5.0.0.tgz", @@ -62228,6 +63241,15 @@ "fd-slicer": "~1.0.1" } }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index d170ee49c809b6..0a19c81d05d43a 100644 --- a/package.json +++ b/package.json @@ -175,6 +175,7 @@ "jest": "26.6.3", "jest-junit": "11.0.0", "jest-message-util": "27.0.6", + "jest-playwright-preset": "1.7.0", "jest-serializer-enzyme": "1.0.0", "jest-watch-typeahead": "0.6.1", "jsdom": "16.4.0", @@ -188,6 +189,8 @@ "nock": "12.0.3", "node-watch": "0.7.0", "patch-package": "6.2.2", + "playwright": "1.14.1", + "playwright-chromium": "1.14.1", "postcss": "8.2.15", "postcss-loader": "6.1.1", "prettier": "npm:wp-prettier@2.2.1-beta-1", @@ -260,7 +263,7 @@ "publish:latest": "lerna publish", "test": "npm run lint && npm run test-unit", "test:create-block": "./bin/test-create-block.sh", - "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", + "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.playwright.config.js", "test-e2e:debug": "wp-scripts --inspect-brk test-e2e --config packages/e2e-tests/jest.config.js --puppeteer-devtools", "test-e2e:watch": "npm run test-e2e -- --watch", "test-performance": "wp-scripts test-e2e --config packages/e2e-tests/jest.performance.config.js", diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index b8c8664651b1c3..d1faccc5f6ef39 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -23,11 +23,9 @@ "node": ">=12" }, "files": [ - "build", - "build-module" + "src" ], - "main": "build/index.js", - "module": "build-module/index.js", + "main": "src/index.js", "dependencies": { "@babel/runtime": "^7.13.10", "@wordpress/api-fetch": "file:../api-fetch", diff --git a/packages/e2e-test-utils/src/click-block-toolbar-button.js b/packages/e2e-test-utils/src/click-block-toolbar-button.js index e73888f0681385..e25cec2f69ac0c 100644 --- a/packages/e2e-test-utils/src/click-block-toolbar-button.js +++ b/packages/e2e-test-utils/src/click-block-toolbar-button.js @@ -11,21 +11,17 @@ import { showBlockToolbar } from './show-block-toolbar'; */ export async function clickBlockToolbarButton( label, type = 'ariaLabel' ) { await showBlockToolbar(); - const BLOCK_TOOLBAR_SELECTOR = 'block-editor-block-toolbar'; - let button; + const BLOCK_TOOLBAR_SELECTOR = '.block-editor-block-toolbar'; if ( type === 'ariaLabel' ) { - button = await page.waitForSelector( - `.${ BLOCK_TOOLBAR_SELECTOR } button[aria-label="${ label }"]` + return await page.click( + `${ BLOCK_TOOLBAR_SELECTOR } button[aria-label="${ label }"]` ); } if ( type === 'content' ) { - button = await page.waitForXPath( - `//*[contains(concat(' ', normalize-space(@class), ' '), ' ${ BLOCK_TOOLBAR_SELECTOR } ')]//button[contains(text(), '${ label }')]` + return await page.click( + `${ BLOCK_TOOLBAR_SELECTOR } button:text("${ label }")` ); } - - await button.evaluate( ( element ) => element.scrollIntoView() ); - await button.click(); } diff --git a/packages/e2e-test-utils/src/click-menu-item.js b/packages/e2e-test-utils/src/click-menu-item.js index e4a29eacf39e60..ee506b07424549 100644 --- a/packages/e2e-test-utils/src/click-menu-item.js +++ b/packages/e2e-test-utils/src/click-menu-item.js @@ -1,18 +1,8 @@ -/** - * External dependencies - */ -import { first } from 'lodash'; - /** * Searches for an item in the menu with the text provided and clicks it. * * @param {string} label The label to search the menu item for. */ export async function clickMenuItem( label ) { - const elementToClick = first( - await page.$x( - `//div[@role="menu"]//span[contains(concat(" ", @class, " "), " components-menu-item__item ")][contains(text(), "${ label }")]` - ) - ); - await elementToClick.click(); + await page.click( `button :text("${ label }")` ); } diff --git a/packages/e2e-test-utils/src/drag-and-resize.js b/packages/e2e-test-utils/src/drag-and-resize.js index aa8a23cd8d4344..f895e6581268b5 100644 --- a/packages/e2e-test-utils/src/drag-and-resize.js +++ b/packages/e2e-test-utils/src/drag-and-resize.js @@ -9,7 +9,7 @@ * @return {Promise} Promise resolving when drag completes. */ export async function dragAndResize( element, delta ) { - const elementPoint = await element.clickablePoint(); + const elementPoint = await element.boundingBox(); await page.mouse.move( elementPoint.x, elementPoint.y ); await page.mouse.down(); await page.mouse.move( elementPoint.x + delta.x, elementPoint.y + delta.y ); diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index 1bcce329f07dff..772b315121e031 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -62,17 +62,17 @@ export async function toggleGlobalBlockInserter() { * Moves focus to the selected block. */ async function focusSelectedBlock() { - // Ideally there shouuld be a UI way to do this. (Focus the selected block) - await page.evaluate( () => { - wp.data - .dispatch( 'core/block-editor' ) - .selectBlock( - wp.data - .select( 'core/block-editor' ) - .getSelectedBlockClientId(), - 0 - ); - } ); + const blockId = await page.evaluate( () => + wp.data.select( 'core/block-editor' ).getSelectedBlockClientId() + ); + const blockElement = await page.waitForSelector( + `[data-block="${ blockId }"]` + ); + // Ideally there should be a UI way to do this. (Focus the selected block) + await page.evaluate( ( id ) => { + wp.data.dispatch( 'core/block-editor' ).selectBlock( id, 0 ); + }, blockId ); + await blockElement.waitForElementState( 'stable' ); } /** @@ -94,10 +94,8 @@ async function waitForInserterCloseAndContentFocus() { */ export async function searchForBlock( searchTerm ) { await openGlobalBlockInserter(); - await page.waitForSelector( INSERTER_SEARCH_SELECTOR ); - await page.focus( INSERTER_SEARCH_SELECTOR ); - await pressKeyWithModifier( 'primary', 'a' ); - await page.keyboard.type( searchTerm ); + await page.fill( INSERTER_SEARCH_SELECTOR, '' ); + await page.type( INSERTER_SEARCH_SELECTOR, searchTerm ); } /** @@ -152,13 +150,9 @@ export async function searchForReusableBlock( searchTerm ) { */ export async function insertBlock( searchTerm ) { await searchForBlock( searchTerm ); - const insertButton = await page.waitForXPath( - `//button//span[contains(text(), '${ searchTerm }')]` - ); - await insertButton.click(); + await page.click( `button :text("${ searchTerm }")` ); + await toggleGlobalBlockInserter(); await focusSelectedBlock(); - // We should wait until the inserter closes and the focus moves to the content. - await waitForInserterCloseAndContentFocus(); } /** diff --git a/packages/e2e-test-utils/src/mocks/set-up-response-mocking.js b/packages/e2e-test-utils/src/mocks/set-up-response-mocking.js index b2ba2d31db0ac7..61687429215588 100644 --- a/packages/e2e-test-utils/src/mocks/set-up-response-mocking.js +++ b/packages/e2e-test-utils/src/mocks/set-up-response-mocking.js @@ -39,8 +39,8 @@ export async function setUpResponseMocking( mocks ) { // We only want to set up the request interception once, or else we get a crash // when we try to process the same request twice. interceptionInitialized = true; - await page.setRequestInterception( true ); - page.on( 'request', async ( request ) => { + await page.route( '**/*', async ( route ) => { + const request = route.request(); for ( let i = 0; i < requestMocks.length; i++ ) { const mock = requestMocks[ i ]; if ( mock.match( request ) ) { @@ -48,7 +48,7 @@ export async function setUpResponseMocking( mocks ) { return; } } - request.continue(); + route.continue(); } ); } // Overwrite with the passed in mocks, so we can change the mocks mid-test to test diff --git a/packages/e2e-test-utils/src/press-key-with-modifier.js b/packages/e2e-test-utils/src/press-key-with-modifier.js index 24eb4c3c86e487..1fbe73d1434289 100644 --- a/packages/e2e-test-utils/src/press-key-with-modifier.js +++ b/packages/e2e-test-utils/src/press-key-with-modifier.js @@ -91,13 +91,12 @@ async function emulateSelectAll() { */ export async function setClipboardData( { plainText = '', html = '' } ) { await page.evaluate( - ( _plainText, _html ) => { + ( args ) => { window._clipboardData = new DataTransfer(); - window._clipboardData.setData( 'text/plain', _plainText ); - window._clipboardData.setData( 'text/html', _html ); + window._clipboardData.setData( 'text/plain', args.plainText ); + window._clipboardData.setData( 'text/html', args.html ); }, - plainText, - html + { plainText, html } ); } diff --git a/packages/e2e-test-utils/src/save-draft.js b/packages/e2e-test-utils/src/save-draft.js index 8660910837026e..11f610b380dd91 100644 --- a/packages/e2e-test-utils/src/save-draft.js +++ b/packages/e2e-test-utils/src/save-draft.js @@ -5,7 +5,6 @@ * @return {Promise} Promise resolving when draft save is complete. */ export async function saveDraft() { - await page.waitForSelector( '.editor-post-save-draft' ); await page.click( '.editor-post-save-draft' ); - return page.waitForSelector( '.editor-post-saved-state.is-saved' ); + return await page.waitForSelector( '.editor-post-saved-state.is-saved' ); } diff --git a/packages/e2e-test-utils/src/set-browser-viewport.js b/packages/e2e-test-utils/src/set-browser-viewport.js index 3408dfe03903eb..8fc99200f2ad7a 100644 --- a/packages/e2e-test-utils/src/set-browser-viewport.js +++ b/packages/e2e-test-utils/src/set-browser-viewport.js @@ -48,6 +48,6 @@ export async function setBrowserViewport( viewport ) { ? PREDEFINED_DIMENSIONS[ viewport ] : viewport; - await page.setViewport( dimensions ); + await page.setViewportSize( dimensions ); await waitForWindowDimensions( dimensions.width, dimensions.height ); } diff --git a/packages/e2e-test-utils/src/transform-block-to.js b/packages/e2e-test-utils/src/transform-block-to.js index 4a6968c684904e..629a2326a596e3 100644 --- a/packages/e2e-test-utils/src/transform-block-to.js +++ b/packages/e2e-test-utils/src/transform-block-to.js @@ -11,25 +11,10 @@ import { showBlockToolbar } from './show-block-toolbar'; export async function transformBlockTo( name ) { await showBlockToolbar(); - const switcherToggle = await page.waitForSelector( - '.block-editor-block-switcher__toggle' + await page.click( '.block-editor-block-switcher__toggle' ); + await page.click( + `.block-editor-block-switcher__popover :text-is("${ name }")` ); - await switcherToggle.evaluate( ( element ) => element.scrollIntoView() ); - await page.waitForSelector( '.block-editor-block-switcher__toggle', { - visible: true, - } ); - await switcherToggle.click(); - await page.waitForSelector( '.block-editor-block-switcher__container', { - visible: true, - } ); - - // Find the block button option within the switcher popover. - const xpath = `//*[contains(@class, "block-editor-block-switcher__popover")]//button[.='${ name }']`; - const insertButton = await page.waitForXPath( xpath, { visible: true } ); - // Clicks may fail if the button is out of view. Assure it is before click. - await insertButton.evaluate( ( element ) => element.scrollIntoView() ); - await insertButton.click(); - // Wait for the transformed block to appear. const BLOCK_SELECTOR = '.block-editor-block-list__block'; const BLOCK_NAME_SELECTOR = `[data-title="${ name }"]`; diff --git a/packages/e2e-test-utils/src/wp-data-select.js b/packages/e2e-test-utils/src/wp-data-select.js index 65e382730292c2..7512782e9becdc 100644 --- a/packages/e2e-test-utils/src/wp-data-select.js +++ b/packages/e2e-test-utils/src/wp-data-select.js @@ -20,13 +20,11 @@ */ export async function wpDataSelect( store, selector, ...parameters ) { return page.evaluate( - ( _store, _selector, ..._parameters ) => { + ( args ) => { return window.wp.data - .select( _store ) - [ _selector ]( ..._parameters ); + .select( args.store ) + [ args.selector ]( ...args.parameters ); }, - store, - selector, - ...parameters + { store, selector, parameters } ); } diff --git a/packages/e2e-tests/config/setup-playwright.js b/packages/e2e-tests/config/setup-playwright.js new file mode 100644 index 00000000000000..9659febf6d8695 --- /dev/null +++ b/packages/e2e-tests/config/setup-playwright.js @@ -0,0 +1,74 @@ +/** + * WordPress dependencies + */ +import { + activateTheme, + deactivatePlugin, + enablePageDialogAccept, + setBrowserViewport, + switchUserToAdmin, + switchUserToTest, + visitAdminPage, +} from '@wordpress/e2e-test-utils'; +import { addQueryArgs } from '@wordpress/url'; + +// The Jest timeout is increased because these tests are a bit slow +jest.setTimeout( 100000 ); + +async function setupBrowser() { + await setBrowserViewport( 'large' ); +} + +/** + * Navigates to the post listing screen and bulk-trashes any posts which exist. + * + * @param {string} postType - String slug for type of post to trash. + * + * @return {Promise} Promise resolving once posts have been trashed. + */ +export async function trashExistingPosts( postType = 'post' ) { + await switchUserToAdmin(); + // Visit `/wp-admin/edit.php` so we can see a list of posts and delete them. + const query = addQueryArgs( '', { + post_type: postType, + } ).slice( 1 ); + await visitAdminPage( 'edit.php', query ); + + // If this selector doesn't exist there are no posts for us to delete. + const bulkSelector = await page.$( '#bulk-action-selector-top' ); + if ( ! bulkSelector ) { + return; + } + + // Select all posts. + await page.click( '[id^=cb-select-all-]' ); + // Select the "bulk actions" > "trash" option. + await page.select( '#bulk-action-selector-top', 'trash' ); + // Submit the form to send all draft/scheduled/published posts to the trash. + await page.click( '#doaction' ); + await page.waitForSelector( 'text=/moved to the trash/i' ); + await switchUserToTest(); +} + +beforeAll( async () => { + // Address Puppetteer vs Playwright API differences before running the tests + page.$x = page.$$; + page.select = page.selectOption; + page.waitForXPath = page.waitForSelector; + page.waitFor = page.waitForTimeout; + page.cookies = () => page.context().cookies(); + + // Initialize the environment + enablePageDialogAccept(); + await activateTheme( 'twentytwentyone' ); + await trashExistingPosts(); + await setupBrowser(); + + // Deactivate the plugin that disables the CSS animations in case it's enabled + // (it is enabled for Puppeteer tests). We don't need to disable any + // animations anymore thanks to Playwright's automatic element stability + // check. + await deactivatePlugin( + 'gutenberg-test-plugin-disables-the-css-animations' + ); +} ); diff --git a/packages/e2e-tests/jest.playwright.config.js b/packages/e2e-tests/jest.playwright.config.js new file mode 100644 index 00000000000000..519933af748493 --- /dev/null +++ b/packages/e2e-tests/jest.playwright.config.js @@ -0,0 +1,27 @@ +module.exports = { + preset: 'jest-playwright-preset', + testMatch: [ '**/specs/editor/blocks/**/*.js' ], + testPathIgnorePatterns: [ '/node_modules/', '/wordpress/' ], + testRunner: 'jest-circus/runner', + reporters: + 'TRAVIS' in process.env && 'CI' in process.env + ? [ + '@wordpress/jest-preset-default/scripts/travis-fold-passes-reporter.js', + ] + : undefined, + setupFiles: [ '/config/gutenberg-phase.js' ], + setupFilesAfterEnv: [ + '/config/setup-playwright.js', + '@wordpress/jest-console', + ], + testEnvironmentOptions: { + 'jest-playwright': { + launchOptions: { + devtools: process.env.PLAYWRIGHT_DEVTOOLS === 'true', + headless: process.env.PLAYWRIGHT_HEADLESS !== 'false', + slowMo: parseInt( process.env.PLAYWRIGHT_SLOWMO, 10 ) || 0, + args: [ '--enable-blink-features=ComputedAccessibilityInfo' ], + }, + }, + }, +}; diff --git a/packages/e2e-tests/specs/editor/blocks/classic.test.js b/packages/e2e-tests/specs/editor/blocks/classic.test.js index d48cd0b826784d..290d81348aab66 100644 --- a/packages/e2e-tests/specs/editor/blocks/classic.test.js +++ b/packages/e2e-tests/specs/editor/blocks/classic.test.js @@ -25,8 +25,7 @@ describe( 'Classic', () => { it( 'should be inserted', async () => { await insertBlock( 'Classic' ); - // Wait for TinyMCE to initialise. - await page.waitForSelector( '.mce-content-body' ); + // Ensure there is focus. await page.focus( '.mce-content-body' ); await page.keyboard.type( 'test' ); @@ -38,21 +37,20 @@ describe( 'Classic', () => { it( 'should insert media, convert to blocks, and undo in one step', async () => { await insertBlock( 'Classic' ); - // Wait for TinyMCE to initialise. - await page.waitForSelector( '.mce-content-body' ); + // Ensure there is focus. - await page.focus( '.mce-content-body' ); + await page.click( '.mce-content-body' ); await page.keyboard.type( 'test' ); // Click the image button. - await page.waitForSelector( 'div[aria-label^="Add Media"]' ); await page.click( 'div[aria-label^="Add Media"]' ); - await page.click( '.media-menu-item#menu-item-gallery' ); // Wait for media modal to appear and upload image. - await page.waitForSelector( '.media-modal input[type=file]' ); - const inputElement = await page.$( '.media-modal input[type=file]' ); + const inputElement = await page.waitForSelector( + '.media-modal input[type=file]', + { state: 'attached' } + ); const testImagePath = path.join( __dirname, '..', @@ -64,11 +62,12 @@ describe( 'Classic', () => { const filename = uuid(); const tmpFileName = path.join( os.tmpdir(), filename + '.png' ); fs.copyFileSync( testImagePath, tmpFileName ); - await inputElement.uploadFile( tmpFileName ); + await inputElement.setInputFiles( tmpFileName ); // Wait for upload. await page.waitForSelector( - `.media-modal li[aria-label="${ filename }"]` + `.media-modal li[aria-label="${ filename }"]`, + { state: 'visible' } ); // Insert the uploaded image. @@ -101,7 +100,12 @@ describe( 'Classic', () => { expect( await getEditedPostContent() ).toMatch( /\\s*
\\s*` @@ -359,6 +363,7 @@ describe( 'Image', () => { await pressKeyWithModifier( 'primary', 'z' ); // Expect an empty image block (placeholder) rather than one with a // broken temporary URL. + await page.waitForSelector( 'text=/Upload an image file/' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); } ); diff --git a/packages/e2e-tests/specs/editor/blocks/list.test.js b/packages/e2e-tests/specs/editor/blocks/list.test.js index b75f79bb7af532..cac8781fb3eb32 100644 --- a/packages/e2e-tests/specs/editor/blocks/list.test.js +++ b/packages/e2e-tests/specs/editor/blocks/list.test.js @@ -36,6 +36,7 @@ describe( 'List', () => { await page.keyboard.type( 'test' ); await pressKeyTimes( 'ArrowLeft', 4 ); await page.keyboard.type( '* ' ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); @@ -51,52 +52,67 @@ describe( 'List', () => { it( 'can undo asterisk transform', async () => { await clickBlockAppender(); await page.keyboard.type( '1. ' ); + await page.waitForSelector( 'ol[aria-label="Block: List"]' ); await pressKeyWithModifier( 'primary', 'z' ); + await page.waitForSelector( 'p:text-is("1. ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should undo asterisk transform with backspace', async () => { await clickBlockAppender(); - await page.keyboard.type( '* ' ); + // A delay is needed here because the UI doesn't keep up and the whole + // content gets removed instead being reverted to "* ". + await page.keyboard.type( '* ', { delay: 100 } ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); + // Adding delay to the Backspace will not have any effect because the + // undo action happens on keydown, not keyup (delay is between those events). await page.keyboard.press( 'Backspace' ); + await page.waitForSelector( 'p:text-is("* ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should undo asterisk transform with backspace after selection changes', async () => { await clickBlockAppender(); - await page.keyboard.type( '* ' ); + await page.keyboard.type( '* ', { delay: 100 } ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); await page.evaluate( () => new Promise( window.requestIdleCallback ) ); await page.keyboard.press( 'Backspace' ); + await page.waitForSelector( 'p:text-is("* ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should undo asterisk transform with backspace setting isTyping state', async () => { await clickBlockAppender(); - await page.keyboard.type( '* ' ); + await page.keyboard.type( '* ', { delay: 100 } ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); await showBlockToolbar(); await page.keyboard.press( 'Backspace' ); + await page.waitForSelector( 'p:text-is("* ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should undo asterisk transform with backspace after selection changes without requestIdleCallback', async () => { await clickBlockAppender(); await page.evaluate( () => delete window.requestIdleCallback ); - await page.keyboard.type( '* ' ); - await new Promise( ( resolve ) => setTimeout( resolve, 100 ) ); + await page.keyboard.type( '* ', { delay: 100 } ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); await page.keyboard.press( 'Backspace' ); + await page.waitForSelector( 'p:text-is("* ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should undo asterisk transform with escape', async () => { await clickBlockAppender(); - await page.keyboard.type( '* ' ); + await page.keyboard.type( '* ', { delay: 100 } ); + await page.waitForSelector( 'ul[aria-label="Block: List"]' ); await page.keyboard.press( 'Escape' ); + await page.waitForSelector( 'p:text-is("* ")' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); @@ -126,8 +142,8 @@ describe( 'List', () => { // Create a list with the slash block shortcut. await clickBlockAppender(); await page.keyboard.type( '/list' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'List')]` + await page.waitForSelector( + 'button[aria-selected="true"]:text("List")' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'Iā€™m a list' ); diff --git a/packages/e2e-tests/specs/editor/blocks/missing.test.js b/packages/e2e-tests/specs/editor/blocks/missing.test.js index 4c74cdab9c336f..1f92ddf9ced0d2 100644 --- a/packages/e2e-tests/specs/editor/blocks/missing.test.js +++ b/packages/e2e-tests/specs/editor/blocks/missing.test.js @@ -25,7 +25,7 @@ describe( 'missing block', () => { expect( hasAlert ).toBe( false ); } ); - it( 'hould strip potentially malicious script tags', async () => { + it( 'should strip potentially malicious script tags', async () => { let hasAlert = false; page.on( 'dialog', () => { diff --git a/packages/e2e-tests/specs/editor/blocks/post-title.test.js b/packages/e2e-tests/specs/editor/blocks/post-title.test.js index 621e6815cfba84..8004054d6417bf 100644 --- a/packages/e2e-tests/specs/editor/blocks/post-title.test.js +++ b/packages/e2e-tests/specs/editor/blocks/post-title.test.js @@ -13,23 +13,18 @@ describe( 'Post Title block', () => { } ); it( 'Can edit the post title', async () => { - // Create a block with some text that will trigger a list creation. await insertBlock( 'Post Title' ); - const editablePostTitleSelector = - '.wp-block-post-title[contenteditable="true"]'; - await page.waitForSelector( editablePostTitleSelector ); - await page.focus( editablePostTitleSelector ); - // Create a second list item. - await page.keyboard.type( 'Just tweaking the post title' ); + const titleText = 'Just tweaking the post title'; + + await page.click( '[aria-label="Block: Post Title"]' ); + await page.keyboard.type( titleText ); await saveDraft(); await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); - const title = await page.$eval( - '.editor-post-title__input', - ( element ) => element.textContent + + await page.waitForSelector( + `.editor-post-title >> text="${ titleText }"` ); - expect( title ).toEqual( 'Just tweaking the post title' ); } ); } ); diff --git a/packages/e2e-tests/specs/editor/blocks/query.test.js b/packages/e2e-tests/specs/editor/blocks/query.test.js index 769abbf76bee37..3a7f4c1375a4ca 100644 --- a/packages/e2e-tests/specs/editor/blocks/query.test.js +++ b/packages/e2e-tests/specs/editor/blocks/query.test.js @@ -11,7 +11,7 @@ import { } from '@wordpress/e2e-test-utils'; const createDemoPosts = async () => { - await createNewPost( { postType: 'post', title: `Post 1` } ); + await createNewPost( { postType: 'post', title: 'Post 1' } ); await publishPost(); }; @@ -44,21 +44,19 @@ describe( 'Query block', () => { await page.waitForSelector( 'li.pattern-slide.active-slide[aria-label="Query Test 1"]' ); - const nextPatternButton = await page.waitForSelector( + await page.click( '.block-editor-block-pattern-setup__navigation button[aria-label="Next pattern"]' ); - await nextPatternButton.click(); await page.waitForSelector( 'li.pattern-slide.active-slide[aria-label="Query Test 2"]' ); // Choose the selected pattern. - const chooseButton = await page.waitForXPath( - '//div[contains(@class, "block-editor-block-pattern-setup__actions")]//button[text()="Choose"]' + await page.click( + '.block-editor-block-pattern-setup__actions button:text("Choose")' ); - chooseButton.click(); // Wait for pattern setup to go away. await page.waitForSelector( '.block-editor-block-pattern-setup', { - hidden: true, + state: 'hidden', } ); /** * We can't use `getEditedPostContent` easily for now because @@ -75,18 +73,16 @@ describe( 'Query block', () => { '.block-editor-block-pattern-setup__display-controls' ); // Click the Grid view button. - const gridViewButton = await page.waitForSelector( + await page.click( '.block-editor-block-pattern-setup__display-controls button[aria-label="Grid view"]' ); - await gridViewButton.click(); // Wait for patterns to be loaded and click the wanted pattern. - const gridPattern = await page.waitForXPath( - '//div[@class="block-editor-block-pattern-setup-list__item-title" and contains(text(), "Query Test 2")]' + await page.click( + '.block-editor-block-pattern-setup-list__item-title:text("Query Test 2")' ); - await gridPattern.click(); // Wait for pattern setup to go away. await page.waitForSelector( '.block-editor-block-pattern-setup', { - hidden: true, + state: 'hidden', } ); /** * We can't use `getEditedPostContent` easily for now because diff --git a/packages/e2e-tests/specs/editor/blocks/quote.test.js b/packages/e2e-tests/specs/editor/blocks/quote.test.js index da4efefb38de13..e02952ae11d538 100644 --- a/packages/e2e-tests/specs/editor/blocks/quote.test.js +++ b/packages/e2e-tests/specs/editor/blocks/quote.test.js @@ -41,8 +41,8 @@ describe( 'Quote', () => { // Create a list with the slash block shortcut. await clickBlockAppender(); await page.keyboard.type( '/quote' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Quote')]` + await page.waitForSelector( + 'button[aria-selected="true"]:text("Quote")' ); await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'Iā€™m a quote' ); diff --git a/packages/e2e-tests/specs/editor/blocks/site-title.test.js b/packages/e2e-tests/specs/editor/blocks/site-title.test.js index 60aefe5637b3aa..78ac7c3d9bab69 100644 --- a/packages/e2e-tests/specs/editor/blocks/site-title.test.js +++ b/packages/e2e-tests/specs/editor/blocks/site-title.test.js @@ -29,34 +29,22 @@ async function setOption( setting, value ) { await switchUserToAdmin(); await visitAdminPage( 'options-general.php' ); - await page.focus( `#${ setting }` ); - await pressKeyWithModifier( 'primary', 'a' ); - await page.type( `#${ setting }`, value ); + await page.fill( `#${ setting }`, value ); await Promise.all( [ page.click( '#submit' ), - page.waitForNavigation( { waitUntil: 'networkidle0' } ), + page.waitForNavigation( { waitUntil: 'networkidle' } ), ] ); await switchUserToTest(); } const saveEntities = async () => { - const savePostSelector = '.editor-post-publish-button__button'; - const savePanelSelector = '.entities-saved-states__panel'; - const entitiesSaveSelector = '.editor-entities-saved-states__save-button'; - const publishPanelSelector = '.editor-post-publish-panel'; - const closePanelButtonSelector = - '.editor-post-publish-panel__header-cancel-button button'; - - await page.click( savePostSelector ); - await page.waitForSelector( savePanelSelector ); - await page.click( entitiesSaveSelector ); - await page.waitForSelector( publishPanelSelector ); - await page.waitForSelector( - '.editor-post-publish-panel__header-cancel-button button:not([disabled])' - ); - await page.click( closePanelButtonSelector ); + await page.click( 'button:text-is("Publish")' ); + await Promise.all( [ + page.waitForResponse( /settings/ ), + page.click( 'button:text-is("Save")' ), + ] ); }; describe( 'Site Title block', () => { @@ -75,12 +63,9 @@ describe( 'Site Title block', () => { it( 'Can edit the site title as admin', async () => { await createNewPost(); await insertBlock( 'Site Title' ); - const editableSiteTitleSelector = - '[aria-label="Block: Site Title"] a[contenteditable="true"]'; - await page.waitForSelector( editableSiteTitleSelector ); - await page.focus( editableSiteTitleSelector ); - await pressKeyWithModifier( 'primary', 'a' ); + await page.click( '[aria-label="Site title text"]' ); + await pressKeyWithModifier( 'primary', 'a' ); await page.keyboard.type( 'New Site Title' ); await saveEntities(); diff --git a/packages/e2e-tests/specs/editor/blocks/spacer.test.js b/packages/e2e-tests/specs/editor/blocks/spacer.test.js index 258fc59a6d1eed..44ed92e8a6238d 100644 --- a/packages/e2e-tests/specs/editor/blocks/spacer.test.js +++ b/packages/e2e-tests/specs/editor/blocks/spacer.test.js @@ -17,8 +17,8 @@ describe( 'Spacer', () => { // Create a spacer with the slash block shortcut. await clickBlockAppender(); await page.keyboard.type( '/spacer' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Spacer')]` + await page.waitForSelector( + 'button[aria-selected="true"]:text-is("Spacer")' ); await page.keyboard.press( 'Enter' ); @@ -29,20 +29,17 @@ describe( 'Spacer', () => { // Create a spacer with the slash block shortcut. await clickBlockAppender(); await page.keyboard.type( '/spacer' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Spacer')]` + await page.waitForSelector( + 'button[aria-selected="true"]:text-is("Spacer")' ); await page.keyboard.press( 'Enter' ); - const resizableHandle = await page.$( + const resizableHandle = await page.waitForSelector( '.block-library-spacer__resize-container .components-resizable-box__handle' ); await dragAndResize( resizableHandle, { x: 0, y: 50 } ); expect( await getEditedPostContent() ).toMatchSnapshot(); - const selectedSpacer = await page.$( - '[data-type="core/spacer"].is-selected' - ); - expect( selectedSpacer ).not.toBe( null ); + await page.waitForSelector( '[data-type="core/spacer"].is-selected' ); } ); } ); diff --git a/packages/e2e-tests/specs/editor/plugins/image-size.test.js b/packages/e2e-tests/specs/editor/plugins/image-size.test.js index a92ae630465506..8123506efc5a8f 100644 --- a/packages/e2e-tests/specs/editor/plugins/image-size.test.js +++ b/packages/e2e-tests/specs/editor/plugins/image-size.test.js @@ -46,7 +46,7 @@ describe( 'changing image size', () => { const filename = uuid(); const tmpFileName = path.join( os.tmpdir(), filename + '.jpg' ); fs.copyFileSync( testImagePath, tmpFileName ); - await inputElement.uploadFile( tmpFileName ); + await inputElement.setInputFiles( tmpFileName ); // Wait for upload to finish. await page.waitForSelector( diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js index d3694734d2db5e..77fd9fd5aeb33f 100644 --- a/packages/e2e-tests/specs/editor/various/change-detection.test.js +++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js @@ -54,16 +54,9 @@ describe( 'Change detection', () => { } async function interceptSave() { - await page.setRequestInterception( true ); - - handleInterceptedRequest = ( interceptedRequest ) => { - if ( interceptedRequest.url().includes( '/wp/v2/posts' ) ) { - hadInterceptedSave = true; - } else { - interceptedRequest.continue(); - } - }; - page.on( 'request', handleInterceptedRequest ); + await page.route( '*/v2/posts', ( route ) => { + route.abort(); + } ); } async function releaseSaveIntercept() { diff --git a/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js index be20ca9e6d52d6..dd682f02e157f3 100644 --- a/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/manage-reusable-blocks.test.js @@ -45,7 +45,7 @@ describe( 'Managing reusable blocks', () => { 'greeting-reusable-block.json' ); const input = await page.$( '.list-reusable-blocks-import-form input' ); - await input.uploadFile( testReusableBlockFile ); + await input.setInputFiles( testReusableBlockFile ); // Submit the form const button = await page.$( diff --git a/packages/e2e-tests/specs/editor/various/rich-text.test.js b/packages/e2e-tests/specs/editor/various/rich-text.test.js index f5113e8b1d16a4..7853220eac9aad 100644 --- a/packages/e2e-tests/specs/editor/various/rich-text.test.js +++ b/packages/e2e-tests/specs/editor/various/rich-text.test.js @@ -265,10 +265,33 @@ describe( 'RichText', () => { await page.keyboard.press( 'Home' ); await page.keyboard.type( '-' ); - await page.keyboard.press( 'End' ); + await page.keyboard.press( 'End' ); // Scrolls the page down + + // Wait for the scroll to finish and hit End again + // const paragraph = await page.waitForSelector( + // '[aria-label="Paragraph block"]' + // ); + // await paragraph.waitForElementState( 'stable' ); + // await page.keyboard.press( 'End' ); + await page.keyboard.type( '+' ); expect( await getEditedPostContent() ).toMatchSnapshot(); + /** + * - Snapshot - 1 + * + Received + 1 + * + * + * -

-12+

+ * +

-+12

+ * + * + * This one's correct to be failing because the first End key press scrolls the page to the end + * and only the second one (after the scroll finished) will move the cursor to the EOL. This can + * be confirmed by testing manually, and TBH I'm not sure why Puppeteer is behaving differently + * here, but I have a hunch that it has something to do with how it resizes the browser to match + * the given viewport. + */ } ); it( 'should update internal selection after fresh focus', async () => { @@ -382,43 +405,42 @@ describe( 'RichText', () => { // Add text and select to color. await page.keyboard.type( '1' ); await pressKeyWithModifier( 'primary', 'a' ); + + // Open the color menu. await clickBlockToolbarButton( 'More' ); + await page.click( 'button:text-is( "Highlight" )' ); - const button = await page.waitForXPath( - `//button[text()='Highlight']` - ); - // Clicks may fail if the button is out of view. Assure it is before click. - await button.evaluate( ( element ) => element.scrollIntoView() ); - await button.click(); - - // Tab to the "Text" tab. - await page.keyboard.press( 'Tab' ); - // Tab to black. - await page.keyboard.press( 'Tab' ); - // Select color other than black. - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Enter' ); + // Apply the "Pale pink" color. + await page.click( 'button[ aria-label="Color: Pale pink" ]' ); + // Make sure the colored text is visible + const coloredTextSelector = '.has-pale-pink-color:text-is( "1" )'; + await page.waitForSelector( coloredTextSelector, { state: 'visible' } ); expect( await getEditedPostContent() ).toMatchSnapshot(); // Dismiss color picker popover - await page.keyboard.press( 'Escape' ); + await page.keyboard.press( 'Escape', { delay: 100 } ); // Navigate to the block. - await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Tab', { delay: 100 } ); // Copy the colored text. await pressKeyWithModifier( 'primary', 'c' ); // Collapsed the selection to the end. - await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.press( 'ArrowRight', { delay: 100 } ); // Create a new paragraph. - await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter', { delay: 100 } ); // Paste the colored text. await pressKeyWithModifier( 'primary', 'v' ); + // Make sure the pasted/second colored text is visible + await page.waitForSelector( + `:nth-match( ${ coloredTextSelector }, 2 )`, + { state: 'visible' } + ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); @@ -430,30 +452,47 @@ describe( 'RichText', () => { await page.keyboard.press( 'ArrowLeft' ); await page.keyboard.type( '1' ); - // Expect '1šŸ“'. + // Expect the text to be typed correctly. + await page.waitForSelector( '1šŸ“', { state: 'visible' } ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); it( 'should show/hide toolbar when entering/exiting format', async () => { const blockToolbarSelector = '.block-editor-block-toolbar'; await clickBlockAppender(); + await page.keyboard.type( '1' ); - expect( await page.$( blockToolbarSelector ) ).toBe( null ); + await page.waitForSelector( blockToolbarSelector, { state: 'hidden' } ); + await pressKeyWithModifier( 'primary', 'b' ); - expect( await page.$( blockToolbarSelector ) ).not.toBe( null ); + await page.waitForSelector( blockToolbarSelector, { + state: 'visible', + } ); + await page.keyboard.type( '2' ); - expect( await page.$( blockToolbarSelector ) ).not.toBe( null ); + await page.waitForSelector( blockToolbarSelector, { + state: 'visible', + } ); + await pressKeyWithModifier( 'primary', 'b' ); - expect( await page.$( blockToolbarSelector ) ).toBe( null ); + await page.waitForSelector( blockToolbarSelector, { state: 'hidden' } ); + await page.keyboard.type( '3' ); await page.keyboard.press( 'ArrowLeft' ); - expect( await page.$( blockToolbarSelector ) ).toBe( null ); + await page.waitForSelector( blockToolbarSelector, { state: 'hidden' } ); + await page.keyboard.press( 'ArrowLeft' ); - expect( await page.$( blockToolbarSelector ) ).not.toBe( null ); + await page.waitForSelector( blockToolbarSelector, { + state: 'visible', + } ); + await page.keyboard.press( 'ArrowLeft' ); - expect( await page.$( blockToolbarSelector ) ).not.toBe( null ); + await page.waitForSelector( blockToolbarSelector, { + state: 'visible', + } ); + await page.keyboard.press( 'ArrowLeft' ); - expect( await page.$( blockToolbarSelector ) ).toBe( null ); + await page.waitForSelector( blockToolbarSelector, { state: 'hidden' } ); } ); it( 'should run input rules after composition end', async () => { @@ -472,6 +511,7 @@ describe( 'RichText', () => { ); } ); + await page.waitForSelector( 'code:text-is( "a" )' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } );