Skip to content

Latest commit

 

History

History
408 lines (331 loc) · 10.3 KB

TypeScript.md

File metadata and controls

408 lines (331 loc) · 10.3 KB
id title
typescript
TypeScript Setup

You can write tests using TypeScript to get autocompletion and type safety.

You will need typescript and ts-node installed as devDependencies. WebdriverIO will automatically detect if these dependencies are installed and will compile your config and tests for you. If you need to configure how ts-node runs please use the environment variables for ts-node or use wdio config's autoCompileOpts section.

$ npm install typescript ts-node --save-dev

The minimum TypeScript version is v4.0.5.

Configuration

You can provide custom ts-node and tsconfig-paths options through your wdio.conf.ts, e.g.:

export const config = {
    // ...
    autoCompileOpts: {
        autoCompile: true,
        // see https://github.com/TypeStrong/ts-node#cli-and-programmatic-options
        // for all available options
        tsNodeOpts: {
            transpileOnly: true,
            project: 'tsconfig.json'
        },
        // tsconfig-paths is only used if "tsConfigPathsOpts" are provided, if you
        // do please make sure "tsconfig-paths" is installed as dependency
        tsConfigPathsOpts: {
            baseUrl: './'
        }
    }
}

If you don't want to use WebdriverIO's internal transpiler functionality you can create your own entrypoint.js file where ts-node is defined manually:

require('ts-node').register(
    {
        transpileOnly: false,
        files: true,
        project: "./tsconfig.json"
    }
)
module.exports = require('./configs/wdio.conf')

In this case you have to pass --no-autoCompileOpts.autoCompile as parameter to the wdio command to disable auto compiling, e.g.:

npx wdio run ./entrypoint.js --no-autoCompileOpts.autoCompile

Framework Setup

And your tsconfig.json needs the following:

<Tabs defaultValue="async" className="runtime" values={[ {label: 'Async Mode', value: 'async'}, {label: 'Sync Mode', value: 'sync'}, ] }>

{
    "compilerOptions": {
        "types": ["node", "webdriverio/sync"]
    }
}

:::caution Synchronous Mode will depcrecated with Node.js v16. With an update to the underlying Chromium version it became technically impossible to provide the same synchronous behavior. We recommend to start transition to asynchronous command execution. For more information, see our RFC. :::

{
    "compilerOptions": {
        "types": ["node", "webdriverio/async"]
    }
}

Please avoid importing webdriverio or @wdio/sync explicitly. WebdriverIO and WebDriver types are accessible from anywhere once added to types in tsconfig.json. If you use additional WebdriverIO services, plugins or the devtools automation package, please also add them to the types list as many provide additional typings.

Framework Types

Depending on the framework you use, you will need to add the types for that framework to your tsconfig.json types property, as well as install its type definitions. This is especially important if you want to have type support for the built-in assertion library expect-webdriverio.

For instance, if you decide to use the Mocha framework, you need to install @types/mocha and add it like this to have all types globally available:

<Tabs defaultValue="mocha" values={[ {label: 'Mocha', value: 'mocha'}, {label: 'Jasmine', value: 'jasmine'}, {label: 'Cucumber', value: 'cucumber'}, ] }>

{
    "compilerOptions": {
        "types": ["node", "webdriverio/async", "@wdio/mocha-framework"]
    }
}
{
    "compilerOptions": {
        "types": ["node", "webdriverio/async", "@wdio/jasmine-framework"]
    }
}
{
    "compilerOptions": {
        "types": ["node", "webdriverio/async", "@wdio/cucumber-framework"]
    }
}

Services

If you use services that add commands to the browser scope you also need to include these into your tsconfig.json. For example if you use the @wdio/devtools-service ensure that you add it to the types as well, e.g.:

{
    "compilerOptions": {
        "types": [
            "node",
            "webdriverio/async",
            "@wdio/mocha-framework",
            "@wdio/devtools-service"
        ]
    }
}

Adding services and reporters to your TypeScript config also strengthen the type safety of your WebdriverIO config file.

Type Definitions

When running WebdriverIO commands all properties are usually typed so that you don't have to deal with importing additional types. However there are cases where you want to define variables upfront. To ensure that these are type safe you can use all types defined in the @wdio/types package. For example if you like to define the remote option for webdriverio you can do:

import type { Capabilities } from '@wdio/types'

