Merge pull request #24218 from woocommerce/new/puppeteer-screenshot-tester

Migrate E2E Test Suite from Selenium Webdriver to Puppeteer
This commit is contained in:
Julia Amosova 2019-12-20 10:34:02 +00:00 committed by GitHub
commit 72315ce39d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 9833 additions and 5157 deletions

View File

@ -1,9 +0,0 @@
{
"presets": [
"es2015",
"stage-2"
],
"plugins": [
"add-module-exports"
]
}

View File

@ -2,13 +2,19 @@
"root": true, "root": true,
"env": { "env": {
"browser": true, "browser": true,
"node": true "es6": true,
"node": true,
"jest/globals": true
}, },
"globals": { "globals": {
"wp": true, "wp": true,
"wpApiSettings": true, "wpApiSettings": true,
"wcSettings": true, "wcSettings": true,
"es6": true "es6": true,
"page": true,
"browser": true,
"context": true,
"jestPuppeteer": true
}, },
"rules": { "rules": {
"camelcase": 0, "camelcase": 0,
@ -16,7 +22,19 @@
"max-len": [ 2, { "code": 140 } ], "max-len": [ 2, { "code": 140 } ],
"no-console": 1 "no-console": 1
}, },
"plugins": [
"jest"
],
"extends": [
"plugin:jest/recommended"
],
"parser": "babel-eslint",
"parserOptions": { "parserOptions": {
"ecmaVersion": 6 "ecmaVersion": 8,
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true,
"jsx": true
}
} }
} }

View File

@ -5,6 +5,8 @@ dist: xenial
services: services:
- xvfb - xvfb
- mysql - mysql
- docker
- docker-compose
sudo: false sudo: false
@ -33,14 +35,14 @@ matrix:
- name: "Coding standard check" - name: "Coding standard check"
php: 7.2 php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1 env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
- name: "e2e tests" - name: "E2E tests"
php: 7.2 script:
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1 - npm install
addons: - npm run build
chrome: beta - docker-compose up --build -d
apt: - bash tests/bin/run-e2e-CI.sh
packages: after_script:
- nginx - docker-compose down -v
- name: "Unit tests code coverage" - name: "Unit tests code coverage"
php: 7.3 php: 7.3
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1 env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
@ -51,7 +53,7 @@ matrix:
- php: 7.3 - php: 7.3
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1 env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
- php: 7.2 - php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1 env: WP_VERSION=latest WP_MULTISITE=0
before_script: before_script:
- export PATH="$HOME/.composer/vendor/bin:$PATH" - export PATH="$HOME/.composer/vendor/bin:$PATH"
@ -74,7 +76,6 @@ before_script:
script: script:
- bash tests/bin/phpunit.sh - bash tests/bin/phpunit.sh
- bash tests/bin/phpcs.sh - bash tests/bin/phpcs.sh
- travis_retry bash tests/bin/run-e2e-CI.sh
after_script: after_script:
- bash tests/bin/travis.sh after - bash tests/bin/travis.sh after

3
Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM wordpress:5.3
COPY . /var/www/html/wp-content/plugins/woocommerce

12
babel.config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};

55
docker-compose.yaml Normal file
View File

@ -0,0 +1,55 @@
version: '3.7'
services:
db:
image: mariadb:10.4
restart: on-failure
environment:
MYSQL_DATABASE: testdb
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
volumes:
- db:/var/lib/mysql
wordpress-woocomerce-dev:
depends_on:
- db
build:
context: .
dockerfile: Dockerfile
ports:
- 8084:80
restart: on-failure
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: testdb
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_TABLE_PREFIX: "wp_"
WORDPRESS_DEBUG: 1
volumes:
- wordpress:/var/www/html
wordpress-cli:
depends_on:
- db
- wordpress-woocomerce-dev
image: wordpress:cli
restart: on-failure
user: xfs
command: >
/bin/sh -c '
wp core install --url=http://localhost:8084 --title="WooCommerce Core E2E Test Suite" --admin_user=admin --admin_password=password --admin_email=admin@woocommercecoree2etestsuite.com --path=/var/www/html --skip-email;
wp plugin activate woocommerce;
wp theme install twentynineteen --activate;
wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=customer --path=/var/www/html;
wp post create --post_type=page --post_status=publish --post_title='Ready' --post_content='E2E-tests.';
'
volumes:
- wordpress:/var/www/html
volumes:
db:
wordpress:

11468
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,28 +13,29 @@
"build": "grunt", "build": "grunt",
"build-watch": "grunt watch", "build-watch": "grunt watch",
"lint:js": "eslint assets/js --ext=js", "lint:js": "eslint assets/js --ext=js",
"test": "cross-env NODE_CONFIG_DIR='./tests/e2e-tests/config' BABEL_ENV=commonjs mocha \"tests/e2e-tests\" --require babel-register --recursive", "test:e2e": "./tests/bin/e2e-test-integration.js",
"test:grep": "cross-env NODE_CONFIG_DIR='./tests/e2e-tests/config' BABEL_ENV=commonjs mocha \"tests/e2e-tests\" --require babel-register --grep ", "test:e2e-dev": "./tests/bin/e2e-test-integration.js --dev",
"test:single": "cross-env NODE_CONFIG_DIR='./tests/e2e-tests/config' BABEL_ENV=commonjs mocha --require babel-register",
"makepot": "grunt makepot", "makepot": "grunt makepot",
"git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install" "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"@wordpress/e2e-test-utils": "^2.2.0",
"autoprefixer": "9.7.3", "autoprefixer": "9.7.3",
"babel": "6.23.0",
"babel-cli": "6.26.0",
"babel-eslint": "10.0.3", "babel-eslint": "10.0.3",
"babel-plugin-add-module-exports": "1.0.2",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-2": "6.24.1",
"chai": "4.2.0", "chai": "4.2.0",
"chai-as-promised": "7.1.1", "chai-as-promised": "7.1.1",
"chromedriver": "79.0.0", "commander": "^3.0.0",
"eslint-plugin-jest": "22.13.6",
"config": "3.2.4", "config": "3.2.4",
"cross-env": "6.0.3", "cross-env": "6.0.3",
"eslint": "6.7.2", "eslint": "6.7.2",
"eslint-config-wpcalypso": "5.0.0", "eslint-config-wpcalypso": "5.0.0",
"eslint-plugin-wpcalypso": "4.1.0", "github-contributors-list": "https://github.com/woocommerce/github-contributors-list/tarball/master",
"grunt": "1.0.4", "grunt": "1.0.4",
"grunt-checktextdomain": "1.0.1", "grunt-checktextdomain": "1.0.1",
"grunt-contrib-clean": "2.0.0", "grunt-contrib-clean": "2.0.0",
@ -54,14 +55,15 @@
"grunt-wp-i18n": "1.0.3", "grunt-wp-i18n": "1.0.3",
"husky": "3.1.0", "husky": "3.1.0",
"istanbul": "1.0.0-alpha.2", "istanbul": "1.0.0-alpha.2",
"jest": "24.9.0",
"jest-puppeteer": "4.3.0",
"lint-staged": "9.5.0", "lint-staged": "9.5.0",
"mocha": "6.2.2", "mocha": "6.2.2",
"node-sass": "4.13.0", "node-sass": "4.13.0",
"prettier": "github:automattic/calypso-prettier#c56b4251", "prettier": "github:automattic/calypso-prettier#c56b4251",
"puppeteer": "2.0.0",
"stylelint": "12.0.0", "stylelint": "12.0.0",
"stylelint-config-wordpress": "15.0.0", "stylelint-config-wordpress": "15.0.0"
"wc-e2e-page-objects": "0.10.0",
"github-contributors-list": "https://github.com/woocommerce/github-contributors-list/tarball/master"
}, },
"engines": { "engines": {
"node": ">=10.15.0", "node": ">=10.15.0",
@ -90,5 +92,8 @@
"> 0.1%", "> 0.1%",
"ie 8", "ie 8",
"ie 9" "ie 9"
] ],
"dependencies": {
"@jest/test-sequencer": "^24.9.0"
}
} }

View File

@ -0,0 +1,41 @@
#!/usr/bin/env node
const { spawnSync } = require( 'child_process' );
const program = require( 'commander' );
program
.usage( '<file ...> [options]' )
.option( '--dev', 'Development mode' )
.parse( process.argv );
const testEnvVars = {
NODE_ENV: 'test:e2e',
JEST_PUPPETEER_CONFIG: 'tests/e2e-tests/config/jest-puppeteer.config.js',
NODE_CONFIG_DIR: 'tests/e2e-tests/config',
};
if ( program.dev ) {
testEnvVars.JEST_PUPPETEER_CONFIG = 'tests/e2e-tests/config/jest-puppeteer.dev.config.js';
}
const envVars = Object.assign( {}, process.env, testEnvVars );
let jestProcess = spawnSync(
'jest',
[
'--maxWorkers=1',
'--config=tests/e2e-tests/config/jest.config.js',
'--rootDir=./',
'--verbose',
program.args,
],
{
stdio: 'inherit',
env: envVars,
}
);
console.log( 'Jest exit code: ' + jestProcess.status );
// Pass Jest exit code to npm
process.exit( jestProcess.status );

View File

@ -221,7 +221,7 @@ PHP
cd $WC_PLUGIN_DIR cd $WC_PLUGIN_DIR
npm install npm install
composer install composer install
grunt e2e-build npm run build
echo "ACTIVATING WooCommerce PLUGIN" echo "ACTIVATING WooCommerce PLUGIN"
php wp-cli.phar plugin activate woocommerce php wp-cli.phar plugin activate woocommerce

View File

@ -1,11 +1,28 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if [[ ${RUN_E2E} == 1 ]]; then # Max amount of time to wait for the Docker container to be built
# Allowing 30 polling attempts, 10 seconds delay between each attempt
MAX_ATTEMPTS=30
WP_SITE_URL="http://localhost:8080" # Delay (in seconds) between each polling attempt
DELAY_SEC=10
# Set base url to that of e2e test suite. # Counter for the loop that checks if the Docker container had been built
export BASE_URL="$WP_SITE_URL" count=0
# Run the tests while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8084/?page_id=4)" != "200" ]]
npm test
fi do
echo "$(date) - Docker container is still being built"
sleep ${DELAY_SEC}
((count++))
if [[ $count -gt ${MAX_ATTEMPTS} ]]; then
echo "$(date) - Docker container couldn't be built"
exit
fi
done
echo "$(date) - Docker container had been built successfully"
npm run test:e2e

217
tests/e2e-tests/README.md Normal file
View File

