codeceptjs 3.7.0-beta.1 → 3.7.0-beta.10

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 (73) hide show
  1. package/README.md +9 -10
  2. package/bin/codecept.js +7 -0
  3. package/lib/actor.js +46 -92
  4. package/lib/ai.js +130 -121
  5. package/lib/codecept.js +2 -2
  6. package/lib/command/check.js +186 -0
  7. package/lib/command/definitions.js +3 -1
  8. package/lib/command/interactive.js +1 -1
  9. package/lib/command/run-workers.js +2 -54
  10. package/lib/command/workers/runTests.js +64 -225
  11. package/lib/container.js +27 -0
  12. package/lib/effects.js +218 -0
  13. package/lib/els.js +87 -106
  14. package/lib/event.js +18 -17
  15. package/lib/heal.js +10 -0
  16. package/lib/helper/AI.js +2 -1
  17. package/lib/helper/Appium.js +31 -22
  18. package/lib/helper/Playwright.js +22 -1
  19. package/lib/helper/Puppeteer.js +5 -0
  20. package/lib/helper/WebDriver.js +29 -8
  21. package/lib/listener/emptyRun.js +2 -5
  22. package/lib/listener/exit.js +5 -8
  23. package/lib/listener/globalTimeout.js +66 -10
  24. package/lib/listener/result.js +12 -0
  25. package/lib/listener/steps.js +3 -6
  26. package/lib/listener/store.js +9 -1
  27. package/lib/mocha/asyncWrapper.js +15 -3
  28. package/lib/mocha/cli.js +79 -28
  29. package/lib/mocha/featureConfig.js +13 -0
  30. package/lib/mocha/hooks.js +32 -3
  31. package/lib/mocha/inject.js +5 -0
  32. package/lib/mocha/scenarioConfig.js +11 -0
  33. package/lib/mocha/suite.js +27 -1
  34. package/lib/mocha/test.js +102 -3
  35. package/lib/mocha/types.d.ts +11 -0
  36. package/lib/output.js +75 -73
  37. package/lib/pause.js +3 -10
  38. package/lib/plugin/analyze.js +349 -0
  39. package/lib/plugin/autoDelay.js +2 -2
  40. package/lib/plugin/commentStep.js +5 -0
  41. package/lib/plugin/customReporter.js +52 -0
  42. package/lib/plugin/heal.js +30 -0
  43. package/lib/plugin/pageInfo.js +140 -0
  44. package/lib/plugin/retryTo.js +18 -118
  45. package/lib/plugin/screenshotOnFail.js +12 -17
  46. package/lib/plugin/standardActingHelpers.js +4 -1
  47. package/lib/plugin/stepByStepReport.js +6 -5
  48. package/lib/plugin/stepTimeout.js +1 -1
  49. package/lib/plugin/tryTo.js +17 -107
  50. package/lib/recorder.js +5 -5
  51. package/lib/rerun.js +43 -42
  52. package/lib/result.js +161 -0
  53. package/lib/step/base.js +228 -0
  54. package/lib/step/config.js +50 -0
  55. package/lib/step/func.js +46 -0
  56. package/lib/step/helper.js +50 -0
  57. package/lib/step/meta.js +99 -0
  58. package/lib/step/record.js +74 -0
  59. package/lib/step/retry.js +11 -0
  60. package/lib/step/section.js +55 -0
  61. package/lib/step.js +20 -347
  62. package/lib/steps.js +50 -0
  63. package/lib/store.js +4 -0
  64. package/lib/timeout.js +66 -0
  65. package/lib/utils.js +93 -0
  66. package/lib/within.js +2 -2
  67. package/lib/workers.js +29 -49
  68. package/package.json +23 -20
  69. package/typings/index.d.ts +5 -4
  70. package/typings/promiseBasedTypes.d.ts +617 -7
  71. package/typings/types.d.ts +663 -34
  72. package/lib/listener/artifacts.js +0 -19
  73. package/lib/plugin/debugErrors.js +0 -67
