Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
586 changes: 291 additions & 295 deletions docs/helpers/Appium.md

Large diffs are not rendered by default.

139 changes: 47 additions & 92 deletions lib/actor.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const Step = require('./step');
const { MetaStep } = require('./step');
const container = require('./container');
const { methodsOfObject } = require('./utils');
const recorder = require('./recorder');
const event = require('./event');
const store = require('./store');
const output = require('./output');
const Step = require('./step')
const MetaStep = require('./step/meta')
const recordStep = require('./step/record')
const container = require('./container')
const { methodsOfObject } = require('./utils')
const { TIMEOUT_ORDER } = require('./step/timeout')
const recorder = require('./recorder')
const event = require('./event')
const store = require('./store')
const output = require('./output')

/**
* @interface
Expand All @@ -21,13 +23,13 @@ class Actor {
* ⚠️ returns a promise which is synchronized internally by recorder
*/
async say(msg, color = 'cyan') {
const step = new Step('say', 'say');
step.status = 'passed';
const step = new Step('say', 'say')
step.status = 'passed'
return recordStep(step, [msg]).then(() => {
// this is backward compatibility as this event may be used somewhere
event.emit(event.step.comment, msg);
output.say(msg, `${color}`);
});
event.emit(event.step.comment, msg)
output.say(msg, `${color}`)
})
}

/**
Expand All @@ -38,14 +40,16 @@ class Actor {
* @inner
*/
limitTime(timeout) {
if (!store.timeouts) return this;
if (!store.timeouts) return this

console.log('I.limitTime() is deprecated, use step.timeout() instead')

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

return this;
return this
}

/**
Expand All @@ -55,11 +59,10 @@ class Actor {
* @inner
*/
retry(opts) {
if (opts === undefined) opts = 1;
recorder.retry(opts);
// remove retry once the step passed
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()));
return this;
console.log('I.retry() is deprecated, use step.retry() instead')
const retryStep = require('./step/retry')
retryStep(opts)
return this
}
}

Expand All @@ -70,102 +73,54 @@ class Actor {
* @ignore
*/
module.exports = function (obj = {}) {
const actor = container.actor() || new Actor();
const actor = container.actor() || new Actor()

// load all helpers once container initialized
container.started(() => {
const translation = container.translation();
const helpers = container.helpers();
const translation = container.translation()
const helpers = container.helpers()

// add methods from enabled helpers
Object.values(helpers).forEach(helper => {
methodsOfObject(helper, 'Helper')
.filter(method => method !== 'constructor' && method[0] !== '_')
.forEach(action => {
const actionAlias = translation.actionAliasFor(action);
const actionAlias = translation.actionAliasFor(action)
if (!actor[action]) {
actor[action] = actor[actionAlias] = function () {
const step = new Step(helper, action);
const step = new Step(helper, action)
if (translation.loaded) {
step.name = actionAlias;
step.actor = translation.I;
step.name = actionAlias
step.actor = translation.I
}
// add methods to promise chain
return recordStep(step, Array.from(arguments));
};
return recordStep(step, Array.from(arguments))
}
}
});
});
})
})

// add translated custom steps from actor
Object.keys(obj).forEach(key => {
const actionAlias = translation.actionAliasFor(key);
const actionAlias = translation.actionAliasFor(key)
if (!actor[actionAlias]) {
actor[actionAlias] = actor[key];
actor[actionAlias] = actor[key]
}
});
})

container.append({
support: {
I: actor,
},
});
});
})
})
// store.actor = actor;
// add custom steps from actor
Object.keys(obj).forEach(key => {
const ms = new MetaStep('I', key);
ms.setContext(actor);
actor[key] = ms.run.bind(ms, obj[key]);
});

return actor;
};

