From d2ba952cc2174b96de3237cb9e7d48c269b53a24 Mon Sep 17 00:00:00 2001 From: kobenguyent Date: Thu, 2 May 2024 11:25:36 +0200 Subject: [PATCH] synchronized with docs --- docs/build/Playwright.js | 24 +- docs/build/REST.js | 29 ++- docs/build/WebDriver.js | 15 +- docs/changelog.md | 96 +++++++ docs/community-helpers.md | 1 + docs/helpers/Detox.md | 246 ++++++++++++------ docs/helpers/Playwright.md | 19 +- docs/helpers/REST.md | 20 ++ docs/locators.md | 9 + docs/wiki/Community-Helpers-&-Plugins.md | 1 + ...verting-Playwright-to-Istanbul-Coverage.md | 38 ++- 11 files changed, 396 insertions(+), 102 deletions(-) diff --git a/docs/build/Playwright.js b/docs/build/Playwright.js index 23a9f1a..89860a4 100644 --- a/docs/build/Playwright.js +++ b/docs/build/Playwright.js @@ -417,7 +417,14 @@ class Playwright extends Helper { } if (this.options.video) { - this.options.recordVideo = { size: parseWindowSize(this.options.windowSize) }; + // set the video resolution with window size + let size = parseWindowSize(this.options.windowSize); + + // if the video resolution is passed, set the record resoultion with that resolution + if (this.options.recordVideo && this.options.recordVideo.size) { + size = parseWindowSize(this.options.recordVideo.size); + } + this.options.recordVideo = { size }; } if (this.options.recordVideo && !this.options.recordVideo.dir) { this.options.recordVideo.dir = `${global.output_dir}/videos/`; @@ -4035,12 +4042,15 @@ class Playwright extends Helper { } /** - * Resets all recorded network requests. + * Starts recording the network traffics. + * This also resets recorded network requests. * * ```js - * I.flushNetworkTraffics(); + * I.startRecordingTraffic(); * ``` * + * @returns {void} automatically synchronized promise through #recorder + * * */ startRecordingTraffic() { @@ -4174,8 +4184,6 @@ class Playwright extends Helper { /** * Returns full URL of request matching parameter "urlMatch". * - * @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression. - * * Examples: * * ```js @@ -4183,6 +4191,7 @@ class Playwright extends Helper { * I.grabTrafficUrl(/session.*start/); * ``` * + * @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression. * @return {Promise<*>} */ grabTrafficUrl(urlMatch) { @@ -4796,6 +4805,11 @@ async function targetCreatedHandler(page) { function parseWindowSize(windowSize) { if (!windowSize) return { width: 800, height: 600 }; + + if (windowSize.width && windowSize.height) { + return { width: parseInt(windowSize.width, 10), height: parseInt(windowSize.height, 10) }; + } + const dimensions = windowSize.split('x'); if (dimensions.length < 2 || windowSize === 'maximize') { console.log('Invalid window size, setting window to default values'); diff --git a/docs/build/REST.js b/docs/build/REST.js index 88c214f..0af3334 100644 --- a/docs/build/REST.js +++ b/docs/build/REST.js @@ -1,5 +1,6 @@ const axios = require('axios').default; const Helper = require('@codeceptjs/helper'); +const { Agent } = require('https'); const Secret = require('../secret'); const { beautify } = require('../utils'); @@ -13,6 +14,7 @@ const { beautify } = require('../utils'); * @prop {boolean} [prettyPrintJson=false] - pretty print json for response/request on console logs * @prop {number} [timeout=1000] - timeout for requests in milliseconds. 10000ms by default * @prop {object} [defaultHeaders] - a list of default headers + * @prop {object} [httpAgent] - create an agent with SSL certificate * @prop {function} [onRequest] - a async function which can update request object. * @prop {function} [onResponse] - a async function which can update response object. * @prop {number} [maxUploadFileSize] - set the max content file size in MB when performing api calls. @@ -40,6 +42,24 @@ const config = {}; * } *} * ``` + * With httpAgent + * + * ```js + * { + * helpers: { + * REST: { + * endpoint: 'http://site.com/api', + * prettyPrintJson: true, + * httpAgent: { + * key: fs.readFileSync(__dirname + '/path/to/keyfile.key'), + * cert: fs.readFileSync(__dirname + '/path/to/certfile.cert'), + * rejectUnauthorized: false, + * keepAlive: true + * } + * } + * } + * } + * ``` * * ## Access From Helpers * @@ -76,7 +96,14 @@ class REST extends Helper { this._setConfig(config); this.headers = { ...this.options.defaultHeaders }; - this.axios = axios.create(); + + // Create an agent with SSL certificate + if (this.options.httpAgent) { + if (!this.options.httpAgent.key || !this.options.httpAgent.cert) throw Error('Please recheck your httpAgent config!'); + this.httpsAgent = new Agent(this.options.httpAgent); + } + + this.axios = this.httpsAgent ? axios.create({ httpsAgent: this.httpsAgent }) : axios.create(); // @ts-ignore this.axios.defaults.headers = this.options.defaultHeaders; } diff --git a/docs/build/WebDriver.js b/docs/build/WebDriver.js index 574d5fd..b0f3526 100644 --- a/docs/build/WebDriver.js +++ b/docs/build/WebDriver.js @@ -2466,14 +2466,21 @@ class WebDriver extends Helper { * */ async saveScreenshot(fileName, fullPage = false) { - const outputFile = screenshotOutputFolder(fileName); + let outputFile = screenshotOutputFolder(fileName); if (this.activeSessionName) { const browser = this.sessionWindows[this.activeSessionName]; - if (browser) { - this.debug(`Screenshot of ${this.activeSessionName} session has been saved to ${outputFile}`); - return browser.saveScreenshot(outputFile); + for (const sessionName in this.sessionWindows) { + const activeSessionPage = this.sessionWindows[sessionName]; + outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`); + + this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`); + + if (browser) { + this.debug(`Screenshot of ${sessionName} session has been saved to ${outputFile}`); + return browser.saveScreenshot(outputFile); + } } } diff --git a/docs/changelog.md b/docs/changelog.md index a1a356f..76ab504 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,102 @@ layout: Section # Releases +## 3.6.2 + +❤ī¸ Thanks all to those who contributed to make this release! ❤ī¸ + +🛩ī¸ *Features* +* feat(REST): support httpAgent conf ([#4328](https://github.com/codeceptjs/CodeceptJS/issues/4328)) - by **[KobeNguyent](https://github.com/KobeNguyent)** + +Support the httpAgent conf to create the TSL connection via REST helper + +``` +{ + helpers: { + REST: { + endpoint: 'http://site.com/api', + prettyPrintJson: true, + httpAgent: { + key: fs.readFileSync(__dirname + '/path/to/keyfile.key'), + cert: fs.readFileSync(__dirname + '/path/to/certfile.cert'), + rejectUnauthorized: false, + keepAlive: true + } + } + } +} +``` + +* feat(wd): screenshots for sessions ([#4322](https://github.com/codeceptjs/CodeceptJS/issues/4322)) - by **[KobeNguyent](https://github.com/KobeNguyent)** + +Currently only screenshot of the active session is saved, this PR aims to save the screenshot of every session for easy debugging + +``` +Scenario('should save screenshot for sessions **[WebDriverIO](https://github.com/WebDriverIO)** **[Puppeteer](https://github.com/Puppeteer)** **[Playwright](https://github.com/Playwright)**', async ({ I }) => { + await I.amOnPage('/form/bug1467'); + await I.saveScreenshot('original.png'); + await I.amOnPage('/'); + await I.saveScreenshot('main_session.png'); + session('john', async () => { + await I.amOnPage('/form/bug1467'); + event.dispatcher.emit(event.test.failed, this); + }); + + const fileName = clearString('should save screenshot for active session **[WebDriverIO](https://github.com/WebDriverIO)** **[Puppeteer](https://github.com/Puppeteer)** **[Playwright](https://github.com/Playwright)**'); + const [original, failed] = await I.getSHA256Digests([ + `${output_dir}/original.png`, + `${output_dir}/john_${fileName}.failed.png`, + ]); + + // Assert that screenshots of same page in same session are equal + await I.expectEqual(original, failed); + + // Assert that screenshots of sessions are created + const [main_original, session_failed] = await I.getSHA256Digests([ + `${output_dir}/main_session.png`, + `${output_dir}/john_${fileName}.failed.png`, + ]); + await I.expectNotEqual(main_original, session_failed); +}); +``` +![Screenshot 2024-04-29 at 11 07 47](https://github.com/codeceptjs/CodeceptJS/assets/7845001/5dddf85a-ed77-474b-adfd-2f208d3c16a8) + + +* feat: locate element with withClassAttr ([#4321](https://github.com/codeceptjs/CodeceptJS/issues/4321)) - by **[KobeNguyent](https://github.com/KobeNguyent)** + +Find an element with class attribute + +```js +// find div with class contains 'form' +locate('div').withClassAttr('text'); +``` + +* fix(playwright): set the record video resolution ([#4311](https://github.com/codeceptjs/CodeceptJS/issues/4311)) - by **[KobeNguyent](https://github.com/KobeNguyent)** +You could now set the recording video resolution +``` + url: siteUrl, + windowSize: '300x500', + show: false, + restart: true, + browser: 'chromium', + trace: true, + video: true, + recordVideo: { + size: { + width: 400, + height: 600, + }, + }, +``` + +🐛 *Bug Fixes* +* fix: several issues of stepByStep report ([#4331](https://github.com/codeceptjs/CodeceptJS/issues/4331)) - by **[KobeNguyent](https://github.com/KobeNguyent)** + +📖 *Documentation* +* fix: wrong format docs ([#4330](https://github.com/codeceptjs/CodeceptJS/issues/4330)) - by **[KobeNguyent](https://github.com/KobeNguyent)** +* fix(docs): wrong method is mentioned ([#4320](https://github.com/codeceptjs/CodeceptJS/issues/4320)) - by **[KobeNguyent](https://github.com/KobeNguyent)** +* fix: ChatGPT docs - by **[davert](https://github.com/davert)** + ## 3.6.1 * Fixed regression in interactive pause. diff --git a/docs/community-helpers.md b/docs/community-helpers.md index 2f135e4..7394b79 100644 --- a/docs/community-helpers.md +++ b/docs/community-helpers.md @@ -43,6 +43,7 @@ Please **add your own** by editing this page. * [codeceptjs-slack-reporter](https://www.npmjs.com/package/codeceptjs-slack-reporter) Get a Slack notification when one or more scenarios fail. * [codeceptjs-browserlogs-plugin](https://github.com/pavkam/codeceptjs-browserlogs-plugin) Record the browser logs for failed tests. * [codeceptjs-testrail](https://github.com/PeterNgTr/codeceptjs-testrail) - a plugin to integrate with [Testrail](https://www.gurock.com/testrail) +* [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - a plugin to generate coverage reports, it integrate with [monocart coverage reports](https://github.com/cenfun/monocart-coverage-reports) ## Browser request control * [codeceptjs-resources-check](https://github.com/luarmr/codeceptjs-resources-check) Load a URL with Puppeteer and listen to the requests while the page is loading. Enabling count the number or check the sizes of the requests. diff --git a/docs/helpers/Detox.md b/docs/helpers/Detox.md index e26f551..759f146 100644 --- a/docs/helpers/Detox.md +++ b/docs/helpers/Detox.md @@ -17,15 +17,15 @@ This is a wrapper on top of [Detox][1] library, aimied to unify testing experien Detox provides a grey box testing for mobile applications, playing especially good for React Native apps. Detox plays quite differently from Appium. To establish detox testing you need to build a mobile application in a special way to inject Detox code. -This why **Detox is grey box testing** solution, so you need an access to application source code, and a way to build and execute it on emulator. +This why **Detox is grey box testing** solution, so you need access to application source code, and a way to build and execute it on emulator. Comparing to Appium, Detox runs faster and more stable but requires an additional setup for build. ### Setup -1. [Install and configure Detox for iOS][2] and [Android][3] -2. [Build an application][4] using `detox build` command. -3. Install [CodeceptJS][5] and detox-helper: +1. [Install and configure Detox][2] +2. [Build an application][3] using `detox build` command. +3. Install [CodeceptJS][4] and detox-helper: npm i @codeceptjs/detox-helper --save @@ -36,15 +36,28 @@ If you completed step 1 and step 2 you should have a configuration similar this: ```js "detox": { - "configurations": { - "ios.sim.debug": { - "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app", - "build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build", - "type": "ios.simulator", - "name": "iPhone 7" - } - } - } + "configurations": { + "ios.sim.debug": { + "device": "simulator", + "app": "ios.debug" + } + }, + "apps": { + "ios.debug": { + "type": "ios.app", + "binaryPath": "../test/ios/build/Build/Products/Debug-iphonesimulator/MyTestApp.app", + "build": "xcodebuild -workspace ../test/ios/MyTestApp.xcworkspace -scheme MyTestApp -configuration Debug -sdk iphonesimulator -derivedDataPath ../test/ios/build" + } + }, + "devices": { + "simulator": { + "type": "ios.simulator", + "device": { + "type": "iPhone 15" + } + } + } + } ``` ### Configuration @@ -57,8 +70,8 @@ In `codecept.conf.js` enable Detox helper: helpers: { Detox: { require: '@codeceptjs/detox-helper', - configuration: '', - } + configuration: '', + } } ``` @@ -86,8 +99,22 @@ I.appendField('name', 'davert'); #### Parameters -- `field` **([string][6] \| [object][7])** -- `value` **[string][6]** +- `field` **([string][5] \| [object][6])** +- `value` **[string][5]** + +### checkIfElementExists + +Checks if an element exists. + +```js +I.checkIfElementExists('~edit'); // located by accessibility id +I.checkIfElementExists('~edit', '#menu'); // element inside #menu +``` + +#### Parameters + +- `locator` **([string][5] \| [object][6])** element to locate +- `context` **([string][5] \| [object][6] | null)** context element (optional, default `null`) ### clearField @@ -100,16 +127,16 @@ I.clearField('~name'); #### Parameters -- `field` **([string][6] \| [object][7])** an input element to clear +- `field` **([string][5] \| [object][6])** an input element to clear ### click -Clicks on an element. +Clicks on an element. Element can be located by its text or id or accessibility id The second parameter is a context (id | type | accessibility id) to narrow the search. -Same as [tap][8] +Same as [tap][7] ```js I.click('Login'); // locate by text @@ -121,8 +148,8 @@ I.click({ ios: 'Save', android: 'SAVE' }, '#main'); // different texts on iOS an #### Parameters -- `locator` **([string][6] \| [object][7])** -- `context` **([string][6] \| [object][7] | null)** (optional, default `null`) +- `locator` **([string][5] \| [object][6])** +- `context` **([string][5] \| [object][6] | null)** (optional, default `null`) ### clickAtPoint @@ -136,9 +163,9 @@ I.clickAtPoint('~save', 10, 10); // locate by accessibility id #### Parameters -- `locator` **([string][6] \| [object][7])** -- `x` **[number][9]** horizontal offset (optional, default `0`) -- `y` **[number][9]** vertical offset (optional, default `0`) +- `locator` **([string][5] \| [object][6])** +- `x` **[number][8]** horizontal offset (optional, default `0`) +- `y` **[number][8]** vertical offset (optional, default `0`) ### dontSee @@ -153,8 +180,8 @@ I.dontSee('Record deleted', '~message'); #### Parameters -- `text` **[string][6]** to check invisibility -- `context` **([string][6] \| [object][7] | null)** element in which to search for text (optional, default `null`) +- `text` **[string][5]** to check invisibility +- `context` **([string][5] \| [object][6] | null)** element in which to search for text (optional, default `null`) ### dontSeeElement @@ -168,8 +195,8 @@ I.dontSeeElement('~edit', '#menu'); // element inside #menu #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `context` **([string][6] \| [object][7] | null)** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `context` **([string][5] \| [object][6] | null)** context element (optional, default `null`) ### dontSeeElementExists @@ -183,8 +210,8 @@ I.dontSeeElementExist('~edit', '#menu'); // element inside #menu #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `context` **([string][6] \| [object][7])** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `context` **([string][5] \| [object][6])** context element (optional, default `null`) ### fillField @@ -199,8 +226,8 @@ I.fillField({ android: 'NAME', ios: 'name' }, 'davert'); #### Parameters -- `field` **([string][6] \| [object][7])** an input element to fill in -- `value` **[string][6]** value to fill +- `field` **([string][5] \| [object][6])** an input element to fill in +- `value` **[string][5]** value to fill ### goBack @@ -210,6 +237,14 @@ Goes back on Android I.goBack(); // on Android only ``` +### grabPlatform + +Grab the device platform + +```js +const platform = await I.grabPlatform(); +``` + ### installApp Installs a configured application. @@ -221,7 +256,7 @@ I.installApp(); ### launchApp -Launches an application. If application instance already exists, use [relaunchApp][10]. +Launches an application. If application instance already exists, use [relaunchApp][9]. ```js I.launchApp(); @@ -239,9 +274,9 @@ I.longPress('Update', 2, '#menu'); // locate by text inside #menu, hold for 2 se #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `sec` **[number][9]** number of seconds to hold tap -- `context` **([string][6] \| [object][7] | null)** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `sec` **[number][8]** number of seconds to hold tap +- `context` **([string][5] \| [object][6] | null)** context element (optional, default `null`) ### multiTap @@ -260,9 +295,9 @@ I.multiTap('Update', 2, '#menu'); // locate by id #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `num` **[number][9]** number of taps -- `context` **([string][6] \| [object][7] | null)** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `num` **[number][8]** number of taps +- `context` **([string][5] \| [object][6] | null)** context element (optional, default `null`) ### relaunchApp @@ -285,7 +320,7 @@ I.runOnAndroid(() => { #### Parameters -- `fn` **[Function][11]** a function which will be executed on android +- `fn` **[Function][10]** a function which will be executed on android ### runOnIOS @@ -300,7 +335,7 @@ I.runOnIOS(() => { #### Parameters -- `fn` **[Function][11]** a function which will be executed on iOS +- `fn` **[Function][10]** a function which will be executed on iOS ### saveScreenshot @@ -312,7 +347,7 @@ I.saveScreenshot('main-window.png'); #### Parameters -- `name` **[string][6]** +- `name` **[string][5]** ### scrollDown @@ -324,7 +359,7 @@ I.scrollDown('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** +- `locator` **([string][5] \| [object][6])** ### scrollLeft @@ -336,7 +371,7 @@ I.scrollLeft('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** +- `locator` **([string][5] \| [object][6])** ### scrollRight @@ -348,7 +383,18 @@ I.scrollRight('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** +- `locator` **([string][5] \| [object][6])** + +### scrollToElement + +Scrolls within a scrollable container to an element. + +#### Parameters + +- `targetLocator` **([string][5] \| [object][6])** Locator of the element to scroll to +- `containerLocator` **([string][5] \| [object][6])** Locator of the scrollable container +- `direction` **[string][5]** 'up' or 'down' (optional, default `'down'`) +- `offset` **[number][8]** Offset for scroll, can be adjusted based on need (optional, default `100`) ### scrollUp @@ -360,7 +406,7 @@ I.scrollUp('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** +- `locator` **([string][5] \| [object][6])** ### see @@ -375,8 +421,8 @@ I.see('Record deleted', '~message'); #### Parameters -- `text` **[string][6]** to check visibility -- `context` **([string][6] \| [object][7] | null)** element inside which to search for text (optional, default `null`) +- `text` **[string][5]** to check visibility +- `context` **([string][5] \| [object][6] | null)** element inside which to search for text (optional, default `null`) ### seeElement @@ -390,8 +436,8 @@ I.seeElement('~edit', '#menu'); // element inside #menu #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `context` **([string][6] \| [object][7] | null)** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `context` **([string][5] \| [object][6] | null)** context element (optional, default `null`) ### seeElementExists @@ -405,8 +451,8 @@ I.seeElementExists('~edit', '#menu'); // element inside #menu #### Parameters -- `locator` **([string][6] \| [object][7])** element to locate -- `context` **([string][6] \| [object][7])** context element (optional, default `null`) +- `locator` **([string][5] \| [object][6])** element to locate +- `context` **([string][5] \| [object][6])** context element (optional, default `null`) ### setLandscapeOrientation @@ -443,8 +489,8 @@ I.swipeUp('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** an element on which to perform swipe -- `speed` **[string][6]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) +- `locator` **([string][5] \| [object][6])** an element on which to perform swipe +- `speed` **[string][5]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) ### swipeLeft @@ -457,8 +503,8 @@ I.swipeUp('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** an element on which to perform swipe -- `speed` **[string][6]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) +- `locator` **([string][5] \| [object][6])** an element on which to perform swipe +- `speed` **[string][5]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) ### swipeRight @@ -471,8 +517,8 @@ I.swipeUp('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** an element on which to perform swipe -- `speed` **[string][6]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) +- `locator` **([string][5] \| [object][6])** an element on which to perform swipe +- `speed` **[string][5]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) ### swipeUp @@ -485,17 +531,17 @@ I.swipeUp('#container'); #### Parameters -- `locator` **([string][6] \| [object][7])** an element on which to perform swipe -- `speed` **[string][6]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) +- `locator` **([string][5] \| [object][6])** an element on which to perform swipe +- `speed` **[string][5]** a speed to perform: `slow` or `fast`. (optional, default `'slow'`) ### tap -Taps on an element. +Taps on an element. Element can be located by its text or id or accessibility id. The second parameter is a context element to narrow the search. -Same as [click][12] +Same as [click][11] ```js I.tap('Login'); // locate by text @@ -507,8 +553,40 @@ I.tap({ ios: 'Save', android: 'SAVE' }, '#main'); // different texts on iOS and #### Parameters -- `locator` **([string][6] \| [object][7])** -- `context` **([string][6] \| [object][7] | null)** (optional, default `null`) +- `locator` **([string][5] \| [object][6])** +- `context` **([string][5] \| [object][6] | null)** (optional, default `null`) + +### tapByLabel + +Clicks on an element. +Element can be located by its label + +The second parameter is a context (id | type | accessibility id) to narrow the search. + +```js +I.tapByLabel('Login'); // locate by text +I.tapByLabel('Login', '#nav'); // locate by text inside #nav +``` + +#### Parameters + +- `locator` **([string][5] \| [object][6])** +- `context` **([string][5] \| [object][6] | null)** (optional, default `null`) + +### tapReturnKey + +Taps return key. +A field can be located by text, accessibility id, id. + +```js +I.tapReturnKey('Username'); +I.tapReturnKey('~name'); +I.tapReturnKey({ android: 'NAME', ios: 'name' }); +``` + +#### Parameters + +- `field` **([string][5] \| [object][6])** an input element to fill in ### wait @@ -520,7 +598,7 @@ I.wait(2); // waits for 2 seconds #### Parameters -- `sec` **[number][9]** number of seconds to wait +- `sec` **[number][8]** number of seconds to wait ### waitForElement @@ -532,8 +610,8 @@ I.waitForElement('#message', 1); // wait for 1 second #### Parameters -- `locator` **([string][6] \| [object][7])** an element to wait for -- `sec` **[number][9]** number of seconds to wait, 5 by default (optional, default `5`) +- `locator` **([string][5] \| [object][6])** an element to wait for +- `sec` **[number][8]** number of seconds to wait, 5 by default (optional, default `5`) ### waitForElementVisible @@ -545,12 +623,12 @@ I.waitForElementVisible('#message', 1); // wait for 1 second #### Parameters -- `locator` **([string][6] \| [object][7])** an element to wait for -- `sec` **[number][9]** number of seconds to wait (optional, default `5`) +- `locator` **([string][5] \| [object][6])** an element to wait for +- `sec` **[number][8]** number of seconds to wait (optional, default `5`) ### waitToHide -Waits an elment to become not visible. +Waits an elmenet to become not visible. ```js I.waitToHide('#message', 2); // wait for 2 seconds @@ -558,29 +636,27 @@ I.waitToHide('#message', 2); // wait for 2 seconds #### Parameters -- `locator` **([string][6] \| [object][7])** an element to wait for -- `sec` **[number][9]** number of seconds to wait (optional, default `5`) +- `locator` **([string][5] \| [object][6])** an element to wait for +- `sec` **[number][8]** number of seconds to wait (optional, default `5`) [1]: https://github.com/wix/Detox -[2]: https://github.com/wix/Detox/blob/master/docs/Introduction.GettingStarted.md - -[3]: https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md +[2]: https://wix.github.io/Detox/docs/introduction/project-setup -[4]: https://github.com/wix/Detox/blob/master/docs/Introduction.GettingStarted.md#step-4-build-your-app-and-run-detox-tests +[3]: https://wix.github.io/Detox/docs/introduction/project-setup#step-5-build-the-app -[5]: https://codecept.io +[4]: https://codecept.io -[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String +[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String -[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object +[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object -[8]: #tap +[7]: #tap -[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number +[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number -[10]: #relaunchApp +[9]: #relaunchApp -[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function +[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function -[12]: #click +[11]: #click diff --git a/docs/helpers/Playwright.md b/docs/helpers/Playwright.md index 0c327c4..c75b5cc 100644 --- a/docs/helpers/Playwright.md +++ b/docs/helpers/Playwright.md @@ -1376,12 +1376,16 @@ Returns **[Promise][22]<[string][9]>** title Returns full URL of request matching parameter "urlMatch". +Examples: + +```js +I.grabTrafficUrl('https://api.example.com/session'); +I.grabTrafficUrl(/session.*start/); +``` + #### Parameters -- `urlMatch` **([string][9] | [RegExp][11])** Expected URL of request in network traffic. Can be a string or a regular expression.Examples:```js - I.grabTrafficUrl('https://api.example.com/session'); - I.grabTrafficUrl(/session.*start/); - ``` +- `urlMatch` **([string][9] | [RegExp][11])** Expected URL of request in network traffic. Can be a string or a regular expression. Returns **[Promise][22]<any>** @@ -2202,12 +2206,15 @@ I.setPlaywrightRequestHeaders({ ### startRecordingTraffic -Resets all recorded network requests. +Starts recording the network traffics. +This also resets recorded network requests. ```js -I.flushNetworkTraffics(); +I.startRecordingTraffic(); ``` +Returns **void** automatically synchronized promise through #recorder + ### startRecordingWebSocketMessages Starts recording of websocket messages. diff --git a/docs/helpers/REST.md b/docs/helpers/REST.md index a237606..cd96d5e 100644 --- a/docs/helpers/REST.md +++ b/docs/helpers/REST.md @@ -26,6 +26,7 @@ Type: [object][4] - `prettyPrintJson` **[boolean][6]?** pretty print json for response/request on console logs - `timeout` **[number][5]?** timeout for requests in milliseconds. 10000ms by default - `defaultHeaders` **[object][4]?** a list of default headers +- `httpAgent` **[object][4]?** create an agent with SSL certificate - `onRequest` **[function][7]?** a async function which can update request object. - `onResponse` **[function][7]?** a async function which can update response object. - `maxUploadFileSize` **[number][5]?** set the max content file size in MB when performing api calls. @@ -46,6 +47,25 @@ Type: [object][4] } } } +``` + + With httpAgent + +```js +{ + helpers: { + REST: { + endpoint: 'http://site.com/api', + prettyPrintJson: true, + httpAgent: { + key: fs.readFileSync(__dirname + '/path/to/keyfile.key'), + cert: fs.readFileSync(__dirname + '/path/to/certfile.cert'), + rejectUnauthorized: false, + keepAlive: true + } + } + } +} ``` ## Access From Helpers diff --git a/docs/locators.md b/docs/locators.md index 2349bc7..d8c1a26 100644 --- a/docs/locators.md +++ b/docs/locators.md @@ -145,6 +145,15 @@ Find an element with provided attributes locate('input').withAttr({ placeholder: 'Type in name' }); ``` +#### withClassAttr + +Find an element with class attribute + +```js +// find div with class contains 'form' +locate('div').withClassAttr('text'); +``` + #### withChild Finds an element which contains a child element provided: diff --git a/docs/wiki/Community-Helpers-&-Plugins.md b/docs/wiki/Community-Helpers-&-Plugins.md index 8ab7f70..a44eb4a 100644 --- a/docs/wiki/Community-Helpers-&-Plugins.md +++ b/docs/wiki/Community-Helpers-&-Plugins.md @@ -34,6 +34,7 @@ Please **add your own** by editing this page. * [codeceptjs-slack-reporter](https://www.npmjs.com/package/codeceptjs-slack-reporter) Get a Slack notification when one or more scenarios fail. * [codeceptjs-browserlogs-plugin](https://github.com/pavkam/codeceptjs-browserlogs-plugin) Record the browser logs for failed tests. * [codeceptjs-testrail](https://github.com/PeterNgTr/codeceptjs-testrail) - a plugin to integrate with [Testrail](https://www.gurock.com/testrail) +* [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - a plugin to generate coverage reports, it integrate with [monocart coverage reports](https://github.com/cenfun/monocart-coverage-reports) ## Browser request control * [codeceptjs-resources-check](https://github.com/luarmr/codeceptjs-resources-check) Load a URL with Puppeteer and listen to the requests while the page is loading. Enabling count the number or check the sizes of the requests. diff --git a/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md b/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md index b0236d0..c790476 100644 --- a/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +++ b/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md @@ -58,4 +58,40 @@ void (async () => { await fs.writeFileSync(`${coverageFolder}/final.json`, JSON.stringify(converter.toIstanbul(), null, 2)) } })() -``` \ No newline at end of file +``` + +Or simply use the plugin [`codeceptjs-monocart-coverage`](https://github.com/cenfun/codeceptjs-monocart-coverage) +- Install +```sh +npm i codeceptjs-monocart-coverage +``` +- Usage +```js +// codecept.conf.js +{ + plugins: { + monocart: { + require: 'codeceptjs-monocart-coverage', + enabled: true, + coverageOptions: { + // more options: https://github.com/cenfun/monocart-coverage-reports + name: 'My CodeceptJS Coverage Report', + outputDir: 'coverage-reports' + } + } + }, + helpers: { + // Coverage is only supported in Playwright or Puppeteer + Playwright: { + browser: 'chromium', + url: 'http://localhost', + show: false + } + // Puppeteer: { + // url: 'http://localhost', + // show: false + // } + } +} +``` +