@@ -0,0 +1,11 @@
1
+ const recorder = require('../recorder')
2
+ const event = require('../event')
3
+
4
+ function retryStep(opts) {
5
+ if (opts === undefined) opts = 1
6
+ recorder.retry(opts)
7
+ // remove retry once the step passed
8
+ recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()))
9
+ }
10
+
11
+ module.exports = retryStep
@@ -0,0 +1,55 @@
1
+ const MetaStep = require('./meta')
2
+ const event = require('../event')
3
+
4
+ let currentSection
5
+
6
+ class Section {
7
+ constructor(name = '') {
8
+ this.name = name
9
+
10
+ this.metaStep = new MetaStep(null, name)
11
+
12
+ this.attachMetaStep = step => {
13
+ if (currentSection !== this) return
14
+ if (!step) return
15
+ const metaStep = getRootMetaStep(step)
16
+
17
+ if (metaStep !== this.metaStep) {
18
+ metaStep.metaStep = this.metaStep
19
+ }
20
+ }
21
+ }
22
+
23
+ hidden() {
24
+ this.metaStep.collapsed = true
25
+ return this
26
+ }
27
+
28
+ start() {
29
+ if (currentSection) currentSection.end()
30
+ currentSection = this
31
+ event.dispatcher.prependListener(event.step.before, this.attachMetaStep)
32
+ event.dispatcher.once(event.test.finished, () => this.end())
33
+ return this
34
+ }
35
+
36
+ end() {
37
+ currentSection = null
38
+ event.dispatcher.off(event.step.started, this.attachMetaStep)
39
+ return this
40
+ }
41
+
42
+ /**
43
+ * @returns {Section}
44
+ */
45
+ static current() {
46
+ return currentSection
47
+ }
48
+ }
49
+
50
+ function getRootMetaStep(step) {
51
+ if (step.metaStep) return getRootMetaStep(step.metaStep)
52
+ return step
53
+ }
54
+
55
+ module.exports = Section
package/lib/step.js CHANGED
@@ -1,353 +1,26 @@
1
- // TODO: place MetaStep in other file, disable rule
2
-
3
- const color = require('chalk')
4
- const store = require('./store')
5
- const Secret = require('./secret')
6
- const event = require('./event')
7
- const { ucfirst } = require('./utils')
8
-
9
- const STACK_LINE = 4
10
-
1
+ // refactored step class, moved to helper
11
2
  /**
12
- * Each command in test executed through `I.` object is wrapped in Step.
13
- * Step allows logging executed commands and triggers hook before and after step execution.
14
- * @param {CodeceptJS.Helper} helper
15
- * @param {string} name
3
+ * Step is wrapper around a helper method.
4
+ * It is used to create a new step that is a combination of other steps.
16
5
  */
