Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ In your `package.json` add a new script:
"scripts" :{
...
"cy:run": "cypress run", // It can be any cypress command with any argument
"cy:parallel" : "cypress-parallel -s cy:run -t 2 -d '<your-cypress-specs-folder>' -a '\"<your-cypress-cmd-args>\"'"
Copy link
Author

Choose a reason for hiding this comment

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

-d still totally works just the same, not broken with this change! --spec just makes more sense to recommend, because it can now be a dir or a file or a glob.

"cy:parallel" : "cypress-parallel -s cy:run -t 2 --spec '<your-cypress-specs-folder(s)-or-file(s)>' -a '\"<your-cypress-cmd-args>\"'"
...
}
```
Expand All @@ -64,33 +64,32 @@ or
Run with npx (no package installation needed)

```
npx cy:parallel -s cy:run -t 2 -d '<your-cypress-specs-folder>' -a '"<your-cypress-cmd-args>"'
npx cy:parallel -s cy:run -t 2 --spec '<your-cypress-specs-folder(s)-or-file(s)>' -a '"<your-cypress-cmd-args>"'
```

## Passing Specs
## Passing Multiple Specs

```
cypress-parallel -s cy:run -t 2 -a '\"<your-cypress-cmd-args>\"' --spec path/to/spec1.spec.js path/to/spec2.spec.js
cypress-parallel -s cy:run -t 2 -a '\"<your-cypress-cmd-args>\"' --spec path/to/spec1.spec.js path/to/spec2.spec.js path/to/more/specs/**/*.js
```

### Scripts options

| Option | Alias | Description | Type |
| ----------------- | ----- | ---------------------------------- | ------ |
| --help | | Show help | |
| --version | | Show version number | |
| --script | -s | Your npm Cypress command | string |
| --args | -a | Your npm Cypress command arguments | string |
| --threads | -t | Number of threads | number |
| --specsDir | -d | Cypress specs directory | string |
| --spec | | Cypress spec file paths | string |
| --weightsJson | -w | Parallel weights json file | string |
| --reporter | -r | Reporter to pass to Cypress. | string |
| --reporterOptions | -o | Reporter options | string |
| --reporterModulePath | -n | Specify the reporter module path | string |
| --bail | -b | Exit on first failing thread | string |
| --verbose | -v | Some additional logging | string |
| --strictMode | -m | Add stricter checks after running the tests | boolean |
| Option | Alias | Description | Type |
| -------------------- | ----- | --------------------------------------------------------------------- | ------- |
| --help | | Show help | |
| --version | | Show version number | |
| --script | -s | Your npm Cypress command | string |
| --args | -a | Your npm Cypress command arguments | string |
| --threads | -t | Number of threads | number |
| --spec | -d | Cypress spec file path(s), or glob pattern(s). Can be one, or a list. | string |
| --weightsJson | -w | Parallel weights json file | string |
| --reporter | -r | Reporter to pass to Cypress. | string |
| --reporterOptions | -o | Reporter options | string |
| --reporterModulePath | -n | Specify the reporter module path | string |
| --bail | -b | Exit on first failing thread | string |
| --verbose | -v | Some additional logging | string |
| --strictMode | -m | Add stricter checks after running the tests | boolean |

**NB**: If you use *cypress-cucumber-preprocesor*, please **disable** the *strictMode* to avoid possible errors:

Expand Down
57 changes: 37 additions & 20 deletions lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ Reduce up to 40% your Cypress suite execution time parallelizing the test run on
## Install
Copy link
Author

@bvandercar-vt bvandercar-vt Jun 5, 2025

Choose a reason for hiding this comment

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

Just copied this from ./README.md to match.


```
npm i cypress-parallel
npm i cypress-parallel -D
```

or

```
yarn add cypress-parallel
yarn add cypress-parallel -D
```

## Add a new script
Expand All @@ -40,7 +40,7 @@ In your `package.json` add a new script:
"scripts" :{
...
"cy:run": "cypress run", // It can be any cypress command with any argument
"cy:parallel" : "cypress-parallel -s cy:run -t 2 -d <your-cypress-specs-folder> -a '\"<your-cypress-cmd-args>\"'"
"cy:parallel" : "cypress-parallel -s cy:run -t 2 --spec '<your-cypress-specs-folder(s)-or-file(s)>' -a '\"<your-cypress-cmd-args>\"'"
...
}
```
Expand All @@ -64,26 +64,32 @@ or
Run with npx (no package installation needed)

