Implement new async API (#6954)
* integrate new async interface into sync * allow access to element props * increase test coverage * fix smoke tests * improve unit testing * add eslint ignore * fix smoke tests * add smoke tests for multiremote * PR feedback * Update docs to prefer new async API (#6987) * update API template to add sync depcrecation warning * rewrite API examples * update guide section examples * fix example * fix xpath code * update section * improve design of depcrecation warning * Update website/docs/SyncVsAsync.md Co-authored-by: Erwin Heitzman <15839059+erwinheitzman@users.noreply.github.com> * TypeScript support for new Async API (#7000) * base implementation * define iterator methods * throw error if element index is out of bounds * type improvements * don't use async types for sync def * add typing tests * update deps * fix build * use rimraf to remove node_modules * revert package-lock changes * more type fixes * fix unit tests * revert package-lock changes * fix call command * attempt to get build fixed * just call npm test * try a different approach * fix typings * fix typing test * Async API blog post (#7181) * Blog post with announcement on improved async api * PR feedback * update boilerplate examples (#7192) Co-authored-by: Erwin Heitzman <15839059+erwinheitzman@users.noreply.github.com> Co-authored-by: Erwin Heitzman <15839059+erwinheitzman@users.noreply.github.com>
This commit is contained in:
parent
d0d3ebb225
commit
f9eae7a674
144 changed files with 2203 additions and 1738 deletions
|
|
@ -250,7 +250,7 @@ To make sure that we don't accidentally change the types and cause users' test t
|
|||
npm run test:typings
|
||||
```
|
||||
|
||||
This will run all the tests for all the type definitions WebdriverIO provides. These tests just check if TypeScript can compile them according to the generated type definitions. All the type checks are located in `/webdriverio/tests/typings`. If you extend a WebdriverIO command or interfaces for other type definitions, please ensure that you have used it in these files. The directory contains tests for the asynchronous usage of WebdriverIO as well as for using it synchronously with `@wdio/sync`.
|
||||
This will run all the tests for all the type definitions WebdriverIO provides. These tests just check if TypeScript can compile them according to the generated type definitions. All the type checks are located in `/webdriverio/tests/typings`. If you extend a WebdriverIO command or interfaces for other type definitions, please ensure that you have used it in these files. The directory contains tests for the asynchronous usage of WebdriverIO.
|
||||
|
||||
For example, to test the `touchActions` properties, we have it tested in `/tests/typings/webdriverio/async.ts`:
|
||||
|
||||
|
|
|
|||
1017
package-lock.json
generated
1017
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,10 @@
|
|||
"test": "run-s test:depcheck test:eslint test:typings test:coverage test:smoke",
|
||||
"test:depcheck": "node ./scripts/depcheck.js",
|
||||
"test:eslint": "eslint --cache packages examples scripts tests",
|
||||
"test:typings": "node tests/typings/setup && npm run ts && npm run clean:tests && node tests/typings/setup --ts=3.9 && npm run ts && npm run clean:tests",
|
||||
"test:typings": "run-s test:typings:*",
|
||||
"test:typings:setup": "node tests/typings/setup && cp ./node_modules/.bin/tsc ./node_modules/.bin/tsc_backup && npm i --no-save tsd",
|
||||
"test:typings:test": "npm run ts && npm run clean:tests && node tests/typings/setup --ts=3.9 && npm run ts && npm run clean:tests",
|
||||
"test:typings:recover": "mv ./node_modules/.bin/tsc_backup ./node_modules/.bin/tsc && npx rimraf @tsd/typescript",
|
||||
"test:coverage": "node --max-old-space-size=8192 ./node_modules/jest/bin/jest.js --coverage --logHeapUsage",
|
||||
"test:smoke": "ts-node ./tests/smoke.runner.js",
|
||||
"test:e2e": "jest --config e2e/jest.config.js",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
* Function to be executed after a test (in Mocha/Jasmine).
|
||||
*/<%
|
||||
if (reporters.length && reporters.includes('allure')) {%>
|
||||
afterTest: function(test, context, { error, result, duration, passed, retries }) {
|
||||
async afterTest: function(test, context, { error, result, duration, passed, retries }) {
|
||||
if (!passed) {
|
||||
browser.takeScreenshot();
|
||||
await browser.takeScreenshot();
|
||||
}
|
||||
},
|
||||
<% } else { %>
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@ const pages = {
|
|||
login: LoginPage
|
||||
}
|
||||
|
||||
Given(/^I am on the (\w+) page$/, <%= _async %>(page) => {
|
||||
<%= _await %>pages[page].open()
|
||||
Given(/^I am on the (\w+) page$/, async (page) => {
|
||||
await pages[page].open()
|
||||
});
|
||||
|
||||
When(/^I login with (\w+) and (.+)$/, <%= _async %>(username, password) => {
|
||||
<%= _await %>LoginPage.login(username, password)
|
||||
When(/^I login with (\w+) and (.+)$/, async (username, password) => {
|
||||
await LoginPage.login(username, password)
|
||||
});
|
||||
|
||||
Then(/^I should see a flash message saying (.*)$/, <%= _async %>(message) => {
|
||||
<%= _await %>expect(SecurePage.flashAlert).toBeExisting();
|
||||
<%= _await %>expect(SecurePage.flashAlert).toHaveTextContaining(message);
|
||||
Then(/^I should see a flash message saying (.*)$/, async (message) => {
|
||||
await expect(SecurePage.flashAlert).toBeExisting();
|
||||
await expect(SecurePage.flashAlert).toHaveTextContaining(message);
|
||||
});
|
||||
<% } else {
|
||||
|
||||
|
|
@ -35,23 +35,18 @@ Then(/^I should see a flash message saying (.*)$/, <%= _async %>(message) => {
|
|||
* step definition with page objects
|
||||
*/
|
||||
%>
|
||||
Given(/^I am on the (\w+) page$/, <%= _async %>(page) => {
|
||||
<%= _await %>browser.url(`https://the-internet.herokuapp.com/${page}`);
|
||||
Given(/^I am on the (\w+) page$/, async (page) => {
|
||||
await browser.url(`https://the-internet.herokuapp.com/${page}`);
|
||||
});
|
||||
|
||||
When(/^I login with (\w+) and (.+)$/, <%= _async %>(username, password) => {
|
||||
<% if (isSync) {
|
||||
%>$('#username').setValue(username);
|
||||
$('#password').setValue(password);
|
||||
$('button[type="submit"]').click(); <% } else {
|
||||
%>await (await $('#username')).setValue(username);
|
||||
await (await $('#password')).setValue(password);
|
||||
await (await $('button[type="submit"]')).click();<%
|
||||
} %>
|
||||
When(/^I login with (\w+) and (.+)$/, async (username, password) => {
|
||||
await $('#username').setValue(username);
|
||||
await $('#password').setValue(password);
|
||||
await $('button[type="submit"]').click();
|
||||
});
|
||||
|
||||
Then(/^I should see a flash message saying (.*)$/, <%= _async %>(message) => {
|
||||
<%= _await %>expect($('#flash')).toBeExisting();
|
||||
<%= _await %>expect($('#flash')).toHaveTextContaining(message);
|
||||
Then(/^I should see a flash message saying (.*)$/, async (message) => {
|
||||
await expect($('#flash')).toBeExisting();
|
||||
await expect($('#flash')).toHaveTextContaining(message);
|
||||
});
|
||||
<% } %>
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ if (usePageObjects) {
|
|||
: `const SecurePage = require('${relativePath}/secure.page');` %>
|
||||
|
||||
describe('My Login application', () => {
|
||||
it('should login with valid credentials', <%= _async %>() => {
|
||||
<%= _await %>LoginPage.open();
|
||||
it('should login with valid credentials', async () => {
|
||||
await LoginPage.open();
|
||||
|
||||
<%= _await %>LoginPage.login('tomsmith', 'SuperSecretPassword!');
|
||||
<%= _await %>expectAsync(SecurePage.flashAlert).toBeExisting();
|
||||
<%= _await %>expectAsync(SecurePage.flashAlert).toHaveTextContaining(
|
||||
await LoginPage.login('tomsmith', 'SuperSecretPassword!');
|
||||
await expectAsync(SecurePage.flashAlert).toBeExisting();
|
||||
await expectAsync(SecurePage.flashAlert).toHaveTextContaining(
|
||||
'You logged into a secure area!');
|
||||
});
|
||||
});
|
||||
|
|
@ -27,20 +27,15 @@ describe('My Login application', () => {
|
|||
* step definition with page objects
|
||||
*/
|
||||
%>describe('My Login application', () => {
|
||||
it('should login with valid credentials', <%= _async %>() => {
|
||||
<%= _await %>browser.url(`https://the-internet.herokuapp.com/login`);
|
||||
it('should login with valid credentials', async () => {
|
||||
await browser.url(`https://the-internet.herokuapp.com/login`);
|
||||
|
||||
<% if (isSync) {
|
||||
%>$('#username').setValue(username);
|
||||
$('#password').setValue(password);
|
||||
$('button[type="submit"]').click(); <% } else {
|
||||
%>await (await $('#username')).setValue('tomsmith');
|
||||
await (await $('#password')).setValue('SuperSecretPassword!');
|
||||
await (await $('button[type="submit"]')).click();<%
|
||||
} %>
|
||||
await $('#username').setValue('tomsmith');
|
||||
await $('#password').setValue('SuperSecretPassword!');
|
||||
await $('button[type="submit"]').click();
|
||||
|
||||
<%= _await %>expectAsync($('#flash')).toBeExisting();
|
||||
<%= _await %>expectAsync($('#flash')).toHaveTextContaining(
|
||||
await expectAsync($('#flash')).toBeExisting();
|
||||
await expectAsync($('#flash')).toHaveTextContaining(
|
||||
'You logged into a secure area!');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<%
|
||||
/**
|
||||
* step definition without page objects
|
||||
* test with page objects
|
||||
*/
|
||||
if (usePageObjects) {
|
||||
%><%- isUsingTypeScript || isUsingBabel
|
||||
|
|
@ -11,12 +11,12 @@ if (usePageObjects) {
|
|||
: `const SecurePage = require('${relativePath}/secure.page');` %>
|
||||
|
||||
describe('My Login application', () => {
|
||||
it('should login with valid credentials', <%= _async %>() => {
|
||||
<%= _await %>LoginPage.open();
|
||||
it('should login with valid credentials', async () => {
|
||||
await LoginPage.open();
|
||||
|
||||
<%= _await %>LoginPage.login('tomsmith', 'SuperSecretPassword!');
|
||||
<%= _await %>expect(SecurePage.flashAlert).toBeExisting();
|
||||
<%= _await %>expect(SecurePage.flashAlert).toHaveTextContaining(
|
||||
await LoginPage.login('tomsmith', 'SuperSecretPassword!');
|
||||
await expect(SecurePage.flashAlert).toBeExisting();
|
||||
await expect(SecurePage.flashAlert).toHaveTextContaining(
|
||||
'You logged into a secure area!');
|
||||
});
|
||||
});
|
||||
|
|
@ -24,23 +24,18 @@ describe('My Login application', () => {
|
|||
<% } else {
|
||||
|
||||
/**
|
||||
* step definition with page objects
|
||||
* test without page objects
|
||||
*/
|
||||
%>describe('My Login application', () => {
|
||||
it('should login with valid credentials', <%= _async %>() => {
|
||||
<%= _await %>browser.url(`https://the-internet.herokuapp.com/login`);
|
||||
it('should login with valid credentials', async () => {
|
||||
await browser.url(`https://the-internet.herokuapp.com/login`);
|
||||
|
||||
<% if (isSync) {
|
||||
%>$('#username').setValue(username);
|
||||
$('#password').setValue(password);
|
||||
$('button[type="submit"]').click(); <% } else {
|
||||
%>await (await $('#username')).setValue('tomsmith');
|
||||
await (await $('#password')).setValue('SuperSecretPassword!');
|
||||
await (await $('button[type="submit"]')).click();<%
|
||||
} %>
|
||||
await $('#username').setValue('tomsmith');
|
||||
await $('#password').setValue('SuperSecretPassword!');
|
||||
await $('button[type="submit"]').click();
|
||||
|
||||
<%= _await %>expect($('#flash')).toBeExisting();
|
||||
<%= _await %>expect($('#flash')).toHaveTextContaining(
|
||||
await expect($('#flash')).toBeExisting();
|
||||
await expect($('#flash')).toHaveTextContaining(
|
||||
'You logged into a secure area!');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,14 +17,10 @@ class LoginPage extends Page {
|
|||
* a method to encapsule automation code to interact with the page
|
||||
* e.g. to login using username and password
|
||||
*/
|
||||
<%= _async %>login (username<%= isUsingTypeScript ? ": string": "" %>, password<%= isUsingTypeScript ? ": string": "" %>) {
|
||||
<% if (isSync) {
|
||||
%>this.inputUsername.setValue(username);
|
||||
this.inputPassword.setValue(password);
|
||||
this.btnSubmit.click(); <% } else {
|
||||
%>await (await this.inputUsername).setValue(username);
|
||||
await (await this.inputPassword).setValue(password);
|
||||
await (await this.btnSubmit).click();<% } %>
|
||||
async login (username<%= isUsingTypeScript ? ": string": "" %>, password<%= isUsingTypeScript ? ": string": "" %>) {
|
||||
await this.inputUsername.setValue(username);
|
||||
await this.inputPassword.setValue(password);
|
||||
await this.btnSubmit.click();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
// ====================
|
||||
// Runner Configuration
|
||||
// ====================
|
||||
//
|
||||
// WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or
|
||||
// on a remote machine).
|
||||
runner: '<%= answers.runner %>',<%
|
||||
//<%
|
||||
|
||||
if(answers.expEnvAccessKey){ %>
|
||||
hostname: '<%= answers.expEnvHostname %>',
|
||||
|
|
@ -187,11 +184,7 @@
|
|||
// Options to be passed to Mocha.
|
||||
// See the full list at http://mochajs.org/
|
||||
mochaOpts: {
|
||||
<% if(answers.isUsingBabel) {
|
||||
%>// Babel setup
|
||||
require: ['@babel/register'],
|
||||
<% }
|
||||
%>ui: 'bdd',
|
||||
ui: 'bdd',
|
||||
timeout: 60000
|
||||
},<%
|
||||
}
|
||||
|
|
@ -199,11 +192,7 @@
|
|||
//
|
||||
// Options to be passed to Jasmine.
|
||||
jasmineOpts: {
|
||||
<% if(answers.isUsingBabel) {
|
||||
%>// Babel setup
|
||||
helpers: [require.resolve('@babel/register')],
|
||||
<% }
|
||||
%>// Jasmine default timeout
|
||||
// Jasmine default timeout
|
||||
defaultTimeoutInterval: 60000,
|
||||
//
|
||||
// The Jasmine framework allows interception of each assertion in order to log the state of the application
|
||||
|
|
@ -222,11 +211,8 @@
|
|||
// <boolean> show full backtrace for errors
|
||||
backtrace: false,
|
||||
// <string[]> ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable)
|
||||
<% if(answers.isUsingBabel) {
|
||||
%>requireModule: ['@babel/register'],
|
||||
<% } else {
|
||||
%>requireModule: [],
|
||||
<% } %>// <boolean> invoke formatters without executing steps
|
||||
requireModule: [],
|
||||
// <boolean> invoke formatters without executing steps
|
||||
dryRun: false,
|
||||
// <boolean> abort the run on first failure
|
||||
failFast: false,
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ class JasmineAdapter {
|
|||
}
|
||||
|
||||
/**
|
||||
* wrap commands with wdio-sync
|
||||
* wrap commands
|
||||
*/
|
||||
INTERFACES['bdd'].forEach((fnName) => {
|
||||
const isTest = TEST_INTERFACES.includes(fnName)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@wdio/logger": "7.7.0",
|
||||
"@wdio/types": "7.8.0"
|
||||
"@wdio/types": "7.8.0",
|
||||
"p-iteration": "^1.1.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from './utils'
|
||||
import {
|
||||
wrapCommand, runFnInFiberContext, executeHooksWithArgs,
|
||||
hasWdioSyncSupport, executeSync, executeAsync,
|
||||
hasWdioSyncSupport, executeSync, executeAsync
|
||||
} from './shim'
|
||||
import { testFnWrapper, runTestInFiberContext } from './test-framework'
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default function WebDriver (options: Record<string, any>, modifier?: Func
|
|||
|
||||
/**
|
||||
* allow to wrap commands if necessary
|
||||
* e.g. in wdio-cli to make them synchronous
|
||||
* e.g. in wdio-cli to allow element chaining
|
||||
*/
|
||||
if (typeof commandWrapper === 'function') {
|
||||
for (const [commandName, { value }] of Object.entries(propertiesObject)) {
|
||||
|
|
@ -51,7 +51,7 @@ export default function WebDriver (options: Record<string, any>, modifier?: Func
|
|||
continue
|
||||
}
|
||||
|
||||
propertiesObject[commandName].value = commandWrapper(commandName, value)
|
||||
propertiesObject[commandName].value = commandWrapper(commandName, value, propertiesObject)
|
||||
propertiesObject[commandName].configurable = true
|
||||
}
|
||||
}
|
||||
|
|
@ -165,8 +165,7 @@ export default function WebDriver (options: Record<string, any>, modifier?: Func
|
|||
const result = func.apply(this, origCommand ? [origCommand, ...args] : args)
|
||||
|
||||
/**
|
||||
* always transform result into promise as we don't know whether or not
|
||||
* the user is running tests with wdio-sync or not
|
||||
* always transform result into promise
|
||||
*/
|
||||
Promise.resolve(result).then((res) => {
|
||||
log.info('RESULT', res)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import iterators from 'p-iteration'
|
||||
import logger from '@wdio/logger'
|
||||
import { Clients } from '@wdio/types'
|
||||
|
||||
const log = logger('@wdio/utils:shim')
|
||||
|
||||
|
|
@ -11,7 +13,51 @@ interface Retries {
|
|||
attempts: number
|
||||
}
|
||||
|
||||
interface WDIOSync {
|
||||
runFnInFiberContext: any
|
||||
wrapCommand: any
|
||||
executeHooksWithArgs: any
|
||||
executeSync: any
|
||||
runSync: any
|
||||
}
|
||||
|
||||
interface PropertiesObject {
|
||||
[key: string]: PropertyDescriptor
|
||||
}
|
||||
|
||||
declare global {
|
||||
var _HAS_FIBER_CONTEXT: boolean
|
||||
}
|
||||
|
||||
const ELEMENT_QUERY_COMMANDS = ['$', '$$', 'custom$', 'custom$$', 'shadow$', 'shadow$$', 'react$', 'react$$']
|
||||
const ELEMENT_PROPS = ['elementId', 'error', 'selector', 'parent', 'index', 'isReactElement', 'length']
|
||||
|
||||
/**
|
||||
* shim to make sure that we only wrap commands if wdio-sync is installed as dependency
|
||||
*/
|
||||
let wdioSync: WDIOSync | undefined
|
||||
export let runAsync = false
|
||||
try {
|
||||
const packageName = '@wdio/sync'
|
||||
wdioSync = require(packageName)
|
||||
hasWdioSyncSupport = true
|
||||
|
||||
/**
|
||||
* only print within worker process
|
||||
*/
|
||||
if (process.send) {
|
||||
log.warn(
|
||||
'You are running tests with @wdio/sync which will be discontinued starting Node.js v16.' +
|
||||
'Read more on https://github.com/webdriverio/webdriverio/discussions/6702'
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
runAsync = true
|
||||
}
|
||||
|
||||
let executeHooksWithArgs = async function executeHooksWithArgsShim<T> (hookName: string, hooks: Function | Function[] = [], args: any[] = []): Promise<(T | Error)[]> {
|
||||
runAsync = true
|
||||
|
||||
/**
|
||||
* make sure hooks are an array of functions
|
||||
*/
|
||||
|
|
@ -60,6 +106,7 @@ let executeHooksWithArgs = async function executeHooksWithArgsShim<T> (hookName:
|
|||
|
||||
let runFnInFiberContext = function (fn: Function) {
|
||||
return function (this: any, ...args: any[]) {
|
||||
runAsync = true
|
||||
return Promise.resolve(fn.apply(this, args))
|
||||
}
|
||||
}
|
||||
|
|
@ -69,8 +116,8 @@ let runFnInFiberContext = function (fn: Function) {
|
|||
* @param commandName name of the command (e.g. getTitle)
|
||||
* @param fn command function
|
||||
*/
|
||||
let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function): (...args: any) => Promise<T> {
|
||||
return async function wrapCommandFn(this: any, ...args: any[]) {
|
||||
let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function, propertiesObject: PropertiesObject): (...args: any) => Promise<T> {
|
||||
async function wrapCommandFn(this: any, ...args: any[]) {
|
||||
const beforeHookArgs = [commandName, args]
|
||||
if (!inCommandHook && this.options.beforeCommand) {
|
||||
inCommandHook = true
|
||||
|
|
@ -99,6 +146,129 @@ let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function): (.
|
|||
|
||||
return commandResult
|
||||
}
|
||||
|
||||
function wrapElementFn (promise: Promise<Clients.Browser>, cmd: Function, args: any[], prevInnerArgs?: { prop: string | number, args: any[] }): any {
|
||||
return new Proxy(
|
||||
Promise.resolve(promise).then((ctx: Clients.Browser) => cmd.call(ctx, ...args)),
|
||||
{
|
||||
get: (target, prop: string) => {
|
||||
/**
|
||||
* if we access an index on an element array promise, e.g.:
|
||||
* ```js
|
||||
* const elems = await $$('foo')[2]
|
||||
* ```
|
||||
*/
|
||||
const numValue = parseInt(prop, 10)
|
||||
if (!isNaN(numValue)) {
|
||||
return wrapElementFn(
|
||||
target,
|
||||
/**
|
||||
* `this` is an array of WebdriverIO elements
|
||||
*
|
||||
* Note(Christian): types for elements are defined in the
|
||||
* webdriverio package and not accessible here
|
||||
*/
|
||||
function (this: object[], index: number) {
|
||||
return this[index]
|
||||
},
|
||||
[prop],
|
||||
{ prop, args }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* if we call a query method on a resolve promise, e.g.:
|
||||
* ```js
|
||||
* await $('foo').$('bar')
|
||||
* ```
|
||||
*/
|
||||
if (ELEMENT_QUERY_COMMANDS.includes(prop)) {
|
||||
return wrapCommand(prop, propertiesObject[prop].value, propertiesObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* if we call an array iterator function like map or forEach on an
|
||||
* set of elements, e.g.:
|
||||
* ```js
|
||||
* await $('body').$('header').$$('div').map((elem) => elem.getLocation())
|
||||
* ```
|
||||
*/
|
||||
if (commandName.endsWith('$$') && typeof iterators[prop as keyof typeof iterators] === 'function') {
|
||||
return (mapIterator: Function) => wrapElementFn(
|
||||
target,
|
||||
function (this: never, mapIterator: Function): any {
|
||||
// @ts-ignore
|
||||
return iterators[prop](this, mapIterator)
|
||||
},
|
||||
[mapIterator]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* allow to grab the length or other properties of fetched element set, e.g.:
|
||||
* ```js
|
||||
* const elemAmount = await $$('foo').length
|
||||
* ```
|
||||
*/
|
||||
if (ELEMENT_PROPS.includes(prop)) {
|
||||
return target.then((res) => res[prop])
|
||||
}
|
||||
|
||||
/**
|
||||
* allow to resolve an chained element query, e.g.:
|
||||
* ```js
|
||||
* const elem = await $('foo').$('bar')
|
||||
* console.log(elem.selector) // "bar"
|
||||
* ```
|
||||
*/
|
||||
if (prop === 'then') {
|
||||
return target[prop].bind(target)
|
||||
}
|
||||
|
||||
/**
|
||||
* call a command on an element query, e.g.:
|
||||
* ```js
|
||||
* const tagName = await $('foo').$('bar').getTagName()
|
||||
* ```
|
||||
*/
|
||||
return (...args: any[]) => target.then(async (elem) => {
|
||||
if (!elem) {
|
||||
let errMsg = 'Element could not be found'
|
||||
const prevElem = await promise
|
||||
if (Array.isArray(prevElem) && prevInnerArgs && prevInnerArgs.prop === 'get') {
|
||||
errMsg = `Index out of bounds! $$(${prevInnerArgs.args[0]}) returned only ${prevElem.length} elements.`
|
||||
}
|
||||
|
||||
throw new Error(errMsg)
|
||||
}
|
||||
return elem[prop](...args)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function chainElementQuery(this: Promise<Clients.Browser>, ...args: any[]): any {
|
||||
return wrapElementFn(this, wrapCommandFn, args)
|
||||
}
|
||||
|
||||
return function (this: Clients.Browser, ...args: any[]) {
|
||||
/**
|
||||
* use sync mode if:
|
||||
* - @wdio/sync package is installed and can be resolved
|
||||
* - we are in a fiber context (flag is set when outer function is wrapped into fibers context)
|
||||
*
|
||||
* also if we run command asynchronous and the command suppose to return an element, we
|
||||
* apply `chainElementQuery` to allow chaining of these promises.
|
||||
*/
|
||||
const command = hasWdioSyncSupport && wdioSync && !runAsync
|
||||
? wdioSync!.wrapCommand(commandName, fn)
|
||||
: ELEMENT_QUERY_COMMANDS.includes(commandName)
|
||||
? chainElementQuery
|
||||
: wrapCommandFn
|
||||
|
||||
return command.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,6 +283,7 @@ async function executeSyncFn (this: any, fn: Function, retries: Retries, args: a
|
|||
this.wdioRetries = retries.attempts
|
||||
|
||||
try {
|
||||
runAsync = true
|
||||
let res = fn.apply(this, args)
|
||||
|
||||
/**
|
||||
|
|
@ -127,7 +298,7 @@ async function executeSyncFn (this: any, fn: Function, retries: Retries, args: a
|
|||
} catch (e) {
|
||||
if (retries.limit > retries.attempts) {
|
||||
retries.attempts++
|
||||
return await executeSync.call(this, fn, retries, args)
|
||||
return await executeSyncFn.call(this, fn, retries, args)
|
||||
}
|
||||
|
||||
return Promise.reject(e)
|
||||
|
|
@ -146,6 +317,7 @@ async function executeAsync(this: any, fn: Function, retries: Retries, args: any
|
|||
this.wdioRetries = retries.attempts
|
||||
|
||||
try {
|
||||
runAsync = true
|
||||
return await fn.apply(this, args)
|
||||
} catch (e) {
|
||||
if (retries.limit > retries.attempts) {
|
||||
|
|
@ -160,27 +332,51 @@ async function executeAsync(this: any, fn: Function, retries: Retries, args: any
|
|||
let executeSync = executeSyncFn
|
||||
|
||||
/**
|
||||
* shim to make sure that we only wrap commands if wdio-sync is installed as dependency
|
||||
* Method to switch between sync and async execution. It allows to have async
|
||||
* tests in between synchronous tests. `fn` can either return a promise (e.g. for `executeSync`)
|
||||
* or a function (e.g. for `runSync`). In both cases we need to make sure that
|
||||
* we flip `runAsync` flag to true to that commands are wrapped with the @wdio/sync
|
||||
* wrapper.
|
||||
*/
|
||||
try {
|
||||
/**
|
||||
* only require `@wdio/sync` if `WDIO_NO_SYNC_SUPPORT` which allows us to
|
||||
* create a smoke test scenario to test actual absence of the package
|
||||
* (internal use only)
|
||||
*/
|
||||
/* istanbul ignore if */
|
||||
if (!process.env.WDIO_NO_SYNC_SUPPORT) {
|
||||
const packageName = '@wdio/sync'
|
||||
const wdioSync = require(packageName)
|
||||
hasWdioSyncSupport = true
|
||||
runFnInFiberContext = wdioSync.runFnInFiberContext
|
||||
wrapCommand = wdioSync.wrapCommand
|
||||
executeHooksWithArgs = wdioSync.executeHooksWithArgs
|
||||
executeSync = wdioSync.executeSync
|
||||
runSync = wdioSync.runSync
|
||||
export function switchSyncFlag (fn: Function) {
|
||||
return function (this: unknown, ...args: any[]) {
|
||||
const switchFlag = runAsync
|
||||
runAsync = false
|
||||
const result = fn.apply(this, args)
|
||||
|
||||
if (typeof result.finally === 'function') {
|
||||
return result.finally(() => (runAsync = switchFlag))
|
||||
}
|
||||
|
||||
if (typeof result === 'function') {
|
||||
return function (this: any, ...args: any[]) {
|
||||
const switchFlagWithinFn = runAsync
|
||||
const res = result.apply(this, args)
|
||||
if (typeof result.finally === 'function') {
|
||||
return result.finally(() => (runAsync = switchFlagWithinFn))
|
||||
}
|
||||
|
||||
runAsync = switchFlagWithinFn
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
runAsync = switchFlag
|
||||
return result
|
||||
}
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* only require `@wdio/sync` if `WDIO_NO_SYNC_SUPPORT` which allows us to
|
||||
* create a smoke test scenario to test actual absence of the package
|
||||
* (internal use only)
|
||||
*/
|
||||
/* istanbul ignore if */
|
||||
if (!process.env.WDIO_NO_SYNC_SUPPORT && hasWdioSyncSupport && wdioSync) {
|
||||
runFnInFiberContext = switchSyncFlag(wdioSync.runFnInFiberContext)
|
||||
executeHooksWithArgs = switchSyncFlag(wdioSync.executeHooksWithArgs)
|
||||
executeSync = switchSyncFlag(wdioSync.executeSync)
|
||||
runSync = switchSyncFlag(wdioSync.runSync)
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -226,4 +226,86 @@ describe('wrapCommand', () => {
|
|||
expect(scope.options!.afterCommand).toBeCalledTimes(1)
|
||||
expect(rawCommand).toBeCalledTimes(2)
|
||||
})
|
||||
|
||||
it('allows to chain element promises', async () => {
|
||||
const scope: Partial<BrowserObject> = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy')
|
||||
}
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand, propertiesObject)
|
||||
expect(await commandA.call(scope).$('foo').getTagName()).toBe('Yayy')
|
||||
})
|
||||
|
||||
it('allows to access indexed element', async () => {
|
||||
const scope: (i: number) => Partial<BrowserObject> = (i) => ({
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy' + i)
|
||||
})
|
||||
const rawCommand$ = jest.fn().mockResolvedValue(scope(0))
|
||||
const rawCommand$$ = jest.fn().mockReturnValue([
|
||||
Promise.resolve(scope(0)),
|
||||
Promise.resolve(scope(1)),
|
||||
Promise.resolve(scope(2))
|
||||
])
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand$ },
|
||||
'$$': { value: rawCommand$$ },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand$, propertiesObject)
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar')[2].getTagName()).toBe('Yayy2')
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar')[2].$('barfoo').getTagName()).toBe('Yayy0')
|
||||
})
|
||||
|
||||
it('offers array methods on elements', async () => {
|
||||
const scope: (i: number) => Partial<BrowserObject> = (i) => ({
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy' + i)
|
||||
})
|
||||
const rawCommand$ = jest.fn().mockResolvedValue(scope(0))
|
||||
const rawCommand$$ = jest.fn().mockReturnValue([
|
||||
Promise.resolve(scope(0)),
|
||||
Promise.resolve(scope(1)),
|
||||
Promise.resolve(scope(2))
|
||||
])
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand$ },
|
||||
'$$': { value: rawCommand$$ },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand$, propertiesObject)
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar').map((el) => el.getTagName()))
|
||||
.toEqual(['Yayy0', 'Yayy1', 'Yayy2'])
|
||||
})
|
||||
|
||||
it('can access element properties', async () => {
|
||||
const scope: Partial<BrowserObject> = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
},
|
||||
selector: 'foobar'
|
||||
}
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand, propertiesObject)
|
||||
expect(await commandA.call(scope).selector).toBe('foobar')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
import { executeHooksWithArgs, runFnInFiberContext, wrapCommand, hasWdioSyncSupport, executeSync, runSync } from '../src/shim'
|
||||
import { wrapCommand as wrapCommandSync } from '@wdio/sync'
|
||||
|
||||
jest.mock('@wdio/sync', () => ({
|
||||
executeHooksWithArgs: 'executeHooksWithArgs',
|
||||
runFnInFiberContext: 'runFnInFiberContext',
|
||||
wrapCommand: 'wrapCommand',
|
||||
executeSync: 'executeSync',
|
||||
runSync: 'runSync'
|
||||
executeHooksWithArgs: jest.fn().mockReturnValue('executeHooksWithArgs'),
|
||||
runFnInFiberContext: jest.fn().mockReturnValue('runFnInFiberContext'),
|
||||
wrapCommand: jest.fn().mockReturnValue(jest.fn()),
|
||||
executeSync: jest.fn().mockReturnValue('executeSync'),
|
||||
runSync: jest.fn().mockReturnValue('runSync')
|
||||
}))
|
||||
|
||||
const command = jest.fn().mockReturnValue({})
|
||||
|
||||
describe('executeHooksWithArgs', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(executeHooksWithArgs).toBe('executeHooksWithArgs')
|
||||
expect(executeHooksWithArgs.call({}, command)).toBe('executeHooksWithArgs')
|
||||
})
|
||||
})
|
||||
|
||||
describe('runFnInFiberContext', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(runFnInFiberContext).toBe('runFnInFiberContext')
|
||||
expect(runFnInFiberContext.call({}, command)).toBe('runFnInFiberContext')
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapCommand', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(wrapCommand).toBe('wrapCommand')
|
||||
global._HAS_FIBER_CONTEXT = true
|
||||
expect(wrapCommandSync).toBeCalledTimes(0)
|
||||
wrapCommand('foo', jest.fn(), {})('foo')
|
||||
expect(wrapCommandSync).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -34,12 +40,12 @@ describe('hasWdioSyncSupport', () => {
|
|||
|
||||
describe('executeSync', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(executeSync).toBe('executeSync')
|
||||
expect(executeSync.call({}, command)).toBe('executeSync')
|
||||
})
|
||||
})
|
||||
|
||||
describe('runSync', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(runSync).toBe('runSync')
|
||||
expect(runSync.call({}, command)).toBe('runSync')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { wrapCommand } from '../src/shim'
|
||||
import { wrapCommand, switchSyncFlag, runAsync } from '../src/shim'
|
||||
|
||||
describe('wrapCommand', () => {
|
||||
it('should run command with before and after hook', async () => {
|
||||
|
|
@ -42,3 +42,55 @@ describe('wrapCommand', () => {
|
|||
expect(afterHook).toBeCalledWith('someCommand', [123, 'barfoo'], undefined, error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('switchSyncFlag', () => {
|
||||
it('should switch runAsync flag', () => {
|
||||
expect(runAsync).toBe(true)
|
||||
switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return {}
|
||||
})()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a promise', async () => {
|
||||
expect(runAsync).toBe(true)
|
||||
await switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return Promise.resolve(true)
|
||||
})()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a function', () => {
|
||||
expect(runAsync).toBe(true)
|
||||
const fn = switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return () => {
|
||||
expect(runAsync).toBe(true)
|
||||
return {}
|
||||
}
|
||||
})()
|
||||
expect(runAsync).toBe(false)
|
||||
// eslint-disable-next-line
|
||||
runAsync = true
|
||||
fn()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a function with promise', async () => {
|
||||
expect(runAsync).toBe(true)
|
||||
const fn = switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return () => {
|
||||
expect(runAsync).toBe(true)
|
||||
return Promise.resolve({})
|
||||
}
|
||||
})()
|
||||
expect(runAsync).toBe(false)
|
||||
// eslint-disable-next-line
|
||||
runAsync = true
|
||||
await fn()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ export default class WebdriverMockService implements Services.ServiceInstance {
|
|||
this._mock.command.getTitle().times(Infinity).reply(200, { value: 'Mock Page Title' })
|
||||
this._mock.command.getUrl().times(Infinity).reply(200, { value: 'https://mymockpage.com' })
|
||||
this._mock.command.getElementRect(ELEMENT_ID).times(2).reply(200, { value: { width: 1, height: 2, x: 3, y: 4 } })
|
||||
this._mock.command.getElementRect(ELEMENT_ALT).times(2).reply(200, { value: { width: 10, height: 20, x: 30, y: 40 } })
|
||||
this._mock.command.getElementRect(ELEMENT_ALT).times(3).reply(200, { value: { width: 10, height: 20, x: 30, y: 40 } })
|
||||
this._mock.command.getElementRect(ELEMENT_REFETCHED).times(1).reply(200, { value: { width: 1, height: 2, x: 3, y: 4 } })
|
||||
this._mock.command.getLogTypes().reply(200, { value: [] })
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ export default class WebdriverMockService implements Services.ServiceInstance {
|
|||
this._browser.addCommand('cucumberScenario', this.cucumberScenario.bind(this))
|
||||
this._browser.addCommand('clickScenario', this.clickScenario.bind(this))
|
||||
this._browser.addCommand('isExistingScenario', this.isExistingScenario.bind(this))
|
||||
this._browser.addCommand('multiremoteFetch', this.multiremoteFetch.bind(this))
|
||||
}
|
||||
|
||||
clickScenario() {
|
||||
|
|
@ -136,6 +138,15 @@ export default class WebdriverMockService implements Services.ServiceInstance {
|
|||
this._mock.command.findElements().times(4).reply(200, { value: [elem2Response] })
|
||||
}
|
||||
|
||||
multiremoteFetch () {
|
||||
const elemResponse = { 'element-6066-11e4-a52e-4f735466cecf': ELEMENT_ID }
|
||||
const elem2Response = { 'element-6066-11e4-a52e-4f735466cecf': ELEMENT_REFETCHED }
|
||||
|
||||
this._mock.command.findElement().twice().reply(200, { value: elemResponse })
|
||||
this._mock.command.findElement().twice().reply(200, { value: elem2Response })
|
||||
this._mock.command.elementClick(ELEMENT_REFETCHED).twice().reply(200, { value: null })
|
||||
}
|
||||
|
||||
customCommandScenario(times = 1) {
|
||||
this.nockReset()
|
||||
|
||||
|
|
|
|||
|
|
@ -27,19 +27,19 @@ import type { Selector, ElementArray } from '../../types'
|
|||
<li><a href="/">Contribute</a></li>
|
||||
</ul>
|
||||
:$.js
|
||||
it('should get text a menu link', () => {
|
||||
const text = $$('#menu')[0];
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
it('should get text a menu link', async () => {
|
||||
const text = await $$('#menu')[0];
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should get text a menu link - JS Function', () => {
|
||||
const text = $$(function() { // Arrow function is not allowed here.
|
||||
it('should get text a menu link - JS Function', async () => {
|
||||
const text = await $$(function() { // Arrow function is not allowed here.
|
||||
// this is Window https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||||
// TypeScript users may do something like this
|
||||
// return (this as Window).document.querySelectorAll('#menu')
|
||||
return this.document.querySelectorAll('#menu'); // Element[]
|
||||
})[0];
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -30,39 +30,39 @@ import type { Selector } from '../../types'
|
|||
<li><a href="/">Contribute</a></li>
|
||||
</ul>
|
||||
:$.js
|
||||
it('should get text a menu link', () => {
|
||||
const text = $('#menu');
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
it('should get text a menu link', async () => {
|
||||
const text = await $('#menu');
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should get text a menu link - JS Function', () => {
|
||||
const text = $(function() { // Arrow function is not allowed here.
|
||||
it('should get text a menu link - JS Function', async () => {
|
||||
const text = await $(function() { // Arrow function is not allowed here.
|
||||
// this is Window https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||||
// TypeScript users may do something like this
|
||||
// return (this as Window).document.querySelector('#menu')
|
||||
return this.document.querySelector('#menu'); // Element
|
||||
});
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should allow to convert protocol result of an element into a WebdriverIO element', () => {
|
||||
const activeElement = browser.getActiveElement();
|
||||
console.log($(activeElement).getTagName()); // outputs active element
|
||||
it('should allow to convert protocol result of an element into a WebdriverIO element', async () => {
|
||||
const activeElement = await browser.getActiveElement();
|
||||
console.log(await $(activeElement).getTagName()); // outputs active element
|
||||
});
|
||||
|
||||
it('should use Androids DataMatcher or ViewMatcher selector', () => {
|
||||
const menuItem = $({
|
||||
it('should use Androids DataMatcher or ViewMatcher selector', async () => {
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"],
|
||||
"class": "androidx.test.espresso.matcher.ViewMatchers"
|
||||
});
|
||||
menuItem.click();
|
||||
await menuItem.click();
|
||||
|
||||
const menuItem = $({
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"]
|
||||
});
|
||||
menuItem.click();
|
||||
await menuItem.click();
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,18 +9,18 @@
|
|||
*
|
||||
* <example>
|
||||
:execute.js
|
||||
browser.addCommand('getUrlAndTitle', function (customParam) {
|
||||
await browser.addCommand('getUrlAndTitle', async function (customParam) {
|
||||
// `this` refers to the `browser` scope
|
||||
return {
|
||||
url: this.getUrl(),
|
||||
title: this.getTitle(),
|
||||
url: await this.getUrl(),
|
||||
title: await this.getTitle(),
|
||||
customParam: customParam
|
||||
}
|
||||
})
|
||||
//usage
|
||||
it('should use my add command', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
const result = browser.getUrlAndTitle('foobar')
|
||||
it('should use my add command', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
const result = await browser.getUrlAndTitle('foobar')
|
||||
|
||||
assert.strictEqual(result.url, 'https://webdriver.io')
|
||||
assert.strictEqual(result.title, 'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
|
||||
|
|
|
|||
|
|
@ -3,19 +3,24 @@
|
|||
* is treated like a synchronous function. It accepts promises and stops the execution until
|
||||
* the promise has resolved.
|
||||
*
|
||||
* This command helps to run asynchronous code within a synchronous context. With
|
||||
* WebdriverIO depcrecating synchronous usage (see [RFC](https://github.com/webdriverio/webdriverio/discussions/6702))
|
||||
* this command is not very useful anymore.
|
||||
*
|
||||
* <example>
|
||||
:call.js
|
||||
it('some testing here', () => {
|
||||
browser.url('http://google.com')
|
||||
it('some testing here', async () => {
|
||||
await browser.url('http://google.com')
|
||||
// make an asynchronous call using any 3rd party library supporting promises
|
||||
// e.g. call to backend or db to inject fixture data
|
||||
browser.call(() => {
|
||||
await browser.call(() => {
|
||||
return somePromiseLibrary.someMethod().then(() => {
|
||||
// ...
|
||||
})
|
||||
})
|
||||
|
||||
// example for async call to 3rd party library that doesn't support promises
|
||||
browser.call(() => {
|
||||
const result = await browser.call(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
someOtherNodeLibrary.someMethod(param1, (err, res) => {
|
||||
if (err) {
|
||||
|
|
@ -25,9 +30,6 @@
|
|||
})
|
||||
})
|
||||
})
|
||||
// continue synchronously
|
||||
$('#elemA').click()
|
||||
$('.firstname').setValue('webdriverbot')
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
@ -36,7 +38,7 @@
|
|||
* @type utility
|
||||
*
|
||||
*/
|
||||
export default function call<T> (fn: () => T) {
|
||||
export default function call<T> (fn: () => T): T | Promise<T> {
|
||||
if (typeof fn === 'function') {
|
||||
return fn()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ import type { ElementArray } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:example.js
|
||||
it('should get all the plugin wrapper buttons', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
it('should get all the plugin wrapper buttons', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
return document.querySelectorAll(selector)
|
||||
})
|
||||
|
||||
const pluginWrapper = browser.custom$$('myStrat', '.pluginWrapper')
|
||||
const pluginWrapper = await browser.custom$$('myStrat', '.pluginWrapper')
|
||||
|
||||
console.log(pluginWrapper.length) // 4
|
||||
console.log(await pluginWrapper.length) // 4
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import { ELEMENT_KEY } from '../../constants'
|
|||
*
|
||||
* <example>
|
||||
:example.js
|
||||
it('should fetch the project title', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
it('should fetch the project title', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
return document.querySelectorAll(selector)
|
||||
})
|
||||
|
||||
const projectTitle = browser.custom$('myStrat', '.projectTitle')
|
||||
const projectTitle = await browser.custom$('myStrat', '.projectTitle')
|
||||
|
||||
console.log(projectTitle.getText()) // WEBDRIVER I/O
|
||||
console.log(await projectTitle.getText()) // WEBDRIVER I/O
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ import WDIORepl from '@wdio/repl'
|
|||
*
|
||||
* <example>
|
||||
:debug.js
|
||||
it('should demonstrate the debug command', () => {
|
||||
$('#input').setValue('FOO')
|
||||
browser.debug() // jumping into the browser and change value of #input to 'BAR'
|
||||
const value = $('#input').getValue()
|
||||
it('should demonstrate the debug command', async () => {
|
||||
await $('#input').setValue('FOO')
|
||||
await browser.debug() // jumping into the browser and change value of #input to 'BAR'
|
||||
const value = await $('#input').getValue()
|
||||
console.log(value) // outputs: "BAR"
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
*
|
||||
* <example>
|
||||
:deleteCookie.js
|
||||
it('should delete cookies', () => {
|
||||
browser.setCookies([
|
||||
it('should delete cookies', async () => {
|
||||
await browser.setCookies([
|
||||
{name: 'test', value: '123'},
|
||||
{name: 'test2', value: '456'},
|
||||
{name: 'test3', value: '789'}
|
||||
])
|
||||
|
||||
let cookies = browser.getCookies()
|
||||
let cookies = await browser.getCookies()
|
||||
console.log(cookies)
|
||||
// outputs:
|
||||
// [
|
||||
|
|
@ -20,8 +20,8 @@
|
|||
// { name: 'test3', value: '789' }
|
||||
// ]
|
||||
|
||||
browser.deleteCookies(['test3'])
|
||||
cookies = browser.getCookies()
|
||||
await browser.deleteCookies(['test3'])
|
||||
cookies = await browser.getCookies()
|
||||
console.log(cookies)
|
||||
// outputs:
|
||||
// [
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
// { name: 'test2', value: '456' }
|
||||
// ]
|
||||
|
||||
browser.deleteCookies()
|
||||
cookies = browser.getCookies()
|
||||
await browser.deleteCookies()
|
||||
cookies = await browser.getCookies()
|
||||
console.log(cookies) // outputs: []
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import { verifyArgsAndStripIfElement } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:execute.js
|
||||
it('should inject javascript on the page', () => {
|
||||
const result = browser.execute((a, b, c, d) => {
|
||||
it('should inject javascript on the page', async () => {
|
||||
const result = await browser.execute((a, b, c, d) => {
|
||||
// browser context - you may not access client or console
|
||||
return a + b + c + d
|
||||
}, 1, 2, 3, 4)
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { verifyArgsAndStripIfElement } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:executeAsync.js
|
||||
it('should execute async JavaScript on the page', () => {
|
||||
browser.setTimeout({ script: 5000 })
|
||||
const result = browser.executeAsync(function(a, b, c, d, done) {
|
||||
it('should execute async JavaScript on the page', async () => {
|
||||
await browser.setTimeout({ script: 5000 })
|
||||
const result = await browser.executeAsync(function(a, b, c, d, done) {
|
||||
// browser context - you may not access client or console
|
||||
setTimeout(() => {
|
||||
done(a + b + c + d)
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@
|
|||
*
|
||||
* <example>
|
||||
:getCookies.js
|
||||
it('should return a cookie for me', () => {
|
||||
browser.setCookies([
|
||||
it('should return a cookie for me', async () => {
|
||||
await browser.setCookies([
|
||||
{name: 'test', value: '123'},
|
||||
{name: 'test2', value: '456'}
|
||||
])
|
||||
const testCookie = browser.getCookies(['test'])
|
||||
const testCookie = await browser.getCookies(['test'])
|
||||
console.log(testCookie); // outputs: [{ name: 'test', value: '123' }]
|
||||
|
||||
const allCookies = browser.getCookies()
|
||||
const allCookies = await browser.getCookies()
|
||||
console.log(allCookies);
|
||||
// outputs:
|
||||
// [
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ const log = logger('webdriverio')
|
|||
*
|
||||
* <example>
|
||||
:getPuppeteer.test.js
|
||||
it('should allow me to use Puppeteer', () => {
|
||||
it('should allow me to use Puppeteer', async () => {
|
||||
// WebDriver command
|
||||
browser.url('https://webdriver.io')
|
||||
await browser.url('https://webdriver.io')
|
||||
|
||||
const puppeteerBrowser = browser.getPuppeteer()
|
||||
const puppeteerBrowser = await browser.getPuppeteer()
|
||||
// switch to Puppeteer
|
||||
const metrics = browser.call(async () => {
|
||||
const pages = await puppeteerBrowser.pages()
|
||||
const metrics = await browser.call(async () => {
|
||||
await pages = await puppeteerBrowser.pages()
|
||||
pages[0].setGeolocation({ latitude: 59.95, longitude: 30.31667 })
|
||||
return pages[0].metrics()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ interface BrowserSize {
|
|||
*
|
||||
* <example>
|
||||
:getWindowSize.js
|
||||
it('should return browser window size', function () {
|
||||
const windowSize = browser.getWindowSize();
|
||||
it('should return browser window size', async () => {
|
||||
const windowSize = await browser.getWindowSize();
|
||||
console.log(windowSize);
|
||||
// outputs `{ width: 1280, height: 767 }`
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ import { checkUnicode } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:keys.js
|
||||
it('copies text out of active element', () => {
|
||||
it('copies text out of active element', async () => {
|
||||
// copies text from an input element
|
||||
const input = $('#username')
|
||||
input.setValue('anonymous')
|
||||
const input = await $('#username')
|
||||
await input.setValue('anonymous')
|
||||
|
||||
browser.keys(['Meta', 'a'])
|
||||
browser.keys(['Meta', 'c'])
|
||||
await browser.keys(['Meta', 'a'])
|
||||
await browser.keys(['Meta', 'c'])
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -31,15 +31,15 @@ export const SESSION_MOCKS: Record<string, Set<Interception>> = {}
|
|||
*
|
||||
* <example>
|
||||
:mock.js
|
||||
it('should mock network resources', () => {
|
||||
it('should mock network resources', async () => {
|
||||
// via static string
|
||||
const userListMock = browser.mock('**' + '/users/list')
|
||||
const userListMock = await browser.mock('**' + '/users/list')
|
||||
// or as regular expression
|
||||
const userListMock = browser.mock(/https:\/\/(domainA|domainB)\.com\/.+/)
|
||||
const userListMock = await browser.mock(/https:\/\/(domainA|domainB)\.com\/.+/)
|
||||
// you can also specifying the mock even more by filtering resources
|
||||
// by request or response headers, status code, postData, e.g. mock only responses with specific
|
||||
// header set and statusCode
|
||||
const strictMock = browser.mock('**', {
|
||||
const strictMock = await browser.mock('**', {
|
||||
// mock all json responses
|
||||
statusCode: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -48,7 +48,7 @@ export const SESSION_MOCKS: Record<string, Set<Interception>> = {}
|
|||
})
|
||||
|
||||
// comparator function
|
||||
const apiV1Mock = browser.mock('**' + '/api/v1', {
|
||||
const apiV1Mock = await browser.mock('**' + '/api/v1', {
|
||||
statusCode: (statusCode) => statusCode >= 200 && statusCode <= 203,
|
||||
headers: (headers) => headers['Authorization'] && headers['Authorization'].startsWith('Bearer '),
|
||||
responseHeaders: (headers) => headers['Impersonation'],
|
||||
|
|
@ -56,9 +56,9 @@ export const SESSION_MOCKS: Record<string, Set<Interception>> = {}
|
|||
})
|
||||
})
|
||||
|
||||
it('should modify API responses', () => {
|
||||
it('should modify API responses', async () => {
|
||||
// filter by method
|
||||
const todoMock = browser.mock('**' + '/todos', {
|
||||
const todoMock = await browser.mock('**' + '/todos', {
|
||||
method: 'get'
|
||||
})
|
||||
|
||||
|
|
@ -84,19 +84,19 @@ export const SESSION_MOCKS: Record<string, Set<Interception>> = {}
|
|||
})
|
||||
})
|
||||
|
||||
it('should modify text assets', () => {
|
||||
const scriptMock = browser.mock('**' + '/script.min.js')
|
||||
it('should modify text assets', async () => {
|
||||
const scriptMock = await browser.mock('**' + '/script.min.js')
|
||||
scriptMock.respond('./tests/fixtures/script.js')
|
||||
})
|
||||
|
||||
it('should redirect web resources', () => {
|
||||
const headerMock = browser.mock('**' + '/header.png')
|
||||
it('should redirect web resources', async () => {
|
||||
const headerMock = await browser.mock('**' + '/header.png')
|
||||
headerMock.respond('https://media.giphy.com/media/F9hQLAVhWnL56/giphy.gif')
|
||||
|
||||
const pageMock = browser.mock('https://google.com/')
|
||||
const pageMock = await browser.mock('https://google.com/')
|
||||
pageMock.respond('https://webdriver.io')
|
||||
browser.url('https://google.com')
|
||||
console.log(browser.getTitle()) // returns "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
|
||||
await browser.url('https://google.com')
|
||||
console.log(await browser.getTitle()) // returns "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,19 +9,21 @@ const log = logger('webdriverio:mockClearAll')
|
|||
*
|
||||
* <example>
|
||||
:mockClearAll.js
|
||||
it('should clear all mocks', () => {
|
||||
const docMock = browser.mock('**', {
|
||||
it('should clear all mocks', async () => {
|
||||
const docMock = await browser.mock('**', {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
})
|
||||
const jsMock = browser.mock('**', {
|
||||
const jsMock = await browser.mock('**', {
|
||||
headers: { 'Content-Type': 'application/javascript' }
|
||||
})
|
||||
|
||||
browser.url('http://guinea-pig.webdriver.io/')
|
||||
await browser.url('http://guinea-pig.webdriver.io/')
|
||||
console.log(docMock.calls.length, jsMock.calls.length) // returns "1 4"
|
||||
browser.url('http://guinea-pig.webdriver.io/')
|
||||
|
||||
await browser.url('http://guinea-pig.webdriver.io/')
|
||||
console.log(docMock.calls.length, jsMock.calls.length) // returns "2 4" (JavaScript comes from cache)
|
||||
browser.mockClearAll()
|
||||
|
||||
await browser.mockClearAll()
|
||||
console.log(docMock.calls.length, jsMock.calls.length) // returns "0 0"
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@ const log = logger('webdriverio:mockRestoreAll')
|
|||
*
|
||||
* <example>
|
||||
:mockRestoreAll.js
|
||||
it('should restore all mocks', () => {
|
||||
const googleMock = browser.mock('https://google.com/')
|
||||
it('should restore all mocks', async () => {
|
||||
const googleMock = await browser.mock('https://google.com/')
|
||||
googleMock.respond('https://webdriver.io')
|
||||
const wdioMock = browser.mock('https://webdriver.io')
|
||||
const wdioMock = await browser.mock('https://webdriver.io')
|
||||
wdioMock.respond('http://json.org')
|
||||
|
||||
browser.url('https://google.com/')
|
||||
console.log(browser.getTitle()) // JSON
|
||||
await browser.url('https://google.com/')
|
||||
console.log(await browser.getTitle()) // JSON
|
||||
|
||||
browser.mockRestoreAll()
|
||||
await browser.mockRestoreAll()
|
||||
|
||||
browser.url('https://google.com/')
|
||||
console.log(browser.getTitle()) // Google
|
||||
await browser.url('https://google.com/')
|
||||
console.log(await browser.getTitle()) // Google
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,21 +10,21 @@ import type { NewWindowOptions } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:newWindowSync.js
|
||||
it('should open a new tab', () => {
|
||||
browser.url('https://google.com')
|
||||
console.log(browser.getTitle()) // outputs: "Google"
|
||||
it('should open a new tab', async () => {
|
||||
await browser.url('https://google.com')
|
||||
console.log(await browser.getTitle()) // outputs: "Google"
|
||||
|
||||
browser.newWindow('https://webdriver.io', {
|
||||
await browser.newWindow('https://webdriver.io', {
|
||||
windowName: 'WebdriverIO window',
|
||||
windowFeature: 'width=420,height=230,resizable,scrollbars=yes,status=1',
|
||||
})
|
||||
console.log(browser.getTitle()) // outputs: "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
|
||||
console.log(await browser.getTitle()) // outputs: "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
|
||||
|
||||
const handles = browser.getWindowHandles()
|
||||
browser.switchToWindow(handles[1])
|
||||
browser.closeWindow()
|
||||
browser.switchToWindow(handles[0])
|
||||
console.log(browser.getTitle()) // outputs: "Google"
|
||||
const handles = await browser.getWindowHandles()
|
||||
await browser.switchToWindow(handles[1])
|
||||
await browser.closeWindow()
|
||||
await browser.switchToWindow(handles[0])
|
||||
console.log(await browser.getTitle()) // outputs: "Google"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@
|
|||
* <example>
|
||||
:execute.js
|
||||
// print milliseconds before pause and return its value.
|
||||
browser.overwriteCommand('pause', function (origPauseFunction, ms) {
|
||||
await browser.overwriteCommand('pause', function (origPauseFunction, ms) {
|
||||
console.log(`Sleeping for ${ms}`)
|
||||
origPauseFunction(ms)
|
||||
return ms
|
||||
})
|
||||
|
||||
// usage
|
||||
it('should use my overwrite command', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
browser.pause(1000) // outputs "Sleeping for 1000"
|
||||
it('should use my overwrite command', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await browser.pause(1000) // outputs "Sleeping for 1000"
|
||||
})
|
||||
* </example>
|
||||
* @alias browser.overwriteCommand
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
*
|
||||
* <example>
|
||||
:pause.js
|
||||
it('should pause the execution', () => {
|
||||
it('should pause the execution', async () => {
|
||||
const starttime = new Date().getTime()
|
||||
browser.pause(3000)
|
||||
await browser.pause(3000)
|
||||
const endtime = new Date().getTime()
|
||||
console.log(endtime - starttime) // outputs: 3000
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ const resqScript = fs.readFileSync(require.resolve('resq'))
|
|||
*
|
||||
* <example>
|
||||
:pause.js
|
||||
it('should calculate 7 * 6', () => {
|
||||
browser.url('https://ahfarmer.github.io/calculator/');
|
||||
it('should calculate 7 * 6', async () => {
|
||||
await browser.url('https://ahfarmer.github.io/calculator/');
|
||||
|
||||
const orangeButtons = browser.react$$('t', {
|
||||
const orangeButtons = await browser.react$$('t', {
|
||||
props: { orange: true }
|
||||
})
|
||||
console.log(orangeButtons.map((btn) => btn.getText())); // prints "[ '÷', 'x', '-', '+', '=' ]"
|
||||
console.log(await orangeButtons.map((btn) => btn.getText()));
|
||||
// prints "[ '÷', 'x', '-', '+', '=' ]"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -21,24 +21,24 @@ const resqScript = fs.readFileSync(require.resolve('resq'))
|
|||
*
|
||||
* <example>
|
||||
:pause.js
|
||||
it('should calculate 7 * 6', () => {
|
||||
browser.url('https://ahfarmer.github.io/calculator/');
|
||||
const appWrapper = browser.$('div#root')
|
||||
it('should calculate 7 * 6', async () => {
|
||||
await browser.url('https://ahfarmer.github.io/calculator/');
|
||||
const appWrapper = await $('div#root')
|
||||
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '7' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: 'x' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '6' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '=' }
|
||||
}).click()
|
||||
|
||||
console.log($('.component-display').getText()); // prints "42"
|
||||
console.log(await $('.component-display').getText()); // prints "42"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ const log = logger('webdriverio')
|
|||
*
|
||||
* <example>
|
||||
:reloadSync.js
|
||||
it('should reload my session with current capabilities', () => {
|
||||
it('should reload my session with current capabilities', async () => {
|
||||
console.log(browser.sessionId) // outputs: e042b3f3cd5a479da4e171825e96e655
|
||||
browser.reloadSession()
|
||||
await browser.reloadSession()
|
||||
console.log(browser.sessionId) // outputs: 9a0d9bf9d4864160aa982c50cf18a573
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type PDFPrintOptions = {
|
|||
* <example>
|
||||
:savePDF.js
|
||||
it('should save a PDF screenshot of the browser view', function () {
|
||||
browser.savePDF('./some/path/screenshot.pdf');
|
||||
await browser.savePDF('./some/path/screenshot.pdf');
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import { getAbsoluteFilepath, assertDirectoryExists } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:saveRecordingScreen.js
|
||||
it('should save a video', () => {
|
||||
browser.startRecordingScreen();
|
||||
$('~BUTTON').click();
|
||||
browser.saveRecordingScreen('./some/path/video.mp4');
|
||||
it('should save a video', async () => {
|
||||
await browser.startRecordingScreen();
|
||||
await $('~BUTTON').click();
|
||||
await browser.saveRecordingScreen('./some/path/video.mp4');
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import { getAbsoluteFilepath, assertDirectoryExists } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:saveScreenshot.js
|
||||
it('should save a screenshot of the browser view', function () {
|
||||
browser.saveScreenshot('./some/path/screenshot.png');
|
||||
it('should save a screenshot of the browser view', async () => {
|
||||
await browser.saveScreenshot('./some/path/screenshot.png');
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import type { Cookie } from '@wdio/protocols'
|
|||
*
|
||||
* <example>
|
||||
:setCookies.js
|
||||
it('should set a cookie for the page', () => {
|
||||
browser.url('/')
|
||||
it('should set a cookie for the page', async () => {
|
||||
await browser.url('/')
|
||||
|
||||
// set a single cookie
|
||||
browser.setCookies({
|
||||
await browser.setCookies({
|
||||
name: 'test1',
|
||||
value: 'one'
|
||||
// The below options are optional
|
||||
|
|
@ -24,12 +24,12 @@ import type { Cookie } from '@wdio/protocols'
|
|||
})
|
||||
|
||||
// set multiple cookies
|
||||
browser.setCookies([
|
||||
await browser.setCookies([
|
||||
{name: 'test2', value: 'two'},
|
||||
{name: 'test3', value: 'three'}
|
||||
])
|
||||
|
||||
const cookies = browser.getCookies()
|
||||
const cookies = await browser.getCookies()
|
||||
console.log(cookies);
|
||||
// outputs:
|
||||
// [
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import type { Timeouts } from '@wdio/protocols'
|
|||
*
|
||||
* <example>
|
||||
:setTimeout.js
|
||||
it('should change timeout duration for session with long code duration', () => {
|
||||
browser.setTimeout({
|
||||
it('should change timeout duration for session with long code duration', async () => {
|
||||
await browser.setTimeout({
|
||||
'pageLoad': 10000,
|
||||
'script': 60000
|
||||
});
|
||||
// Execute code which takes a long time
|
||||
browser.executeAsync((done) => {
|
||||
await browser.executeAsync((done) => {
|
||||
console.log('Wake me up before you go!');
|
||||
setTimeout(done, 59000);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ const maxWindowSize = Number.MAX_SAFE_INTEGER
|
|||
*
|
||||
* <example>
|
||||
* :setWindowSize.js
|
||||
it('should re-size browser outer window with 500 width and 600 height', function () {
|
||||
browser.setWindowSize(500, 600);
|
||||
it('should re-size browser outer window with 500 width and 600 height', async () => {
|
||||
await browser.setWindowSize(500, 600);
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@
|
|||
*
|
||||
* <example>
|
||||
:switchWindow.js
|
||||
it('should switch to another window', () => {
|
||||
it('should switch to another window', async () => {
|
||||
// open url
|
||||
browser.url('https://google.com')
|
||||
await browser.url('https://google.com')
|
||||
|
||||
// create new window
|
||||
browser.newWindow('https://webdriver.io')
|
||||
await browser.newWindow('https://webdriver.io')
|
||||
|
||||
// switch back via url match
|
||||
browser.switchWindow('google.com')
|
||||
await browser.switchWindow('google.com')
|
||||
|
||||
// switch back via title match
|
||||
browser.switchWindow('Next-gen browser and mobile automation test framework for Node.js')
|
||||
await browser.switchWindow('Next-gen browser and mobile automation test framework for Node.js')
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@
|
|||
*
|
||||
* <example>
|
||||
:throttle.js
|
||||
it('should throttle the network', () => {
|
||||
it('should throttle the network', async () => {
|
||||
// via static string preset
|
||||
browser.throttle('Regular 3G')
|
||||
await browser.throttle('Regular 3G')
|
||||
|
||||
// via custom values
|
||||
browser.throttle({
|
||||
'offline': false,
|
||||
'downloadThroughput': 200 * 1024 / 8,
|
||||
'uploadThroughput': 200 * 1024 / 8,
|
||||
'latency': 20
|
||||
await browser.throttle({
|
||||
offline: false,
|
||||
downloadThroughput: 200 * 1024 / 8,
|
||||
uploadThroughput: 200 * 1024 / 8,
|
||||
latency: 20
|
||||
})
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -17,18 +17,18 @@ import { TouchActions } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:touchAction.js
|
||||
it('should do a touch gesture', function () {
|
||||
const screen = $('//UITextbox');
|
||||
it('should do a touch gesture', async () => {
|
||||
const screen = await $('//UITextbox');
|
||||
|
||||
// simple touch action on element
|
||||
browser.touchAction({
|
||||
await browser.touchAction({
|
||||
action: 'tap',
|
||||
element: screen
|
||||
});
|
||||
|
||||
// simple touch action x y variables
|
||||
// tap location is 30px right and 20px down relative from the viewport
|
||||
browser.touchAction({
|
||||
await browser.touchAction({
|
||||
action: 'tap',
|
||||
x: 30,
|
||||
y:20
|
||||
|
|
@ -36,7 +36,7 @@ import { TouchActions } from '../../types'
|
|||
|
||||
// simple touch action x y variables
|
||||
// tap location is 30px right and 20px down relative from the center of the element
|
||||
browser.touchAction({
|
||||
await browser.touchAction({
|
||||
action: 'tap',
|
||||
x: 30,
|
||||
y:20,
|
||||
|
|
@ -45,7 +45,7 @@ import { TouchActions } from '../../types'
|
|||
|
||||
// multi action on an element
|
||||
// drag&drop from position 200x200 down 100px on the screen
|
||||
browser.touchAction([
|
||||
await browser.touchAction([
|
||||
{ action: 'press', x: 200, y: 200 },
|
||||
{ action: 'moveTo', x: 200, y: 300 },
|
||||
'release'
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ import type { Capabilities } from '@wdio/types'
|
|||
:uploadFile.js
|
||||
const path = require('path');
|
||||
|
||||
it('should upload a file', function () {
|
||||
browser.url('https://the-internet.herokuapp.com/upload')
|
||||
it('should upload a file', async () => {
|
||||
await browser.url('https://the-internet.herokuapp.com/upload')
|
||||
|
||||
const filePath = '/path/to/some/file.png'
|
||||
const remoteFilePath = browser.uploadFile(filePath)
|
||||
const remoteFilePath = await browser.uploadFile(filePath)
|
||||
|
||||
$('#file-upload').setValue(remoteFilePath)
|
||||
$('#file-submit').click()
|
||||
await $('#file-upload').setValue(remoteFilePath)
|
||||
await $('#file-submit').click()
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,21 +10,23 @@ import { validateUrl } from '../../utils'
|
|||
* <example>
|
||||
:url.js
|
||||
// navigate to a new URL
|
||||
browser.url('https://webdriver.io');
|
||||
await browser.url('https://webdriver.io');
|
||||
// receive url
|
||||
console.log(browser.getUrl()); // outputs: "https://webdriver.io"
|
||||
console.log(await browser.getUrl()); // outputs: "https://webdriver.io"
|
||||
|
||||
:baseUrlResolutions.js
|
||||
// With a base URL of http://example.com/site, the following url parameters resolve as such:
|
||||
// When providing a scheme:
|
||||
// https://webdriver.io
|
||||
browser.url('https://webdriver.io');
|
||||
await browser.url('https://webdriver.io');
|
||||
|
||||
// When not starting with a slash, the URL resolves relative to the baseUrl
|
||||
// http://example.com/site/relative
|
||||
browser.url('relative');
|
||||
await browser.url('relative');
|
||||
|
||||
// When starting with a slash, the URL resolves relative to the root path of the baseUrl
|
||||
// http://example.com/rootRelative
|
||||
browser.url('/rootRelative');
|
||||
await browser.url('/rootRelative');
|
||||
* </example>
|
||||
*
|
||||
* @param {String=} url the URL to navigate to
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ import type { WaitUntilOptions } from '../../types'
|
|||
<div id="someText">I am some text</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
$('#someText').html('I am now different');
|
||||
await $('#someText').html('I am now different');
|
||||
}, 1000);
|
||||
</script>
|
||||
|
||||
:waitUntil.js
|
||||
it('should wait until text has changed', () => {
|
||||
browser.waitUntil(
|
||||
() => $('#someText').getText() === 'I am now different',
|
||||
it('should wait until text has changed', async () => {
|
||||
await browser.waitUntil(
|
||||
async () => (await $('#someText').getText()) === 'I am now different',
|
||||
{
|
||||
timeout: 5000,
|
||||
timeoutMsg: 'expected text to be different after 5s'
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@
|
|||
<li><a href="/">Contribute</a></li>
|
||||
</ul>
|
||||
:$.js
|
||||
it('should get text a menu link', () => {
|
||||
const text = $('#menu');
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
it('should get text a menu link', async () => {
|
||||
const text = await $('#menu');
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should get text a menu link - JS Function', () => {
|
||||
const text = $('#menu');
|
||||
console.log(text.$$(function() { // Arrow function is not allowed here.
|
||||
it('should get text a menu link - JS Function', async () => {
|
||||
const text = await $('#menu');
|
||||
console.log(await text.$$(function() { // Arrow function is not allowed here.
|
||||
// this is Element https://developer.mozilla.org/en-US/docs/Web/API/Element
|
||||
// in this particular example it is HTMLUListElement
|
||||
// TypeScript users may do something like this
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@
|
|||
<li><a href="/">Contribute</a></li>
|
||||
</ul>
|
||||
:$.js
|
||||
it('should get text a menu link', () => {
|
||||
const text = $('#menu');
|
||||
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
it('should get text a menu link', async () => {
|
||||
const text = await $('#menu');
|
||||
console.log(await text.$$('li')[2].$('a').getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should get text a menu link - JS Function', () => {
|
||||
const text = $('#menu');
|
||||
console.log(text.$$('li')[2].$(function() { // Arrow function is not allowed here.
|
||||
it('should get text a menu link - JS Function', async () => {
|
||||
const text = await $('#menu');
|
||||
console.log(await text.$$('li')[2].$(function() { // Arrow function is not allowed here.
|
||||
// this is Element https://developer.mozilla.org/en-US/docs/Web/API/Element
|
||||
// in this particular example it is HTMLLIElement
|
||||
// TypeScript users may do something like this
|
||||
|
|
@ -40,24 +40,24 @@
|
|||
}).getText()); // outputs: "API"
|
||||
});
|
||||
|
||||
it('should allow to convert protocol result of an element into a WebdriverIO element', () => {
|
||||
const activeElement = browser.getActiveElement();
|
||||
console.log($(activeElement).getTagName()); // outputs active element
|
||||
it('should allow to convert protocol result of an element into a WebdriverIO element', async () => {
|
||||
const activeElement = await browser.getActiveElement();
|
||||
console.log(await $(activeElement).getTagName()); // outputs active element
|
||||
});
|
||||
|
||||
it('should use Androids DataMatcher or ViewMatcher selector', () => {
|
||||
const menuItem = $({
|
||||
it('should use Androids DataMatcher or ViewMatcher selector', async () => {
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"],
|
||||
"class": "androidx.test.espresso.matcher.ViewMatchers"
|
||||
});
|
||||
menuItem.click();
|
||||
await menuItem.click();
|
||||
|
||||
const menuItem = $({
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"]
|
||||
});
|
||||
menuItem.click();
|
||||
await menuItem.click();
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ const isValidType = (value: unknown) => (
|
|||
*
|
||||
* <example>
|
||||
:addValue.js
|
||||
it('should demonstrate the addValue command', () => {
|
||||
let input = $('.input')
|
||||
input.addValue('test')
|
||||
input.addValue(123)
|
||||
it('should demonstrate the addValue command', async () => {
|
||||
let input = await $('.input')
|
||||
await input.addValue('test')
|
||||
await input.addValue(123)
|
||||
|
||||
value = input.getValue()
|
||||
value = await input.getValue()
|
||||
assert(value === 'test123') // true
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@
|
|||
*
|
||||
* <example>
|
||||
:clearValue.js
|
||||
it('should demonstrate the clearValue command', function () {
|
||||
const elem = $('.input')
|
||||
elem.setValue('test123')
|
||||
it('should demonstrate the clearValue command', async () => {
|
||||
const elem = await $('.input')
|
||||
await elem.setValue('test123')
|
||||
|
||||
const value = elem.getValue()
|
||||
const value = await elem.getValue()
|
||||
console.log(value) // returns 'test123'
|
||||
|
||||
elem.clearValue()
|
||||
value = elem.getValue()
|
||||
await elem.clearValue()
|
||||
value = await elem.getValue()
|
||||
assert(value === ''); // true
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -22,18 +22,18 @@ import { ClickOptions } from '../../types'
|
|||
<button id="myButton" onclick="document.getElementById('someText').innerHTML='I was clicked'">Click me</button>
|
||||
<div id="someText">I was not clicked</div>
|
||||
:click.js
|
||||
it('should demonstrate the click command', () => {
|
||||
const myButton = $('#myButton')
|
||||
myButton.click()
|
||||
const myText = $('#someText')
|
||||
const text = myText.getText()
|
||||
it('should demonstrate the click command', async () => {
|
||||
const myButton = await $('#myButton')
|
||||
await myButton.click()
|
||||
const myText = await $('#someText')
|
||||
const text = await myText.getText()
|
||||
assert(text === 'I was clicked') // true
|
||||
})
|
||||
:example.js
|
||||
it('should fetch menu links and visit each page', () => {
|
||||
const links = $$('#menu a')
|
||||
links.forEach((link) => {
|
||||
link.click()
|
||||
it('should fetch menu links and visit each page', async () => {
|
||||
const links = await $$('#menu a')
|
||||
await links.forEach(async (link) => {
|
||||
await link.click()
|
||||
})
|
||||
})
|
||||
* </example>
|
||||
|
|
@ -42,9 +42,9 @@ import { ClickOptions } from '../../types'
|
|||
:example.html
|
||||
<button id="myButton">Click me</button>
|
||||
:example.js
|
||||
it('should demonstrate a click using an offset', () => {
|
||||
const myButton = $('#myButton')
|
||||
myButton.click({ x: 30 }) // clicks 30 horizontal pixels away from location of the button (from center point of element)
|
||||
it('should demonstrate a click using an offset', async () => {
|
||||
const myButton = await $('#myButton')
|
||||
await myButton.click({ x: 30 }) // clicks 30 horizontal pixels away from location of the button (from center point of element)
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
@ -52,13 +52,13 @@ import { ClickOptions } from '../../types'
|
|||
:example.html
|
||||
<button id="myButton">Click me</button>
|
||||
:example.js
|
||||
it('should demonstrate a right click passed as string', () => {
|
||||
const myButton = $('#myButton')
|
||||
myButton.click({ button: 'right' }) // opens the contextmenu at the location of the button
|
||||
it('should demonstrate a right click passed as string', async () => {
|
||||
const myButton = await $('#myButton')
|
||||
await myButton.click({ button: 'right' }) // opens the contextmenu at the location of the button
|
||||
})
|
||||
it('should demonstrate a right click passed as number while adding an offset', () => {
|
||||
const myButton = $('#myButton')
|
||||
myButton.click({ button: 2, x: 30, y: 40 }) // opens the contextmenu 30 horizontal and 40 vertical pixels away from location of the button (from the center of element)
|
||||
it('should demonstrate a right click passed as number while adding an offset', async () => {
|
||||
const myButton = await $('#myButton')
|
||||
await myButton.click({ button: 2, x: 30, y: 40 }) // opens the contextmenu 30 horizontal and 40 vertical pixels away from location of the button (from the center of element)
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ import type { ElementArray } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:example.js
|
||||
it('should get all the plugin wrapper buttons', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
it('should get all the plugin wrapper buttons', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
return document.querySelectorAll(selector)
|
||||
})
|
||||
|
||||
const pluginRowBlock = browser.custom$('myStrat', '.pluginRowBlock')
|
||||
const pluginWrapper = pluginRowBlock.custom$$('myStrat', '.pluginWrapper')
|
||||
const pluginRowBlock = await browser.custom$('myStrat', '.pluginRowBlock')
|
||||
const pluginWrapper = await pluginRowBlock.custom$$('myStrat', '.pluginWrapper')
|
||||
|
||||
console.log(pluginWrapper.length) // 4
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import { ELEMENT_KEY } from '../../constants'
|
|||
*
|
||||
* <example>
|
||||
:example.js
|
||||
it('should fetch the project title', () => {
|
||||
browser.url('https://webdriver.io')
|
||||
browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
it('should fetch the project title', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await browser.addLocatorStrategy('myStrat', (selector) => {
|
||||
return document.querySelectorAll(selector)
|
||||
})
|
||||
|
||||
const header = browser.custom$('myStrat', 'header')
|
||||
const projectTitle = header.custom$('myStrat', '.projectTitle')
|
||||
const header = await browser.custom$('myStrat', 'header')
|
||||
const projectTitle = await header.custom$('myStrat', '.projectTitle')
|
||||
|
||||
console.log(projectTitle.getText()) // WEBDRIVER I/O
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<button id="myButton" ondblclick="document.getElementById('someText').innerHTML='I was dblclicked'">Click me</button>
|
||||
<div id="someText">I was not clicked</div>
|
||||
:doubleClick.js
|
||||
it('should demonstrate the doubleClick command', () => {
|
||||
const myButton = $('#myButton')
|
||||
myButton.doubleClick()
|
||||
it('should demonstrate the doubleClick command', async () => {
|
||||
const myButton = await $('#myButton')
|
||||
await myButton.doubleClick()
|
||||
|
||||
const value = myButton.getText()
|
||||
const value = await myButton.getText()
|
||||
assert(value === 'I was dblclicked') // true
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -28,15 +28,15 @@ type ElementCoordinates = {
|
|||
*
|
||||
* <example>
|
||||
:example.test.js
|
||||
it('should demonstrate the dragAndDrop command', () => {
|
||||
const elem = $('#someElem')
|
||||
const target = $('#someTarget')
|
||||
it('should demonstrate the dragAndDrop command', async () => {
|
||||
const elem = await $('#someElem')
|
||||
const target = await $('#someTarget')
|
||||
|
||||
// drag and drop to other element
|
||||
elem.dragAndDrop(target)
|
||||
await elem.dragAndDrop(target)
|
||||
|
||||
// drag and drop relative from current position
|
||||
elem.dragAndDrop({ x: 100, y: 200 })
|
||||
await elem.dragAndDrop({ x: 100, y: 200 })
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
<input type="submit" name="submit" value="submit"></input>
|
||||
</form>
|
||||
:getAttribute.js
|
||||
it('should demonstrate the getAttribute command', () => {
|
||||
const form = $('form')
|
||||
const attr = form.getAttribute('method')
|
||||
it('should demonstrate the getAttribute command', async () => {
|
||||
const form = await $('form')
|
||||
const attr = await form.getAttribute('method')
|
||||
console.log(attr) // outputs: "post"
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import { parseCSS } from '../../utils'
|
|||
:example.html
|
||||
<label id="myLabel" for="input" style="color: #0088cc; font-family: helvetica, arial, freesans, clean, sans-serif, width: 100px">Some Label</label>
|
||||
:getCSSProperty.js
|
||||
it('should demonstrate the getCSSProperty command', () => {
|
||||
const elem = $('#myLabel')
|
||||
const color = elem.getCSSProperty('color')
|
||||
it('should demonstrate the getCSSProperty command', async () => {
|
||||
const elem = await $('#myLabel')
|
||||
const color = await elem.getCSSProperty('color')
|
||||
console.log(color)
|
||||
// outputs the following:
|
||||
// {
|
||||
|
|
@ -36,7 +36,7 @@ import { parseCSS } from '../../utils'
|
|||
// }
|
||||
// }
|
||||
|
||||
const font = elem.getCSSProperty('font-family')
|
||||
const font = await elem.getCSSProperty('font-family')
|
||||
console.log(font)
|
||||
// outputs the following:
|
||||
// {
|
||||
|
|
@ -49,7 +49,7 @@ import { parseCSS } from '../../utils'
|
|||
// }
|
||||
// }
|
||||
|
||||
var width = elem.getCSSProperty('width')
|
||||
var width = await elem.getCSSProperty('width')
|
||||
console.log(width)
|
||||
// outputs the following:
|
||||
// {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
*
|
||||
* <example>
|
||||
:getComputedLabel.js
|
||||
it('should demonstrate the getComputedLabel command', () => {
|
||||
browser.url('https://www.google.com/ncr')
|
||||
const elem = $('*[name="q"]');
|
||||
console.log(elem.getComputedLabel()); // outputs: "Search"
|
||||
it('should demonstrate the getComputedLabel command', async () => {
|
||||
await browser.url('https://www.google.com/ncr')
|
||||
const elem = await $('*[name="q"]');
|
||||
console.log(await elem.getComputedLabel()); // outputs: "Search"
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
*
|
||||
* <example>
|
||||
:getComputedRole.js
|
||||
it('should demonstrate the getComputedRole command', () => {
|
||||
browser.url('https://www.google.com/ncr')
|
||||
const elem = $('*[name="q"]');
|
||||
console.log(elem.getComputedRole()); // outputs: "combobox"
|
||||
it('should demonstrate the getComputedRole command', async () => {
|
||||
await browser.url('https://www.google.com/ncr')
|
||||
const elem = await $('*[name="q"]');
|
||||
console.log(await elem.getComputedRole()); // outputs: "combobox"
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ import getHTMLScript from '../../scripts/getHTML'
|
|||
<span>Lorem ipsum dolor amet</span>
|
||||
</div>
|
||||
:getHTML.js
|
||||
it('should get html for certain elements', () => {
|
||||
var outerHTML = $('#test').getHTML();
|
||||
it('should get html for certain elements', async () => {
|
||||
var outerHTML = await $('#test').getHTML();
|
||||
console.log(outerHTML);
|
||||
// outputs:
|
||||
// "<div id="test"><span>Lorem ipsum dolor amet</span></div>"
|
||||
|
||||
var innerHTML = $('#test').getHTML(false);
|
||||
var innerHTML = await $('#test').getHTML(false);
|
||||
console.log(innerHTML);
|
||||
// outputs:
|
||||
// "<span>Lorem ipsum dolor amet</span>"
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ function getLocation (this: WebdriverIO.Element, prop: keyof Location): Promise<
|
|||
*
|
||||
* <example>
|
||||
:getLocation.js
|
||||
it('should demonstrate the getLocation function', () => {
|
||||
browser.url('http://github.com');
|
||||
const logo = $('.octicon-mark-github')
|
||||
const location = logo.getLocation();
|
||||
it('should demonstrate the getLocation function', async () => {
|
||||
await browser.url('http://github.com');
|
||||
const logo = await $('.octicon-mark-github')
|
||||
const location = await logo.getLocation();
|
||||
console.log(location); // outputs: { x: 150, y: 20 }
|
||||
|
||||
const xLocation = logo.getLocation('x')
|
||||
const xLocation = await logo.getLocation('x')
|
||||
console.log(xLocation); // outputs: 150
|
||||
|
||||
const yLocation = logo.getLocation('.octicon-mark-github', 'y')
|
||||
const yLocation = await logo.getLocation('y')
|
||||
console.log(yLocation); // outputs: 20
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import getPropertyScript from '../../scripts/getProperty'
|
|||
*
|
||||
* <example>
|
||||
:getProperty.js
|
||||
it('should demonstrate the getProperty command', () => {
|
||||
var elem = $('body')
|
||||
var tag = elem.getProperty('tagName')
|
||||
it('should demonstrate the getProperty command', async () => {
|
||||
var elem = await $('body')
|
||||
var tag = await elem.getProperty('tagName')
|
||||
console.log(tag) // outputs: "BODY"
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ function getSize (this: WebdriverIO.Element, prop: keyof RectReturn): Promise<nu
|
|||
*
|
||||
* <example>
|
||||
:getSize.js
|
||||
it('should demonstrate the getSize command', () => {
|
||||
browser.url('http://github.com')
|
||||
const logo = $('.octicon-mark-github')
|
||||
it('should demonstrate the getSize command', async () => {
|
||||
await browser.url('http://github.com')
|
||||
const logo = await $('.octicon-mark-github')
|
||||
|
||||
const size = logo.getSize()
|
||||
const size = await logo.getSize()
|
||||
console.log(size) // outputs: { width: 32, height: 32 }
|
||||
|
||||
const width = logo.getSize('width')
|
||||
const width = await logo.getSize('width')
|
||||
console.log(width) // outputs: 32
|
||||
|
||||
const height = logo.getSize('height')
|
||||
const height = await logo.getSize('height')
|
||||
console.log(height) // outputs: 32
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
<div id="elem">Lorem ipsum</div>
|
||||
|
||||
:getTagName.js
|
||||
it('should demonstrate the getTagName command', () => {
|
||||
const elem = $('#elem');
|
||||
it('should demonstrate the getTagName command', async () => {
|
||||
const elem = await $('#elem');
|
||||
|
||||
const tagName = elem.getTagName();
|
||||
const tagName = await elem.getTagName();
|
||||
console.log(tagName); // outputs: "div"
|
||||
})
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@
|
|||
</div>
|
||||
<span style="display: none">I am invisible</span>
|
||||
:getText.js
|
||||
it('should demonstrate the getText function', () => {
|
||||
const elem = $('#elem');
|
||||
console.log(elem.getText());
|
||||
it('should demonstrate the getText function', async () => {
|
||||
const elem = await $('#elem');
|
||||
console.log(await elem.getText());
|
||||
// outputs the following:
|
||||
// "Lorem ipsum dolor sit amet,consetetur sadipscing elitr"
|
||||
|
||||
const span = $('span');
|
||||
console.log(span.getText());
|
||||
const span = await $('span');
|
||||
console.log(await span.getText());
|
||||
// outputs "" (empty string) since element is not interactable
|
||||
});
|
||||
it('get content from table cell', () => {
|
||||
browser.url('http://the-internet.herokuapp.com/tables');
|
||||
const rows = $$('#table1 tr');
|
||||
const columns = rows[1].$$('td'); // get columns of 2nd row
|
||||
console.log(columns[2].getText()); // get text of 3rd column
|
||||
it('get content from table cell', async () => {
|
||||
await browser.url('http://the-internet.herokuapp.com/tables');
|
||||
const rows = await $$('#table1 tr');
|
||||
const columns = await rows[1].$$('td'); // get columns of 2nd row
|
||||
console.log(await columns[2].getText()); // get text of 3rd column
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
:index.html
|
||||
<input type="text" value="John Doe" id="username">
|
||||
:getValue.js
|
||||
it('should demonstrate the getValue command', () => {
|
||||
const inputUser = $('#username');
|
||||
const value = inputUser.getValue();
|
||||
it('should demonstrate the getValue command', async () => {
|
||||
const inputUser = await $('#username');
|
||||
const value = await inputUser.getValue();
|
||||
console.log(value); // outputs: "John Doe"
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ import isElementClickableScript from '../../scripts/isElementClickable'
|
|||
*
|
||||
* <example>
|
||||
:isClickable.js
|
||||
it('should detect if an element is clickable', () => {
|
||||
const el = $('#el')
|
||||
let clickable = el.isClickable();
|
||||
it('should detect if an element is clickable', async () => {
|
||||
const el = await $('#el')
|
||||
let clickable = await el.isClickable();
|
||||
console.log(clickable); // outputs: true or false
|
||||
|
||||
// wait for element to be clickable
|
||||
browser.waitUntil(() => el.isClickable())
|
||||
await browser.waitUntil(() => el.isClickable())
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,26 +17,26 @@ const noW3CEndpoint = ['microsoftedge', 'msedge', 'safari', 'chrome', 'safari te
|
|||
<div id="notInViewport" style="position:absolute; left: 9999999"></div>
|
||||
<div id="zeroOpacity" style="opacity: 0"></div>
|
||||
:isDisplayed.js
|
||||
it('should detect if an element is displayed', () => {
|
||||
let elem = $('#notDisplayed');
|
||||
let isDisplayed = elem.isDisplayed();
|
||||
it('should detect if an element is displayed', async () => {
|
||||
let elem = await $('#notDisplayed');
|
||||
let isDisplayed = await elem.isDisplayed();
|
||||
console.log(isDisplayed); // outputs: false
|
||||
|
||||
elem = $('#notVisible');
|
||||
elem = await $('#notVisible');
|
||||
|
||||
isDisplayed = elem.isDisplayed();
|
||||
isDisplayed = await elem.isDisplayed();
|
||||
console.log(isDisplayed); // outputs: false
|
||||
|
||||
elem = $('#notExisting');
|
||||
isDisplayed = elem.isDisplayed();
|
||||
elem = await $('#notExisting');
|
||||
isDisplayed = await elem.isDisplayed();
|
||||
console.log(isDisplayed); // outputs: false
|
||||
|
||||
elem = $('#notInViewport');
|
||||
isDisplayed = elem.isDisplayed();
|
||||
elem = await $('#notInViewport');
|
||||
isDisplayed = await elem.isDisplayed();
|
||||
console.log(isDisplayed); // outputs: true
|
||||
|
||||
elem = $('#zeroOpacity');
|
||||
isDisplayed = elem.isDisplayed();
|
||||
elem = await $('#zeroOpacity');
|
||||
isDisplayed = await elem.isDisplayed();
|
||||
console.log(isDisplayed); // outputs: true
|
||||
});
|
||||
* </example>
|
||||
|
|
@ -65,9 +65,9 @@ export default async function isDisplayed (this: WebdriverIO.Element) {
|
|||
* - Safari and Chrome work in jsonwp mode and Appium just rewrites W3C requests from upstream to jsonwp if needed
|
||||
*/
|
||||
const useAtom = (
|
||||
browser.isDevTools ||
|
||||
await browser.isDevTools ||
|
||||
(
|
||||
browser.isW3C &&
|
||||
await browser.isW3C &&
|
||||
!browser.isMobile &&
|
||||
noW3CEndpoint.includes((browser.capabilities as Capabilities.Capabilities).browserName?.toLowerCase()!)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,20 +14,20 @@ import isElementInViewportScript from '../../scripts/isElementInViewport'
|
|||
<div id="zeroOpacity" style="opacity: 0"></div>
|
||||
:isDisplayedInViewport.js
|
||||
:isDisplayed.js
|
||||
it('should detect if an element is visible', () => {
|
||||
let isDisplayedInViewport = $('#notDisplayed').isDisplayedInViewport();
|
||||
it('should detect if an element is visible', async () => {
|
||||
let isDisplayedInViewport = await $('#notDisplayed').isDisplayedInViewport();
|
||||
console.log(isDisplayedInViewport); // outputs: false
|
||||
|
||||
isDisplayedInViewport = $('#notVisible').isDisplayedInViewport();
|
||||
isDisplayedInViewport = await $('#notVisible').isDisplayedInViewport();
|
||||
console.log(isDisplayedInViewport); // outputs: false
|
||||
|
||||
isDisplayedInViewport = $('#notExisting').isDisplayedInViewport();
|
||||
isDisplayedInViewport = await $('#notExisting').isDisplayedInViewport();
|
||||
console.log(isDisplayedInViewport); // outputs: false
|
||||
|
||||
isDisplayedInViewport = $('#notInViewport').isDisplayedInViewport();
|
||||
isDisplayedInViewport = await $('#notInViewport').isDisplayedInViewport();
|
||||
console.log(isDisplayedInViewport); // outputs: false
|
||||
|
||||
isDisplayedInViewport = $('#zeroOpacity').isDisplayedInViewport();
|
||||
isDisplayedInViewport = await $('#zeroOpacity').isDisplayedInViewport();
|
||||
console.log(isDisplayedInViewport); // outputs: false
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -9,17 +9,17 @@
|
|||
<input type="text" name="inputField" class="input3" disabled="disabled">
|
||||
|
||||
:isEnabled.js
|
||||
it('should detect if an element is enabled', () => {
|
||||
let elem = $('.input1')
|
||||
let isEnabled = elem.isEnabled();
|
||||
it('should detect if an element is enabled', async () => {
|
||||
let elem = await $('.input1')
|
||||
let isEnabled = await elem.isEnabled();
|
||||
console.log(isEnabled); // outputs: true
|
||||
|
||||
elem = $('.input2')
|
||||
isEnabled = elem.isEnabled();
|
||||
elem = await $('.input2')
|
||||
isEnabled = await elem.isEnabled();
|
||||
console.log(isEnabled2); // outputs: false
|
||||
|
||||
elem = $('.input3')
|
||||
isEnabled = elem.isEnabled();
|
||||
elem = await $('.input3')
|
||||
isEnabled = await elem.isEnabled();
|
||||
console.log(isEnabled3); // outputs: false
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ const getWebElement = (el: WebdriverIO.Element) => ({
|
|||
*
|
||||
* <example>
|
||||
:isEqual.js
|
||||
it('should detect if an element is clickable', () => {
|
||||
const el = $('#el')
|
||||
const sameEl = $('#el')
|
||||
const anotherEl = $('#anotherEl')
|
||||
it('should detect if an element is clickable', async () => {
|
||||
const el = await $('#el')
|
||||
const sameEl = await $('#el')
|
||||
const anotherEl = await $('#anotherEl')
|
||||
|
||||
el.isEqual(sameEl) // outputs: true
|
||||
|
||||
|
|
|
|||
|
|
@ -9,25 +9,25 @@
|
|||
<div id="notInViewport" style="position:absolute; left: 9999999"></div>
|
||||
<div id="zeroOpacity" style="opacity: 0"></div>
|
||||
:isExisting.js
|
||||
it('should detect if elements are existing', () => {
|
||||
let elem = $('#someRandomNonExistingElement')
|
||||
let isExisting = elem.isExisting()
|
||||
it('should detect if elements are existing', async () => {
|
||||
let elem = await $('#someRandomNonExistingElement')
|
||||
let isExisting = await elem.isExisting()
|
||||
console.log(isExisting); // outputs: false
|
||||
|
||||
elem = $('#notDisplayed')
|
||||
isExisting = elem.isExisting()
|
||||
elem = await $('#notDisplayed')
|
||||
isExisting = await elem.isExisting()
|
||||
console.log(isExisting); // outputs: true
|
||||
|
||||
elem = $('#notVisible')
|
||||
isExisting = elem.isExisting()
|
||||
elem = await $('#notVisible')
|
||||
isExisting = await elem.isExisting()
|
||||
console.log(isExisting); // outputs: true
|
||||
|
||||
elem = $('#notInViewport')
|
||||
isExisting = elem.isExisting()
|
||||
elem = await $('#notInViewport')
|
||||
isExisting = await elem.isExisting()
|
||||
console.log(isExisting); // outputs: true
|
||||
|
||||
elem = $('#zeroOpacity')
|
||||
isExisting = elem.isExisting()
|
||||
elem = await $('#zeroOpacity')
|
||||
isExisting = await elem.isExisting()
|
||||
console.log(isExisting); // outputs: true
|
||||
});
|
||||
* </example>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ import isFocusedScript from '../../scripts/isFocused'
|
|||
:index.html
|
||||
<input name="login" autofocus="" />
|
||||
:hasFocus.js
|
||||
it('should detect the focus of an element', () => {
|
||||
browser.url('/');
|
||||
const loginInput = $('[name="login"]');
|
||||
console.log(loginInput.isFocused()); // outputs: false
|
||||
it('should detect the focus of an element', async () => {
|
||||
await browser.url('/');
|
||||
const loginInput = await $('[name="login"]');
|
||||
console.log(await loginInput.isFocused()); // outputs: false
|
||||
|
||||
loginInput.click();
|
||||
console.log(loginInput.isFocused()); // outputs: true
|
||||
await loginInput.click();
|
||||
console.log(await loginInput.isFocused()); // outputs: true
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@
|
|||
</select>
|
||||
|
||||
:isSelected.js
|
||||
it('should detect if an element is selected', () => {
|
||||
let element = $('[value="Layla Terry"]');
|
||||
console.log(element.isSelected()); // outputs: true
|
||||
it('should detect if an element is selected', async () => {
|
||||
let element = await $('[value="Layla Terry"]');
|
||||
console.log(await element.isSelected()); // outputs: true
|
||||
|
||||
element = $('[value="Bill Gilbert"]')
|
||||
console.log(element.isSelected()); // outputs: false
|
||||
element = await $('[value="Bill Gilbert"]')
|
||||
console.log(await element.isSelected()); // outputs: false
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
<p>Sibling Three</p>
|
||||
</div>
|
||||
:nextElement.js
|
||||
it('should get text from next sibling element', () => {
|
||||
const elem = $$('p');
|
||||
console.log(elem[1].nextElement().getText()); // outputs: "Sibling Three"
|
||||
it('should get text from next sibling element', async () => {
|
||||
const elem = await $$('p');
|
||||
const nextElement = await elem[1].nextElement()
|
||||
console.log(await nextElement.getText()); // outputs: "Sibling Three"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
<p>Sibling Three</p>
|
||||
</div>
|
||||
:parentElement.js
|
||||
it('should get class from parent element', () => {
|
||||
const elem = $$('p');
|
||||
console.log(elem[2].parentElement().getAttribute('class')); // outputs: "parent"
|
||||
it('should get class from parent element', async () => {
|
||||
const elem = await $$('p');
|
||||
const parent = await elem[2].parentElement()
|
||||
console.log(await parent.getAttribute('class')); // outputs: "parent"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
<p>Sibling Three</p>
|
||||
</div>
|
||||
:previousElement.js
|
||||
it('should get text from previous sibling element', () => {
|
||||
const elem = $$('p');
|
||||
console.log(elem[1].previousElement().getText()); // outputs: "Sibling One"
|
||||
it('should get text from previous sibling element', async () => {
|
||||
const elem = await $$('p');
|
||||
const previousElem = await elem[1].previousElement()
|
||||
console.log(await previousElem.getText()); // outputs: "Sibling One"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ const resqScript = fs.readFileSync(require.resolve('resq'))
|
|||
*
|
||||
* <example>
|
||||
:pause.js
|
||||
it('should calculate 7 * 6', () => {
|
||||
browser.url('https://ahfarmer.github.io/calculator/');
|
||||
it('should calculate 7 * 6', async () => {
|
||||
await browser.url('https://ahfarmer.github.io/calculator/');
|
||||
|
||||
const orangeButtons = browser.react$$('t', {
|
||||
const orangeButtons = await browser.react$$('t', {
|
||||
props: { orange: true }
|
||||
})
|
||||
console.log(orangeButtons.map((btn) => btn.getText())); // prints "[ '÷', 'x', '-', '+', '=' ]"
|
||||
console.log(await orangeButtons.map((btn) => btn.getText()));
|
||||
// prints "[ '÷', 'x', '-', '+', '=' ]"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -21,24 +21,24 @@ const resqScript = fs.readFileSync(require.resolve('resq'))
|
|||
*
|
||||
* <example>
|
||||
:pause.js
|
||||
it('should calculate 7 * 6', () => {
|
||||
browser.url('https://ahfarmer.github.io/calculator/');
|
||||
const appWrapper = browser.$('div#root')
|
||||
it('should calculate 7 * 6', async () => {
|
||||
await browser.url('https://ahfarmer.github.io/calculator/');
|
||||
const appWrapper = await browser.$('div#root')
|
||||
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '7' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: 'x' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '6' }
|
||||
}).click()
|
||||
browser.react$('t', {
|
||||
await browser.react$('t', {
|
||||
props: { name: '=' }
|
||||
}).click()
|
||||
|
||||
console.log($('.component-display').getText()); // prints "42"
|
||||
console.log(await $('.component-display').getText()); // prints "42"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { getAbsoluteFilepath, assertDirectoryExists } from '../../utils'
|
|||
*
|
||||
* <example>
|
||||
:saveScreenshot.js
|
||||
it('should save a screenshot of the browser view', function () {
|
||||
const elem = $('#someElem');
|
||||
elem.saveScreenshot('./some/path/elemScreenshot.png');
|
||||
it('should save a screenshot of the browser view', async () => {
|
||||
const elem = await $('#someElem');
|
||||
await elem.saveScreenshot('./some/path/elemScreenshot.png');
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { ELEMENT_KEY } from '../../constants'
|
|||
*
|
||||
* <example>
|
||||
:scrollIntoView.js
|
||||
it('should demonstrate the scrollIntoView command', () => {
|
||||
const elem = $('#myElement');
|
||||
it('should demonstrate the scrollIntoView command', async () => {
|
||||
const elem = await $('#myElement');
|
||||
// scroll to specific element
|
||||
elem.scrollIntoView();
|
||||
await elem.scrollIntoView();
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@ import { getElementFromResponse } from '../../utils'
|
|||
<option name="someName5" value="someValue5">seis</option>
|
||||
</select>
|
||||
:selectByAttribute.js
|
||||
it('Should demonstrate the selectByAttribute command', () => {
|
||||
const selectBox = $('#selectbox');
|
||||
const value = selectBox.getValue();
|
||||
it('Should demonstrate the selectByAttribute command', async () => {
|
||||
const selectBox = await $('#selectbox');
|
||||
const value = await selectBox.getValue();
|
||||
console.log(value); // returns "someValue0"
|
||||
|
||||
selectBox.selectByAttribute('value', 'someValue3');
|
||||
console.log(selectBox.getValue()); // returns "someValue3"
|
||||
await selectBox.selectByAttribute('value', 'someValue3');
|
||||
console.log(await selectBox.getValue()); // returns "someValue3"
|
||||
|
||||
selectBox.selectByAttribute('name', 'someName5');
|
||||
console.log(selectBox.getValue()); // returns "someValue5"
|
||||
await selectBox.selectByAttribute('name', 'someName5');
|
||||
console.log(await selectBox.getValue()); // returns "someValue5"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ import { getElementFromResponse } from '../../utils'
|
|||
<option value="someValue5">seis</option>
|
||||
</select>
|
||||
:selectByIndex.js
|
||||
it('Should demonstrate the selectByIndex command', () => {
|
||||
const selectBox = $('#selectbox');
|
||||
console.log(selectBox.getValue()); // returns "someValue0"
|
||||
selectBox.selectByIndex(4);
|
||||
console.log(selectBox.getValue()); // returns "someValue4"
|
||||
it('Should demonstrate the selectByIndex command', async () => {
|
||||
const selectBox = await $('#selectbox');
|
||||
console.log(await selectBox.getValue()); // returns "someValue0"
|
||||
await selectBox.selectByIndex(4);
|
||||
console.log(await selectBox.getValue()); // returns "someValue4"
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ import { getElementFromResponse } from '../../utils'
|
|||
<option value="someValue5">seis</option>
|
||||
</select>
|
||||
:selectByVisibleText.js
|
||||
it('demonstrate the selectByVisibleText command', () => {
|
||||
const selectBox = $('#selectbox');
|
||||
console.log(selectBox.getText('option:checked')); // returns "uno"
|
||||
selectBox.selectByVisibleText('cuatro');
|
||||
console.log(selectBox.getText('option:checked')); // returns "cuatro"
|
||||
it('demonstrate the selectByVisibleText command', async () => {
|
||||
const selectBox = await $('#selectbox');
|
||||
console.log(await selectBox.getText('option:checked')); // returns "uno"
|
||||
await selectBox.selectByVisibleText('cuatro');
|
||||
console.log(await selectBox.getText('option:checked')); // returns "cuatro"
|
||||
})
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import type { CommandOptions, Value } from './addValue'
|
|||
*
|
||||
* <example>
|
||||
:setValue.js
|
||||
it('should set value for a certain element', () => {
|
||||
const input = $('.input');
|
||||
input.setValue('test123');
|
||||
it('should set value for a certain element', async () => {
|
||||
const input = await $('.input');
|
||||
await input.setValue('test123');
|
||||
|
||||
console.log(input.getValue()); // outputs: 'test123'
|
||||
console.log(await input.getValue()); // outputs: 'test123'
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { shadowFnFactory } from '../../scripts/shadowFnFactory'
|
|||
*
|
||||
* <example>
|
||||
:shadow$$.js
|
||||
it('should return elements inside a shadowRoot', () => {
|
||||
const innerEl = $('.input').shadow$$('#innerEl');
|
||||
console.log(innerEl.getValue()); // outputs: 'test123'
|
||||
it('should return elements inside a shadowRoot', async () => {
|
||||
const innerEl = await $('.input').shadow$$('#innerEl');
|
||||
console.log(await innerEl.getValue()); // outputs: 'test123'
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { shadowFnFactory } from '../../scripts/shadowFnFactory'
|
|||
*
|
||||
* <example>
|
||||
:shadow$$.js
|
||||
it('should return an element inside a shadowRoot', () => {
|
||||
const innerEl = $('.input').shadow$('#innerEl');
|
||||
console.log(innerEl.getValue()); // outputs: 'test123'
|
||||
it('should return an element inside a shadowRoot', async () => {
|
||||
const innerEl = await $('.input').shadow$('#innerEl');
|
||||
console.log(await innerEl.getValue()); // outputs: 'test123'
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,28 +17,28 @@ import type { TouchActions } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:touchAction.js
|
||||
it('should do a touch gesture', function () {
|
||||
const screen = $('//UITextbox');
|
||||
it('should do a touch gesture', async () => {
|
||||
const screen = await $('//UITextbox');
|
||||
|
||||
// simple touch action on element
|
||||
screen.touchAction('tap');
|
||||
await screen.touchAction('tap');
|
||||
|
||||
// simple touch action using selector and x y variables
|
||||
// tap location is 30px right and 20px down relative from the center of the element
|
||||
screen.touchAction({
|
||||
await screen.touchAction({
|
||||
action: 'tap', x: 30, y:20
|
||||
})
|
||||
|
||||
// multi action on an element (drag&drop)
|
||||
screen.touchAction([
|
||||
await screen.touchAction([
|
||||
'press',
|
||||
{ action: 'moveTo', x: 200, y: 300 },
|
||||
'release'
|
||||
])
|
||||
|
||||
// drag&drop to element
|
||||
const otherElement = $('//UIAApplication[1]/UIAElement[2]')
|
||||
screen.touchAction([
|
||||
const otherElement = await $('//UIAApplication[1]/UIAElement[2]')
|
||||
await screen.touchAction([
|
||||
'press',
|
||||
{ action: 'moveTo', element: otherElement },
|
||||
'release'
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import type { WaitForOptions } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:waitForClickable.js
|
||||
it('should detect when element is clickable', () => {
|
||||
const elem = $('#elem')
|
||||
elem.waitForClickable({ timeout: 3000 });
|
||||
it('should detect when element is clickable', async () => {
|
||||
const elem = await $('#elem')
|
||||
await elem.waitForClickable({ timeout: 3000 });
|
||||
});
|
||||
it('should detect when element is no longer clickable', () => {
|
||||
const elem = $('#elem')
|
||||
elem.waitForClickable({ reverse: true });
|
||||
it('should detect when element is no longer clickable', async () => {
|
||||
const elem = await $('#elem')
|
||||
await elem.waitForClickable({ reverse: true });
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ import type { WaitForOptions } from '../../types'
|
|||
}, 2000);
|
||||
</script>
|
||||
:waitForDisplayedExample.js
|
||||
it('should detect when element is visible', () => {
|
||||
const elem = $('#elem')
|
||||
elem.waitForDisplayed({ timeout: 3000 });
|
||||
it('should detect when element is visible', async () => {
|
||||
const elem = await $('#elem')
|
||||
await elem.waitForDisplayed({ timeout: 3000 });
|
||||
});
|
||||
it('should detect when element is no longer visible', () => {
|
||||
const elem = $('#elem')
|
||||
elem.waitForDisplayed({ reverse: true });
|
||||
it('should detect when element is no longer visible', async () => {
|
||||
const elem = await $('#elem')
|
||||
await elem.waitForDisplayed({ reverse: true });
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ import type { WaitForOptions } from '../../types'
|
|||
}, 2000);
|
||||
</script>
|
||||
:waitForEnabledExample.js
|
||||
it('should detect when element is enabled', () => {
|
||||
$('#username').waitForEnabled({ timeout: 3000 });
|
||||
it('should detect when element is enabled', async () => {
|
||||
await $('#username').waitForEnabled({ timeout: 3000 });
|
||||
});
|
||||
|
||||
it('should detect when element is disabled', () => {
|
||||
elem = $('#username');
|
||||
elem.waitForEnabled({ reverse: true })
|
||||
it('should detect when element is disabled', async () => {
|
||||
elem = await $('#username');
|
||||
await elem.waitForEnabled({ reverse: true })
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ import type { WaitForOptions } from '../../types'
|
|||
*
|
||||
* <example>
|
||||
:waitForExistSyncExample.js
|
||||
it('should display a notification message after successful form submit', function () {
|
||||
const form = $('form');
|
||||
const notification = $('.notification');
|
||||
form.$(".send").click();
|
||||
notification.waitForExist({ timeout: 5000 });
|
||||
expect(notification.getText()).to.be.equal('Data transmitted successfully!')
|
||||
it('should display a notification message after successful form submit', async () => {
|
||||
const form = await $('form');
|
||||
const notification = await $('.notification');
|
||||
await form.$(".send").click();
|
||||
await notification.waitForExist({ timeout: 5000 });
|
||||
expect(await notification.getText()).to.be.equal('Data transmitted successfully!')
|
||||
});
|
||||
it('should remove a message after successful form submit', function () {
|
||||
const form = $('form');
|
||||
const message = $('.message');
|
||||
form.$(".send").click();
|
||||
message.waitForExist({ reverse: true });
|
||||
it('should remove a message after successful form submit', async () => {
|
||||
const form = await $('form');
|
||||
const message = await $('.message');
|
||||
await form.$(".send").click();
|
||||
await message.waitForExist({ reverse: true });
|
||||
});
|
||||
* </example>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@
|
|||
<div id="someText">I am some text</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
$('#someText').html('I am now different');
|
||||
await $('#someText').html('I am now different');
|
||||
}, 1000);
|
||||
</script>
|
||||
|
||||
:waitUntil.js
|
||||
it('should wait until text has changed', () => {
|
||||
const elem = $('#someText')
|
||||
elem.waitUntil(function () {
|
||||
return this.getText() === 'I am now different'
|
||||
it('should wait until text has changed', async () => {
|
||||
const elem = await $('#someText')
|
||||
await elem.waitUntil(async () => {
|
||||
return (await this.getText()) === 'I am now different'
|
||||
}, {
|
||||
timeout: 5000,
|
||||
timeoutMsg: 'expected text to be different after 5s'
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue