Skip to content

Commit 1107121

Browse files
committed
feat: Introduce the option "singleElementSelections" to enforce selectors mathing none or one element; never multiple ones
1 parent dee2886 commit 1107121

34 files changed

+212
-46
lines changed

Gruntfile.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,19 @@ module.exports = function (grunt) {
338338
value: '<div class="class" tabindex="0">Text</div>'
339339
}
340340
},
341+
{
342+
options: {
343+
singleElementSelections: true
344+
},
345+
isExisting: 'select'
346+
},
347+
{
348+
options: {
349+
singleElementSelections: true,
350+
force: true
351+
},
352+
isExisting: 'option'
353+
},
341354
coverage ? { wait: 1 } : {
342355
focus: 'body',
343356
wait: function (browser) {

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Default options support the most usual usage scenario:
9696
fileNumberSeparator: '.',
9797
hangOnError: false,
9898
snapshotOnError: '_last-error',
99+
singleElementSelections: false,
99100
force: false
100101
},
101102
'google': {
@@ -221,6 +222,14 @@ Default value: '_last-error'
221222

222223
If set to a non-empty string, if will be used as a file name for an automatically taken snapshot and screenshot (if those are enabled), if the task execution fails.
223224

225+
### singleElementSelections
226+
Type: `Boolean`
227+
Default value: false
228+
229+
If set to `true`, it will enforce every selector match either one or none elements. If any selector matches multiple elements, the instruction will fail. Tests usually address just one element. This setting helps to uncover errors like checking `isFocus` for multiple elements, which has unpredictable results.
230+
231+
This option can be set for the whole task or within a single command object.
232+
224233
#### force
225234
Type: `Boolean`
226235
Default value: false

tasks/html-dom-snapshot.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module.exports = grunt => {
5555
fileNumberSeparator: '.',
5656
hangOnError: false,
5757
snapshotOnError: '_last-error',
58+
singleElementSelections: false,
5859
force: false,
5960
verbose: false
6061
})

tasks/instructions/addValue.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
'use strict'
22

3+
const { checkSingleElement } = require('./utils/elements')
4+
35
module.exports = {
46
detect: function (command) {
57
return !!command.addValue
68
},
79

8-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
911
const addValue = command.addValue
1012
const selector = addValue.selector
1113
const value = addValue.value
1214
grunt.output.writeln('Add "' + value + '" to value of "' +
1315
selector + '".')
16+
if (options.singleElementSelections) {
17+
await checkSingleElement(client, selector)
18+
}
1419
return client.$(selector)
1520
.then(element => element.addValue(value))
1621
}

tasks/instructions/clearValue.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
'use strict'
22

3-
const { findElement } = require('./utils/elements')
3+
const { findElement, checkSingleElement } = require('./utils/elements')
44

55
module.exports = {
66
detect: function (command) {
77
return !!command.clearValue
88
},
99

10-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
1111
const clearValue = command.clearValue
1212
grunt.output.writeln('Clear value of "' + clearValue + '".')
13+
if (options.singleElementSelections) {
14+
await checkSingleElement(client, clearValue)
15+
}
1316
return findElement(client, clearValue)
1417
.then(element => client.elementClear(element))
1518
}

tasks/instructions/click.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
'use strict'
22

3-
const { findElement } = require('./utils/elements')
3+
const { findElement, checkSingleElement } = require('./utils/elements')
44

55
module.exports = {
66
detect: function (command) {
77
return !!command.click
88
},
99

10-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
1111
const click = command.click
1212
grunt.output.writeln('Click on "' + click + '".')
13+
if (options.singleElementSelections) {
14+
await checkSingleElement(client, click)
15+
}
1316
return findElement(client, click)
1417
.then(element => client.elementClick(element))
1518
}

tasks/instructions/clickIfVisible.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
'use strict'
22

3-
const { findElement } = require('./utils/elements')
3+
const { findElement, checkSingleElement } = require('./utils/elements')
44

55
module.exports = {
66
detect: function (command) {
77
return !!command.clickIfVisible
88
},
99

10-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
1111
const clickIfVisible = command.clickIfVisible
1212
grunt.output.writeln('Checking visibility of "' + clickIfVisible + '"...')
1313
let clickable
14+
if (options.singleElementSelections) {
15+
await checkSingleElement(client, clickIfVisible)
16+
}
1417
return findElement(client, clickIfVisible)
1518
.then(element => {
1619
clickable = element

tasks/instructions/elementSendKeys.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
'use strict'
22

3-
const { findElement } = require('./utils/elements')
3+
const { findElement, checkSingleElement } = require('./utils/elements')
44

55
module.exports = {
66
detect: function (command) {
77
return !!command.elementSendKeys
88
},
99

10-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
1111
const elementSendKeys = command.elementSendKeys
1212
const selector = elementSendKeys.selector
1313
let keys = elementSendKeys.keys || []
1414
let text = elementSendKeys.text || ''
15-
// const separateKeys = Array.isArray(keys)
1615
const message = text
1716
? 'Send text "' + text + '" to "' + selector + '".'
1817
: 'Send keys "' + keys.join('", "') + '" to "' + selector + '".'
19-
// if (!separateKeys) {
20-
// keys = keys.split('')
21-
// }
2218
grunt.output.writeln(message)
19+
if (options.singleElementSelections) {
20+
await checkSingleElement(client, selector)
21+
}
2322
return findElement(client, selector)
2423
.then(element => client.elementSendKeys(element, text, keys))
2524
}

tasks/instructions/focus.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66

77
/* istanbul ignore next */
88

9+
const { checkSingleElement } = require('./utils/elements')
10+
911
module.exports = {
1012
detect: function (command) {
1113
return !!command.focus
1214
},
1315

14-
perform: function (grunt, target, client, command) {
16+
perform: async function (grunt, target, client, command, options) {
1517
const focus = command.focus
1618
grunt.output.writeln('Focus "' + focus + '".')
19+
if (options.singleElementSelections) {
20+
await checkSingleElement(client, focus)
21+
}
1722
return client.execute(function (selector) {
1823
var element = document.querySelector(selector)
1924
if (!element) {

tasks/instructions/hasAttribute.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
'use strict'
22

3+
const { checkSingleElement } = require('./utils/elements')
4+
35
module.exports = {
46
detect: function (command) {
57
return !!command.hasAttribute
68
},
79

8-
perform: function (grunt, target, client, command) {
10+
perform: async function (grunt, target, client, command, options) {
911
const hasAttribute = command.hasAttribute
1012
const selector = hasAttribute.selector
1113
const name = hasAttribute.name
1214
const expected = hasAttribute.value
1315
grunt.log.ok('Looking for attribute "' + name + '" with value "' +
1416
expected + '" at "' + selector + '".')
17+
if (options.singleElementSelections) {
18+
await checkSingleElement(client, selector)
19+
}
1520
return client.$(selector)
1621
.then(element => element.getAttribute(name))
1722
.then(function (actual) {

0 commit comments

Comments
 (0)