Skip to content

Commit ed18031

Browse files
committed
feat: Add various value getting and checking instructions
setViewport, selectOptionByIndex, selectOptionByValue, hasAttribute, hasClass, hasValue, hasText, hasInnerHtml, hasOuterHtml, isEnabled, isExisting, isFocused, isSelected, isVisible, isVisibleWithinViewport, isNotEnabled, isNotExisting, isNotFocused, isNotSelected, isNotVisible, isNotVisibleWithinViewport
1 parent ab8d25c commit ed18031

27 files changed

+610
-12
lines changed

Gruntfile.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,90 @@ module.exports = function (grunt) {
247247
}
248248
});
249249
}
250+
},
251+
{
252+
url: 'http://localhost:8881/test/pages/values.html',
253+
isEnabled: 'select',
254+
isExisting: 'select',
255+
isFocused: 'select',
256+
isVisible: 'select',
257+
isVisibleWithinViewport: 'select',
258+
isNotEnabled: 'input',
259+
isNotExisting: 'textarea',
260+
isNotFocused: 'input',
261+
isNotVisible: 'input',
262+
isNotVisibleWithinViewport: 'input',
263+
hasAtribute: {
264+
selector: 'input',
265+
name: 'disabled',
266+
value: 'disabled'
267+
},
268+
hasValue: {
269+
selector: 'input',
270+
value: 'test'
271+
},
272+
hasText: {
273+
selector: 'div',
274+
value: 'Text'
275+
},
276+
hasInnerHtml: {
277+
selector: 'div',
278+
value: 'Text'
279+
},
280+
hasOuterHtml: {
281+
selector: 'div',
282+
value: '<div class="class" tabindex="0">Text</div>'
283+
}
284+
},
285+
{
286+
hasClass: {
287+
selector: 'div',
288+
value: 'class'
289+
}
290+
},
291+
{
292+
hasClass: {
293+
selector: 'div',
294+
value: '!none'
295+
}
296+
},
297+
{
298+
hasClass: {
299+
selector: 'div',
300+
value: 'class none'
301+
}
302+
},
303+
{
304+
selectOptionByIndex: {
305+
selector: 'select',
306+
index: 1
307+
},
308+
hasValue: {
309+
selector: 'select',
310+
value: '2'
311+
}
312+
},
313+
{
314+
selectOptionByValue: {
315+
selector: 'select',
316+
value: '1'
317+
},
318+
hasValue: {
319+
selector: 'select',
320+
value: '1'
321+
}
322+
},
323+
{
324+
setViewport: {
325+
width: 768,
326+
height: 480
327+
}
328+
},
329+
{
330+
setViewport: {}
331+
},
332+
{
333+
setViewport: {}
250334
}
251335
],
252336
scenarios: 'test/scenarios/*.js'

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,15 +219,36 @@ You can use sub-tasks, `commands` and `scenarios` to structure your code and exe
219219

220220
One of the instructions has to be present in every command. These properties are evaluated (and their effect is executed) in the order, in which they are listed below:
221221

222+
* setViewport
222223
* url
223224
* go
224225
* clearValue
225226
* setValue
226227
* addValue
228+
* selectOptionByIndex
229+
* selectOptionByValue
227230
* moveCursor
228231
* click
229232
* keys
230233
* wait
234+
* hasAttribute
235+
* hasClass
236+
* hasValue
237+
* hasText
238+
* hasInnerHtml
239+
* hasOuterHtml
240+
* isEnabled
241+
* isExisting
242+
* isFocused
243+
* isSelected
244+
* isVisible
245+
* isVisibleWithinViewport
246+
* isNotEnabled
247+
* isNotExisting
248+
* isNotFocused
249+
* isNotSelected
250+
* isNotVisible
251+
* isNotVisibleWithinViewport
231252
* file
232253

233254
#### url
@@ -313,6 +334,44 @@ Appends to the current value of an input element. The object should contain the
313334
}
314335
```
315336

337+
#### selectOptionByIndex
338+
Type: `Object`
339+
340+
Select an `option` element of a `select` element by the specified (zero-based)
341+
index. The object should contain the following properties:
342+
343+
* `selector` - `String` - selector of a select element.
344+
* `index` - `Number` - numeric (0-based integer) index of an option to select.
345+
346+
```js
347+
{
348+
url: 'https://example.com',
349+
setValue: {
350+
selector: 'select',
351+
index: 1 // select second option
352+
}
353+
}
354+
```
355+
356+
#### selectOptionByValue
357+
Type: `Object`
358+
359+
Select an `option` element of a `select` element by the specified value of
360+
the `value` attribute. The object should contain the following properties:
361+
362+
* `selector` - `String` - selector of a select element.
363+
* `value` - `String` - value of the `value` attribute of an option to select.
364+
365+
```js
366+
{
367+
url: 'https://example.com',
368+
setValue: {
369+
selector: 'select',
370+
value: 'second'
371+
}
372+
}
373+
```
374+
316375
#### moveCursor
317376
Type: `String` | `Object`
318377

@@ -664,6 +723,7 @@ your code using Grunt.
664723
665724
## Release History
666725
726+
* 2018-02-26 [v0.5.0] Allow checking and setting various properties
667727
* 2018-02-22 [v0.4.0] Allow sending key strokes to the browser
668728
* 2018-01-30 [v0.3.0] Allow specifying test commands in separate modules
669729
* 2018-01-27 [v0.2.0] Allow saving screenshots in addition to snapshots

tasks/html-dom-snapshot.js

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ const fs = require('fs'),
1313
path = require('path'),
1414
mkdirp = require('mkdirp'),
1515
instructions = [
16-
'url', 'go', 'clearValue', 'setValue', 'addValue', 'moveCursor',
17-
'click', 'keys', 'wait'
16+
'setViewport', 'url', 'go', 'clearValue', 'setValue', 'addValue',
17+
'selectOptionByIndex', 'selectOptionByValue', 'moveCursor',
18+
'click', 'keys', 'wait', 'hasAttribute', 'hasClass', 'hasValue',
19+
'hasText', 'hasInnerHtml', 'hasOuterHtml',
20+
'isEnabled', 'isExisting', 'isFocused', 'isSelected', 'isVisible',
21+
'isVisibleWithinViewport', 'isNotEnabled', 'isNotExisting',
22+
'isNotFocused', 'isNotSelected', 'isNotVisible',
23+
'isNotVisibleWithinViewport'
1824
].map(function (instruction) {
1925
return require('./instructions/' + instruction);
2026
});
@@ -44,11 +50,15 @@ module.exports = function (grunt) {
4450
target = this.target,
4551
pages = data.pages,
4652
snapshots = options.dest,
53+
viewport = options.viewport,
54+
lastViewport = {
55+
width: viewport.width,
56+
height: viewport.height
57+
},
4758
client = webdriverio.remote({
4859
desiredCapabilities: options.browserCapabilities
4960
});
50-
var lastViewport = options.viewport,
51-
urlCount = 0,
61+
var urlCount = 0,
5262
snapshotCount = 0,
5363
screenshotCount = 0,
5464
commands;
@@ -145,7 +155,9 @@ module.exports = function (grunt) {
145155
}
146156

147157
function performCommand(command) {
148-
const commandOptions = Object.assign({}, options, command.options || {}),
158+
const commandOptions = Object.assign({
159+
lastViewport: lastViewport
160+
}, options, command.options || {}),
149161
file = command.file,
150162
viewport = commandOptions.viewport,
151163
screenshots = commandOptions.screenshots,
@@ -170,9 +182,10 @@ module.exports = function (grunt) {
170182
'in the target "' + target + '".\n' +
171183
JSON.stringify(command));
172184
}
173-
if (viewport.width !== lastViewport.width ||
174-
viewport.height !== lastViewport.height) {
175-
lastViewport = viewport;
185+
if ((viewport.width !== lastViewport.width ||
186+
viewport.height !== lastViewport.height) && !lastViewport.explicit) {
187+
lastViewport.width = viewport.width;
188+
lastViewport.height = viewport.height;
176189
viewportSet = setViewportSize();
177190
} else {
178191
viewportSet = Promise.resolve();
@@ -182,8 +195,10 @@ module.exports = function (grunt) {
182195
}
183196
return commandInstructions.reduce(function (previous, instruction) {
184197
return previous.then(function () {
185-
if (instruction.detected) {
186-
return instruction.perform(grunt, target, client, command, commandOptions);
198+
const detected = instruction.detected;
199+
if (detected) {
200+
return instruction.perform(grunt, target, client, command,
201+
commandOptions, detected);
187202
}
188203
});
189204
}, viewportSet)

tasks/instructions/go.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ module.exports = {
77

88
perform: function (grunt, target, client, command) {
99
const go = command.go;
10-
if (!(go === 'back' || go === 'forward' || go === 'refresh')) {
10+
if (!(go === 'back' || go === 'forward' || go === 'refresh' ||
11+
go === 'reload')) {
1112
throw new Error('Invalid direction to go to: "' + go +
1213
'" in the target "' + target + '".\n' +
1314
JSON.stringify(command));

tasks/instructions/hasAttribute.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
module.exports = {
4+
detect: function (command) {
5+
return !!command.hasAttribute;
6+
},
7+
8+
perform: function (grunt, target, client, command) {
9+
const hasAttribute = command.hasAttribute,
10+
selector = hasAttribute.selector,
11+
name = hasAttribute.name,
12+
expected = hasAttribute.value;
13+
grunt.log.ok('Looking for attribute "' + name + '" with value "' +
14+
expected + '" at "' + selector + '".');
15+
return client.getAttribute(selector, name)
16+
.then(function (actual) {
17+
if (actual != expected) {
18+
throw new Error ('Value of attribute "' + name + '" at "' +
19+
selector + '" was not "' + expected + '" but "' + actual + '".');
20+
}
21+
});
22+
}
23+
};

tasks/instructions/hasClass.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
module.exports = {
4+
detect: function (command) {
5+
return !!command.hasClass;
6+
},
7+
8+
perform: function (grunt, target, client, command) {
9+
const hasClass = command.hasClass,
10+
selector = hasClass.selector,
11+
expected = hasClass.value || '',
12+
expectedList = expected.split(/ +/);
13+
grunt.log.ok('Looking for classes "' + expected + '" at "' +
14+
selector + '".');
15+
return client.getAttribute(selector, 'class')
16+
.then(function (actual) {
17+
const actualList = (actual || '').split(/ +/);
18+
if (!expectedList.some(function (expected) {
19+
return expected.startsWith('!') ?
20+
actualList.indexOf(expected.substr(1)) < 0 :
21+
actualList.indexOf(expected) >= 0;
22+
})) {
23+
throw new Error ('Classes "' + expected +
24+
'" were not found among "' + actual + '".at "' + selector + '".');
25+
}
26+
});
27+
}
28+
};

tasks/instructions/hasInnerHtml.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
module.exports = {
4+
detect: function (command) {
5+
return !!command.hasInnerHtml;
6+
},
7+
8+
perform: function (grunt, target, client, command) {
9+
const hasInnerHtml = command.hasInnerHtml,
10+
selector = hasInnerHtml.selector,
11+
expected = hasInnerHtml.value;
12+
grunt.log.ok('Comparing inner HTML at "' + selector + '" to "' +
13+
expected + '".');
14+
return client.getHTML(selector, false)
15+
.then(function (actual) {
16+
if (actual != expected) {
17+
throw new Error ('Inner HTML at "' + selector + '" was not "' +
18+
expected + '" but "' + actual + '".');
19+
}
20+
});
21+
}
22+
};

tasks/instructions/hasOuterHtml.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
module.exports = {
4+
detect: function (command) {
5+
return !!command.hasInnhasOuterHtmlerHtml;
6+
},
7+
8+
perform: function (grunt, target, client, command) {
9+
const hasOuterHtml = command.hasOuterHtml,
10+
selector = hasOuterHtml.selector,
11+
expected = hasOuterHtml.value;
12+
grunt.log.ok('Comparing outer HTML at "' + selector + '" to "' +
13+
expected + '".');
14+
return client.getHTML(selector, true)
15+
.then(function (actual) {
16+
if (actual != expected) {
17+
throw new Error ('Outer HTML at "' + selector + '" was not "' +
18+
expected + '" but "' + actual + '".');
19+
}
20+
});
21+
}
22+
};

tasks/instructions/hasText.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
module.exports = {
4+
detect: function (command) {
5+
return !!command.hasText;
6+
},
7+
8+
perform: function (grunt, target, client, command) {
9+
const hasText = command.hasText,
10+
selector = hasText.selector,
11+
expected = hasText.value;
12+
grunt.log.ok('Comparing text at "' + selector + '" to "' +
13+
expected + '".');
14+
return client.getText(selector)
15+
.then(function (actual) {
16+
if (actual != expected) {
17+
throw new Error ('Text at "' + selector + '" was not "' +
18+
expected + '" but "' + actual + '".');
19+
}
20+
});
21+
}
22+
};

0 commit comments

Comments
 (0)