diff --git a/docs/helpers/Appium.md b/docs/helpers/Appium.md index a6fd268a4..134cd93f9 100644 --- a/docs/helpers/Appium.md +++ b/docs/helpers/Appium.md @@ -841,6 +841,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters diff --git a/docs/helpers/Nightmare.md b/docs/helpers/Nightmare.md index feacd009e..2c0cd7197 100644 --- a/docs/helpers/Nightmare.md +++ b/docs/helpers/Nightmare.md @@ -91,6 +91,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters diff --git a/docs/helpers/Playwright.md b/docs/helpers/Playwright.md index 45aeec26b..20cc6a474 100644 --- a/docs/helpers/Playwright.md +++ b/docs/helpers/Playwright.md @@ -74,6 +74,7 @@ Type: [object][5] - `ignoreLog` **[Array][15]<[string][8]>?** An array with console message types that are not logged to debug log. Default value is `['warning', 'log']`. E.g. you can set `[]` to log all messages. See all possible [values][38]. - `ignoreHTTPSErrors` **[boolean][26]?** Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false` - `bypassCSP` **[boolean][26]?** bypass Content Security Policy or CSP +- `highlightElement` **[boolean][26]?** highlight the interacting elements @@ -378,6 +379,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters @@ -1826,6 +1829,9 @@ I.type('4141555311111111', 100); // passing in an array I.type(['T', 'E', 'X', 'T']); + +// passing a secret +I.type(secret('123456')); ``` #### Parameters diff --git a/docs/helpers/Puppeteer.md b/docs/helpers/Puppeteer.md index 37628f28d..74290cd87 100644 --- a/docs/helpers/Puppeteer.md +++ b/docs/helpers/Puppeteer.md @@ -56,6 +56,7 @@ Type: [object][4] - `manualStart` **[boolean][17]?** do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`. - `browser` **[string][6]?** can be changed to `firefox` when using [puppeteer-firefox][2]. - `chrome` **[object][4]?** pass additional [Puppeteer run options][22]. +- `highlightElement` **[boolean][17]?** highlight the interacting elements @@ -296,6 +297,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters @@ -1786,6 +1789,9 @@ I.type('4141555311111111', 100); // passing in an array I.type(['T', 'E', 'X', 'T']); + +// passing a secret +I.type(secret('123456')); ``` #### Parameters diff --git a/docs/helpers/TestCafe.md b/docs/helpers/TestCafe.md index 34075cf45..1c90ea632 100644 --- a/docs/helpers/TestCafe.md +++ b/docs/helpers/TestCafe.md @@ -116,6 +116,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters diff --git a/docs/helpers/WebDriver.md b/docs/helpers/WebDriver.md index 9db5a18ae..83be16919 100644 --- a/docs/helpers/WebDriver.md +++ b/docs/helpers/WebDriver.md @@ -27,7 +27,7 @@ Type: [object][16] ### Properties - `url` **[string][17]** base url of website to be tested. -- `browser` **[string][17]** browser in which to perform testing. +- `browser` **[string][17]** Browser in which to perform testing. - `basicAuth` **[string][17]?** (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'} - `host` **[string][17]?** WebDriver host to connect. - `port` **[number][20]?** WebDriver port to connect. @@ -45,6 +45,7 @@ Type: [object][16] - `desiredCapabilities` **[object][16]?** Selenium's [desired capabilities][6]. - `manualStart` **[boolean][29]?** do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`. - `timeouts` **[object][16]?** [WebDriver timeouts][34] defined as hash. +- `highlightElement` **[boolean][29]?** highlight the interacting elements @@ -382,7 +383,7 @@ this.helpers['WebDriver']._locate({name: 'password'}).then //... ### _locateCheckable -Find a checkbox by providing human readable text: +Find a checkbox by providing human-readable text: ```js this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ... @@ -394,7 +395,7 @@ this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions'). ### _locateClickable -Find a clickable element by providing human readable text: +Find a clickable element by providing human-readable text: ```js const els = await this.helpers.WebDriver._locateClickable('Next page'); @@ -408,7 +409,7 @@ const els = await this.helpers.WebDriver._locateClickable('Next page', '.pages') ### _locateFields -Find field elements by providing human readable text: +Find field elements by providing human-readable text: ```js this.helpers['WebDriver']._locateFields('Your email').then // ... @@ -464,6 +465,8 @@ Field is located by name, label, CSS or XPath ```js I.appendField('#myTextField', 'appended'); +// typing secret +I.appendField('password', secret('123456')); ``` #### Parameters @@ -1985,6 +1988,9 @@ I.type('4141555311111111', 100); // passing in an array I.type(['T', 'E', 'X', 'T']); + +// passing a secret +I.type(secret('123456')); ``` #### Parameters diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 854742388..7f8061237 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -43,6 +43,7 @@ const { setRestartStrategy, restartsSession, restartsContext, restartsBrowser, } = require('./extras/PlaywrightRestartOpts'); const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine'); +const { highlightElement, unhighlightElement } = require('./scripts/highlightElement'); const pathSeparator = path.sep; @@ -89,6 +90,7 @@ const pathSeparator = path.sep; * @prop {string[]} [ignoreLog] - An array with console message types that are not logged to debug log. Default value is `['warning', 'log']`. E.g. you can set `[]` to log all messages. See all possible [values](https://playwright.dev/docs/api/class-consolemessage#console-message-type). * @prop {boolean} [ignoreHTTPSErrors] - Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false` * @prop {boolean} [bypassCSP] - bypass Content Security Policy or CSP + * @prop {boolean} [highlightElement] - highlight the interacting elements */ const config = {}; @@ -1464,7 +1466,16 @@ class Playwright extends Helper { } else if (editable) { await this._evaluateHandeInContext(el => el.innerHTML = '', el); } + + if (this.options.highlightElement) { + highlightElement(el, this.page); + } + await el.type(value.toString(), { delay: this.options.pressKeyDelay }); + + if (this.options.highlightElement) { + unhighlightElement(el, this.page); + } return this._waitForAction(); } @@ -2589,6 +2600,11 @@ async function proceedClick(locator, context = null, options = {}) { } else { assertElementExists(els, locator, 'Clickable element'); } + + const element = els[0]; + if (this.options.highlightElement) { + highlightElement(element, this.page); + } /* using the force true options itself but instead dispatching a click */ @@ -2603,6 +2619,10 @@ async function proceedClick(locator, context = null, options = {}) { promises.push(this.waitForNavigation()); } promises.push(this._waitForAction()); + + if (this.options.highlightElement) { + unhighlightElement(element, this.page); + } return Promise.all(promises); } diff --git a/lib/helper/Puppeteer.js b/lib/helper/Puppeteer.js index be58d7927..f5c0a1581 100644 --- a/lib/helper/Puppeteer.js +++ b/lib/helper/Puppeteer.js @@ -33,6 +33,7 @@ const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnection const Popup = require('./extras/Popup'); const Console = require('./extras/Console'); const findReact = require('./extras/React'); +const { highlightElement, unhighlightElement } = require('./scripts/highlightElement'); let puppeteer; let perfTiming; @@ -65,6 +66,7 @@ const consoleLogStore = new Console(); * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`. * @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox). * @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions). + * @prop {boolean} [highlightElement] - highlight the interacting elements */ const config = {}; @@ -1287,7 +1289,14 @@ class Puppeteer extends Helper { } else if (editable) { await this._evaluateHandeInContext(el => el.innerHTML = '', el); } + + if (this.options.highlightElement) { + highlightElement(el, this.page); + } await el.type(value.toString(), { delay: this.options.pressKeyDelay }); + if (this.options.highlightElement) { + unhighlightElement(el, this.page); + } return this._waitForAction(); } @@ -2314,12 +2323,22 @@ async function proceedClick(locator, context = null, options = {}) { } else { assertElementExists(els, locator, 'Clickable element'); } + + if (this.options.highlightElement) { + highlightElement(els[0], this.page); + } + await els[0].click(options); const promises = []; if (options.waitForNavigation) { promises.push(this.waitForNavigation()); } promises.push(this._waitForAction()); + + if (this.options.highlightElement) { + unhighlightElement(els[0], this.page); + } + return Promise.all(promises); } diff --git a/lib/helper/WebDriver.js b/lib/helper/WebDriver.js index 31b6ca7d7..10ea7ffba 100644 --- a/lib/helper/WebDriver.js +++ b/lib/helper/WebDriver.js @@ -5,6 +5,7 @@ const path = require('path'); const fs = require('fs'); const Helper = require('@codeceptjs/helper'); +const crypto = require('crypto'); const stringIncludes = require('../assert/include').includes; const { urlEquals, equals } = require('../assert/equal'); const { debug } = require('../output'); @@ -27,6 +28,7 @@ const { const ElementNotFound = require('./errors/ElementNotFound'); const ConnectionRefused = require('./errors/ConnectionRefused'); const Locator = require('../locator'); +const { highlightElement, unhighlightElement } = require('./scripts/highlightElement'); const SHADOW = 'shadow'; const webRoot = 'body'; @@ -39,7 +41,7 @@ const webRoot = 'body'; * @typedef WebDriverConfig * @type {object} * @prop {string} url - base url of website to be tested. - * @prop {string} browser browser in which to perform testing. + * @prop {string} browser - Browser in which to perform testing. * @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'} * @prop {string} [host=localhost] - WebDriver host to connect. * @prop {number} [port=4444] - WebDriver port to connect. @@ -57,6 +59,7 @@ const webRoot = 'body'; * @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities). * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`. * @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash. + * @prop {boolean} [highlightElement] - highlight the interacting elements */ const config = {}; @@ -822,7 +825,7 @@ class WebDriver extends Helper { } /** - * Find a checkbox by providing human readable text: + * Find a checkbox by providing human-readable text: * * ```js * this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ... @@ -835,7 +838,7 @@ class WebDriver extends Helper { } /** - * Find a clickable element by providing human readable text: + * Find a clickable element by providing human-readable text: * * ```js * const els = await this.helpers.WebDriver._locateClickable('Next page'); @@ -850,7 +853,7 @@ class WebDriver extends Helper { } /** - * Find field elements by providing human readable text: + * Find field elements by providing human-readable text: * * ```js * this.helpers['WebDriver']._locateFields('Your email').then // ... @@ -914,6 +917,10 @@ class WebDriver extends Helper { assertElementExists(res, locator, 'Clickable element'); } const elem = usingFirstElement(res); + + if (this.options.highlightElement) { + highlightElement(elem, this.browser); + } return this.browser[clickMethod](getElementId(elem)); } @@ -1024,6 +1031,13 @@ class WebDriver extends Helper { const res = await findFields.call(this, field); assertElementExists(res, field, 'Field'); const elem = usingFirstElement(res); + if (this.options.highlightElement) { + highlightElement(elem, this.browser); + } + + if (this.options.highlightElement) { + unhighlightElement(elem, this.browser); + } return elem.setValue(value.toString()); } @@ -1035,6 +1049,12 @@ class WebDriver extends Helper { const res = await findFields.call(this, field); assertElementExists(res, field, 'Field'); const elem = usingFirstElement(res); + if (this.options.highlightElement) { + highlightElement(elem, this.browser); + } + if (this.options.highlightElement) { + unhighlightElement(elem, this.browser); + } return elem.addValue(value.toString()); } @@ -1046,6 +1066,12 @@ class WebDriver extends Helper { const res = await findFields.call(this, field); assertElementExists(res, field, 'Field'); const elem = usingFirstElement(res); + if (this.options.highlightElement) { + highlightElement(elem, this.browser); + } + if (this.options.highlightElement) { + unhighlightElement(elem, this.browser); + } return elem.clearValue(getElementId(elem)); } diff --git a/lib/helper/scripts/highlightElement.js b/lib/helper/scripts/highlightElement.js new file mode 100644 index 000000000..c9aa4346c --- /dev/null +++ b/lib/helper/scripts/highlightElement.js @@ -0,0 +1,15 @@ +module.exports.highlightElement = (element, context) => { + try { + context.evaluate(el => el.style.border = '2px solid red', element); + } catch (e) { + context.execute(el => el.style.border = '2px solid red', element); + } +}; + +module.exports.unhighlightElement = (element, context) => { + try { + context.evaluate(el => el.style.border = '', element); + } catch (e) { + context.execute(el => el.style.border = '', element); + } +}; diff --git a/typings/tslint.json b/typings/tslint.json index 1180f682c..9cf38c96e 100644 --- a/typings/tslint.json +++ b/typings/tslint.json @@ -14,7 +14,8 @@ "array-type": false, "trim-file": false, "no-consecutive-blank-lines": false, - "no-redundant-jsdoc": false + "no-redundant-jsdoc": false, + "adjacent-overload-signatures": false }, "linterOptions": { "exclude": [