Home

Awesome

test npm version

Alpine.js Test Utils

Utilities for testing Alpine.js components.

This library allows you to quickly and easily write tests for Alpine.js applications via Node.js using any testing library.

That means you can use AVA, Tape, Mocha, Jest or whatever other testing library you enjoy using.

This project is not officially affiliated with Alpine.js, it's maintained by community members. For any feedback, questions or issues, please create issues and pull requests or merely upvote or comment on existing issues or pull requests.

Table of Contents

Installation

Prerequisites

Install Package

The following recommended installation requires npm. If you are unfamiliar with npm, see the npm docs. Npm comes installed with Node.js since node version 0.8.x, therefore, you likely already have it.

npm install --save-dev alpine-test-utils

You may also use yarn to install.

yarn add --dev alpine-test-utils

Peer Dependencies

IMPORTANT: If you're loading Alpine.js from CDN (using a script tag) you'll need to install alpinejs in order to use alpine-test-utils. It should be the same version as the version of Alpine.js you are loading from CDN. using.

npm install --save-dev alpinejs
# or for Yarn users
yarn add --dev alpinejs

<a name="quick-start"></a>

Quick Start, Write your first test

Here's an example to render a simple Alpine.js component using Jest/Jasmine syntax:

import {render} from 'alpine-test-utils';

test('test foo component', () => {
  const componentHtml = `<div x-data="{foo: 'bar'}">
    <span x-text="foo"></span>
  </div>`
  const component = render(componentHtml);
  expect(component.querySelector('span').textContent).toEqual('bar');
});

For more complex use cases, please see USE_CASES.md or for the full API, see the following section.

API

MethodDescription
renderRender & run Alpine.js component markup
loadExtract Alpine.js component markup from files
loadSyncSynchronous variant of load
waitForWait for an assertion to pass
$nextTickWait for a re-render or async work to happen
setGlobalOverride globals using an object
setMutationObserverSet a custom MutationObserver implementation

render

Render Alpine.js Component Markup to JSDOM & initialise Alpine.js.

Parameters:

Returns:

Usage Example: render a component and check text is displayed as per x-data.

test('component renders content of "foo" in span', () => {
  const component = render(`<div x-data="{ foo: 'bar' }">
    <span x-text="foo"></span>
  </div>`);
  expect(component.querySelector('span').textContent).toEqual('bar');
});

For a more advanced example see Clicking a button to toggle visibility.

load/loadSync

Load markup from a file asynchronously using a path.

Note: when a single x-data Alpine.js component is found in the file, it is returned. If multiple components are found, all are returned in an Array.

Parameters:

Returns:

Usage Example: load a PHP template, see the full use-case.

test('my test', async () => {
  const markupAsync = await load(path.join(__dirname, '../fixtures/template.php'));
  const markupSync = loadSync(path.join(__dirname, '../fixtures/template.php'));
});

waitFor

Wait until assertions pass, wrapper for wait-for-expect.

Parameters:

Returns: Promise that resolves/rejects based on whether the assertions eventually pass.

Usage example: for more advanced use-cases see Clicking a button to toggle visibility and Intercepting fetch calls & waiting for re-renders

test('clicking a button to toggle visibility', async () => {
  const component = render(`<div x-data="{ isOpen: false }">
    <button @click="isOpen = !isOpen"></button>
    <span x-show="isOpen"></span>
  </div>`);

  expect(component.querySelector('span').style.display).toEqual('none');
  component.querySelector('button').click();
  await waitFor(() => {
    expect(component.querySelector('span').style.display).toEqual('');
  });
});

$nextTick

Note: prefer waitFor it's more flexible and accurate.

Function to wait until a render/async operation happens.

Parameters: none.

Returns:

Note this exported as a global from the Alpine Test Utils module and is attached to components during render, see render.

test('clicking a button to toggle visibility', async () => {
  const component = render(`<div x-data="{ isOpen: false }">
    <button @click="isOpen = !isOpen"></button>
    <span x-show="isOpen"></span>
  </div>`);

  expect(component.querySelector('span').style.display).toEqual('none');
  component.querySelector('button').click();
  await component.$nextTick();
  expect(component.querySelector('span').style.display).toEqual('');
});

setGlobal

Override Node.js global using passed override object.

The implementation is literally Object.assign(global, override).

Parameters:

Returns: none.

Usage example: overring global.fetch, see the full use case Intercepting fetch calls & waiting for re-renders.

test('intercepting fetch calls', async () => {
  setGlobal({
    fetch: () =>
      Promise.resolve({
        json: () => Promise.resolve(['data-1', 'data-2'])
      })
  });
});

Roadmap

If you are interested in the future direction of this project, please take a look at the open issues and pull requests. I would love to hear your feedback!

Contributing

Requirements

Setup

  1. Clone the repository
  2. Run yarn or npm install installs all required dependencies.
  3. Run yarn test to run all tests :D.

npm scripts

Equivalent npm run <script> should also work

About

This package is maintained by Hugo from Code with Hugo and Alpine.js Weekly.

Acknowledgments

Special thanks to:

LICENSE

Code is licensed under the MIT License.