codeceptjs 3.6.10 → 3.7.0-beta.2

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 (105) hide show
  1. package/README.md +81 -110
  2. package/bin/codecept.js +2 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +46 -36
  5. package/lib/assert/empty.js +3 -5
  6. package/lib/assert/equal.js +4 -7
  7. package/lib/assert/include.js +4 -6
  8. package/lib/assert/throws.js +2 -4
  9. package/lib/assert/truth.js +2 -2
  10. package/lib/codecept.js +87 -83
  11. package/lib/command/configMigrate.js +2 -4
  12. package/lib/command/definitions.js +5 -25
  13. package/lib/command/generate.js +10 -14
  14. package/lib/command/gherkin/snippets.js +10 -8
  15. package/lib/command/gherkin/steps.js +1 -1
  16. package/lib/command/info.js +1 -3
  17. package/lib/command/init.js +8 -12
  18. package/lib/command/interactive.js +1 -1
  19. package/lib/command/list.js +1 -1
  20. package/lib/command/run-multiple.js +12 -35
  21. package/lib/command/run-workers.js +10 -10
  22. package/lib/command/utils.js +5 -6
  23. package/lib/command/workers/runTests.js +14 -17
  24. package/lib/container.js +327 -237
  25. package/lib/data/context.js +10 -13
  26. package/lib/data/dataScenarioConfig.js +8 -8
  27. package/lib/data/dataTableArgument.js +6 -6
  28. package/lib/data/table.js +5 -11
  29. package/lib/els.js +177 -0
  30. package/lib/event.js +1 -0
  31. package/lib/heal.js +78 -80
  32. package/lib/helper/ApiDataFactory.js +3 -6
  33. package/lib/helper/Appium.js +15 -30
  34. package/lib/helper/FileSystem.js +3 -3
  35. package/lib/helper/GraphQLDataFactory.js +3 -3
  36. package/lib/helper/JSONResponse.js +57 -37
  37. package/lib/helper/Nightmare.js +35 -53
  38. package/lib/helper/Playwright.js +189 -251
  39. package/lib/helper/Protractor.js +54 -77
  40. package/lib/helper/Puppeteer.js +134 -232
  41. package/lib/helper/REST.js +5 -17
  42. package/lib/helper/TestCafe.js +21 -44
  43. package/lib/helper/WebDriver.js +103 -162
  44. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  45. package/lib/listener/artifacts.js +2 -2
  46. package/lib/listener/emptyRun.js +58 -0
  47. package/lib/listener/exit.js +4 -4
  48. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  49. package/lib/listener/{timeout.js → globalTimeout.js} +9 -8
  50. package/lib/listener/helpers.js +15 -15
  51. package/lib/listener/mocha.js +1 -1
  52. package/lib/listener/steps.js +17 -12
  53. package/lib/listener/store.js +12 -0
  54. package/lib/mocha/asyncWrapper.js +204 -0
  55. package/lib/{interfaces → mocha}/bdd.js +3 -3
  56. package/lib/mocha/cli.js +257 -0
  57. package/lib/mocha/factory.js +104 -0
  58. package/lib/{interfaces → mocha}/featureConfig.js +11 -12
  59. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  60. package/lib/mocha/hooks.js +83 -0
  61. package/lib/mocha/index.js +12 -0
  62. package/lib/mocha/inject.js +24 -0
  63. package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
  64. package/lib/mocha/suite.js +55 -0
  65. package/lib/mocha/test.js +60 -0
  66. package/lib/mocha/types.d.ts +31 -0
  67. package/lib/mocha/ui.js +219 -0
  68. package/lib/output.js +28 -10
  69. package/lib/pause.js +159 -135
  70. package/lib/plugin/autoDelay.js +4 -4
  71. package/lib/plugin/autoLogin.js +6 -7
  72. package/lib/plugin/commentStep.js +1 -1
  73. package/lib/plugin/coverage.js +10 -19
  74. package/lib/plugin/customLocator.js +3 -3
  75. package/lib/plugin/debugErrors.js +2 -2
  76. package/lib/plugin/eachElement.js +1 -1
  77. package/lib/plugin/fakerTransform.js +1 -1
  78. package/lib/plugin/heal.js +6 -9
  79. package/lib/plugin/retryFailedStep.js +4 -4
  80. package/lib/plugin/retryTo.js +2 -2
  81. package/lib/plugin/screenshotOnFail.js +9 -36
  82. package/lib/plugin/selenoid.js +15 -35
  83. package/lib/plugin/stepByStepReport.js +51 -13
  84. package/lib/plugin/stepTimeout.js +4 -11
  85. package/lib/plugin/subtitles.js +4 -4
  86. package/lib/plugin/tryTo.js +1 -1
  87. package/lib/plugin/wdio.js +8 -10
  88. package/lib/recorder.js +142 -121
  89. package/lib/secret.js +1 -1
  90. package/lib/step.js +160 -144
  91. package/lib/store.js +6 -2
  92. package/lib/template/heal.js +2 -11
  93. package/lib/utils.js +224 -216
  94. package/lib/within.js +73 -55
  95. package/lib/workers.js +265 -261
  96. package/package.json +46 -47
  97. package/typings/index.d.ts +172 -184
  98. package/typings/promiseBasedTypes.d.ts +95 -516
  99. package/typings/types.d.ts +169 -587
  100. package/lib/cli.js +0 -256
  101. package/lib/helper/ExpectHelper.js +0 -391
  102. package/lib/helper/SoftExpectHelper.js +0 -381
  103. package/lib/mochaFactory.js +0 -113
  104. package/lib/scenario.js +0 -224
  105. package/lib/ui.js +0 -236
