| name | write-unit-test |
| description | Generate unit tests for Nuxeo Elements Polymer components. Use this skill when the user wants to write tests, add test coverage, create test files, or test a web component. Generates @web/test-runner + Mocha + Chai + Sinon test suites using @nuxeo/testing-helpers with proper fixtures, mocks, and assertions. Also use when the user mentions testing an element, verifying behavior, or checking component functionality. |
Write Unit Test
Generate unit test files for Nuxeo Elements Polymer components using @web/test-runner + Mocha + Chai + Sinon.
Workflow
- Identify the element to test (read its source first to understand properties, methods, events)
- Determine what to test: properties, methods, events, DOM rendering, user interactions
- Generate the test file following the patterns below
- Run
npm test to verify (or npm run test:core, npm run test:ui, npm run test:dataviz)
The test runner loads one generated barrel per package (<package>/test/load-all-tests.js).
After creating a new test file, run npm run update-test-load-all (or npm test, which
regenerates it automatically) so the new suite is picked up.
File Location
core/test/nuxeo-{element-name}.test.js # For core elements
ui/test/nuxeo-{element-name}.test.js # For UI elements
dataviz/test/nuxeo-{element-name}.test.js # For dataviz elements
Template: Basic Test
import { fixture, html } from '@nuxeo/testing-helpers';
import '../nuxeo-{element-name}.js';
suite('nuxeo-{element-name}', () => {
let element;
setup(async () => {
element = await fixture(html`<nuxeo-{element-name}></nuxeo-{element-name}>`);
});
test('should initialize with default properties', () => {
expect(element).to.exist;
});
});
Template: Test with Document Fixture
import { fixture, html } from '@nuxeo/testing-helpers';
import '../nuxeo-{element-name}.js';
suite('nuxeo-{element-name}', () => {
let element;
const document = {
'entity-type': 'document',
uid: '1234',
path: '/default-domain/workspaces/test-doc',
type: 'File',
title: 'Test Document',
properties: {
'dc:title': 'Test Document',
'dc:description': 'A test document',
'dc:created': '2023-01-01T00:00:00.000Z',
'dc:modified': '2023-06-15T12:00:00.000Z',
'dc:creator': 'Administrator',
'dc:contributors': ['Administrator'],
},
contextParameters: {
permissions: ['ReadWrite'],
},
facets: ['Versionable', 'Commentable'],
};
setup(async () => {
element = await fixture(html`<nuxeo-{element-name} .document="${document}"></nuxeo-{element-name}>`);
});
test('should display document title', () => {
expect(element.document.title).to.equal('Test Document');
});
});
Template: Test with Sinon Stubs
import { fixture, html } from '@nuxeo/testing-helpers';
import '../nuxeo-{element-name}.js';
suite('nuxeo-{element-name}', () => {
let element;
setup(async () => {
element = await fixture(html`<nuxeo-{element-name}></nuxeo-{element-name}>`);
sinon.stub(element, 'hasPermission').returns(true);
sinon.stub(element, 'isTrashed').returns(false);
});
suite('permission checks', () => {
test('should allow action when user has permission', () => {
expect(element.hasPermission('Write')).to.be.true;
});
test('should call method when triggered', () => {
const spy = sinon.spy(element, '_someMethod');
element._someMethod();
expect(spy).to.have.been.calledOnce;
});
});
});
Template: Test with Nuxeo Server Mock
import { fixture, fakeServer, html, waitForEvent } from '@nuxeo/testing-helpers';
import '../nuxeo-{element-name}.js';
suite('nuxeo-{element-name}', () => {
let server;
let element;
setup(async () => {
server = fakeServer.create();
element = await fixture(html`<nuxeo-{element-name}></nuxeo-{element-name}>`);
});
teardown(() => {
server.restore();
});
test('should fetch data from server', async () => {
server.respondWith('GET', '/api/v1/path/to/resource',
{ 'entity-type': 'document', title: 'Test' },
);
element.someProperty = 'value';
await waitForEvent(element, 'response');
expect(element.data).to.exist;
});
});
Rules
- File naming:
<package>/test/nuxeo-{element-name}.test.js
- NEVER use
.only — lint will block it
- Globals available (from
test/setup.js): expect, assert, sinon
- Use
@nuxeo/testing-helpers for fixture, fakeServer, html, waitForEvent
- Use
suite/test (Mocha TDD interface), not describe/it
- Use
setup/teardown, not beforeEach/afterEach
- Async setup:
setup(async () => { element = await fixture(…) })
- Property binding in fixtures: use
.property="${value}" syntax in template literals
- Sinon stubs: Stub behavior methods in setup
- Event testing: Use
sinon.spy or waitForEvent for async events
- Include the Hyland license header at the top of every test file
Common Assertion Patterns
expect(element.someProperty).to.equal('value');
expect(element.someProperty).to.be.true;
expect(element.someProperty).to.be.null;
expect(element.someArray).to.have.lengthOf(3);
expect(element.someObject).to.deep.equal({ key: 'value' });
expect(element.shadowRoot.querySelector('.my-class')).to.exist;
expect(element.shadowRoot.querySelector('#myId').textContent).to.equal('text');
expect(element.shadowRoot.querySelectorAll('li')).to.have.lengthOf(5);
expect(spy).to.have.been.called;
expect(spy).to.have.been.calledOnce;
expect(spy).to.have.been.calledWith('arg');
expect(stub).to.have.returned('value');
const listener = sinon.spy();
element.addEventListener('my-event', listener);
element.dispatchEvent(new CustomEvent('my-event', { detail: 'data' }));
expect(listener).to.have.been.called;
What to Test
- Default property values after element creation
- Property changes and their effects on the DOM
- Method behavior (especially private
_methods)
- Event firing (via
this.dispatchEvent() or this.fire())
- Conditional rendering (e.g.,
dom-if templates)
- User interactions (click handlers, input changes)
- Behavior methods (e.g., permission checks, formatting)
- Edge cases (null values, empty arrays, missing properties)