@ -0,0 +1,217 @@
# WooCommerce End to End Tests
Automated end-to-end tests for WooCommerce.
## Table of contents
- [Pre-requisites](#pre-requisites)
- [Install NodeJS](#install-nodejs)
- [Install Docker](#install-docker)
- [Configuration](#configuration)
- [Test Environment Configuration](#test-environment-configuration)
- [Environment Variables](#environment-variables)
- [Jest test sequencer](#jest-test-sequencer)
- [Running tests](#running-tests)
- [Prep work for running tests](#prep-work-for-running-tests)
- [How to run tests in headless mode](#how-to-run-tests-in-headless-mode)
- [How to run tests in non-headless mode](#how-to-run-tests-in-non-headless-mode)
- [How to run an individual test](#how-to-run-an-individual-test)
- [How to skip tests](#how-to-skip-tests)
- [Writing tests](#writing-tests)
- [Debugging tests](#debugging-tests)
- [Docker basics](#docker-basics)
- [How to stop and restart Docker](#how-to-stop-and-restart-docker)
- [How to stop Docker and do a clean restart](#how-to-stop-docker-and-do-a-clean-restart)
## Pre-requisites
### Install NodeJS
Install NodeJS on Mac:
```bash
brew install node
```
### Install Docker
Install Docker Desktop if you don't have it installed:
- [Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/install/)
- [Docker Desktop for Windows](https://docs.docker.com/docker-for-windows/install/)
Once installed, you should see `Docker Desktop is running` message with the green light next to it indicating that everything is working as expected.
Note, that if you install docker through other methods such as homebrew, for example, your steps to set it up will be different. The commands listed in steps below may also vary.
### Configuration
#### Test Environment Configuration
We recommend using Docker for running tests locally in order for the test environment to match the setup on Travis CI (where Docker is also used for running tests). [An official WordPress Docker image](https://github.com/docker-library/docs/blob/master/wordpress/README.md) is used to build the site. Once the site using the WP Docker image is built, the current WooCommerce dev branch is being copied to the `plugins` folder of that newly built test site. No WooCommerce Docker image is being built or needed.
#### Environment Variables
During the process of Docker building a container with test site for running tests, site URL is being set. Admin and customer users are also being created in advance with details specified in the `docker-compose.yaml` file. As a result, there is `./tests/e2e-tests/config/default.json` file that contains pre-set variables needed to run the test:
```
{
"url": "http://localhost:8084/",
"users": {
"admin": {
"username": "admin",
"password": "password"
},
"customer": {
"username": "customer",
"password": "password"
}
}
}
```
If you changed either site URL or one of the users details in the `docker-compose.yaml` file, you'd need to copy the content of the `default.json`, paste it to `test:e2e.json` and edit it further there to match your own setup.
## Jest test sequencer
[Jest](https://jestjs.io/) is being used to run e2e tests. By default, jest runs tests ordered by the time it takes to run the test (the test that takes longer to run will be run first, the test that takes less time to run will run last). Jest sequencer introduces tools that can be used to specify the order in which the tests are being run. In our case, they are being run in alphabetical order of the directories where tests are located. This way, tests in the new directory `activate-and-setup` will run first.
Setup Wizard e2e test (located in `activate-and-setup` directory) will run before all other tests. This will allow making sure that WooCommerce is activated on the site and for the setup wizard to be completed on a brand new install of WooCommerce.
## Running tests
### Prep work for running tests
- Checkout the branch to test and stay on this branch.
- Run `nmp install`
- Run `composer install --no-dev`
- Run `npm run build`
- Run the following command to build the test site using Docker: `docker-compose up` and watch the site being built. Note that it may take a few minutes the first time you do that. The process is considered completed when the messages letting you know that WordPress was installed, WooCommerce was activated and users created will be displayed:
```
wordpress-cli_1 | Success: WordPress installed successfully.
wordpress-cli_1 | Plugin 'woocommerce' activated.
wordpress-cli_1 | Success: Activated 1 of 1 plugins.
wordpress-cli_1 | Success: Created user 2.
woocommerce_wordpress-cli_1 exited with code 0
woocommerce_wordpress-cli_1 exited with code 0
```
- Open new terminal window and `cd` to the current branch again.
- Run the following command to make sure the containers were built and running: `docker ps`. You should see the 2 following containers:
- `woocommerce_wordpress-woocomerce-dev`
- `mariadb:10.4`
- Navigate to `http://localhost:8084/`. If everything went well, you should be able to access the site.
### How to run tests in headless mode
To run e2e tests in headless mode use the following command:
```bash
npm run test:e2e
```
### How to run tests in non-headless mode
Tests are being run headless by default. However, sometimes it's useful to observe the browser while running tests. To do so, you can run tests in a non-headless (dev) mode:
```bash
npm run test:e2e-dev
```
The dev mode also enables SlowMo mode. SlowMo slows down Puppeteers operations so we can better see what is happening in the browser. You can adjust the SlowMo value by editing `PUPPETEER_SLOWMO` variable in `./tests/e2e-tests/config/jest-puppeteer.dev.config.js` file. The default `PUPPETEER_SLOWMO=50` means test actions will be slowed down by 50 milliseconds.
### How to run an individual test
To run an individual test, use the direct path to the spec. For example:
```bash
npm run test:e2e ./tests/e2e-tests/specs/wp-admin/wp-admin-product-new.test.js
```
### How to skip tests
To skip the tests, use `.only` in the relevant test entry to specify the tests that you do want to run.
For example, in order to skip Setup Wizard tests, add `.only` to the login and activation tests as follows in the `setup-wizard.test.js`:
```
it.only( 'Can login', async () => {}
```
```
it.only( 'Can make sure WooCommerce is activated. If not, activate it', async () => {}
```
As a result, when you run `setup-wizard.test.js`, only the login and activate tests will run. The rest will be skipped. You should see the following in the terminal:
```
PASS tests/e2e-tests/specs/activate-and-setup/setup-wizard.test.js (11.927s)
Store owner can login and make sure WooCommerce is activated
✓ Can login (7189ms)
✓ Can make sure WooCommerce is activated. If not, activate it (1187ms)
Store owner can go through store Setup Wizard
○ skipped Can start Setup Wizard
○ skipped Can fill out Store setup details
○ skipped Can fill out Payment details
○ skipped Can fill out Shipping details
○ skipped Can fill out Recommended details
○ skipped Can skip Activate Jetpack section
○ skipped Can finish Setup Wizard - Ready! section
Store owner can finish initial store setup
○ skipped Can enable tax rates and calculations
○ skipped Can configure permalink settings
```
You can also use `.skip` in the same fashion. For example:
```
it.skip( 'Can start Setup Wizard', async () => {}
```
Finally, you can aply both `.only` and `.skip` to `describe` part of the test:
```
describe.skip( 'Store owner can go through store Setup Wizard', () => {}
```
## Writing tests
We use the following tools to write e2e tests:
- [Puppeteer](https://github.com/GoogleChrome/puppeteer) a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol
- [jest-puppeteer](https://github.com/smooth-code/jest-puppeteer) provides all required configuration to run tests using Puppeteer
- [expect-puppeteer](https://github.com/smooth-code/jest-puppeteer/tree/master/packages/expect-puppeteer) assertion library for Puppeteer
Tests are kept in `tests/e2e-tests/specs` folder.
The following packages are being used to write tests:
- `e2e-test-utils` - End-To-End (E2E) test utils for WordPress. You can find the full list of utils [here](https://github.com/WordPress/gutenberg/tree/master/packages/e2e-test-utils).
## Debugging tests
For Puppeteer debugging, follow [Google's documentation](https://developers.google.com/web/tools/puppeteer/debugging).
## Docker basics
### How to stop and restart Docker
- Press `Ctrl+C` in the terminal window where the containers are running
- Stop the container(s) using the following command: `docker-compose down`
- Restart the containers using the following command: `docker-compose up`
### How to stop Docker and do a clean restart
Steps below will allow building a brand new site with a clean DB and no data as it was built initially:
- Press `Ctrl+C` in the terminal window where the containers are running
- Stop the container(s) and delete all volumes using the following command: `docker-compose down -v`
- Restart the containers using the following command: `docker-compose up --build`

View File

@ -1,62 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { WPLogin, WPAdmin } from 'wp-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Check for functional WordPress installation', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
// login
test.before( () => {
const wpLogin = new WPLogin( driver, { url: manager.getPageUrl( '/wp-login.php' ) } );
wpLogin.login( config.get( 'users.admin.username' ), config.get( 'users.admin.password' ) );
} );
// Update the database to the current version of WP
test.it( 'update WordPress database', () => {
const updateArgs = { url: manager.getPageUrl( '/wp-admin/upgrade.php?step=upgrade_db' ), visit: true };
const update = new WPAdmin( driver, updateArgs );
} );
// Check theme status after conditionally attempting to revert to the default theme
test.it( 'have working theme', () => {
const themesArgs = { url: manager.getPageUrl( '/wp-admin/themes.php' ), visit: true };
const themes = new WPAdmin( driver, themesArgs );
driver.navigate().refresh();
assert.eventually.notEqual( themes.hasNotice( 'The active theme is broken.' ) );
assert.eventually.notEqual( themes.hasNotice( 'No themes found.' ) );
assert.eventually.notEqual( themes.hasNotice( 'ERROR:' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,3 +0,0 @@
{
"url": "BASE_URL"
}

View File

@ -1,15 +1,62 @@
{ {
"url": "http://example.com", "url": "http://localhost:8084/",
"users": { "users": {
"admin": { "admin": {
"username": "", "username": "admin",
"password": "" "password": "password"
}, },
"customer": { "customer": {
"username": "", "username": "customer",
"password": "" "password": "password"
} }
}, },
"startBrowserTimeoutMs": 30000, "products": {
"mochaTimeoutMs": 120000 "simple": {
"name": "Simple product"
},
"variable": {
"name": "Variable Product with Three Variations"
}
},
"addresses": {
"admin": {
"store": {
"firstname": "John",
"lastname": "Doe",
"company": "Automattic",
"country": "United States (US)",
"addressfirstline": "addr 1",
"addresssecondline": "addr 2",
"city": "San Francisco",
"state": "CA",
"postcode": "94107"
}
},
"customer": {
"billing": {
"firstname": "John",
"lastname": "Doe",
"company": "Automattic",
"country": "United States (US)",
"addressfirstline": "addr 1",
"addresssecondline": "addr 2",
"city": "San Francisco",
"state": "CA",
"postcode": "94107",
"phone": "123456789",
"email": "john.doe@example.com"
},
"shipping": {
"firstname": "John",
"lastname": "Doe",
"company": "Automattic",
"country": "United States (US)",
"addressfirstline": "addr 1",
"addresssecondline": "addr 2",
"city": "San Francisco",
"state": "CA",
"postcode": "94107"
}
}
}
} }

View File

@ -0,0 +1,12 @@
const Sequencer = require('@jest/test-sequencer').default;
class CustomSequencer extends Sequencer {
sort(tests) {
// Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}
module.exports = CustomSequencer;

View File

@ -0,0 +1,8 @@
/** @format */
module.exports = {
launch: {
// Required for the logged out and logged in tests so they don't share app state/token.
browserContext: 'incognito',
}
};

View File

@ -0,0 +1,20 @@
/** @format */
module.exports = {
launch: {
slowMo: process.env.PUPPETEER_SLOWMO ? false : 50,
headless: process.env.PUPPETEER_HEADLESS || false,
ignoreHTTPSErrors: true,
args: [
'--window-size=1920,1080',
'--user-agent=puppeteer-debug',
],
devtools: true,
defaultViewport: {
width: 1280,
height: 800,
},
// Required for the logged out and logged in tests so they don't share app state/token.
browserContext: 'incognito',
}
};

View File

@ -0,0 +1,34 @@
/**
* @flow strict
* @format
*/
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// Automatically clear mock calls and instances between every test
clearMocks: true,
// An array of file extensions your modules use
moduleFileExtensions: [ 'js' ],
preset: 'jest-puppeteer',
// Where to look for test files
roots: [ '<rootDir>/tests/e2e-tests/specs' ],
//setupFiles: [ '<rootDir>/.node_modules/regenerator-runtime/runtime' ],
// A list of paths to modules that run some code to configure or set up the testing framework
// before each test
setupFilesAfterEnv: [
'<rootDir>/tests/e2e-tests/config/jest.setup.js',
'expect-puppeteer',
],
// The glob patterns Jest uses to detect test files
testMatch: [ '**/*.(test|spec).js' ],
// Sort test path alphabetically. This is needed so that `activate-and-setup` tests run first
testSequencer: '<rootDir>/tests/e2e-tests/config/jest-custom-sequencer.js',
};

View File

@ -0,0 +1,12 @@
/** format */
//Set the default test timeout to 30s
let jestTimeoutInMilliSeconds = 30000;
// When running test in the Development mode, the test timeout is increased to 2 minutes which allows for errors to be inspected.
// Use `await jestPuppeteer.debug()` in test code to pause execution.
if ( process.env.JEST_PUPPETEER_CONFIG === 'tests/e2e-tests/config/jest-puppeteer.dev.config.js' ) {
jestTimeoutInMilliSeconds = 120000;
}
jest.setTimeout( jestTimeoutInMilliSeconds );

View File

@ -1,13 +0,0 @@
{
"url": "http://local.wordpress.dev/",
"users": {
"admin": {
"username": "admin",
"password": "password"
},
"customer": {
"username": "Customer",
"password": "password"
}
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -1,118 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { ShopPage, CartPage } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Cart page', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'should displays no item in the cart', () => {
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal( cartPage.hasNoItem(), true );
} );
test.it( 'should adds the product to the cart when "Add to cart" is clicked', () => {
const shopPage = new ShopPage( driver, { url: manager.getPageUrl( '/shop' ) } );
assert.eventually.equal( shopPage.addProductToCart( 'Album' ), true );
assert.eventually.equal( shopPage.addProductToCart( 'Polo' ), true );
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal( cartPage.hasItem( 'Album' ), true );
assert.eventually.equal( cartPage.hasItem( 'Polo' ), true );
} );
test.it( 'should increases item qty when "Add to cart" of the same product is clicked', () => {
const shopPage = new ShopPage( driver, { url: manager.getPageUrl( '/shop' ) } );
assert.eventually.equal( shopPage.addProductToCart( 'Album' ), true );
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal( cartPage.hasItem( 'Album', { qty: 2 } ), true );
assert.eventually.equal( cartPage.hasItem( 'Polo', { qty: 1 } ), true );
} );
test.it( 'should updates qty when updated via qty input', () => {
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
cartPage.getItem( 'Album', { qty: 2 } ).setQty( 4 );
cartPage.getItem( 'Polo', { qty: 1 } ).setQty( 3 );
cartPage.update();
assert.eventually.equal( cartPage.hasItem( 'Album', { qty: 4 } ), true );
assert.eventually.equal( cartPage.hasItem( 'Polo', { qty: 3 } ), true );
} );
test.it( 'should remove the item from the cart when remove is clicked', () => {
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
cartPage.getItem( 'Album', { qty: 4 } ).remove();
cartPage.getItem( 'Polo', { qty: 3 } ).remove();
assert.eventually.equal( cartPage.hasNoItem(), true );
} );
test.it( 'should update subtotal in cart totals when adding product to the cart', () => {
const shopPage = new ShopPage( driver, { url: manager.getPageUrl( '/shop' ) } );
assert.eventually.equal( shopPage.addProductToCart( 'Album' ), true );
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal(
cartPage.hasItem( 'Album', { qty: 1 } ),
true,
'Cart item "Album" with qty 1 is not displayed'
);
assert.eventually.equal(
cartPage.hasSubtotal( '15.00' ),
true,
'Cart totals does not display subtotal of 15.00'
);
cartPage.getItem( 'Album', { qty: 1 } ).setQty( 2 );
cartPage.update();
assert.eventually.equal(
cartPage.hasSubtotal( '30.00' ),
true,
'Cart totals does not display subtotal of 30.00'
);
} );
test.it( 'should go to the checkout page when "Proceed to Checkout" is clicked', () => {
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
const checkoutPage = cartPage.checkout();
assert.eventually.equal(
checkoutPage.components.orderReview.displayed(),
true,
'Order review in checkout page is not displayed'
);
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,162 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { Helper, PageMap, CheckoutOrderReceivedPage, StoreOwnerFlow, GuestCustomerFlow } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
const PAGE = PageMap.PAGE;
const storeOwnerFlowArgs = {
baseUrl: config.get( 'url' ),
username: config.get( 'users.admin.username' ),
password: config.get( 'users.admin.password' )
};
const assertOrderItem = ( orderReview, itemName, attrs ) => {
assert.eventually.ok(
orderReview.hasItem( itemName, attrs ),
`Could not find order item "${ itemName }" with qty ${ attrs.qty } and total ${ attrs.total }`
);
};
let manager;
let driver;
test.describe( 'Checkout Page', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
const storeOwner = new StoreOwnerFlow( driver, storeOwnerFlowArgs );
// General settings for this test.
storeOwner.setGeneralSettings( {
baseLocation: [ 'United States', 'United States (US) — California' ],
sellingLocation: 'Sell to all countries',
enableTaxes: true,
currency: [ 'United States', 'United States (US) dollar ($)' ],
} );
// Make sure payment method is set in setting.
storeOwner.enableBACS();
storeOwner.enableCOD();
storeOwner.enablePayPal();
storeOwner.logout();
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'should displays cart items in order review', () => {
const guest = new GuestCustomerFlow( driver, { baseUrl: config.get( 'url' ) } );
guest.fromShopAddProductsToCart( 'Beanie', 'Long Sleeve Tee' );
const checkoutPage = guest.openCheckout();
assert.eventually.ok( Helper.waitTillUIBlockNotPresent( driver ) );
const orderReview = checkoutPage.components.orderReview;
assertOrderItem( orderReview, 'Beanie', { qty: '1', total: '18.00' } );
assertOrderItem( orderReview, 'Long Sleeve Tee', { qty: '1', total: '25.00' } );
assert.eventually.ok( orderReview.hasSubtotal( '43.00' ), 'Could not find subtotal 43.00' );
} );
test.it( 'allows customer to choose available payment methods', () => {
const guest = new GuestCustomerFlow( driver, { baseUrl: config.get( 'url' ) } );
guest.fromShopAddProductsToCart( 'Beanie', 'Long Sleeve Tee' );
const checkoutPage = guest.openCheckout();
assert.eventually.ok( Helper.waitTillUIBlockNotPresent( driver ) );
assert.eventually.ok( checkoutPage.selectPaymentMethod( 'PayPal' ) );
assert.eventually.ok( checkoutPage.selectPaymentMethod( 'Direct bank transfer' ) );
assert.eventually.ok( checkoutPage.selectPaymentMethod( 'Cash on delivery' ) );
} );
test.it( 'allows customer to fill billing details', () => {
const guest = new GuestCustomerFlow( driver, { baseUrl: config.get( 'url' ) } );
guest.fromShopAddProductsToCart( 'Beanie', 'Long Sleeve Tee' );
const checkoutPage = guest.open( PAGE.CHECKOUT );
assert.eventually.ok( Helper.waitTillUIBlockNotPresent( driver ) );
const billingDetails = checkoutPage.components.billingDetails;
assert.eventually.ok( billingDetails.setFirstName( 'John' ) );
assert.eventually.ok( billingDetails.setLastName( 'Doe' ) );
assert.eventually.ok( billingDetails.setCompany( 'Automattic' ) );
assert.eventually.ok( billingDetails.setEmail( 'john.doe@example.com' ) );
assert.eventually.ok( billingDetails.setPhone( '123456789' ) );
assert.eventually.ok( billingDetails.selectCountry( 'united states', 'United States (US)' ) );
assert.eventually.ok( billingDetails.setAddress1( 'addr 1' ) );
assert.eventually.ok( billingDetails.setAddress2( 'addr 2' ) );
assert.eventually.ok( billingDetails.setCity( 'San Francisco' ) );
assert.eventually.ok( billingDetails.selectState( 'cali', 'California' ) );
assert.eventually.ok( billingDetails.setZip( '94107' ) );
} );
test.it( 'allows customer to fill shipping details', () => {
const guest = new GuestCustomerFlow( driver, { baseUrl: config.get( 'url' ) } );
guest.fromShopAddProductsToCart( 'Beanie', 'Long Sleeve Tee' );
const checkoutPage = guest.open( PAGE.CHECKOUT );
assert.eventually.ok( Helper.waitTillUIBlockNotPresent( driver ) );
assert.eventually.ok( checkoutPage.checkShipToDifferentAddress() );
const shippingDetails = checkoutPage.components.shippingDetails;
assert.eventually.ok( shippingDetails.setFirstName( 'John' ) );
assert.eventually.ok( shippingDetails.setLastName( 'Doe' ) );
assert.eventually.ok( shippingDetails.setCompany( 'Automattic' ) );
assert.eventually.ok( shippingDetails.selectCountry( 'united states', 'United States (US)' ) );
assert.eventually.ok( shippingDetails.setAddress1( 'addr 1' ) );
assert.eventually.ok( shippingDetails.setAddress2( 'addr 2' ) );
assert.eventually.ok( shippingDetails.setCity( 'San Francisco' ) );
assert.eventually.ok( shippingDetails.selectState( 'cali', 'California' ) );
assert.eventually.ok( shippingDetails.setZip( '94107' ) );
} );
test.it( 'allows guest customer to place order', () => {
const guest = new GuestCustomerFlow( driver, { baseUrl: config.get( 'url' ) } );
guest.fromShopAddProductsToCart( 'Beanie', 'Long Sleeve Tee' );
const checkoutPage = guest.open( PAGE.CHECKOUT );
const billingDetails = checkoutPage.components.billingDetails;
Helper.waitTillUIBlockNotPresent( driver );
billingDetails.setFirstName( 'John' );
billingDetails.setLastName( 'Doe' );
billingDetails.setCompany( 'Automattic' );
billingDetails.setEmail( 'john.doe@example.com' );
billingDetails.setPhone( '123456789' );
billingDetails.selectCountry( 'united states', 'United States (US)' );
billingDetails.setAddress1( 'addr 1' );
billingDetails.setAddress2( 'addr 2' );
billingDetails.setCity( 'San Francisco' );
billingDetails.selectState( 'cali', 'California' );
billingDetails.setZip( '94107' );
Helper.waitTillUIBlockNotPresent( driver );
checkoutPage.selectPaymentMethod( 'Cash on delivery' );
checkoutPage.placeOrder();
Helper.waitTillUIBlockNotPresent( driver );
assert.eventually.ok(
checkoutPage.hasText( 'Order received' )
);
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,129 +0,0 @@
/**
* External dependencies
*/
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { CustomerFlow, MyAccountPage, PageMap } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'My account page', function() {
const loginAsCustomer = () => {
return new CustomerFlow( driver, {
baseUrl: config.get( 'url' ),
username: config.get( 'users.customer.username' ),
password: config.get( 'users.customer.password' )
} );
};
const getMyAccountSubPageUrl = path => {
return PageMap.getPageUrl( config.get( 'url' ), {
path: '/my-account/%s'
}, path );
};
const untrailingslashit = url => {
return url.endsWith( '/' ) ? url.substring( 0, url.length - 1 ) : url;
};
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'allows customer to login', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
assert.eventually.ok( myAccount.hasText( 'Hello Customer' ), 'see "Hello Customer" text' );
assert.eventually.ok( myAccount.hasMenu( 'Dashboard' ), 'see Dashboard menu.' );
assert.eventually.ok( myAccount.hasMenu( 'Orders' ), 'see Orders menu' );
} );
test.it( 'allows customer to see orders', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Orders' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'orders' ) )
);
assert.eventually.ok( myAccount.hasText( 'Orders' ), 'see "Orders" text' );
} );
test.it( 'allows customer to see downloads', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Downloads' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'downloads' ) )
);
assert.eventually.ok( myAccount.hasText( 'Downloads' ), 'see "Downloads" text' );
} );
test.it( 'allows customer to edit addresses', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Addresses' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'edit-address' ) )
);
assert.eventually.ok( myAccount.hasText( 'Addresses' ), 'see "Addresses" text' );
} );
test.it( 'allows customer to edit account details', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Account details' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'edit-account' ) )
);
assert.eventually.ok( myAccount.hasText( 'Account details' ), 'see "Account details" text' );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,75 +0,0 @@
/**
* External dependencies
*/
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { SingleProductPage, CartPage } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Single Product Page', function() {
const visitProductByPath = path => {
return new SingleProductPage( driver, { url: manager.getPageUrl( path ) } );
};
const visitCart = () => {
return new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
};
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'should be able to add simple products to the cart', () => {
const productPage = visitProductByPath( '/product/t-shirt' );
productPage.setQuantity( 5 );
productPage.addToCart();
assert.eventually.equal( visitCart().hasItem( 'T-Shirt', { qty: 5 } ), true );
} );
test.it( 'should be able to add variation products to the cart', () => {
let variableProductPage;
variableProductPage = visitProductByPath( '/product/hoodie' );
variableProductPage.selectVariation( 'Color', 'Blue' );
variableProductPage.selectVariation( 'Logo', 'Yes' );
driver.sleep( 500 );
variableProductPage.addToCart();
assert.eventually.ok( visitCart().hasItem( 'Hoodie - Blue, Yes' ), '"Hoodie - Blue, Yes" in the cart' );
variableProductPage = visitProductByPath( '/product/hoodie' );
variableProductPage.selectVariation( 'Color', 'Green' );
variableProductPage.selectVariation( 'Logo', 'No' );
driver.sleep( 500 );
variableProductPage.addToCart();
assert.eventually.ok( visitCart().hasItem( 'Hoodie - Green, No' ), '"Hoodie - Green, No" in the cart' );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -0,0 +1,253 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import {
permalinkSettingsPageSaveChanges,
setCheckbox,
settingsPageSaveChanges,
verifyCheckboxIsSet,
verifyCheckboxIsUnset, verifyValueOfInputField
} from '../../utils';
const config = require( 'config' );
describe( 'Store owner can login and make sure WooCommerce is activated', () => {
it( 'can login', async () => {
await StoreOwnerFlow.login();
} );
it( 'can make sure WooCommerce is activated. If not, activate it', async () => {
const slug = 'woocommerce';
await StoreOwnerFlow.openPlugins();
const disableLink = await page.$( `tr[data-slug="${ slug }"] .deactivate a` );
if ( disableLink ) {
return;
}
await page.click( `tr[data-slug="${ slug }"] .activate a` );
await page.waitForSelector( `tr[data-slug="${ slug }"] .deactivate a` );
} );
} );
describe( 'Store owner can go through store Setup Wizard', () => {
it( 'can start Setup Wizard when visiting the site for the first time. Skip all other times.', async () => {
// Check if Setup Wizard Notice is visible on the screen.
// If yes - proceed with Setup Wizard, if not - skip Setup Wizard (already been completed).
const setupWizardNotice = await Promise.race( [
new Promise( resolve => setTimeout( () => resolve(), 1000 ) ), // resolves without value after 1s
page.waitForSelector('.updated.woocommerce-message.wc-connect', { visible: true } )
] );
if ( setupWizardNotice ) {
await StoreOwnerFlow.runSetupWizard();
// Check if the New Setup Wizard Notice (since 3.9) is visible on the screen.
// If yes - continue with the old Setup Wizard.
// If not - the test will continue with the old wizard by default.
const newSetupWizardNotice = await Promise.race( [
new Promise( resolve => setTimeout( () => resolve(), 1000) ), // resolves without value after 1s
page.waitForSelector( '.wc-setup-step__new_onboarding-wrapper', { visible: true } )
] );
if ( newSetupWizardNotice ) {
// Continue with the old setup wizard
await Promise.all( [
// Click on "Continue with the old setup wizard" footer link to start the old setup wizard
page.$eval( '.wc-setup-footer-links', elem => elem.click() ),
// Wait for the Payment section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
} else {
// Fill out store setup section details
// Select country where the store is located
await expect( page ).toSelect( 'select[name="store_country"]', config.get( 'addresses.admin.store.country' ) );
// Fill store's address - first line
await expect( page ).toFill( '#store_address', config.get( 'addresses.admin.store.addressfirstline' ) );
// Fill store's address - second line
await expect( page ).toFill( '#store_address_2', config.get( 'addresses.admin.store.addresssecondline' ) );
// Fill the city where the store is located
await expect( page ).toFill( '#store_city', config.get( 'addresses.admin.store.city' ) );
// Select the state where the store is located
await expect( page ).toSelect( 'select[name="store_state"]', config.get( 'addresses.admin.store.state') );
// Fill postcode of the store
await expect( page ).toFill( '#store_postcode', config.get( 'addresses.admin.store.postcode' ) );
// Select currency and type of products to sell details
await expect( page ).toSelect( 'select[name="currency_code"]', '\n' +
'\t\t\t\t\t\tUnited States (US) dollar ($ USD)\t\t\t\t\t' );
await expect( page ).toSelect( 'select[name="product_type"]', 'I plan to sell both physical and digital products' );
// Verify that checkbox next to "I will also be selling products or services in person." is not selected
await verifyCheckboxIsUnset( '#woocommerce_sell_in_person' );
// Click on "Let's go!" button to move to the next step
await page.$eval( 'button[name=save_step]', elem => elem.click() );
// Wait for usage tracking pop-up window to appear
await page.waitForSelector( '#wc-backbone-modal-dialog' );
await expect( page ).toMatchElement(
'.wc-backbone-modal-header', { text: 'Help improve WooCommerce with usage tracking' }
);
await page.waitForSelector( '#wc_tracker_checkbox_dialog' );
// Verify that checkbox next to "Enable usage tracking and help improve WooCommerce" is not selected
await verifyCheckboxIsUnset( '#wc_tracker_checkbox_dialog' );
await Promise.all( [
// Click on "Continue" button to move to the next step
page.$eval( '#wc_tracker_submit', elem => elem.click() ),
// Wait for the Payment section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
// Fill out payment section details
// Turn off Stripe account toggle
await page.click( '.wc-wizard-service-toggle' );
await Promise.all( [
// Click on "Continue" button to move to the next step
page.click( 'button[name=save_step]', { text: 'Continue' } ),
// Wait for the Shipping section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
// Fill out shipping section details
// Turn off WooCommerce Shipping option
await page.$eval( '#wc_recommended_woocommerce_services', elem => elem.click() );
await page.waitForSelector( 'select[name="shipping_zones[domestic][method]"]' );
await page.waitForSelector( 'select[name="shipping_zones[intl][method]"]' );
// Select Flat Rate shipping method for domestic shipping zone
await page.evaluate( () => {
document.querySelector( 'select[name="shipping_zones[domestic][method]"] > option:nth-child(1)' ).selected = true;
let element = document.querySelector( 'select[name="shipping_zones[domestic][method]"]' );
let event = new Event( 'change', { bubbles: true } );
event.simulated = true;
element.dispatchEvent( event );
} );
await page.$eval( 'input[name="shipping_zones[domestic][flat_rate][cost]"]', e => e.setAttribute( 'value', '10.00' ) );
// Select Flat Rate shipping method for the rest of the world shipping zone
await page.evaluate( () => {
document.querySelector( 'select[name="shipping_zones[intl][method]"] > option:nth-child(1)' ).selected = true;
let element = document.querySelector( 'select[name="shipping_zones[intl][method]"]' );
let event = new Event( 'change', { bubbles: true } );
event.simulated = true;
element.dispatchEvent( event );
} );
await page.$eval( 'input[name="shipping_zones[intl][flat_rate][cost]"]', e => e.setAttribute( 'value', '20.00' ) );
// Select product weight and product dimensions options
await expect( page ).toSelect( 'select[name="weight_unit"]', 'Pounds' );
await expect( page ).toSelect( 'select[name="dimension_unit"]', 'Inches' );
await Promise.all( [
// Click on "Continue" button to move to the next step
page.click( 'button[name=save_step]', { text: 'Continue' } ),
// Wait for the Recommended section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
// Fill out recommended section details
// Turn off Storefront Theme option
// await page.waitForSelector( '#wc_recommended_storefront_theme', { visible: true } );
// await page.$eval( '#wc_recommended_storefront_theme', elem => elem.click() );
// Turn off Automated Taxes option
await page.waitForSelector( '#wc_recommended_automated_taxes', { visible: true } );
await page.$eval( '#wc_recommended_automated_taxes', elem => elem.click() );
// Turn off WooCommerce Admin option
await page.waitForSelector( '#wc_recommended_wc_admin', { visible: true } );
await page.$eval( '#wc_recommended_wc_admin', elem => elem.click() );
// Turn off Mailchimp option
await page.waitForSelector( '#wc_recommended_mailchimp', { visible: true } );
await page.$eval( '#wc_recommended_mailchimp', elem => elem.click() );
// Turn off Facebook option
await page.waitForSelector( '#wc_recommended_facebook', { visible: true } );
await page.$eval( '#wc_recommended_facebook', elem => elem.click() );
await Promise.all( [
// Click on "Continue" button to move to the next step
page.click( 'button[name=save_step]', { text: 'Continue' } ),
// Wait for the Jetpack section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
// Skip activate Jetpack section
// Click on "Skip this step" in order to skip Jetpack installation
await page.click( '.wc-setup-footer-links' );
// Finish Setup Wizard - Ready! section
// Visit Dashboard
await StoreOwnerFlow.openDashboard();
}
}
} );
} );
describe( 'Store owner can finish initial store setup', () => {
it( 'can enable tax rates and calculations', async () => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Enable tax rates and calculations
await setCheckbox( '#woocommerce_calc_taxes' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
} );
it( 'can configure permalink settings', async () => {
// Go to Permalink Settings page
await StoreOwnerFlow.openPermalinkSettings();
// Select "Post name" option in common settings section
await page.click( 'input[value="/%postname%/"]', { text: ' Post name' } );
// Select "Custom base" in product permalinks section
await page.click( '#woocommerce_custom_selection' );
// Fill custom base slug to use
await expect( page ).toFill( '#woocommerce_permalink_structure', '/product/' );
await permalinkSettingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#setting-error-settings_updated', { text: 'Permalink structure updated.' } ),
verifyValueOfInputField( '#permalink_structure', '/%postname%/' ),
verifyValueOfInputField( '#woocommerce_permalink_structure', '/product/' ),
] );
} );
} );

View File

@ -0,0 +1,81 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { createSimpleProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { uiUnblocked } from '../../utils';
describe( 'Cart page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
await createSimpleProduct();
await StoreOwnerFlow.logout();
} );
it( 'should display no item in the cart', async () => {
await CustomerFlow.goToCart();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
it( 'should add the product to the cart when "Add to cart" is clicked', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( 'Simple product' );
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Simple product' );
} );
it( 'should increase item qty when "Add to cart" of the same product is clicked', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( 'Simple product' );
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Simple product', 2 );
} );
it( 'should update qty when updated via qty input', async () => {
await CustomerFlow.goToCart();
await CustomerFlow.setCartQuantity( 'Simple product', 4 );
await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked();
await CustomerFlow.productIsInCart( 'Simple product', 4 );
} );
it( 'should remove the item from the cart when remove is clicked', async () => {
await CustomerFlow.goToCart();
await CustomerFlow.removeFromCart( 'Simple product' );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
it( 'should update subtotal in cart totals when adding product to the cart', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( 'Simple product' );
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Simple product', 1 );
await expect( page ).toMatchElement( '.cart-subtotal .amount', { text: '$9.99' } );
await CustomerFlow.setCartQuantity( 'Simple product', 2 );
await expect( page ).toClick( 'button', { text: 'Update cart' } );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-subtotal .amount', { text: '$19.98' } );
} );
it( 'should go to the checkout page when "Proceed to Checkout" is clicked', async () => {
await CustomerFlow.goToCart();
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
expect( page ).toClick( '.checkout-button', { text: 'Proceed to checkout' } ),
] );
await expect( page ).toMatchElement( '#order_review' );
} );
} );

View File

@ -0,0 +1,158 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { createSimpleProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { setCheckbox, settingsPageSaveChanges, uiUnblocked, verifyCheckboxIsSet } from '../../utils';
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
let orderId;
describe( 'Checkout page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
await createSimpleProduct();
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Set base location with state CA.
await expect( page ).toSelect( 'select[name="woocommerce_default_country"]', 'United States (US) — California' );
// Sell to all countries
await expect( page ).toSelect( '#woocommerce_allowed_countries', 'Sell to all countries' );
// Set currency to USD
await expect( page ).toSelect( '#woocommerce_currency', 'United States (US) dollar ($)' );
// Tax calculation should have been enabled by another test - no-op
// Save
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( 'select[name="woocommerce_default_country"]', { text: 'United States (US) — California' } ),
expect( page ).toMatchElement( '#woocommerce_allowed_countries', { text: 'Sell to all countries' } ),
expect( page ).toMatchElement( '#woocommerce_currency', { text: 'United States (US) dollar ($)' } ),
] );
// Enable BACS payment method
await StoreOwnerFlow.openSettings( 'checkout', 'bacs' );
await setCheckbox( '#woocommerce_bacs_enabled' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await verifyCheckboxIsSet( '#woocommerce_bacs_enabled' );
// Enable COD payment method
await StoreOwnerFlow.openSettings( 'checkout', 'cod' );
await setCheckbox( '#woocommerce_cod_enabled' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await verifyCheckboxIsSet( '#woocommerce_cod_enabled' );
// Enable PayPal payment method
await StoreOwnerFlow.openSettings( 'checkout', 'paypal' );
await setCheckbox( '#woocommerce_paypal_enabled' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await verifyCheckboxIsSet( '#woocommerce_paypal_enabled' );
await StoreOwnerFlow.logout();
} );
it( 'should display cart items in order review', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( simpleProductName );
await CustomerFlow.goToCheckout();
await CustomerFlow.productIsInCheckout( simpleProductName, `1`, `9.99`, `9.99` );
} );
it( 'allows customer to choose available payment methods', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( simpleProductName );
await CustomerFlow.goToCheckout();
await CustomerFlow.productIsInCheckout( simpleProductName, `2`, `19.98`, `19.98` );
await expect( page ).toClick( '.wc_payment_method label', { text: 'PayPal' } );
await expect( page ).toClick( '.wc_payment_method label', { text: 'Direct bank transfer' } );
await expect( page ).toClick( '.wc_payment_method label', { text: 'Cash on delivery' } );
} );
it( 'allows customer to fill billing details', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( simpleProductName );
await CustomerFlow.goToCheckout();
await CustomerFlow.productIsInCheckout( simpleProductName, `3`, `29.97`, `29.97` );
await CustomerFlow.fillBillingDetails( config.get( 'addresses.customer.billing' ) );
} );
it( 'allows customer to fill shipping details', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( simpleProductName );
await CustomerFlow.goToCheckout();
await CustomerFlow.productIsInCheckout( simpleProductName, `4`, `39.96`, `39.96` );
// Select checkbox to ship to a different address
await page.evaluate( () => {
document.querySelector( '#ship-to-different-address-checkbox' ).click();
} );
await uiUnblocked();
await CustomerFlow.fillShippingDetails( config.get( 'addresses.customer.shipping' ) );
} );
it( 'allows guest customer to place order', async () => {
await CustomerFlow.goToShop();
await CustomerFlow.addToCartFromShopPage( simpleProductName );
await CustomerFlow.goToCheckout();
await CustomerFlow.productIsInCheckout( simpleProductName, `5`, `49.95`, `49.95` );
await CustomerFlow.fillBillingDetails( config.get( 'addresses.customer.billing' ) );
await uiUnblocked();
await expect( page ).toClick( '.wc_payment_method label', { text: 'Cash on delivery' } );
await expect( page ).toMatchElement( '.payment_method_cod', { text: 'Pay with cash upon delivery.' } );
await uiUnblocked();
await CustomerFlow.placeOrder();
await expect( page ).toMatch( 'Order received' );
// Get order ID from the order received html element on the page
let orderReceivedHtmlElement = await page.$( '.woocommerce-order-overview__order.order' );
let orderReceivedText = await page.evaluate( element => element.textContent, orderReceivedHtmlElement );
return orderId = orderReceivedText.split( /(\s+)/ )[6].toString();
} );
it( 'store owner can confirm the order was received', async () => {
await StoreOwnerFlow.login();
await StoreOwnerFlow.openAllOrdersView();
// Click on the order which was placed in the previous step
await Promise.all( [
page.click( '#post-' + orderId ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
// Verify that the order page is indeed of the order that was placed
// Verify order number
await expect( page ).toMatchElement( '.woocommerce-order-data__heading', { text: 'Order #' + orderId + ' details' } );
// Verify product name
await expect( page ).toMatchElement( '.wc-order-item-name', { text: simpleProductName } );
// Verify product cost
await expect( page ).toMatchElement( '.woocommerce-Price-amount.amount', { text: '9.99' } );
// Verify product quantity
await expect( page ).toMatchElement( '.quantity', { text: '5' } );
// Verify total order amount without shipping
await expect( page ).toMatchElement( '.line_cost', { text: '49.95' } );
} );
} );

View File

@ -0,0 +1,46 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
describe( 'My account page', () => {
it( 'allows customer to login', async () => {
await StoreOwnerFlow.logout();
await CustomerFlow.login();
await expect( page ).toMatch( 'Hello' );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Dashboard' } );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Orders' } );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Downloads' } );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Addresses' } );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Account details' } );
await expect( page ).toMatchElement( '.woocommerce-MyAccount-navigation-link', { text: 'Logout' } );
} );
it( 'allows customer to see orders', async () => {
await CustomerFlow.goToOrders();
await expect( page.url() ).toMatch( 'my-account/orders' );
await expect( page ).toMatchElement( 'h1', { text: 'Orders' } );
} );
it( 'allows customer to see downloads', async () => {
await CustomerFlow.goToDownloads();
expect( page.url() ).toMatch( 'my-account/downloads' );
await expect( page ).toMatchElement( 'h1', { text: 'Downloads' } );
} );
it( 'allows customer to see addresses', async () => {
await CustomerFlow.goToAddresses();
expect( page.url() ).toMatch( 'my-account/edit-address' );
await expect( page ).toMatchElement( 'h1', { text: 'Addresses' } );
} );
it( 'allows customer to see account details', async () => {
await CustomerFlow.goToAccountDetails();
expect( page.url() ).toMatch( 'my-account/edit-account' );
await expect( page ).toMatchElement( 'h1', { text: 'Account details' } );
} );
} );

View File

@ -0,0 +1,67 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { createSimpleProduct, createVariableProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { uiUnblocked } from '../../utils';
let simplePostIdValue;
let variablePostIdValue;
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
describe( 'Single Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
simplePostIdValue = await createSimpleProduct();
await StoreOwnerFlow.logout();
} );
it( 'should be able to add simple products to the cart', async () => {
// Add 5 simple products to cart
await CustomerFlow.goToProduct( simplePostIdValue );
await expect( page ).toFill( 'div.quantity input.qty', '5' );
await CustomerFlow.addToCart();
await expect( page ).toMatchElement( '.woocommerce-message', { text: 'have been added to your cart.' } );
// Verify cart contents
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( simpleProductName, 5 );
// Remove items from cart
await CustomerFlow.removeFromCart( simpleProductName );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
} );
describe( 'Variable Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
variablePostIdValue = await createVariableProduct();
await StoreOwnerFlow.logout();
} );
it( 'should be able to add variation products to the cart', async () => {
// Add a product with one set of variations to cart
await CustomerFlow.goToProduct( variablePostIdValue );
await expect( page ).toSelect( '#attr-1', 'val1' );
await expect( page ).toSelect( '#attr-2', 'val1' );
await expect( page ).toSelect( '#attr-3', 'val1' );
await CustomerFlow.addToCart();
await expect( page ).toMatchElement( '.woocommerce-message', { text: 'has been added to your cart.' } );
// Verify cart contents
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Variable Product with Three Variations' );
// Remove items from cart
await CustomerFlow.removeFromCart( 'Variable Product with Three Variations' );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
} );

View File

@ -0,0 +1,41 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { clickTab, verifyPublishAndTrash } from '../../utils';
describe( 'Add New Coupon Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create new coupon', async () => {
// Go to "add coupon" page
await StoreOwnerFlow.openNewCoupon();
// Make sure we're on the add coupon page
await expect( page.title() ).resolves.toMatch( 'Add new coupon' );
// Fill in coupon code and description
await expect( page ).toFill( '#title', 'code-' + new Date().getTime().toString() );
await expect( page ).toFill( '#woocommerce-coupon-description', 'test coupon' );
// Set general coupon data
await clickTab( 'General' );
await expect( page ).toSelect( '#discount_type', 'Fixed cart discount' );
await expect( page ).toFill( '#coupon_amount', '100' );
// Publish coupon, verify that it was published. Trash coupon, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'#message',
'Coupon updated.',
'1 coupon moved to the Trash.'
);
} );
} );

View File

@ -0,0 +1,37 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { verifyPublishAndTrash } from '../../utils';
describe( 'Add New Order Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create new order', async () => {
// Go to "add order" page
await StoreOwnerFlow.openNewOrder();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new order' );
// Set order data
await expect( page ).toSelect( '#order_status', 'Processing' );
await expect( page ).toFill( 'input[name=order_date]', '2018-12-13' );
await expect( page ).toFill( 'input[name=order_date_hour]', '18' );
await expect( page ).toFill( 'input[name=order_date_minute]', '55' );
// Create order, verify that it was created. Trash order, verify that it was trashed.
await verifyPublishAndTrash(
'.order_actions li .save_order',
'#message',
'Order updated.',
'1 order moved to the Trash.'
);
} );
} );

View File

@ -0,0 +1,195 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { clickTab, uiUnblocked } from '../../utils';
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
const verifyPublishAndTrash = async () => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish product
await expect( page ).toClick( '#publish' );
await page.waitForSelector( '.updated.notice', { text: 'Product published.' } );
// Verify
await expect( page ).toMatchElement( '.updated.notice', { text: 'Product published.' } );
await page.waitForSelector( 'a', { text: 'Move to Trash' } );
// Trash product
await expect( page ).toClick( 'a', { text: 'Move to Trash' } );
await page.waitForSelector( '.updated.notice', { text: '1 product moved to the Trash.' } );
// Verify
await expect( page ).toMatchElement( '.updated.notice', { text: '1 product moved to the Trash.' } );
};
describe( 'Add New Simple Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create simple virtual product titled "Simple Product" with regular price $9.99', async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', simpleProductName );
await expect( page ).toClick( '#_virtual' );
await clickTab( 'General' );
await expect( page ).toFill( '#_regular_price', '9.99' );
// Publish product, verify that it was published. Trash product, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'.updated.notice',
'Product published.',
'Move to Trash',
'1 product moved to the Trash.'
);
} );
} );
describe( 'Add New Variable Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create product with variations', async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', 'Variable Product with Three Variations' );
await expect( page ).toSelect( '#product-type', 'Variable product' );
// Create attributes for variations
await clickTab( 'Attributes' );
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' );
for ( let i = 0; i < 3; i++ ) {
await expect( page ).toClick( 'button.add_attribute', { text: 'Add' } );
// Wait for attribute form to load
await uiUnblocked();
await page.focus( `input[name="attribute_names[${ i }]"]` );
await expect( page ).toFill( `input[name="attribute_names[${ i }]"]`, 'attr #' + ( i + 1 ) );
await expect( page ).toFill( `textarea[name="attribute_values[${ i }]"]`, 'val1 | val2' );
await expect( page ).toClick( `input[name="attribute_variation[${ i }]"]` );
}
await expect( page ).toClick( 'button', { text: 'Save attributes' } );
// Wait for attribute form to save (triggers 2 UI blocks)
await uiUnblocked();
await uiUnblocked();
// Create variations from attributes
await clickTab( 'Variations' );
await page.waitForSelector( 'select.variation_actions:not([disabled])' );
await page.focus( 'select.variation_actions' );
await expect( page ).toSelect( 'select.variation_actions', 'Create variations from all attributes' );
const firstDialog = await expect( page ).toDisplayDialog( async () => {
// Using this technique since toClick() isn't working.
// See: https://github.com/GoogleChrome/puppeteer/issues/1805#issuecomment-464802876
page.$eval( 'a.do_variation_action', elem => elem.click() );
} );
expect( firstDialog.message() ).toMatch( 'Are you sure you want to link all variations?' );
const secondDialog = await expect( page ).toDisplayDialog( async () => {
await firstDialog.accept();
} );
expect( secondDialog.message() ).toMatch( '8 variations added' );
await secondDialog.dismiss();
// Set some variation data
await uiUnblocked();
await uiUnblocked();
await page.waitForSelector( '.woocommerce_variation .handlediv' );
// Verify that variations were created
await Promise.all( [
expect( page ).toMatchElement( 'select[name="attribute_attr-1[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[1]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[2]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[3]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[4]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[5]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[6]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[7]"]', { text: 'val2' } ),
] );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(2) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[0]"]', '9.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(3) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[1]"]', '11.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(4) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toClick( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_weight[2]"]', '200' );
await expect( page ).toFill( 'input[name="variable_length[2]"]', '10' );
await expect( page ).toFill( 'input[name="variable_width[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_height[2]"]', '15' );
await page.focus( 'button.save-variation-changes' );
await expect( page ).toClick( 'button.save-variation-changes', { text: 'Save changes' } );
// Publish product, verify that it was published. Trash product, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'.updated.notice',
'Product published.',
'Move to Trash',
'1 product moved to the Trash.'
);
} );
} );

View File

@ -0,0 +1,71 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { settingsPageSaveChanges, verifyValueOfInputField } from '../../utils';
describe( 'WooCommerce General Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can update settings', async () => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Set selling location to all countries first,
// so we can choose california as base location.
await expect( page ).toSelect( '#woocommerce_allowed_countries', 'Sell to all countries' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_allowed_countries', { text: 'Sell to all countries' } ),
] );
// Set base location with state CA.
await expect( page ).toSelect( 'select[name="woocommerce_default_country"]', 'United States (US) — California' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( 'select[name="woocommerce_default_country"]', { text: 'United States (US) — California' } ),
] );
// Set selling location to specific countries first, so we can choose U.S as base location (without state).
// This will makes specific countries option appears.
await expect( page ).toSelect( '#woocommerce_allowed_countries', 'Sell to specific countries' );
await expect( page ).toSelect( 'select[name="woocommerce_specific_allowed_countries[]"]', 'United States (US)' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_allowed_countries', { text: 'Sell to specific countries' } ),
expect( page ).toMatchElement( 'select[name="woocommerce_specific_allowed_countries[]"]', { text: 'United States (US)' } ),
] );
// Set currency options.
await expect( page ).toFill( '#woocommerce_price_thousand_sep', ',' );
await expect( page ).toFill( '#woocommerce_price_decimal_sep', '.' );
await expect( page ).toFill( '#woocommerce_price_num_decimals', '2' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyValueOfInputField( '#woocommerce_price_thousand_sep', ',' ),
verifyValueOfInputField( '#woocommerce_price_decimal_sep', '.' ),
verifyValueOfInputField( '#woocommerce_price_num_decimals', '2' ),
] );
} );
} );

View File

@ -0,0 +1,50 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { setCheckbox, settingsPageSaveChanges, unsetCheckbox, verifyCheckboxIsSet, verifyCheckboxIsUnset } from '../../utils';
describe( 'WooCommerce Products > Downloadable Products Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can update settings', async () => {
// Go to downloadable products settings page
await StoreOwnerFlow.openSettings( 'products', 'downloadable' );
// Make sure the product tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Products' } );
await expect( page ).toMatchElement( 'ul.subsubsub > li > a.current', { text: 'Downloadable products' } );
await expect( page ).toSelect( '#woocommerce_file_download_method', 'Redirect only' );
await setCheckbox( '#woocommerce_downloads_require_login' );
await setCheckbox( '#woocommerce_downloads_grant_access_after_payment' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_file_download_method', { text: 'Redirect only' } ),
verifyCheckboxIsSet( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsSet( '#woocommerce_downloads_grant_access_after_payment' ),
] );
await expect( page ).toSelect( '#woocommerce_file_download_method', 'Force downloads' );
await unsetCheckbox( '#woocommerce_downloads_require_login' );
await unsetCheckbox( '#woocommerce_downloads_grant_access_after_payment' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_file_download_method', { text: 'Force downloads' } ),
verifyCheckboxIsUnset( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsUnset( '#woocommerce_downloads_grant_access_after_payment' ),
] );
} );
} );

View File

@ -0,0 +1,170 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import {
clearAndFillInput,
setCheckbox,
settingsPageSaveChanges,
uiUnblocked,
verifyCheckboxIsSet,
verifyValueOfInputField
} from '../../utils';
describe( 'WooCommerce Tax Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can enable tax calculation', async() => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Enable tax calculation
await setCheckbox( 'input[name="woocommerce_calc_taxes"]' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
// Verify that tax settings are now present
await expect( page ).toMatchElement( 'a.nav-tab', { text: 'Tax' } );
} );
it( 'can set tax options', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Prices exclusive of tax
await expect( page ).toClick( 'input[name="woocommerce_prices_include_tax"][value="no"]' );
// Tax based on customer shipping address
await expect( page ).toSelect( '#woocommerce_tax_based_on', 'Customer shipping address' );
// Standard tax class for shipping
await expect( page ).toSelect( '#woocommerce_shipping_tax_class', 'Standard' );
// Leave rounding unchecked (no-op)
// Display prices excluding tax
await expect( page ).toSelect( '#woocommerce_tax_display_shop', 'Excluding tax' );
// Display prices including tax in cart and at checkout
await expect( page ).toSelect( '#woocommerce_tax_display_cart', 'Including tax' );
// Display a single tax total
await expect( page ).toSelect( '#woocommerce_tax_total_display', 'As a single total' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyValueOfInputField( 'input[name="woocommerce_prices_include_tax"][value="no"]', 'no' ),
expect( page ).toMatchElement( '#woocommerce_tax_based_on', { text: 'Customer shipping address' } ),
expect( page ).toMatchElement( '#woocommerce_shipping_tax_class', { text: 'Standard' } ),
expect( page ).toMatchElement( '#woocommerce_tax_display_shop', { text: 'Excluding tax' } ),
expect( page ).toMatchElement( '#woocommerce_tax_display_cart', { text: 'Including tax' } ),
expect( page ).toMatchElement( '#woocommerce_tax_total_display', { text: 'As a single total' } ),
] );
} );
it( 'can add tax classes', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Remove additional tax classes
await clearAndFillInput( '#woocommerce_tax_classes', '' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_tax_classes', { text: '' } ),
] );
// Add a "fancy" tax class
await clearAndFillInput( '#woocommerce_tax_classes', 'Fancy' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( 'ul.subsubsub > li > a', { text: 'Fancy rates' } ),
] );
} );
it( 'can set rate settings', async () => {
// Go to "fancy" rates tax settings page
await StoreOwnerFlow.openSettings( 'tax', 'fancy' );
// Make sure the tax tab is active, with the "fancy" subsection
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
await expect( page ).toMatchElement( 'ul.subsubsub > li > a.current', { text: 'Fancy rates' } );
// Create a state tax
await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect( page ).toFill( 'input[name^="tax_rate_country[new-0"]', 'US' );
await expect( page ).toFill( 'input[name^="tax_rate_state[new-0"]', 'CA' );
await expect( page ).toFill( 'input[name^="tax_rate[new-0"]', '7.5' );
await expect( page ).toFill( 'input[name^="tax_rate_name[new-0"]', 'CA State Tax' );
// Create a federal tax
await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect( page ).toFill( 'input[name^="tax_rate_country[new-1"]', 'US' );
await expect( page ).toFill( 'input[name^="tax_rate[new-1"]', '1.5' );
await expect( page ).toFill( 'input[name^="tax_rate_priority[new-1"]', '2' );
await expect( page ).toFill( 'input[name^="tax_rate_name[new-1"]', 'Federal Tax' );
await expect( page ).toClick( 'input[name^="tax_rate_shipping[new-1"]' );
// Save changes (AJAX here)
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 2 tax rates
expect( await page.$$( '#rates tr' ) ).toHaveLength( 2 );
// Delete federal rate
await expect( page ).toClick( '#rates tr:nth-child(2) input' );
await expect( page ).toClick( '.wc_tax_rates a.remove_tax_rates' );
// Save changes (AJAX here)
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 1 rate
expect( await page.$$( '#rates tr' ) ).toHaveLength( 1 );
await expect( page ).toMatchElement(
'#rates tr:first-of-type input[name^="tax_rate_state"][value="CA"]'
);
} );
it( 'can remove tax classes', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Remove "fancy" tax class
await clearAndFillInput( '#woocommerce_tax_classes', ' ' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).not.toMatchElement( 'ul.subsubsub > li > a', { text: 'Fancy rates' } ),
] );
await page.waitFor( 10000 );
} );
} );

