codeceptjs 3.6.6 → 4.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codecept.js +81 -84
- package/lib/actor.js +13 -13
- package/lib/ai.js +13 -10
- package/lib/assert/empty.js +21 -20
- package/lib/assert/equal.js +39 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +47 -46
- package/lib/assert/throws.js +11 -13
- package/lib/assert/truth.js +22 -19
- package/lib/assert.js +2 -4
- package/lib/cli.js +49 -57
- package/lib/codecept.js +155 -142
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +52 -58
- package/lib/command/definitions.js +89 -88
- package/lib/command/dryRun.js +68 -71
- package/lib/command/generate.js +188 -197
- package/lib/command/gherkin/init.js +16 -27
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +38 -40
- package/lib/command/init.js +288 -290
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +2 -6
- package/lib/command/run-multiple.js +93 -113
- package/lib/command/run-rerun.js +25 -20
- package/lib/command/run-workers.js +66 -64
- package/lib/command/run.js +29 -26
- package/lib/command/utils.js +65 -80
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +9 -10
- package/lib/container.js +48 -40
- package/lib/data/context.js +59 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +20 -26
- package/lib/dirname.js +5 -0
- package/lib/event.js +167 -163
- package/lib/heal.js +17 -13
- package/lib/helper/AI.js +41 -130
- package/lib/helper/ApiDataFactory.js +69 -73
- package/lib/helper/Appium.js +381 -412
- package/lib/helper/Expect.js +425 -0
- package/lib/helper/ExpectHelper.js +48 -40
- package/lib/helper/FileSystem.js +79 -80
- package/lib/helper/GraphQL.js +43 -44
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +62 -65
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +14 -12
- package/lib/helper/Nightmare.js +566 -662
- package/lib/helper/Playwright.js +1216 -1361
- package/lib/helper/Protractor.js +627 -663
- package/lib/helper/Puppeteer.js +1128 -1231
- package/lib/helper/REST.js +68 -159
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +484 -490
- package/lib/helper/WebDriver.js +1156 -1297
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +18 -21
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +7 -14
- package/lib/helper/network/utils.js +2 -3
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +7 -6
- package/lib/helper.js +3 -1
- package/lib/history.js +5 -6
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +41 -25
- package/lib/interfaces/bdd.js +64 -47
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +118 -124
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +30 -32
- package/lib/listener/steps.js +53 -50
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +10 -6
- package/lib/mochaFactory.js +15 -18
- package/lib/output.js +10 -6
- package/lib/parser.js +12 -15
- package/lib/pause.js +33 -40
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +37 -29
- package/lib/plugin/autoLogin.js +65 -70
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +67 -115
- package/lib/plugin/customLocator.js +20 -21
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +108 -67
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +39 -32
- package/lib/plugin/retryTo.js +40 -46
- package/lib/plugin/screenshotOnFail.js +87 -109
- package/lib/plugin/selenoid.js +118 -131
- package/lib/plugin/standardActingHelpers.js +8 -2
- package/lib/plugin/stepByStepReport.js +91 -110
- package/lib/plugin/stepTimeout.js +23 -24
- package/lib/plugin/subtitles.js +35 -34
- package/lib/plugin/tryTo.js +30 -40
- package/lib/plugin/wdio.js +75 -78
- package/lib/recorder.js +17 -14
- package/lib/rerun.js +10 -11
- package/lib/scenario.js +23 -25
- package/lib/secret.js +2 -4
- package/lib/session.js +10 -10
- package/lib/step.js +9 -12
- package/lib/store.js +3 -2
- package/lib/transform.js +1 -1
- package/lib/translation.js +8 -7
- package/lib/ui.js +14 -12
- package/lib/utils.js +72 -70
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +25 -27
- package/lib/workers.js +32 -29
- package/package.json +53 -51
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +13 -9
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +65 -415
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import Container from '../container.js';
|
|
4
|
+
import recorder from '../recorder.js';
|
|
5
|
+
import * as event from '../event.js';
|
|
6
|
+
import * as output from '../output.js';
|
|
7
|
+
import { fileExists, clearString } from '../utils.js';
|
|
8
|
+
import * as Codeceptjs from '../index.js';
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
const recorder = require('../recorder')
|
|
6
|
-
const event = require('../event')
|
|
7
|
-
const output = require('../output')
|
|
8
|
-
const { fileExists, clearString } = require('../utils')
|
|
9
|
-
const Codeceptjs = require('../index')
|
|
10
|
+
import supportedHelpers from './standardActingHelpers.js';
|
|
10
11
|
|
|
11
12
|
const defaultConfig = {
|
|
12
13
|
uniqueScreenshotNames: false,
|
|
13
14
|
disableScreenshots: false,
|
|
14
15
|
fullPageScreenshots: false,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const supportedHelpers = require('./standardActingHelpers')
|
|
16
|
+
};
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Creates screenshot on failure. Screenshot is saved into `output` directory.
|
|
@@ -42,140 +41,119 @@ const supportedHelpers = require('./standardActingHelpers')
|
|
|
42
41
|
*
|
|
43
42
|
*
|
|
44
43
|
*/
|
|
45
|
-
|
|
46
|
-
const helpers = Container.helpers()
|
|
47
|
-
let helper
|
|
44
|
+
export default function (config) {
|
|
45
|
+
const helpers = Container.helpers();
|
|
46
|
+
let helper;
|
|
48
47
|
|
|
49
48
|
for (const helperName of supportedHelpers) {
|
|
50
49
|
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
51
|
-
helper = helpers[helperName]
|
|
50
|
+
helper = helpers[helperName];
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
if (!helper) return // no helpers for screenshot
|
|
54
|
+
if (!helper) return; // no helpers for screenshot
|
|
56
55
|
|
|
57
|
-
const options = Object.assign(defaultConfig, helper.options, config)
|
|
56
|
+
const options = Object.assign(defaultConfig, helper.options, config);
|
|
58
57
|
|
|
59
58
|
if (helpers.Mochawesome) {
|
|
60
59
|
if (helpers.Mochawesome.config) {
|
|
61
|
-
options.uniqueScreenshotNames = helpers.Mochawesome.config.uniqueScreenshotNames
|
|
60
|
+
options.uniqueScreenshotNames = helpers.Mochawesome.config.uniqueScreenshotNames;
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
if (Codeceptjs.container.mocha
|
|
66
|
-
options.reportDir =
|
|
67
|
-
Codeceptjs.container.mocha
|
|
68
|
-
Codeceptjs.container.mocha().options.reporterOptions.reportDir
|
|
64
|
+
if (Codeceptjs.container.mocha) {
|
|
65
|
+
options.reportDir = Codeceptjs.container.mocha.options.reporterOptions
|
|
66
|
+
&& Codeceptjs.container.mocha.options.reporterOptions.reportDir;
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
if (options.disableScreenshots) {
|
|
72
70
|
// old version of disabling screenshots
|
|
73
|
-
return
|
|
71
|
+
return;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
event.dispatcher.on(event.test.failed, (test) => {
|
|
77
75
|
if (test.ctx?._runnable.title.includes('hook: ')) {
|
|
78
|
-
output.plugin(
|
|
79
|
-
'screenshotOnFail',
|
|
80
|
-
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
81
|
-
)
|
|
76
|
+
output.output.plugin('screenshotOnFail', 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.');
|
|
82
77
|
return
|
|
83
78
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
|
|
80
|
+
recorder.add('screenshot of failed test', async () => {
|
|
81
|
+
let fileName = clearString(test.title);
|
|
82
|
+
const dataType = 'image/png';
|
|
83
|
+
// This prevents data driven to be included in the failed screenshot file name
|
|
84
|
+
if (fileName.indexOf('{') !== -1) {
|
|
85
|
+
fileName = fileName.substr(0, (fileName.indexOf('{') - 3)).trim();
|
|
86
|
+
}
|
|
87
|
+
if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook') fileName = clearString(`${test.title}_${test.ctx.test.title}`);
|
|
88
|
+
if (options.uniqueScreenshotNames && test) {
|
|
89
|
+
const uuid = _getUUID(test);
|
|
90
|
+
fileName = `${fileName.substring(0, 10)}_${uuid}.failed.png`;
|
|
91
|
+
} else {
|
|
92
|
+
fileName += '.failed.png';
|
|
93
|
+
}
|
|
94
|
+
output.output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot');
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
if (options.reportDir) {
|
|
98
|
+
fileName = path.join(options.reportDir, fileName);
|
|
99
|
+
const mochaReportDir = path.resolve(process.cwd(), options.reportDir);
|
|
100
|
+
if (!fileExists(mochaReportDir)) {
|
|
101
|
+
fs.mkdirSync(mochaReportDir);
|
|
102
|
+
}
|
|
92
103
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
fileName += '.failed.png'
|
|
104
|
+
await helper.saveScreenshot(fileName, options.fullPageScreenshots);
|
|
105
|
+
|
|
106
|
+
if (!test.artifacts) test.artifacts = {};
|
|
107
|
+
test.artifacts.screenshot = path.join(global.output_dir, fileName);
|
|
108
|
+
if (Container.mocha().options.reporterOptions['mocha-junit-reporter'] && Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments) {
|
|
109
|
+
test.attachments = [path.join(global.output_dir, fileName)];
|
|
100
110
|
}
|
|
101
|
-
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot')
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
if (options.reportDir) {
|
|
105
|
-
fileName = path.join(options.reportDir, fileName)
|
|
106
|
-
const mochaReportDir = path.resolve(process.cwd(), options.reportDir)
|
|
107
|
-
if (!fileExists(mochaReportDir)) {
|
|
108
|
-
fs.mkdirSync(mochaReportDir)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
await helper.saveScreenshot(fileName, options.fullPageScreenshots)
|
|
112
|
-
|
|
113
|
-
if (!test.artifacts) test.artifacts = {}
|
|
114
|
-
test.artifacts.screenshot = path.join(global.output_dir, fileName)
|
|
115
|
-
if (
|
|
116
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'] &&
|
|
117
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments
|
|
118
|
-
) {
|
|
119
|
-
test.attachments = [path.join(global.output_dir, fileName)]
|
|
120
|
-
}
|
|
121
111
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const sessions = helper.sessionPages || helper.sessionWindows
|
|
132
|
-
for (const sessionName in sessions) {
|
|
133
|
-
const screenshotFileName = `${sessionName}_${fileName}`
|
|
134
|
-
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(
|
|
135
|
-
global.output_dir,
|
|
136
|
-
screenshotFileName,
|
|
137
|
-
)
|
|
138
|
-
allureReporter.addAttachment(
|
|
139
|
-
`${sessionName} - Last Seen Screenshot`,
|
|
140
|
-
fs.readFileSync(path.join(global.output_dir, screenshotFileName)),
|
|
141
|
-
dataType,
|
|
142
|
-
)
|
|
143
|
-
}
|
|
112
|
+
const allureReporter = Container.plugins('allure');
|
|
113
|
+
if (allureReporter) {
|
|
114
|
+
allureReporter.addAttachment('Main session - Last Seen Screenshot', fs.readFileSync(path.join(global.output_dir, fileName)), dataType);
|
|
115
|
+
|
|
116
|
+
if (helper.activeSessionName) {
|
|
117
|
+
for (const sessionName in helper.sessionPages) {
|
|
118
|
+
const screenshotFileName = `${sessionName}_${fileName}`;
|
|
119
|
+
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(global.output_dir, screenshotFileName);
|
|
120
|
+
allureReporter.addAttachment(`${sessionName} - Last Seen Screenshot`, fs.readFileSync(path.join(global.output_dir, screenshotFileName)), dataType);
|
|
144
121
|
}
|
|
145
122
|
}
|
|
123
|
+
}
|
|
146
124
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
125
|
+
const cucumberReporter = Container.plugins('cucumberJsonReporter');
|
|
126
|
+
if (cucumberReporter) {
|
|
127
|
+
cucumberReporter.addScreenshot(test.artifacts.screenshot);
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
output.output.plugin(err);
|
|
131
|
+
if (
|
|
132
|
+
err
|
|
133
|
+
&& err.type
|
|
134
|
+
&& err.type === 'RuntimeError'
|
|
135
|
+
&& err.message
|
|
136
|
+
&& (
|
|
137
|
+
err.message.indexOf('was terminated due to') > -1
|
|
138
|
+
|| err.message.indexOf('no such window: target window already closed') > -1
|
|
139
|
+
)
|
|
140
|
+
) {
|
|
141
|
+
output.output.log(`Can't make screenshot, ${err}`);
|
|
142
|
+
helper.isRunning = false;
|
|
164
143
|
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
})
|
|
144
|
+
}
|
|
145
|
+
}, true);
|
|
146
|
+
});
|
|
169
147
|
|
|
170
148
|
function _getUUID(test) {
|
|
171
149
|
if (test.uuid) {
|
|
172
|
-
return test.uuid
|
|
150
|
+
return test.uuid;
|
|
173
151
|
}
|
|
174
152
|
|
|
175
|
-
if (test.ctx && test.ctx.test
|
|
176
|
-
return test.ctx.test.uuid
|
|
153
|
+
if (test.ctx && test.ctx.test?.uuid) {
|
|
154
|
+
return test.ctx.test.uuid;
|
|
177
155
|
}
|
|
178
156
|
|
|
179
|
-
return Math.floor(new Date().getTime() / 1000)
|
|
157
|
+
return Math.floor(new Date().getTime() / 1000);
|
|
180
158
|
}
|
|
181
159
|
}
|
package/lib/plugin/selenoid.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { default as axios } from 'axios';
|
|
5
|
+
import { clearString, deepMerge } from '../utils.js';
|
|
6
|
+
import {
|
|
7
|
+
container, event, recorder, output,
|
|
8
|
+
} from '../index.js';
|
|
9
|
+
|
|
10
|
+
const exec = util.promisify(require('child_process').exec);
|
|
8
11
|
|
|
9
12
|
const defaultBrowserConfig = {
|
|
10
13
|
chrome: {
|
|
@@ -27,7 +30,7 @@ const defaultBrowserConfig = {
|
|
|
27
30
|
},
|
|
28
31
|
},
|
|
29
32
|
},
|
|
30
|
-
}
|
|
33
|
+
};
|
|
31
34
|
|
|
32
35
|
const dockerCreateScriptArr = [
|
|
33
36
|
'docker create --rm --name $name$',
|
|
@@ -39,30 +42,29 @@ const dockerCreateScriptArr = [
|
|
|
39
42
|
`-e OVERRIDE_VIDEO_OUTPUT_DIR=${global.output_dir}/video/`,
|
|
40
43
|
'$additionalParams$',
|
|
41
44
|
'aerokube/selenoid:latest-release -log-output-dir /opt/selenoid/logs',
|
|
42
|
-
]
|
|
45
|
+
];
|
|
43
46
|
|
|
44
47
|
const dockerImageCheckScript = [
|
|
45
48
|
'docker images',
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
].join(' ')
|
|
50
|
-
|
|
51
|
-
let dockerCreateScript = dockerCreateScriptArr.join(' ')
|
|
52
|
-
let dockerStartScript = 'docker start $name$'
|
|
53
|
-
let dockerStopScript = 'docker stop $name$'
|
|
54
|
-
let seleniumUrl = 'http://localhost:$port$'
|
|
55
|
-
|
|
56
|
-
const supportedHelpers = ['WebDriver']
|
|
57
|
-
const SELENOID_START_TIMEOUT = 2000
|
|
58
|
-
const SELENOID_STOP_TIMEOUT = 10000
|
|
59
|
-
const wait = (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
49
|
+
'--filter reference=\'selenoid/video-recorder\'',
|
|
50
|
+
'--filter reference=\'selenoid/chrome:latest\'',
|
|
51
|
+
'--filter reference=\'selenoid/firefox:latest\'',
|
|
52
|
+
].join(' ');
|
|
53
|
+
|
|
54
|
+
let dockerCreateScript = dockerCreateScriptArr.join(' ');
|
|
55
|
+
let dockerStartScript = 'docker start $name$';
|
|
56
|
+
let dockerStopScript = 'docker stop $name$';
|
|
57
|
+
let seleniumUrl = 'http://localhost:$port$';
|
|
58
|
+
|
|
59
|
+
const supportedHelpers = ['WebDriver'];
|
|
60
|
+
const SELENOID_START_TIMEOUT = 2000;
|
|
61
|
+
const SELENOID_STOP_TIMEOUT = 10000;
|
|
62
|
+
const wait = time => new Promise((res) => {
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
res();
|
|
66
|
+
}, time);
|
|
67
|
+
});
|
|
66
68
|
|
|
67
69
|
/**
|
|
68
70
|
* [Selenoid](https://aerokube.com/selenoid/) plugin automatically starts browsers and video recording.
|
|
@@ -180,18 +182,18 @@ const wait = (time) =>
|
|
|
180
182
|
*/
|
|
181
183
|
|
|
182
184
|
const selenoid = (config) => {
|
|
183
|
-
const helpers = container.helpers()
|
|
184
|
-
let helperName
|
|
185
|
+
const helpers = container.helpers();
|
|
186
|
+
let helperName;
|
|
185
187
|
|
|
186
188
|
for (const name of supportedHelpers) {
|
|
187
189
|
if (Object.keys(helpers).indexOf(name) > -1) {
|
|
188
|
-
helperName = name
|
|
190
|
+
helperName = name;
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
if (!helperName) {
|
|
193
|
-
output.print(`Selenoid plugin supported only for: ${supportedHelpers.toString()}`)
|
|
194
|
-
return // no helpers for Selenoid
|
|
195
|
+
output.print(`Selenoid plugin supported only for: ${supportedHelpers.toString()}`);
|
|
196
|
+
return; // no helpers for Selenoid
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
const {
|
|
@@ -201,184 +203,169 @@ const selenoid = (config) => {
|
|
|
201
203
|
additionalParams = '',
|
|
202
204
|
autoCreate = true,
|
|
203
205
|
port = 4444,
|
|
204
|
-
} = config
|
|
205
|
-
const passedTests = []
|
|
206
|
+
} = config;
|
|
207
|
+
const passedTests = [];
|
|
206
208
|
|
|
207
|
-
recorder.startUnlessRunning()
|
|
208
|
-
replaceScriptConfig({ name, additionalParams, port })
|
|
209
|
+
recorder.startUnlessRunning();
|
|
210
|
+
replaceScriptConfig({ name, additionalParams, port });
|
|
209
211
|
|
|
210
212
|
if (autoStart) {
|
|
211
213
|
event.dispatcher.on(event.all.before, () => {
|
|
212
214
|
recorder.add('Starting selenoid', () => {
|
|
213
|
-
output.debug('Staring Selenoid... ')
|
|
215
|
+
output.debug('Staring Selenoid... ');
|
|
214
216
|
return createAndStart(autoCreate)
|
|
215
217
|
.then(() => output.debug('Selenoid started'))
|
|
216
|
-
.catch((err) => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
})
|
|
220
|
-
})
|
|
218
|
+
.catch((err) => { throw new Error(err.stack); });
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
221
|
|
|
222
222
|
event.dispatcher.on(event.all.after, () => {
|
|
223
223
|
recorder.add('Stopping selenoid', () => {
|
|
224
|
-
output.debug('Stopping Selenoid...')
|
|
224
|
+
output.debug('Stopping Selenoid...');
|
|
225
225
|
return wait(SELENOID_STOP_TIMEOUT)
|
|
226
226
|
.then(() => deletePassedTests(passedTests))
|
|
227
227
|
.then(stopSelenoid)
|
|
228
228
|
.then(() => output.debug('Selenoid stopped'))
|
|
229
|
-
.catch((err) => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
})
|
|
233
|
-
})
|
|
229
|
+
.catch((err) => { throw new Error(err.stack); });
|
|
230
|
+
});
|
|
231
|
+
});
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
event.dispatcher.on(event.all.before, () => {
|
|
237
235
|
switch (helperName) {
|
|
238
|
-
case 'WebDriver':
|
|
239
|
-
setOptionsForWebdriver(config)
|
|
240
|
-
break
|
|
236
|
+
case 'WebDriver': setOptionsForWebdriver(config); break;
|
|
241
237
|
}
|
|
242
|
-
})
|
|
238
|
+
});
|
|
243
239
|
|
|
244
240
|
event.dispatcher.on(event.test.before, (test) => {
|
|
245
241
|
switch (helperName) {
|
|
246
|
-
case 'WebDriver':
|
|
247
|
-
setTestConfigForWebdriver(test)
|
|
248
|
-
break
|
|
242
|
+
case 'WebDriver': setTestConfigForWebdriver(test); break;
|
|
249
243
|
}
|
|
250
|
-
})
|
|
244
|
+
});
|
|
251
245
|
|
|
252
246
|
if (config.enableVideo) {
|
|
253
247
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
254
248
|
if (deletePassed) {
|
|
255
|
-
passedTests.push(test.title)
|
|
249
|
+
passedTests.push(test.title);
|
|
256
250
|
} else {
|
|
257
|
-
test.artifacts.video = videoSaved(test)
|
|
251
|
+
test.artifacts.video = videoSaved(test);
|
|
258
252
|
}
|
|
259
|
-
})
|
|
253
|
+
});
|
|
260
254
|
|
|
261
255
|
event.dispatcher.on(event.test.failed, (test) => {
|
|
262
|
-
test.artifacts.video = videoSaved(test)
|
|
263
|
-
})
|
|
256
|
+
test.artifacts.video = videoSaved(test);
|
|
257
|
+
});
|
|
264
258
|
}
|
|
265
|
-
}
|
|
259
|
+
};
|
|
266
260
|
|
|
267
|
-
|
|
261
|
+
export default selenoid;
|
|
268
262
|
|
|
269
263
|
function videoSaved(test) {
|
|
270
|
-
const fileName = `${clearString(test.title)}.mp4
|
|
271
|
-
const videoFile = path.join(global.output_dir, 'video', fileName)
|
|
272
|
-
output.debug(`Video has been saved to file://${videoFile}`)
|
|
273
|
-
const allureReporter = container.plugins('allure')
|
|
264
|
+
const fileName = `${clearString(test.title)}.mp4`;
|
|
265
|
+
const videoFile = path.join(global.output_dir, 'video', fileName);
|
|
266
|
+
output.debug(`Video has been saved to file://${videoFile}`);
|
|
267
|
+
const allureReporter = container.plugins('allure');
|
|
274
268
|
if (allureReporter) {
|
|
275
|
-
allureReporter.addAttachment('Video', fs.readFileSync(videoFile), 'video/mp4')
|
|
269
|
+
allureReporter.addAttachment('Video', fs.readFileSync(videoFile), 'video/mp4');
|
|
276
270
|
}
|
|
277
|
-
return videoFile
|
|
271
|
+
return videoFile;
|
|
278
272
|
}
|
|
279
273
|
|
|
280
274
|
const createSelenoidConfig = () => {
|
|
281
|
-
const configPath = path.join(global.codecept_dir, 'browsers.json')
|
|
275
|
+
const configPath = path.join(global.codecept_dir, 'browsers.json');
|
|
282
276
|
return new Promise((res, rej) => {
|
|
283
277
|
try {
|
|
284
278
|
if (fs.existsSync(configPath)) {
|
|
285
|
-
res(true)
|
|
279
|
+
res(true);
|
|
286
280
|
} else {
|
|
287
|
-
const data = new Uint8Array(Buffer.from(JSON.stringify(defaultBrowserConfig)))
|
|
288
|
-
fs.writeFileSync(configPath, data)
|
|
289
|
-
res(true)
|
|
281
|
+
const data = new Uint8Array(Buffer.from(JSON.stringify(defaultBrowserConfig)));
|
|
282
|
+
fs.writeFileSync(configPath, data);
|
|
283
|
+
res(true);
|
|
290
284
|
}
|
|
291
285
|
} catch (err) {
|
|
292
|
-
rej(err.stack)
|
|
286
|
+
rej(err.stack);
|
|
293
287
|
}
|
|
294
|
-
})
|
|
295
|
-
}
|
|
288
|
+
});
|
|
289
|
+
};
|
|
296
290
|
|
|
297
|
-
const createSelenoid = () => exec(dockerCreateScript)
|
|
291
|
+
const createSelenoid = () => exec(dockerCreateScript);
|
|
298
292
|
|
|
299
|
-
const startSelenoid = () => exec(dockerStartScript)
|
|
293
|
+
const startSelenoid = () => exec(dockerStartScript);
|
|
300
294
|
|
|
301
|
-
const stopSelenoid = () => exec(dockerStopScript)
|
|
295
|
+
const stopSelenoid = () => exec(dockerStopScript);
|
|
302
296
|
|
|
303
|
-
const checkDockerImage = () => exec(dockerImageCheckScript)
|
|
297
|
+
const checkDockerImage = () => exec(dockerImageCheckScript);
|
|
304
298
|
|
|
305
299
|
const pullImage = async () => {
|
|
306
|
-
const { stdout } = await checkDockerImage()
|
|
307
|
-
const pulls = []
|
|
308
|
-
let resultPromise
|
|
300
|
+
const { stdout } = await checkDockerImage();
|
|
301
|
+
const pulls = [];
|
|
302
|
+
let resultPromise;
|
|
309
303
|
|
|
310
|
-
output.print('Pulling in Selenoid containers. This may take a while when running the first time...')
|
|
304
|
+
output.print('Pulling in Selenoid containers. This may take a while when running the first time...');
|
|
311
305
|
|
|
312
|
-
console.time('Pulled containers')
|
|
306
|
+
console.time('Pulled containers');
|
|
313
307
|
if (!stdout.includes('selenoid/video-recorder')) {
|
|
314
|
-
output.debug('Pulling selenoid/video-recorder...')
|
|
315
|
-
resultPromise = exec('docker pull selenoid/video-recorder:latest-release')
|
|
316
|
-
output.debug('Pulled in selenoid/video-recorder')
|
|
317
|
-
)
|
|
318
|
-
pulls.push(resultPromise)
|
|
308
|
+
output.debug('Pulling selenoid/video-recorder...');
|
|
309
|
+
resultPromise = exec('docker pull selenoid/video-recorder:latest-release')
|
|
310
|
+
.then(() => output.debug('Pulled in selenoid/video-recorder'));
|
|
311
|
+
pulls.push(resultPromise);
|
|
319
312
|
}
|
|
320
313
|
if (!stdout.includes('selenoid/chrome')) {
|
|
321
|
-
output.debug('Pulling selenoid/chrome...')
|
|
322
|
-
resultPromise = exec('docker pull selenoid/chrome:latest')
|
|
323
|
-
output.debug('Pulled in selenoid/video-recorder')
|
|
324
|
-
)
|
|
325
|
-
pulls.push(resultPromise)
|
|
314
|
+
output.debug('Pulling selenoid/chrome...');
|
|
315
|
+
resultPromise = exec('docker pull selenoid/chrome:latest')
|
|
316
|
+
.then(() => output.debug('Pulled in selenoid/video-recorder'));
|
|
317
|
+
pulls.push(resultPromise);
|
|
326
318
|
}
|
|
327
319
|
if (!stdout.includes('selenoid/firefox')) {
|
|
328
|
-
output.debug('Pulling selenoid/firefox...')
|
|
329
|
-
resultPromise = exec('docker pull selenoid/firefox:latest')
|
|
330
|
-
|
|
320
|
+
output.debug('Pulling selenoid/firefox...');
|
|
321
|
+
resultPromise = exec('docker pull selenoid/firefox:latest')
|
|
322
|
+
.then(() => output.debug('Pulled in selenoid/chrome'));
|
|
323
|
+
pulls.push(resultPromise);
|
|
331
324
|
}
|
|
332
325
|
|
|
333
326
|
return Promise.all(pulls).then(() => {
|
|
334
|
-
console.timeEnd('Pulled containers')
|
|
335
|
-
})
|
|
336
|
-
}
|
|
327
|
+
console.timeEnd('Pulled containers');
|
|
328
|
+
});
|
|
329
|
+
};
|
|
337
330
|
|
|
338
331
|
function createAndStart(autoCreate) {
|
|
339
|
-
const selenoidCreated = autoCreate ? createSelenoidConfig().then(createSelenoid).then(pullImage) : Promise.resolve()
|
|
340
|
-
return selenoidCreated.then(startSelenoid).then(() => wait(SELENOID_START_TIMEOUT))
|
|
332
|
+
const selenoidCreated = autoCreate ? createSelenoidConfig().then(createSelenoid).then(pullImage) : Promise.resolve();
|
|
333
|
+
return selenoidCreated.then(startSelenoid).then(() => wait(SELENOID_START_TIMEOUT));
|
|
341
334
|
}
|
|
342
335
|
|
|
343
336
|
function deletePassedTests(passedTests) {
|
|
344
|
-
const deleteVideoPromiseList = passedTests
|
|
345
|
-
|
|
346
|
-
.map((test) => axios.delete(`${seleniumUrl}/video/${test}.mp4`))
|
|
347
|
-
const deleteLogPromiseList = passedTests
|
|
348
|
-
.map(clearString)
|
|
349
|
-
.map((test) => axios.delete(`${seleniumUrl}/logs/${test}.log`))
|
|
337
|
+
const deleteVideoPromiseList = passedTests.map(clearString).map(test => axios.delete(`${seleniumUrl}/video/${test}.mp4`));
|
|
338
|
+
const deleteLogPromiseList = passedTests.map(clearString).map(test => axios.delete(`${seleniumUrl}/logs/${test}.log`));
|
|
350
339
|
|
|
351
340
|
return Promise.all(deleteVideoPromiseList.concat(deleteLogPromiseList))
|
|
352
341
|
.then(() => output.debug('Deleted videos and logs for all passed tests'))
|
|
353
|
-
.catch(
|
|
342
|
+
.catch(err => output.output.error(`Error while deleting video and log files ${err.stack}`));
|
|
354
343
|
}
|
|
355
344
|
|
|
356
345
|
function setOptionsForWebdriver(config) {
|
|
357
|
-
const WebDriver = container.helpers('WebDriver')
|
|
358
|
-
WebDriver._setConfig(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}),
|
|
362
|
-
)
|
|
346
|
+
const WebDriver = container.helpers('WebDriver');
|
|
347
|
+
WebDriver._setConfig(deepMerge(WebDriver.options, {
|
|
348
|
+
capabilities: { 'selenoid:options': config },
|
|
349
|
+
}));
|
|
363
350
|
}
|
|
364
351
|
|
|
365
352
|
function setTestConfigForWebdriver(test) {
|
|
366
|
-
const WebDriver = container.helpers('WebDriver')
|
|
367
|
-
const fileName = clearString(test.title)
|
|
368
|
-
const { options } = WebDriver
|
|
353
|
+
const WebDriver = container.helpers('WebDriver');
|
|
354
|
+
const fileName = clearString(test.title);
|
|
355
|
+
const { options } = WebDriver;
|
|
369
356
|
recorder.add('setting selenoid capabilities', () => {
|
|
370
|
-
options.capabilities['selenoid:options'].name = test.title
|
|
371
|
-
options.capabilities['selenoid:options'].videoName = `${fileName}.mp4
|
|
372
|
-
options.capabilities['selenoid:options'].logName = `${fileName}.log
|
|
373
|
-
WebDriver._setConfig(options)
|
|
374
|
-
})
|
|
357
|
+
options.capabilities['selenoid:options'].name = test.title;
|
|
358
|
+
options.capabilities['selenoid:options'].videoName = `${fileName}.mp4`;
|
|
359
|
+
options.capabilities['selenoid:options'].logName = `${fileName}.log`;
|
|
360
|
+
WebDriver._setConfig(options);
|
|
361
|
+
});
|
|
375
362
|
}
|
|
376
363
|
|
|
377
364
|
function replaceScriptConfig(config) {
|
|
378
365
|
for (const key of Object.keys(config)) {
|
|
379
|
-
dockerCreateScript = dockerCreateScript.replace(new RegExp(`\\$${key}\\$`, 'g'), config[key])
|
|
366
|
+
dockerCreateScript = dockerCreateScript.replace(new RegExp(`\\$${key}\\$`, 'g'), config[key]);
|
|
380
367
|
}
|
|
381
|
-
dockerStartScript = dockerStartScript.replace('$name$', config.name)
|
|
382
|
-
dockerStopScript = dockerStopScript.replace('$name$', config.name)
|
|
383
|
-
seleniumUrl = seleniumUrl.replace('$port$', config.port)
|
|
368
|
+
dockerStartScript = dockerStartScript.replace('$name$', config.name);
|
|
369
|
+
dockerStopScript = dockerStopScript.replace('$name$', config.name);
|
|
370
|
+
seleniumUrl = seleniumUrl.replace('$port$', config.port);
|
|
384
371
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
const standardActingHelpers = [
|
|
1
|
+
const standardActingHelpers = [
|
|
2
|
+
'Playwright',
|
|
3
|
+
'WebDriver',
|
|
4
|
+
'Puppeteer',
|
|
5
|
+
'Appium',
|
|
6
|
+
'TestCafe',
|
|
7
|
+
];
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
export default standardActingHelpers;
|