diff --git a/.github/workflows/main.yml b/.github/workflows/check.yml similarity index 91% rename from .github/workflows/main.yml rename to .github/workflows/check.yml index 712b6d6d6..38d630179 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/check.yml @@ -14,3 +14,4 @@ jobs: tests: "./test/**/*_test.js" token: ${{ secrets.GITHUB_TOKEN }} has-tests-label: true + comment-on-empty: true diff --git a/.gitignore b/.gitignore index e0bb9e879..33ba18d19 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ testpullfilecache* package-lock.json yarn.lock /.vs +typings/types.d.ts \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f8532ebdf..350e2fb17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## 2.4.0 + +* Improved setup wizard with `npx codecept init`: + * **enabled [retryFailedStep](/plugins/#retryfailedstep) plugin for new setups**. + * enabled [@codeceptjs/configure](/configuration/#common-configuration-patterns) to toggle headless/window mode via env variable + * creates a new test on init + * removed question on "steps file", create it by default. +* Added [pauseOnFail plugin](/plugins/#pauseonfail). *Sponsored by Paul Vincent Beigang and his book "[Practical End 2 End Testing with CodeceptJS](https://leanpub.com/codeceptjs/)"*. +* Added [`run-rerun` command](/commands/#run-rerun) to run tests multiple times to detect and fix flaky tests. By @Ilrilan and @Vorobeyko. +* Added [`Scenario.todo()` to declare tests as pending](/basics#todotest). See #2100 by @Vorobeyko +* Added support for absolute path for `output` dir. See #2049 by @elukoyanov +* Fixed error in `npx codecept init` caused by calling `console.print`. See #2071 by @Atinux. +* [Filesystem] Methods added by @aefluke: + * `seeFileNameMatching` + * `grabFileNames` +* [Puppeteer] Fixed grabbing attributes with hyphen by @Holorium +* [TestCafe] Fixed `grabAttributeFrom` method by @elukoyanov +* [MockRequest] Added support for [Polly config options](https://netflix.github.io/pollyjs/#/configuration?id=configuration) by @ecrmnn +* [TestCafe] Fixes exiting with zero code on failure. Fixed #2090 with #2106 by @koushikmohan1996 +* [WebDriver][Puppeteer] Added basicAuth support via config. Example: `basicAuth: {username: 'username', password: 'password'}`. See #1962 by @PeterNgTr +* [WebDriver][Appium] Added `scrollIntoView` by @pablopaul +* Fixed #2118: No error stack trace for syntax error by @senthillkumar +* Added `parse()` method to data table inside Cucumber tests. Use it to obtain rows and hashes for test data. See #2082 by @Sraime + ## 2.3.6 * Create better Typescript definition file through JSDoc. By @lemnis @@ -8,7 +32,7 @@ exports.config = { tests: '{./workers/base_test.workers.js,./workers/test_grep.workers.js}', } ``` -* Added new command `npx codeceptjs info` which print information about your environment and CodeceptJS configs. By @jamesgeorge007 +* Added new command `npx codeceptjs info` which print information about your environment and CodeceptJS configs. By @jamesgeorge007 * Fixed some typos in documantation. By @pablopaul @atomicpages @EricTendian * Added PULL_REQUEST template. * [Puppeteer][WebDriver] Added `waitForClickable` for waiting clickable element on page. diff --git a/docs/advanced.md b/docs/advanced.md index 4d57d02a0..ab10fc804 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -228,3 +228,45 @@ Please note that some config changes can't be applied on the fly. For instance, Configuration changes will be reverted after a test or a suite. + +### Rerunning Flaky Tests Multiple Times + +End to end tests can be flaky for various reasons. Even when we can't do anything to solve this problem it we can do next two things: + +* Detect flaky tests in our suite +* Fix flaky tests by rerunning them. + +Both tasks can be achieved with [`run-rerun` command](/commands/#run-rerun) which runs tests multiple times until all tests are passed. + +You should set min and max runs boundaries so when few tests fail in a row you can rerun them until they are succeeded. + +```js +// inside to codecept.conf.js +exports.config = { // ... + rerun: { + // run 4 times until 1st success + minSuccess: 1, + maxReruns: 4, + } +} +``` + +If you want to check all your tests for stability you can set high boundaries for minimal success: + +```js +// inside to codecept.conf.js +exports.config = { // ... + rerun: { + // run all tests must pass exactly 5 times + minSuccess: 5, + maxReruns: 5, + } +} +``` + +Now execute tests with `run-rerun` command: + +``` +npx codeceptjs run-rerun +``` + diff --git a/docs/basics.md b/docs/basics.md index ee5f140b0..42f714f57 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -450,7 +450,7 @@ The interactive shell can be started outside of test context by running npx codeceptjs shell ``` -### Pause on Failure +### Pause on Failure To start interactive pause automatically for a failing test you can run tests with [pauseOnFail Plugin](/plugins/#pauseonfail). When a test fails, the pause mode will be activated, so you can inspect current browser session before it is closed. @@ -465,14 +465,24 @@ This is an **essential feature to debug flaky tests**, as you can analyze them i By default CodeceptJS saves a screenshot of a failed test. This can be configured in [screenshotOnFail Plugin](/plugins/#screenshotonfail) +> **screenshotOnFail plugin is enabled by default** for new setups + ### Step By Step Report To see how the test was executed, use [stepByStepReport Plugin](/plugins/#stepbystepreport). It saves a screenshot of each passed step and shows them in a nice slideshow. ## Retries +### Auto Retry + +You can auto-retry a failed step by enabling [retryFailedStep Plugin](/plugins/#retryfailedstep). + +> **autoRetry plugin is enabled by default** for new setups since CodeceptJS 2.4 + ### Retry Step +Unless you use retryFailedStep plugin you can manually control retries in your project. + If you have a step which often fails, you can retry execution for this single step. Use the `retry()` function before an action to ask CodeceptJS to retry it on failure: @@ -505,9 +515,6 @@ I.retry({ Pass a function to the `when` option to retry only when an error matches the expected one. -### Auto Retry - -You can auto-retry a failed step by enabling [retryFailedStep Plugin](/plugins/#retryfailedstep). ### Retry Scenario @@ -601,8 +608,11 @@ within('.js-signup-form', () => { I.see('There were problems creating your account.'); ``` +> ⚠ `within` can cause problems when used incorrectly. If you see a weired behavior of a test try to refactor it to not use `within`. It is recommended to keep within for simplest cases when possible. + `within` can also work with IFrames. A special `frame` locator is required to locate the iframe and get into its context. + See example: ```js @@ -611,6 +621,8 @@ within({frame: "#editor"}, () => { }); ``` +> ℹ IFrames can also be accessed via `I.switchTo` command of a corresponding helper. + Nested IFrames can be set by passing an array *(WebDriver, Nightmare & Puppeteer only)*: ```js @@ -759,12 +771,14 @@ Like in Mocha you can use `x` and `only` to skip tests or to run a single test. * `xScenario` - skips current test * `Scenario.only` - executes only the current test -## Todo test +## Todo Test + +You can use `Scenario.todo` when you are planning on writing tests. -You can use `Scenario.todo` when you are planning on writing tests. This test will be skipped like with usual `skip`. -But this method to adds skip-message: `Test not implemented!` to test instance. +This test will be skipped like with regular `Scenario.skip` but with additional message "Test not implemented!": + +Use it with a test body as a test plan: -Example #1 - with callback: ```js Scenario.todo('Test', I => { /** @@ -774,19 +788,11 @@ Scenario.todo('Test', I => { * Result: * 3. Field contains text */ -}) -``` - -Example #2 - without callback: -```js -Scenario.todo('Test') +}); ``` -And you can access the `message` of `test.opts.skipInfo` in the events +Or even without a test body: -Example #3 - access into event hooks ```js - event.dispatcher.on(event.test.before, (test) => { - test.opts.skipInfo // contains {message and description} - }); -``` \ No newline at end of file +Scenario.todo('Test'); +``` diff --git a/docs/bdd.md b/docs/bdd.md index b898708fc..c5e0833d1 100644 --- a/docs/bdd.md +++ b/docs/bdd.md @@ -11,7 +11,7 @@ Behavior Driven Development (BDD) is a popular software development methodology. BDD was introduced by [Dan North](https://dannorth.net/introducing-bdd/). He described it as: -> outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters. +> outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters. BDD has its own evolution from the days it was born, started by replacing "test" to "should" in unit tests, and moving towards powerful tools like Cucumber and Behat, which made user stories (human readable text) to be executed as an acceptance test. @@ -253,10 +253,11 @@ Given('I have products in my cart', (table) => { // eslint-disable-line }); ``` -You can also use the parse() function to obtain an object that allow you to get a simple version of the table parsed by column or row, with header (or not) : -- raw(): returns the table as a 2-D array -- rows(): returns the table as a 2-D array, without the first row -- hashes(): returns an array of objects where each row is converted to an object (column header is the key) +You can also use the `parse()` method to obtain an object that allow you to get a simple version of the table parsed by column or row, with header (or not): + +- `raw()` - returns the table as a 2-D array +- `rows()` - returns the table as a 2-D array, without the first row +- `hashes()` - returns an array of objects where each row is converted to an object (column header is the key) If we use hashes() with the previous exemple : diff --git a/docs/commands.md b/docs/commands.md index a8653e15c..5c610ad07 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -88,26 +88,35 @@ Run tests in parallel threads. npx codeceptjs run-workers 3 ``` -## Run Rerun +## Run Rerun -Run tests with rerun of all suite some cycles. Use config.rerun params: +Run tests multiple times to detect and fix flaky tests. ``` +npx codeceptjs run-rerun +``` + +For this command configuration is required: + +```js { -... -rerun: { - //how many times all tests in suite must pass + // inside codecept.conf.js + rerun: { + // how many times all tests should pass minSuccess: 2, - //how many times we can try to rerun all test suite for reaching minSuccess count of passed test suite + + // how many times to try to rerun all tests maxReruns: 4, } } ``` -For example: - - minSuccess 1, maxReruns 5 - CodeceptJS will run all test suite no more than 5 times, until first successful run - - minSuccess 3, maxReruns 5 - CodeceptJS will run all test suite no more than 5 times, until reaching 3 successfull runs - - minSuccess 10, maxReruns 10 - CodeceptJS will run all test suite 10 times, and if any one test in any run will fail - all suite is failed. +Use Cases: + +* `minSuccess: 1, maxReruns: 5` - run all tests no more than 5 times, until first successful run. +* `minSuccess: 3, maxReruns: 5` - run all tests no more than 5 times, until reaching 3 successfull runs. +* `minSuccess: 10, maxReruns: 10` - run all tests exactly 10 times, to check their stability. + ## Dry Run diff --git a/docs/helpers/ApiDataFactory.md b/docs/helpers/ApiDataFactory.md index 57951a264..bb837d773 100644 --- a/docs/helpers/ApiDataFactory.md +++ b/docs/helpers/ApiDataFactory.md @@ -1,6 +1,6 @@ --- -permalink: helpers/ApiDataFactory -editLink: https://github.com/Codeception/CodeceptJS/blob/master/lib/helper/ApiDataFactory.js +permalink: /helpers/ApiDataFactory +editLink: false sidebar: auto title: ApiDataFactory --- diff --git a/docs/helpers/Appium.md b/docs/helpers/Appium.md index 51cbef9f6..7f8a2865b 100644 --- a/docs/helpers/Appium.md +++ b/docs/helpers/Appium.md @@ -1,10 +1,3 @@ ---- -permalink: helpers/Appium -editLink: https://github.com/Codeception/CodeceptJS/blob/master/lib/helper/Appium.js -sidebar: auto -title: Appium ---- - ## Appium @@ -37,7 +30,7 @@ This helper should be configured in codecept.json or codecept.conf.js - `port`: (default: '4723') Appium port - `platform`: (Android or IOS), which mobile OS to use; alias to desiredCapabilities.platformName - `restart`: restart browser or app between tests (default: true), if set to false cookies will be cleaned but browser window will be kept and for apps nothing will be changed. -- `desiredCapabilities`: [], Appium capabilities, see below +- `desiredCapabilities`: \[], Appium capabilities, see below - `platformName` - Which mobile OS platform to use - `appPackage` - Java package of the Android app you want to run - `appActivity` - Activity name for the Android activity you want to launch from your package. @@ -121,161 +114,199 @@ let browser = this.helpers['Appium'].browser - `config` -### _switchToContext +### runOnIOS -Switch to the specified context. +Execute code only on iOS -#### Parameters +```js +I.runOnIOS(() => { + I.click('//UIAApplication[1]/UIAWindow[1]/UIAButton[1]'); + I.see('Hi, IOS', '~welcome'); +}); +``` -- `context` **any** the context to switch to +Additional filter can be applied by checking for capabilities. +For instance, this code will be executed only on iPhone 5s: -### appendField +```js +I.runOnIOS({deviceName: 'iPhone 5s'},() => { + // ... +}); +``` -Appends text to a input field or textarea. -Field is located by name, label, CSS or XPath +Also capabilities can be checked by a function. ```js -I.appendField('#myTextField', 'appended'); +I.runOnAndroid((caps) => { + // caps is current config of desiredCapabiliites + return caps.platformVersion >= 6 +},() => { + // ... +}); ``` #### Parameters -- `field` **([string][4] | [object][5])** located by label|name|CSS|XPath|strict locator -- `value` **[string][4]** text value to append. +- `caps` **any** +- `fn` **any** -### checkOption +### runOnAndroid -Selects a checkbox or radio button. -Element is located by label or name or CSS or XPath. +Execute code only on Android -The second parameter is a context (CSS or XPath locator) to narrow the search. +```js +I.runOnAndroid(() => { + I.click('io.selendroid.testapp:id/buttonTest'); +}); +``` + +Additional filter can be applied by checking for capabilities. +For instance, this code will be executed only on Android 6.0: ```js -I.checkOption('#agree'); -I.checkOption('I Agree to Terms and Conditions'); -I.checkOption('agree', '//form'); +I.runOnAndroid({platformVersion: '6.0'},() => { + // ... +}); +``` + +Also capabilities can be checked by a function. +In this case, code will be executed only on Android >= 6. + +```js +I.runOnAndroid((caps) => { + // caps is current config of desiredCapabiliites + return caps.platformVersion >= 6 +},() => { + // ... +}); ``` #### Parameters -- `field` **([string][4] | [object][5])** checkbox located by label | name | CSS | XPath | strict locator. -- `context` **([string][4]? | [object][5])** (optional, `null` by default) element located by CSS | XPath | strict locator. +- `caps` **any** +- `fn` **any** -### click +### runInWeb -Perform a click on a link or a button, given by a locator. -If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. -For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched. -For images, the "alt" attribute and inner text of any parent links are searched. +Execute code only in Web mode. -The second parameter is a context (CSS or XPath locator) to narrow the search. +```js +I.runInWeb(() => { + I.waitForElement('#data'); + I.seeInCurrentUrl('/data'); +}); +``` + +#### Parameters + +- `fn` **any** + +### seeAppIsInstalled + +Check if an app is installed. ```js -// simple link -I.click('Logout'); -// button of form -I.click('Submit'); -// CSS button -I.click('#form input[type=submit]'); -// XPath -I.click('//form/*[@type=submit]'); -// link in context -I.click('Logout', '#nav'); -// using strict locator -I.click({css: 'nav a.login'}); +I.seeAppIsInstalled("com.example.android.apis"); ``` #### Parameters -- `locator` **([string][4] | [object][5])** clickable link or button located by text, or any element located by CSS|XPath|strict locator. -- `context` **([string][4]? | [object][5])** (optional, `null` by default) element to search in CSS|XPath|Strict locator. +- `bundleId` **[string][4]** String ID of bundled appAppium: support only Android -### closeApp +### seeAppIsNotInstalled -Close the given application. +Check if an app is not installed. ```js -I.closeApp(); +I.seeAppIsNotInstalled("com.example.android.apis"); ``` -Appium: support only iOS +#### Parameters -### dontSee +- `bundleId` **[string][4]** String ID of bundled appAppium: support only Android -Opposite to `see`. Checks that a text is not present on a page. -Use context parameter to narrow down the search. +### installApp + +Install an app on device. ```js -I.dontSee('Login'); // assume we are already logged in. -I.dontSee('Login', '.nav'); // no login inside .nav element +I.installApp('/path/to/file.apk'); ``` #### Parameters -- `text` **[string][4]** which is not present. -- `context` **([string][4] | [object][5])?** (optional) element located by CSS|XPath|strict locator in which to perfrom search. +- `path` **[string][4]** path to apk fileAppium: support only Android -### dontSeeCheckboxIsChecked +### removeApp -Verifies that the specified checkbox is not checked. +Remove an app from the device. ```js -I.dontSeeCheckboxIsChecked('#agree'); // located by ID -I.dontSeeCheckboxIsChecked('I agree to terms'); // located by label -I.dontSeeCheckboxIsChecked('agree'); // located by name +I.removeApp('appName', 'com.example.android.apis'); ``` #### Parameters -- `field` **([string][4] | [object][5])** located by label|name|CSS|XPath|strict locator. +- `appId` **[string][4]** +- `bundleId` **[string][4]** String ID of bundleAppium: support only Android -### dontSeeElement +### seeCurrentActivityIs -Opposite to `seeElement`. Checks that element is not visible (or in DOM) +Check current activity on an Android device. ```js -I.dontSeeElement('.modal'); // modal is not shown +I.seeCurrentActivityIs(".HomeScreenActivity") ``` #### Parameters -- `locator` **([string][4] | [object][5])** located by CSS|XPath|Strict locator. +- `currentActivity` **[string][4]** Appium: support only Android -### dontSeeInField +### seeDeviceIsLocked -Checks that value of input field or textarea doesn't equal to given value -Opposite to `seeInField`. +Check whether the device is locked. ```js -I.dontSeeInField('email', 'user@user.com'); // field by name -I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS +I.seeDeviceIsLocked(); +``` + +Appium: support only Android + +### seeDeviceIsUnlocked + +Check whether the device is not locked. + +```js +I.seeDeviceIsUnlocked(); +``` + +Appium: support only Android + +### seeOrientationIs + +Check the device orientation + +```js +I.seeOrientationIs('PORTRAIT'); +I.seeOrientationIs('LANDSCAPE') ``` #### Parameters -- `field` **([string][4] | [object][5])** located by label|name|CSS|XPath|strict locator. -- `value` **[string][4]** value to check. +- `orientation` **(`"LANDSCAPE"` \| `"PORTRAIT"`)** LANDSCAPE or PORTRAITAppium: support Android and iOS -### fillField +### setOrientation -Fills a text field or textarea, after clearing its value, with the given string. -Field is located by name, label, CSS, or XPath. +Set a device orientation. Will fail, if app will not set orientation ```js -// by label -I.fillField('Email', 'hello@world.com'); -// by name -I.fillField('password', secret('123456')); -// by CSS -I.fillField('form#login input[name=username]', 'John'); -// or by strict locator -I.fillField({css: 'form#login input[name=username]'}, 'John'); +I.setOrientation('PORTRAIT'); +I.setOrientation('LANDSCAPE') ``` #### Parameters -- `field` **([string][4] | [object][5])** located by label|name|CSS|XPath|strict locator. -- `value` **[string][4]** text value to fill. +- `orientation` **(`"LANDSCAPE"` \| `"PORTRAIT"`)** LANDSCAPE or PORTRAITAppium: support Android and iOS ### grabAllContexts @@ -337,782 +368,1307 @@ let settings = await I.grabSettings(); Appium: support Android and iOS -### grabTextFrom - -Retrieves a text from an element located by CSS or XPath and returns it to test. -Resumes test execution, so **should be used inside async with `await`** operator. - -```js -let pin = await I.grabTextFrom('#pin'); -``` +### \_switchToContext -If multiple elements found returns an array of texts. +Switch to the specified context. #### Parameters -- `locator` **([string][4] | [object][5])** element located by CSS|XPath|strict locator. - -Returns **[Promise][6]<([string][4] | [Array][7]<[string][4]>)>** attribute value +- `context` **any** the context to switch to -### grabValueFrom +### switchToWeb -Retrieves a value from a form element located by CSS or XPath and returns it to test. -Resumes test execution, so **should be used inside async function with `await`** operator. +Switches to web context. +If no context is provided switches to the first detected web context ```js -let email = await I.grabValueFrom('input[name=email]'); +// switch to first web context +I.switchToWeb(); + +// or set the context explicitly +I.switchToWeb('WEBVIEW_io.selendroid.testapp'); ``` #### Parameters -- `locator` **([string][4] | [object][5])** field located by label|name|CSS|XPath|strict locator. - -Returns **[Promise][6]<[string][4]>** attribute value +- `context` **[string][4]?** -### hideDeviceKeyboard +### switchToNative -Hide the keyboard. +Switches to native context. +By default switches to NATIVE_APP context unless other specified. ```js -// taps outside to hide keyboard per default -I.hideDeviceKeyboard(); -I.hideDeviceKeyboard('tapOutside'); +I.switchToNative(); -// or by pressing key -I.hideDeviceKeyboard('pressKey', 'Done'); +// or set context explicitly +I.switchToNative('SOME_OTHER_CONTEXT'); ``` #### Parameters -- `strategy` **(`"tapOutside"` | `"pressKey"`)** desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)Appium: support Android and iOS -- `key` +- `context` **any** (optional, default `null`) -### installApp +### startActivity -Install an app on device. +Start an arbitrary Android activity during a session. ```js -I.installApp('/path/to/file.apk'); +I.startActivity('io.selendroid.testapp', '.RegisterUserActivity'); ``` +Appium: support only Android + #### Parameters -- `path` **[string][4]** path to apk fileAppium: support only Android +- `appPackage` +- `appActivity` -### makeTouchAction +### setNetworkConnection -The Touch Action API provides the basis of all gestures that can be -automated in Appium. At its core is the ability to chain together ad hoc -individual actions, which will then be applied to an element in the -application on the device. -[See complete documentation][8] +Set network connection mode. + +- airplane mode +- wifi mode +- data data ```js -I.makeTouchAction("~buttonStartWebviewCD", 'tap'); +I.setNetworkConnection(0) // airplane mode off, wifi off, data off +I.setNetworkConnection(1) // airplane mode on, wifi off, data off +I.setNetworkConnection(2) // airplane mode off, wifi on, data off +I.setNetworkConnection(4) // airplane mode off, wifi off, data on +I.setNetworkConnection(6) // airplane mode off, wifi on, data on ``` -Appium: support Android and iOS +See corresponding [webdriverio reference][5]. -#### Parameters +Appium: support only Android + +#### Parameters + +- `value` + +### setSettings + +Update the current setting on the device + +```js +I.setSettings({cyberdelia: 'open'}); +``` + +#### Parameters + +- `settings` **[object][6]** objectAppium: support Android and iOS + +### hideDeviceKeyboard + +Hide the keyboard. + +```js +// taps outside to hide keyboard per default +I.hideDeviceKeyboard(); +I.hideDeviceKeyboard('tapOutside'); + +// or by pressing key +I.hideDeviceKeyboard('pressKey', 'Done'); +``` + +#### Parameters + +- `strategy` **(`"tapOutside"` \| `"pressKey"`)** desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)Appium: support Android and iOS +- `key` + +### sendDeviceKeyEvent + +Send a key event to the device. +List of keys: [https://developer.android.com/reference/android/view/KeyEvent.html][7] + +```js +I.sendDeviceKeyEvent(3); +``` + +#### Parameters + +- `keyValue` **[number][8]** Device specific key valueAppium: support only Android + +### openNotifications + +Open the notifications panel on the device. + +```js +I.openNotifications(); +``` + +Appium: support only Android + +### makeTouchAction + +The Touch Action API provides the basis of all gestures that can be +automated in Appium. At its core is the ability to chain together ad hoc +individual actions, which will then be applied to an element in the +application on the device. +[See complete documentation][9] + +```js +I.makeTouchAction("~buttonStartWebviewCD", 'tap'); +``` + +Appium: support Android and iOS + +#### Parameters + +- `locator` +- `action` + +### tap + +Taps on element. + +```js +I.tap("~buttonStartWebviewCD"); +``` + +Shortcut for `makeTouchAction` + +#### Parameters + +- `locator` **any** + +### swipe + +Perform a swipe on the screen or an element. + +```js +let locator = "#io.selendroid.testapp:id/LinearLayout1"; +I.swipe(locator, 800, 1200, 1000); +``` + +[See complete reference][10] + +#### Parameters + +- `locator` **([string][4] \| [object][6])** +- `xoffset` **[number][8]** +- `yoffset` **[number][8]** +- `speed` **[number][8]** (optional), 1000 by defaultAppium: support Android and iOS (optional, default `1000`) + +### performSwipe + +Perform a swipe on the screen. + +```js +I.performswipe(100,200); +``` + +#### Parameters + +- `from` **[number][8]** +- `to` **[number][8]** Appium: support Android and iOS + +### swipeDown + +Perform a swipe down on an element. + +```js +let locator = "#io.selendroid.testapp:id/LinearLayout1"; +I.swipeDown(locator); // simple swipe +I.swipeDown(locator, 500); // set speed +I.swipeDown(locator, 1200, 1000); // set offset and speed +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** +- `yoffset` **[number][8]?** (optional) (optional, default `1000`) +- `speed` **[number][8]** (optional), 1000 by defaultAppium: support Android and iOS (optional, default `1000`) + +### swipeLeft + +Perform a swipe left on an element. + +```js +let locator = "#io.selendroid.testapp:id/LinearLayout1"; +I.swipeLeft(locator); // simple swipe +I.swipeLeft(locator, 500); // set speed +I.swipeLeft(locator, 1200, 1000); // set offset and speed +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** +- `xoffset` **[number][8]?** (optional) (optional, default `1000`) +- `speed` **[number][8]** (optional), 1000 by defaultAppium: support Android and iOS (optional, default `1000`) + +### swipeRight + +Perform a swipe right on an element. + +```js +let locator = "#io.selendroid.testapp:id/LinearLayout1"; +I.swipeRight(locator); // simple swipe +I.swipeRight(locator, 500); // set speed +I.swipeRight(locator, 1200, 1000); // set offset and speed +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** +- `xoffset` **[number][8]?** (optional) (optional, default `1000`) +- `speed` **[number][8]** (optional), 1000 by defaultAppium: support Android and iOS (optional, default `1000`) + +### swipeUp + +Perform a swipe up on an element. + +```js +let locator = "#io.selendroid.testapp:id/LinearLayout1"; +I.swipeUp(locator); // simple swipe +I.swipeUp(locator, 500); // set speed +I.swipeUp(locator, 1200, 1000); // set offset and speed +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** +- `yoffset` **[number][8]?** (optional) (optional, default `1000`) +- `speed` **[number][8]** (optional), 1000 by defaultAppium: support Android and iOS (optional, default `1000`) + +### swipeTo + +Perform a swipe in selected direction on an element to searchable element. + +```js +I.swipeTo( + "android.widget.CheckBox", // searchable element + "//android.widget.ScrollView/android.widget.LinearLayout", // scroll element + "up", // direction + 30, + 100, + 500); +``` + +#### Parameters + +- `searchableLocator` **[string][4]** +- `scrollLocator` **[string][4]** +- `direction` **[string][4]** +- `timeout` **[number][8]** +- `offset` **[number][8]** +- `speed` **[number][8]** Appium: support Android and iOS + +### touchPerform + +Performs a specific touch action. +The action object need to contain the action name, x/y coordinates + +```js +I.touchPerform([{ + action: 'press', + options: { + x: 100, + y: 200 + } +}, {action: 'release'}]) + +I.touchPerform([{ + action: 'tap', + options: { + element: '1', // json web element was queried before + x: 10, // x offset + y: 20, // y offset + count: 1 // number of touches + } +}]); +``` + +Appium: support Android and iOS + +#### Parameters + +- `actions` + +### pullFile + +Pulls a file from the device. + +```js +I.pullFile('/storage/emulated/0/DCIM/logo.png', 'my/path'); +// save file to output dir +I.pullFile('/storage/emulated/0/DCIM/logo.png', output_dir); +``` + +Appium: support Android and iOS + +#### Parameters + +- `path` +- `dest` + +### shakeDevice + +Perform a shake action on the device. + +```js +I.shakeDevice(); +``` + +Appium: support only iOS + +### rotate + +Perform a rotation gesture centered on the specified element. + +```js +I.rotate(120, 120) +``` + +See corresponding [webdriverio reference][11]. + +Appium: support only iOS + +#### Parameters + +- `x` +- `y` +- `duration` +- `radius` +- `rotation` +- `touchCount` + +### setImmediateValue + +Set immediate value in app. + +See corresponding [webdriverio reference][12]. + +Appium: support only iOS + +#### Parameters + +- `id` +- `value` + +### simulateTouchId + +Simulate Touch ID with either valid (match == true) or invalid (match == false) fingerprint. + +```js +I.touchId(); // simulates valid fingerprint +I.touchId(true); // simulates valid fingerprint +I.touchId(false); // simulates invalid fingerprint +``` + +Appium: support only iOS +TODO: not tested + +#### Parameters + +- `match` + +### closeApp + +Close the given application. + +```js +I.closeApp(); +``` + +Appium: support only iOS + +### appendField + +Appends text to a input field or textarea. +Field is located by name, label, CSS or XPath + +```js +I.appendField('#myTextField', 'appended'); +``` + +#### Parameters + +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator +- `value` **[string][4]** text value to append. + +### checkOption + +Selects a checkbox or radio button. +Element is located by label or name or CSS or XPath. + +The second parameter is a context (CSS or XPath locator) to narrow the search. + +```js +I.checkOption('#agree'); +I.checkOption('I Agree to Terms and Conditions'); +I.checkOption('agree', '//form'); +``` + +#### Parameters + +- `field` **([string][4] \| [object][6])** checkbox located by label | name | CSS | XPath | strict locator. +- `context` **([string][4]? | [object][6])** (optional, `null` by default) element located by CSS | XPath | strict locator. (optional, default `null`) + +### click + +Perform a click on a link or a button, given by a locator. +If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. +For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched. +For images, the "alt" attribute and inner text of any parent links are searched. + +The second parameter is a context (CSS or XPath locator) to narrow the search. + +```js +// simple link +I.click('Logout'); +// button of form +I.click('Submit'); +// CSS button +I.click('#form input[type=submit]'); +// XPath +I.click('//form/*[@type=submit]'); +// link in context +I.click('Logout', '#nav'); +// using strict locator +I.click({css: 'nav a.login'}); +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** clickable link or button located by text, or any element located by CSS|XPath|strict locator. +- `context` **([string][4]? | [object][6])** (optional, `null` by default) element to search in CSS|XPath|Strict locator. (optional, default `null`) + +### dontSeeCheckboxIsChecked + +Verifies that the specified checkbox is not checked. + +```js +I.dontSeeCheckboxIsChecked('#agree'); // located by ID +I.dontSeeCheckboxIsChecked('I agree to terms'); // located by label +I.dontSeeCheckboxIsChecked('agree'); // located by name +``` + +#### Parameters + +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator. + +### dontSeeElement + +Opposite to `seeElement`. Checks that element is not visible (or in DOM) -- `locator` -- `action` +```js +I.dontSeeElement('.modal'); // modal is not shown +``` -### openNotifications +#### Parameters -Open the notifications panel on the device. +- `locator` **([string][4] \| [object][6])** located by CSS|XPath|Strict locator. + +### dontSeeInField + +Checks that value of input field or textarea doesn't equal to given value +Opposite to `seeInField`. ```js -I.openNotifications(); +I.dontSeeInField('email', 'user@user.com'); // field by name +I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS ``` -Appium: support only Android +#### Parameters -### performSwipe +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator. +- `value` **[string][4]** value to check. -Perform a swipe on the screen. +### dontSee + +Opposite to `see`. Checks that a text is not present on a page. +Use context parameter to narrow down the search. ```js -I.performswipe(100,200); +I.dontSee('Login'); // assume we are already logged in. +I.dontSee('Login', '.nav'); // no login inside .nav element ``` #### Parameters -- `from` **[number][9]** -- `to` **[number][9]** Appium: support Android and iOS +- `text` **[string][4]** which is not present. +- `context` **([string][4] \| [object][6])?** (optional) element located by CSS|XPath|strict locator in which to perfrom search. (optional, default `null`) -### pullFile +### fillField -Pulls a file from the device. +Fills a text field or textarea, after clearing its value, with the given string. +Field is located by name, label, CSS, or XPath. ```js -I.pullFile('/storage/emulated/0/DCIM/logo.png', 'my/path'); -// save file to output dir -I.pullFile('/storage/emulated/0/DCIM/logo.png', output_dir); +// by label +I.fillField('Email', 'hello@world.com'); +// by name +I.fillField('password', secret('123456')); +// by CSS +I.fillField('form#login input[name=username]', 'John'); +// or by strict locator +I.fillField({css: 'form#login input[name=username]'}, 'John'); ``` -Appium: support Android and iOS +#### Parameters + +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator. +- `value` **[string][4]** text value to fill. + +### grabTextFrom + +Retrieves a text from an element located by CSS or XPath and returns it to test. +Resumes test execution, so **should be used inside async with `await`** operator. + +```js +let pin = await I.grabTextFrom('#pin'); +``` + +If multiple elements found returns an array of texts. #### Parameters -- `path` -- `dest` +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. -### removeApp +Returns **[Promise][13]<([string][4] \| [Array][14]<[string][4]>)>** attribute value -Remove an app from the device. +### grabValueFrom + +Retrieves a value from a form element located by CSS or XPath and returns it to test. +Resumes test execution, so **should be used inside async function with `await`** operator. ```js -I.removeApp('appName', 'com.example.android.apis'); +let email = await I.grabValueFrom('input[name=email]'); ``` #### Parameters -- `appId` **[string][4]** -- `bundleId` **[string][4]** String ID of bundleAppium: support only Android +- `locator` **([string][4] \| [object][6])** field located by label|name|CSS|XPath|strict locator. -### rotate +Returns **[Promise][13]<[string][4]>** attribute value -Perform a rotation gesture centered on the specified element. +### scrollIntoView + +Scroll element into viewport. ```js -I.rotate(120, 120) +I.scrollIntoView('#submit'); +I.scrollIntoView('#submit', true); +I.scrollIntoView('#submit', { behavior: "smooth", block: "center", inline: "center" }); ``` -See corresponding [webdriverio reference][10]. +#### Parameters -Appium: support only iOS +- `locator` **([string][4] \| [object][6])** located by CSS|XPath|strict locator. +- `scrollIntoViewOptions` +- `alignToTop` **([boolean][15] \| [object][6])** (optional) or scrollIntoViewOptions (optional), see [https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView][16].Supported only for web testing + +### seeCheckboxIsChecked + +Verifies that the specified checkbox is checked. + +```js +I.seeCheckboxIsChecked('Agree'); +I.seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms +I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'}); +``` #### Parameters -- `x` -- `y` -- `duration` -- `radius` -- `rotation` -- `touchCount` +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator. -### runInWeb +### seeElement -Execute code only in Web mode. +Checks that a given Element is visible +Element is located by CSS or XPath. ```js -I.runInWeb(() => { - I.waitForElement('#data'); - I.seeInCurrentUrl('/data'); -}); +I.seeElement('#modal'); ``` #### Parameters -- `fn` **any** +- `locator` **([string][4] \| [object][6])** located by CSS|XPath|strict locator. -### runOnAndroid +### seeInField -Execute code only on Android +Checks that the given input field or textarea equals to given value. +For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. ```js -I.runOnAndroid(() => { - I.click('io.selendroid.testapp:id/buttonTest'); -}); +I.seeInField('Username', 'davert'); +I.seeInField({css: 'form textarea'},'Type your comment here'); +I.seeInField('form input[type=hidden]','hidden_value'); +I.seeInField('#searchform input','Search'); ``` -Additional filter can be applied by checking for capabilities. -For instance, this code will be executed only on Android 6.0: +#### Parameters + +- `field` **([string][4] \| [object][6])** located by label|name|CSS|XPath|strict locator. +- `value` **[string][4]** value to check. + +### see + +Checks that a page contains a visible text. +Use context parameter to narrow down the search. ```js -I.runOnAndroid({platformVersion: '6.0'},() => { - // ... -}); +I.see('Welcome'); // text welcome on a page +I.see('Welcome', '.content'); // text inside .content div +I.see('Register', {css: 'form.register'}); // use strict locator ``` -Also capabilities can be checked by a function. -In this case, code will be executed only on Android >= 6. +#### Parameters + +- `text` **[string][4]** expected on page. +- `context` **([string][4]? | [object][6])** (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text. (optional, default `null`) + +### selectOption + +Selects an option in a drop-down select. +Field is searched by label | name | CSS | XPath. +Option is selected by visible text or by value. ```js -I.runOnAndroid((caps) => { - // caps is current config of desiredCapabiliites - return caps.platformVersion >= 6 -},() => { - // ... -}); +I.selectOption('Choose Plan', 'Monthly'); // select by label +I.selectOption('subscription', 'Monthly'); // match option by text +I.selectOption('subscription', '0'); // or by value +I.selectOption('//form/select[@name=account]','Premium'); +I.selectOption('form select[name=account]', 'Premium'); +I.selectOption({css: 'form select[name=account]'}, 'Premium'); +``` + +Provide an array for the second argument to select multiple options. + +```js +I.selectOption('Which OS do you use?', ['Android', 'iOS']); ``` #### Parameters -- `caps` **any** -- `fn` **any** +- `select` **([string][4] \| [object][6])** field located by label|name|CSS|XPath|strict locator. +- `option` **([string][4] \| [Array][14]<any>)** visible text or value of option.Supported only for web testing -### runOnIOS +### waitForElement -Execute code only on iOS +Waits for element to be present on page (by default waits for 1sec). +Element can be located by CSS or XPath. ```js -I.runOnIOS(() => { - I.click('//UIAApplication[1]/UIAWindow[1]/UIAButton[1]'); - I.see('Hi, IOS', '~welcome'); -}); +I.waitForElement('.btn.continue'); +I.waitForElement('.btn.continue', 5); // wait for 5 secs ``` -Additional filter can be applied by checking for capabilities. -For instance, this code will be executed only on iPhone 5s: +#### Parameters + +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. +- `sec` **[number][8]?** (optional, `1` by default) time in seconds to wait (optional, default `null`) + +### waitForVisible + +Waits for an element to become visible on a page (by default waits for 1sec). +Element can be located by CSS or XPath. ```js -I.runOnIOS({deviceName: 'iPhone 5s'},() => { - // ... -}); +I.waitForVisible('#popup'); ``` -Also capabilities can be checked by a function. +#### Parameters + +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. +- `sec` **[number][8]** (optional, `1` by default) time in seconds to wait (optional, default `1`) + +### waitForInvisible + +Waits for an element to be removed or become invisible on a page (by default waits for 1sec). +Element can be located by CSS or XPath. ```js -I.runOnAndroid((caps) => { - // caps is current config of desiredCapabiliites - return caps.platformVersion >= 6 -},() => { - // ... -}); +I.waitForInvisible('#popup'); ``` #### Parameters -- `caps` **any** -- `fn` **any** +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. +- `sec` **[number][8]** (optional, `1` by default) time in seconds to wait (optional, default `1`) -### scrollIntoView +### waitForText -Scroll element into viewport. +Waits for a text to appear (by default waits for 1sec). +Element can be located by CSS or XPath. +Narrow down search results by providing context. ```js -I.scrollIntoView('#submit'); -I.scrollIntoView('#submit', true); -I.scrollIntoView('#submit', { behavior: "smooth", block: "center", inline: "center" }); +I.waitForText('Thank you, form has been submitted'); +I.waitForText('Thank you, form has been submitted', 5, '#modal'); ``` #### Parameters -- `locator` **([string][4] | [object][5])** located by CSS|XPath|strict locator. -- `scrollIntoViewOptions` -- `alignToTop` **([boolean][11] | [object][5])** (optional) or scrollIntoViewOptions (optional), see [https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView][12].Supported only for web testing +- `text` **[string][4]** to wait for. +- `sec` **[number][8]** (optional, `1` by default) time in seconds to wait (optional, default `1`) +- `context` **([string][4] \| [object][6])?** (optional) element located by CSS|XPath|strict locator. (optional, default `null`) -### see +### \_locate -Checks that a page contains a visible text. -Use context parameter to narrow down the search. +Get elements by different locator types, including strict locator. +Should be used in custom helpers: ```js -I.see('Welcome'); // text welcome on a page -I.see('Welcome', '.content'); // text inside .content div -I.see('Register', {css: 'form.register'}); // use strict locator +this.helpers['WebDriver']._locate({name: 'password'}).then //... ``` #### Parameters -- `text` **[string][4]** expected on page. -- `context` **([string][4]? | [object][5])** (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text. +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. +- `smartWait` (optional, default `false`) + +### \_locateCheckable + +Find a checkbox by providing human readable text: + +```js +this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ... +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. + +### \_locateClickable + +Find a clickable element by providing human readable text: + +```js +this.helpers['WebDriver']._locateClickable('Next page').then // ... +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. + +### \_locateFields + +Find field elements by providing human readable text: + +```js +this.helpers['WebDriver']._locateFields('Your email').then // ... +``` + +#### Parameters + +- `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator. + +### defineTimeout -### seeAppIsInstalled +Set [WebDriver timeouts][17] in realtime. -Check if an app is installed. +Timeouts are expected to be passed as object: ```js -I.seeAppIsInstalled("com.example.android.apis"); +I.defineTimeout({ script: 5000 }); +I.defineTimeout({ implicit: 10000, pageLoad: 10000, script: 5000 }); ``` #### Parameters -- `bundleId` **[string][4]** String ID of bundled appAppium: support only Android +- `timeouts` **WebdriverIO.Timeouts** WebDriver timeouts object. -### seeAppIsNotInstalled +### amOnPage -Check if an app is not installed. +Opens a web page in a browser. Requires relative or absolute url. +If url starts with `/`, opens a web page of a site defined in `url` config parameter. ```js -I.seeAppIsNotInstalled("com.example.android.apis"); +I.amOnPage('/'); // opens main page of website +I.amOnPage('https://github.com'); // opens github +I.amOnPage('/login'); // opens a login page ``` #### Parameters -- `bundleId` **[string][4]** String ID of bundled appAppium: support only Android +- `url` **[string][4]** url path or global url. -### seeCheckboxIsChecked +### doubleClick -Verifies that the specified checkbox is checked. +Performs a double-click on an element matched by link|button|label|CSS or XPath. +Context can be specified as second parameter to narrow search. ```js -I.seeCheckboxIsChecked('Agree'); -I.seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms -I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'}); +I.doubleClick('Edit'); +I.doubleClick('Edit', '.actions'); +I.doubleClick({css: 'button.accept'}); +I.doubleClick('.btn.edit'); ``` #### Parameters -- `field` **([string][4] | [object][5])** located by label|name|CSS|XPath|strict locator. +- `locator` **([string][4] \| [object][6])** clickable link or button located by text, or any element located by CSS|XPath|strict locator. +- `context` **([string][4]? | [object][6])** (optional, `null` by default) element to search in CSS|XPath|Strict locator.{{ react }} (optional, default `null`) -### seeCurrentActivityIs +### rightClick -Check current activity on an Android device. +Performs right click on a clickable element matched by semantic locator, CSS or XPath. ```js -I.seeCurrentActivityIs(".HomeScreenActivity") +// right click element with id el +I.rightClick('#el'); +// right click link or button with text "Click me" +I.rightClick('Click me'); +// right click button with text "Click me" inside .context +I.rightClick('Click me', '.context'); ``` #### Parameters -- `currentActivity` **[string][4]** Appium: support only Android +- `locator` **([string][4] \| [object][6])** clickable element located by CSS|XPath|strict locator. +- `context` **([string][4]? | [object][6])** (optional, `null` by default) element located by CSS|XPath|strict locator.{{ react }} (optional, default `null`) -### seeDeviceIsLocked +### clearField -Check whether the device is locked. +Clears a `