@rnw-community/wdio
TypeScript icon, indicating that this package has built-in type declarations

0.67.0 • Public • Published

WDIO commands and utils

WDIO commands and utils.

npm version npm downloads

TODO

  • [ ] Refactor tests to mock MockElement(rename to MockElementPromise), use it instead of mockElement object
  • [ ] Rename all tests its properly after API stabilizes
  • [ ] Write docs with the examples
  • [ ] Add tests for extended class instance ctor return from async function - it resolves to WDIO element =)
  • [ ] Rename everything with $ to CSS to make things more clear, leave backward compatibility
  • [ ] Remove all dependencies from @wdio, library should be agnostic, and support wdio and playwright via adapters(maybe others):
  • [ ] Create generic adapter and element interface
  • [ ] Playwright does not support ChainablePromise, find a best way for this API(we still can have it btw)
  • [ ] Add wdio adapter
  • [ ] Add playwright adapter
  • [ ] Add puppeteer adapter
  • [ ] Add check and human readable error for stale/missing RootEl in the rooted component and tests/docs for this case
  • [ ] rename get*Component* mixins to community standard
  • [ ] refactor Component classes proxy approach, add more tests
  • [ ] add documentation and tests for each custom command, make writing this streamlined and extendable
  • [ ] add documentation and tests for parent component inheritance and tests

Installation

Install additional peer dependencies:

Usage

Utils

setTestID

Setting testID for each platform can produce warning, setTestID(...ids) fixes it and has support for dynamically generated components.

Example usage:

import React, { FC } from 'react';
import { Text } from 'react-native';

import { IOSTestIDProps, setTestId } from '@rnw-community/wdio';

export const DynamicComponent: = ({ testID = 'ParentTestID' }:IOSTestIDProps) => (
    <Text {...setTestId(testID, `Text`)}>Text</Text>
);

Which will generate ParentTestID_Text;

getTestID

setPropTestID

Setting testID similar to setTestID but with overriding default testID.

import React from 'react';
import { Text } from 'react-native';
import { TestIDProps } from '@rnw-community/wdio';

interface Props extends TestIDProps, React.PropsWithChildren {
    testID?: string;
}

const defaultTestID = 'DynamicComponent.Root';

export const DynamicComponent = ({ children, ...props }: Props) => (
    <View {...setPropTestID(defaultTestID, props)}>{children}</View>
);

Commands

Setup

copy 2 d.ts files into root of your e2e project

add wdio.config.js to your e2e project

import { addWdioCommands } from '@rnw-community/wdio';

export const wdioBaseConfiguration = (): WebdriverIO.Config => ({
    //...your config
    before(_capabilities, _specs, browser: WebdriverIO.Browser) {
        addWdioCommands(browser);
    },
});

Usage

openDeepLink

No more manually open safary, locate address and enter link with unicode enter in the end

import driver from '@wdio/globals';

describe('DeepLink', () => {
    it('should open deep link', async () => {
        await driver.openDeepLink('myapp://products/1234');
    });
});

testID$ and testID$$

testID$ and testID$$ are the same as $ and $$ but with support for testID selector

import driver from '@wdio/globals';

describe('DynamicComponent', () => {
    it('should find component', async () => {
        await expect(driver.testID$('DynamicComponent.Root')).toBeDisplayed();
    });
});

testID$$Index

testID$$Index is the same as $$ but with support for testID selector and index. Returns element by index

import driver from '@wdio/globals';

describe('DynamicComponent', () => {
    it('should find root element', async () => {
        await expect(driver.testID$$Index('DynamicComponent.Root', 0)).toBeDisplayed();
    });
});

slowInput

slowInput is the same as setValue but with support for typing speed

import driver from '@wdio/globals';
import { FormSelectors } from 'my-react-native-project/src/form.selectors';

describe('Form', () => {
    it('should input test slowly', async () => {
        await driver.testID$(FormSelectors.Input).slowInput('test', 100);
    });
});

clearInput

clearInput does several things:

  • clearValue which usually doesn't work
  • setValue('') which usually doesn't work either
  • gets text and deletes it character by character
import driver from '@wdio/globals';
import { FormSelectors } from 'my-react-native-project/src/form.selectors';

describe('Form', () => {
    it('should clear input', async () => {
        await driver.testID$(FormSelectors.Input).clearInput();
    });
});