View File

@ -0,0 +1,178 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from "./flows";
import { clickTab, uiUnblocked } from "./index";
const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' );
const verifyAndPublish = async () => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish product
await expect( page ).toClick( '#publish' );
await page.waitForSelector( '.updated.notice' );
// Verify
await expect( page ).toMatchElement( '.updated.notice', { text: 'Product published.' } );
};
/**
* Create simple product.
*/
const createSimpleProduct = async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', simpleProductName );
await clickTab( 'General' );
await expect( page ).toFill( '#_regular_price', '9.99' );
await verifyAndPublish();
const simplePostId = await page.$( '#post_ID' );
let simplePostIdValue = ( await ( await simplePostId.getProperty( 'value' ) ).jsonValue() );
return simplePostIdValue;
} ;
/**
* Create variable product.
*/
const createVariableProduct = async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', 'Variable Product with Three Variations' );
await expect( page ).toSelect( '#product-type', 'Variable product' );
// Create attributes for variations
await clickTab( 'Attributes' );
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' );
for ( let i = 0; i < 3; i++ ) {
await expect( page ).toClick( 'button.add_attribute', { text: 'Add' } );
// Wait for attribute form to load
await uiUnblocked();
await page.focus( `input[name="attribute_names[${ i }]"]` );
await expect( page ).toFill( `input[name="attribute_names[${ i }]"]`, 'attr #' + ( i + 1 ) );
await expect( page ).toFill( `textarea[name="attribute_values[${ i }]"]`, 'val1 | val2' );
await expect( page ).toClick( `input[name="attribute_variation[${ i }]"]` );
}
await expect( page ).toClick( 'button', { text: 'Save attributes' } );
// Wait for attribute form to save (triggers 2 UI blocks)
await uiUnblocked();
await uiUnblocked();
// Create variations from attributes
await clickTab( 'Variations' );
await page.waitForSelector( 'select.variation_actions:not([disabled])' );
await page.focus( 'select.variation_actions' );
await expect( page ).toSelect( 'select.variation_actions', 'Create variations from all attributes' );
const firstDialog = await expect( page ).toDisplayDialog( async () => {
// Using this technique since toClick() isn't working.
// See: https://github.com/GoogleChrome/puppeteer/issues/1805#issuecomment-464802876
page.$eval( 'a.do_variation_action', elem => elem.click() );
} );
expect( firstDialog.message() ).toMatch( 'Are you sure you want to link all variations?' );
const secondDialog = await expect( page ).toDisplayDialog( async () => {
await firstDialog.accept();
} );
expect( secondDialog.message() ).toMatch( '8 variations added' );
await secondDialog.dismiss();
// Set some variation data
await uiUnblocked();
await uiUnblocked();
await page.waitForSelector( '.woocommerce_variation .handlediv' );
// Verify that variations were created
await Promise.all( [
expect( page ).toMatchElement( 'select[name="attribute_attr-1[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[1]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[2]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[3]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[4]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[5]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[6]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[7]"]', { text: 'val2' } ),
] );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(2) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[0]"]', '9.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(3) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[1]"]', '11.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(4) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toClick( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_weight[2]"]', '200' );
await expect( page ).toFill( 'input[name="variable_length[2]"]', '10' );
await expect( page ).toFill( 'input[name="variable_width[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_height[2]"]', '15' );
await page.focus( 'button.save-variation-changes' );
await expect( page ).toClick( 'button.save-variation-changes', { text: 'Save changes' } );
await verifyAndPublish();
const variablePostId = await page.$( '#post_ID' );
let variablePostIdValue = ( await ( await variablePostId.getProperty( 'value' ) ).jsonValue() );
return variablePostIdValue;
};
export { createSimpleProduct, createVariableProduct };

