From 23df1dbf873568e07bc5f0dacbcdc1f8f06c8e03 Mon Sep 17 00:00:00 2001 From: Puja Jagani Date: Tue, 17 Sep 2024 23:35:59 +0530 Subject: [PATCH] Add selector and error line for "staleElementReferenceError" (#4214) Fixes #4184 --- examples/tests/staleElementReferenceError.js | 16 ++++++++++++ lib/element/command.js | 2 +- lib/transport/selenium-webdriver/actions.js | 25 ++++++++++++++++--- .../api/commands/element/testClickAndHold.js | 23 +++++++++-------- .../api/commands/element/testDoubleClick.js | 6 ++--- .../api/commands/element/testDragAndDrop.js | 6 +++-- .../api/commands/element/testMoveToElement.js | 16 +++++++++--- .../api/commands/element/testRightClick.js | 25 ++++++++++--------- 8 files changed, 83 insertions(+), 36 deletions(-) create mode 100644 examples/tests/staleElementReferenceError.js diff --git a/examples/tests/staleElementReferenceError.js b/examples/tests/staleElementReferenceError.js new file mode 100644 index 0000000000..b5b2828f6d --- /dev/null +++ b/examples/tests/staleElementReferenceError.js @@ -0,0 +1,16 @@ +describe('stale element reference error', function() { + + it('should add selector and error line for stale element reference error', function() { + const deletedElement = element(by.id('deleted')); + const deleteElement = element(by.id('delete')); + browser + .navigateTo('https://www.selenium.dev/selenium/web/javascriptPage.html') + .click(deletedElement); + + browser.click(deleteElement); + + browser.click(deletedElement); + }); + + after(browser => browser.end()); +}); diff --git a/lib/element/command.js b/lib/element/command.js index 5a71187be5..19869ff765 100644 --- a/lib/element/command.js +++ b/lib/element/command.js @@ -232,7 +232,7 @@ class ElementCommand extends EventEmitter { elementId = selector; } - return this.transport.executeProtocolAction({actionName, args: [elementId, ...args], sessionId}); + return this.transport.executeProtocolAction({actionName, args: [elementId, ...args, {selector: this.selector, stackTrace: this.stackTrace}], sessionId}); } async complete(err, response) { diff --git a/lib/transport/selenium-webdriver/actions.js b/lib/transport/selenium-webdriver/actions.js index e24a26c9a3..b50c8b75e0 100644 --- a/lib/transport/selenium-webdriver/actions.js +++ b/lib/transport/selenium-webdriver/actions.js @@ -2,6 +2,7 @@ const {error} = require('selenium-webdriver'); const {Logger, isObject, isFunction, isString} = require('../../utils'); const MethodMappings = require('./method-mappings.js'); +const {filterStackTrace} = require('../../utils/stackTrace'); class TransportActions { get transport() { @@ -70,7 +71,8 @@ class TransportActions { return this.makeResult(result); } catch (err) { - const error = this.handleError(err, name); + + const error = this.handleError(err, name, args); return { error, @@ -96,7 +98,7 @@ class TransportActions { return result; } - handleError(err, commandName) { + handleError(err, commandName, args = undefined) { let errorMsg = 'Unknown error'; try { this.transport.handleErrorResponse(err); @@ -123,7 +125,24 @@ class TransportActions { const {shouldRegisterError} = this.transport; const {lastError, retriesCount} = this; if (shouldRegisterError(err) && (retriesCount < 1 || lastError && err.name !== lastError.name)) { - Logger.error(`Error while running .${commandName}() protocol action: ${errorMsg}\n`); + err.message = `Error while running .${commandName}() protocol action: ${errorMsg}\n`; + + if (args && args.length > 0) { + const lastArg = args[args.length - 1]; + if (lastArg && typeof lastArg === 'object' && !Array.isArray(lastArg)) { + const {selector = null, stackTrace = null} = lastArg; + + if (selector) { + err.message += `Selector: ${selector}\n`; + } + + if (stackTrace) { + err.stack = filterStackTrace(stackTrace); + } + } + } + + Logger.error(err); this.lastError = err; } if (this.lastError && err.name === this.lastError.name) { diff --git a/test/src/api/commands/element/testClickAndHold.js b/test/src/api/commands/element/testClickAndHold.js index 7c3619f3a2..7fc45acf71 100644 --- a/test/src/api/commands/element/testClickAndHold.js +++ b/test/src/api/commands/element/testClickAndHold.js @@ -19,12 +19,12 @@ describe('.clickAndHold()', function() { return Promise.resolve({ status: 0, - value:null + value: null }); - } + }; this.client.api.clickAndHold('#weblogin', function callback(result) { - assert.deepStrictEqual(clickAndHoldArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(clickAndHoldArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, 0); assert.strictEqual(this, api); }); @@ -39,16 +39,16 @@ describe('.clickAndHold()', function() { clickAndHoldArgs = args; return Promise.resolve({ - status:0, + status: 0, value: null - }) - } + }); + }; this.client.api.useXpath() .clickAndHold('//weblogin', function callback(result) { - assert.deepStrictEqual(clickAndHoldArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(clickAndHoldArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, 0); - }) + }); this.client.start(done); @@ -60,21 +60,22 @@ describe('.clickAndHold()', function() { let clickAndHoldArgs; this.client.transport.Actions.session.pressAndHold = function(args) { clickAndHoldArgs = args; + return Promise.resolve({ status: -1, value: null, error: new Error('Element could not be scrolled into view') }); - } + }; this.client.api.clickAndHold({ retryInterval: 50, timeout: 100, selector: '#weblogin' }, function(result) { - assert.deepStrictEqual(clickAndHoldArgs.args,['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(clickAndHoldArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, -1); - assert.strictEqual(result.value.error,'An error occurred while running .clickAndHold() command on <#weblogin>: Element could not be scrolled into view') + assert.strictEqual(result.value.error, 'An error occurred while running .clickAndHold() command on <#weblogin>: Element could not be scrolled into view'); }); this.client.start(done); diff --git a/test/src/api/commands/element/testDoubleClick.js b/test/src/api/commands/element/testDoubleClick.js index 9115b636b8..87b1388b9b 100644 --- a/test/src/api/commands/element/testDoubleClick.js +++ b/test/src/api/commands/element/testDoubleClick.js @@ -25,7 +25,7 @@ describe('.doubleClick()', function() { this.client.api.doubleClick('#weblogin', function callback(result) { assert.strictEqual(result.status, 0); - assert.deepStrictEqual(doubleClickArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']); + assert.ok(doubleClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(this, api); }); @@ -46,7 +46,7 @@ describe('.doubleClick()', function() { this.client.api.useXpath() .doubleClick('//weblogin', function callback(result) { - assert.deepStrictEqual(doubleClickArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']); + assert.ok(doubleClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, 0); }); this.client.start(done); @@ -70,7 +70,7 @@ describe('.doubleClick()', function() { timeout: 100, selector: '#weblogin' }, function(result) { - assert.deepStrictEqual(doubleClickArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']); + assert.ok(doubleClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, -1); assert.strictEqual(result.value.error, 'An error occurred while running .doubleClick() command on <#weblogin>: Element could not be scrolled into view'); }); diff --git a/test/src/api/commands/element/testDragAndDrop.js b/test/src/api/commands/element/testDragAndDrop.js index 47e840dc21..1e73817bf2 100644 --- a/test/src/api/commands/element/testDragAndDrop.js +++ b/test/src/api/commands/element/testDragAndDrop.js @@ -21,7 +21,8 @@ describe('moveToElement', function() { }; this.client.api.dragAndDrop('css selector', '#weblogin', {x: 10, y: 10}, function(result) { - assert.deepStrictEqual(dragArgs, ['0', {x: 10, y: 10}]); + assert.deepEqual(dragArgs[0], '0'); + assert.deepEqual(dragArgs[1], {x: 10, y: 10}); assert.strictEqual(result.status, 0); }); @@ -39,7 +40,8 @@ describe('moveToElement', function() { }; this.client.api.dragAndDrop('css selector', '#weblogin', '0', function(result) { - assert.deepStrictEqual(dragArgs, ['0', '0']); + assert.deepEqual(dragArgs[0], '0'); + assert.deepEqual(dragArgs[1], '0'); assert.strictEqual(result.status, 0); }); diff --git a/test/src/api/commands/element/testMoveToElement.js b/test/src/api/commands/element/testMoveToElement.js index 0ed96ad68e..fda812c892 100644 --- a/test/src/api/commands/element/testMoveToElement.js +++ b/test/src/api/commands/element/testMoveToElement.js @@ -21,16 +21,24 @@ describe('moveToElement', function() { }; this.client.api.moveToElement('css selector', '#weblogin', null, null, function(result) { - assert.deepStrictEqual(moveToArgs, ['0', null, null]); + assert.deepStrictEqual(moveToArgs[0], '0'); + assert.deepStrictEqual(moveToArgs[1], null); + assert.deepStrictEqual(moveToArgs[2], null); assert.strictEqual(result.status, 0); }).moveToElement('#weblogin', null, null, function(result) { - assert.deepStrictEqual(moveToArgs, ['0', null, null]); + assert.deepStrictEqual(moveToArgs[0], '0'); + assert.deepStrictEqual(moveToArgs[1], null); + assert.deepStrictEqual(moveToArgs[2], null); assert.strictEqual(result.status, 0); }).moveTo('0', null, null, function(result) { - assert.deepStrictEqual(moveToArgs, ['0', 0, 0]); + assert.deepStrictEqual(moveToArgs[0], '0'); + assert.deepStrictEqual(moveToArgs[1], 0); + assert.deepStrictEqual(moveToArgs[2], 0); assert.strictEqual(result.status, 0); }).moveToElement('#weblogin', 1, 1, function(result) { - assert.deepStrictEqual(moveToArgs, ['0', 1, 1]); + assert.deepStrictEqual(moveToArgs[0], '0'); + assert.deepStrictEqual(moveToArgs[1], 1); + assert.deepStrictEqual(moveToArgs[2], 1); assert.strictEqual(result.status, 0); }); diff --git a/test/src/api/commands/element/testRightClick.js b/test/src/api/commands/element/testRightClick.js index e699c16ced..56fad84f66 100644 --- a/test/src/api/commands/element/testRightClick.js +++ b/test/src/api/commands/element/testRightClick.js @@ -19,13 +19,13 @@ describe('.rightClick()', function() { rightClickArgs = args; return Promise.resolve({ - status:0, + status: 0, value: null - }) - } + }); + }; this.client.api.rightClick('#weblogin', function callback(result) { - assert.deepStrictEqual(rightClickArgs.args,['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(rightClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, 0); assert.strictEqual(this, api); }); @@ -39,16 +39,16 @@ describe('.rightClick()', function() { rightClickArgs = args; return Promise.resolve({ - status:0, + status: 0, value: null - }) - } + }); + }; this.client.api.useXpath() .rightClick('//weblogin', function callback(result) { - assert.deepStrictEqual(rightClickArgs.args, ['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(rightClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, 0); - }) + }); this.client.start(done); }); @@ -57,21 +57,22 @@ describe('.rightClick()', function() { let rightClickArgs; this.client.transport.Actions.session.contextClick = function(args) { rightClickArgs = args; + return Promise.resolve({ status: -1, value: null, error: new Error('Element could not be scrolled into view') }); - } + }; this.client.api.rightClick({ retryInterval: 50, timeout: 100, selector: '#weblogin' }, function(result) { - assert.deepStrictEqual(rightClickArgs.args,['5cc459b8-36a8-3042-8b4a-258883ea642b']) + assert.ok(rightClickArgs.args.includes('5cc459b8-36a8-3042-8b4a-258883ea642b')); assert.strictEqual(result.status, -1); - assert.strictEqual(result.value.error,'An error occurred while running .rightClick() command on <#weblogin>: Element could not be scrolled into view') + assert.strictEqual(result.value.error, 'An error occurred while running .rightClick() command on <#weblogin>: Element could not be scrolled into view'); }); this.client.start(done);