Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallelize cypress testing #3460

Merged
merged 20 commits into from Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions venia-integration-tests/README.md
Expand Up @@ -7,6 +7,32 @@
3. Run `yarn test`
4. Now select test to Run from Cypress UI.

## Run tests in headless mode
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcalcaben can you please verify the docs?


Running tests in the headless mode is the preferred option when testing in the Continuous Integration. Before updating/creating any tests, please run the test suite in headless mode to make sure they are running as expected.

Run the following command to test in headless mode:

`yarn test:headless`

Use the **required** `--url` or `--baseUrl` parameter to provide the URL of **your storefront application** the tests will run against.

`yarn test:headless` also accepts the following arguments:

`--help`: Show all the CLI arguments supported by the headless mode

`--threads` or `-t`: Will take a number argument that specifies the number of parallel async processes that will be used to speed up the tests.

___Note___: _After testing we realized 4 is the best option while running locally. Adding more proccess might create more overhead, so make sure you take due diligence while using this option._

`--update` or `-u`: Use this option if you want to update the snapshots.

`--spec` or `-s`: String of comma separated test files to run. If not provided all tests will run.

Example:

`yarn test:headless -u --url https://develop.pwa-venia.com -p 4 -s ./src/tests/integrationTests/pageBuilder/banner.spec.js,./src/tests/e2eTests/wishList/singleWishlistAddRemoveProduct.spec.js`

# Adding Cypress UI Tests

We follow Test Triangle for testing pwa-studio project where we expect most of our code coverage by Unit tests. This allows us to be very less dependent on UI tests which are expensive to maintain and can be very unstable in long run.
Expand Down
8 changes: 5 additions & 3 deletions venia-integration-tests/package.json
Expand Up @@ -5,19 +5,21 @@
"scripts": {
"clearCache:mac": "rm -rf ~/Library/Application\\ Support/Cypress/cy/*",
"test": "cypress open --browser chrome --config-file cypress.config.json",
"test:ci": "./run-tests",
"test:ci": "./run-tests.js -t 4",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels a little static to me, to always set it as 4. different machines will have different capacity based on the system cpu, so I think this should be calculated based on the machine. using os.cpus you can get information about the machine and then based on that run threads.

Also, it seems like maybe we always want to to run threads and maybe two commands isn't necessary?

"test:debug": "node --inspect-brk ./node_modules/cypress/bin/cypress run --browser chrome --config-file cypress.config.json",
"test:devTest": "jest",
"test:firefox": "cypress open --browser firefox --config-file cypress.config.json",
"test:headless": "./run-tests",
"test:headless": "./run-tests.js",
"test:run": "cypress run --browser chrome --config-file cypress.config.json"
},
"author": "Magento Commerce",
"license": "MIT",
"devDependencies": {
"cypress": "~7.1.0",
"cypress-image-snapshot": "~4.0.1",
"glob": "~7.2.0",
"jest": "~26.0.1",
"jimp": "~0.16.1"
"jimp": "~0.16.1",
"yargs": "~17.2.1"
}
}
34 changes: 0 additions & 34 deletions venia-integration-tests/run-tests

This file was deleted.

136 changes: 136 additions & 0 deletions venia-integration-tests/run-tests.js
@@ -0,0 +1,136 @@
#!/usr/bin/env node
const glob = require('glob');
const { exec } = require('child_process');

var argv = require('yargs/yargs')(process.argv.slice(2))
.option('url', {
alias: 'baseUrl',
demandOption: true,
describe: 'Storefront application URL to run tests against',
type: 'string',
nargs: 1
})
.option('t', {
alias: 'threads',
default: 1,
describe: 'Number of parallel runs',
type: 'number',
nargs: 1
})
.option('u', {
alias: 'update',
default: false,
describe: 'Update snapshots',
type: 'boolean'
})
.option('s', {
alias: 'spec',
default: null,
describe: 'Spec files to run',
type: 'string',
nargs: 1
})
.option('h', {
alias: 'help',
describe: 'Show run-tests help'
})
.help('h', 'Show run-tests help')
.version(false).argv;

