wdio-component-testing-service
TypeScript icon, indicating that this package has built-in type declarations

0.0.1 • Public • Published

WebdriverIO Component Testing Service Test

A WebdriverIO service that allows to run component tests for Lit, Vue or Svelte web components.

This WebdriverIO service allows to render components of your favorite frontend framework and run tests on it.

Supported Frameworks:

⚠️ Attention: this project is currenlty work in progress! Changes to interfaces might happen at any time without announcement. Any feedback is welcome!

Install

To set-up the service, run:

npm i --save-dev wdio-component-testing-service

or using Yarn:

yarn add --dev wdio-component-testing-service

Then add the service to the service list in your wdio.conf.(t|j)s:

// wdio.conf.js
export const config = {
    // ...
    services: [
        ['component-testing', {
            framework: 'lit' // (optional), also available: "vue", "svelte"
        }]
    ]
}

Usage

The service adds two commands to the browser object that you can use to run your tests: mount and the more versatile render method.

mount

The mount method allows to specify a single file component and its props to be rendered within the browser. It returns the root element of the rendered web component.

Signature
browser.mount(fileName: string, props: Record<string, any>, options: RenderOptions) => WebdriverIO.Element
Parameters
  • fileName: path to component file
  • props: props object to be passed as input parameters to the component
  • options: render options
    • framework: (optional) framework used to render the component (mostly detect automatically)
    • imports: (optional) a set of file references or inline scripts to be loaded before the component is rendered

Using Lit

Given the following Lit component:
./example/lit/HelloWorld.ts
import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'

@customElement('hello-world')
export default class MyElement extends LitElement {
    declare verb: string
    declare subject: string

    static properties = {
        verb: { type: String },
        subject: { type: String }
    }

    constructor () {
        super()
        this.verb = 'Hello'
        this.subject = 'World'
    }

    render() {
        return html/*html*/`<b>${this.verb} ${this.subject}!</b>
    }
}

A component test using this service could look like this:

describe('component testing service', () => {
    it('should mount a lit component', async () => {
        const elem = await browser.mount('./example/lit/HelloWorld.ts', {}, {
            framework: 'lit'
        })
        await expect(elem).toHaveText('Hello World!')
    })

    it('should mount a lit component with props', async () => {
        const options = { framework: 'lit' as const }
        const elem = await browser.mount('./example/lit/HelloWorld.ts', {
            verb: 'Love',
            subject: 'Web Components'
        }, options)
        await expect(elem).toHaveText('Love Web Components!')
    })
})

Using Svelte

Given the following Svelte component:
./example/svelte/HelloWorld.svelte
<script>
    export let verb = 'Hello';
    export let subject = 'World';
</script>

<h1>{verb} {subject}!</h1>

A component test using this service could look like this:

describe('component testing service', () => {
    it('should mount a Svelte component', async () => {
        const elem = await browser.mount('./example/svelte/HelloWorld.svelte')
        await expect(elem).toHaveText('Hello World!')
    })

    it('should mount a Svelte component with props', async () => {
        const elem = await browser.mount('./example/svelte/HelloWorld.svelte', {
            verb: 'Love',
            subject: 'Web Components'
        })
        await expect(elem).toHaveText('Love Web Components!')
    })
})

Using Vue.js

Given the following Vue.js component:
./example/svelte/HelloWorld.svelte
<script>
export default {
    props: {
        verb: String,
        subject: String
    },
    data() {
        return {
            message: `${this.verb || 'Hello'} ${this.subject || 'World'}!`
        }
    },
    methods: {
        reverseMessage() {
            this.message = this.message.split('').reverse().join('')
        },
        notify() {
            alert('navigation was prevented.')
        }
    }
}
</script>

<template>
    <!--
        Note we don't need .value inside templates because
        refs are automatically "unwrapped" in templates.
      -->
    <h1>{{ message }}</h1>

    <!--
        Bind to a method/function.
        The @click syntax is short for v-on:click.
      -->
    <button @click="reverseMessage">Reverse Message</button>

    <!-- Can also be an inline expression statement -->
    <button @click="message += '!'">Append "!"</button>

    <!--
        Vue also provides modifiers for common tasks
        such as e.preventDefault() and e.stopPropagation()
      -->
    <a href="https://vuejs.org" @click.prevent="notify">
        A link with e.preventDefault()
    </a>
</template>

<style>
button,
a {
    display: block;
    margin-bottom: 1em;
}
</style>

A component test using this service could look like this:

describe('component testing service', () => {
    it('should mount a Vue component', async () => {
        const elem = await browser.mount('./example/vue/HelloWorld.vue')

        const h1 = await elem.$('h1')
        const [ reverseBtn, appendBtn ] = await elem.$$('button')
        await expect(h1).toHaveText('Hello World!')

        await reverseBtn.click()
        await expect(h1).toHaveText('!dlroW olleH')

        await appendBtn.click()
        await expect(h1).toHaveText('!dlroW olleH!')
    })

    it('should mount a Vue component with props', async () => {
        const elem = await browser.mount('./example/vue/HelloWorld.vue', {
            verb: 'Love',
            subject: 'Web Components'
        })

        const h1 = await elem.$('h1')
        await expect(h1).toHaveText('Love Web Components!')
    })
})

render

The render method is more versatile and allows rendering all kinds of HTML. It is being used internally when calling the mount.

Signature
browser.render(html: string | Function, options: RenderOptions) => WebdriverIO.Element
Parameters
  • fileName: path to component file
  • options: render options
    • framework: (optional) framework used to render the component (mostly detect automatically)
    • imports: (optional) a set of file references or inline scripts to be loaded before the component is rendered
    • props: props object to be passed as input parameters to the component

Example

The following test shows how render can be used to render dynamic templates, e.g.:

describe('component testing service base features', () => {
    it('should allow referenced imports', async () => {
        const elem = await browser.render(({ tagName, verb, subject }) => (
            /*html*/`<${a.tagName}>${a.verb} ${a.subject}</${a.tagName}>`
        ), {
            imports: {
                tagName: () => 'i',
                verb: () => 'Hello',
                subject: () => 'World!'
            }
        })
        expect(await elem.getHTML()).toBe('<i>Hello World!</i>')
    })
})

Package Sidebar

Install

npm i wdio-component-testing-service

Weekly Downloads

5

Version

0.0.1

License

MIT

Unpacked Size

65.7 kB

Total Files

52

Last publish

Collaborators

  • wdio-user