Skip to content

Commit ac4da06

Browse files
author
DavertMik
committed
fix async loading of helper methods
1 parent 8c0ec87 commit ac4da06

File tree

2 files changed

+105
-91
lines changed

2 files changed

+105
-91
lines changed

lib/actor.js

Lines changed: 87 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
const Step = require('./step');
2-
const { MetaStep } = require('./step');
3-
const container = require('./container');
4-
const { methodsOfObject } = require('./utils');
5-
const recorder = require('./recorder');
6-
const event = require('./event');
7-
const store = require('./store');
8-
const output = require('./output');
1+
const Step = require('./step')
2+
const { MetaStep } = require('./step')
3+
const container = require('./container')
4+
const { methodsOfObject } = require('./utils')
5+
const recorder = require('./recorder')
6+
const event = require('./event')
7+
const store = require('./store')
8+
const output = require('./output')
99

1010
/**
1111
* @interface
@@ -21,13 +21,13 @@ class Actor {
2121
* ⚠️ returns a promise which is synchronized internally by recorder
2222
*/
2323
async say(msg, color = 'cyan') {
24-
const step = new Step('say', 'say');
25-
step.status = 'passed';
24+
const step = new Step('say', 'say')
25+
step.status = 'passed'
2626
return recordStep(step, [msg]).then(() => {
2727
// this is backward compatibility as this event may be used somewhere
28-
event.emit(event.step.comment, msg);
29-
output.say(msg, `${color}`);
30-
});
28+
event.emit(event.step.comment, msg)
29+
output.say(msg, `${color}`)
30+
})
3131
}
3232

3333
/**
@@ -38,14 +38,14 @@ class Actor {
3838
* @inner
3939
*/
4040
limitTime(timeout) {
41-
if (!store.timeouts) return this;
41+
if (!store.timeouts) return this
4242

4343
event.dispatcher.prependOnceListener(event.step.before, (step) => {
44-
output.log(`Timeout to ${step}: ${timeout}s`);
45-
step.setTimeout(timeout * 1000, Step.TIMEOUT_ORDER.codeLimitTime);
46-
});
44+
output.log(`Timeout to ${step}: ${timeout}s`)
45+
step.setTimeout(timeout * 1000, Step.TIMEOUT_ORDER.codeLimitTime)
46+
})
4747

48-
return this;
48+
return this
4949
}
5050

5151
/**
@@ -55,11 +55,11 @@ class Actor {
5555
* @inner
5656
*/
5757
retry(opts) {
58-
if (opts === undefined) opts = 1;
59-
recorder.retry(opts);
58+
if (opts === undefined) opts = 1
59+
recorder.retry(opts)
6060
// remove retry once the step passed
61-
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()));
62-
return this;
61+
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()))
62+
return this
6363
}
6464
}
6565

@@ -71,91 +71,98 @@ class Actor {
7171
*/
7272
module.exports = function (obj = {}) {
7373
if (!store.actor) {
74-
store.actor = new Actor();
74+
store.actor = new Actor()
7575
}
76-
const actor = store.actor;
76+
const actor = store.actor
7777

78-
const translation = container.translation();
78+
const translation = container.translation()
7979

8080
if (Object.keys(obj).length > 0) {
81-
Object.keys(obj)
82-
.forEach(action => {
83-
const actionAlias = translation.actionAliasFor(action);
84-
85-
const currentMethod = obj[action];
86-
const ms = new MetaStep('I', action);
87-
if (translation.loaded) {
88-
ms.name = actionAlias;
89-
ms.actor = translation.I;
90-
}
91-
ms.setContext(actor);
92-
actor[action] = actor[actionAlias] = ms.run.bind(ms, currentMethod);
93-
});
81+
Object.keys(obj).forEach((action) => {
82+
const actionAlias = translation.actionAliasFor(action)
83+
84+
const currentMethod = obj[action]
85+
const ms = new MetaStep('I', action)
86+
if (translation.loaded) {
87+
ms.name = actionAlias
88+
ms.actor = translation.I
89+
}
90+
ms.setContext(actor)
91+
actor[action] = actor[actionAlias] = ms.run.bind(ms, currentMethod)
92+
})
9493
}
9594

96-
const helpers = container.helpers();
95+
container.started().then(() => {
96+
const helpers = container.helpers()
9797

98-
// add methods from enabled helpers
99-
Object.values(helpers)
100-
.forEach((helper) => {
98+
// add methods from enabled helpers
99+
Object.values(helpers).forEach((helper) => {
101100
methodsOfObject(helper, 'Helper')
102-
.filter(method => method !== 'constructor' && method[0] !== '_')
101+
.filter((method) => method !== 'constructor' && method[0] !== '_')
103102
.forEach((action) => {
104-
const actionAlias = translation.actionAliasFor(action);
103+
const actionAlias = translation.actionAliasFor(action)
105104
if (!actor[action]) {
106105
actor[action] = actor[actionAlias] = function () {
107-
const step = new Step(helper, action);
106+
const step = new Step(helper, action)
108107
if (translation.loaded) {
109-
step.name = actionAlias;
110-
step.actor = translation.I;
108+
step.name = actionAlias
109+
step.actor = translation.I
111110
}
112111
// add methods to promise chain
113-
return recordStep(step, Array.from(arguments));
114-
};
112+
return recordStep(step, Array.from(arguments))
113+
}
115114
}
116-
});
117-
});
115+
})
116+
})
117+
})
118118