@@ -13,8 +13,8 @@ module.exports = function (context) {
13
13
  fn = opts
14
14
  opts = {}
15
15
  }
16
- opts.data = data.map((dataRow) => dataRow.data)
17
- data.forEach((dataRow) => {
16
+ opts.data = data.map(dataRow => dataRow.data)
17
+ data.forEach(dataRow => {
18
18
  const dataTitle = replaceTitle(title, dataRow)
19
19
  if (dataRow.skip) {
20
20
  context.xScenario(dataTitle)
@@ -32,8 +32,8 @@ module.exports = function (context) {
32
32
  fn = opts
33
33
  opts = {}
34
34
  }
35
- opts.data = data.map((dataRow) => dataRow.data)
36
- data.forEach((dataRow) => {
35
+ opts.data = data.map(dataRow => dataRow.data)
36
+ data.forEach(dataRow => {
37
37
  const dataTitle = replaceTitle(title, dataRow)
38
38
  if (dataRow.skip) {
39
39
  context.xScenario(dataTitle)
@@ -51,8 +51,8 @@ module.exports = function (context) {
51
51
  context.xData = function (dataTable) {
52
52
  const data = detectDataType(dataTable)
53
53
  return {
54
- Scenario: (title) => {
55
- data.forEach((dataRow) => {
54
+ Scenario: title => {
55
+ data.forEach(dataRow => {
56
56
  const dataTitle = replaceTitle(title, dataRow)
57
57
  context.xScenario(dataTitle)
58
58
  })
@@ -69,10 +69,7 @@ function replaceTitle(title, dataRow) {
69
69
 
70
70
  // if `dataRow` is object and has own `toString()` method,
71
71
  // it should be printed
72
- if (
73
- Object.prototype.toString.call(dataRow.data) === Object().toString() &&
74
- dataRow.data.toString() !== Object().toString()
75
- ) {
72
+ if (Object.prototype.toString.call(dataRow.data) === Object().toString() && dataRow.data.toString() !== Object().toString()) {
76
73
  return `${title} | ${dataRow.data}`
77
74
  }
78
75
 
@@ -102,7 +99,7 @@ function detectDataType(dataTable) {
102
99
  return dataTable()
103
100
  }
104
101
  if (Array.isArray(dataTable)) {
105
- return dataTable.map((item) => {
102
+ return dataTable.map(item => {
106
103
  if (isTableDataRow(item)) {
107
104
  return item
108
105
  }
@@ -117,10 +114,10 @@ function detectDataType(dataTable) {
117
114
  }
118
115
 
119
116
  function maskSecretInTitle(scenarios) {
120
- scenarios.forEach((scenario) => {
117
+ scenarios.forEach(scenario => {
121
118
  const res = []
122
119
 
123
- scenario.test.title.split(',').forEach((item) => {
120
+ scenario.test.title.split(',').forEach(item => {
124
121
  res.push(item.replace(/{"_secret":"(.*)"}/, '"*****"'))
125
122
  })
126
123
 
@@ -10,7 +10,7 @@ class DataScenarioConfig {
10
10
  * @param {*} err
11
11
  */
12
12
  throws(err) {
13
- this.scenarios.forEach((scenario) => scenario.throws(err))
13
+ this.scenarios.forEach(scenario => scenario.throws(err))
14
14
  return this
15
15
  }
16
16
 
@@ -21,7 +21,7 @@ class DataScenarioConfig {
21
21
  *
22
22
  */
23
23
  fails() {
24
- this.scenarios.forEach((scenario) => scenario.fails())
24
+ this.scenarios.forEach(scenario => scenario.fails())
25
25
  return this
26
26
  }
27
27
 
@@ -31,7 +31,7 @@ class DataScenarioConfig {
31
31
  * @param {*} retries
32
32
  */
33
33
  retry(retries) {
34
- this.scenarios.forEach((scenario) => scenario.retry(retries))
34
+ this.scenarios.forEach(scenario => scenario.retry(retries))
35
35
  return this
36
36
  }
37
37
 
@@ -40,7 +40,7 @@ class DataScenarioConfig {
40
40
  * @param {*} timeout
41
41
  */
42
42
  timeout(timeout) {
43
- this.scenarios.forEach((scenario) => scenario.timeout(timeout))
43
+ this.scenarios.forEach(scenario => scenario.timeout(timeout))
44
44
  return this
45
45
  }
46
46
 
@@ -49,7 +49,7 @@ class DataScenarioConfig {
49
49
  * Helper name can be omitted and values will be applied to first helper.
50
50
  */
51
51
  config(helper, obj) {
52
- this.scenarios.forEach((scenario) => scenario.config(helper, obj))
52
+ this.scenarios.forEach(scenario => scenario.config(helper, obj))
53
53
  return this
54
54
  }
55
55
 
@@ -58,7 +58,7 @@ class DataScenarioConfig {
58
58
  * @param {*} tagName
59
59
  */
60
60
  tag(tagName) {
61
- this.scenarios.forEach((scenario) => scenario.tag(tagName))
61
+ this.scenarios.forEach(scenario => scenario.tag(tagName))
62
62
  return this
63
63
  }
64
64
 
@@ -67,7 +67,7 @@ class DataScenarioConfig {
67
67
  * @param {*} obj
68
68
  */
69
69
  inject(obj) {
70
- this.scenarios.forEach((scenario) => scenario.inject(obj))
70
+ this.scenarios.forEach(scenario => scenario.inject(obj))
71
71
  return this
72
72
  }
73
73
 
@@ -76,7 +76,7 @@ class DataScenarioConfig {
76
76
  * @param {*} dependencies
77
77
  */
78
78
  injectDependencies(dependencies) {
79
- this.scenarios.forEach((scenario) => scenario.injectDependencies(dependencies))
79
+ this.scenarios.forEach(scenario => scenario.injectDependencies(dependencies))
80
80
  return this
81
81
  }
82
82
  }
@@ -5,8 +5,8 @@
5
5
  class DataTableArgument {
6
6
  /** @param {*} gherkinDataTable */
7
7
  constructor(gherkinDataTable) {
8
- this.rawData = gherkinDataTable.rows.map((row) => {
9
- return row.cells.map((cell) => {
8
+ this.rawData = gherkinDataTable.rows.map(row => {
9
+ return row.cells.map(cell => {
10
10
  return cell.value
11
11
  })
12
12
  })
@@ -34,7 +34,7 @@ class DataTableArgument {
34
34
  hashes() {
35
35
  const copy = this.raw()
36
36
  const header = copy.shift()
37
- return copy.map((row) => {
37
+ return copy.map(row => {
38
38
  const r = {}
39
39
  row.forEach((cell, index) => (r[header[index]] = cell))
40
40
  return r
@@ -47,19 +47,19 @@ class DataTableArgument {
47
47
  */
48
48
  rowsHash() {
49
49
  const rows = this.raw()
50
- const everyRowHasTwoColumns = rows.every((row) => row.length === 2)
50
+ const everyRowHasTwoColumns = rows.every(row => row.length === 2)
51
51
  if (!everyRowHasTwoColumns) {
52
52
  throw new Error('rowsHash can only be called on a data table where all rows have exactly two columns')
53
53
  }
54
54
  /** @type {Record<string, string>} */
55
55
  const result = {}
56
- rows.forEach((x) => (result[x[0]] = x[1]))
56
+ rows.forEach(x => (result[x[0]] = x[1]))
57
57
  return result
58
58
  }
59
59
 
60
60
  /** Transposed the data */
61
61
  transpose() {
62
- this.rawData = this.rawData[0].map((x, i) => this.rawData.map((y) => y[i]))
62
+ this.rawData = this.rawData[0].map((x, i) => this.rawData.map(y => y[i]))
63
63
  }
64
64
  }
65
65
 
package/lib/data/table.js CHANGED
@@ -10,13 +10,10 @@ class DataTable {
10
10
 
11
11
  /** @param {Array<*>} array */
12
12
  add(array) {
13
- if (array.length !== this.array.length)
14
- throw new Error(
15
- `There is too many elements in given data array. Please provide data in this format: ${this.array}`,
16
- )
13
+ if (array.length !== this.array.length) throw new Error(`There is too many elements in given data array. Please provide data in this format: ${this.array}`)
17
14
  const tempObj = {}
18
15
  let arrayCounter = 0
19
- this.array.forEach((elem) => {
16
+ this.array.forEach(elem => {
20
17
  tempObj[elem] = array[arrayCounter]
21
18
  tempObj.toString = () => JSON.stringify(tempObj)
22
19
  arrayCounter++
@@ -26,13 +23,10 @@ class DataTable {
26
23
 
27
24
  /** @param {Array<*>} array */
28
25
  xadd(array) {
29
- if (array.length !== this.array.length)
30
- throw new Error(
31
- `There is too many elements in given data array. Please provide data in this format: ${this.array}`,
32
- )
26
+ if (array.length !== this.array.length) throw new Error(`There is too many elements in given data array. Please provide data in this format: ${this.array}`)
33
27
  const tempObj = {}
34
28
  let arrayCounter = 0
35
- this.array.forEach((elem) => {
29
+ this.array.forEach(elem => {
36
30
  tempObj[elem] = array[arrayCounter]
37
31
  tempObj.toString = () => JSON.stringify(tempObj)
38
32
  arrayCounter++
@@ -42,7 +36,7 @@ class DataTable {
42
36
 
43
37
  /** @param {Function} func */
44
38
  filter(func) {
45
- return this.rows.filter((row) => func(row.data))
39
+ return this.rows.filter(row => func(row.data))
46
40
  }
47
41
  }
48
42
 
package/lib/els.js ADDED
@@ -0,0 +1,177 @@
1
+ const output = require('./output');
2
+ const store = require('./store');
3
+ const recorder = require('./recorder');
4
+ const container = require('./container');
5
+ const event = require('./event');
6
+ const Step = require('./step');
7
+ const { truth } = require('./assert/truth');
8
+ const { isAsyncFunction, humanizeFunction } = require('./utils');
9
+
10
+ function element(purpose, locator, fn) {
11
+ if (!fn) {
12
+ fn = locator;
13
+ locator = purpose;
14
+ purpose = 'first element';
15
+ }
16
+
17
+ const step = prepareStep(purpose, locator, fn);
18
+ if (!step) return;
19
+
20
+ return executeStep(step, async () => {
21
+ const els = await step.helper._locate(locator);
22
+ output.debug(`Found ${els.length} elements, using first element`);
23
+
24
+ return fn(els[0]);
25
+ });
26
+ }
27
+
28
+ function eachElement(purpose, locator, fn) {
29
+ if (!fn) {
30
+ fn = locator;
31
+ locator = purpose;
32
+ purpose = 'for each element';
33
+ }
34
+
35
+ const step = prepareStep(purpose, locator, fn);
36
+ if (!step) return;
37
+
38
+ return executeStep(step, async () => {
39
+ const els = await step.helper._locate(locator);
40
+ output.debug(`Found ${els.length} elements for each elements to iterate`);
41
+
42
+ const errs = [];
43
+ let i = 0;
44
+ for (const el of els) {
45
+ try {
46
+ await fn(el, i);
47
+ } catch (err) {
48
+ output.error(`eachElement: failed operation on element #${i} ${el}`);
49
+ errs.push(err);
50
+ }
51
+ i++;
52
+ }
53
+
54
+ if (errs.length) {
55
+ throw errs[0];
56
+ }
57
+ });
58
+ }
59
+
60
+ function expectElement(locator, fn) {
61
+ const step = prepareStep('expect element to be', locator, fn);
62
+ if (!step) return;
63
+
64
+ return executeStep(step, async () => {
65
+ const els = await step.helper._locate(locator);
66
+ output.debug(`Found ${els.length} elements, first will be used for assertion`);
67
+
68
+ const result = await fn(els[0]);
69
+ const assertion = truth(`element (${locator})`, fn.toString());
70
+ assertion.assert(result);
71
+ });
72
+ }
73
+
74
+ function expectAnyElement(locator, fn) {
75
+ const step = prepareStep('expect any element to be', locator, fn);
76
+ if (!step) return;
77
+
78
+ return executeStep(step, async () => {
79
+ const els = await step.helper._locate(locator);
80
+ output.debug(`Found ${els.length} elements, at least one should pass the assertion`);
81
+
82
+ const assertion = truth(`any element of (${locator})`, fn.toString());
83
+
84
+ let found = false;
85
+ for (const el of els) {
86
+ const result = await fn(el);
87
+ if (result) {
88
+ found = true;
89
+ break;
90
+ }
91
+ }
92
+ if (!found) throw assertion.getException();
93
+ });
94
+ }
95
+
96
+ function expectAllElements(locator, fn) {
97
+ const step = prepareStep('expect all elements', locator, fn);
98
+ if (!step) return;
99
+
100
+ return executeStep(step, async () => {
101
+ const els = await step.helper._locate(locator);
102
+ output.debug(`Found ${els.length} elements, all should pass the assertion`);
103
+
104
+ let i = 1;
105
+ for (const el of els) {
106
+ output.debug(`checking element #${i}: ${el}`);
107
+ const result = await fn(el);
108
+ const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn));
109
+ assertion.assert(result);
110
+ i++;
111
+ }
112
+ });
113
+ }
114
+
115
+ module.exports = {
116
+ element,
117
+ eachElement,
118
+ expectElement,
119
+ expectAnyElement,
120
+ expectAllElements,
121
+ };
122
+
123
+ function prepareStep(purpose, locator, fn) {
124
+ if (store.dryRun) return;
125
+ const helpers = Object.values(container.helpers());
126
+
127
+ const helper = helpers.filter(h => !!h._locate)[0];
128
+
129
+ if (!helper) {
130
+ throw new Error('No helper enabled with _locate method with returns a list of elements.');
131
+ }
132
+
133
+ if (!isAsyncFunction(fn)) {
134
+ throw new Error('Async function should be passed into each element');
135
+ }
136
+
137
+ const isAssertion = purpose.startsWith('expect');
138
+
139
+ const step = new Step(helper, `${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`);
140
+ step.setActor('EL');
141
+ step.setArguments([humanizeFunction(fn)]);
142
+ step.helperMethod = '_locate';
143
+
144
+ return step;
145
+ }
146
+
147
+ async function executeStep(step, action) {
148
+ let error;
149
+ const promise = recorder.add('register element wrapper', async () => {
150
+ event.emit(event.step.started, step);
151
+
152
+ try {
153
+ await action();
154
+ } catch (err) {
155
+ recorder.throw(err);
156
+ event.emit(event.step.failed, step, err);
157
+ event.emit(event.step.finished, step);
158
+ // event.emit(event.step.after, step)
159
+ error = err;
160
+ // await recorder.promise();
161
+ return;
162
+ }
163
+
164
+ event.emit(event.step.after, step);
165
+ event.emit(event.step.passed, step);
166
+ event.emit(event.step.finished, step);
167
+ });
168
+
169
+ // await recorder.promise();
170
+
171
+ // if (error) {
172
+ // console.log('error', error.inspect())
173
+ // return recorder.throw(error);
174
+ // }
175
+
176
+ return promise;
177
+ }
package/lib/event.js CHANGED
@@ -60,6 +60,7 @@ module.exports = {
60
60
  passed: 'hook.passed',
61
61
  failed: 'hook.failed',
62
62
  },
63
+
63
64
  /**
64
65
  * @type {object}
65
66
  * @constant
package/lib/heal.js CHANGED
@@ -1,122 +1,125 @@
1
- const debug = require('debug')('codeceptjs:heal');
2
- const colors = require('chalk');
3
- const Container = require('./container');
4
- const recorder = require('./recorder');
5
- const output = require('./output');
6
- const event = require('./event');
1
+ const debug = require('debug')('codeceptjs:heal')
2
+ const colors = require('chalk')
3
+ const Container = require('./container')
4
+ const recorder = require('./recorder')
5
+ const output = require('./output')
6
+ const event = require('./event')
7
7
 
8
8
  /**
9
9
  * @class
10
10
  */
11
11
  class Heal {
12
12
  constructor() {
13
- this.recipes = {};
14
- this.fixes = [];
15
- this.prepareFns = [];
16
- this.contextName = null;
17
- this.numHealed = 0;
13
+ this.recipes = {}
14
+ this.fixes = []
15
+ this.prepareFns = []
16
+ this.contextName = null
17
+ this.numHealed = 0
18
18
  }
19
19
 
20
20
  clear() {
21
- this.recipes = {};
22
- this.fixes = [];
23
- this.prepareFns = [];
24
- this.contextName = null;
25
- this.numHealed = 0;
21
+ this.recipes = {}
22
+ this.fixes = []
23
+ this.prepareFns = []
24
+ this.contextName = null
25
+ this.numHealed = 0
26
26
  }
27
27
 
28
28
  addRecipe(name, opts = {}) {
29
- if (!opts.priority) opts.priority = 0;
29
+ if (!opts.priority) opts.priority = 0
30
30
 
31
- if (!opts.fn) throw new Error(`Recipe ${name} should have a function 'fn' to execute`);
31
+ if (!opts.fn) throw new Error(`Recipe ${name} should have a function 'fn' to execute`)
32
32
 
33
- this.recipes[name] = opts;
33
+ this.recipes[name] = opts
34
34
  }
35
35
 
36
36
  connectToEvents() {
37
- event.dispatcher.on(event.suite.before, (suite) => {
38
- this.contextName = suite.title;
39
- });
37
+ event.dispatcher.on(event.suite.before, suite => {
38
+ this.contextName = suite.title
39
+ })
40
40
 
41
- event.dispatcher.on(event.test.started, (test) => {
42
- this.contextName = test.fullTitle();
43
- });
41
+ event.dispatcher.on(event.test.started, test => {
42
+ this.contextName = test.fullTitle()
43
+ })
44
44
 
45
45
  event.dispatcher.on(event.test.finished, () => {
46
- this.contextName = null;
47
- });
46
+ this.contextName = null
47
+ })
48
48
  }
49
49
 
50
50
  hasCorrespondingRecipes(step) {
51
- return matchRecipes(this.recipes, this.contextName)
52
- .filter(r => !r.steps || r.steps.includes(step.name))
53
- .length > 0;
51
+ return matchRecipes(this.recipes, this.contextName).filter(r => !r.steps || r.steps.includes(step.name)).length > 0
54
52
  }
55
53
 
56
54
  async getCodeSuggestions(context) {
57
- const suggestions = [];
58
- const recipes = matchRecipes(this.recipes, this.contextName);
55
+ const suggestions = []
56
+ const recipes = matchRecipes(this.recipes, this.contextName)
59
57
 
60
- debug('Recipes', recipes);
58
+ debug('Recipes', recipes)
61
59
 
62
- const currentOutputLevel = output.level();
63
- output.level(0);
60
+ const currentOutputLevel = output.level()
61
+ output.level(0)
64
62
 
65
- for (const [property, prepareFn] of Object.entries(recipes.map(r => r.prepare).filter(p => !!p).reduce((acc, obj) => ({ ...acc, ...obj }), {}))) {
66
- if (!prepareFn) continue;
63
+ for (const [property, prepareFn] of Object.entries(
64
+ recipes
65
+ .map(r => r.prepare)
66
+ .filter(p => !!p)
67
+ .reduce((acc, obj) => ({ ...acc, ...obj }), {}),
68
+ )) {
69
+ if (!prepareFn) continue
67
70
 
68
- if (context[property]) continue;
69
- context[property] = await prepareFn(Container.support());
71
+ if (context[property]) continue
72
+ context[property] = await prepareFn(Container.support())
70
73
  }
71
74
 
72
- output.level(currentOutputLevel);
75
+ output.level(currentOutputLevel)
73
76
 
74
77
  for (const recipe of recipes) {
75
- let snippets = await recipe.fn(context);
76
- if (!Array.isArray(snippets)) snippets = [snippets];
78
+ let snippets = await recipe.fn(context)
79
+ if (!Array.isArray(snippets)) snippets = [snippets]
77
80
 
78
81
  suggestions.push({
79
82
  name: recipe.name,
80
83
  snippets,
81
- });
84
+ })
82
85
  }
83
86
 
84
- return suggestions.filter(s => !isBlank(s.snippets));
87
+ return suggestions.filter(s => !isBlank(s.snippets))
85
88
  }
86
89
 
87
90
  async healStep(failedStep, error, failureContext = {}) {
88
- output.debug(`Trying to heal ${failedStep.toCode()} step`);
91
+ output.debug(`Trying to heal ${failedStep.toCode()} step`)
89
92
 
90
93
  Object.assign(failureContext, {
91
94
  error,
92
95
  step: failedStep,
93
96
  prevSteps: failureContext?.test?.steps?.slice(0, -1) || [],
94
- });
97
+ })
95
98
 
96
- const suggestions = await this.getCodeSuggestions(failureContext);
99
+ const suggestions = await this.getCodeSuggestions(failureContext)
97
100
 
98
101
  if (suggestions.length === 0) {
99
- debug('No healing suggestions found');
100
- throw error;
102
+ debug('No healing suggestions found')
103
+ throw error
101
104
  }
102
105
 
103
- output.debug(`Received ${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`);
106
+ output.debug(`Received ${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`)
104
107
 
105
- debug(suggestions);
108
+ debug(suggestions)
106
109
 
107
110
  for (const suggestion of suggestions) {
108
111
  for (const codeSnippet of suggestion.snippets) {
109
112
  try {
110
- debug('Executing', codeSnippet);
111
- recorder.catch((e) => {
112
- debug(e);
113
- });
113
+ debug('Executing', codeSnippet)
114
+ recorder.catch(e => {
115
+ debug(e)
116
+ })
114
117
 
115
118
  if (typeof codeSnippet === 'string') {
116
- const I = Container.support('I');
117
- await eval(codeSnippet); // eslint-disable-line
119
+ const I = Container.support('I')
120
+ await eval(codeSnippet)
118
121
  } else if (typeof codeSnippet === 'function') {
119
- await codeSnippet(Container.support());
122
+ await codeSnippet(Container.support())
120
123
  }
121
124
 
122
125
  this.fixes.push({
@@ -124,49 +127,44 @@ class Heal {
124
127
  test: failureContext?.test,
125
128
  step: failedStep,
126
129
  snippet: codeSnippet,
127
- });
130
+ })
128
131
 
129
- recorder.add('healed', () => output.print(colors.bold.green(` Code healed successfully by ${suggestion.name}`), colors.gray('(no errors thrown)')));
130
- this.numHealed++;
132
+ recorder.add('healed', () => output.print(colors.bold.green(` Code healed successfully by ${suggestion.name}`), colors.gray('(no errors thrown)')))
133
+ this.numHealed++
131
134
  // recorder.session.restore();
132
- return;
135
+ return
133
136
  } catch (err) {
134
- debug('Failed to execute code', err);
135
- recorder.ignoreErr(err); // healing did not help
136
- recorder.catchWithoutStop(err);
137
- await recorder.promise(); // wait for all promises to resolve
137
+ debug('Failed to execute code', err)
138
+ recorder.ignoreErr(err) // healing did not help
139
+ recorder.catchWithoutStop(err)
140
+ await recorder.promise() // wait for all promises to resolve
138
141
  }
139
142
  }
140
143
  }
141
- output.debug(`Couldn't heal the code for ${failedStep.toCode()}`);
142
- recorder.throw(error);
144
+ output.debug(`Couldn't heal the code for ${failedStep.toCode()}`)
145
+ recorder.throw(error)
143
146
  }
144
147
 
145
148
  static setDefaultHealers() {
146
- require('./template/heal');
149
+ require('./template/heal')
147
150
  }
148
151
  }
149
152
 
150
- const heal = new Heal();
153
+ const heal = new Heal()
151
154
 
152
- module.exports = heal;
155
+ module.exports = heal
153
156
 
154
157
  function matchRecipes(recipes, contextName) {
155
158
  return Object.entries(recipes)
156
159
  .filter(([, recipe]) => !contextName || !recipe.grep || new RegExp(recipe.grep).test(contextName))
157
160
  .sort(([, a], [, b]) => a.priority - b.priority)
158
161
  .map(([name, recipe]) => {
159
- recipe.name = name;
160
- return recipe;
162
+ recipe.name = name
163
+ return recipe
161
164
  })
162
- .filter(r => !!r.fn);
165
+ .filter(r => !!r.fn)
163
166
  }
164
167
 
165
168
  function isBlank(value) {
166
- return (
167
- value == null
168
- || (Array.isArray(value) && value.length === 0)
169
- || (typeof value === 'object' && Object.keys(value).length === 0)
170
- || (typeof value === 'string' && value.trim() === '')
171
- );
169
+ return value == null || (Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0) || (typeof value === 'string' && value.trim() === '')
172
170
  }
@@ -217,7 +217,7 @@ class ApiDataFactory extends Helper {
217
217
  }
218
218
 
219
219
  this.created = {}
220
- Object.keys(this.factories).forEach((f) => (this.created[f] = []))
220
+ Object.keys(this.factories).forEach(f => (this.created[f] = []))
221
221
  }
222
222
 
223
223
  static _checkRequirements() {
@@ -357,7 +357,7 @@ Current file error: ${err.message}`)
357
357
 
358
358
  request.baseURL = this.config.endpoint
359
359
 
360
- return this.restHelper._executeRequest(request).then((resp) => {
360
+ return this.restHelper._executeRequest(request).then(resp => {
361
361
  const id = this._fetchId(resp.data, factory)
362
362
  this.created[factory].push(id)
363
363
  this.debugSection('Created', `Id: ${id}`)
@@ -391,10 +391,7 @@ Current file error: ${err.message}`)
391
391
  request.baseURL = this.config.endpoint
392
392
 
393
393
  if (request.url.match(/^undefined/)) {
394
- return this.debugSection(
395
- 'Please configure the delete request in your ApiDataFactory helper',
396
- "delete: () => ({ method: 'DELETE', url: '/api/users' })",
397
- )
394
+ return this.debugSection('Please configure the delete request in your ApiDataFactory helper', "delete: () => ({ method: 'DELETE', url: '/api/users' })")
398
395
  }
399
396
 
400
397
  return this.restHelper._executeRequest(request).then(() => {