const config: Capabilities.WebdriverIO = {
    hostname: 'http://localhost',
    port: '4444' // Error: Type 'string' is not assignable to type 'number'.ts(2322)
    capabilities: {
        browserName: 'chrome'
    }
}

Adding Custom Commands

With TypeScript, it's easy to extend WebdriverIO interfaces. Add types to your custom commands like this:

  1. Create a type definition file (e.g., ./src/types/wdio.d.ts)

  2. a. If using a module-style type definition file (using import/export and declare global WebdriverIO in the type definition file), make sure to include the file path in the tsconfig.json include property.

    b. If using ambient-style type definition files (no import/export in type definition files and declare namespace WebdriverIO for custom commands), make sure the tsconfig.json does not contain any include section, since this will cause all type definition files not listed in the include section to not be recognized by typescript.

<Tabs defaultValue="modules" values={[ {label: 'Modules (using import/export)', value: 'modules'}, {label: 'Ambient Type Definitions (no tsconfig include)', value: 'ambient'}, ] }>

{
    "compilerOptions": { ... },
    "include": [
        "./test/**/*.ts",
        "./src/types/**/*.ts"
    ]
}
{
    "compilerOptions": { ... }
}
  1. Add definitions for your commands according to your execution mode.

<Tabs defaultValue="modules" values={[ {label: 'Modules (using import/export)', value: 'modules'}, {label: 'Ambient Type Definitions', value: 'ambient'}, ] }>

<Tabs defaultValue="async" values={[ {label: 'Async', value: 'async'}, {label: 'Sync', value: 'sync'}, ] }>

declare global {
    namespace WebdriverIO {
        interface Browser {
            browserCustomCommand: (arg: any) => void
        }

        interface MultiRemoteBrowser {
            browserCustomCommand: (arg: any) => void
        }

        interface Element {
            elementCustomCommand: (arg: any) => number
        }
    }
}
declare global {
    namespace WebdriverIO {
        interface Browser {
            browserCustomCommand: (arg: any) => Promise<void>
        }

        interface MultiRemoteBrowser {
            browserCustomCommand: (arg: any) => Promise<void>
        }

        interface Element {
            elementCustomCommand: (arg: any) => Promise<number>
        }
    }
}

<Tabs defaultValue="async" values={[ {label: 'Async', value: 'async'}, {label: 'Sync', value: 'sync'}, ] }>

declare namespace WebdriverIO {
    interface Browser {
        browserCustomCommand: (arg: any) => void
    }

    interface MultiRemoteBrowser {
        browserCustomCommand: (arg: any) => void
    }

    interface Element {
        elementCustomCommand: (arg: any) => number
    }
}
declare namespace WebdriverIO {
    interface Browser {
        browserCustomCommand: (arg: any) => Promise<void>
    }

    interface MultiRemoteBrowser {
        browserCustomCommand: (arg: any) => Promise<void>
    }

    interface Element {
        elementCustomCommand: (arg: any) => Promise<number>
    }
}

Tips and Hints

tsconfig.json example

<Tabs defaultValue="modules" values={[ {label: 'Modules (using import/export)', value: 'modules'}, {label: 'Ambient Type Definitions (no tsconfig include)', value: 'ambient'}, ] }>

{
  "compilerOptions": {
    "outDir": "./.tsbuild/",
    "sourceMap": false,
    "target": "es2019",
    "module": "commonjs",
    "removeComments": true,
    "noImplicitAny": true,
    "strictPropertyInitialization": true,
    "strictNullChecks": true,
    "types": [
      "node",
      "webdriverio/async",
      "@wdio/mocha-framework"
    ]
  },
  "include": [
    "./test/**/*.ts",
    "./src/types/**/*.ts"
  ]
}
{
  "compilerOptions": {
    "outDir": "./.tsbuild/",
    "sourceMap": false,
    "target": "es2019",
    "module": "commonjs",
    "removeComments": true,
    "noImplicitAny": true,
    "strictPropertyInitialization": true,
    "strictNullChecks": true,
    "types": [
      "node",
      "webdriverio/async",
      "@wdio/mocha-framework"
    ]
  }
}

Compile & Lint

To be entirely safe, you may consider following the best practices: compile your code with TypeScript compiler (run tsc or npx tsc) and have eslint running on pre-commit hook.