1

Spectron is a node.js framework for automating Electron apps. I'm using Spectron along with AVA and Typescript to do automated Integration Testing. I'm using AVA's suggest method for making the test's context typesafe but I'm not able to figure out how to get typesafety on the Spectron's client property which is a webdriverio client. I'm only able to see a few properties which the Spectron typescript definition file has provided and this is causing typescript transpile errors.

These are the errors I'm getting:

src/pages/drive-shell.ts(7,34): error TS2339: Property 'waitForVisible' does not exist on type 'SpectronClient'.
src/pages/login.ts(7,34): error TS2339: Property 'waitForVisible' does not exist on type 'SpectronClient'.
src/pages/login.ts(11,21): error TS2339: Property 'setValue' does not exist on type 'SpectronClient'.
src/pages/login.ts(12,21): error TS2339: Property 'setValue' does not exist on type 'SpectronClient'.
src/pages/login.ts(13,21): error TS2339: Property 'click' does not exist on type 'SpectronClient'.
Evan Larsen
  • 9,935
  • 4
  • 46
  • 60

3 Answers3

4

As of right now (July 2019), WebdriverIO has moved to version 5.0, but Spectron still uses WebdriverIO 4. Make sure to specify the version used in Spectron when installing the WebdriverIO typings to resolve this issue:

npm i -S @types/webdriverio@^4.8.0
modelDBA
  • 176
  • 1
  • 8
2

I actually solved this as I was typing up the question but figured since I did some searching and couldn't find any solutions I thought I might answer my own question in order to help others.

I needed to get the typings for webdriver io

npm i -S @types/webdriverio

and then I imported that type into my login.ts script and use that as the SpectronClient

import * as WebdriverIO from 'webdriverio';
export class Login {
    constructor(private client: WebdriverIO.Client<void>) { }

    public async waitForPageToLoad() {
        return await this.client.waitForVisible('#username');
    }

    public login(username: string, password: string) {
        this.client.setValue('#username', username);
        this.client.setValue('#Password', password);
        this.client.click('#login');
    }
}

and here is my full test.ts test script

import * as ava from 'ava';
import { Application } from 'spectron';
import { Login } from './pages/login';
import { Settings } from './settings';

function contextualize<T>(getContext: () => Promise<T>): ava.RegisterContextual<T> {
    ava.test.beforeEach(async (t) => {
        Object.assign(t.context, await getContext());
    });
    return ava.test;
}
const test = contextualize(async () => {
    const app = new Application({
        path: '../electron.exe',
        args: ['../app/index.html'],
    });
    await app.start();
    return { app };
});

test.afterEach.always(async (t) => await t.context.app.stop());

test('can login', async (t) => {
    const login = new Login(t.context.app.client);
    await login.waitForPageToLoad();
    login.login(Settings.username, Settings.password);
});
Evan Larsen
  • 9,935
  • 4
  • 46
  • 60
0

After spending almost one entire day and digging deep into the source code I found the Spectron documentation is wrong.

It clearly says,

Spectron uses WebdriverIO and exposes the managed client property on the created Application instances. The client API is WebdriverIO's browser object.

But as can be seen in the WebdriverIO documentation the functions like click, setValue, etc are not part of the browser object. They are available under the element object. So here is what I did,

const inputText = await app.client.$('user_name'); // get hold of the element
inputText.click(); // get access to the WebdriverIO functions
coda
  • 2,188
  • 2
  • 22
  • 26