function recordStep(step, args) {
step.status = 'queued';
step.setArguments(args);

// run async before step hooks
event.emit(event.step.before, step);

const task = `${step.name}: ${step.humanizeArgs()}`;
let val;

// run step inside promise
recorder.add(
task,
() => {
if (!step.startTime) {
// step can be retries
event.emit(event.step.started, step);
step.startTime = Date.now();
}
return (val = step.run(...args));
},
false,
undefined,
step.getTimeout(),
);

event.emit(event.step.after, step);

recorder.add('step passed', () => {
step.endTime = Date.now();
event.emit(event.step.passed, step, val);
event.emit(event.step.finished, step);
});

recorder.catchWithoutStop(err => {
step.status = 'failed';
step.endTime = Date.now();
event.emit(event.step.failed, step);
event.emit(event.step.finished, step);
throw err;
});

recorder.add('return result', () => val);
// run async after step hooks
const ms = new MetaStep('I', key)
ms.setContext(actor)
actor[key] = ms.run.bind(ms, obj[key])
})

return recorder.promise();
return actor
}
5 changes: 5 additions & 0 deletions lib/helper/Playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -3499,6 +3499,11 @@ async function proceedSee(assertType, text, context, strict = false) {
allText = await Promise.all(els.map(el => el.innerText()))
}

if (store?.currentStep?.opts?.ignoreCase === true) {
text = text.toLowerCase()
allText = allText.map(elText => elText.toLowerCase())
}

if (strict) {
return allText.map(elText => equals(description)[assertType](text, elText))
}
Expand Down
5 changes: 5 additions & 0 deletions lib/helper/Puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,11 @@ async function proceedSee(assertType, text, context, strict = false) {
allText = await Promise.all(els.map(el => el.getProperty('innerText').then(p => p.jsonValue())))
}

if (store?.currentStep?.opts?.ignoreCase === true) {
text = text.toLowerCase()
allText = allText.map(elText => elText.toLowerCase())
}

if (strict) {
return allText.map(elText => equals(description)[assertType](text, elText))
}
Expand Down
10 changes: 9 additions & 1 deletion lib/helper/WebDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const Helper = require('@codeceptjs/helper')
const promiseRetry = require('promise-retry')
const stringIncludes = require('../assert/include').includes
const { urlEquals, equals } = require('../assert/equal')
const store = require('../store')
const { debug } = require('../output')
const { empty } = require('../assert/empty')
const { truth } = require('../assert/truth')
Expand Down Expand Up @@ -2698,7 +2699,14 @@ async function proceedSee(assertType, text, context, strict = false) {
const smartWaitEnabled = assertType === 'assert'
const res = await this._locate(withStrictLocator(context), smartWaitEnabled)
assertElementExists(res, context)
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
let selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))

// apply ignoreCase option
if (store?.currentStep?.opts?.ignoreCase === true) {
text = text.toLowerCase()
selected = selected.map(elText => elText.toLowerCase())
}

if (strict) {
if (Array.isArray(selected) && selected.length !== 0) {
return selected.map(elText => equals(description)[assertType](text, elText))
Expand Down
2 changes: 1 addition & 1 deletion lib/listener/globalTimeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const output = require('../output')
const recorder = require('../recorder')
const Config = require('../config')
const { timeouts } = require('../store')
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
const { TIMEOUT_ORDER } = require('../step/timeout')

module.exports = function () {
let timeout
Expand Down
3 changes: 3 additions & 0 deletions lib/listener/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ module.exports = function () {
event.dispatcher.on(event.step.started, step => {
step.startedAt = +new Date()
step.test = currentTest
store.currentStep = step
if (currentHook && Array.isArray(currentHook.steps)) {
return currentHook.steps.push(step)
}
Expand All @@ -84,5 +85,7 @@ module.exports = function () {
step.finishedAt = +new Date()
if (step.startedAt) step.duration = step.finishedAt - step.startedAt
debug(`Step '${step}' finished; Duration: ${step.duration || 0}ms`)
store.currentStep = null
store.stepOptions = null
})
}
2 changes: 1 addition & 1 deletion lib/plugin/stepTimeout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const event = require('../event')
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
const { TIMEOUT_ORDER } = require('../step/timeout')

const defaultConfig = {
timeout: 150,
Expand Down
2 changes: 1 addition & 1 deletion lib/recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ module.exports = {
}
if (retry === undefined) retry = true
if (!running && !force) {
return
return Promise.resolve()
}
tasks.push(taskName)
debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`))
Expand Down
Loading
Loading