17
- class Step {
18
- static get TIMEOUT_ORDER() {
19
- return {
20
- /**
21
- * timeouts set with order below zero only override timeouts of higher order if their value is smaller
22
- */
23
- testOrSuite: -5,
24
- /**
25
- * 0-9 - designated for override of timeouts set from code, 5 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=true
26
- */
27
- stepTimeoutHard: 5,
28
- /**
29
- * 10-19 - designated for timeouts set from code, 15 is order of I.setTimeout(t) operation
30
- */
31
- codeLimitTime: 15,
32
- /**
33
- * 20-29 - designated for timeout settings which could be overriden in tests code, 25 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=false
34
- */
35
- stepTimeoutSoft: 25,
36
- }
37
- }
38
-
39
- constructor(helper, name) {
40
- /** @member {string} */
41
- this.actor = 'I' // I = actor
42
- /** @member {CodeceptJS.Helper} */
43
- this.helper = helper // corresponding helper
44
- /** @member {string} */
45
- this.name = name // name of a step console
46
- /** @member {string} */
47
- this.helperMethod = name // helper method
48
- /** @member {string} */
49
- this.status = 'pending'
50
- /**
51
- * @member {string} suffix
52
- * @memberof CodeceptJS.Step#
53
- */
54
- /** @member {string} */
55
- this.prefix = this.suffix = ''
56
- /** @member {string} */
57
- this.comment = ''
58
- /** @member {Array<*>} */
59
- this.args = []
60
- /** @member {MetaStep} */
61
- this.metaStep = undefined
62
- /** @member {string} */
63
- this.stack = ''
64
-
65
- const timeouts = new Map()
66
- /**
67
- * @method
68
- * @returns {number|undefined}
69
- */
70
- this.getTimeout = function () {
71
- let totalTimeout
72
- // iterate over all timeouts starting from highest values of order
73
- new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
74
- if (
75
- timeout !== undefined &&
76
- // when orders >= 0 - timeout value overrides those set with higher order elements
77
- (order >= 0 ||
78
- // when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
79
- totalTimeout === undefined ||
80
- // when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
81
- (timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
82
- ) {
83
- totalTimeout = timeout
84
- }
85
- })
86
- return totalTimeout
87
- }
88
- /**
89
- * @method
90
- * @param {number} timeout - timeout in milliseconds or 0 if no timeout
91
- * @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
92
- * When order below 0 value of timeout only override if new value is lower
93
- */
94
- this.setTimeout = function (timeout, order) {
95
- timeouts.set(order, timeout)
96
- }
97
-
98
- this.setTrace()
99
- }
100
-
101
- /** @function */
102
- setTrace() {
103
- Error.captureStackTrace(this)
104
- }
105
-
106
- /** @param {Array<*>} args */
107
- setArguments(args) {
108
- this.args = args
109
- }
110
-
111
- /**
112
- * @param {...any} args
113
- * @return {*}
114
- */
115
- run() {
116
- this.args = Array.prototype.slice.call(arguments)
117
- if (store.dryRun) {
118
- this.setStatus('success')
119
- return Promise.resolve(new Proxy({}, dryRunResolver()))
120
- }
121
- let result
122
- try {
123
- if (this.helperMethod !== 'say') {
124
- result = this.helper[this.helperMethod].apply(this.helper, this.args)
125
- }
126
- this.setStatus('success')
127
- } catch (err) {
128
- this.setStatus('failed')
129
- throw err
130
- }
131
- return result
132
- }
133
-
134
- setActor(actor) {
135
- this.actor = actor || ''
136
- }
137
-
138
- /** @param {string} status */
139
- setStatus(status) {
140
- this.status = status
141
- if (this.metaStep) {
142
- this.metaStep.setStatus(status)
143
- }
144
- }
145
-
146
- /** @return {string} */
147
- humanize() {
148
- return humanizeString(this.name)
149
- }
150
-
151
- /** @return {string} */
152
- humanizeArgs() {
153
- return this.args
154
- .map(arg => {
155
- if (!arg) {
156
- return ''
157
- }
158
- if (typeof arg === 'string') {
159
- return `"${arg}"`
160
- }
161
- if (Array.isArray(arg)) {
162
- try {
163
- const res = JSON.stringify(arg)
164
- return res
165
- } catch (err) {
166
- return `[${arg.toString()}]`
167
- }
168
- } else if (typeof arg === 'function') {
169
- return arg.toString()
170
- } else if (typeof arg === 'undefined') {
171
- return `${arg}`
172
- } else if (arg instanceof Secret) {
173
- return arg.getMasked()
174
- } else if (arg.toString && arg.toString() !== '[object Object]') {
175
- return arg.toString()
176
- } else if (typeof arg === 'object') {
177
- const returnedArg = {}
178
- for (const [key, value] of Object.entries(arg)) {
179
- returnedArg[key] = value
180
- if (value instanceof Secret) returnedArg[key] = value.getMasked()
181
- }
182
- return JSON.stringify(returnedArg)
183
- }
184
- return arg
185
- })
186
- .join(', ')
187
- }
188
-
189
- /** @return {string} */
190
- line() {
191
- const lines = this.stack.split('\n')
192
- if (lines[STACK_LINE]) {
193
- return lines[STACK_LINE].trim()
194
- .replace(global.codecept_dir || '', '.')
195
- .trim()
196
- }
197
- return ''
198
- }
199
-
200
- /** @return {string} */
201
- toString() {
202
- return ucfirst(`${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`).trim()
203
- }
204
-
205
- /** @return {string} */
206
- toCliStyled() {
207
- return `${this.prefix}${this.actor} ${color.italic(this.humanize())} ${color.yellow(this.humanizeArgs())}${this.suffix}`
208
- }
209
-
210
- /** @return {string} */
211
- toCode() {
212
- return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
213
- }
214
-
215
- isMetaStep() {
216
- return this.constructor.name === 'MetaStep'
217
- }
218
-
219
- /** @return {boolean} */
220
- hasBDDAncestor() {
221
- let hasBDD = false
222
- let processingStep
223
- processingStep = this
224
-
225
- while (processingStep.metaStep) {
226
- if (processingStep.metaStep.actor.match(/^(Given|When|Then|And)/)) {
227
- hasBDD = true
228
- break
229
- } else {
230
- processingStep = processingStep.metaStep
231
- }
232
- }
233
- return hasBDD
234
- }
235
- }
236
-
237
- /** @extends Step */
238
- class MetaStep extends Step {
239
- constructor(obj, method) {
240
- if (!method) method = ''
241
- super(null, method)
242
- this.actor = obj
243
- }
6
+ const BaseStep = require('./step/base')
7
+ const StepConfig = require('./step/config')
8
+ const Step = require('./step/helper')
244
9
 
245
- /** @return {boolean} */
246
- isBDD() {
247
- if (this.actor && this.actor.match && this.actor.match(/^(Given|When|Then|And)/)) {
248
- return true
249
- }
250
- return false
251
- }
252
-
253
- toCliStyled() {
254
- return this.toString()
255
- }
256
-
257
- toString() {
258
- const actorText = this.actor
259
-
260
- if (this.isBDD()) {
261
- return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`
262
- }
263
-
264
- if (actorText === 'I') {
265
- return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
266
- }
267
-
268
- return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
269
- }
270
-
271
- humanize() {
272
- return humanizeString(this.name)
273
- }
274
-
275
- setTrace() {}
276
-
277
- setContext(context) {
278
- this.context = context
279
- }
280
-
281
- /** @return {*} */
282
- run(fn) {
283
- this.status = 'queued'
284
- this.setArguments(Array.from(arguments).slice(1))
285
- let result
286
-
287
- const registerStep = step => {
288
- this.metaStep = null
289
- step.metaStep = this
290
- }
291
- event.dispatcher.prependListener(event.step.before, registerStep)
292
- // Handle async and sync methods.
293
- if (fn.constructor.name === 'AsyncFunction') {
294
- result = fn
295
- .apply(this.context, this.args)
296
- .then(result => {
297
- return result
298
- })
299
- .catch(error => {
300
- this.setStatus('failed')
301
- throw error
302
- })
303
- .finally(() => {
304
- this.endTime = Date.now()
305
- event.dispatcher.removeListener(event.step.before, registerStep)
306
- })
307
- } else {
308
- try {
309
- this.startTime = Date.now()
310
- result = fn.apply(this.context, this.args)
311
- } catch (error) {
312
- this.setStatus('failed')
313
- throw error
314
- } finally {
315
- this.endTime = Date.now()
316
- event.dispatcher.removeListener(event.step.before, registerStep)
317
- }
318
- }
319
-
320
- return result
321
- }
322
- }
323
-
324
- Step.TIMEOUTS = {}
10
+ /**
11
+ * MetaStep is a step that is used to wrap other steps.
12
+ * It is used to create a new step that is a combination of other steps.
13
+ * It is used to create a new step that is a combination of other steps.
14
+ */
15
+ const MetaStep = require('./step/meta')
325
16
 
326
- /** @type {Class<MetaStep>} */
327
- Step.MetaStep = MetaStep
17
+ /**
18
+ * Step used to execute a single function
19
+ */
20
+ const FuncStep = require('./step/func')
328
21
 
329
22
  module.exports = Step
330
-
331
- function dryRunResolver() {
332
- return {
333
- get(target, prop) {
334
- if (prop === 'toString') return () => '<VALUE>'
335
- return new Proxy({}, dryRunResolver())
336
- },
337
- }
338
- }
339
-
340
- function humanizeString(string) {
341
- // split strings by words, then make them all lowercase
342
- const _result = string
343
- .replace(/([a-z](?=[A-Z]))/g, '$1 ')
344
- .split(' ')
345
- .map(word => word.toLowerCase())
346
-
347
- _result[0] = _result[0] === 'i' ? capitalizeFLetter(_result[0]) : _result[0]
348
- return _result.join(' ').trim()
349
- }
350
-
351
- function capitalizeFLetter(string) {
352
- return string[0].toUpperCase() + string.slice(1)
353
- }
23
+ module.exports.MetaStep = MetaStep
24
+ module.exports.BaseStep = BaseStep
25
+ module.exports.StepConfig = StepConfig
26
+ module.exports.FuncStep = FuncStep
package/lib/steps.js ADDED
@@ -0,0 +1,50 @@
1
+ const StepConfig = require('./step/config')
2
+ const Section = require('./step/section')
3
+ function stepOpts(opts = {}) {
4
+ return new StepConfig(opts)
5
+ }
6
+
7
+ function stepTimeout(timeout) {
8
+ return new StepConfig().timeout(timeout)
9
+ }
10
+
11
+ function stepRetry(retry) {
12
+ return new StepConfig().retry(retry)
13
+ }
14
+
15
+ function section(name) {
16
+ if (!name) return endSection()
17
+ return new Section(name).start()
18
+ }
19
+
20
+ function endSection() {
21
+ return Section.current().end()
22
+ }
23
+
24
+ // Section function to be added here
25
+
26
+ const step = {
27
+ // steps.opts syntax
28
+ opts: stepOpts,
29
+ timeout: stepTimeout,
30
+ retry: stepRetry,
31
+
32
+ // one-function syntax
33
+ stepTimeout,
34
+ stepRetry,
35
+ stepOpts,
36
+
37
+ // sections
38
+ section,
39
+ endSection,
40
+
41
+ Section: section,
42
+ EndSection: endSection,
43
+
44
+ // shortcuts
45
+ Given: () => section('Given'),
46
+ When: () => section('When'),
47
+ Then: () => section('Then'),
48
+ }
49
+
50
+ module.exports = step
package/lib/store.js CHANGED
@@ -13,6 +13,10 @@ const store = {
13
13
  onPause: false,
14
14
  /** @type {CodeceptJS.Test | null} */
15
15
  currentTest: null,
16
+ /** @type {any} */
17
+ currentStep: null,
18
+ /** @type {CodeceptJS.Suite | null} */
19
+ currentSuite: null,
16
20
  }
17
21
 
18
22
  module.exports = store
package/lib/timeout.js ADDED
@@ -0,0 +1,66 @@
1
+ const TIMEOUT_ORDER = {
2
+ /**
3
+ * timeouts set with order below zero only override timeouts of higher order if their value is smaller
4
+ */
5
+ testOrSuite: -5,
6
+ /**
7
+ * 0-9 - designated for override of timeouts set from code, 5 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=true
8
+ */
9
+ stepTimeoutHard: 5,
10
+ /**
11
+ * 10-19 - designated for timeouts set from code, 15 is order of I.setTimeout(t) operation
12
+ */
13
+ codeLimitTime: 15,
14
+ /**
15
+ * 20-29 - designated for timeout settings which could be overriden in tests code, 25 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=false
16
+ */
17
+ stepTimeoutSoft: 25,
18
+ }
19
+
20
+ function getCurrentTimeout(timeouts) {
21
+ let totalTimeout
22
+ // iterate over all timeouts starting from highest values of order
23
+ new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
24
+ if (
25
+ timeout !== undefined &&
26
+ // when orders >= 0 - timeout value overrides those set with higher order elements
27
+ (order >= 0 ||
28
+ // when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
29
+ totalTimeout === undefined ||
30
+ // when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
31
+ (timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
32
+ ) {
33
+ totalTimeout = timeout
34
+ }
35
+ })
36
+ return totalTimeout
37
+ }
38
+
39
+ class TimeoutError extends Error {
40
+ constructor(message) {
41
+ super(message)
42
+ this.name = 'TimeoutError'
43
+ }
44
+ }
45
+
46
+ class TestTimeoutError extends TimeoutError {
47
+ constructor(timeout) {
48
+ super(`Timeout ${timeout}s exceeded (with Before hook)`)
49
+ this.name = 'TestTimeoutError'
50
+ }
51
+ }
52
+
53
+ class StepTimeoutError extends TimeoutError {
54
+ constructor(timeout, step) {
55
+ super(`Step ${step.toCode().trim()} timed out after ${timeout}s`)
56
+ this.name = 'StepTimeoutError'
57
+ }
58
+ }
59
+
60
+ module.exports = {
61
+ TIMEOUT_ORDER,
62
+ getCurrentTimeout,
63
+ TimeoutError,
64
+ TestTimeoutError,
65
+ StepTimeoutError,
66
+ }
package/lib/utils.js CHANGED
@@ -1,9 +1,11 @@
1
1
  const fs = require('fs')
2
2
  const os = require('os')
3
3
  const path = require('path')
4
+ const chalk = require('chalk')
4
5
  const getFunctionArguments = require('fn-args')
5
6
  const deepClone = require('lodash.clonedeep')
6
7
  const { convertColorToRGBA, isColorProperty } = require('./colorUtils')
8
+ const Fuse = require('fuse.js')
7
9
 
8
10
  function deepMerge(target, source) {
9
11
  const merge = require('lodash.merge')
@@ -484,3 +486,94 @@ module.exports.humanizeFunction = function (fn) {
484
486
 
485
487
  return simplified
486
488
  }
489
+
490
+ /**
491
+ * Searches through a given data source using the Fuse.js library for fuzzy searching.
492
+ *
493
+ * @function searchWithFusejs
494
+ * @param {Array|Object} source - The data source to search through. This can be an array of objects or strings.
495
+ * @param {string} searchString - The search query string to match against the source.
496
+ * @param {Object} [opts] - Optional configuration object for Fuse.js.
497
+ * @param {boolean} [opts.includeScore=true] - Whether to include the score of the match in the results.
498
+ * @param {number} [opts.threshold=0.6] - Determines the match threshold; lower values mean stricter matching.
499
+ * @param {boolean} [opts.caseSensitive=false] - Whether the search should be case-sensitive.
500
+ * @param {number} [opts.distance=100] - Determines how far apart the search term is allowed to be from the target.
501
+ * @param {number} [opts.maxPatternLength=32] - The maximum length of the search pattern. Patterns longer than this are ignored.
502
+ * @param {boolean} [opts.ignoreLocation=false] - Whether the location of the match is ignored when scoring.
503
+ * @param {boolean} [opts.ignoreFieldNorm=false] - When true, the field's length is not considered when scoring.
504
+ * @param {Array<string>} [opts.keys=[]] - List of keys to search in the objects of the source array.
505
+ * @param {boolean} [opts.shouldSort=true] - Whether the results should be sorted by score.
506
+ * @param {string} [opts.sortFn] - A custom sorting function for sorting results.
507
+ * @param {number} [opts.minMatchCharLength=1] - The minimum number of characters that must match.
508
+ * @param {boolean} [opts.useExtendedSearch=false] - Enables extended search capabilities.
509
+ *
510
+ * @returns {Array<Object>} - An array of search results. Each result contains an item and, if `includeScore` is true, a score.
511
+ *
512
+ * @example
513
+ * const data = [
514
+ * { title: "Old Man's War", author: "John Scalzi" },
515
+ * { title: "The Lock Artist", author: "Steve Hamilton" },
516
+ * ];
517
+ *
518
+ * const options = {
519
+ * keys: ['title', 'author'],
520
+ * includeScore: true,
521
+ * threshold: 0.4,
522
+ * caseSensitive: false,
523
+ * distance: 50,
524
+ * ignoreLocation: true,
525
+ * };
526
+ *
527
+ * const results = searchWithFusejs(data, 'lock', options);
528
+ * console.log(results);
529
+ */
530
+ module.exports.searchWithFusejs = function (source, searchString, opts) {
531
+ const fuse = new Fuse(source, opts)
532
+
533
+ return fuse.search(searchString)
534
+ }
535
+
536
+ module.exports.humanizeString = function (string) {
537
+ // split strings by words, then make them all lowercase
538
+ const _result = string
539
+ .replace(/([a-z](?=[A-Z]))/g, '$1 ')
540
+ .split(' ')
541
+ .map(word => word.toLowerCase())
542
+
543
+ _result[0] = _result[0] === 'i' ? this.ucfirst(_result[0]) : _result[0]
544
+ return _result.join(' ').trim()
545
+ }
546
+
547
+ module.exports.serializeError = function (error) {
548
+ if (error) {
549
+ const { stack, uncaught, message, actual, expected } = error
550
+ return { stack, uncaught, message, actual, expected }
551
+ }
552
+ return null
553
+ }
554
+
555
+ module.exports.base64EncodeFile = function (filePath) {
556
+ return Buffer.from(fs.readFileSync(filePath)).toString('base64')
557
+ }
558
+
559
+ module.exports.markdownToAnsi = function (markdown) {
560
+ return (
561
+ markdown
562
+ // Headers (# Text) - make blue and bold
563
+ .replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, text) => {
564
+ return chalk.bold.blue(`${hashes} ${text}`)
565
+ })
566
+ // Bullet points - replace with yellow bullet character
567
+ .replace(/^[-*]\s+(.+)$/gm, (_, text) => {
568
+ return `${chalk.yellow('•')} ${text}`
569
+ })
570
+ // Bold (**text**) - make bold
571
+ .replace(/\*\*(.+?)\*\*/g, (_, text) => {
572
+ return chalk.bold(text)
573
+ })
574
+ // Italic (*text*) - make italic (dim in terminals)
575
+ .replace(/\*(.+?)\*/g, (_, text) => {
576
+ return chalk.italic(text)
577
+ })
578
+ )
579
+ }
package/lib/within.js CHANGED
@@ -3,7 +3,7 @@ const store = require('./store')
3
3
  const recorder = require('./recorder')
4
4
  const container = require('./container')
5
5
  const event = require('./event')
6
- const Step = require('./step')
6
+ const MetaStep = require('./step/meta')
7
7
  const { isAsyncFunction } = require('./utils')
8
8
 
9
9
  /**
@@ -76,7 +76,7 @@ function within(context, fn) {
76
76
 
77
77
  module.exports = within
78
78
 
79
- class WithinStep extends Step.MetaStep {
79
+ class WithinStep extends MetaStep {
80
80
  constructor(locator, fn) {
81
81
  super('Within')
82
82
  this.args = [locator]