Skip to content

Instantly share code, notes, and snippets.

@kristoferjoseph
Created February 12, 2026 16:13
Show Gist options
  • Select an option

  • Save kristoferjoseph/a243ef6daa01811ad57b341745ee278a to your computer and use it in GitHub Desktop.

Select an option

Save kristoferjoseph/a243ef6daa01811ad57b341745ee278a to your computer and use it in GitHub Desktop.
Test HTML

@enhance/html-test

Browser-based HTML test runner with TAP output for testing Declarative Shadow DOM and enhance-dsd generated HTML.

Features

  • Zero external dependencies - Uses only Node.js built-ins
  • TAP v13 output - Compatible with standard TAP consumers
  • Browser & CI support - Works in both interactive and headless modes
  • Native DOM testing - Test real Declarative Shadow DOM in the browser
  • Import maps - Use bare specifiers for framework and utilities
  • Self-contained tests - HTML and assertions in the same file
  • Simple HTTP server - Built-in server for local and CI testing

Installation

npm install @enhance/html-test

Quick Start

1. Create a Test File

Create a file named test/my-component.test.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Test: My Component</title>
</head>
<body>
  <!-- Your HTML to test -->
  <my-component enhanced="">
    <template shadowrootmode="open">
      <style>:host { display: block; }</style>
      <slot></slot>
    </template>
    Hello World
  </my-component>

  <!-- Tests -->
  <script type="module">
    import { test, assert } from '@enhance/test-framework';

    test('has shadow root', () => {
      const el = document.querySelector('my-component');
      assert.ok(el.shadowRoot, 'Should have shadow root');
    });

    test('has enhanced attribute', () => {
      const el = document.querySelector('my-component');
      assert.strictEqual(el.getAttribute('enhanced'), '✨');
    });
  </script>
</body>
</html>

2. Run Tests

Interactive mode (opens in browser):

npm start
# or
html-test

Headless mode (for CI):

html-test --headless

CLI Usage

html-test [options]

Options:
  --port, -p <port>       Port to run server on (default: 3000)
  --root, -r <path>       Root directory to serve files from (default: cwd)
  --headless, -h          Run in headless Chrome mode
  --timeout, -t <ms>      Test timeout in milliseconds (default: 60000)

Examples

# Start interactive server on port 8080
html-test --port 8080

# Run headless tests with custom root
html-test --headless --root ./test

# CI/CD mode with TAP output
html-test --headless > test-results.tap

Test File Format

Test files are standard HTML files with .test.html extension containing:

  1. HTML to test - Your component markup, including Declarative Shadow DOM
  2. Test script - ES module that imports the test framework and defines tests
<!DOCTYPE html>
<html>
<head>
  <title>Test: Component Name</title>
</head>
<body>
  <!-- HTML being tested -->
  <my-element>
    <template shadowrootmode="open">
      <p>Shadow content</p>
    </template>
  </my-element>

  <!-- Test script -->
  <script type="module">
    import { test, assert, describe } from '@enhance/test-framework';

    describe('my-element', () => {
      test('renders correctly', () => {
        const el = document.querySelector('my-element');
        assert.ok(el);
      });
    });
  </script>
</body>
</html>

Test Framework API

Test Functions

test(name, fn)

Define a test.

test('component exists', () => {
  const el = document.querySelector('my-component');
  assert.ok(el);
});

describe(name, fn)

Group related tests (optional).

describe('my-component', () => {
  test('has shadow root', () => {
    assert.shadowRoot('my-component');
  });

  test('renders content', () => {
    assert.textContent('my-component', 'Expected text');
  });
});

Assertion Methods

Basic Assertions

  • assert.ok(value, message) - Value is truthy
  • assert.not(value, message) - Value is falsy
  • assert.strictEqual(actual, expected, message) - Strict equality (===)
  • assert.equal(actual, expected, message) - Loose equality (==)
  • assert.deepEqual(actual, expected, message) - Deep object/array equality
  • assert.match(string, regex, message) - String matches regex
  • assert.throws(fn, message) - Function throws an error

DOM Assertions

  • assert.querySelector(selector, message) - Element exists (returns element)
  • assert.textContent(selector, expected, message) - Element text content matches
  • assert.attribute(selector, attr, expected, message) - Attribute value matches
  • assert.hasAttribute(selector, attr, message) - Element has attribute
  • assert.shadowRoot(selector, message) - Element has shadow root (returns shadowRoot)

Import Maps

The test runner automatically provides an import map with:

  • @enhance/test-framework - The test framework
  • test-utils - Your local test/utils.js file (if it exists)
  • All packages in the monorepo

Using Shared Test Utilities

Create test/utils.js:

export function shadowQuery(selector, shadowSelector) {
  const host = document.querySelector(selector);
  return host?.shadowRoot?.querySelector(shadowSelector);
}

export function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Use in tests:

import { test, assert } from '@enhance/test-framework';
import { shadowQuery, sleep } from 'test-utils';

test('finds element in shadow DOM', () => {
  const el = shadowQuery('my-component', 'p');
  assert.ok(el);
});

TAP Output

Tests output TAP version 13 format:

TAP version 13
# Subtest: has shadow root
ok 1 - has shadow root
  ---
  duration_ms: 2.50
  type: 'test'
  ...
# Subtest: has enhanced attribute
ok 2 - has enhanced attribute
  ---
  duration_ms: 1.25
  type: 'test'
  ...
1..2
# tests 2
# pass 2
# fail 0
# duration_ms 3.75

GitHub Actions Integration

name: Test
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
      - run: npm ci
      - name: Install Chrome
        run: |
          wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
          sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
          sudo apt-get update
          sudo apt-get install google-chrome-stable
      - name: Run HTML tests
        run: npx html-test --headless

Programmatic API

import { createTestServer, collectTAP } from '@enhance/html-test';

// Create and start server
const server = createTestServer({ port: 3000, root: './test' });
await server.listen();

// Collect TAP output
const tap = await collectTAP('http://localhost:3000');
console.log(tap);

// Cleanup
await server.close();

Directory Structure

my-project/
├── test/
│   ├── utils.js              # Shared test utilities
│   ├── component-a.test.html # Test file
│   └── component-b.test.html # Test file
└── package.json

Browser Support

Requires a browser with support for:

  • Declarative Shadow DOM (<template shadowrootmode="open">)
  • ES Modules
  • Import maps

Tested with:

  • Chrome/Chromium 90+
  • Edge 90+
  • Safari 16.4+

License

Apache-2.0

Author

Kristofer Joseph (@dam)

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