1.0.0 • Published 5 years ago

test-scenarii v1.0.0

Weekly downloads
1
License
MIT
Repository
-
Last release
5 years ago

test-scenarii

This package offers a way to write scenarized tests with maximum flexibility and code reuse, while retaining great readability.

The classic use case is to run several tests with very similar workflows — only with a twist each time.

Here's an example with a Todo App, where the creation of a TodoItem can be done 2 different ways: by setting a timestamp first, then the text or vice versa.

const { createTestChain, setChainProps } = require('test-scenarii')

describe(`Setting datetime then text`, () =>
{
    let testChain = null

    beforeAll( () =>
    {
        testChain = createTestChain({ runTests : true })
    })

    it(`must create the TodoItem from text then a datetime`, async () =>
    {
        await testChain(
            testStep.openApplication(),
            testStep.clickTodoItemCreateButton(),
            testStep.clickTimeTag(),
            testStep.setTodoItemText(),
            testStep.clickValidationButton()
        )
    })

    it(`must create the TodoItem from a datetime then text`, async () =>
    {
        await testChain(
            setChainProps({ runTests : false }),     // Turn testing off to prevent duplicate snapshots
            testStep.openApplication(),
            testStep.clickTodoItemCreateButton(),
            setChainProps({ runTests : true }),     // Turn testing back on
			testStep.setTodoItemText(),             // Notice clickTimeTag() and setTodoItemText()
			testStep.clickTimeTag(),                // are inverted here, compared to the previous test
            testStep.clickValidationButton()
        )
    })
})

GitHub coming soon

Table of Content

Installation

The test-scenarii package is distributed via npm. To install it, run:

npm install -D test-scenarii

The package will be installed in the devDependencies, alongside the test-runner you want to use it with, whether Jest, Jasmine or Mocha.

API

createTestChain

createTestChain() initializes a test chain with a set of props. The returned chain can be used several times in a row.

It works asynchronously as a default. See createTestChainSync() for the synchronous version.

createTestChain() and createTestChainSync() offer 2 ways to handle prop initialization: via a simple object or a prop getter.

// Initialize the chain with one set of props, no matter how many times it's used
testChain = createTestChain({ runTests : true })

// or

// Initialize the chain with a prop getter, which will be called everytime the chain is started
// This allows for some room in the initialization of the props in subsequent chains
const uuid = require('uuid/v4')
const getProps = () => ({ testRunUUID : uuid() })

let testChain = null

beforeAll( () =>
{
    testChain = createTestChain(getProps)
}

it(`uses the created chain for the first time`, async () =>
{
    await testChain( (ctx) =>
    {
        console.log(ctx.props.testRunUUID)      // A v4 UUID
    })
})

it(`uses the created chain for the second time`, async () =>
{
    await testChain( (ctx) =>
    {
        console.log(ctx.props.testRunUUID)      // A totally different v4 UUID
    })
})

createTestChainSync

createTestChainSync() works exactly the same as createTestChain(), except it only handles synchronous test steps

it(`should work exactly the same, except synchronously`, () =>
{
    testChain(
        testStep.some(),
        testStep.synchronous(),
        testStep.sequence(),
        testStep.of(),
        testStep.actions()
    )
})

setChainProps

The setChainProps() helper updates prop values somewhere along the chain.

Props can be updated by simply returning an object from any test step (it's actually how it's implemented); it improves readability however, to have them as a distinct step in the chain.

it(`makes no difference whether you use setChainProps() or a regular test step`, () =>
{
    testChain(
        setChainProps({ runTests : false })
    )

    // is exactly the same as:

    testChain(
        (ctx) => ({ runTests : false })
    )
}

Test Steps

The way the test steps are structured is key. The bare minimum they should do is separate the action they perform from the test they perform, in a way to is prop-controlled.

Examples use Puppeteer to navigate a project via Chromium instance.

Test steps can be made parametric via the following pattern:

// The following test step:
export function clickTimeTag (timeTag)
{
    return async (ctx) =>
    {
        // The Action
        await ctx.props.page.click(`button[data-time-tag=${timeTag}`)
        await wait(300)

        // The Test: snapshot-test the UI (toggleable at will via context props)
        if (ctx.props.runTests)
        {
            const renderedHTML = await ctx.props.page.$eval('.whole-panel', (el) => el.outerHTML)
            expect( renderedHTML ).toMatchSnapshot()
        }

        // Another Test: screenshot-test the UI (also toggleable at will via context props)
        if (ctx.props.runTests && ctx.props.takeScreenshots)
        {
            await ctx.props.page.screenshot({ path : `clickTimeTag-${Date.now()}` })
        }
    }
}

// Can be used via the following test chain:

const page = await browser.newPage()
await page.goto('http://localhost:3000')

const testChain = createTestChain(
{
    runTests: true,
    takeScreenshots: false,
    page: page
})

License

MIT

1.0.0

5 years ago

1.0.0-rc3

5 years ago

1.0.0-rc2

5 years ago

1.0.0-rc

5 years ago

0.2.42

5 years ago

0.2.41

5 years ago

0.2.4

5 years ago

0.2.3

6 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago