codeceptjs 4.0.0-rc.2 → 4.0.0-rc.20

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.
Files changed (294) hide show
  1. package/README.md +39 -27
  2. package/bin/codecept.js +15 -2
  3. package/bin/codeceptq.js +49 -0
  4. package/bin/mcp-server.js +1187 -0
  5. package/docs/advanced.md +201 -0
  6. package/docs/agents.md +159 -0
  7. package/docs/ai.md +537 -0
  8. package/docs/aitrace.md +266 -0
  9. package/docs/api.md +332 -0
  10. package/docs/assertions.md +415 -0
  11. package/docs/auth.md +318 -0
  12. package/docs/basics.md +424 -0
  13. package/docs/bdd.md +539 -0
  14. package/docs/best.md +240 -0
  15. package/docs/bootstrap.md +132 -0
  16. package/docs/commands.md +352 -0
  17. package/docs/community-helpers.md +63 -0
  18. package/docs/configuration.md +230 -0
  19. package/docs/continuous-integration.md +497 -0
  20. package/docs/custom-helpers.md +297 -0
  21. package/docs/data.md +448 -0
  22. package/docs/debugging.md +332 -0
  23. package/docs/detox.md +235 -0
  24. package/docs/docker.md +136 -0
  25. package/docs/effects.md +179 -0
  26. package/docs/element-based-testing.md +295 -0
  27. package/docs/element-selection.md +125 -0
  28. package/docs/els.md +328 -0
  29. package/docs/examples.md +161 -0
  30. package/docs/heal.md +213 -0
  31. package/docs/helpers/ApiDataFactory.md +267 -0
  32. package/docs/helpers/Appium.md +1405 -0
  33. package/docs/helpers/Detox.md +665 -0
  34. package/docs/helpers/ExpectHelper.md +275 -0
  35. package/docs/helpers/FileSystem.md +152 -0
  36. package/docs/helpers/GraphQL.md +152 -0
  37. package/docs/helpers/GraphQLDataFactory.md +226 -0
  38. package/docs/helpers/JSONResponse.md +255 -0
  39. package/docs/helpers/Mochawesome.md +8 -0
  40. package/docs/helpers/MockRequest.md +377 -0
  41. package/docs/helpers/MockServer.md +212 -0
  42. package/docs/helpers/Playwright.md +2969 -0
  43. package/docs/helpers/Polly.md +44 -0
  44. package/docs/helpers/Protractor.md +1769 -0
  45. package/docs/helpers/Puppeteer-firefox.md +86 -0
  46. package/docs/helpers/Puppeteer.md +2690 -0
  47. package/docs/helpers/REST.md +289 -0
  48. package/docs/helpers/SoftExpectHelper.md +352 -0
  49. package/docs/helpers/WebDriver.md +2682 -0
  50. package/docs/hooks.md +339 -0
  51. package/docs/index.md +111 -0
  52. package/docs/installation.md +83 -0
  53. package/docs/internal-api.md +265 -0
  54. package/docs/internal-test-server.md +89 -0
  55. package/docs/locators.md +355 -0
  56. package/docs/mcp.md +485 -0
  57. package/docs/migration-4.md +556 -0
  58. package/docs/mobile.md +338 -0
  59. package/docs/pageobjects.md +399 -0
  60. package/docs/parallel.md +585 -0
  61. package/docs/playwright.md +714 -0
  62. package/docs/plugins.md +866 -0
  63. package/docs/puppeteer.md +314 -0
  64. package/docs/quickstart.md +120 -0
  65. package/docs/react.md +70 -0
  66. package/docs/reports.md +483 -0
  67. package/docs/retry.md +274 -0
  68. package/docs/secrets.md +150 -0
  69. package/docs/sessions.md +80 -0
  70. package/docs/shadow.md +68 -0
  71. package/docs/test-structure.md +275 -0
  72. package/docs/timeouts.md +183 -0
  73. package/docs/translation.md +247 -0
  74. package/docs/tutorial.md +271 -0
  75. package/docs/typescript.md +374 -0
  76. package/docs/web-element.md +251 -0
  77. package/docs/webdriver.md +708 -0
  78. package/docs/within.md +55 -0
  79. package/lib/ai.js +3 -2
  80. package/lib/aria.js +260 -0
  81. package/lib/assertions.js +18 -0
  82. package/lib/codecept.js +26 -23
  83. package/lib/command/check.js +2 -1
  84. package/lib/command/dryRun.js +24 -5
  85. package/lib/command/generate.js +2 -0
  86. package/lib/command/gherkin/snippets.js +5 -4
  87. package/lib/command/init.js +248 -269
  88. package/lib/command/list.js +150 -10
  89. package/lib/command/query.js +218 -0
  90. package/lib/command/run-multiple.js +2 -0
  91. package/lib/command/run-workers.js +2 -0
  92. package/lib/command/run.js +1 -1
  93. package/lib/command/workers/runTests.js +10 -10
  94. package/lib/config.js +77 -4
  95. package/lib/container.js +114 -17
  96. package/lib/effects.js +17 -0
  97. package/lib/element/WebElement.js +246 -2
  98. package/lib/els.js +12 -6
  99. package/lib/globals.js +32 -19
  100. package/lib/heal.js +4 -3
  101. package/lib/helper/ApiDataFactory.js +2 -1
  102. package/lib/helper/Appium.js +8 -8
  103. package/lib/helper/FileSystem.js +3 -2
  104. package/lib/helper/GraphQLDataFactory.js +2 -1
  105. package/lib/helper/Playwright.js +228 -162
  106. package/lib/helper/Puppeteer.js +208 -76
  107. package/lib/helper/WebDriver.js +173 -68
  108. package/lib/helper/errors/MultipleElementsFound.js +27 -110
  109. package/lib/helper/errors/NonFocusedType.js +8 -0
  110. package/lib/helper/extras/Download.js +45 -0
  111. package/lib/helper/extras/PlaywrightReactVueLocator.js +45 -36
  112. package/lib/helper/extras/elementSelection.js +58 -0
  113. package/lib/helper/extras/focusCheck.js +43 -0
  114. package/lib/helper/extras/richTextEditor.js +178 -0
  115. package/lib/helper/scripts/dropFile.js +11 -0
  116. package/lib/history.js +3 -2
  117. package/lib/html.js +103 -16
  118. package/lib/index.js +9 -1
  119. package/lib/listener/config.js +6 -4
  120. package/lib/listener/emptyRun.js +2 -1
  121. package/lib/listener/globalRetry.js +32 -6
  122. package/lib/listener/helpers.js +4 -1
  123. package/lib/listener/mocha.js +2 -1
  124. package/lib/listener/pageobjects.js +43 -0
  125. package/lib/listener/result.js +3 -2
  126. package/lib/locator.js +126 -3
  127. package/lib/mocha/cli.js +14 -2
  128. package/lib/mocha/factory.js +7 -2
  129. package/lib/mocha/inject.js +1 -1
  130. package/lib/mocha/scenarioConfig.js +2 -1
  131. package/lib/mocha/ui.js +5 -6
  132. package/lib/parser.js +2 -2
  133. package/lib/pause.js +38 -4
  134. package/lib/plugin/aiTrace.js +453 -0
  135. package/lib/plugin/analyze.js +1 -1
  136. package/lib/plugin/auth.js +3 -3
  137. package/lib/plugin/browser.js +77 -0
  138. package/lib/plugin/expose.js +159 -0
  139. package/lib/plugin/heal.js +44 -1
  140. package/lib/plugin/pageInfo.js +53 -49
  141. package/lib/plugin/pause.js +131 -0
  142. package/lib/plugin/pauseOnFail.js +10 -34
  143. package/lib/plugin/retryFailedStep.js +28 -19
  144. package/lib/plugin/screencast.js +287 -0
  145. package/lib/plugin/screenshot.js +563 -0
  146. package/lib/plugin/screenshotOnFail.js +8 -171
  147. package/lib/rerun.js +2 -1
  148. package/lib/result.js +2 -1
  149. package/lib/step/base.js +3 -2
  150. package/lib/step/config.js +15 -2
  151. package/lib/step/record.js +2 -2
  152. package/lib/store.js +72 -3
  153. package/lib/translation.js +2 -1
  154. package/lib/utils/mask_data.js +2 -1
  155. package/lib/utils/pluginParser.js +151 -0
  156. package/lib/utils/trace.js +297 -0
  157. package/lib/utils.js +77 -3
  158. package/lib/workers.js +52 -22
  159. package/package.json +19 -13
  160. package/typings/index.d.ts +19 -5
  161. package/docs/webapi/amOnPage.mustache +0 -11
  162. package/docs/webapi/appendField.mustache +0 -11
  163. package/docs/webapi/attachFile.mustache +0 -12
  164. package/docs/webapi/blur.mustache +0 -18
  165. package/docs/webapi/checkOption.mustache +0 -13
  166. package/docs/webapi/clearCookie.mustache +0 -9
  167. package/docs/webapi/clearField.mustache +0 -9
  168. package/docs/webapi/click.mustache +0 -29
  169. package/docs/webapi/clickLink.mustache +0 -8
  170. package/docs/webapi/closeCurrentTab.mustache +0 -7
  171. package/docs/webapi/closeOtherTabs.mustache +0 -8
  172. package/docs/webapi/dontSee.mustache +0 -11
  173. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  174. package/docs/webapi/dontSeeCookie.mustache +0 -8
  175. package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
  176. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  177. package/docs/webapi/dontSeeElement.mustache +0 -8
  178. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  179. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  180. package/docs/webapi/dontSeeInField.mustache +0 -11
  181. package/docs/webapi/dontSeeInSource.mustache +0 -8
  182. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  183. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  184. package/docs/webapi/doubleClick.mustache +0 -13
  185. package/docs/webapi/downloadFile.mustache +0 -12
  186. package/docs/webapi/dragAndDrop.mustache +0 -9
  187. package/docs/webapi/dragSlider.mustache +0 -11
  188. package/docs/webapi/executeAsyncScript.mustache +0 -24
  189. package/docs/webapi/executeScript.mustache +0 -26
  190. package/docs/webapi/fillField.mustache +0 -16
  191. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  192. package/docs/webapi/focus.mustache +0 -13
  193. package/docs/webapi/forceClick.mustache +0 -28
  194. package/docs/webapi/forceRightClick.mustache +0 -18
  195. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  196. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  197. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  198. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  199. package/docs/webapi/grabCookie.mustache +0 -11
  200. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  201. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  202. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  203. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  204. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  205. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  206. package/docs/webapi/grabGeoLocation.mustache +0 -8
  207. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  208. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  209. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  210. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  211. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  212. package/docs/webapi/grabPopupText.mustache +0 -5
  213. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  214. package/docs/webapi/grabSource.mustache +0 -8
  215. package/docs/webapi/grabTextFrom.mustache +0 -10
  216. package/docs/webapi/grabTextFromAll.mustache +0 -9
  217. package/docs/webapi/grabTitle.mustache +0 -8
  218. package/docs/webapi/grabValueFrom.mustache +0 -9
  219. package/docs/webapi/grabValueFromAll.mustache +0 -8
  220. package/docs/webapi/grabWebElement.mustache +0 -9
  221. package/docs/webapi/grabWebElements.mustache +0 -9
  222. package/docs/webapi/moveCursorTo.mustache +0 -12
  223. package/docs/webapi/openNewTab.mustache +0 -7
  224. package/docs/webapi/pressKey.mustache +0 -12
  225. package/docs/webapi/pressKeyDown.mustache +0 -12
  226. package/docs/webapi/pressKeyUp.mustache +0 -12
  227. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  228. package/docs/webapi/refreshPage.mustache +0 -6
  229. package/docs/webapi/resizeWindow.mustache +0 -6
  230. package/docs/webapi/rightClick.mustache +0 -14
  231. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  232. package/docs/webapi/saveScreenshot.mustache +0 -12
  233. package/docs/webapi/say.mustache +0 -10
  234. package/docs/webapi/scrollIntoView.mustache +0 -11
  235. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  236. package/docs/webapi/scrollPageToTop.mustache +0 -6
  237. package/docs/webapi/scrollTo.mustache +0 -12
  238. package/docs/webapi/see.mustache +0 -11
  239. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  240. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  241. package/docs/webapi/seeCookie.mustache +0 -8
  242. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  243. package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
  244. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  245. package/docs/webapi/seeElement.mustache +0 -8
  246. package/docs/webapi/seeElementInDOM.mustache +0 -8
  247. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  248. package/docs/webapi/seeInField.mustache +0 -12
  249. package/docs/webapi/seeInPopup.mustache +0 -8
  250. package/docs/webapi/seeInSource.mustache +0 -7
  251. package/docs/webapi/seeInTitle.mustache +0 -8
  252. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  253. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  254. package/docs/webapi/seeTextEquals.mustache +0 -9
  255. package/docs/webapi/seeTitleEquals.mustache +0 -8
  256. package/docs/webapi/seeTraffic.mustache +0 -36
  257. package/docs/webapi/selectOption.mustache +0 -21
  258. package/docs/webapi/setCookie.mustache +0 -16
  259. package/docs/webapi/setGeoLocation.mustache +0 -12
  260. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  261. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  262. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  263. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  264. package/docs/webapi/switchTo.mustache +0 -9
  265. package/docs/webapi/switchToNextTab.mustache +0 -10
  266. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  267. package/docs/webapi/type.mustache +0 -21
  268. package/docs/webapi/uncheckOption.mustache +0 -13
  269. package/docs/webapi/wait.mustache +0 -8
  270. package/docs/webapi/waitForClickable.mustache +0 -11
  271. package/docs/webapi/waitForCookie.mustache +0 -9
  272. package/docs/webapi/waitForDetached.mustache +0 -10
  273. package/docs/webapi/waitForDisabled.mustache +0 -6
  274. package/docs/webapi/waitForElement.mustache +0 -11
  275. package/docs/webapi/waitForEnabled.mustache +0 -6
  276. package/docs/webapi/waitForFunction.mustache +0 -17
  277. package/docs/webapi/waitForInvisible.mustache +0 -10
  278. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  279. package/docs/webapi/waitForText.mustache +0 -13
  280. package/docs/webapi/waitForValue.mustache +0 -10
  281. package/docs/webapi/waitForVisible.mustache +0 -10
  282. package/docs/webapi/waitInUrl.mustache +0 -9
  283. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  284. package/docs/webapi/waitToHide.mustache +0 -10
  285. package/docs/webapi/waitUrlEquals.mustache +0 -10
  286. package/lib/helper/AI.js +0 -214
  287. package/lib/listener/enhancedGlobalRetry.js +0 -110
  288. package/lib/plugin/enhancedRetryFailedStep.js +0 -99
  289. package/lib/plugin/htmlReporter.js +0 -3648
  290. package/lib/plugin/stepByStepReport.js +0 -427
  291. package/lib/plugin/subtitles.js +0 -89
  292. package/lib/retryCoordinator.js +0 -207
  293. package/typings/promiseBasedTypes.d.ts +0 -9469
  294. package/typings/types.d.ts +0 -11402
