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