const { baseUrl, threads, update, spec } = argv;

if (!baseUrl) {
console.error(
'Missing baseUrl. Please provide a baseUrl using the --baseUrl arg'
);

return;
}

const files = spec ? spec.split(',') : glob.sync('./src/tests/**/*.spec.js');

if (files.length < threads) {
console.error('Can not have more parallel runs than tests.');

return;
}

const testsPerRun = files.length / threads;
const dockerRuns = {};

const port = new URL(baseUrl).port;

let dockerCommand = null;

if (port) {
// run docker on local instance
console.log(`Running tests on local instance ${baseUrl}`);

dockerCommand = `docker run --rm -v ${
process.env.PWD
}:/venia-integration-tests -w /venia-integration-tests --entrypoint=cypress cypress/included:8.3.1 run --browser chrome --config baseUrl=https://host.docker.internal:${port},screenshotOnRunFailure=false --config-file cypress.config.json --env updateSnapshots=${update} --headless`;
revanth0212 marked this conversation as resolved.
Show resolved Hide resolved
} else {
// run docker on remote instance
console.log(`Running tests on remote instance ${baseUrl}`);

dockerCommand = `docker run --rm -v ${
process.env.PWD
}:/venia-integration-tests -w /venia-integration-tests --entrypoint=cypress cypress/included:8.3.1 run --browser chrome --config baseUrl=${baseUrl},screenshotOnRunFailure=false --config-file cypress.config.json --env updateSnapshots=${update} --headless`;
}

const start = process.hrtime();

for (let i = 0; i < threads; i++) {
const filesToTest = files.slice(testsPerRun * i, testsPerRun * (i + 1));

const commandWithSpecFiles = `${dockerCommand} --spec ${filesToTest.join(
','
)}`;

console.log(`Running ${commandWithSpecFiles} \n`);

const run = exec(commandWithSpecFiles);

run.stdout.on('data', data => {
if (data !== '\n' || data !== '\r' || data.trim() !== '') {
console.log(`docker run ${i + 1} => ${data}`);
}
});

run.stderr.on('data', data => {
console.error(`docker run ${i + 1} => ${data}`);
});

run.on('close', code => {
dockerRuns[i].completed = true;

const timeTaken = process.hrtime(dockerRuns[i].started)[0];

console.log(
`docker run ${i +
1} exited with ${code} code in ${timeTaken} seconds`
);

if (Object.values(dockerRuns).every(r => r.completed)) {
const totalTime = process.hrtime(start)[0];

console.log(`\nAll runs completed in ${totalTime} seconds\n`);

process.exit();
}
});

dockerRuns[i] = {
process: run,
completed: false,
started: process.hrtime()
};
}

process.on('SIGINT', function() {
console.log('Received kill signal. Killing all cypress tests. \n');

exec(
"docker ps -a | awk '{ print $1,$2 }' | grep cypress/included | awk '{print $1 }' | xargs -I {} docker kill {}"
);
});
55 changes: 54 additions & 1 deletion venia-integration-tests/yarn.lock
Expand Up @@ -1493,6 +1493,15 @@ cliui@^6.0.0:
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"

cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"

co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
Expand Down Expand Up @@ -2195,7 +2204,7 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==

get-caller-file@^2.0.1:
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
Expand Down Expand Up @@ -2275,6 +2284,18 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"

glob@~7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"

global-dirs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
Expand Down Expand Up @@ -4979,6 +5000,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
Expand Down Expand Up @@ -5057,6 +5087,11 @@ y18n@^4.0.0:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==

y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==

yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
Expand All @@ -5070,6 +5105,11 @@ yargs-parser@^18.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"

yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==

yargs@^15.4.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
Expand All @@ -5087,6 +5127,19 @@ yargs@^15.4.1:
y18n "^4.0.0"
yargs-parser "^18.1.2"

yargs@~17.2.1:
version "17.2.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea"
integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"

yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
Expand Down