Skip to content

Instantly share code, notes, and snippets.

@bholtbholt
Last active June 20, 2024 08:34
Show Gist options
  • Save bholtbholt/c8351665a861aee62e915d8b32e2c759 to your computer and use it in GitHub Desktop.
Save bholtbholt/c8351665a861aee62e915d8b32e2c759 to your computer and use it in GitHub Desktop.
Testing Stimulus with Jest in a Rails App. Stimulus isn't mounted before the test runs, so these helpers wrap the calls in async functions to fix race conditions.
// This helper file provides a consistent API for testing Stimulus Controllers
//
// Use:
// import { getHTML, setHTML, startStimulus } from './_stimulus_helper';
// import MyController from '@javascripts/controllers/my_controller';
//
// beforeEach(() => startStimulus('my', MyController));
// test('should do something', async () => {
// await setHTML(`<button data-controller="my" data-action="my#action">click</button>`);
//
// const button = screen.getByText('click');
// await button.click();
//
// expect(getHTML()).toEqual('something');
// });
//
import { Application } from '@hotwired/stimulus';
// Initializes and registers the controller for the test file
// Use it in a before block:
// beforeEach(() => startStimulus('dom', DomController));
//
// @name = string of the controller
// @controller = controller class
//
// https://stimulus.hotwired.dev/handbook/installing#using-other-build-systems
export function startStimulus(name, controller) {
const application = Application.start();
application.register(name, controller);
}
// Helper function for setting HTML
// - It trims content to prevent false negatives
// - It's async so there's time for the Stimulus controller to load
//
// Use within tests:
// await setHTML(`<p>My HTML Content</p>`);
export async function setHTML(content = '') {
document.body.innerHTML = content.trim();
return document.body.innerHTML;
}
// Helper function for getting HTML content
// - Trims content to prevent false negatives
// - Is consistent with setHTML
//
// Use within tests:
// expect(getHTML()).toEqual('something');
export function getHTML() {
return document.body.innerHTML.trim();
}
import { screen } from '@testing-library/dom';
import { getHTML, setHTML, startStimulus } from './_stimulus_helper';
import DomController from '@javascripts/controllers/dom_controller';
beforeEach(() => startStimulus('dom', DomController));
test('should remove itself', async () => {
await setHTML(`<button data-controller="dom" data-action="dom#remove">remove</button>`);
const button = screen.getByText('remove');
await button.click();
expect(getHTML()).toEqual('');
});
test('should remove the parent element', async () => {
await setHTML(`
<div data-controller="dom">
<a data-action="dom#remove:once">remove</a>
</div>
`);
const button = screen.getByText('remove');
await button.click();
expect(getHTML()).toEqual('');
});
module.exports = {
setupFilesAfterEnv: ['./jest.setup.js'],
testMatch: ['**/test/**/*.test.js'],
cacheDirectory: './tmp/cache/jest',
moduleNameMapper: {
'@javascripts(.*)$': '<rootDir>/app/assets/javascripts$1',
},
testEnvironment: 'jsdom',
transformIgnorePatterns: ['node_modules']
};
import 'regenerator-runtime/runtime';
import '@testing-library/jest-dom';
@gwdox
Copy link

gwdox commented Mar 25, 2023

Thanks. Will report back if I figure out how to do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment