Skip to content

Commit

Permalink
Bug(checkbox|radio): Set checked value on native element setter so th…
Browse files Browse the repository at this point in the history
…at events are triggered properly (#2377)

* Bug(checkbox|radio): Set checked value on native element setter so that events are triggered properly

Signed-off-by: atulv <[email protected]>

* Bug(specs): make specs more deterministic to avoid failing in headful mode

Signed-off-by: atulv <[email protected]>
  • Loading branch information
lethaldose committed Nov 3, 2021
1 parent f33c9f4 commit e07b474
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 30 deletions.
3 changes: 2 additions & 1 deletion lib/elements/checkBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class CheckBox extends Element {
return result.value;
}
async check() {
await this.registerNativeValueSetter();
const navigationOptions = setNavigationOptions({});
await doActionAwaitingNavigation(navigationOptions, async () => {
const objectId = this.get();
Expand Down Expand Up @@ -49,7 +50,7 @@ class CheckBox extends Element {
}

function setChecked(value) {
this.checked = value;
this.setNativeValue(this, 'checked', value);
['change', 'input', 'click'].forEach((ev) => {
let event = new Event(ev, { bubbles: true });
try {
Expand Down
38 changes: 30 additions & 8 deletions lib/elements/element.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { setNativeValue } = require('./elementHelper');
/**
* Abstract Element present on the web page. Extra methods are available based on the element type.
* @see {ElementWrapper} for methods available
*/

class Element {
constructor(objectId, description, runtimeHandler) {
this.objectId = objectId;
Expand Down Expand Up @@ -126,13 +126,35 @@ class Element {
}

async registerNativeValueSetter() {
return await this.runtimeHandler.runtimeEvaluate(
`if (typeof globalThis.setNativeValue === 'undefined') {
globalThis.setNativeValue = ` +
setNativeValue.toString() +
`
}`,
);
function defineNativeSetterProperty() {
const setNativeValue = function (element, propName, value) {
const { set: valueSetter } = Object.getOwnPropertyDescriptor(element, propName) || {};
const prototype = Object.getPrototypeOf(element);
const { set: prototypeValueSetter } =
Object.getOwnPropertyDescriptor(prototype, propName) || {};

if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else if (valueSetter) {
valueSetter.call(element, value);
} else {
throw new Error('The given element does not have a value setter');
}
};

if (typeof this.setNativeValue === 'undefined') {
Object.defineProperty(this.constructor.prototype, 'setNativeValue', {
configurable: true,
enumerable: false,
value: setNativeValue,
});
}
}

await this.runtimeHandler.runtimeCallFunctionOn(defineNativeSetterProperty, null, {
objectId: this.get(),
});
}
}

module.exports = Element;
15 changes: 1 addition & 14 deletions lib/elements/elementHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,4 @@ const highlightElement = async (element) => {
}
};

const setNativeValue = function (element, propName, value) {
const { set: valueSetter } = Object.getOwnPropertyDescriptor(element, propName) || {};
const prototype = Object.getPrototypeOf(element);
const { set: prototypeValueSetter } = Object.getOwnPropertyDescriptor(prototype, propName) || {};
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else if (valueSetter) {
valueSetter.call(element, value);
} else {
throw new Error('The given element does not have a value setter');
}
};

module.exports = { highlightElement, setNativeValue };
module.exports = { highlightElement };
3 changes: 2 additions & 1 deletion lib/elements/radioButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class RadioButton extends Element {
return result.value;
}
async select() {
await this.registerNativeValueSetter();
const navigationOptions = setNavigationOptions({});
await doActionAwaitingNavigation(navigationOptions, async () => {
const objectId = this.get();
Expand Down Expand Up @@ -49,7 +50,7 @@ class RadioButton extends Element {
}

function setChecked(value) {
this.checked = value;
this.setNativeValue(this, 'checked', value);
['change', 'input', 'click'].forEach((ev) => {
let event = new Event(ev, { bubbles: true });
try {
Expand Down
6 changes: 3 additions & 3 deletions lib/elements/range.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { setNavigationOptions } = require('../config');
const { descEvent } = require('../eventBus');
const Element = require('./element');
const { defaultConfig } = require('../config');
const { highlightElement, setNativeValue } = require('./elementHelper');
const { highlightElement } = require('./elementHelper');
const { doActionAwaitingNavigation } = require('../doActionAwaitingNavigation');

class Range extends Element {
Expand Down Expand Up @@ -31,15 +31,15 @@ class Range extends Element {
}

function setRange(value) {
setNativeValue(this, 'value', value);
this.setNativeValue(this, 'value', value);

let rangeValues = {};
rangeValues['min'] = this.min || 0;
rangeValues['max'] = this.max || 100;
rangeValues['current'] = this.value;

let selectAndDispatchEvent = function (self, value) {
setNativeValue(self, 'value', value);
self.setNativeValue(self, 'value', value);

['change', 'input'].forEach((ev) => {
let event = new Event(ev, { bubbles: true });
Expand Down
6 changes: 6 additions & 0 deletions test/functional-tests/specs/ElementsAPI.spec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Radio Button without for
tags: knownIssue
* Radio Button "Y"

Range
------
* Ensure Range "Age" exists
* Select the value of Range "Age" to "51"


File field
----------

Expand Down
6 changes: 6 additions & 0 deletions test/functional-tests/specs/ElementsAPIIFrame.spec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Radio Button without for
tags: knownIssue
* Radio Button "Y"


Range
------
* Ensure Range "Age" exists
* Select the value of Range "Age" to "51"

Attach file
-----------

Expand Down
6 changes: 3 additions & 3 deletions test/functional-tests/specs/browserActions.spec
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


## Switch To

* Navigate to "http://localhost:3001/"
* Click "Multiple Windows"
* Assert page navigated to "/windows"
* Click "Click Here"
* Switch to tab with url "http://localhost:3001/windows/new"
* Assert page navigated to "/windows/new"
* Switch to tab with url "http://localhost:3001/windows"
* Assert page navigated to "/windows"
Expand All @@ -17,7 +17,6 @@
|text|Opening a new window|

## Switch to with name

* Open Tab with name "newTab"
* Open Tab "http://localhost:3001/"
* Switch to tab with name "newTab"
Expand All @@ -36,6 +35,7 @@
* Open Tab "http://localhost:3001/dropdown"
* Open Tab "http://localhost:3001/"
* Close Tab
* Switch to tab with url "http://localhost:3001/dropdown"
* Close Tab
* Assert title to be "Document"

Expand Down Expand Up @@ -101,7 +101,7 @@
* Set timezone "America/Jamaica"
* Assert page has set timezome

## Click & Release To Element
## Click & Release To Element
* Navigate to relative path "./specs/data/MouseMoveTest.html"
* Press & Release To Element with element1 and "0","100" co-ordinates
* Assert text "button2" exists on the page.
Expand Down
4 changes: 4 additions & 0 deletions test/functional-tests/specs/data/HTMLElements.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
<input type="radio" name="gender" value="female" id="female"><label for="female">Female</label>
<input type="radio" name="gender" value="other" id="other"><label for="other">Other</label>

<div>
<label for="age-range">Age</label>
<input id="age-range" type="range" name="Age" value="11">
</div>
<div id="myDIV">
<div id="content">
Some text inside a div element.<br><br>
Expand Down
14 changes: 14 additions & 0 deletions test/functional-tests/tests/htmlElementAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
dropDown,
checkBox,
radioButton,
range,
click,
write,
attach,
Expand Down Expand Up @@ -86,6 +87,19 @@ export default class HtmlElementAPI {
assert.ok(await button.isSelected());
}

@Step('Ensure Range <rangeName> exists')
public async rangeExists(rangeName: string) {
const rangeInput = range(rangeName);
assert.ok(await rangeInput.exists());
}

@Step('Select the value of Range <rangeName> to <rangeValue>')
public async rangeValue(rangeName: string, rangeValue: string) {
const rangeInput = range(rangeName);
await rangeInput.select(rangeValue);
assert.equal(await rangeInput.value(), rangeValue);
}

@Step('Attach file <fileName> to file field <FileFieldName>')
public async attachFile(fileName: string, FileFieldName: SearchElement) {
const field = fileField(FileFieldName);
Expand Down
15 changes: 15 additions & 0 deletions test/unit-tests/elements/checkBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('CheckBox', () => {
},
};
},
async runtimeEvaluate(exp, executionContextId, opt = {}) {
return true;
},
};

beforeEach(() => {
Expand Down Expand Up @@ -48,10 +51,21 @@ describe('CheckBox', () => {
},
},
};

Object.defineProperty(Object.prototype, 'checked', {
configurable: true,
get: function () {
return this.checked;
},
set: function (val) {
this.checked = val;
},
});
});
afterEach(() => {
CheckBox = rewire('../../../lib/elements/checkBox');
dispatchedEvent = null;
delete Object.prototype.checked;
});

it('should be element', () => {
Expand All @@ -76,6 +90,7 @@ describe('CheckBox', () => {
describe('check', () => {
it('should check an uncheckedd checkbox', async () => {
let objectId = 28;

const checkBox = new CheckBox(objectId, 'description', runtimeHandler);
expect(nodes[objectId].checked).to.be.false;

Expand Down
13 changes: 13 additions & 0 deletions test/unit-tests/elements/radioButton.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('RadioButton', () => {
},
};
},
async runtimeEvaluate(exp, executionContextId, opt = {}) {
return true;
},
};
beforeEach(() => {
RadioButton = rewire('../../../lib/elements/radioButton');
Expand Down Expand Up @@ -47,10 +50,20 @@ describe('RadioButton', () => {
},
},
};
Object.defineProperty(Object.prototype, 'checked', {
configurable: true,
get: function () {
return this.checked;
},
set: function (val) {
this.checked = val;
},
});
});
afterEach(() => {
RadioButton = rewire('../../../lib/elements/radioButton');
dispatchedEvent = null;
delete Object.prototype.checked;
});

it('should be element', () => {
Expand Down

0 comments on commit e07b474

Please sign in to comment.