@@ -1,427 +0,0 @@
1
- import colors from 'chalk'
2
- import crypto from 'crypto'
3
- import figures from 'figures'
4
- import fs from 'fs'
5
- import { mkdirp } from 'mkdirp'
6
- import path from 'path'
7
- import cheerio from 'cheerio'
8
-
9
- import Container from '../container.js'
10
- import recorder from '../recorder.js'
11
- import event from '../event.js'
12
- import output from '../output.js'
13
- import { template, deleteDir } from '../utils.js'
14
-
15
- const supportedHelpers = Container.STANDARD_ACTING_HELPERS
16
-
17
- const defaultConfig = {
18
- deleteSuccessful: true,
19
- animateSlides: true,
20
- ignoreSteps: [],
21
- fullPageScreenshots: false,
22
- output: global.output_dir,
23
- screenshotsForAllureReport: false,
24
- disableScreenshotOnFail: true,
25
- }
26
-
27
- const templates = {}
28
-
29
- /**
30
- * ![step-by-step-report](https://codecept.io/img/codeceptjs-slideshow.gif)
31
- *
32
- * Generates step by step report for a test.
33
- * After each step in a test a screenshot is created. After test executed screenshots are combined into slideshow.
34
- * By default, reports are generated only for failed tests.
35
- *
36
- *
37
- * Run tests with plugin enabled:
38
- *
39
- * ```
40
- * npx codeceptjs run --plugins stepByStepReport
41
- * ```
42
- *
43
- * #### Configuration
44
- *
45
- * ```js
46
- * "plugins": {
47
- * "stepByStepReport": {
48
- * "enabled": true
49
- * }
50
- * }
51
- * ```
52
- *
53
- * Possible config options:
54
- *
55
- * * `deleteSuccessful`: do not save screenshots for successfully executed tests. Default: true.
56
- * * `animateSlides`: should animation for slides to be used. Default: true.
57
- * * `ignoreSteps`: steps to ignore in report. Array of RegExps is expected. Recommended to skip `grab*` and `wait*` steps.
58
- * * `fullPageScreenshots`: should full page screenshots be used. Default: false.
59
- * * `output`: a directory where reports should be stored. Default: `output`.
60
- * * `screenshotsForAllureReport`: If Allure plugin is enabled this plugin attaches each saved screenshot to allure report. Default: false.
61
- * * `disableScreenshotOnFail : Disables the capturing of screeshots after the failed step. Default: true.
62
- *
63
- * @param {*} config
64
- */
65
-
66
- export default function (config) {
67
- const helpers = Container.helpers()
68
- let helper
69
-
70
- config = Object.assign(defaultConfig, config)
71
-
72
- for (const helperName of supportedHelpers) {
73
- if (Object.keys(helpers).indexOf(helperName) > -1) {
74
- helper = helpers[helperName]
75
- }
76
- }
77
-
78
- if (!helper) return // no helpers for screenshot
79
-
80
- let dir
81
- let stepNum
82
- let slides = {}
83
- let error
84
- let savedStep = null
85
- let currentTest = null
86
- let scenarioFailed = false
87
-
88
- const recordedTests = {}
89
- const pad = '0000'
90
- const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
91
-
92
- event.dispatcher.on(event.suite.before, suite => {
93
- stepNum = -1
94
- })
95
-
96
- event.dispatcher.on(event.test.before, test => {
97
- const sha256hash = crypto
98
- .createHash('sha256')
99
- .update(test.file + test.title)
100
- .digest('hex')
101
- dir = path.join(reportDir, `record_${sha256hash}`)
102
- mkdirp.sync(dir)
103
- stepNum = 0
104
- error = null
105
- slides = {}
106
- savedStep = null
107
- currentTest = test
108
- })
109
-
110
- event.dispatcher.on(event.step.failed, step => {
111
- recorder.add('screenshot of failed test', async () => persistStep(step), true)
112
- })
113
-
114
- event.dispatcher.on(event.step.after, step => {
115
- recorder.add('screenshot of step of test', async () => persistStep(step), true)
116
- })
117
-
118
- event.dispatcher.on(event.test.passed, test => {
119
- if (!config.deleteSuccessful) return persist(test)
120
- // cleanup
121
- deleteDir(dir)
122
- })
123
-
124
- event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
125
- if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
126
- // no browser here
127
- return
128
- }
129
-
130
- persist(test)
131
- })
132
-
133
- event.dispatcher.on(event.all.result, () => {
134
- if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
135
- generateRecordsHtml(recordedTests)
136
- })
137
-
138
- event.dispatcher.on(event.workers.result, async () => {
139
- await recorder.add(() => {
140
- const recordedTests = getRecordFoldersWithDetails(reportDir)
141
- generateRecordsHtml(recordedTests)
142
- })
143
- })
144
-
145
- function getRecordFoldersWithDetails(dirPath) {
146
- let results = {}
147
-
148
- try {
149
- const items = fs.readdirSync(dirPath, { withFileTypes: true })
150
-
151
- items.forEach(item => {
152
- if (item.isDirectory() && item.name.startsWith('record_')) {
153
- const recordFolderPath = path.join(dirPath, item.name)
154
- const indexPath = path.join(recordFolderPath, 'index.html')
155
-
156
- let name = ''
157
- if (fs.existsSync(indexPath)) {
158
- try {
159
- const htmlContent = fs.readFileSync(indexPath, 'utf-8')
160
- const $ = cheerio.load(htmlContent)
161
- name = $('.navbar-brand').text().trim()
162
- } catch (err) {
163
- console.error(`Error reading index.html in ${recordFolderPath}:`, err.message)
164
- }
165
- }
166
-
167
- results[name || 'Unkown'] = `${item.name}/index.html`
168
- }
169
- })
170
- } catch (err) {
171
- console.error(`Error reading directory ${dirPath}:`, err.message)
172
- }
173
-
174
- return results
175
- }
176
-
177
- function generateRecordsHtml(recordedTests) {
178
- let links = ''
179
-
180
- for (const link in recordedTests) {
181
- links += `<li><a href="${recordedTests[link]}">${link}</a></li>\n`
182
- }
183
-
184
- const indexHTML = template(templates.index, {
185
- time: Date().toString(),
186
- records: links,
187
- })
188
-
189
- fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
190
-
191
- output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`)
192
- }
193
-
194
- async function persistStep(step) {
195
- if (stepNum === -1) return // Ignore steps from BeforeSuite function
196
- if (isStepIgnored(step)) return
197
- if (savedStep === step) return // already saved
198
- // Ignore steps from BeforeSuite function
199
- if (scenarioFailed && config.disableScreenshotOnFail) return
200
- if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
201
- if (!step.test) return // Ignore steps from AfterSuite
202
-
203
- const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png`
204
- if (step.status === 'failed') {
205
- scenarioFailed = true
206
- }
207
- stepNum++
208
- slides[fileName] = step
209
- try {
210
- await helper.saveScreenshot(path.join(dir, fileName), config.fullPageScreenshots)
211
- } catch (err) {
212
- output.plugin(`Can't save step screenshot: ${err}`)
213
- error = err
214
- return
215
- } finally {
216
- savedStep = step
217
- }
218
-
219
- if (!currentTest.artifacts.screenshots) currentTest.artifacts.screenshots = []
220
- // added attachments to test
221
- currentTest.artifacts.screenshots.push(path.join(dir, fileName))
222
-
223
- const allureReporter = Container.plugins('allure')
224
- if (allureReporter && config.screenshotsForAllureReport) {
225
- output.plugin('stepByStepReport', 'Adding screenshot to Allure')
226
- allureReporter.addAttachment(`Screenshot of step ${step}`, fs.readFileSync(path.join(dir, fileName)), 'image/png')
227
- }
228
- }
229
-
230
- function persist(test) {
231
- if (error) return
232
-
233
- let indicatorHtml = ''
234
- let slideHtml = ''
235
-
236
- for (const i in slides) {
237
- const step = slides[i]
238
- const stepNum = parseInt(i, 10)
239
- indicatorHtml += template(templates.indicator, {
240
- step: stepNum,
241
- isActive: stepNum ? '' : 'class="active"',
242
- })
243
-
244
- slideHtml += template(templates.slides, {
245
- image: i,
246
- caption: step.toString().replace(/\[\d{2}m/g, ''), // remove ANSI escape sequence
247
- isActive: stepNum ? '' : 'active',
248
- isError: step.status === 'failed' ? 'error' : '',
249
- })
250
- }
251
-
252
- const html = template(templates.global, {
253
- indicators: indicatorHtml,
254
- slides: slideHtml,
255
- feature: test.parent && test.parent.title,
256
- test: test.title,
257
- carousel_class: config.animateSlides ? ' slide' : '',
258
- })
259
-
260
- const index = path.join(dir, 'index.html')
261
- fs.writeFileSync(index, html)
262
- recordedTests[`${test.parent.title}: ${test.title}`] = path.relative(reportDir, index)
263
- }
264
-
265
- function isStepIgnored(step) {
266
- if (!config.ignoreSteps) return
267
- for (const pattern of config.ignoreSteps || []) {
268
- if (step.name.match(pattern)) return true
269
- }
270
- return false
271
- }
272
- }
273
-
274
- templates.slides = `
275
- <div class="item {{isActive}}">
276
- <div class="fill">
277
- <img src="{{image}}">
278
- </div>
279
- <div class="carousel-caption {{isError}}">
280
- <h2>{{caption}}</h2>
281
- <small>scroll up and down to see the full page</small>
282
- </div>
283
- </div>
284
- `
285
-
286
- templates.indicator = `
287
- <li data-target="#steps" data-slide-to="{{step}}" {{isActive}}></li>
288
- `
289
-
290
- templates.index = `
291
- <!DOCTYPE html>
292
- <html lang="en">
293
- <head>
294
- <meta charset="utf-8">
295
- <meta name="viewport" content="width=device-width, initial-scale=1">
296
- <title>Step by Steps Report</title>
297
-
298
- <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
299
- </head>
300
- <body>
301
- <!-- Navigation -->
302
- <nav class="navbar navbar-default" role="navigation">
303
- <div class="navbar-header">
304
- <a class="navbar-brand" href="#">Step by Step Report
305
- </a>
306
- </div>
307
- </nav>
308
- <div class="container">
309
- <h1>Recorded <small>@ {{time}}</small></h1>
310
- <ul>
311
- {{records}}
312
- </ul>
313
- </div>
314
-
315
- </body>
316
- </html>
317
- `
318
-
319
- templates.global = `
320
- <!DOCTYPE html>
321
- <html lang="en">
322
- <head>
323
- <meta charset="utf-8">
324
- <meta name="viewport" content="width=device-width, initial-scale=1">
325
- <title>Recorder Result</title>
326
-
327
- <!-- Bootstrap Core CSS -->
328
- <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
329
-
330
- <style>
331
- html,
332
- body {
333
- height: 100%;
334
- }
335
- .carousel,
336
- .item,
337
- .active {
338
- height: 100%;
339
- }
340
- .navbar {
341
- margin-bottom: 0px !important;
342
- }
343
- .carousel-caption {
344
- background: rgba(0,0,0,0.8);
345
- padding-bottom: 50px !important;
346
- }
347
- .carousel-caption.error {
348
- background: #c0392b !important;
349
- }
350
-
351
- .carousel-inner {
352
- height: 100%;
353
- }
354
-
355
- .fill {
356
- width: 100%;
357
- height: 100%;
358
- text-align: center;
359
- overflow-y: scroll;
360
- background-position: top;
361
- -webkit-background-size: cover;
362
- -moz-background-size: cover;
363
- background-size: cover;
364
- -o-background-size: cover;
365
- }
366
- </style>
367
- </head>
368
- <body>
369
- <!-- Navigation -->
370
- <nav class="navbar navbar-default" role="navigation">
371
- <div class="navbar-header">
372
- <a class="navbar-brand" href="../records.html">
373
- &laquo;
374
- {{feature}}
375
- <small>{{test}}</small>
376
- </a>
377
- </div>
378
- </nav>
379
- <header id="steps" class="carousel{{carousel_class}}">
380
- <!-- Indicators -->
381
- <ol class="carousel-indicators">
382
- {{indicators}}
383
- </ol>
384
-
385
- <!-- Wrapper for Slides -->
386
- <div class="carousel-inner">
387
- {{slides}}
388
- </div>
389
-
390
- <!-- Controls -->
391
- <a class="left carousel-control" href="#steps" data-slide="prev">
392
- <span class="icon-prev"></span>
393
- </a>
394
- <a class="right carousel-control" href="#steps" data-slide="next">
395
- <span class="icon-next"></span>
396
- </a>
397
-
398
- </header>
399
-
400
- <!-- jQuery -->
401
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
402
- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
403
-
404
- <!-- Script to Activate the Carousel -->
405
- <script>
406
- $('.carousel').carousel({
407
- wrap: true,
408
- interval: false
409
- })
410
-
411
- $(document).bind('keyup', function(e) {
412
- if(e.keyCode==39){
413
- jQuery('a.carousel-control.right').trigger('click');
414
- }
415
-
416
- else if(e.keyCode==37){
417
- jQuery('a.carousel-control.left').trigger('click');
418
- }
419
-
420
- });
421
-
422
- </script>
423
-
424
- </body>
425
-
426
- </html>
427
- `
@@ -1,89 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid'
2
- import fs from 'fs'
3
- const fsPromise = fs.promises
4
- import path from 'path'
5
- import event from '../event.js'
6
-
7
- // This will convert a given timestamp in milliseconds to
8
- // an SRT recognized timestamp, ie HH:mm:ss,SSS
9
- function formatTimestamp(timestampInMs) {
10
- const date = new Date(0, 0, 0, 0, 0, 0, timestampInMs)
11
- const hours = date.getHours()
12
- const minutes = date.getMinutes()
13
- const seconds = date.getSeconds()
14
- const ms = timestampInMs - (hours * 3600000 + minutes * 60000 + seconds * 1000)
15
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')},${ms.toString().padStart(3, '0')}`
16
- }
17
-
18
- let steps = {}
19
- let testStartedAt
20
- /**
21
- * Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test
22
- *
23
- * #### Configuration
24
- * ```js
25
- * plugins: {
26
- * subtitles: {
27
- * enabled: true
28
- * }
29
- * }
30
- * ```
31
- */
32
- export default function () {
33
- event.dispatcher.on(event.test.before, _ => {
34
- testStartedAt = Date.now()
35
- steps = {}
36
- })
37
-
38
- event.dispatcher.on(event.step.started, step => {
39
- const stepStartedAt = Date.now()
40
- step.id = uuidv4()
41
-
42
- let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})`
43
- if (title.length > 100) {
44
- title = `${title.substring(0, 100)}...`
45
- }
46
-
47
- steps[step.id] = {
48
- start: formatTimestamp(stepStartedAt - testStartedAt),
49
- startedAt: stepStartedAt,
50
- title,
51
- }
52
- })
53
-
54
- event.dispatcher.on(event.step.finished, step => {
55
- if (step && step.id && steps[step.id]) {
56
- steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
57
- }
58
- })
59
-
60
- event.dispatcher.on(event.test.after, async test => {
61
- if (test && test.artifacts && test.artifacts.video) {
62
- const stepsSortedByStartTime = Object.values(steps)
63
- stepsSortedByStartTime.sort((stepA, stepB) => {
64
- return stepA.startedAt - stepB.startedAt
65
- })
66
-
67
- let subtitle = ''
68
-
69
- // For an SRT file, every subtitle has to be in the format as mentioned below:
70
- //
71
- // 1
72
- // HH:mm:ss,SSS --> HH:mm:ss,SSS
73
- // [title]
74
- stepsSortedByStartTime.forEach((step, index) => {
75
- if (step.end) {
76
- subtitle = `${subtitle}${index + 1}
77
- ${step.start} --> ${step.end}
78
- ${step.title}
79
-
80
- `
81
- }
82
- })
83
-
84
- const { dir: artifactsDirectory, name: fileName } = path.parse(test.artifacts.video)
85
- await fsPromise.writeFile(`${artifactsDirectory}/${fileName}.srt`, subtitle)
86
- test.artifacts.subtitle = `${artifactsDirectory}/${fileName}.srt`
87
- }
88
- })
89
- }