initial e2e-env component, props @jeffstieler
- copied from https://github.com/woocommerce/woocommerce-admin/pull/3717
- 696ed0d1d9
This commit is contained in:
parent
18ff3960b7
commit
c8d643b4ac
|
@ -0,0 +1,15 @@
|
|||
# WordPress container environment
|
||||
WORDPRESS_DB_HOST=db
|
||||
WORDPRESS_DB_NAME=testdb
|
||||
WORDPRESS_DB_USER=wordpress
|
||||
WORDPRESS_DB_PASSWORD=wordpress
|
||||
WORDPRESS_TABLE_PREFIX=wp_
|
||||
WORDPRESS_DEBUG=1
|
||||
|
||||
# WordPress CLI environment
|
||||
WORDPRESS_PORT=8084
|
||||
WORDPRESS_HOST=wordpress-www:80
|
||||
WORDPRESS_TITLE=WooCommerce Core E2E Test Suite
|
||||
WORDPRESS_LOGIN=admin
|
||||
WORDPRESS_PASSWORD=password
|
||||
WORDPRESS_EMAIL=admin@woocommercecoree2etestsuite.com
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
globals: {
|
||||
page: true,
|
||||
browser: true,
|
||||
context: true,
|
||||
jestPuppeteer: true,
|
||||
},
|
||||
plugins: [ 'jest' ],
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -0,0 +1,53 @@
|
|||
version: ~> 1.0
|
||||
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- node_modules
|
||||
- $HOME/.npm
|
||||
- $HOME/.composer/cache
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release\/.*/
|
||||
|
||||
before_install:
|
||||
- timedatectl
|
||||
- nvm install --latest-npm
|
||||
|
||||
before_script:
|
||||
- export PATH="$HOME/.composer/vendor/bin:$PATH"
|
||||
- npm install
|
||||
- |
|
||||
if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
|
||||
phpenv config-rm xdebug.ini
|
||||
else
|
||||
echo "xdebug.ini does not exist"
|
||||
fi
|
||||
- |
|
||||
if [[ ! -z "$WP_VERSION" ]] ; then
|
||||
composer install --no-dev
|
||||
npm explore @woocommerce/e2e-env -- npm run install-wp-tests -- wc_e2e_tests root ' ' localhost $WP_VERSION
|
||||
composer global require "phpunit/phpunit=4.8.*|5.7.*"
|
||||
fi
|
||||
- |
|
||||
if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then
|
||||
composer install
|
||||
fi
|
||||
|
||||
jobs:
|
||||
fast_finish: true
|
||||
include:
|
||||
- name: E2E Tests
|
||||
script:
|
||||
- npm explore @woocommerce/e2e-env -- npm run docker:up
|
||||
- composer install
|
||||
- npm run build
|
||||
- npm explore @woocommerce/e2e-env -- npm run test:e2e
|
|
@ -0,0 +1,3 @@
|
|||
1.0.0 (unreleased)
|
||||
|
||||
- Initial release
|
|
@ -0,0 +1,174 @@
|
|||
# End to End Testing Environment
|
||||
|
||||
A reusable and extendable E2E testing environment for WooCommerce extensions.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @woocommerce/e2e-env --save
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The `@woocommerce/e2e-env` package exports configuration objects that can be consumed in JavaScript config files in your project. Additionally, it contains several files to serve as the base for a Docker container and Travis CI setup.
|
||||
|
||||
### Babel Config
|
||||
|
||||
Extend your project's Babel config to contain the expected presets for E2E testing.
|
||||
|
||||
```js
|
||||
const { babelConfig: e2eBabelConfig } = require( '@woocommerce/e2e-env' );
|
||||
|
||||
module.exports = function( api ) {
|
||||
api.cache( true );
|
||||
|
||||
return {
|
||||
...e2eBabelConfig,
|
||||
presets: [
|
||||
...e2eBabelConfig.presets,
|
||||
'@wordpress/babel-preset-default',
|
||||
],
|
||||
....
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### ES Lint Config
|
||||
|
||||
The E2E environment uses Puppeteer for headless browser testing, which uses certain globals variables. Avoid ES Lint errors by extending the config.
|
||||
|
||||
```js
|
||||
const { esLintConfig: baseConfig } = require( '@woocommerce/e2e-env' );
|
||||
|
||||
module.exports = {
|
||||
...baseConfig,
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
extends: [
|
||||
...baseConfig.extends,
|
||||
'wpcalypso/react',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
],
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
'jsx-a11y',
|
||||
],
|
||||
env: {
|
||||
...baseConfig.env,
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
globals: {
|
||||
...baseConfig.globals,
|
||||
wp: true,
|
||||
wpApiSettings: true,
|
||||
wcSettings: true,
|
||||
},
|
||||
....
|
||||
};
|
||||
```
|
||||
|
||||
### Jest Config
|
||||
|
||||
The E2E environment uses Jest as a test runner. Extending the base config is needed in order for Jest to run your project's test files.
|
||||
|
||||
```js
|
||||
const path = require( 'path' );
|
||||
const { jestConfig: baseE2Econfig } = require( '@woocommerce/e2e-env' );
|
||||
|
||||
module.exports = {
|
||||
...baseE2Econfig,
|
||||
// Specify the path of your project's E2E tests here.
|
||||
roots: [ path.resolve( __dirname, '../specs' ) ],
|
||||
};
|
||||
```
|
||||
|
||||
**NOTE:** Your project's Jest config file is expected to found at: `tests/e2e-tests/config/jest.config.js`.
|
||||
|
||||
### Webpack Config
|
||||
|
||||
The E2E environment provides a `@woocommerce/e2e-tests` alias for easy use of the WooCommerce E2E test helpers.
|
||||
|
||||
```js
|
||||
const { webpackAlias: coreE2EAlias } = require( '@woocommerce/e2e-env' );
|
||||
|
||||
module.exports = {
|
||||
....
|
||||
resolve: {
|
||||
alias: {
|
||||
...coreE2EAlias,
|
||||
....
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Docker Setup
|
||||
|
||||
The E2E environment will look for a `docker-compose.yaml` file in your project root. This will be combined with the base Docker config in the package. This is where you'll map your local project files into the Docker container(s).
|
||||
|
||||
```yaml
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
|
||||
wordpress-www:
|
||||
volumes:
|
||||
# This path is relative to the first config file
|
||||
# which is in node_modules/@woocommerce/e2e-env
|
||||
- "../../../:/var/www/html/wp-content/plugins/your-project-here"
|
||||
|
||||
wordpress-cli:
|
||||
volumes:
|
||||
- "../../../:/var/www/html/wp-content/plugins/your-project-here"
|
||||
```
|
||||
|
||||
#### Docker Container Initialization Script
|
||||
|
||||
You can provide an initialization script that will run in the WP-CLI Docker container. Place an executable file at `tests/e2e-tests/docker/initialize.sh` in your project and it will be copied into the container and executed. While you can run any commands you wish, the intent here is to use WP-CLI to set up your testing environment. E.g.:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
echo "Initializing WooCommerce E2E"
|
||||
|
||||
wp plugin install woocommerce --activate
|
||||
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.'
|
||||
```
|
||||
|
||||
### Travis CI
|
||||
|
||||
The E2E environment includes a base `.travis.yml` file that sets up a WordPress testing environment and defines a job for running E2E tests. Opt in to [Travis Build Config Imports](https://docs.travis-ci.com/user/build-config-imports/) using `version: ~> 1.0` in your config file.
|
||||
|
||||
```yaml
|
||||
version: ~> 1.0
|
||||
|
||||
import:
|
||||
- source: node_modules/@woocommerce/e2e-env/.travis.yml
|
||||
mode: deep_merge_prepend # Merge the package config first.
|
||||
|
||||
....
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Start Docker
|
||||
|
||||
```bash
|
||||
npm explore @woocommerce/e2e-env -- npm run docker:up
|
||||
```
|
||||
|
||||
Run E2E Tests
|
||||
|
||||
```bash
|
||||
npm explore @woocommerce/e2e-env -- npm run test:e2e
|
||||
npm explore @woocommerce/e2e-env -- npm run test:e2e-dev
|
||||
```
|
||||
|
||||
Stop Docker
|
||||
|
||||
```bash
|
||||
npm explore @woocommerce/e2e-env -- npm run docker:down
|
||||
```
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { spawnSync } = require( 'child_process' );
|
||||
const program = require( 'commander' );
|
||||
const path = require( 'path' );
|
||||
const fs = require( 'fs' );
|
||||
const getAppPath = require( '../utils/app-root' );
|
||||
|
||||
const dockerArgs = [];
|
||||
let command = '';
|
||||
|
||||
program
|
||||
.command( 'up', 'Start and build the Docker container' )
|
||||
.command( 'down', 'Stop the Docker container and remove volumes' )
|
||||
.action( ( cmd, options ) => {
|
||||
if ( 'up' === options[ 0 ] ) {
|
||||
command = 'up';
|
||||
dockerArgs.push( 'up', '--build', '-d' );
|
||||
}
|
||||
|
||||
if ( 'down' === options[ 0 ] ) {
|
||||
command = 'down';
|
||||
dockerArgs.push( 'down', '-v' );
|
||||
}
|
||||
} )
|
||||
.parse( process.argv );
|
||||
|
||||
const appPath = getAppPath();
|
||||
const envVars = {};
|
||||
|
||||
if ( appPath ) {
|
||||
// Look for a Docker compose file in the dependent app's path.
|
||||
const appDockerComposefile = path.resolve( appPath, 'docker-compose.yaml' );
|
||||
|
||||
// Specify the app's Docker compose file in our command.
|
||||
if ( fs.existsSync( appDockerComposefile ) ) {
|
||||
dockerArgs.unshift( '-f', appDockerComposefile );
|
||||
}
|
||||
|
||||
if ( 'up' === command ) {
|
||||
// Look for an initialization script in the dependent app.
|
||||
const appInitFile = path.resolve( appPath, 'tests/e2e-tests/docker/initialize.sh' );
|
||||
|
||||
// If found, copy it into the wp-cli Docker context so
|
||||
// it gets picked up by the entrypoint script.
|
||||
if ( fs.existsSync( appInitFile ) ) {
|
||||
fs.copyFileSync(
|
||||
appInitFile,
|
||||
path.resolve( __dirname, '../docker/wp-cli/initialize.sh' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Provide an "app name" to use in Docker container names.
|
||||
envVars.APP_NAME = path.basename( appPath );
|
||||
}
|
||||
|
||||
// Ensure that the first Docker compose file loaded is from our local env.
|
||||
dockerArgs.unshift( '-f', path.resolve( __dirname, '../docker-compose.yaml' ) );
|
||||
|
||||
const dockerProcess = spawnSync(
|
||||
'docker-compose',
|
||||
dockerArgs,
|
||||
{
|
||||
stdio: 'inherit',
|
||||
env: Object.assign( {}, process.env, envVars ),
|
||||
}
|
||||
);
|
||||
|
||||
console.log( 'Docker exit code: ' + dockerProcess.status );
|
||||
|
||||
// Pass Docker exit code to npm
|
||||
process.exit( dockerProcess.status );
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { spawnSync } = require( 'child_process' );
|
||||
const program = require( 'commander' );
|
||||
const path = require( 'path' );
|
||||
const fs = require( 'fs' );
|
||||
const getAppPath = require( '../utils/app-root' );
|
||||
|
||||
program
|
||||
.usage( '<file ...> [options]' )
|
||||
.option( '--dev', 'Development mode' )
|
||||
.parse( process.argv );
|
||||
|
||||
const testEnvVars = {
|
||||
NODE_ENV: 'test:e2e',
|
||||
JEST_PUPPETEER_CONFIG: path.resolve(
|
||||
__dirname,
|
||||
'../config/jest-puppeteer.config.js'
|
||||
),
|
||||
NODE_CONFIG_DIR: path.resolve(
|
||||
__dirname,
|
||||
'../config'
|
||||
),
|
||||
};
|
||||
|
||||
let jestCommand = 'jest';
|
||||
const jestArgs = [
|
||||
'--maxWorkers=1',
|
||||
'--rootDir=./',
|
||||
'--verbose',
|
||||
...program.args,
|
||||
];
|
||||
|
||||
if ( program.dev ) {
|
||||
testEnvVars.JEST_PUPPETEER_CONFIG = path.resolve(
|
||||
__dirname,
|
||||
'../config/jest-puppeteer.dev.config.js'
|
||||
);
|
||||
jestCommand = 'npx';
|
||||
jestArgs.unshift( 'ndb', 'jest' );
|
||||
}
|
||||
|
||||
const envVars = Object.assign( {}, process.env, testEnvVars );
|
||||
|
||||
const appPath = getAppPath();
|
||||
let configPath = path.resolve( __dirname, '../config/jest.config.js' );
|
||||
|
||||
// Look for a Jest config in the dependent app's path.
|
||||
if ( appPath ) {
|
||||
const appConfig = path.resolve( appPath, 'tests/e2e-tests/config/jest.config.js' );
|
||||
|
||||
if ( fs.existsSync( appConfig ) ) {
|
||||
configPath = appConfig;
|
||||
}
|
||||
}
|
||||
|
||||
jestArgs.push( '--config=' + configPath );
|
||||
|
||||
const jestProcess = spawnSync(
|
||||
jestCommand,
|
||||
jestArgs,
|
||||
{
|
||||
stdio: 'inherit',
|
||||
env: envVars,
|
||||
}
|
||||
);
|
||||
|
||||
console.log( 'Jest exit code: ' + jestProcess.status );
|
||||
|
||||
// Pass Jest exit code to npm
|
||||
process.exit( jestProcess.status );
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DB_NAME=$1
|
||||
DB_USER=$2
|
||||
# Trim whitespace to work around an issue supplying an empty string through both `npm explore` and `npm run`.
|
||||
DB_PASS=${3//[[:blank:]]/}
|
||||
DB_HOST=${4-localhost}
|
||||
WP_VERSION=${5-latest}
|
||||
SKIP_DB_CREATE=${6-false}
|
||||
|
||||
# directories
|
||||
TMPDIR=${TMPDIR-/tmp}
|
||||
TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
|
||||
WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
|
||||
WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
|
||||
|
||||
download() {
|
||||
if [ `which curl` ]; then
|
||||
curl -s "$1" > "$2";
|
||||
elif [ `which wget` ]; then
|
||||
wget -nv -O "$2" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
WP_TESTS_TAG="branches/$WP_VERSION"
|
||||
elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
|
||||
# version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
|
||||
WP_TESTS_TAG="tags/${WP_VERSION%??}"
|
||||
else
|
||||
WP_TESTS_TAG="tags/$WP_VERSION"
|
||||
fi
|
||||
elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
WP_TESTS_TAG="trunk"
|
||||
else
|
||||
# http serves a single offer, whereas https serves multiple. we only want one
|
||||
download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
|
||||
grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
|
||||
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
|
||||
if [[ -z "$LATEST_VERSION" ]]; then
|
||||
echo "Latest WordPress version could not be found"
|
||||
exit 1
|
||||
fi
|
||||
WP_TESTS_TAG="tags/$LATEST_VERSION"
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
install_wp() {
|
||||
|
||||
if [ -d $WP_CORE_DIR ]; then
|
||||
return;
|
||||
fi
|
||||
|
||||
mkdir -p $WP_CORE_DIR
|
||||
|
||||
if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
mkdir -p $TMPDIR/wordpress-nightly
|
||||
download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
|
||||
unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
|
||||
mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
|
||||
else
|
||||
if [ $WP_VERSION == 'latest' ]; then
|
||||
local ARCHIVE_NAME='latest'
|
||||
elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
|
||||
# https serves multiple offers, whereas http serves single.
|
||||
download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
|
||||
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
|
||||
# version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
|
||||
LATEST_VERSION=${WP_VERSION%??}
|
||||
else
|
||||
# otherwise, scan the releases and get the most up to date minor version of the major release
|
||||
local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
|
||||
LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
|
||||
fi
|
||||
if [[ -z "$LATEST_VERSION" ]]; then
|
||||
local ARCHIVE_NAME="wordpress-$WP_VERSION"
|
||||
else
|
||||
local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
|
||||
fi
|
||||
else
|
||||
local ARCHIVE_NAME="wordpress-$WP_VERSION"
|
||||
fi
|
||||
download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
|
||||
tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
|
||||
fi
|
||||
|
||||
download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
|
||||
}
|
||||
|
||||
install_test_suite() {
|
||||
# portable in-place argument for both GNU sed and Mac OSX sed
|
||||
if [[ $(uname -s) == 'Darwin' ]]; then
|
||||
local ioption='-i .bak'
|
||||
else
|
||||
local ioption='-i'
|
||||
fi
|
||||
|
||||
# set up testing suite if it doesn't yet exist
|
||||
if [ ! -d $WP_TESTS_DIR ]; then
|
||||
# set up testing suite
|
||||
mkdir -p $WP_TESTS_DIR
|
||||
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
|
||||
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
|
||||
fi
|
||||
|
||||
if [ ! -f wp-tests-config.php ]; then
|
||||
download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
# remove all forward slashes in the end
|
||||
WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
|
||||
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_db() {
|
||||
|
||||
if [ ${SKIP_DB_CREATE} = "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# parse DB_HOST for port or socket references
|
||||
local PARTS=(${DB_HOST//\:/ })
|
||||
local DB_HOSTNAME=${PARTS[0]};
|
||||
local DB_SOCK_OR_PORT=${PARTS[1]};
|
||||
local EXTRA=""
|
||||
|
||||
if ! [ -z $DB_HOSTNAME ] ; then
|
||||
if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
|
||||
elif ! [ -z $DB_SOCK_OR_PORT ] ; then
|
||||
EXTRA=" --socket=$DB_SOCK_OR_PORT"
|
||||
elif ! [ -z $DB_HOSTNAME ] ; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
|
||||
fi
|
||||
fi
|
||||
|
||||
# create database
|
||||
mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||
}
|
||||
|
||||
install_deps() {
|
||||
|
||||
# Script Variables
|
||||
BRANCH=$TRAVIS_BRANCH
|
||||
REPO=$TRAVIS_REPO_SLUG
|
||||
WORKING_DIR="$PWD"
|
||||
|
||||
if [ "$TRAVIS_PULL_REQUEST_BRANCH" != "" ]; then
|
||||
BRANCH=$TRAVIS_PULL_REQUEST_BRANCH
|
||||
REPO=$TRAVIS_PULL_REQUEST_SLUG
|
||||
fi
|
||||
|
||||
# checkout dev version of woocommerce
|
||||
cd "$WP_CORE_DIR/wp-content/plugins"
|
||||
git clone --depth 1 "https://github.com/woocommerce/woocommerce.git"
|
||||
# install dependencies
|
||||
cd woocommerce
|
||||
npm install
|
||||
composer install --no-dev
|
||||
|
||||
# Back to original dir
|
||||
cd "$WORKING_DIR"
|
||||
}
|
||||
|
||||
install_wp
|
||||
install_test_suite
|
||||
install_db
|
||||
install_deps
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"url": "http://localhost:8084/",
|
||||
"users": {
|
||||
"admin": {
|
||||
"username": "admin",
|
||||
"password": "password"
|
||||
},
|
||||
"customer": {
|
||||
"username": "customer",
|
||||
"password": "password"
|
||||
}
|
||||
},
|
||||
"products": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
global.process.env = {
|
||||
...global.process.env,
|
||||
// Gutenberg test util functions expect the test url to be at :8889, we change it to 8084.
|
||||
WP_BASE_URL: 'http://localhost:8084',
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
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;
|
|
@ -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',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
/** @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=chrome' ],
|
||||
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',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
module.exports = {
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// An array of file extensions your modules use
|
||||
moduleFileExtensions: [ 'js' ],
|
||||
|
||||
moduleNameMapper: {
|
||||
'@woocommerce/e2e-tests/(.*)':
|
||||
'<rootDir>/node_modules/woocommerce/tests/e2e-tests/$1',
|
||||
},
|
||||
|
||||
preset: 'jest-puppeteer',
|
||||
|
||||
setupFiles: [ '<rootDir>/config/env.setup.js' ],
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework
|
||||
// before each test
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/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>/config/jest-custom-sequencer.js',
|
||||
|
||||
transformIgnorePatterns: [ 'node_modules/(?!(woocommerce)/)' ],
|
||||
};
|
|
@ -0,0 +1,271 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
clearLocalStorage,
|
||||
enablePageDialogAccept,
|
||||
isOfflineMode,
|
||||
setBrowserViewport,
|
||||
switchUserToAdmin,
|
||||
switchUserToTest,
|
||||
visitAdminPage,
|
||||
} from '@wordpress/e2e-test-utils';
|
||||
|
||||
//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 );
|
||||
|
||||
/**
|
||||
* Array of page event tuples of [ eventName, handler ].
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
const pageEvents = [];
|
||||
/**
|
||||
* Set of console logging types observed to protect against unexpected yet
|
||||
* handled (i.e. not catastrophic) errors or warnings. Each key corresponds
|
||||
* to the Puppeteer ConsoleMessage type, its value the corresponding function
|
||||
* on the console global object.
|
||||
*
|
||||
* @type {Object<string,string>}
|
||||
*/
|
||||
const OBSERVED_CONSOLE_MESSAGE_TYPES = {
|
||||
warning: 'warn',
|
||||
error: 'error',
|
||||
};
|
||||
|
||||
async function setupBrowser() {
|
||||
await clearLocalStorage();
|
||||
await setBrowserViewport( 'large' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the post listing screen and bulk-trashes any posts which exist.
|
||||
*
|
||||
* @return {Promise} Promise resolving once posts have been trashed.
|
||||
*/
|
||||
async function trashExistingPosts() {
|
||||
await switchUserToAdmin();
|
||||
// Visit `/wp-admin/edit.php` so we can see a list of posts and delete them.
|
||||
await visitAdminPage( 'edit.php' );
|
||||
|
||||
// If this selector doesn't exist there are no posts for us to delete.
|
||||
const bulkSelector = await page.$( '#bulk-action-selector-top' );
|
||||
if ( ! bulkSelector ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Select all posts.
|
||||
await page.waitForSelector( '#cb-select-all-1' );
|
||||
await page.click( '#cb-select-all-1' );
|
||||
// Select the "bulk actions" > "trash" option.
|
||||
await page.select( '#bulk-action-selector-top', 'trash' );
|
||||
// Submit the form to send all draft/scheduled/published posts to the trash.
|
||||
await page.click( '#doaction' );
|
||||
await page.waitForXPath(
|
||||
'//*[contains(@class, "updated notice")]/p[contains(text(), "moved to the Trash.")]'
|
||||
);
|
||||
await switchUserToTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the product listing screen and bulk-trashes any product which exist.
|
||||
*
|
||||
* @return {Promise} Promise resolving once products have been trashed.
|
||||
*/
|
||||
async function trashExistingProducts() {
|
||||
await switchUserToAdmin();
|
||||
// Visit `/wp-admin/edit.php?post_type=product` so we can see a list of products and delete them.
|
||||
await visitAdminPage( 'edit.php', 'post_type=product' );
|
||||
|
||||
// If this selector doesn't exist there are no products for us to delete.
|
||||
const bulkSelector = await page.$( '#bulk-action-selector-top' );
|
||||
if ( ! bulkSelector ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Select all products.
|
||||
await page.waitForSelector( '#cb-select-all-1' );
|
||||
await page.click( '#cb-select-all-1' );
|
||||
// Select the "bulk actions" > "trash" option.
|
||||
await page.select( '#bulk-action-selector-top', 'trash' );
|
||||
// Submit the form to send all draft/scheduled/published posts to the trash.
|
||||
await page.click( '#doaction' );
|
||||
await page.waitForXPath(
|
||||
'//*[contains(@class, "updated notice")]/p[contains(text(), "moved to the Trash.")]'
|
||||
);
|
||||
await switchUserToTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to woocommerce import page and imports sample products.
|
||||
*
|
||||
* @return {Promise} Promise resolving once products have been imported.
|
||||
*/
|
||||
async function importSampleProducts() {
|
||||
await switchUserToAdmin();
|
||||
// Visit Import Products page.
|
||||
await visitAdminPage(
|
||||
'edit.php',
|
||||
'post_type=product&page=product_importer'
|
||||
);
|
||||
await page.click( 'a.woocommerce-importer-toggle-advanced-options' );
|
||||
await page.focus( '#woocommerce-importer-file-url' );
|
||||
// local path for sample data that is included with woo.
|
||||
await page.keyboard.type(
|
||||
'wp-content/plugins/woocommerce/sample-data/sample_products.csv'
|
||||
);
|
||||
await page.click( '.wc-actions .button-next' );
|
||||
await page.waitForSelector( '.wc-importer-mapping-table' );
|
||||
await page.select(
|
||||
'.wc-importer-mapping-table tr:nth-child(29) select',
|
||||
''
|
||||
);
|
||||
await page.click( '.wc-actions .button-next' );
|
||||
await page.waitForXPath(
|
||||
"//*[@class='woocommerce-importer-done' and contains(., 'Import complete! ')]"
|
||||
);
|
||||
await switchUserToTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event listener to the page to handle additions of page event
|
||||
* handlers, to assure that they are removed at test teardown.
|
||||
*/
|
||||
function capturePageEventsForTearDown() {
|
||||
page.on( 'newListener', ( eventName, listener ) => {
|
||||
pageEvents.push( [ eventName, listener ] );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all bound page event handlers.
|
||||
*/
|
||||
function removePageEvents() {
|
||||
pageEvents.forEach( ( [ eventName, handler ] ) => {
|
||||
page.removeListener( eventName, handler );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a page event handler to emit uncaught exception to process if one of
|
||||
* the observed console logging types is encountered.
|
||||
*/
|
||||
function observeConsoleLogging() {
|
||||
page.on( 'console', ( message ) => {
|
||||
const type = message.type();
|
||||
if ( ! OBSERVED_CONSOLE_MESSAGE_TYPES.hasOwnProperty( type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let text = message.text();
|
||||
|
||||
// An exception is made for _blanket_ deprecation warnings: Those
|
||||
// which log regardless of whether a deprecated feature is in use.
|
||||
if ( text.includes( 'This is a global warning' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A chrome advisory warning about SameSite cookies is informational
|
||||
// about future changes, tracked separately for improvement in core.
|
||||
//
|
||||
// See: https://core.trac.wordpress.org/ticket/37000
|
||||
// See: https://www.chromestatus.com/feature/5088147346030592
|
||||
// See: https://www.chromestatus.com/feature/5633521622188032
|
||||
if (
|
||||
text.includes( 'A cookie associated with a cross-site resource' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Viewing posts on the front end can result in this error, which
|
||||
// has nothing to do with Gutenberg.
|
||||
if ( text.includes( 'net::ERR_UNKNOWN_URL_SCHEME' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Network errors are ignored only if we are intentionally testing
|
||||
// offline mode.
|
||||
if (
|
||||
text.includes( 'net::ERR_INTERNET_DISCONNECTED' ) &&
|
||||
isOfflineMode()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// As of WordPress 5.3.2 in Chrome 79, navigating to the block editor
|
||||
// (Posts > Add New) will display a console warning about
|
||||
// non - unique IDs.
|
||||
// See: https://core.trac.wordpress.org/ticket/23165
|
||||
if ( text.includes( 'elements with non-unique id #_wpnonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// As of WordPress 5.3.2 in Chrome 79, navigating to the block editor
|
||||
// (Posts > Add New) will display a console warning about
|
||||
// non - unique IDs.
|
||||
// See: https://core.trac.wordpress.org/ticket/23165
|
||||
if ( text.includes( 'elements with non-unique id #_wpnonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logFunction = OBSERVED_CONSOLE_MESSAGE_TYPES[ type ];
|
||||
|
||||
// As of Puppeteer 1.6.1, `message.text()` wrongly returns an object of
|
||||
// type JSHandle for error logging, instead of the expected string.
|
||||
//
|
||||
// See: https://github.com/GoogleChrome/puppeteer/issues/3397
|
||||
//
|
||||
// The recommendation there to asynchronously resolve the error value
|
||||
// upon a console event may be prone to a race condition with the test
|
||||
// completion, leaving a possibility of an error not being surfaced
|
||||
// correctly. Instead, the logic here synchronously inspects the
|
||||
// internal object shape of the JSHandle to find the error text. If it
|
||||
// cannot be found, the default text value is used instead.
|
||||
text = get(
|
||||
message.args(),
|
||||
[ 0, '_remoteObject', 'description' ],
|
||||
text
|
||||
);
|
||||
|
||||
// Disable reason: We intentionally bubble up the console message
|
||||
// which, unless the test explicitly anticipates the logging via
|
||||
// @wordpress/jest-console matchers, will cause the intended test
|
||||
// failure.
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console[ logFunction ]( text );
|
||||
} );
|
||||
}
|
||||
|
||||
// Before every test suite run, delete all content created by the test. This ensures
|
||||
// other posts/comments/etc. aren't dirtying tests and tests don't depend on
|
||||
// each other's side-effects.
|
||||
beforeAll( async () => {
|
||||
capturePageEventsForTearDown();
|
||||
enablePageDialogAccept();
|
||||
observeConsoleLogging();
|
||||
await trashExistingPosts();
|
||||
await trashExistingProducts();
|
||||
await setupBrowser();
|
||||
await importSampleProducts();
|
||||
} );
|
||||
|
||||
afterEach( async () => {
|
||||
await setupBrowser();
|
||||
} );
|
||||
|
||||
afterAll( () => {
|
||||
removePageEvents();
|
||||
} );
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,58 @@
|
|||
version: '3.3'
|
||||
|
||||
services:
|
||||
|
||||
db:
|
||||
container_name: "${APP_NAME}_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-www:
|
||||
container_name: "${APP_NAME}_wordpress-www"
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./docker/wordpress
|
||||
ports:
|
||||
- ${WORDPRESS_PORT}: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:
|
||||
container_name: "${APP_NAME}_wordpress-cli"
|
||||
depends_on:
|
||||
- db
|
||||
- wordpress-www
|
||||
build:
|
||||
context: ./docker/wp-cli
|
||||
restart: on-failure
|
||||
environment:
|
||||
WORDPRESS_PORT: 8084
|
||||
WORDPRESS_HOST: wordpress-www:80
|
||||
WORDPRESS_TITLE: "WooCommerce Core E2E Test Suite"
|
||||
WORDPRESS_LOGIN: admin
|
||||
WORDPRESS_PASSWORD: password
|
||||
WORDPRESS_EMAIL: "admin@woocommercecoree2etestsuite.com"
|
||||
DOMAIN_NAME:
|
||||
|
||||
volumes:
|
||||
- wordpress:/var/www/html
|
||||
|
||||
volumes:
|
||||
db:
|
||||
wordpress:
|
|
@ -0,0 +1 @@
|
|||
FROM wordpress:5.3
|
|
@ -0,0 +1,22 @@
|
|||
FROM wordpress:cli-php7.4
|
||||
|
||||
USER root
|
||||
|
||||
COPY wait-for-it.sh /usr/local/bin/wait-for-it
|
||||
RUN chown xfs:xfs /usr/local/bin/wait-for-it && \
|
||||
chmod +x /usr/local/bin/wait-for-it
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chown xfs:xfs /usr/local/bin/entrypoint.sh && \
|
||||
chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
RUN chown xfs:xfs /home/www-data
|
||||
|
||||
COPY initialize.sh /usr/local/bin/initialize.sh
|
||||
RUN chown xfs:xfs /usr/local/bin/initialize.sh && \
|
||||
chmod +x /usr/local/bin/initialize.sh
|
||||
|
||||
USER xfs
|
||||
RUN mkdir /home/www-data/.wp-cli && echo "path: /var/www/html" > /home/www-data/.wp-cli/config.yml
|
||||
USER root
|
||||
ENTRYPOINT ["entrypoint.sh"]
|
|
@ -0,0 +1,65 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
declare -p WORDPRESS_HOST
|
||||
wait-for-it ${WORDPRESS_HOST} -t 120
|
||||
|
||||
## if file exists then exit early because initialization already happened.
|
||||
if [ -f /var/www/html/.initialized ]
|
||||
then
|
||||
echo "The environment has already been initialized."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
chown xfs:xfs /var/www/html/wp-content
|
||||
chown xfs:xfs /var/www/html/wp-content/plugins
|
||||
|
||||
## switch user
|
||||
if [ $UID -eq 0 ]; then
|
||||
user=xfs
|
||||
dir=/var/www/html
|
||||
cd "$dir"
|
||||
exec su -s /bin/bash "$user" "$0" -- "$@"
|
||||
# nothing will be executed beyond that line,
|
||||
# because exec replaces running process with the new one
|
||||
fi
|
||||
|
||||
declare -p WORDPRESS_PORT
|
||||
[[ "${WORDPRESS_PORT}" == 80 ]] && \
|
||||
URL="http://localhost" || \
|
||||
URL="http://localhost:${WORDPRESS_PORT}"
|
||||
|
||||
if $(wp core is-installed);
|
||||
then
|
||||
echo "Wordpress is already installed..."
|
||||
else
|
||||
declare -p WORDPRESS_TITLE >/dev/null
|
||||
declare -p WORDPRESS_LOGIN >/dev/null
|
||||
declare -p WORDPRESS_PASSWORD >/dev/null
|
||||
declare -p WORDPRESS_EMAIL >/dev/null
|
||||
echo "Installing wordpress..."
|
||||
wp core install \
|
||||
--url=${URL} \
|
||||
--title="$WORDPRESS_TITLE" \
|
||||
--admin_user=${WORDPRESS_LOGIN} \
|
||||
--admin_password=${WORDPRESS_PASSWORD} \
|
||||
--admin_email=${WORDPRESS_EMAIL} \
|
||||
--skip-email
|
||||
fi
|
||||
|
||||
## Check for an initialization script.
|
||||
declare -r INIT_SCRIPT=$(command -v initialize.sh)
|
||||
|
||||
if [[ -x ${INIT_SCRIPT} ]]; then
|
||||
. "$INIT_SCRIPT"
|
||||
fi
|
||||
|
||||
declare -r CURRENT_DOMAIN=$(wp option get siteurl)
|
||||
|
||||
if ! [[ ${CURRENT_DOMAIN} == ${URL} ]]; then
|
||||
echo "Replacing ${CURRENT_DOMAIN} with ${URL} in database..."
|
||||
wp search-replace ${CURRENT_DOMAIN} ${URL}
|
||||
fi
|
||||
|
||||
echo "Visit $(wp option get siteurl)"
|
||||
touch /var/www/html/.initialized
|
|
@ -0,0 +1,208 @@
|
|||
#!/usr/bin/env bash
|
||||
#source https://github.com/vishnubob/wait-for-it/pull/81
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Original work Copyright (c) 2016 Giles Hall: wait-for-it.sh
|
||||
#Modified work Copyright (c) 2019 iturgeon: wait-for-it.sh
|
||||
#
|
||||
#Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
#this software and associated documentation files (the "Software"), to deal in
|
||||
#the Software without restriction, including without limitation the rights to
|
||||
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
#of the Software, and to permit persons to whom the Software is furnished to do
|
||||
#so, subject to the following conditions:
|
||||
#
|
||||
#The above copyright notice and this permission notice shall be included in all
|
||||
#copies or substantial portions of the Software.
|
||||
#
|
||||
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
#SOFTWARE.
|
||||
|
||||
# Use this script to test if a given TCP host/port are available
|
||||
|
||||
WAITFORIT_cmdname=${0##*/}
|
||||
|
||||
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << USAGE >&2
|
||||
Usage:
|
||||
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
|
||||
-h HOST | --host=HOST Host or IP under test
|
||||
-p PORT | --port=PORT TCP port under test
|
||||
Alternatively, you specify the host and port as host:port
|
||||
-s | --strict Only execute subcommand if the test succeeds
|
||||
-q | --quiet Don't output any status messages
|
||||
-t TIMEOUT | --timeout=TIMEOUT
|
||||
Timeout in seconds, zero for no timeout
|
||||
-- COMMAND ARGS Execute command with args after the test finishes
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
|
||||
else
|
||||
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
|
||||
fi
|
||||
WAITFORIT_start_ts=$(date +%s)
|
||||
while :
|
||||
do
|
||||
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
|
||||
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
|
||||
WAITFORIT_result=$?
|
||||
else
|
||||
(echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
|
||||
WAITFORIT_result=$?
|
||||
fi
|
||||
if [[ $WAITFORIT_result -eq 0 ]]; then
|
||||
WAITFORIT_end_ts=$(date +%s)
|
||||
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
return $WAITFORIT_result
|
||||
}
|
||||
|
||||
wait_for_wrapper()
|
||||
{
|
||||
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
|
||||
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
|
||||
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
|
||||
else
|
||||
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
|
||||
fi
|
||||
WAITFORIT_PID=$!
|
||||
trap "kill -INT -$WAITFORIT_PID" INT
|
||||
wait $WAITFORIT_PID
|
||||
WAITFORIT_RESULT=$?
|
||||
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
|
||||
fi
|
||||
return $WAITFORIT_RESULT
|
||||
}
|
||||
|
||||
# process arguments
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case "$1" in
|
||||
*:* )
|
||||
WAITFORIT_hostport=(${1//:/ })
|
||||
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
|
||||
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
|
||||
shift 1
|
||||
;;
|
||||
--child)
|
||||
WAITFORIT_CHILD=1
|
||||
shift 1
|
||||
;;
|
||||
-q | --quiet)
|
||||
WAITFORIT_QUIET=1
|
||||
shift 1
|
||||
;;
|
||||
-s | --strict)
|
||||
WAITFORIT_STRICT=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
WAITFORIT_HOST="$2"
|
||||
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--host=*)
|
||||
WAITFORIT_HOST="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
-p)
|
||||
WAITFORIT_PORT="$2"
|
||||
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--port=*)
|
||||
WAITFORIT_PORT="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
-t)
|
||||
WAITFORIT_TIMEOUT="$2"
|
||||
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--timeout=*)
|
||||
WAITFORIT_TIMEOUT="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
WAITFORIT_CLI=("$@")
|
||||
break
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echoerr "Unknown argument: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
|
||||
echoerr "Error: you need to provide a host and port to test."
|
||||
usage
|
||||
fi
|
||||
|
||||
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
|
||||
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
|
||||
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
|
||||
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
|
||||
WAITFORIT_ISBUSY=0
|
||||
WAITFORIT_BUSYTIMEFLAG=""
|
||||
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
|
||||
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
|
||||
|
||||
# check to see if we're using busybox?
|
||||
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
|
||||
WAITFORIT_ISBUSY=1
|
||||
fi
|
||||
|
||||
# see if timeout.c args have been updated in busybox v1.30.0 or newer
|
||||
# note: this requires the use of bash on Alpine
|
||||
if [[ $WAITFORIT_ISBUSY && $(busybox | head -1) =~ ^.*v([[:digit:]]+)\.([[:digit:]]+)\..+$ ]]; then
|
||||
if [[ ${BASH_REMATCH[1]} -le 1 && ${BASH_REMATCH[2]} -lt 30 ]]; then
|
||||
# using pre 1.30.0 version with `-t SEC` arg
|
||||
WAITFORIT_BUSYTIMEFLAG="-t"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
|
||||
wait_for
|
||||
WAITFORIT_RESULT=$?
|
||||
exit $WAITFORIT_RESULT
|
||||
else
|
||||
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
|
||||
wait_for_wrapper
|
||||
WAITFORIT_RESULT=$?
|
||||
else
|
||||
wait_for
|
||||
WAITFORIT_RESULT=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $WAITFORIT_CLI != "" ]]; then
|
||||
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
|
||||
exit $WAITFORIT_RESULT
|
||||
fi
|
||||
exec "${WAITFORIT_CLI[@]}"
|
||||
else
|
||||
exit $WAITFORIT_RESULT
|
||||
fi
|
|
@ -0,0 +1,12 @@
|
|||
// Internal dependencies
|
||||
const babelConfig = require( './babel.config' );
|
||||
const esLintConfig = require( './.eslintrc.js' );
|
||||
const jestConfig = require( './config/jest.config.js' );
|
||||
const webpackAlias = require( './webpack-alias' );
|
||||
|
||||
module.exports = {
|
||||
babelConfig,
|
||||
esLintConfig,
|
||||
jestConfig,
|
||||
webpackAlias,
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "@woocommerce/e2e-env",
|
||||
"version": "1.0.0",
|
||||
"description": "WooCommerce End to End Testing Environment Configuration.",
|
||||
"author": "Automattic",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"keywords": [
|
||||
"wordpress",
|
||||
"woocommerce",
|
||||
"e2e",
|
||||
"puppeteer"
|
||||
],
|
||||
"homepage": "https://github.com/woocommerce/woocommerce-admin/tree/master/packages/e2e-env/README.md",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin/issues"
|
||||
},
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.8.4",
|
||||
"@babel/polyfill": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"@wordpress/e2e-test-utils": "^4.3.0",
|
||||
"@wordpress/eslint-plugin": "^4.0.0",
|
||||
"@wordpress/jest-preset-default": "^5.4.0",
|
||||
"app-root-path": "^3.0.0",
|
||||
"jest": "^25.1.0",
|
||||
"jest-puppeteer": "^4.4.0",
|
||||
"ndb": "^1.1.5",
|
||||
"puppeteer": "^2.1.1",
|
||||
"woocommerce": "git+https://github.com/woocommerce/woocommerce.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"docker:up": "./bin/docker-compose.js up",
|
||||
"docker:down": "./bin/docker-compose.js down",
|
||||
"docker:clear-all": "docker rmi --force $(docker images -q)",
|
||||
"docker:ssh": "docker exec -it woo-blocks_wordpress-www_1 /bin/bash",
|
||||
"install-wp-tests": "./bin/install-wp-tests.sh",
|
||||
"test:e2e": "./bin/e2e-test-integration.js",
|
||||
"test:e2e-dev": "./bin/e2e-test-integration.js --dev"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
const path = require( 'path' );
|
||||
|
||||
const getAppRoot = () => {
|
||||
// Figure out where we're installed.
|
||||
// Typically will be in node_modules/, but WooCommerce Admin
|
||||
// uses a local file path (packages/e2e-env).
|
||||
let appPath = false;
|
||||
const moduleDir = path.dirname( require.resolve( '@woocommerce/e2e-env' ) );
|
||||
|
||||
if ( -1 < moduleDir.indexOf( 'node_modules' ) ) {
|
||||
appPath = moduleDir.split( 'node_modules' )[ 0 ];
|
||||
} else if ( -1 < moduleDir.indexOf( 'packages/e2e-env' ) ) {
|
||||
appPath = moduleDir.split( 'packages/e2e-env' )[ 0 ];
|
||||
}
|
||||
|
||||
return appPath;
|
||||
};
|
||||
|
||||
module.exports = getAppRoot;
|
|
@ -0,0 +1,5 @@
|
|||
const getAppRoot = require( './app-root' );
|
||||
|
||||
module.exports = {
|
||||
getAppRoot,
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const path = require( 'path' );
|
||||
|
||||
module.exports = {
|
||||
'@woocommerce/e2e-tests': path.resolve(
|
||||
__dirname,
|
||||
'node_modules/woocommerce/tests/e2e-tests'
|
||||
),
|
||||
};
|
Loading…
Reference in New Issue