{"id":17292008,"url":"https://github.com/eklem/batr","last_synced_at":"2025-09-10T05:36:34.091Z","repository":{"id":37950130,"uuid":"360574501","full_name":"eklem/batr","owner":"eklem","description":"Bundle and test CommonJS and ESM in NodeJS and UMD in the browser with AvaJS and Playwright. And repeat with i.e GitHub CI.","archived":false,"fork":false,"pushed_at":"2025-07-12T07:13:35.000Z","size":1953,"stargazers_count":2,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"trunk","last_synced_at":"2025-08-23T08:24:54.343Z","etag":null,"topics":["ava-tests","bundle","commonjs","continuous-integration","esm","nodejs","ui-testing","umd"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eklem.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-22T15:52:55.000Z","updated_at":"2023-10-06T07:28:01.000Z","dependencies_parsed_at":"2023-11-29T06:28:23.926Z","dependency_job_id":"56c30791-2451-4cd0-9505-8c364c6d2fc8","html_url":"https://github.com/eklem/batr","commit_stats":{"total_commits":289,"total_committers":2,"mean_commits":144.5,"dds":0.301038062283737,"last_synced_commit":"98349783a987e14eb28ca5f32e27faf9e76b7565"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/eklem/batr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eklem%2Fbatr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eklem%2Fbatr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eklem%2Fbatr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eklem%2Fbatr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eklem","download_url":"https://codeload.github.com/eklem/batr/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eklem%2Fbatr/sbom","scorecard":{"id":370655,"data":{"date":"2025-08-11","repo":{"name":"github.com/eklem/batr","commit":"3735b05ef6e7e69cb0e9021df4648b7dbb029564"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/4 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/tests.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/eklem/batr/tests.yml/trunk?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/eklem/batr/tests.yml/trunk?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/tests.yml:18","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'trunk'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":4,"reason":"6 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T12:50:48.695Z","repository_id":37950130,"created_at":"2025-08-18T12:50:48.695Z","updated_at":"2025-08-18T12:50:48.695Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272959401,"owners_count":25022056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ava-tests","bundle","commonjs","continuous-integration","esm","nodejs","ui-testing","umd"],"created_at":"2024-10-15T10:42:31.348Z","updated_at":"2025-08-31T08:38:57.977Z","avatar_url":"https://github.com/eklem.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# batr\n**B**undle **A**nd **T**est ... and **R**epeat\n\n![batr-logo](https://user-images.githubusercontent.com/236656/115827172-3757dd00-a40c-11eb-9687-70bb6e623d2b.png)\n\nBundle and test CommonJS and ESM in NodeJS and UMD in the browser with Rollup, AvaJS and Playwright. And repeat with GitHub Actions workflow.\n\nA 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](https://blogg.knowit.no/solutions-no/test-setup-for-javascript/web-development-with-less-stress-and-pain-my-solution-so-far-batr).\n\nI'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.\n\n[![NPM version](http://img.shields.io/npm/v/batr.svg?style=flat)](https://npmjs.org/package/batr)\n[![NPM downloads](http://img.shields.io/npm/dm/batr.svg?style=flat)](https://npmjs.org/package/batr) \n[![Build Status](https://github.com/eklem/batr/actions/workflows/tests.yml/badge.svg)](https://github.com/eklem/batr/actions/workflows/tests.yml) \n[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)\n\n## Example setup\nFor an actual working example, check out [batr-example](http://github.com/eklem/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.\n\n## Libraries used:\n* [AvaJS](https://github.com/avajs/ava)\n* [Playwright](https://playwright.dev/docs/intro)\n* [Rollup](https://rollupjs.org/guide/en/) + plugins `@rollup/plugin-commonjs`, `@rollup/plugin-json` and `@rollup/plugin-node-resolve`\n* [StandardJS](https://standardjs.com/)\n\n**Integrations**\n* Using [GitHub Actions workflow](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions) for continuous integration.\n\n## Get started\n\n### Add batr devDependency\nAll the dependencies in one. Security updates and version bumps done mostly at the start of every month, so less GitHub dependabot noise.\n\n```javaScript\n  \"devDependencies\": {\n    \"batr\": \"^1.0.5\"\n  }\n```\nThe underlying libraries are used (required and imported) as normal.\n\n### Define main, module and browser\n* `main` - CJS - CommonJS\n* `module` - ESM - ES Modules\n* `browser` - UMD - Universal Module Definitions\n\n```javaScript\n  \"main\": \"./dist/batr-example.cjs.js\",\n  \"module\": \"./dist/batr-example.esm.mjs\",\n  \"browser\": \"./dist/batr-example.umd.js\",\n```\n\nMakes 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\"`.\n\n### Tests\n\n#### Build/bundle and tests from package.json\n\n```javaScript\n  \"scripts\": {\n    \"build\": \"rollup --config\",\n    \"test\": \"standard './*.js' \u0026\u0026 npm run build  \u0026\u0026 npx ava ./test/test.cjs.js \u0026\u0026 npx ava ./test/test.esm.mjs \u0026\u0026 npx ava ./test/ui-test.js\"\n  }\n```\n\n#### Rollup config for bundling CJS, ESM and UMD\n\n```javaScript\nimport resolve from '@rollup/plugin-node-resolve'\nimport commonjs from '@rollup/plugin-commonjs'\nimport json from '@rollup/plugin-json'\nimport pkg from './package.json'\n\nexport default [\n  // browser-friendly UMD build\n  // CommonJS (for Node) and ES module (for bundlers) build.\n  // (We could have three entries in the configuration array\n  // instead of two, but it's quicker to generate multiple\n  // builds from a single configuration where possible, using\n  // an array for the `output` option, where we can specify\n  // `file` and `format` for each target)\n  {\n    input: './src/index.js',\n    output: [\n      { name: 'math', file: pkg.browser, format: 'umd', exports: 'named' },\n      { file: pkg.main, format: 'cjs' },\n      { file: pkg.module, format: 'es' }\n    ],\n    plugins: [\n      resolve(), // so Rollup can find `ms`\n      commonjs(), // so Rollup can convert `ms` to an ES module\n      json() // for Rollup to be able to read content from package.json\n    ]\n  }\n]\n```\n\n#### Actual test scripts\n\n##### Main - ./dist/batr-example.cjs.js\n```javaScript\nconst test = require('ava')\nconst { add, subtract, multiply, divide } = require('../dist/batr-example.cjs.js')\n\ntest('addition a + b', (t) =\u003e {\n  const expected = 31\n  const addition = add(7, 24)\n  t.deepEqual(addition, expected)\n})\n\ntest('subtraction a - b', (t) =\u003e {\n  const expected = -17\n  const subtraction = subtract(7, 24)\n  t.deepEqual(subtraction, expected)\n})\n\ntest('multiplication a * b', (t) =\u003e {\n  const expected = 168\n  const multiplication = multiply(7, 24)\n  t.deepEqual(multiplication, expected)\n})\n\ntest('division a * b', (t) =\u003e {\n  const expected = 0.2916666666666667\n  const division = divide(7, 24)\n  t.deepEqual(division, expected)\n})\n```\n\n##### Module - ./dist/batr-example.esm.mjs\nSame tests as for `Main`, just using `import` instead of `require`.\n```javaScript\nimport test from 'ava'\nimport { add, subtract, multiply, divide } from '../dist/batr-example.esm.mjs'\n\n// Tests are identical to Main/CJS tests\n})\n```\n\n##### Browser - ./dist/ui-test.js\nSimilar tests, but done through recorded user interactions in a browser. You recorded with `playwright codegen`. Create your prototype and do something like this:\n```console\nnpx playwright codegen -o javascript index.html\n```\n\n[Playwright has good documentation on how to record](https://playwright.dev/docs/codegen#generate-tests) 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.\n\nTo 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.\n\nAlso, you can test with different browsers or more than one browser, and emulate devices like an Iphone.\n\n```javaScript\nconst { chromium } = require('playwright')\nconst test = require('ava')\nconst browserPromise = chromium.launch({\n  headless: true\n  // slowMo: 500\n})\n\nconst path = require('path')\nasync function pageMacro (t, callback) {\n  const browser = await browserPromise\n  const page = await browser.newPage()\n  await page.setViewportSize({ width: 640, height: 480 })\n  try {\n    await callback(t, page)\n  } finally {\n    await page.close()\n  }\n}\n\ntest('Add numbers 4 and 7, subtract 7 from 4, multiply 4 and finally divide 4 by 7', pageMacro, async (t, page) =\u003e {\n  // t.plan(4)\n  const filePath = await path.resolve('./demo/index.html')\n  const url = 'file://' + filePath\n\n  // Go to ./index.html\n  await page.goto(url)\n\n  // Click first number input field and delete\n  await page.click('#firstNumber')\n  await page.keyboard.press('Backspace')\n\n  // Type number\n  await page.keyboard.type('4')\n\n  // Press Tab twice to get to next number\n  await page.keyboard.press('Tab')\n  await page.keyboard.press('Tab')\n\n  // Fill #secondNumber\n  await page.keyboard.type('7')\n\n  // Press Tab with modifiers\n  await page.press('#secondNumber', 'Shift+Tab')\n\n  // screenshot, 1st task\n  await page.screenshot({ path: './screenshots/screenshot-01.png' })\n\n  // Test that 4 + 7 gives 11\n  t.deepEqual(await page.textContent('#result span'), '11')\n\n  // Select subtract\n  await page.selectOption('select[name=\"calculation\"]', 'subtract')\n\n  // screenshot, 2nd task\n  await page.screenshot({ path: './screenshots/screenshot-02.png' })\n\n  // Test that 4 - 7 gives -3\n  t.deepEqual(await page.textContent('#result span'), '-3')\n\n  // Select multiply\n  await page.selectOption('select[name=\"calculation\"]', 'multiply')\n\n  // screenshot, 3rd task\n  await page.screenshot({ path: './screenshots/screenshot-03.png' })\n\n  // Test that 3 * 11 gives 28\n  t.deepEqual(await page.textContent('#result span'), '28')\n\n  // Select divide\n  await page.selectOption('select[name=\"calculation\"]', 'divide')\n\n  // screenshot, 4th task\n  await page.screenshot({ path: './screenshots/screenshot-04.png' })\n\n  // Test that 4 / 7 gives 0.5714285714285714\n  t.deepEqual(await page.textContent('#result span'), '0.5714285714285714')\n})\n```\n\n#### Continuous integration with GitHub Actions workflow\n`ubuntu-latest` is easy going, but you can test OSX and Windows too. Check [GitHubs runs-on documentiation](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on).\n`.github/workflows/tests.yml`:\n```yml\nname: tests\non:\n  - push\n  - pull_request\njobs:\n  run-tests:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [12.x, 14.x, 16.x]\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: ${{ matrix.node-version }}\n      - run: npm install\n      - run: sudo apt-get install xvfb\n      - run: xvfb-run --auto-servernum npm test\n```\n\n## Background and goal\n* Use less time on updating the same bundle and test framework code in different libraries.\n* Quicker bundling and test setup when creating new libraries.\n* As few dependencies as possible, or a good balance between dependencies and function, to not have minor updates all the time.\n* 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.\n\n### Easy setup of\n* Ava tests in Node.js\n* Possibly duplicat Ava tests in browser\n* User-like interaction tests in browser, supported by Ava\n* Bundling \u0026 buildin  g for the browser, CommonJS and ESM\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feklem%2Fbatr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feklem%2Fbatr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feklem%2Fbatr/lists"}