Skip to content

Commit

Permalink
feat(run-workers) : Add support of multiple browsers for run-workers (#…
Browse files Browse the repository at this point in the history
…3606)

* feat(run-workers) : Add support of multiple for run-workers

* Fix multiple array not defined error

* Fix unit test case

* Update parallel.md
  • Loading branch information
karanshah-browserstack committed May 28, 2023
1 parent 11109b6 commit dfec1d9
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 11 deletions.
2 changes: 1 addition & 1 deletion bin/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ program.command('run [test]')
.option('--child <string>', 'option for child processes')
.action(errorHandler(require('../lib/command/run')));

program.command('run-workers <workers>')
program.command('run-workers <workers> [selectedRuns...]')
.description('Executes tests in workers')
.option('-c, --config [file]', 'configuration file to be used')
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
Expand Down
56 changes: 56 additions & 0 deletions docs/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,62 @@ By default the tests are assigned one by one to the available workers this may l
npx codeceptjs run-workers --suites 2
```

## Parallel Execution by Workers on Multiple Browsers

To run tests in parallel across multiple browsers, modify your `codecept.conf.js` file to configure multiple browsers on which you want to run your tests and your tests will run across multiple browsers.

Start with modifying the `codecept.conf.js` file. Add multiple key inside the config which will be used to configure multiple profiles.

```
exports.config = {
helpers: {
WebDriver: {
url: 'http://localhost:3000',
desiredCapabilties: {}
}
},
multiple: {
profile1: {
browsers: [
{
browser: "firefox",
desiredCapabilties: {
// override capabilties related to firefox
}
},
{
browser: "chrome",
desiredCapabilties: {
// override capabilties related to chrome
}
}
]
},
profile2: {
browsers: [
{
browser: "safari",
desiredCapabilties: {
// override capabilties related to safari
}
}
]
}
}
};
```
To trigger tests on all the profiles configured, you can use the following command:
```
npx codeceptjs run-workers 3 all -c codecept.conf.js
```
This will run your tests across all browsers configured from profile1 & profile2 on 3 workers.

To trigger tests on specific profile, you can use the following command:
```
npx codeceptjs run-workers 2 profile1 -c codecept.conf.js
```
This will run your tests across 2 browsers from profile1 on 2 workers.

## Custom Parallel Execution

To get a full control of parallelization create a custom execution script to match your needs.
Expand Down
3 changes: 2 additions & 1 deletion lib/command/run-workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const output = require('../output');
const event = require('../event');
const Workers = require('../workers');

module.exports = async function (workerCount, options) {
module.exports = async function (workerCount, selectedRuns, options) {
process.env.profile = options.profile;

const { config: testConfig, override = '' } = options;
Expand All @@ -15,6 +15,7 @@ module.exports = async function (workerCount, options) {
by,
testConfig,
options,
selectedRuns,
};

const numberOfWorkers = parseInt(workerCount, 10);
Expand Down
66 changes: 57 additions & 9 deletions lib/workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ const MochaFactory = require('./mochaFactory');
const Container = require('./container');
const { getTestRoot } = require('./command/utils');
const { isFunction, fileExists } = require('./utils');
const { replaceValueDeep, deepClone } = require('./utils');
const mainConfig = require('./config');
const output = require('./output');
const event = require('./event');
const recorder = require('./recorder');
const runHook = require('./hooks');
const WorkerStorage = require('./workerStorage');
const collection = require('./command/run-multiple/collection');

const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js');

Expand Down Expand Up @@ -79,15 +81,39 @@ const repackTest = (test) => {
return test;
};

const createWorkerObjects = (testGroups, config, testRoot, options) => {
return testGroups.map((tests, index) => {
const workerObj = new WorkerObject(index);
workerObj.addConfig(config);
workerObj.addTests(tests);
workerObj.setTestRoot(testRoot);
workerObj.addOptions(options);
return workerObj;
const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns) => {
selectedRuns = options && options.all && config.multiple ? Object.keys(config.multiple) : selectedRuns;
if (selectedRuns === undefined || !selectedRuns.length || config.multiple === undefined) {
return testGroups.map((tests, index) => {
const workerObj = new WorkerObject(index);
workerObj.addConfig(config);
workerObj.addTests(tests);
workerObj.setTestRoot(testRoot);
workerObj.addOptions(options);
return workerObj;
});
}
const workersToExecute = [];
collection.createRuns(selectedRuns, config).forEach((worker) => {
const workerName = worker.getOriginalName() || worker.getName();
const workerConfig = worker.getConfig();
workersToExecute.push(getOverridenConfig(workerName, workerConfig, config));
});
const workers = [];
let index = 0;
testGroups.forEach((tests) => {
const testWorkerArray = [];
workersToExecute.forEach((finalConfig) => {
const workerObj = new WorkerObject(index++);
workerObj.addConfig(finalConfig);
workerObj.addTests(tests);
workerObj.setTestRoot(testRoot);
workerObj.addOptions(options);
testWorkerArray.push(workerObj);
});
workers.push(...testWorkerArray);
});
return workers;
};

const indexOfSmallestElement = (groups) => {
Expand Down Expand Up @@ -115,6 +141,28 @@ const convertToMochaTests = (testGroup) => {
return group;
};

const getOverridenConfig = (workerName, workerConfig, config) => {
// clone config
const overriddenConfig = deepClone(config);

// get configuration
const browserConfig = workerConfig.browser;

for (const key in browserConfig) {
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key]);
}

// override tests configuration
if (overriddenConfig.tests) {
overriddenConfig.tests = workerConfig.tests;
}

if (overriddenConfig.gherkin && workerConfig.gherkin && workerConfig.gherkin.features) {
overriddenConfig.gherkin.features = workerConfig.gherkin.features;
}
return overriddenConfig;
};

class WorkerObject {
/**
* @param {Number} workerIndex - Unique ID for worker
Expand Down Expand Up @@ -183,7 +231,7 @@ class Workers extends EventEmitter {

_initWorkers(numberOfWorkers, config) {
this.splitTestsByGroups(numberOfWorkers, config);
this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options);
this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options, config.selectedRuns);
this.numberOfWorkers = this.workers.length;
}

Expand Down
30 changes: 30 additions & 0 deletions test/unit/worker_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,34 @@ describe('Workers', () => {
done();
});
});

it('should run worker with multiple config', (done) => {
const workerConfig = {
by: 'test',
testConfig: './test/data/sandbox/codecept.multiple.js',
options: {},
selectedRuns: ['mobile'],
};

const workers = new Workers(2, workerConfig);

for (const worker of workers.getWorkers()) {
worker.addConfig({
helpers: {
FileSystem: {},
Workers: {
require: './custom_worker_helper',
},
},
});
}

workers.run();

workers.on(event.all.result, (status) => {
expect(workers.getWorkers().length).equal(8);
expect(status).equal(true);
done();
});
});
});

0 comments on commit dfec1d9

Please sign in to comment.