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
193 changes: 87 additions & 106 deletions lib/els.js
Original file line number Diff line number Diff line change
@@ -1,115 +1,124 @@
const output = require('./output');
const store = require('./store');
const recorder = require('./recorder');
const container = require('./container');
const event = require('./event');
const Step = require('./step');
const { truth } = require('./assert/truth');
const { isAsyncFunction, humanizeFunction } = require('./utils');
const output = require('./output')
const store = require('./store')
const container = require('./container')
const StepConfig = require('./step/config')
const recordStep = require('./step/record')
const FuncStep = require('./step/func')
const { truth } = require('./assert/truth')
const { isAsyncFunction, humanizeFunction } = require('./utils')

function element(purpose, locator, fn) {
if (!fn) {
fn = locator;
locator = purpose;
purpose = 'first element';
let stepConfig
if (arguments[arguments.length - 1] instanceof StepConfig) {
stepConfig = arguments[arguments.length - 1]
}

const step = prepareStep(purpose, locator, fn);
if (!step) return;
if (!fn || fn === stepConfig) {
fn = locator
locator = purpose
purpose = 'first element'
}

return executeStep(step, async () => {
const els = await step.helper._locate(locator);
output.debug(`Found ${els.length} elements, using first element`);
const step = prepareStep(purpose, locator, fn)
if (!step) return

return fn(els[0]);
});
return executeStep(
step,
async () => {
const els = await step.helper._locate(locator)
output.debug(`Found ${els.length} elements, using first element`)

return fn(els[0])
},
stepConfig,
)
}

function eachElement(purpose, locator, fn) {
if (!fn) {
fn = locator;
locator = purpose;
purpose = 'for each element';
fn = locator
locator = purpose
purpose = 'for each element'
}

const step = prepareStep(purpose, locator, fn);
if (!step) return;
const step = prepareStep(purpose, locator, fn)
if (!step) return

return executeStep(step, async () => {
const els = await step.helper._locate(locator);
output.debug(`Found ${els.length} elements for each elements to iterate`);
const els = await step.helper._locate(locator)
output.debug(`Found ${els.length} elements for each elements to iterate`)

const errs = [];
let i = 0;
const errs = []
let i = 0
for (const el of els) {
try {
await fn(el, i);
await fn(el, i)
} catch (err) {
output.error(`eachElement: failed operation on element #${i} ${el}`);
errs.push(err);
output.error(`eachElement: failed operation on element #${i} ${el}`)
errs.push(err)
}
i++;
i++
}

if (errs.length) {
throw errs[0];
throw errs[0]
}
});
})
}

function expectElement(locator, fn) {
const step = prepareStep('expect element to be', locator, fn);
if (!step) return;
const step = prepareStep('expect element to be', locator, fn)
if (!step) return

return executeStep(step, async () => {
const els = await step.helper._locate(locator);
output.debug(`Found ${els.length} elements, first will be used for assertion`);
const els = await step.helper._locate(locator)
output.debug(`Found ${els.length} elements, first will be used for assertion`)

const result = await fn(els[0]);
const assertion = truth(`element (${locator})`, fn.toString());
assertion.assert(result);
});
const result = await fn(els[0])
const assertion = truth(`element (${locator})`, fn.toString())
assertion.assert(result)
})
}

function expectAnyElement(locator, fn) {
const step = prepareStep('expect any element to be', locator, fn);
if (!step) return;
const step = prepareStep('expect any element to be', locator, fn)
if (!step) return

return executeStep(step, async () => {
const els = await step.helper._locate(locator);
output.debug(`Found ${els.length} elements, at least one should pass the assertion`);
const els = await step.helper._locate(locator)
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)

const assertion = truth(`any element of (${locator})`, fn.toString());
const assertion = truth(`any element of (${locator})`, fn.toString())

let found = false;
let found = false
for (const el of els) {
const result = await fn(el);
const result = await fn(el)
if (result) {
found = true;
break;
found = true
break
}
}
if (!found) throw assertion.getException();
});
if (!found) throw assertion.getException()
})
}

