Skip to content

Commit

Permalink
Implement new async API (#6954)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
christian-bromann and erwinheitzman committed Jul 28, 2021
1 parent d0d3ebb commit f9eae7a
Show file tree
Hide file tree
Showing 144 changed files with 2,205 additions and 1,740 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`:

Expand Down
1,017 changes: 456 additions & 561 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions packages/wdio-cli/src/templates/afterTest.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,36 @@ 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 {
/**
* 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);
});
<% } %>
Original file line number Diff line number Diff line change
Expand Up @@ -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!');
});
});
Expand All @@ -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!');
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<%
/**
* step definition without page objects
* test with page objects
*/
if (usePageObjects) {
%><%- isUsingTypeScript || isUsingBabel
Expand All @@ -11,36 +11,31 @@ 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!');
});
});
<% } 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!');
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down
24 changes: 5 additions & 19 deletions packages/wdio-cli/src/templates/wdio.conf.tpl.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>',
Expand Down Expand Up @@ -187,23 +184,15 @@
// 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
},<%
}
if(answers.framework === 'jasmine') { %>
//
// 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
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/wdio-jasmine-framework/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class JasmineAdapter {
}

/**
* wrap commands with wdio-sync
* wrap commands
*/
INTERFACES['bdd'].forEach((fnName) => {
const isTest = TEST_INTERFACES.includes(fnName)
Expand Down
3 changes: 2 additions & 1 deletion packages/wdio-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion packages/wdio-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 3 additions & 4 deletions packages/wdio-utils/src/monad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ 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)) {
if (typeof value !== 'function') {
continue
}

propertiesObject[commandName].value = commandWrapper(commandName, value)
propertiesObject[commandName].value = commandWrapper(commandName, value, propertiesObject)
propertiesObject[commandName].configurable = true
}
}
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit f9eae7a

Please sign in to comment.