Skip to content

Commit

Permalink
Introducing the .first() action
Browse files Browse the repository at this point in the history
  • Loading branch information
Joris-van-der-Wel committed Nov 13, 2018
1 parent 0545d10 commit 6eeb20a
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 2 deletions.
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -251,3 +251,11 @@ await wait.selector('p.introduction').containsText('hello')
await wait.selector('p.introduction').containsText(/hello/)
await wait.selectorAll('p').containsText('ipsum').amount(10, 50);
```

#### .first()
This action returns the first element (index 0) from `current value`

```javascript
const oneImage = await wait.selectorAll('.gallery img').isDisplayed().first();
const button = await wait.selectorAll('button').check(n => /Confirm Order/i.test(n.textContent)).first()
```
7 changes: 7 additions & 0 deletions lib/ExpressionChainable.js
Expand Up @@ -149,6 +149,13 @@ class ExpressionChainable {
containsText(text) {
return this.createNextExpression(new actions.ContainsText(text));
}

/**
* @return {ExpressionChainable}
*/
first() {
return this.createNextExpression(new actions.First());
}
}

module.exports = ExpressionChainable;
20 changes: 20 additions & 0 deletions lib/actions.js
Expand Up @@ -547,6 +547,25 @@ class ContainsText extends Action {
}
}

class First extends Action {
constructor() {
super();
Object.freeze(this);
}

execute(currentResult) {
if (Array.isArray(currentResult)) {
const result = currentResult.length ? currentResult[0] : null;
return executeSuccess(result);
}
return executeSuccess(currentResult);
}

describe() {
return `but only returning the first result`;
}
}

const DEFAULT_AMOUNT_ACTION = new Amount(1, Infinity);

exports.Action = Action;
Expand All @@ -564,3 +583,4 @@ exports.Delay = Delay;
exports.IsDisplayed = IsDisplayed;
exports.Check = Check;
exports.ContainsText = ContainsText;
exports.First = First;
46 changes: 45 additions & 1 deletion test/integration/selector.js
Expand Up @@ -2,7 +2,7 @@
/* global BLUEFOX_TEST_ENV */

const {describe, it, beforeEach, afterEach} = require('mocha-sugar-free');
const {assert: {isBelow, isAtLeast}} = require('chai');
const {assert: {isBelow, isAtLeast, deepEqual: deq, strictEqual: eq}} = require('chai');

const {navigate, closeWindow, run, getProgress} = BLUEFOX_TEST_ENV;

Expand Down Expand Up @@ -134,4 +134,48 @@ describe('Waiting for a CSS Selector', {slow: 2000, timeout: 20000}, () => {
isAtLeast(progress.indexOf('after wait 0'), 0);
isBelow(progress.indexOf('modification'), progress.indexOf('after wait 0'));
});

it('Should wait for multiple elements', async () => {
await navigate('static/mutation-add-element.html');

{
const elements = await run(async ({window, Bluefox, reportProgress}) => {
const elements = await new Bluefox().target(window).timeout('10s').selectorAll('p, strong').amount(4);
reportProgress('after wait 0');
return elements.map(element => element.id);
});
deq(elements, ['firstP', 'modification', 'secondP', 'thirdP']);
}
{
const elements = await run(async ({window, Bluefox, reportProgress}) => {
const elements = await new Bluefox().target(window).timeout('10s').selectorAll('p, strong').amount(4);
reportProgress('after wait 1');
return elements.map(element => element.id);
});
deq(elements, ['firstP', 'modification', 'secondP', 'thirdP']);
}

const progress = await getProgress();
isAtLeast(progress.indexOf('modification'), 0);
isAtLeast(progress.indexOf('after wait 0'), 0);
isAtLeast(progress.indexOf('after wait 1'), 0);
isBelow(progress.indexOf('modification'), progress.indexOf('after wait 0'));
isBelow(progress.indexOf('modification'), progress.indexOf('after wait 1'));
});

it('Should wait for multiple elements but returning only the first', async () => {
await navigate('static/mutation-add-element.html');

const element = await run(async ({window, Bluefox, reportProgress}) => {
const element = await new Bluefox().target(window).timeout('10s').selectorAll('p, strong').amount(4).first();
reportProgress('after wait 0');
return element.id;
});
eq(element, 'firstP');

const progress = await getProgress();
isAtLeast(progress.indexOf('modification'), 0);
isAtLeast(progress.indexOf('after wait 0'), 0);
isBelow(progress.indexOf('modification'), progress.indexOf('after wait 0'));
});
});
3 changes: 2 additions & 1 deletion test/unit/Expression.js
Expand Up @@ -338,6 +338,7 @@ describe('Expression', () => {
.xpathAll('.//img')
.isDisplayed()
.amount(2, 10)
.first()
.describe(),
'The expression sets the target to <#document>, waits up to 4.5 seconds until the HTML document has finished parsing, ' +
'waits until 3 seconds have elapsed since the start of the execution, waits up to 4.5 seconds until the HTML document ' +
Expand All @@ -347,7 +348,7 @@ describe('Expression', () => {
'ollTop > 0`, waits up to 4.5 seconds until all synchronous resources of the HTML document have been loaded, finds the' +
' first element matching the XPath expression “./../section”, finds all elements matching the XPath expression “.//img' +
'”, but only including elements which are displayed on the page, waits up to 10 seconds until between 2 and 10 (inclus' +
'ive) results are found.'
'ive) results are found, but only returning the first result.'
);
});

Expand Down
66 changes: 66 additions & 0 deletions test/unit/actions.js
Expand Up @@ -1264,4 +1264,70 @@ describe('actions', () => {
});
});
});

describe('First', () => {
it('Should be frozen', () => {
ok(Object.isFrozen(new actions.First()));
});

describe('#execute()', () => {
let foo;
let bar;
let baz;

beforeEach(() => {
foo = document.createElement('div');
bar = document.createElement('div');
baz = document.createElement('div');
foo.textContent = 'FOO Foo!';
bar.textContent = 'BAR Bar!';
baz.textContent = 'BAZ Baz!';
bar.appendChild(baz);
});

it('Should return null as-is', () => {
const action = new actions.First();
const result = action.execute(null);
eq(result.status, RESULT_STATUS_SUCCESS);
ok(result.value === null);
});

it('Should return null for an empty array', () => {
const action = new actions.First();
const result = action.execute([]);
eq(result.status, RESULT_STATUS_SUCCESS);
ok(result.value === null);
});

it('Should return a non array value as-is', () => {
const action = new actions.First();
const result = action.execute(foo);
eq(result.status, RESULT_STATUS_SUCCESS);
ok(result.value === foo);
});

it('Should return the first element for an array', () => {
const action = new actions.First();
const result = action.execute([foo]);
eq(result.status, RESULT_STATUS_SUCCESS);
ok(result.value === foo);
});

it('Should return the first element for an array', () => {
const action = new actions.First();
const result = action.execute([bar, foo]);
eq(result.status, RESULT_STATUS_SUCCESS);
ok(result.value === bar);
});
});

describe('#describe()', () => {
it('Should describe the filter', () => {
eq(
new actions.First().describe(),
'but only returning the first result'
);
});
});
});
});

0 comments on commit 6eeb20a

Please sign in to comment.