relativeClick

relativeClick clicks on element relative to it's size in percents

import driver from '@wdio/globals';
import { FormSelectors } from 'my-react-native-project/src/form.selectors';

describe('Form', () => {
    it('should click on the center of the element', async () => {
        await driver.testID$(FormSelectors.Button).relativeClick(50, 50);
    });
});

Components

createComponent

createComponent is a helper function to create wdio components with testID support

import { createComponent } from '@rnw-community/wdio';
import { CardSelectors } from 'my-react-native-project/src/card.selectors';

describe('Card', () => {
    it('should find component', async () => {
        const card = createComponent(CardSelectors);
        const cards = await card.els();
        await expect(cards).toHaveLength(3);

        const lastCard = await card.byIdx(2);
        await expect(lastCard.Root).toBeDisplayed();
        await expect(lastCard.Text).toHaveText('Card 3');
        await lastCard.CloseButton.click();
        await expect(card.els()).resolves.toHaveLength(2);
    });
});

getComponent

getComponent is a helper function to get wdio component class with testID support

card.component.ts

import { getComponent } from '@rnw-community/wdio';
import { CardSelectors } from 'my-react-native-project/src/card.selectors';

export class Card extends getComponent(CardSelectors) {
    public async close() {
        await this.CloseButton.click();
    }
}

card.spec.ts

import { Card } from './card.component';

describe('Card', () => {
    it('should find component', async () => {
        const card = new Card();
        const cards = await card.els();
        await expect(cards).toHaveLength(3);

        const lastCard = await card.byIdx(2);
        await expect(lastCard.Root).toBeDisplayed();
        await expect(lastCard.Text).toHaveText('Card 3');
        // no need to use lastCard.CloseButton.click();
        await lastCard.close();
        await expect(card.els()).resolves.toHaveLength(2);
    });
});

createRootedComponent

createRootedComponent is a helper function to create wdio components with testID support and root element

import { createRootedComponent } from '@rnw-community/wdio';
import { CardSelectors } from 'my-react-native-project/src/card.selectors';

describe('Card', () => {
    it('should find component', async () => {
        const card = createRootedComponent(CardSelectors);
        const cards = await card.els();
        await expect(cards).toHaveLength(3);

        const lastCard = await card.byIdx(2);
        // no need to use lastCard.Root because it's already in the Card class
        await expect(lastCard).toBeDisplayed();
        await expect(lastCard.Text).toHaveText('Card 3');
        await lastCard.CloseButton.click();
        await expect(card.els()).resolves.toHaveLength(2);
    });
});

getRootedComponent

getRootedComponent is a helper function to get wdio component class with testID support and root element

card.component.ts

import { getRootedComponent } from '@rnw-community/wdio';
import { CardSelectors } from 'my-react-native-project/src/card.selectors';

export class Card extends getRootedComponent(CardSelectors) {
    public async close() {
        await this.CloseButton.click();
    }
}

card.spec.ts

import { Card } from './card.component';

describe('Card', () => {
    it('should find component', async () => {
        const card = new Card();
        const cards = await card.els();
        await expect(cards).toHaveLength(3);

        const lastCard = await card.byIdx(2);
        // no need to use lastCard.Root because it's already in the Card class
        await expect(lastCard).toBeDisplayed();
        await expect(lastCard.Text).toHaveText('Card 3');
        // no need to use lastCard.CloseButton.click();
        await lastCard.close();
        await expect(card.els()).resolves.toHaveLength(2);
    });
});

Recommendations

Create Selectors enum for each component close to the component file

card.selectors.ts

export enum CardSelectors {
    Root = 'Root',
    Text = 'Text',
}

card.tsx

import { CardSelectors as Selectors } from './card.selectors';
export const Card = () => (
    <View {...setTestID(CardSelectors.Root)}>
        <Text {...setTestID(CardSelectors.Text)}>Text</Text>
    </View>
);

Export all selectors from your project in selectors.ts file in your src folder

selectors.ts

export * from './card/card.selectors';

License

This library is licensed under The MIT License.

Package Sidebar

Install

npm i @rnw-community/wdio

Weekly Downloads

135

Version

0.67.0

License

MIT

Unpacked Size

240 kB

Total Files

547

Last publish

Collaborators

  • vitalyiegorov