π§ͺ @pokujs/angular is a Poku plugin for Angular component testing with DOM adapters.
Tip
Render standalone Angular components in isolated test files β automatic TypeScript loader injection, DOM environment setup, Angular TestBed configuration, and optional render metrics included.
npm i -D @pokujs/angularInstall a DOM adapter (at least one is required):
# happy-dom (recommended)
npm i -D happy-dom \
@happy-dom/global-registrator |
# jsdom
npm i -D jsdom |
// poku.config.js
import { defineConfig } from 'poku';
import { angularTestingPlugin } from '@pokujs/angular/plugin';
export default defineConfig({
plugins: [
angularTestingPlugin({
dom: 'happy-dom',
}),
],
});// tests/counter.test.ts
import { afterEach, assert, test } from 'poku';
import { cleanup, fireEvent, render, screen } from '@pokujs/angular';
import { CounterButton } from './CounterButton.ts';
afterEach(cleanup);
await test('increments the counter', async () => {
await render(CounterButton, { inputs: { initialCount: 1 } });
assert.strictEqual(
screen.getByRole('heading').textContent,
'Count: 1'
);
await fireEvent.click(screen.getByRole('button', { name: 'Increment' }));
assert.strictEqual(
screen.getByRole('heading').textContent,
'Count: 2'
);
});Important
Because Angular's TestBed module state is global, use await test(...) (not bare test(...)) within each test file to ensure tests execute sequentially. Concurrent test execution will cause configureTestingModule() to reset a sibling test's fixture mid-run.
| Node.js β₯ 20 | Bun β₯ 1 | |
|---|---|---|
| happy-dom | β | β |
| jsdom | β | β |
Mounts a standalone Angular component into a fresh TestBed module and returns Testing Library queries together with Angular-specific helpers.
const view = await render(MyComponent, {
/** Signal inputs (input() / input.required()) */
inputs: { title: 'Hello' },
/** Additional providers for the test module */
providers: [{ provide: TOKEN, useValue: 'value' }],
/** Additional imports (shared modules, pipes, directives) */
imports: [SharedModule],
/** Set false to skip the initial detectChanges() cycle */
detectChanges: true,
});
// Testing Library queries (scoped to document.body)
view.getByRole('heading');
// Angular-specific helpers
await view.detectChanges(); // run a CD cycle
await view.rerender({ title: 'Hi' }); // update inputs
view.unmount(); // destroy fixture
view.fixture; // raw ComponentFixtureRuns a factory function inside Angular's injection context so it can call inject() and use signals.
const { result, rerender, unmount } = renderHook(
() => inject(CounterService)
);
result.current.increment();
assert.strictEqual(result.current.count(), 1);Destroys all mounted fixtures and resets TestBed. Call in afterEach.
afterEach(cleanup);Lazy proxy over @testing-library/dom's screen β safe across test isolation boundaries.
Async wrapper around @testing-library/dom's fireEvent β automatically triggers Angular change detection after every event.
await fireEvent.click(button);
await fireEvent.input(input, { target: { value: 'hello' } });angularTestingPlugin({
/**
* DOM adapter to use.
* - 'happy-dom' β fast, recommended for most tests
* - 'jsdom' β broader browser API coverage
* - { setupModule } β path to a custom DOM setup module
*/
dom: 'happy-dom',
/** Base URL assigned to the DOM environment. */
domUrl: 'http://localhost:3000/',
/**
* Render metrics. Disabled by default.
* Pass `true` for defaults, or an object for fine-grained control.
*/
metrics: {
enabled: true,
topN: 5,
minDurationMs: 0,
reporter(summary) {
console.log(summary.topSlowest);
},
},
});