119-
return actor;
120-
};
119+
return actor
120+
}
121121

122122
function recordStep(step, args) {
123-
step.status = 'queued';
124-
step.setArguments(args);
123+
step.status = 'queued'
124+
step.setArguments(args)
125125

126126
// run async before step hooks
127-
event.emit(event.step.before, step);
127+
event.emit(event.step.before, step)
128128

129-
const task = `${step.name}: ${step.humanizeArgs()}`;
130-
let val;
129+
const task = `${step.name}: ${step.humanizeArgs()}`
130+
let val
131131

132132
// run step inside promise
133-
recorder.add(task, () => {
134-
if (!step.startTime) { // step can be retries
135-
event.emit(event.step.started, step);
136-
step.startTime = Date.now();
137-
}
138-
return val = step.run(...args);
139-
}, false, undefined, step.getTimeout());
140-
141-
event.emit(event.step.after, step);
133+
recorder.add(
134+
task,
135+
() => {
136+
if (!step.startTime) {
137+
// step can be retries
138+
event.emit(event.step.started, step)
139+
step.startTime = Date.now()
140+
}
141+
return (val = step.run(...args))
142+
},
143+
false,
144+
undefined,
145+
step.getTimeout(),
146+
)
147+
148+
event.emit(event.step.after, step)
142149

143150
recorder.add('step passed', () => {
144-
step.endTime = Date.now();
145-
event.emit(event.step.passed, step, val);
146-
event.emit(event.step.finished, step);
147-
});
151+
step.endTime = Date.now()
152+
event.emit(event.step.passed, step, val)
153+
event.emit(event.step.finished, step)
154+
})
148155

149156
recorder.catchWithoutStop((err) => {
150-
step.status = 'failed';
151-
step.endTime = Date.now();
152-
event.emit(event.step.failed, step);
153-
event.emit(event.step.finished, step);
154-
throw err;
155-
});
156-
157-
recorder.add('return result', () => val);
157+
step.status = 'failed'
158+
step.endTime = Date.now()
159+
event.emit(event.step.failed, step)
160+
event.emit(event.step.finished, step)
161+
throw err
162+
})
163+
164+
recorder.add('return result', () => val)
158165
// run async after step hooks
159166

160-
return recorder.promise();
167+
return recorder.promise()
161168
}

lib/container.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const WorkerStorage = require('./workerStorage')
1111
const store = require('./store')
1212
const ai = require('./ai')
1313

14+
let asyncHelperPromise = Promise.resolve()
15+
1416
let container = {
1517
helpers: {},
1618
support: {},
@@ -47,6 +49,7 @@ class Container {
4749
container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
4850
container.support = createSupportObjects(config.include || {})
4951
container.plugins = createPlugins(config.plugins || {}, opts)
52+
5053
if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
5154
if (config.gherkin) loadGherkinSteps(config.gherkin.steps || [])
5255
if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
@@ -138,6 +141,10 @@ class Container {
138141
container.translation = loadTranslation()
139142
}
140143

144+
static async started() {
145+
return asyncHelperPromise
146+
}
147+
141148
/**
142149
* Share data across worker threads
143150
*
@@ -170,21 +177,21 @@ function createHelpers(config) {
170177
if (!HelperClass) {
171178
HelperClass = requireHelperFromModule(helperName, config)
172179
}
173-
console.log('HelperClass', 'aa', HelperClass)
174180

175181
// handle async CJS modules that use dynamic import
176182
if (isAsyncFunction(HelperClass)) {
177-
HelperClass = HelperClass().then((ResolvedHelperClass) => {
178-
// Check if ResolvedHelperClass is a constructor function
179-
if (typeof ResolvedHelperClass?.constructor !== 'function') {
180-
throw new Error(`Helper class from module '${helperName}' is not a class. Use CJS async module syntax.`)
181-
}
182-
try {
183+
helpers[helperName] = {}
184+
185+
asyncHelperPromise = asyncHelperPromise
186+
.then(() => HelperClass())
187+
.then((ResolvedHelperClass) => {
188+
// Check if ResolvedHelperClass is a constructor function
189+
if (typeof ResolvedHelperClass?.constructor !== 'function') {
190+
throw new Error(`Helper class from module '${helperName}' is not a class. Use CJS async module syntax.`)
191+
}
192+
183193
helpers[helperName] = new ResolvedHelperClass(config[helperName])
184-
} catch (err) {
185-
throw new Error(`Could not load helper ${helperName} as async module (${err.message})`)
186-
}
187-
})
194+
})
188195

189196
continue
190197
}

0 commit comments

Comments
 (0)