@substrate-system/tapout
v0.0.30
Published
Run tests in a browser from the command line.
Downloads
909
Readme
tapout
The easiest way to run tests in a browser from the command line. Just pipe some JS into this command. A spiritual successor to tape-run.
This uses playwright under the hood.
Usage: tapout [options]
Options:
-t, --timeout <ms> Timeout in milliseconds (default: 10000)
-b, --browser <name> Browser to use: chromium, firefox, webkit, edge (default: chromium)
-r, --reporter <name> Output format: tap, html (default: tap)
--outdir <path> Output directory for HTML reports (default: current directory)
--outfile <name> Output filename for HTML reports (default: index.html)
-h, --help Show this help message
Examples:
cat test.js | tapout --timeout 5000
cat test.js | tapout --browser firefox
cat test.js | tapout -b webkit -t 3000
cat test.js | tapout --browser edge
cat test.js | tapout --reporter html
cat test.js | tapout --reporter html --outdir ./reports
cat test.js | tapout --reporter html --outfile my-test-results.htmlFeaturing
- Cross-browser testing: Run tests in Chrome, Firefox, Safari (WebKit), or Edge
- Vite support: Automatic support for
import.meta.envvariables - Smart timeout handling: Use custom timeouts or auto-timeout
- Beautiful HTML reports: Generate HTML reports perfect for CI
- TAP compatible: Standard TAP output works with any TAP formatter
- Zero configuration: Just pipe JavaScript into this command
- CI/CD friendly: Proper exit codes and error detection
Install
npm i -D @substrate-system/tapoutUse
Pipe some Javascript to this command.
cat ./test/index.js | npx tapoutUse shell redirection
npx esbuild --bundle ./test/index.ts | npx tapout | npx tap-specwindow.testsFinished
The test browser will automatically close within few seconds of no activity.
To explicitly end the tests, set a property on window.
import { test } from '@substrate-system/tapzero'
test('example test', (t) => {
t.ok(true)
})
test('all done', () => {
// This will cause the tests to exit immediately.
// @ts-expect-error tests
window.testsFinished = true
})Vite environment variables
Vite environment variables, like import.meta.env.DEV are defined, so your
tests wont break if you use them in your application code.
import.meta.env.DEV-true(tests run in development mode)import.meta.env.PROD-falseimport.meta.env.MODE-"test"import.meta.env.BASE_URL-"/"import.meta.env.SSR-false
Example
// Your Vite app code can use these environment variables
if (import.meta.env.DEV) {
console.log('Running in development mode')
}
const apiUrl = import.meta.env.DEV ?
'http://localhost:3000/api' :
'https://production.api.com'CI
After npm install, you will need to do an npx playwright install.
For example, in Github CI,
# ...
- name: npm install, build
run: |
npm install
npm run build --if-present
npm run lint
npx playwright install --with-deps
env:
CI: true
# ...Generate HTML reports
By default writes to stdout.
cat ./test/index.js | npx tapout --reporter html > index.html
open index.html # View the generated reportHTML Summary
--reporter htmlwith no other options -> output HTML to stdout--reporter html --outfile filename.html-> save tofilename.htmlin current directory--reporter html --outdir ./reports-> save to./reports/index.html--reporter html --outdir ./reports --outfile custom.html-> save to./reports/custom.html
-b, --browser
Pass in the name of a browser to use. Default is Chrome.
Possibilities are chromium, firefox, webkit, or edge.
-t, --timeout
Pass in a different timeout value. The default is 10 seconds.
The timeout respects the auto-finish behavior:
- With default timeout (no
-tflag): Auto-finish triggers after a short delay (1-2 seconds) when no test activity is detected - With custom timeout (using
-t): Auto-finish uses 80% of the specified timeout, giving tests more time to complete naturally
cat test.js | npx tapout --timeout 5000Error Detection: tapout automatically detects and reports test failures from:
- Unhandled promise rejections
- Uncaught exceptions
- Console error messages with common error patterns
- TAP "not ok" results
Tests will exit with code 1 if any errors are detected, making it perfect for CI/CD pipelines.
-r, --reporter
Choose the output format. Default is TAP.
[!NOTE]
For HTML output, you will want to redirect stdout to a file.cat test.js | npx tapout --reporter html > test-output.html
Available reporters:
tap- TAP output (default) - Standard Test Anything Protocol formathtml- Generate an HTML report file with beautiful, responsive design
# Generate HTML report
cat test.js | npx tapout --reporter html
# Generate HTML and output to stdout
cat test.js | npx tapout --reporter html > my-report.html
# Use TAP output (default)
cat test.js | npx tapout
# Customize output location
cat test.js | npx tapout --reporter html --outdir ./reports
cat test.js | npx tapout --reporter html --outfile my-test-results.html
cat test.js | npx tapout --reporter html --outdir ./reports --outfile custom-report.htmlThe HTML reporter generates an index.html file by default with:
- Test summary with pass/fail counts and percentages
- Individual test results with status indicators
- Browser and timing information
Output Control:
--outdir <path>- Specify where to save the HTML report (default: current directory)--outfile <name>- Specify the filename for the HTML report (default: index.html)- If neither
--outdirnor--outfileis specified, HTML output is sent to stdout
GitHub Pages Integration
The generated HTML file is self-contained and can be easily hosted on GitHub Pages or any static hosting service. Simply commit the HTML file to your repository.
# Example CI workflow
npm test 2>&1 | npx tapout --reporter html --outfile test-results.html
git add test-results.html
git commit -m "Update test results"
git pushAccessibility Testing with Axe
You can integrate axe-core to test accessibility.
1. Install axe-core
npm install --save-dev axe-core2. Create an accessibility helper
// test/helpers/axe-helper.js
import axe from 'axe-core'
/**
* Run axe accessibility scan and assert no violations
*
* @param t - Tapzero tester
* @param {Partial<{ context, tags, rules }>} options Options object
*/
export async function assertNoViolations(
t,
options = {},
message = 'should have no accessibility violations'
) {
const {
context = document,
tags = ['wcag2a', 'wcag2aa'],
rules = {}
} = options
const results = await axe.run(context, {
runOnly: { type: 'tag', values: tags },
rules
})
t.equal(results.violations.length, 0, message)
// Log violations for debugging
if (results.violations.length > 0) {
console.error('\n=== Accessibility Violations ===')
results.violations.forEach((violation, index) => {
console.error(`\n${index + 1}. ${violation.help}`)
console.error(` Impact: ${violation.impact}`)
console.error(` WCAG: ${violation.tags.filter(tag => tag.startsWith('wcag')).join(', ')}`)
console.error(` Affected elements: ${violation.nodes.length}`)
violation.nodes.forEach((node, nodeIndex) => {
console.error(` ${nodeIndex + 1}. ${node.html}`)
console.error(` ${node.failureSummary}`)
})
})
console.error('\n================================\n')
}
return results
}
/**
* Check WCAG compliance level
*/
export async function assertWCAGCompliance(t, level = 'AA', options = {}) {
const tags = {
'A': ['wcag2a'],
'AA': ['wcag2a', 'wcag2aa'],
'AAA': ['wcag2a', 'wcag2aa', 'wcag2aaa']
}
return assertNoViolations(t, {
...options,
tags: tags[level] || tags['AA']
}, `should meet WCAG ${level} compliance`)
}3. Use in your tests
import { test } from '@substrate-system/tapzero'
import {
assertNoViolations,
assertWCAGCompliance
} from './helpers/axe-helper.js'
test('page has no accessibility violations', async (t) => {
document.body.innerHTML = `
<main>
<h1>Welcome</h1>
<button>Click me</button>
<img src="test.jpg" alt="Test image" />
</main>
`
await assertNoViolations(t)
})
test('form meets WCAG AA compliance', async (t) => {
document.body.innerHTML = `
<form>
<label for="username">Username</label>
<input id="username" type="text" />
<label for="password">Password</label>
<input id="password" type="password" />
<button type="submit">Submit</button>
</form>
`
await assertWCAGCompliance(t, 'AA')
})
test('can test specific elements', async (t) => {
document.body.innerHTML = `
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
`
const nav = document.querySelector('nav')
await assertNoViolations(t, {
context: nav
}, 'navigation should be accessible')
})
test('cleanup', () => {
// @ts-expect-error browser global
window.testsFinished = true
})4. Run the accessibility tests
# Bundle and run
npx esbuild test/a11y-test.js --bundle --format=esm | npx tapout | npx tap-spec
# Or add to package.json scripts
npm run test:a11yAdvanced Use
Test specific WCAG rules
import axe from 'axe-core'
test('check color contrast only', async (t) => {
document.body.innerHTML = `<div style="color: #333; background: #fff;">
Content
</div>`
const results = await axe.run(document, {
runOnly: { type: 'rule', values: ['color-contrast'] }
})
t.equal(results.violations.length, 0, 'should pass color contrast')
})Disable specific rules
await assertNoViolations(t, {
rules: {
'color-contrast': { enabled: false } // Skip incomplete styles
}
})Component testing
import { MyComponent } from '../src/components/MyComponent.js'
test('MyComponent is accessible', async (t) => {
const container = document.createElement('div')
document.body.appendChild(container)
// Render your component
const component = new MyComponent()
component.mount(container)
// Test accessibility
await assertNoViolations(t, { context: container })
container.remove()
})Example Tests
Write tests for the browser environment.
[!TIP] End the tests explicity with
window.testsFinished = true. Else they time out naturally, which is ok too.
// test/index.ts
import { test } from '@substrate-system/tapzero'
test('example', t => {
t.ok(document.body, 'should find a body tag')
})
test('all done', t => {
// @ts-expect-error explicitly end
window.testsFinished = true
})Run the tests on the command line.
npx esbuild ./test/index.ts | npx tapoutHTML report
# HTML reporter examples
npm run test:simple -- --reporter html # Generate HTML reportTest
See the test/ directory.
npm test