```
npx cy:parallel -s cy:run -t 2 -d <your-cypress-specs-folder> -a '\"<your-cypress-cmd-args>\"'
npx cy:parallel -s cy:run -t 2 --spec '<your-cypress-specs-folder(s)-or-file(s)>' -a '"<your-cypress-cmd-args>"'
```

## Passing Multiple Specs

```
cypress-parallel -s cy:run -t 2 -a '\"<your-cypress-cmd-args>\"' --spec path/to/spec1.spec.js path/to/spec2.spec.js path/to/more/specs/**/*.js
```

### Scripts options

| Option | Alias | Description | Type |
| ----------------- | ----- | ---------------------------------- | ------ |
| --help | | Show help | |
| --version | | Show version number | |
| --script | -s | Your npm Cypress command | string |
| --args | -a | Your npm Cypress command arguments | string |
| --threads | -t | Number of threads | number |
| --specsDir | -d | Cypress specs directory | string |
| --weightsJson | -w | Parallel weights json file | string |
| --reporter | -r | Reporter to pass to Cypress. | string |
| --reporterOptions | -o | Reporter options | string |
| --reporterModulePath | -n | Specify the reporter module path | string |
| --bail | -b | Exit on first failing thread | string |
| --verbose | -v | Some additional logging | string |
| --strictMode | -m | Add stricter checks after running the tests | boolean |
| Option | Alias | Description | Type |
| -------------------- | ----- | --------------------------------------------------------------------- | ------- |
| --help | | Show help | |
| --version | | Show version number | |
| --script | -s | Your npm Cypress command | string |
| --args | -a | Your npm Cypress command arguments | string |
| --threads | -t | Number of threads | number |
| --spec | -d | Cypress spec file path(s), or glob pattern(s). Can be one, or a list. | string |
| --weightsJson | -w | Parallel weights json file | string |
| --reporter | -r | Reporter to pass to Cypress. | string |
| --reporterOptions | -o | Reporter options | string |
| --reporterModulePath | -n | Specify the reporter module path | string |
| --bail | -b | Exit on first failing thread | string |
| --verbose | -v | Some additional logging | string |
| --strictMode | -m | Add stricter checks after running the tests | boolean |

**NB**: If you use *cypress-cucumber-preprocesor*, please **disable** the *strictMode* to avoid possible errors:

