Skills for building AEM Edge Delivery Services sites — block development, content modeling, code review, testing, and page import.
82
76%
Does it follow best practices?
Impact
88%
1.04xAverage score across 6 eval scenarios
Advisory
Suggest reviewing before use
Unit tests are keeper tests for logic-heavy code that benefits from automated regression testing. This guide covers when to write unit tests, how to structure them, and best practices for maintainable test suites.
Write unit tests for:
Do NOT write unit tests for:
This guide assumes Vitest is already configured in the project. If not, see vitest-setup.md for one-time setup instructions.
Verify test setup exists:
npm test # Should run without errors (even if no tests exist yet)If the command fails or Vitest is not installed, consult vitest-setup.md.
Important: Ensure test files are not served to production by adding them to .hlxignore:
# .hlxignore
test/
*.test.jsThis prevents test files from being accessible on your live site.
Place test files next to the code they test:
scripts/utils/my-utility.js → test/utils/my-utility.test.jsblocks/hero/utils.js → test/blocks/hero/utils.test.jsNaming convention: {filename}.test.js
import { describe, it, expect } from 'vitest';
import { myUtility } from '../../scripts/utils/my-utility.js';
describe('myUtility', () => {
it('should transform input correctly', () => {
const input = { foo: 'bar' };
const result = myUtility(input);
expect(result).toEqual({ foo: 'BAR' });
});
it('should handle edge cases', () => {
expect(myUtility(null)).toBeNull();
expect(myUtility({})).toEqual({});
});
});npm test # Run all tests once
npm run test:watch # Run tests in watch mode
npm run test:ui # Open interactive UI
npm run test:coverage # Generate coverage reportGood unit tests are:
Here's an example of a keeper test that's worth maintaining:
// scripts/utils/url-helpers.js
export function normalizeUrl(url, base) {
if (!url) return '';
if (url.startsWith('http://') || url.startsWith('https://')) return url;
if (url.startsWith('/')) return `${base}${url}`;
return `${base}/${url}`;
}
// test/utils/url-helpers.test.js
import { describe, it, expect } from 'vitest';
import { normalizeUrl } from '../../scripts/utils/url-helpers.js';
describe('normalizeUrl', () => {
const base = 'https://example.com';
it('returns empty string for null/undefined', () => {
expect(normalizeUrl(null, base)).toBe('');
expect(normalizeUrl(undefined, base)).toBe('');
});
it('returns absolute URLs unchanged', () => {
expect(normalizeUrl('https://other.com/path', base)).toBe('https://other.com/path');
expect(normalizeUrl('http://other.com/path', base)).toBe('http://other.com/path');
});
it('prepends base to root-relative URLs', () => {
expect(normalizeUrl('/path/to/page', base)).toBe('https://example.com/path/to/page');
});
it('prepends base with slash to relative URLs', () => {
expect(normalizeUrl('path/to/page', base)).toBe('https://example.com/path/to/page');
});
});Why this test is worth maintaining:
When testing DOM-dependent code, jsdom provides a browser-like environment:
import { describe, it, expect, beforeEach } from 'vitest';
import { JSDOM } from 'jsdom';
describe('DOM manipulation', () => {
let document;
beforeEach(() => {
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
document = dom.window.document;
global.document = document;
});
it('should create elements', () => {
const div = document.createElement('div');
div.textContent = 'Hello';
expect(div.textContent).toBe('Hello');
});
});Integration tests validate that multiple components work together correctly. These fall into the "keeper" category if they test critical workflows.
When to write integration tests:
Integration tests use the same Vitest setup as unit tests but test multiple components together.
Example integration test:
// test/integration/auto-blocking.test.js
import { describe, it, expect, beforeEach } from 'vitest';
import { JSDOM } from 'jsdom';
import { buildAutoBlocks } from '../../scripts/scripts.js';
describe('Auto-blocking integration', () => {
let document;
beforeEach(() => {
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
document = dom.window.document;
global.document = document;
});
it('should auto-block hero from first section with image', () => {
const main = document.createElement('main');
const section = document.createElement('div');
const picture = document.createElement('picture');
section.appendChild(picture);
main.appendChild(section);
buildAutoBlocks(main);
const hero = main.querySelector('.hero');
expect(hero).toBeTruthy();
expect(hero.querySelector('picture')).toBeTruthy();
});
});Integration tests are worth maintaining if:
it('should fetch data asynchronously', async () => {
const result = await fetchData();
expect(result).toBeDefined();
});describe('Calculator', () => {
let calculator;
beforeEach(() => {
calculator = new Calculator();
});
it('should add numbers', () => {
expect(calculator.add(2, 3)).toBe(5);
});
});it('should throw error for invalid input', () => {
expect(() => {
validateInput('invalid');
}).toThrow('Invalid input');
});Once you've written unit tests:
npm run test:watchnpm testnpm run test:coverageevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
skills
analyze-and-plan
block-collection-and-party
block-inventory
building-blocks
code-review
content-driven-development
content-modeling
docs-search
find-test-content
generate-import-html
identify-page-structure
page-decomposition
page-import
preview-import
scrape-webpage
testing-blocks