View File

@ -0,0 +1,317 @@
/**
* @format
*/
/**
* External dependencies
*/
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
import { clearAndFillInput } from './index';
const config = require( 'config' );
const baseUrl = config.get( 'url' );
const WP_ADMIN_LOGIN = baseUrl + 'wp-login.php';
const WP_ADMIN_DASHBOARD = baseUrl + 'wp-admin';
const WP_ADMIN_PLUGINS = baseUrl + 'wp-admin/plugins.php';
const WP_ADMIN_SETUP_WIZARD = baseUrl + 'wp-admin/admin.php?page=wc-setup';
const WP_ADMIN_ALL_ORDERS_VIEW = baseUrl + 'wp-admin/edit.php?post_type=shop_order';
const WP_ADMIN_NEW_COUPON = baseUrl + 'wp-admin/post-new.php?post_type=shop_coupon';
const WP_ADMIN_NEW_ORDER = baseUrl + 'wp-admin/post-new.php?post_type=shop_order';
const WP_ADMIN_NEW_PRODUCT = baseUrl + 'wp-admin/post-new.php?post_type=product';
const WP_ADMIN_WC_SETTINGS = baseUrl + 'wp-admin/admin.php?page=wc-settings&tab=';
const WP_ADMIN_PERMALINK_SETTINGS = baseUrl + 'wp-admin/options-permalink.php';
const SHOP_PAGE = baseUrl + 'shop';
const SHOP_PRODUCT_PAGE = baseUrl + '?p=';
const SHOP_CART_PAGE = baseUrl + 'cart';
const SHOP_CHECKOUT_PAGE = baseUrl + 'checkout/';
const SHOP_MY_ACCOUNT_PAGE = baseUrl + 'my-account/';
const MY_ACCOUNT_ORDERS = baseUrl + 'my-account/orders';
const MY_ACCOUNT_DOWNLOADS = baseUrl + 'my-account/downloads';
const MY_ACCOUNT_ADDRESSES = baseUrl + 'my-account/edit-address';
const MY_ACCOUNT_ACCOUNT_DETAILS = baseUrl + 'my-account/edit-account';
const getProductColumnExpression = ( productTitle ) => (
'td[@class="product-name" and ' +
`a[contains(text(), "${ productTitle }")]` +
']'
);
const getQtyColumnExpression = ( args ) => (
'td[@class="product-quantity" and ' +
'.//' + getQtyInputExpression( args ) +
']'
);
const getQtyInputExpression = ( args = {} ) => {
let qtyValue = '';
if ( args.checkQty ) {
qtyValue = ` and @value="${ args.qty }"`;
}
return 'input[contains(@class, "input-text")' + qtyValue + ']';
};
const getCartItemExpression = ( productTitle, args ) => (
'//tr[contains(@class, "cart_item") and ' +
getProductColumnExpression( productTitle ) +
' and ' +
getQtyColumnExpression( args ) +
']'
);
const getRemoveExpression = () => (
'td[@class="product-remove"]//a[@class="remove"]'
);
const CustomerFlow = {
addToCart: async () => {
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.single_add_to_cart_button' ),
] );
},
addToCartFromShopPage: async ( productTitle ) => {
const addToCartXPath = `//li[contains(@class, "type-product") and a/h2[contains(text(), "${ productTitle }")]]` +
'//a[contains(@class, "add_to_cart_button") and contains(@class, "ajax_add_to_cart")';
const [ addToCartButton ] = await page.$x( addToCartXPath + ']' );
addToCartButton.click();
await page.waitFor( addToCartXPath + ' and contains(@class, "added")]' );
},
goToCheckout: async () => {
await page.goto( SHOP_CHECKOUT_PAGE, {
waitUntil: 'networkidle0',
} );
},
goToOrders: async () => {
await page.goto( MY_ACCOUNT_ORDERS, {
waitUntil: 'networkidle0',
} );
},
goToDownloads: async () => {
await page.goto( MY_ACCOUNT_DOWNLOADS, {
waitUntil: 'networkidle0',
} );
},
goToAddresses: async () => {
await page.goto( MY_ACCOUNT_ADDRESSES, {
waitUntil: 'networkidle0',
} );
},
goToAccountDetails: async () => {
await page.goto( MY_ACCOUNT_ACCOUNT_DETAILS, {
waitUntil: 'networkidle0',
} );
},
goToProduct: async ( postID ) => {
await page.goto( SHOP_PRODUCT_PAGE + postID, {
waitUntil: 'networkidle0',
} );
},
goToShop: async () => {
await page.goto(SHOP_PAGE, {
waitUntil: 'networkidle0',
});
},
placeOrder: async () => {
await Promise.all( [
expect( page ).toClick( '#place_order' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
},
productIsInCheckout: async ( productTitle, quantity, total, cartSubtotal ) => {
await expect( page ).toMatchElement( '.product-name', { text: productTitle } );
await expect( page ).toMatchElement( '.product-quantity', { text: quantity } );
await expect( page ).toMatchElement( '.product-total .amount', { text: total } );
await expect( page ).toMatchElement( '.cart-subtotal .amount', { text: cartSubtotal } );
},
goToCart: async () => {
await page.goto( SHOP_CART_PAGE, {
waitUntil: 'networkidle0',
} );
},
login: async () => {
await page.goto( SHOP_MY_ACCOUNT_PAGE, {
waitUntil: 'networkidle0',
} );
await expect( page.title() ).resolves.toMatch( 'My account' );
await page.type( '#username', config.get('users.customer.username') );
await page.type( '#password', config.get('users.customer.password') );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button[name="login"]' ),
] );
},
productIsInCart: async ( productTitle, quantity = null ) => {
const cartItemArgs = quantity ? { qty: quantity } : {};
const cartItemXPath = getCartItemExpression( productTitle, cartItemArgs );
await expect( page.$x( cartItemXPath ) ).resolves.toHaveLength( 1 );
},
fillBillingDetails: async ( customerBillingDetails ) => {
await expect( page ).toFill( '#billing_first_name', customerBillingDetails.firstname );
await expect( page ).toFill( '#billing_last_name', customerBillingDetails.lastname );
await expect( page ).toFill( '#billing_company', customerBillingDetails.company );
await expect( page ).toSelect( '#billing_country', customerBillingDetails.country );
await expect( page ).toFill( '#billing_address_1', customerBillingDetails.addressfirstline );
await expect( page ).toFill( '#billing_address_2', customerBillingDetails.addresssecondline );
await expect( page ).toFill( '#billing_city', customerBillingDetails.city );
await expect( page ).toSelect( '#billing_state', customerBillingDetails.state );
await expect( page ).toFill( '#billing_postcode', customerBillingDetails.postcode );
await expect( page ).toFill( '#billing_phone', customerBillingDetails.phone );
await expect( page ).toFill( '#billing_email', customerBillingDetails.email );
},
fillShippingDetails: async ( customerShippingDetails ) => {
await expect( page ).toFill( '#shipping_first_name', customerShippingDetails.firstname );
await expect( page ).toFill( '#shipping_last_name', customerShippingDetails.lastname );
await expect( page ).toFill( '#shipping_company', customerShippingDetails.company );
await expect( page ).toSelect( '#shipping_country', customerShippingDetails.country );
await expect( page ).toFill( '#shipping_address_1', customerShippingDetails.addressfirstline );
await expect( page ).toFill( '#shipping_address_2', customerShippingDetails.addresssecondline );
await expect( page ).toFill( '#shipping_city', customerShippingDetails.city );
await expect( page ).toSelect( '#shipping_state', customerShippingDetails.state );
await expect( page ).toFill( '#shipping_postcode', customerShippingDetails.postcode );
},
removeFromCart: async ( productTitle ) => {
const cartItemXPath = getCartItemExpression(productTitle);
const removeItemXPath = cartItemXPath + '//' + getRemoveExpression();
const [removeButton] = await page.$x(removeItemXPath);
await removeButton.click();
},
setCartQuantity: async ( productTitle, quantityValue ) => {
const cartItemXPath = getCartItemExpression( productTitle );
const quantityInputXPath = cartItemXPath + '//' + getQtyInputExpression();
const [ quantityInput ] = await page.$x( quantityInputXPath );
await quantityInput.focus();
await pressKeyWithModifier( 'primary', 'a' );
await quantityInput.type( quantityValue.toString() );
},
};
const StoreOwnerFlow = {
login: async () => {
await page.goto( WP_ADMIN_LOGIN, {
waitUntil: 'networkidle0',
} );
await expect( page.title() ).resolves.toMatch( 'Log In' );
await clearAndFillInput( '#user_login', ' ' );
await page.type( '#user_login', config.get( 'users.admin.username' ) );
await page.type( '#user_pass', config.get( 'users.admin.password' ) );
await Promise.all( [
page.click( 'input[type=submit]' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
},
logout: async () => {
await page.goto(baseUrl + 'wp-login.php?action=logout', {
waitUntil: 'networkidle0',
});
await expect(page).toMatch('You are attempting to log out');
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.click('a'),
]);
},
openAllOrdersView: async () => {
await page.goto( WP_ADMIN_ALL_ORDERS_VIEW, {
waitUntil: 'networkidle0',
} );
},
openDashboard: async () => {
await page.goto( WP_ADMIN_DASHBOARD, {
waitUntil: 'networkidle0',
} );
},
openNewCoupon: async () => {
await page.goto( WP_ADMIN_NEW_COUPON, {
waitUntil: 'networkidle0',
} );
},
openNewOrder: async () => {
await page.goto( WP_ADMIN_NEW_ORDER, {
waitUntil: 'networkidle0',
} );
},
openNewProduct: async () => {
await page.goto( WP_ADMIN_NEW_PRODUCT, {
waitUntil: 'networkidle0',
} );
},
openPermalinkSettings: async () => {
await page.goto( WP_ADMIN_PERMALINK_SETTINGS, {
waitUntil: 'networkidle0',
} );
},
openPlugins: async () => {
await page.goto( WP_ADMIN_PLUGINS, {
waitUntil: 'networkidle0',
} );
},
openSettings: async ( tab, section = null ) => {
let settingsUrl = WP_ADMIN_WC_SETTINGS + tab;
if ( section ) {
settingsUrl += `&section=${ section }`;
}
await page.goto( settingsUrl, {
waitUntil: 'networkidle0',
} );
},
runSetupWizard: async () => {
await page.goto( WP_ADMIN_SETUP_WIZARD, {
waitUntil: 'networkidle0',
} );
},
};
export { CustomerFlow, StoreOwnerFlow };

View File

@ -0,0 +1,175 @@
/**
* External dependencies
*/
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
const flows = require( './flows' );
/**
* Perform a "select all" and then fill a input.
*
* @param {string} selector
* @param {string} value
*/
const clearAndFillInput = async ( selector, value ) => {
await page.focus( selector );
await pressKeyWithModifier( 'primary', 'a' );
await page.type( selector, value );
};
/**
* Click a tab (on post type edit screen).
*
* @param {string} tabName Tab label
*/
const clickTab = async ( tabName ) => {
await expect( page ).toClick( '.wc-tabs > li > a', { text: tabName } );
};
/**
* Save changes on a WooCommerce settings page.
*/
const settingsPageSaveChanges = async () => {
await page.focus( 'button.woocommerce-save-button' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button.woocommerce-save-button' ),
] );
};
/**
* Save changes on Permalink settings page.
*/
const permalinkSettingsPageSaveChanges = async () => {
await page.focus( '.wp-core-ui .button-primary' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.wp-core-ui .button-primary' ),
] );
};
/**
* Set checkbox.
*
* @param {string} selector
*/
const setCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus !== true ) {
await page.click( selector );
}
};
/**
* Unset checkbox.
*
* @param {string} selector
*/
const unsetCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus === true ) {
await page.click( selector );
}
};
/**
* Wait for UI blocking to end.
*/
const uiUnblocked = async () => {
await page.waitForFunction( () => ! Boolean( document.querySelector( '.blockUI' ) ) );
};
/**
* Publish, verify that item was published. Trash, verify that item was trashed.
*
* @param {string} button (Publish)
* @param {string} publishNotice
* @param {string} publishVerification
* @param {string} trashVerification
*/
const verifyPublishAndTrash = async ( button, publishNotice, publishVerification, trashVerification ) => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish
await expect( page ).toClick( button );
await page.waitForSelector( publishNotice );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: publishVerification } );
if ( button === '.order_actions li .save_order' ) {
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Processing' } );
await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content',
{
text: 'Order status changed from Pending payment to Processing.',
}
);
}
// Trash
await expect( page ).toClick( 'a', { text: "Move to Trash" } );
await page.waitForSelector( '#message' );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: trashVerification } );
};
/**
* Verify that checkbox is set.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsSet = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).toBe( true );
};
/**
* Verify that checkbox is unset.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsUnset = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).not.toBe( true );
};
/**
* Verify the value of input field once it was saved (can be used for radio buttons verification as well).
*
* @param {string} selector Selector of the input field that needs to be verified.
* @param {string} value Value of the input field that needs to be verified.
*/
const verifyValueOfInputField = async( selector, value ) => {
await page.focus( selector );
const field = await page.$( selector );
const fieldValue = ( await ( await field.getProperty( 'value' ) ).jsonValue() );
await expect( fieldValue ).toBe( value );
};
module.exports = {
...flows,
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField,
};