function expectAllElements(locator, fn) {
const step = prepareStep('expect all elements', locator, fn);
if (!step) return;
const step = prepareStep('expect all elements', locator, fn)
if (!step) return

return executeStep(step, async () => {
const els = await step.helper._locate(locator);
output.debug(`Found ${els.length} elements, all should pass the assertion`);
const els = await step.helper._locate(locator)
output.debug(`Found ${els.length} elements, all should pass the assertion`)

let i = 1;
let i = 1
for (const el of els) {
output.debug(`checking element #${i}: ${el}`);
const result = await fn(el);
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn));
assertion.assert(result);
i++;
output.debug(`checking element #${i}: ${el}`)
const result = await fn(el)
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
assertion.assert(result)
i++
}
});
})
}

module.exports = {
Expand All @@ -118,60 +127,32 @@ module.exports = {
expectElement,
expectAnyElement,
expectAllElements,
};
}

function prepareStep(purpose, locator, fn) {
if (store.dryRun) return;
const helpers = Object.values(container.helpers());
if (store.dryRun) return
const helpers = Object.values(container.helpers())

const helper = helpers.filter(h => !!h._locate)[0];
const helper = helpers.filter(h => !!h._locate)[0]

if (!helper) {
throw new Error('No helper enabled with _locate method with returns a list of elements.');
throw new Error('No helper enabled with _locate method with returns a list of elements.')
}

if (!isAsyncFunction(fn)) {
throw new Error('Async function should be passed into each element');
throw new Error('Async function should be passed into each element')
}

const isAssertion = purpose.startsWith('expect');
const isAssertion = purpose.startsWith('expect')

const step = new Step(helper, `${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`);
step.setActor('EL');
step.setArguments([humanizeFunction(fn)]);
step.helperMethod = '_locate';
const step = new FuncStep(`${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`)
step.setHelper(helper)
step.setArguments([humanizeFunction(fn)]) // user defined function is a passed argument

return step;
return step
}

async function executeStep(step, action) {
let error;
const promise = recorder.add('register element wrapper', async () => {
event.emit(event.step.started, step);

try {
await action();
} catch (err) {
recorder.throw(err);
event.emit(event.step.failed, step, err);
event.emit(event.step.finished, step);
// event.emit(event.step.after, step)
error = err;
// await recorder.promise();
return;
}

event.emit(event.step.after, step);
event.emit(event.step.passed, step);
event.emit(event.step.finished, step);
});

// await recorder.promise();

// if (error) {
// console.log('error', error.inspect())
// return recorder.throw(error);
// }

return promise;
async function executeStep(step, action, stepConfig = {}) {
step.setCallable(action)
return recordStep(step, [stepConfig])
}
46 changes: 46 additions & 0 deletions lib/step/func.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const BaseStep = require('./base')
const store = require('../store')

/**
* Function executed as a step
*/
class FuncStep extends BaseStep {
// this is actual function that should be executed within step
setCallable(fn) {
this.fn = fn
}

// helper is optional, if we need to allow step to access helper methods
setHelper(helper) {
this.helper = helper
}

run() {
if (!this.fn) throw new Error('Function is not set')

// we wrap that function to track time and status
// and disable it in dry run mode
this.args = Array.prototype.slice.call(arguments)
this.startTime = +Date.now()

if (store.dryRun) {
this.setStatus('success')
// should we add Proxy and dry run resolver here?
return Promise.resolve(true)
}

let result
try {
result = this.fn.apply(this.helper, this.args)
this.setStatus('success')
this.endTime = +Date.now()
} catch (err) {
this.endTime = +Date.now()
this.setStatus('failed')
throw err
}
return result
}
}

module.exports = FuncStep
Loading
Loading