Expand All @@ -105,10 +111,21 @@ npx cy:parallel -s cy:run -t 2 -d <your-cypress-specs-folder> -a '\"<your-cypres
}
```

## Env variables

### CYPRESS_THREAD

You can get the current thread index by reading the `CYPRESS_THREAD` variable.

```javascript
const threadIndex = process.env.CYPRESS_THREAD;
// return 1, 2, 3, 4, ...
```

# Contributors

Looking for contributors.

# License

MIT
This project is licensed under the MIT license. See [LICENSE](LICENSE).
4 changes: 2 additions & 2 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const path = require('path');
const fs = require('fs-extra');
const { settings } = require('./settings');

const { getTestSuitePaths, distributeTestsByWeight, getMaxPathLenghtFrom } = require('./test-suites');
const { getTestSuitePaths, distributeTestsByWeight, getMaxPathLengthFrom } = require('./test-suites');
const {
formatTime,
generateWeightsFile,
Expand Down Expand Up @@ -55,7 +55,7 @@ async function start() {
let table = new Table({
head: ['Spec', 'Time', 'Tests', 'Passing', 'Failing', 'Pending'],
style: { head: ['blue'] },
colWidths: [getMaxPathLenghtFrom(testSuitePaths), 10, 10, 10, 10, 10]
colWidths: [getMaxPathLengthFrom(testSuitePaths), 10, 10, 10, 10, 10]
});

let totalTests = 0;
Expand Down
27 changes: 19 additions & 8 deletions lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@ const argv = yargs
type: 'boolean',
description: 'Exit on first suite finishing with errors'
})
.option('specsDir', {
alias: 'd',
type: 'string',
description: 'Cypress specs directory'
})
.option('spec', {
alias: ['specsDir', 'd'], // include specDir and -d so that is non-breaking. In future version, can remove.
type: 'array',
description: 'List of Cypress spec paths'
description: 'Cypress spec path(s), or glob pattern(s). Can be one, or a list.',
default: ['cypress/integration/**/*']
})
.option('args', {
alias: 'a',
Expand Down Expand Up @@ -83,10 +80,24 @@ const COLORS = [
'\x1b[40m'
];

/** @type {{
threadCount: number
testSuitesPaths: string[]
shouldBail: boolean
isVerbose: boolean
weightsJSON: string
defaultWeight: number
reporter?: string
reporterModulePath: string
reporterOptions?: string
reporterOptionsPath?: string
script: string
strictMode: boolean
scriptArguments: string[]
}} */
const settings = {
threadCount: argv.threads ? argv.threads : 2,
testSuitesPath: argv.specsDir ? argv.specsDir : 'cypress/integration',
testSuitesPaths: argv.spec ? argv.spec : undefined,
testSuitesPaths: argv.spec,
shouldBail: argv.bail ? argv.bail : false,
isVerbose: argv.verbose ? argv.verbose : false,
weightsJSON: argv.weightsJson
Expand Down
56 changes: 29 additions & 27 deletions lib/test-suites.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ const { glob } = require('glob');

const { settings } = require('./settings');

const getFilePathsByPath = (dir) =>
fs.readdirSync(dir).reduce((files, file) => {
const name = path.join(dir, file);
const isDirectory = fs.statSync(name).isDirectory();
if (isDirectory) return [...files, ...getFilePathsByPath(name)];
return [...files, name];
}, []);

async function getTestSuitePaths() {
const isPattern = settings.testSuitesPath.includes('*');

let fileList;
if (settings.testSuitesPaths) {
fileList = settings.testSuitesPaths;
} else if (isPattern) {
console.log(`Using pattern ${settings.testSuitesPath} to find test suites`);
fileList = await glob(settings.testSuitesPath, { ignore: 'node_modules/**' });
} else {
console.log(
'DEPRECATED: using path is deprecated and will be removed, switch to glob pattern'
);
fileList = getFilePathsByPath(settings.testSuitesPath);
let fileList = [];
for (const suitePath of settings.testSuitesPaths) {
let globPattern = undefined
if (suitePath.includes('*')) {
globPattern = suitePath
} else if (fs.existsSync(suitePath) && fs.lstatSync(suitePath).isDirectory()) {
console.log(
'DEPRECATED: using directory path is deprecated and will be removed, switch to glob pattern'
);
globPattern = `${suitePath}/**/*`
}

if (globPattern) {
if (settings.isVerbose) {
console.log(`Using pattern ${globPattern} to find test suites`);
}
const thisFileList = await glob(globPattern, { ignore: 'node_modules/**' })
fileList.concat(thisFileList);
} else {
fileList.append(suitePath)
}
}

console.log(`${fileList.length} test suite(s) found.`);
Expand All @@ -36,19 +36,21 @@ async function getTestSuitePaths() {

// We can't run more threads than suites
if (fileList.length < settings.threadCount) {
console.log(
`Thread setting is ${settings.threadCount}, but only ${fileList.length} test suite(s) were found. Adjusting configuration accordingly.`
);
if (settings.isVerbose) {
console.log(
`Thread setting is ${settings.threadCount}, but only ${fileList.length} test suite(s) were found. Adjusting configuration accordingly.`
);
}
settings.threadCount = fileList.length;
}

return fileList;
}

function getMaxPathLenghtFrom(testSuitePaths) {
function getMaxPathLengthFrom(testSuitePaths) {
let maxLength = 10;

for(let path of testSuitePaths){
for (let path of testSuitePaths) {
maxLength = Math.max(maxLength, path.length);
}

Expand Down Expand Up @@ -99,5 +101,5 @@ function distributeTestsByWeight(testSuitePaths) {
module.exports = {
getTestSuitePaths,
distributeTestsByWeight,
getMaxPathLenghtFrom
getMaxPathLengthFrom
};