diff --git a/.eslintignore b/.eslintignore index 2f20ff7d63625..8fe28b3d6ebc9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ build coverage -cypress node_modules vendor diff --git a/.eslintrc.js b/.eslintrc.js index 46d36c1037e88..09de31bcbe9a4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,4 +92,13 @@ module.exports = { }, ], }, + overrides: [ + { + files: [ 'test/e2e/**/*.js' ], + globals: { + page: true, + browser: true, + }, + }, + ], }; diff --git a/.gitignore b/.gitignore index 8beb1e5bf24c7..13e6170afaa53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Directories/files that may be generated by this project build coverage -cypress /hooks node_modules gutenberg.zip @@ -14,4 +13,3 @@ languages/gutenberg.pot phpcs.xml yarn.lock docker-compose.override.yml -cypress.env.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 817f693eb9295..a0037880bea26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ The workflow is documented in greater detail in the [repository management](./do ## Testing -Gutenberg contains both PHP and JavaScript code, and encourages testing and code style linting for both. It also incorporates end-to-end testing using [Cypress](https://www.cypress.io/). You can find out more details in [Testing Overview document](./docs/testing-overview.md). +Gutenberg contains both PHP and JavaScript code, and encourages testing and code style linting for both. It also incorporates end-to-end testing using [Google Puppeteer](https://developers.google.com/web/tools/puppeteer/). You can find out more details in [Testing Overview document](./docs/testing-overview.md). ## How Designers Can Contribute diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 87ff4b29a8f83..0000000000000 --- a/cypress.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "baseUrl": "http://localhost:8888", - "env": { - "username": "admin", - "password": "password" - }, - "integrationFolder": "test/e2e/integration", - "supportFile": "test/e2e/support", - "videoRecording": false, - "chromeWebSecurity": false, - "pluginsFile": "test/e2e/plugins" -} diff --git a/docs/testing-overview.md b/docs/testing-overview.md index b4242382137a5..6b66eafd09520 100644 --- a/docs/testing-overview.md +++ b/docs/testing-overview.md @@ -4,9 +4,9 @@ Gutenberg contains both PHP and JavaScript code, and encourages testing and code ## Why test? -Aside from the joy testing will bring to your life, tests are important not only because they help to ensure that our application behaves as it should, but also because they provide concise examples of how to use a piece of code. +Aside from the joy testing will bring to your life, tests are important not only because they help to ensure that our application behaves as it should, but also because they provide concise examples of how to use a piece of code. -Tests are also part of our code base, which means we apply to them the same standards we apply to all our application code. +Tests are also part of our code base, which means we apply to them the same standards we apply to all our application code. As with all code, tests have to be maintained. Writing tests for the sake of having a test isn't the goal – rather we should try to strike the right balance between covering expected and unexpected behaviours, speedy execution and code maintenance. @@ -44,7 +44,7 @@ Keep your tests in a `test` folder in your working directory. The test file shou Only test files (with at least one test case) should live directly under `/test`. If you need to add external mocks or fixtures, place them in a sub folder, for example: * `test/mocks/[file-name.js` -* `test/fixtures/[file-name].js` +* `test/fixtures/[file-name].js` ### Importing tests @@ -90,7 +90,7 @@ describe( 'CheckboxWithLabel', () => { The Jest API includes some nifty [setup and teardown methods](https://facebook.github.io/jest/docs/en/setup-teardown.html) that allow you to perform tasks *before* and *after* each or all of your tests, or tests within a specific `describe` block. -These methods can handle asynchronous code to allow setup that you normally cannot do inline. As with [individual test cases](https://facebook.github.io/jest/docs/en/asynchronous.html#promises), you can return a Promise and Jest will wait for it to resolve: +These methods can handle asynchronous code to allow setup that you normally cannot do inline. As with [individual test cases](https://facebook.github.io/jest/docs/en/asynchronous.html#promises), you can return a Promise and Jest will wait for it to resolve: ```javascript // one-time setup for *all* tests @@ -105,7 +105,7 @@ afterAll( () => { ``` `afterEach` and `afterAll` provide a perfect (and preferred) way to 'clean up' after our tests, for example, by resetting state data. - + Avoid placing clean up code after assertions since, if any of those tests fail, the clean up won't take place and may cause failures in unrelated tests. ### Mocking dependencies @@ -152,9 +152,9 @@ Because we're passing the list as an argument, we can pass mock `validValuesLis #### Imported dependencies -Often our code will use methods and properties from imported external and internal libraries in multiple places, which makes passing around arguments messy and impracticable. For these cases `jest.mock` offers a neat way to stub these dependencies. +Often our code will use methods and properties from imported external and internal libraries in multiple places, which makes passing around arguments messy and impracticable. For these cases `jest.mock` offers a neat way to stub these dependencies. -For instance, lets assume we have `config` module to control a great deal of functionality via feature flags. +For instance, lets assume we have `config` module to control a great deal of functionality via feature flags. ```javascript // bilbo.js @@ -361,12 +361,6 @@ or interactively npm run test-e2e:watch ``` -If you're using another local environment setup, you can still run the e2e tests by overriding the base URL and the default WP username/password used in the tests like so: - -```bash -cypress_base_url=http://my-custom-basee-url cypress_username=myusername cypress_password=mypassword npm run test-e2e -``` - ## PHP Testing Tests for PHP use [PHPUnit](https://phpunit.de/) as the testing framework. If you're using the built-in [local environment](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md#local-environment), you can run the PHP tests locally using this command: diff --git a/package-lock.json b/package-lock.json index 8b0c7430b23a4..498202cb7aa10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -232,27 +232,6 @@ "to-fast-properties": "2.0.0" } }, - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" - } - }, - "@cypress/xvfb": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.1.3.tgz", - "integrity": "sha512-EfRzw+wgI0Zdb4ZlhSvjh3q7I+oenqEYPXvr7oH/2RnzQqGDrPr7IU1Pi2yzGwoXmkNUQbo6qvntnItvQj0F4Q==", - "dev": true, - "requires": { - "lodash.once": "4.1.1" - } - }, "@romainberger/css-diff": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@romainberger/css-diff/-/css-diff-1.0.3.tgz", @@ -271,57 +250,11 @@ "@types/jquery": "3.3.0" } }, - "@types/blob-util": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/blob-util/-/blob-util-1.3.3.tgz", - "integrity": "sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==", - "dev": true - }, - "@types/bluebird": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.18.tgz", - "integrity": "sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w==", - "dev": true - }, - "@types/chai": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.8.tgz", - "integrity": "sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg==", - "dev": true - }, - "@types/chai-jquery": { - "version": "1.1.35", - "resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.35.tgz", - "integrity": "sha512-7aIt9QMRdxuagLLI48dPz96YJdhu64p6FCa6n4qkGN5DQLHnrIjZpD9bXCvV2G0NwgZ1FAmfP214dxc5zNCfgQ==", - "dev": true, - "requires": { - "@types/chai": "4.0.8", - "@types/jquery": "3.3.0" - } - }, "@types/jquery": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.0.tgz", "integrity": "sha512-szaKV2OQgwxYTGTY6qd9eeBfGGCaP7n2OGit4JdbOcfGgc9VWjfhMhnu5AVNhIAu8WWDIB36q9dfPVba1fGeIQ==" }, - "@types/lodash": { - "version": "4.14.87", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.87.tgz", - "integrity": "sha512-AqRC+aEF4N0LuNHtcjKtvF9OTfqZI0iaBoe3dA6m/W+/YZJBZjBmW/QIZ8fBeXC6cnytSY9tBoFBqZ9uSCeVsw==", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", - "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", - "dev": true - }, - "@types/mocha": { - "version": "2.2.44", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.44.tgz", - "integrity": "sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw==", - "dev": true - }, "@types/node": { "version": "9.4.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz", @@ -338,22 +271,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-15.6.7.tgz", "integrity": "sha512-HMfRuwiTp7/MfjPOsVlvlduouJH3haDzjc0oXqZy3ZMn3OTl3i4gGgbxsqzA/u9gNyl/oKkwOrU2oVR6vG5SAw==" }, - "@types/sinon": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.0.0.tgz", - "integrity": "sha512-cuK4xM8Lg2wd8cxshcQa8RG4IK/xfyB6TNE6tNVvkrShR4xdrYgsV04q6Dp6v1Lp6biEFdzD8k8zg/ujQeiw+A==", - "dev": true - }, - "@types/sinon-chai": { - "version": "2.7.29", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-2.7.29.tgz", - "integrity": "sha512-EkI/ZvJT4hglWo7Ipf9SX+J+R9htNOMjW8xiOhce7+0csqvgoF5IXqY5Ae1GqRgNtWCuaywR5HjVa1snkTqpOw==", - "dev": true, - "requires": { - "@types/chai": "4.0.8", - "@types/sinon": "4.0.0" - } - }, "@wordpress/a11y": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-1.0.6.tgz", @@ -568,6 +485,15 @@ } } }, + "agent-base": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", + "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -1988,12 +1914,6 @@ "isarray": "1.0.0" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -2128,12 +2048,6 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true - }, "check-node-version": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/check-node-version/-/check-node-version-3.1.1.tgz", @@ -2259,44 +2173,6 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz", "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "cli-spinners": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", - "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", - "dev": true - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", @@ -2618,15 +2494,6 @@ "readable-stream": "2.3.3" } }, - "common-tags": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.4.0.tgz", - "integrity": "sha1-EYe+Tz1M8MBCfUP3Tu8fc1AWFMA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -3029,281 +2896,6 @@ "array-find-index": "1.0.2" } }, - "cypress": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-1.4.1.tgz", - "integrity": "sha1-YvQHSgDm8S4t/jiKf06BaknOsD8=", - "dev": true, - "requires": { - "@cypress/listr-verbose-renderer": "0.4.1", - "@cypress/xvfb": "1.1.3", - "@types/blob-util": "1.3.3", - "@types/bluebird": "3.5.18", - "@types/chai": "4.0.8", - "@types/chai-jquery": "1.1.35", - "@types/jquery": "3.2.16", - "@types/lodash": "4.14.87", - "@types/minimatch": "3.0.1", - "@types/mocha": "2.2.44", - "@types/sinon": "4.0.0", - "@types/sinon-chai": "2.7.29", - "bluebird": "3.5.0", - "chalk": "2.1.0", - "check-more-types": "2.24.0", - "commander": "2.11.0", - "common-tags": "1.4.0", - "debug": "3.1.0", - "extract-zip": "1.6.6", - "fs-extra": "4.0.1", - "getos": "2.8.4", - "glob": "7.1.2", - "is-ci": "1.0.10", - "is-installed-globally": "0.1.0", - "lazy-ass": "1.6.0", - "listr": "0.12.0", - "lodash": "4.17.4", - "minimist": "1.2.0", - "progress": "1.1.8", - "ramda": "0.24.1", - "request": "2.81.0", - "request-progress": "0.3.1", - "supports-color": "5.1.0", - "tmp": "0.0.31", - "url": "0.11.0", - "yauzl": "2.8.0" - }, - "dependencies": { - "@types/jquery": { - "version": "3.2.16", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.2.16.tgz", - "integrity": "sha512-q2WC02YxQoX2nY1HRKlYGHpGP1saPmD7GN0pwCDlTz35a4eOtJG+aHRlXyjCuXokUukSrR2aXyBhSW3j+jPc0A==", - "dev": true - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.1" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", - "dev": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - }, - "dependencies": { - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "is-ci": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", - "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", - "dev": true, - "requires": { - "ci-info": "1.1.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "supports-color": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", - "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -3595,12 +3187,6 @@ "integrity": "sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU=", "dev": true }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, "element-closest": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/element-closest/-/element-closest-2.0.2.tgz", @@ -3830,6 +3416,21 @@ "event-emitter": "0.3.5" } }, + "es6-promise": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "4.2.4" + } + }, "es6-set": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", @@ -4289,12 +3890,6 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -4413,15 +4008,6 @@ "requires": { "minimist": "0.0.8" } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "1.0.1" - } } } }, @@ -4488,16 +4074,6 @@ "pend": "1.2.0" } }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, "file-entry-cache": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", @@ -4657,17 +4233,6 @@ "mime-types": "2.1.17" } }, - "fs-extra": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.1.tgz", - "integrity": "sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "3.0.1", - "universalify": "0.1.1" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5795,26 +5360,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, - "getos": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/getos/-/getos-2.8.4.tgz", - "integrity": "sha1-e4YD02GcKOOMsP56T2PDrLgNUWM=", - "dev": true, - "requires": { - "async": "2.1.4" - }, - "dependencies": { - "async": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", - "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - } - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -5867,15 +5412,6 @@ "is-glob": "2.0.1" } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "1.3.5" - } - }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -6125,6 +5661,27 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "https-proxy-agent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz", + "integrity": "sha512-uUWcfXHvy/dwfM9bqa6AozvAjS32dZSTUYd/4SEpYKRg6LEcPLshksnQYRudM9AyNvUARMfAg5TLjUDyX/K4vA==", + "dev": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -6474,16 +6031,6 @@ "is-extglob": "1.0.0" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "0.1.1", - "is-path-inside": "1.0.1" - } - }, "is-my-json-valid": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz", @@ -7814,15 +7361,6 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, - "jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11" - } - }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -7862,12 +7400,6 @@ "is-buffer": "1.1.6" } }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true - }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -7912,72 +7444,6 @@ "computed-style": "0.1.4" } }, - "listr": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", - "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "figures": "1.7.0", - "indent-string": "2.1.0", - "is-promise": "2.1.0", - "is-stream": "1.1.0", - "listr-silent-renderer": "1.1.1", - "listr-update-renderer": "0.2.0", - "listr-verbose-renderer": "0.4.1", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "ora": "0.2.3", - "p-map": "1.2.0", - "rxjs": "5.5.6", - "stream-to-observable": "0.1.0", - "strip-ansi": "3.0.1" - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", - "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "elegant-spinner": "1.0.1", - "figures": "1.7.0", - "indent-string": "3.2.0", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" - } - }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -8135,12 +7601,6 @@ "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", "dev": true }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -8159,33 +7619,6 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "1.1.3" - } - }, - "log-update": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", - "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "cli-cursor": "1.0.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - } - } - }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -8443,6 +7876,12 @@ "brorand": "1.1.0" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", @@ -9050,12 +8489,6 @@ "wrappy": "1.0.2" } }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -9088,18 +8521,6 @@ } } }, - "ora": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", - "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-spinners": "0.1.2", - "object-assign": "4.1.1" - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -9159,12 +8580,6 @@ "p-limit": "1.2.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -9865,9 +9280,9 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, "promise": { @@ -9892,6 +9307,12 @@ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -9922,6 +9343,35 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "puppeteer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.2.0.tgz", + "integrity": "sha512-4sY/6mB7+kNPGAzPGKq65tH0VG3ohUEkXHuOReB9K/tw3m1TqifYmxnMR/uDeci/UPwyk5K1gWYh8rw0U0Zscw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "extract-zip": "1.6.6", + "https-proxy-agent": "2.2.0", + "mime": "1.6.0", + "progress": "2.0.0", + "proxy-from-env": "1.0.0", + "rimraf": "2.6.2", + "ws": "3.3.3" + }, + "dependencies": { + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -9976,12 +9426,6 @@ "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", "dev": true }, - "ramda": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", - "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=", - "dev": true - }, "randexp": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", @@ -10520,15 +9964,6 @@ "uuid": "3.1.0" } }, - "request-progress": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-0.3.1.tgz", - "integrity": "sha1-ByHBBdipasayzossia4tXs/Pazo=", - "dev": true, - "requires": { - "throttleit": "0.0.2" - } - }, "request-promise-core": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", @@ -10617,16 +10052,6 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -10779,23 +10204,6 @@ "rx-lite": "4.0.8" } }, - "rxjs": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", - "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - }, - "dependencies": { - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - } - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -11135,12 +10543,6 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, "sntp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", @@ -11278,12 +10680,6 @@ "xtend": "4.0.1" } }, - "stream-to-observable": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", - "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=", - "dev": true - }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -11617,12 +11013,6 @@ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", "dev": true }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -11654,15 +11044,6 @@ "integrity": "sha1-JL9k/x0eaBkOFUYaZY3CV6Qmxe4=", "dev": true }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -11890,12 +11271,6 @@ "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", "dev": true }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true - }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -12321,12 +11696,11 @@ } }, "yauzl": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", - "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "buffer-crc32": "0.2.13", "fd-slicer": "1.0.1" } } diff --git a/package.json b/package.json index a25dfcbf2196c..b7d287c673cde 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "concurrently": "3.5.0", "core-js": "2.5.3", "cross-env": "3.2.4", - "cypress": "1.4.1", "deep-freeze": "0.0.1", "eslint": "4.16.0", "eslint-config-wordpress": "2.0.0", @@ -87,6 +86,7 @@ "phpegjs": "1.0.0-beta7", "postcss-loader": "2.0.6", "prismjs": "1.6.0", + "puppeteer": "1.2.0", "raw-loader": "0.5.1", "react-test-renderer": "16.0.0", "sass-loader": "6.0.6", @@ -117,23 +117,6 @@ } } }, - "jest": { - "collectCoverageFrom": [ - "(blocks|components|date|editor|element|i18n|data|utils|edit-post|viewport|plugins|core-data)/**/*.js" - ], - "moduleNameMapper": { - "@wordpress\\/(blocks|components|date|editor|element|i18n|data|utils|edit-post|viewport|plugins|core-data)": "$1" - }, - "preset": "@wordpress/jest-preset-default", - "setupFiles": [ - "core-js/fn/symbol/async-iterator", - "/test/unit/setup-blocks.js", - "/test/unit/setup-wp-aliases.js" - ], - "transform": { - "\\.pegjs$": "/test/unit/pegjs-transform.js" - } - }, "scripts": { "prebuild": "check-node-version --package", "build": "cross-env NODE_ENV=production webpack", @@ -151,13 +134,13 @@ "fixtures:regenerate": "npm run fixtures:clean && npm run fixtures:generate", "package-plugin": "./bin/build-plugin-zip.sh", "pot-to-php": "./bin/pot-to-php.js", - "test-unit": "wp-scripts test-unit-js", + "test-unit": "wp-scripts test-unit-js --config test/unit/jest.config.json", "test-unit-php": "docker-compose run --rm wordpress_phpunit phpunit", "test-unit-php-multisite": "docker-compose run -e WP_MULTISITE=1 --rm wordpress_phpunit phpunit", "test-unit:coverage": "npm run test-unit -- --coverage", "test-unit:coverage-ci": "npm run test-unit -- --coverage --maxWorkers 1 && codecov", "test-unit:watch": "npm run test-unit -- --watch", - "test-e2e": "cypress run --browser chrome", - "test-e2e:watch": "cypress open" + "test-e2e": "wp-scripts test-unit-js --config test/e2e/jest.config.json", + "test-e2e:watch": "npm run test-e2e -- --watch" } } diff --git a/test/e2e/.eslintrc.js b/test/e2e/.eslintrc.js deleted file mode 100644 index b7dc12c1a1669..0000000000000 --- a/test/e2e/.eslintrc.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - root: true, - extends: [ - '../../eslint/config.js', - ], - env: { - mocha: true, - }, - globals: { - cy: true, - Cypress: true, - expect: true, - }, -}; diff --git a/test/e2e/integration/001-hello-gutenberg.js b/test/e2e/integration/001-hello-gutenberg.js deleted file mode 100644 index 37e21e5ec84ac..0000000000000 --- a/test/e2e/integration/001-hello-gutenberg.js +++ /dev/null @@ -1,37 +0,0 @@ -describe( 'Hello Gutenberg', () => { - before( () => { - cy.newPost(); - } ); - - it( 'Should show the New Post Page in Gutenberg', () => { - // Assertions - cy.url().should( 'include', 'post-new.php' ); - cy.get( '[placeholder="Add title"]' ).should( 'exist' ); - } ); - - it( 'Should have no history', () => { - cy.get( '.editor-history__undo:not( :disabled )' ).should( 'not.exist' ); - cy.get( '.editor-history__redo:not( :disabled )' ).should( 'not.exist' ); - } ); - - it( 'Should not prompt to confirm unsaved changes', ( done ) => { - const timeout = setTimeout( () => { - done( new Error( 'Expected page reload' ) ); - }, 5000 ); - - cy.window().then( ( window ) => { - function verify( event ) { - expect( event.returnValue ).to.equal( '' ); - - window.removeEventListener( 'beforeunload', verify ); - clearTimeout( timeout ); - - done(); - } - - window.addEventListener( 'beforeunload', verify ); - - cy.reload(); - } ); - } ); -} ); diff --git a/test/e2e/integration/002-adding-blocks.js b/test/e2e/integration/002-adding-blocks.js deleted file mode 100644 index 3520d72eefdba..0000000000000 --- a/test/e2e/integration/002-adding-blocks.js +++ /dev/null @@ -1,51 +0,0 @@ -describe( 'Adding blocks', () => { - before( () => { - cy.newPost(); - } ); - - it( 'Should insert content using the placeholder and the regular inserter', () => { - // Default block appender is provisional - cy.get( '.editor-default-block-appender' ).click(); - cy.get( '.editor-post-title__input' ).click(); - cy.get( '[data-type="core/paragraph"]' ).should( 'have.length', 0 ); - - // Using the placeholder - cy.get( '.editor-default-block-appender' ).click(); - cy.focused().type( 'Paragraph block' ); - - // Using the slash command - // TODO: Test omitted because Cypress doesn't update the selection - // object properly, so the slash inserter is not showing up. - - // Using the regular inserter - cy.get( '.edit-post-header [aria-label="Add block"]' ).click(); - cy.get( '[placeholder="Search for a block"]' ).type( 'code' ); - cy.get( '.editor-inserter__block' ).contains( 'Code' ).click(); - cy.get( '[placeholder="Write code…"]' ).type( 'Code block' ); - - // Using the between inserter - cy.document().trigger( 'mousemove', { clientX: 200, clientY: 300 } ); - cy.document().trigger( 'mousemove', { clientX: 250, clientY: 350 } ); - cy.get( '[data-type="core/paragraph"] .editor-block-list__insertion-point-inserter' ).click(); - cy.focused().type( 'Second paragraph' ); - - // Switch to Text Mode to check HTML Output - cy.get( '.edit-post-more-menu [aria-label="More"]' ).click(); - cy.get( 'button' ).contains( 'Code Editor' ).click(); - - // Assertions - cy.get( '.editor-post-text-editor' ).should( 'have.value', [ - '', - '

Paragraph block

', - '', - '', - '', - '

Second paragraph

', - '', - '', - '', - '
Code block
', - '', - ].join( '\n' ) ); - } ); -} ); diff --git a/test/e2e/integration/003-multi-block-selection.js b/test/e2e/integration/003-multi-block-selection.js deleted file mode 100644 index f79d24125027b..0000000000000 --- a/test/e2e/integration/003-multi-block-selection.js +++ /dev/null @@ -1,60 +0,0 @@ -describe( 'Multi-block selection', () => { - before( () => { - cy.newPost(); - } ); - - it( 'Should select/unselect multiple blocks', () => { - const lastBlockSelector = '.editor-block-list__block-edit:last [contenteditable="true"]:first'; - const firstBlockContainerSelector = '.editor-block-list__block:first'; - const lastBlockContainerSelector = '.editor-block-list__block:last'; - const multiSelectedCssClass = 'is-multi-selected'; - - // Creating test blocks - cy.get( '.editor-default-block-appender' ).click(); - cy.get( lastBlockSelector ).type( 'First Paragraph' ); - cy.get( '.edit-post-header [aria-label="Add block"]' ).click(); - cy.get( '[placeholder="Search for a block"]' ).type( 'Paragraph' ); - cy.get( '.editor-inserter__block' ).contains( 'Paragraph' ).click(); - cy.get( lastBlockSelector ).type( 'Second Paragraph' ); - - // Default: No selection - cy.get( firstBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - cy.get( lastBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - - // Multiselect via Shift + click - cy.get( firstBlockContainerSelector ).click(); - cy.get( 'body' ).type( '{shift}', { release: false } ); - cy.get( lastBlockContainerSelector ).click(); - - // Verify selection - cy.get( firstBlockContainerSelector ).should( 'have.class', multiSelectedCssClass ); - cy.get( lastBlockContainerSelector ).should( 'have.class', multiSelectedCssClass ); - - // Unselect - cy.get( 'body' ).type( '{shift}' ); // releasing shift - cy.get( lastBlockContainerSelector ).click(); - - // No selection - cy.get( firstBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - cy.get( lastBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - - // Multiselect via keyboard - const isMacOs = Cypress.platform === 'darwin'; - if ( isMacOs ) { - cy.get( 'body' ).type( '{meta}a' ); - } else { - cy.get( 'body' ).type( '{ctrl}a' ); - } - - // Verify selection - cy.get( firstBlockContainerSelector ).should( 'have.class', multiSelectedCssClass ); - cy.get( lastBlockContainerSelector ).should( 'have.class', multiSelectedCssClass ); - - // Unselect - cy.get( 'body' ).type( '{esc}' ); - - // No selection - cy.get( firstBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - cy.get( lastBlockContainerSelector ).should( 'not.have.class', multiSelectedCssClass ); - } ); -} ); diff --git a/test/e2e/integration/004-managing-links.js b/test/e2e/integration/004-managing-links.js deleted file mode 100644 index dca8a7ef95cf0..0000000000000 --- a/test/e2e/integration/004-managing-links.js +++ /dev/null @@ -1,66 +0,0 @@ -describe( 'Managing links', () => { - beforeEach( () => { - cy.newPost(); - } ); - - const fixedIsOn = 'button.is-selected:contains("Fix Toolbar to Top")'; - const fixedIsOff = 'button:contains("Fix Toolbar to Top"):not(".is-selected")'; - - const setFixedToolbar = ( b ) => { - cy.get( '.edit-post-more-menu button' ).click(); - - cy.get( 'body' ).then( ( $body ) => { - const candidate = b ? fixedIsOff : fixedIsOn; - const toggleNeeded = $body.find( candidate ); - if ( toggleNeeded.length ) { - return 'button:contains("Fix Toolbar to Top")'; - } - - return '.edit-post-more-menu button'; - } ).then( ( selector ) => { - cy.log( ' selector " + selector ', selector ); - cy.get( selector ).click(); - } ); - }; - - it( 'Pressing Left and Esc in Link Dialog in "Fixed to Toolbar" mode', () => { - setFixedToolbar( true ); - - cy.get( '.editor-default-block-appender' ).click(); - - cy.focused().type( 'Text' ); - - cy.get( 'button[aria-label="Link"]' ).click(); - - // Typing "left" should not close the dialog - cy.focused().type( '{leftarrow}' ); - cy.get( '.blocks-format-toolbar__link-modal' ).should( 'be.visible' ); - - // Escape should close the dialog still. - cy.focused().type( '{esc}' ); - cy.get( '.blocks-format-toolbar__link-modal' ).should( 'not.exist' ); - } ); - - it( 'Pressing Left and Esc in Link Dialog in "Docked Toolbar" mode', () => { - setFixedToolbar( false ); - - cy.get( '.editor-default-block-appender' ).click(); - - cy.focused().type( 'Text' ); - - // we need to trigger isTyping = false - const lastBlockSelector = '.editor-block-list__block-edit:last [contenteditable="true"]:first'; - cy.get( lastBlockSelector ).trigger( 'mousemove', { clientX: 200, clientY: 300 } ); - cy.get( lastBlockSelector ).trigger( 'mousemove', { clientX: 250, clientY: 350 } ); - - cy.get( 'button[aria-label="Link"]' ).click(); - - // Typing "left" should not close the dialog - cy.get( '.blocks-url-input input' ).type( '{leftarrow}' ); - cy.get( '.blocks-format-toolbar__link-modal' ).should( 'be.visible' ); - - // Escape should close the dialog still. - cy.focused().type( '{esc}' ); - cy.get( '.blocks-format-toolbar__link-modal' ).should( 'not.exist' ); - } ); -} ); diff --git a/test/e2e/jest.config.json b/test/e2e/jest.config.json new file mode 100644 index 0000000000000..a8889633f29b9 --- /dev/null +++ b/test/e2e/jest.config.json @@ -0,0 +1,7 @@ +{ + "rootDir": "../../", + "preset": "@wordpress/jest-preset-default", + "setupFiles": [], + "testMatch": [ "/test/e2e/specs/**/(*.)(spec|test).js?(x)" ], + "timers": "real" +} diff --git a/test/e2e/plugins/index.js b/test/e2e/plugins/index.js deleted file mode 100644 index 4f3eb1ddebb82..0000000000000 --- a/test/e2e/plugins/index.js +++ /dev/null @@ -1,14 +0,0 @@ -const promisify = require( 'util.promisify' ); -const exec = promisify( require( 'child_process' ).exec ); - -module.exports = ( on, config ) => { - // Retrieve the port that the docker container is running on - return exec( 'docker-compose run -T --rm cli option get siteurl' ) - .then( ( stdout ) => { - config.baseUrl = stdout.trim(); - return config; - } ) - .catch( () => { - return config; - } ); -}; diff --git a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap new file mode 100644 index 0000000000000..571013e6decb0 --- /dev/null +++ b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`adding blocks Should insert content using the placeholder and the regular inserter 1`] = ` +" +

Paragraph block

+ + + +

Second paragraph

+ + + +
+

Quote block

+
+ + + +
Code block
+" +`; diff --git a/test/e2e/specs/a11y.test.js b/test/e2e/specs/a11y.test.js new file mode 100644 index 0000000000000..c90224fadd09d --- /dev/null +++ b/test/e2e/specs/a11y.test.js @@ -0,0 +1,26 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; + +describe( 'a11y', () => { + beforeAll( async () => { + await newDesktopBrowserPage(); + await newPost(); + } ); + + it( 'tabs header bar', async () => { + await page.keyboard.down( 'Control' ); + await page.keyboard.press( '~' ); + await page.keyboard.up( 'Control' ); + + await page.keyboard.press( 'Tab' ); + + const isFocusedToggle = await page.$eval( ':focus', ( focusedElement ) => { + return focusedElement.classList.contains( 'editor-inserter__toggle' ); + } ); + + expect( isFocusedToggle ).toBe( true ); + } ); +} ); diff --git a/test/e2e/specs/adding-blocks.test.js b/test/e2e/specs/adding-blocks.test.js new file mode 100644 index 0000000000000..5ab3af5b4742d --- /dev/null +++ b/test/e2e/specs/adding-blocks.test.js @@ -0,0 +1,55 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; + +describe( 'adding blocks', () => { + beforeAll( async () => { + await newDesktopBrowserPage(); + await newPost(); + } ); + + it( 'Should insert content using the placeholder and the regular inserter', async () => { + // Default block appender is provisional + await page.click( '.editor-default-block-appender' ); + await page.click( '.editor-post-title__input' ); + + // Post is empty, the newly created paragraph has been removed on focus out + const paragraphBlock = await page.$( '[data-type="core/paragraph"]' ); + expect( paragraphBlock ).toBeNull(); + + // Using the placeholder + await page.click( '.editor-default-block-appender' ); + await page.keyboard.type( 'Paragraph block' ); + + // Using the slash command + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '/quote' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Quote block' ); + + // Using the regular inserter + await page.click( '.edit-post-header [aria-label="Add block"]' ); + await page.keyboard.type( 'code' ); + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Code block' ); + + // Using the between inserter + await page.mouse.move( 200, 300 ); + await page.mouse.move( 250, 350 ); + await page.click( '[data-type="core/paragraph"] .editor-block-list__insertion-point-inserter' ); + await page.keyboard.type( 'Second paragraph' ); + + // Switch to Text Mode to check HTML Output + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + const codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; + await codeEditorButton.click( 'button' ); + + // Assertions + const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + + expect( textEditorContent ).toMatchSnapshot(); + } ); +} ); diff --git a/test/e2e/specs/hello.test.js b/test/e2e/specs/hello.test.js new file mode 100644 index 0000000000000..080478b850d62 --- /dev/null +++ b/test/e2e/specs/hello.test.js @@ -0,0 +1,31 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, visitAdmin, newDesktopBrowserPage } from '../support/utils'; + +describe( 'hello', () => { + beforeAll( async () => { + await newDesktopBrowserPage(); + await newPost(); + } ); + + it( 'Should show the New Post Page in Gutenberg', async () => { + expect( page.url() ).toEqual( expect.stringContaining( 'post-new.php' ) ); + const title = await page.$( '[placeholder="Add title"]' ); + expect( title ).not.toBeNull(); + } ); + + it( 'Should have no history', async () => { + const undoButton = await page.$( '.editor-history__undo:not( :disabled )' ); + const redoButton = await page.$( '.editor-history__redo:not( :disabled )' ); + + expect( undoButton ).toBeNull(); + expect( redoButton ).toBeNull(); + } ); + + it( 'Should not prompt to confirm unsaved changes', async () => { + await visitAdmin( 'edit.php' ); + expect( page.url() ).not.toEqual( expect.stringContaining( 'post-new.php' ) ); + } ); +} ); diff --git a/test/e2e/specs/managing-links.test.js b/test/e2e/specs/managing-links.test.js new file mode 100644 index 0000000000000..a88766b3b528f --- /dev/null +++ b/test/e2e/specs/managing-links.test.js @@ -0,0 +1,66 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; + +describe( 'Managing links', () => { + beforeEach( async () => { + await newDesktopBrowserPage(); + await newPost(); + } ); + + const setFixedToolbar = async ( b ) => { + await page.click( '.edit-post-more-menu button' ); + const button = ( await page.$x( '//button[contains(text(), \'Fix Toolbar to Top\')]' ) )[ 0 ]; + const buttonClassNameProperty = await button.getProperty( 'className' ); + const buttonClassName = await buttonClassNameProperty.jsonValue(); + const isSelected = buttonClassName.indexOf( 'is-selected' ) !== -1; + if ( isSelected !== b ) { + await button.click(); + } else { + await page.click( '.edit-post-more-menu button' ); + } + }; + + it( 'Pressing Left and Esc in Link Dialog in "Fixed to Toolbar" mode', async () => { + await setFixedToolbar( true ); + + await page.click( '.editor-default-block-appender' ); + await page.keyboard.type( 'Text' ); + await page.click( 'button[aria-label="Link"]' ); + + // Typing "left" should not close the dialog + await page.keyboard.press( 'ArrowLeft' ); + let modal = await page.$( '.blocks-format-toolbar__link-modal' ); + expect( modal ).not.toBeNull(); + + // Escape should close the dialog still. + await page.keyboard.press( 'Escape' ); + modal = await page.$( '.blocks-format-toolbar__link-modal' ); + expect( modal ).toBeNull(); + } ); + + it( 'Pressing Left and Esc in Link Dialog in "Docked Toolbar" mode', async () => { + setFixedToolbar( false ); + + await page.click( '.editor-default-block-appender' ); + await page.keyboard.type( 'Text' ); + + // we need to trigger isTyping = false + await page.mouse.move( 200, 300 ); + await page.mouse.move( 250, 350 ); + + await page.click( 'button[aria-label="Link"]' ); + + // Typing "left" should not close the dialog + await page.keyboard.press( 'ArrowLeft' ); + let modal = await page.$( '.blocks-format-toolbar__link-modal' ); + expect( modal ).not.toBeNull(); + + // Escape should close the dialog still. + await page.keyboard.press( 'Escape' ); + modal = await page.$( '.blocks-format-toolbar__link-modal' ); + expect( modal ).toBeNull(); + } ); +} ); diff --git a/test/e2e/specs/multi-block-selection.test.js b/test/e2e/specs/multi-block-selection.test.js new file mode 100644 index 0000000000000..09142e94846b0 --- /dev/null +++ b/test/e2e/specs/multi-block-selection.test.js @@ -0,0 +1,78 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; + +describe( 'Multi-block selection', () => { + beforeAll( async () => { + await newDesktopBrowserPage(); + await newPost(); + } ); + + it( 'Should select/unselect multiple blocks', async () => { + const firstBlockSelector = '[data-type="core/paragraph"]'; + const secondBlockSelector = '[data-type="core/image"]'; + const thirdBlockSelector = '[data-type="core/quote"]'; + const multiSelectedCssClass = 'is-multi-selected'; + + // Creating test blocks + await page.click( '.editor-default-block-appender' ); + await page.keyboard.type( 'First Paragraph' ); + await page.click( '.edit-post-header [aria-label="Add block"]' ); + await page.keyboard.type( 'Image' ); + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Enter' ); + await page.click( '.edit-post-header [aria-label="Add block"]' ); + await page.keyboard.type( 'Quote' ); + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Quote Block' ); + + const blocks = [ firstBlockSelector, secondBlockSelector, thirdBlockSelector ]; + const expectMultiSelected = ( selectors, areMultiSelected ) => { + selectors.forEach( async ( selector ) => { + const className = await page.$eval( selector, ( element ) => element.className ); + if ( areMultiSelected ) { + expect( className ).toEqual( expect.stringContaining( multiSelectedCssClass ) ); + } else { + expect( className ).not.toEqual( expect.stringContaining( multiSelectedCssClass ) ); + } + } ); + }; + + // Default: No selection + expectMultiSelected( blocks, false ); + + // Multiselect via Shift + click + await page.mouse.move( 200, 300 ); + await page.click( firstBlockSelector ); + await page.keyboard.down( 'Shift' ); + await page.click( thirdBlockSelector ); + await page.keyboard.up( 'Shift' ); + + // Verify selection + expectMultiSelected( blocks, true ); + + // Unselect + await page.click( secondBlockSelector ); + + // No selection + expectMultiSelected( blocks, false ); + + // Multiselect via keyboard + await page.click( 'body' ); + await page.keyboard.down( 'Meta' ); + await page.keyboard.press( 'a' ); + await page.keyboard.up( 'Meta' ); + + // Verify selection + expectMultiSelected( blocks, true ); + + // Unselect + await page.keyboard.press( 'Escape' ); + + // No selection + expectMultiSelected( blocks, false ); + } ); +} ); diff --git a/test/e2e/support/bootstrap.js b/test/e2e/support/bootstrap.js new file mode 100644 index 0000000000000..51dd2beedc6f1 --- /dev/null +++ b/test/e2e/support/bootstrap.js @@ -0,0 +1,15 @@ +/** + * External dependencies + */ +import puppeteer from 'puppeteer'; + +// The Jest timeout is increased because these tests are a bit slow +jest.setTimeout( 100000 ); + +beforeAll( async () => { + global.browser = await puppeteer.launch(); +} ); + +afterAll( async () => { + await browser.close(); +} ); diff --git a/test/e2e/support/gutenberg-commands.js b/test/e2e/support/gutenberg-commands.js deleted file mode 100644 index 3ece8e5001f64..0000000000000 --- a/test/e2e/support/gutenberg-commands.js +++ /dev/null @@ -1,11 +0,0 @@ -Cypress.Commands.add( 'newPost', () => { - cy.visitAdmin( '/post-new.php' ); -} ); - -Cypress.Commands.add( 'visitAdmin', ( adminPath ) => { - cy.visit( '/wp-admin/' + adminPath ).location( 'pathname' ).then( ( path ) => { - if ( path.endsWith( '/wp-login.php' ) ) { - cy.login(); - } - } ); -} ); diff --git a/test/e2e/support/index.js b/test/e2e/support/index.js deleted file mode 100644 index 6ed6aa3c8e2aa..0000000000000 --- a/test/e2e/support/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import './user-commands'; -import './gutenberg-commands'; - -Cypress.Cookies.defaults( { - whitelist: /^wordpress_/, -} ); diff --git a/test/e2e/support/user-commands.js b/test/e2e/support/user-commands.js deleted file mode 100644 index 44b6a4453e876..0000000000000 --- a/test/e2e/support/user-commands.js +++ /dev/null @@ -1,14 +0,0 @@ -Cypress.Commands.add( 'login', ( username = Cypress.env( 'username' ), password = Cypress.env( 'password' ) ) => { - // A best practice would be to avoid this in each test - // and fake it by calling an API and setting a cookie - // (not sure this is possible in WP) - - cy.location( 'pathname' ).then( ( path ) => { - if ( ! path.endsWith( '/wp-login.php' ) ) { - cy.visit( '/wp-login.php' ); - } - } ); - cy.get( '#user_login' ).type( username ); - cy.get( '#user_pass' ).type( password ); - cy.get( '#wp-submit' ).click(); -} ); diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js new file mode 100644 index 0000000000000..81e897230a33c --- /dev/null +++ b/test/e2e/support/utils.js @@ -0,0 +1,72 @@ +/** + * Node dependencies + */ +import path from 'path'; +import url from 'url'; + +const BASE_URL = 'http://localhost:8888'; +const USERNAME = 'admin'; +const PASSWORD = 'password'; +const NAVIGATION_TIMEOUT = 20000; + +function getUrl( urlPath ) { + return path.join( BASE_URL, urlPath ); +} + +function getCurrentPathName() { + return url.parse( page.url() ).pathname; +} + +async function gotoUrl( destUrl ) { + const promise = page.goto( destUrl, { timeout: 0 } ); + await new Promise( ( resolve ) => { + let resolved = false; + const markResolvedAndResolve = () => { + if ( ! resolved ) { + resolve(); + resolved = true; + } + }; + setTimeout( markResolvedAndResolve, NAVIGATION_TIMEOUT ); + promise.then( markResolvedAndResolve ); + } ); +} + +async function login() { + if ( getCurrentPathName() !== '/wp-login.php' ) { + await gotoUrl( getUrl( 'wp-login.php' ) ); + } + + await page.type( '#user_login', USERNAME ); + await page.type( '#user_pass', PASSWORD ); + await page.click( '#wp-submit' ); + + await new Promise( ( resolve ) => { + let resolved = false; + const markResolvedAndResolve = () => { + if ( ! resolved ) { + resolve(); + resolved = true; + } + }; + setTimeout( markResolvedAndResolve, NAVIGATION_TIMEOUT ); + page.waitForNavigation( { timeout: 0 } ).then( markResolvedAndResolve ); + } ); +} + +export async function visitAdmin( adminPath ) { + await gotoUrl( path.join( BASE_URL, 'wp-admin', adminPath ) ); + if ( getCurrentPathName() === '/wp-login.php' ) { + await login(); + return visitAdmin( adminPath ); + } +} + +export async function newPost() { + await visitAdmin( 'post-new.php' ); +} + +export async function newDesktopBrowserPage() { + global.page = await browser.newPage(); + await page.setViewport( { width: 1000, height: 700 } ); +} diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json new file mode 100644 index 0000000000000..5cd92a72ebfee --- /dev/null +++ b/test/unit/jest.config.json @@ -0,0 +1,22 @@ +{ + "rootDir": "../../", + "collectCoverageFrom": [ + "(blocks|components|date|editor|element|i18n|data|utils|edit-post|viewport|plugins|core-data)/**/*.js" + ], + "moduleNameMapper": { + "@wordpress\\/(blocks|components|date|editor|element|i18n|data|utils|edit-post|viewport|plugins|core-data)": "$1" + }, + "preset": "@wordpress/jest-preset-default", + "setupFiles": [ + "core-js/fn/symbol/async-iterator", + "/test/unit/setup-blocks.js", + "/test/unit/setup-wp-aliases.js" + ], + "transform": { + "\\.pegjs$": "/test/unit/pegjs-transform.js" + }, + "testPathIgnorePatterns": [ + "/node_modules/", + "/test/e2e" + ] +}