batr
v2.1.10
Published
Bundle and test CommonJS and ESM in NodeJS and UMD in the browser with AvaJS and Playwright. And repeat with Travis-CI.
Downloads
424
Maintainers
Readme
batr
Bundle And Test ... and Repeat
Bundle and test CommonJS and ESM in NodeJS and UMD in the browser with Rollup, AvaJS and Playwright. And repeat with GitHub Actions workflow.
A little blogpost on why I thought Batr was a good idea: Test setup for JavaScript/web development with less stress and pain— My solution so far: Batr.
I'm using AvaJS since I want a simple enough test framework and don't want to be too smart about assertions. The needs are not that big. For UI tests it's good to be a little repetitive. If you want to test a sequence of interactions A, B, C and D, then test them all synchronously in one go. You'll get to test the transition between the interactions and that the result of interaction A, doesn't screw up interaction B and so on.
Example setup
For an actual working example, check out batr-example on how to use batr. It's an example library with minimal of functions and user-interface to show-case how to set up batr
. The examples here are lifted from that library.
Libraries used:
- AvaJS
- Playwright
- Rollup + plugins
@rollup/plugin-commonjs
,@rollup/plugin-json
and@rollup/plugin-node-resolve
- StandardJS
Integrations
- Using GitHub Actions workflow for continuous integration.
Get started
Add batr devDependency
All the dependencies in one. Security updates and version bumps done mostly at the start of every month, so less GitHub dependabot noise.
"devDependencies": {
"batr": "^1.0.5"
}
The underlying libraries are used (required and imported) as normal.
Define main, module and browser
main
- CJS - CommonJSmodule
- ESM - ES Modulesbrowser
- UMD - Universal Module Definitions
"main": "./dist/batr-example.cjs.js",
"module": "./dist/batr-example.esm.mjs",
"browser": "./dist/batr-example.umd.js",
Makes pointers to which files are used for what. Used i.e. when bundling correct distribution files with Rollup and to use the correct file when doing const moduleName = require('moduleName')
or import moduleName from "moduleName"
.
Tests
Build/bundle and tests from package.json
"scripts": {
"build": "rollup --config",
"test": "standard './*.js' && npm run build && npx ava ./test/test.cjs.js && npx ava ./test/test.esm.mjs && npx ava ./test/ui-test.js"
}
Rollup config for bundling CJS, ESM and UMD
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import pkg from './package.json'
export default [
// browser-friendly UMD build
// CommonJS (for Node) and ES module (for bundlers) build.
// (We could have three entries in the configuration array
// instead of two, but it's quicker to generate multiple
// builds from a single configuration where possible, using
// an array for the `output` option, where we can specify
// `file` and `format` for each target)
{
input: './src/index.js',
output: [
{ name: 'math', file: pkg.browser, format: 'umd', exports: 'named' },
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' }
],
plugins: [
resolve(), // so Rollup can find `ms`
commonjs(), // so Rollup can convert `ms` to an ES module
json() // for Rollup to be able to read content from package.json
]
}
]
Actual test scripts
Main - ./dist/batr-example.cjs.js
const test = require('ava')
const { add, subtract, multiply, divide } = require('../dist/batr-example.cjs.js')
test('addition a + b', (t) => {
const expected = 31
const addition = add(7, 24)
t.deepEqual(addition, expected)
})
test('subtraction a - b', (t) => {
const expected = -17
const subtraction = subtract(7, 24)
t.deepEqual(subtraction, expected)
})
test('multiplication a * b', (t) => {
const expected = 168
const multiplication = multiply(7, 24)
t.deepEqual(multiplication, expected)
})
test('division a * b', (t) => {
const expected = 0.2916666666666667
const division = divide(7, 24)
t.deepEqual(division, expected)
})
Module - ./dist/batr-example.esm.mjs
Same tests as for Main
, just using import
instead of require
.
import test from 'ava'
import { add, subtract, multiply, divide } from '../dist/batr-example.esm.mjs'
// Tests are identical to Main/CJS tests
})
Browser - ./dist/ui-test.js
Similar tests, but done through recorded user interactions in a browser. You recorded with playwright codegen
. Create your prototype and do something like this:
npx playwright codegen -o javascript index.html
Playwright has good documentation on how to record user interactions and generating test-code for different programming languages. I'm guessing it's good practice to swap some of the HTML references with a little more solid CSS selectors so that the tests won't fail becuase of small HTML changes.
To see more of what's going on you can set healess: false
and slow it down with sloMo: 500
, but it will fail if you try it on i.e. a server, since there it's running headless.
Also, you can test with different browsers or more than one browser, and emulate devices like an Iphone.
const { chromium } = require('playwright')
const test = require('ava')
const browserPromise = chromium.launch({
headless: true
// slowMo: 500
})
const path = require('path')
async function pageMacro (t, callback) {
const browser = await browserPromise
const page = await browser.newPage()
await page.setViewportSize({ width: 640, height: 480 })
try {
await callback(t, page)
} finally {
await page.close()
}
}
test('Add numbers 4 and 7, subtract 7 from 4, multiply 4 and finally divide 4 by 7', pageMacro, async (t, page) => {
// t.plan(4)
const filePath = await path.resolve('./demo/index.html')
const url = 'file://' + filePath
// Go to ./index.html
await page.goto(url)
// Click first number input field and delete
await page.click('#firstNumber')
await page.keyboard.press('Backspace')
// Type number
await page.keyboard.type('4')
// Press Tab twice to get to next number
await page.keyboard.press('Tab')
await page.keyboard.press('Tab')
// Fill #secondNumber
await page.keyboard.type('7')
// Press Tab with modifiers
await page.press('#secondNumber', 'Shift+Tab')
// screenshot, 1st task
await page.screenshot({ path: './screenshots/screenshot-01.png' })
// Test that 4 + 7 gives 11
t.deepEqual(await page.textContent('#result span'), '11')
// Select subtract
await page.selectOption('select[name="calculation"]', 'subtract')
// screenshot, 2nd task
await page.screenshot({ path: './screenshots/screenshot-02.png' })
// Test that 4 - 7 gives -3
t.deepEqual(await page.textContent('#result span'), '-3')
// Select multiply
await page.selectOption('select[name="calculation"]', 'multiply')
// screenshot, 3rd task
await page.screenshot({ path: './screenshots/screenshot-03.png' })
// Test that 3 * 11 gives 28
t.deepEqual(await page.textContent('#result span'), '28')
// Select divide
await page.selectOption('select[name="calculation"]', 'divide')
// screenshot, 4th task
await page.screenshot({ path: './screenshots/screenshot-04.png' })
// Test that 4 / 7 gives 0.5714285714285714
t.deepEqual(await page.textContent('#result span'), '0.5714285714285714')
})
Continuous integration with GitHub Actions workflow
ubuntu-latest
is easy going, but you can test OSX and Windows too. Check GitHubs runs-on documentiation.
.github/workflows/tests.yml
:
name: tests
on:
- push
- pull_request
jobs:
run-tests:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: sudo apt-get install xvfb
- run: xvfb-run --auto-servernum npm test
Background and goal
- Use less time on updating the same bundle and test framework code in different libraries.
- Quicker bundling and test setup when creating new libraries.
- As few dependencies as possible, or a good balance between dependencies and function, to not have minor updates all the time.
- New NPM release every month, meaning less noise from Dependabot. Batr + dependencies will only be devDependencies, and security issues will not be a big problem.
Easy setup of
- Ava tests in Node.js
- Possibly duplicat Ava tests in browser
- User-like interaction tests in browser, supported by Ava
- Bundling & buildin g for the browser, CommonJS and ESM