View File

@ -1,60 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { StoreOwnerFlow } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Add New Coupon Page', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'can create new coupon', () => {
const flowArgs = {
baseUrl: config.get( 'url' ),
username: config.get( 'users.admin.username' ),
password: config.get( 'users.admin.password' )
};
const storeOwner = new StoreOwnerFlow( driver, flowArgs );
const couponPage = storeOwner.openNewCoupon();
couponPage.setTitle( 'code-' + new Date().getTime().toString() );
couponPage.setDescription( 'test coupon' );
const couponData = couponPage.components.metaBoxCouponData;
const generalPanel = couponData.clickTab( 'General' );
generalPanel.selectDiscountType( 'Cart Discount' );
generalPanel.setCouponAmount( '100' );
couponPage.publish();
assert.eventually.ok( couponPage.hasNotice( 'Coupon updated.' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,61 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { StoreOwnerFlow } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Add New Order Page', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'can create new order', () => {
const flowArgs = {
baseUrl: config.get( 'url' ),
username: config.get( 'users.admin.username' ),
password: config.get( 'users.admin.password' )
};
const storeOwner = new StoreOwnerFlow( driver, flowArgs );
const orderPage = storeOwner.openNewOrder();
const orderData = orderPage.components.metaBoxOrderData;
orderData.selectOrderStatus( 'Processing' );
orderData.setOrderDate( '2016-12-13' );
orderData.setOrderDateHour( '18' );
orderData.setOrderDateMinute( '55' );
orderPage.components.metaBoxOrderActions.saveOrder();
assert.eventually.ok( orderData.hasOrderStatus( 'Processing' ) );
const orderNotes = orderPage.components.metaBoxOrderNotes;
assert.eventually.ok( orderNotes.hasNote( 'Order status changed from Pending payment to Processing.' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,138 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { WPLogin } from 'wp-e2e-page-objects';
import { WPAdminProductNew } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'Add New Product Page', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
// login
test.before( () => {
const wpLogin = new WPLogin( driver, { url: manager.getPageUrl( '/wp-login.php' ) } );
wpLogin.login( config.get( 'users.admin.username' ), config.get( 'users.admin.password' ) );
} );
test.it( 'can create simple virtual product titled "Simple Product" with regular price $9.99', () => {
const product = new WPAdminProductNew( driver, { url: manager.getPageUrl( '/wp-admin/post-new.php?post_type=product' ) } );
product.setTitle( 'Simple Product' );
const productData = product.components.metaBoxProductData;
productData.selectProductType( 'Simple product' );
productData.checkVirtual();
const panelGeneral = productData.clickTab( 'General' );
panelGeneral.setRegularPrice( '9.99' );
product.publish();
assert.eventually.ok( product.hasNotice( 'Product published.' ) );
product.moveToTrash();
assert.eventually.ok( product.hasNotice( '1 product moved to the Trash.' ) );
} );
test.it( 'can create product with variations', () => {
const product = new WPAdminProductNew( driver, { url: manager.getPageUrl( '/wp-admin/post-new.php?post_type=product' ) } );
product.setTitle( 'Variable Product with Two Variations' );
const productData = product.components.metaBoxProductData;
productData.selectProductType( 'Variable product' );
const panelAttributes = productData.clickTab( 'Attributes' );
panelAttributes.selectAttribute( 'Custom product attribute' );
const attr1 = panelAttributes.add();
assert.eventually.ok( attr1.displayed() );
attr1.setName( 'attr #1' );
attr1.checkVisibleOnTheProductPage();
attr1.checkUsedForVariations();
attr1.setValue( 'val1 | val2' );
attr1.toggle();
const attr2 = panelAttributes.add();
assert.eventually.ok( attr2.displayed() );
attr2.setName( 'attr #2' );
attr2.checkVisibleOnTheProductPage();
attr2.checkUsedForVariations();
attr2.setValue( 'val1 | val2' );
const attr3 = panelAttributes.add();
assert.eventually.ok( attr3.displayed() );
attr3.setName( 'attr #3' );
attr3.checkVisibleOnTheProductPage();
attr3.checkUsedForVariations();
attr3.setValue( 'val1 | val2' );
attr3.toggle(); attr2.toggle();
panelAttributes.saveAttributes();
const panelVaritions = productData.clickTab( 'Variations' );
panelVaritions.selectAction( 'Create variations from all attributes' );
panelVaritions.go();
// We start at two because of a new message introduced at the top of the panel.
const var1 = panelVaritions.getRow( 2 );
var1.toggle();
var1.checkEnabled();
var1.checkVirtual();
var1.setRegularPrice( '9.99' );
const var2 = panelVaritions.getRow( 3 );
var2.toggle();
var2.checkEnabled();
var2.checkVirtual();
var2.setRegularPrice( '11.99' );
const var3 = panelVaritions.getRow( 4 );
var3.toggle();
var3.checkEnabled();
var3.checkManageStock();
var3.setRegularPrice( '20' );
var3.setWeight( '200' );
var3.setDimensionLength( '10' );
var3.setDimensionWidth( '20' );
var3.setDimensionHeight( '15' );
panelVaritions.saveChanges();
helper.scrollUp( driver );
helper.scrollUp( driver );
helper.scrollUp( driver );
product.publish();
assert.eventually.ok( product.hasNotice( 'Product published.' ) );
product.moveToTrash();
assert.eventually.ok( product.hasNotice( '1 product moved to the Trash.' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,80 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { WPLogin } from 'wp-e2e-page-objects';
import { WPAdminWCSettingsGeneral } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'WooCommerce General Settings', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
// login
test.before( () => {
const wpLogin = new WPLogin( driver, { url: manager.getPageUrl( '/wp-login.php' ) } );
wpLogin.login( config.get( 'users.admin.username' ), config.get( 'users.admin.password' ) );
} );
test.it( 'can update settings', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=general' ) };
const settings = new WPAdminWCSettingsGeneral( driver, settingsArgs );
assert.eventually.ok( settings.hasActiveTab( 'General' ) );
// Set selling location to all countries first, so we can choose california
// as base location.
settings.selectSellingLocation( 'Sell to all countries' );
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
// Set base location with state CA.
settings.selectBaseLocation( 'california', 'United States (US) — California' );
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
// Set selling location to specific countries first, so we can choose
// U.S as base location (without state). This will makes specific
// countries option appears.
settings.selectSellingLocation( 'Sell to specific countries' );
settings.removeChoiceInSellToSpecificCountries( 'United States (US)' );
settings.setSellToSpecificCountries( 'united states', 'United States (US)' );
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
// Set currency options.
settings.setThousandSeparator( ',' );
settings.setDecimalSeparator( '.' );
settings.setNumberOfDecimals( '2' );
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,65 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { WPLogin } from 'wp-e2e-page-objects';
import { WPAdminWCSettingsProductsDownloadable } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'WooCommerce Products > Downloadable Products Settings', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
// login
test.before( () => {
const wpLogin = new WPLogin( driver, { url: manager.getPageUrl( '/wp-login.php' ) } );
wpLogin.login( config.get( 'users.admin.username' ), config.get( 'users.admin.password' ) );
} );
test.it( 'can update settings', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=products&section=downloadable' ) };
const settings = new WPAdminWCSettingsProductsDownloadable( driver, settingsArgs );
assert.eventually.ok( settings.hasActiveTab( 'Products' ) );
assert.eventually.ok( settings.hasActiveSubTab( 'Downloadable products' ) );
settings.selectFileDownloadMethod( 'Redirect only' );
settings.checkDownloadsRequireLogin();
settings.checkGrantAccessAfterPayment();
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
settings.selectFileDownloadMethod( 'Force downloads' );
settings.uncheckDownloadsRequireLogin();
settings.uncheckGrantAccessAfterPayment();
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -1,109 +0,0 @@
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { WPLogin } from 'wp-e2e-page-objects';
import { WPAdminWCSettingsTax, WPAdminWCSettingsTaxRates } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'WooCommerce Tax Settings', function() {
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
// login
test.before( () => {
const wpLogin = new WPLogin( driver, { url: manager.getPageUrl( '/wp-login.php' ) } );
wpLogin.login( config.get( 'users.admin.username' ), config.get( 'users.admin.password' ) );
} );
test.it( 'can set tax options', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=tax' ) };
const settings = new WPAdminWCSettingsTax( driver, settingsArgs );
assert.eventually.ok( settings.hasActiveTab( 'Tax' ) );
settings.selectPricesEnteredWithNoTax();
settings.selectCalculateTaxBasedOn( 'Customer shipping address' );
settings.selectShippingTaxClass( 'Standard' );
settings.uncheckRounding();
settings.selectDisplayPricesInTheShop( 'Excluding tax' );
settings.selectDisplayPricesDuringCartAndCheckout( 'Including tax' );
settings.selectDisplayTaxTotals( 'As a single total' );
settings.saveChanges();
assert.eventually.ok( settings.hasNotice( 'Your settings have been saved.' ) );
} );
test.it( 'can add tax classes', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=tax' ) };
const settings = new WPAdminWCSettingsTax( driver, settingsArgs );
settings.removeAdditionalTaxClasses();
settings.saveChanges();
settings.addAdditionalTaxClass( 'Fancy' );
settings.saveChanges();
assert.eventually.ok( settings.hasSubTab( 'Fancy rates' ) );
} );
test.it( 'can set rate settings', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=tax&section=fancy' ) };
const settings = new WPAdminWCSettingsTaxRates( driver, settingsArgs );
settings.insertRow();
settings.setCountryCode( 1, 'US' );
settings.setStateCode( 1, 'CA' );
settings.setRate( 1, '7.5' );
settings.setTaxName( 1, 'CA State Tax' );
settings.insertRow();
settings.setCountryCode( 2, 'US' );
settings.setRate( 2, '1.5' );
settings.setPriority( 2, '2' );
settings.setTaxName( 2, 'Federal Tax' );
settings.uncheckShipping( 2 );
settings.saveChanges();
settings.removeRow( 2 );
settings.saveChanges();
assert.eventually.ifError( helper.isEventuallyPresentAndDisplayed( driver, settings.getSelector( 2 ), 1000 ) );
} );
test.it( 'can remove tax classes', () => {
const settingsArgs = { url: manager.getPageUrl( '/wp-admin/admin.php?page=wc-settings&tab=tax' ) };
const settings = new WPAdminWCSettingsTax( driver, settingsArgs );
settings.removeAdditionalTaxClass( 'Fancy' );
settings.saveChanges();
assert.eventually.ifError( settings.hasSubTab( 'Fancy rates' ) );
} );
// take screenshot
test.afterEach( function() {
if ( this.currentTest.state === 'failed' ) {
helper.takeScreenshot( manager, this.currentTest );
}
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );