├── .github ├── dependabot.yml └── workflows │ └── test-and-release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo.png ├── lib ├── log.js ├── puppeteer.js ├── webpack.config.js └── wrap-loader.cjs ├── package.json ├── polendina-cli.js ├── polendina-node-cli.js ├── polendina.js ├── resources ├── bare-run.js ├── bare.js ├── common-run.js ├── index.html ├── mocha-run.js ├── page-run.js ├── tape-run.js └── test-registry.cjs └── test ├── common.js ├── fixtures ├── bare-async-esm │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test │ │ └── test-3.js ├── bare-async-failure-esm │ ├── index.js │ ├── package.json │ ├── test-1.js │ └── test-2.js ├── bare-async-failure │ ├── index.js │ ├── package.json │ ├── test-1.js │ └── test-2.js ├── bare-async │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test │ │ └── test-3.js ├── bare-sync-esm │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test │ │ └── test-3.js ├── bare-sync-failure-esm │ ├── index.js │ ├── package.json │ ├── test-1.js │ └── test-2.js ├── bare-sync-failure │ ├── index.js │ ├── package.json │ ├── test-1.js │ └── test-2.js ├── bare-sync │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test │ │ └── test-3.js ├── mocha-esm │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test-3.js ├── mocha-failure-esm │ ├── package.json │ └── test.js ├── mocha-failure │ ├── package.json │ └── test.js ├── mocha │ ├── index.js │ ├── package.json │ ├── test-1.js │ ├── test-2.js │ └── test-3.js ├── tape-esm │ ├── index.js │ ├── package.json │ └── test.js ├── tape-failure-esm │ ├── package.json │ └── test.js ├── tape-failure │ ├── package.json │ └── test.js ├── tape │ ├── index.js │ ├── package.json │ └── test.js └── webpack-merge │ ├── index.js │ ├── package.json │ ├── test.js │ └── webpack.config.js ├── test-bare-async.js ├── test-bare-sync.js ├── test-mocha.js ├── test-polendina-node.js ├── test-tape.js └── test-webpack-merge.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | commit-message: 8 | prefix: 'chore' 9 | include: 'scope' 10 | 11 | - package-ecosystem: 'npm' 12 | directory: '/' 13 | schedule: 14 | interval: 'daily' 15 | commit-message: 16 | prefix: 'chore' 17 | include: 'scope' 18 | 19 | - package-ecosystem: 'npm' 20 | directory: '/test/fixtures/bare-async' 21 | schedule: 22 | interval: 'daily' 23 | commit-message: 24 | prefix: 'chore' 25 | include: 'scope' 26 | 27 | - package-ecosystem: 'npm' 28 | directory: '/test/fixtures/bare-async-esm' 29 | schedule: 30 | interval: 'daily' 31 | commit-message: 32 | prefix: 'chore' 33 | include: 'scope' 34 | 35 | - package-ecosystem: 'npm' 36 | directory: '/test/fixtures/bare-async-failure' 37 | schedule: 38 | interval: 'daily' 39 | commit-message: 40 | prefix: 'chore' 41 | include: 'scope' 42 | 43 | - package-ecosystem: 'npm' 44 | directory: '/test/fixtures/bare-async-failure-esm' 45 | schedule: 46 | interval: 'daily' 47 | commit-message: 48 | prefix: 'chore' 49 | include: 'scope' 50 | 51 | - package-ecosystem: 'npm' 52 | directory: '/test/fixtures/bare-sync' 53 | schedule: 54 | interval: 'daily' 55 | commit-message: 56 | prefix: 'chore' 57 | include: 'scope' 58 | 59 | - package-ecosystem: 'npm' 60 | directory: '/test/fixtures/bare-sync-esm' 61 | schedule: 62 | interval: 'daily' 63 | commit-message: 64 | prefix: 'chore' 65 | include: 'scope' 66 | 67 | - package-ecosystem: 'npm' 68 | directory: '/test/fixtures/bare-sync-failure' 69 | schedule: 70 | interval: 'daily' 71 | commit-message: 72 | prefix: 'chore' 73 | include: 'scope' 74 | 75 | - package-ecosystem: 'npm' 76 | directory: '/test/fixtures/bare-sync-failure-esm' 77 | schedule: 78 | interval: 'daily' 79 | commit-message: 80 | prefix: 'chore' 81 | include: 'scope' 82 | 83 | - package-ecosystem: 'npm' 84 | directory: '/test/fixtures/mocha' 85 | schedule: 86 | interval: 'daily' 87 | commit-message: 88 | prefix: 'chore' 89 | include: 'scope' 90 | 91 | - package-ecosystem: 'npm' 92 | directory: '/test/fixtures/mocha-esm' 93 | schedule: 94 | interval: 'daily' 95 | commit-message: 96 | prefix: 'chore' 97 | include: 'scope' 98 | 99 | - package-ecosystem: 'npm' 100 | directory: '/test/fixtures/mocha-failure' 101 | schedule: 102 | interval: 'daily' 103 | commit-message: 104 | prefix: 'chore' 105 | include: 'scope' 106 | 107 | - package-ecosystem: 'npm' 108 | directory: '/test/fixtures/mocha-failure-esm' 109 | schedule: 110 | interval: 'daily' 111 | commit-message: 112 | prefix: 'chore' 113 | include: 'scope' 114 | 115 | - package-ecosystem: 'npm' 116 | directory: '/test/fixtures/tape' 117 | schedule: 118 | interval: 'daily' 119 | commit-message: 120 | prefix: 'chore' 121 | include: 'scope' 122 | 123 | - package-ecosystem: 'npm' 124 | directory: '/test/fixtures/tape-esm' 125 | schedule: 126 | interval: 'daily' 127 | commit-message: 128 | prefix: 'chore' 129 | include: 'scope' 130 | 131 | - package-ecosystem: 'npm' 132 | directory: '/test/fixtures/tape-failure' 133 | schedule: 134 | interval: 'daily' 135 | commit-message: 136 | prefix: 'chore' 137 | include: 'scope' 138 | 139 | - package-ecosystem: 'npm' 140 | directory: '/test/fixtures/tape-failure-esm' 141 | schedule: 142 | interval: 'daily' 143 | commit-message: 144 | prefix: 'chore' 145 | include: 'scope' 146 | 147 | - package-ecosystem: 'npm' 148 | directory: '/test/fixtures/webpack-merge' 149 | schedule: 150 | interval: 'daily' 151 | commit-message: 152 | prefix: 'chore' 153 | include: 'scope' 154 | 155 | -------------------------------------------------------------------------------- /.github/workflows/test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Test & Maybe Release 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | node: [lts/*, current] 9 | os: [macos-latest, ubuntu-latest, windows-latest] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4 14 | - name: Use Node.js ${{ matrix.node }} 15 | uses: actions/setup-node@v4.4.0 16 | with: 17 | node-version: ${{ matrix.node }} 18 | - name: Install Dependencies 19 | run: | 20 | npm install --no-progress 21 | - name: Run tests 22 | run: | 23 | npm config set script-shell bash 24 | npm test 25 | release: 26 | name: Release 27 | needs: test 28 | runs-on: ubuntu-latest 29 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 35 | - name: Setup Node.js 36 | uses: actions/setup-node@v4.4.0 37 | with: 38 | node-version: lts/* 39 | - name: Install dependencies 40 | run: | 41 | npm install --no-progress --no-package-lock --no-save 42 | - name: Install plugins 43 | run: | 44 | npm install \ 45 | @semantic-release/commit-analyzer \ 46 | conventional-changelog-conventionalcommits \ 47 | @semantic-release/release-notes-generator \ 48 | @semantic-release/npm \ 49 | @semantic-release/github \ 50 | @semantic-release/git \ 51 | @semantic-release/changelog \ 52 | --no-progress --no-package-lock --no-save 53 | - name: Release 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 57 | run: npx semantic-release 58 | 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [3.2.11](https://github.com/rvagg/polendina/compare/v3.2.10...v3.2.11) (2025-05-31) 2 | 3 | ### Trivial Changes 4 | 5 | * **deps:** bump yargs from 17.7.2 to 18.0.0 ([720a32d](https://github.com/rvagg/polendina/commit/720a32d6870e1ec5f70e916846119313601e27e3)) 6 | * update st to version that fixes deprecations ([c0c0636](https://github.com/rvagg/polendina/commit/c0c0636240cd93348f71ceed7f679a66aff592d3)) 7 | 8 | ## [3.2.10](https://github.com/rvagg/polendina/compare/v3.2.9...v3.2.10) (2025-04-14) 9 | 10 | ### Trivial Changes 11 | 12 | * **deps:** bump actions/setup-node from 4.3.0 to 4.4.0 ([12fcf24](https://github.com/rvagg/polendina/commit/12fcf242f503693a1c4737e6ced422723745d040)) 13 | 14 | ## [3.2.9](https://github.com/rvagg/polendina/compare/v3.2.8...v3.2.9) (2025-03-17) 15 | 16 | ### Trivial Changes 17 | 18 | * **deps:** bump actions/setup-node from 4.2.0 to 4.3.0 ([5a81af6](https://github.com/rvagg/polendina/commit/5a81af665d93c86776b6b8acd82305bad6d8a0d1)) 19 | 20 | ## [3.2.8](https://github.com/rvagg/polendina/compare/v3.2.7...v3.2.8) (2025-01-28) 21 | 22 | ### Trivial Changes 23 | 24 | * **deps:** bump actions/setup-node from 4.1.0 to 4.2.0 ([c113908](https://github.com/rvagg/polendina/commit/c1139085228977340f9e49058a3fa8583adc4d66)) 25 | 26 | ## [3.2.7](https://github.com/rvagg/polendina/compare/v3.2.6...v3.2.7) (2025-01-13) 27 | 28 | ### Trivial Changes 29 | 30 | * **deps:** bump puppeteer from 23.11.1 to 24.0.0 ([3db9243](https://github.com/rvagg/polendina/commit/3db9243474c81e14331a373c310b655a216a9f99)) 31 | 32 | ## [3.2.6](https://github.com/rvagg/polendina/compare/v3.2.5...v3.2.6) (2024-12-03) 33 | 34 | ### Trivial Changes 35 | 36 | * **deps-dev:** bump mocha in /test/fixtures/mocha ([2694528](https://github.com/rvagg/polendina/commit/2694528c0dd35224c6dfa9f608cd89285afc187e)) 37 | * **deps-dev:** bump mocha in /test/fixtures/mocha-esm ([42d3c28](https://github.com/rvagg/polendina/commit/42d3c28e42513aa87204c1ecac9d960d54c275c6)) 38 | * **deps-dev:** bump mocha in /test/fixtures/mocha-failure ([56aef00](https://github.com/rvagg/polendina/commit/56aef004cd68f3c5c80fd4ba8344ae04c6b99193)) 39 | * **deps-dev:** bump mocha in /test/fixtures/mocha-failure-esm ([e685972](https://github.com/rvagg/polendina/commit/e685972590ad5bea7f7bc3b99fd63f4c5d81e7b3)) 40 | 41 | ## [3.2.5](https://github.com/rvagg/polendina/compare/v3.2.4...v3.2.5) (2024-10-25) 42 | 43 | ### Bug Fixes 44 | 45 | * adapt to Node.js 23 ESM CJS import wrapper ([85c1353](https://github.com/rvagg/polendina/commit/85c13537efa1316f7e77e269cf9a4e7664fa6e59)) 46 | 47 | ### Trivial Changes 48 | 49 | * **deps:** bump actions/setup-node from 4.0.4 to 4.1.0 ([f52212d](https://github.com/rvagg/polendina/commit/f52212d85c8e2a06a3ef596a7f0130a65fda76a2)) 50 | * update deps ([cfd7be4](https://github.com/rvagg/polendina/commit/cfd7be4fff78503eccef4fc328cec14ecff68f0e)) 51 | 52 | ## [3.2.4](https://github.com/rvagg/polendina/compare/v3.2.3...v3.2.4) (2024-09-26) 53 | 54 | ### Trivial Changes 55 | 56 | * **deps-dev:** bump chai from 4.5.0 to 5.1.1 in /test/fixtures/mocha ([ab2e24f](https://github.com/rvagg/polendina/commit/ab2e24fd27591fa820c12eb5c667904047afc023)) 57 | * **deps-dev:** bump chai in /test/fixtures/bare-async-esm ([3cd6cda](https://github.com/rvagg/polendina/commit/3cd6cda77d356c18617c376c65737a025f790c7c)) 58 | * **deps-dev:** bump chai in /test/fixtures/bare-async-failure-esm ([093dd73](https://github.com/rvagg/polendina/commit/093dd73580347d783fe55231b8ba77fa53afca95)) 59 | * **deps-dev:** bump chai in /test/fixtures/bare-sync-esm ([7249d99](https://github.com/rvagg/polendina/commit/7249d995edf86cfc756cbca2e305cbf35c68f5af)) 60 | * **deps-dev:** bump chai in /test/fixtures/bare-sync-failure-esm ([dfa8b32](https://github.com/rvagg/polendina/commit/dfa8b3253a5038f2ac9748b7b21db56380da7808)) 61 | * **deps-dev:** bump chai in /test/fixtures/mocha-esm ([2f437a2](https://github.com/rvagg/polendina/commit/2f437a23b5249a75e68854a8e41b8c5039063827)) 62 | * **deps-dev:** bump chai in /test/fixtures/mocha-failure ([a27175a](https://github.com/rvagg/polendina/commit/a27175ab02c7bee34dbb3be443accae34913818e)) 63 | * **deps-dev:** bump chai in /test/fixtures/mocha-failure-esm ([24d22a7](https://github.com/rvagg/polendina/commit/24d22a71bb48875c279956b608fc49e9a3efe69e)) 64 | * **deps-dev:** bump chai in /test/fixtures/webpack-merge ([850adbb](https://github.com/rvagg/polendina/commit/850adbbe29c6e668fb61c1ca30e4634b99a96a13)) 65 | 66 | ## [3.2.3](https://github.com/rvagg/polendina/compare/v3.2.2...v3.2.3) (2024-09-26) 67 | 68 | ### Trivial Changes 69 | 70 | * **deps:** bump actions/checkout from 3 to 4 ([c49b799](https://github.com/rvagg/polendina/commit/c49b7993eafccb7815a80950ff4bd3f10934a7f4)) 71 | 72 | ## [3.2.2](https://github.com/rvagg/polendina/compare/v3.2.1...v3.2.2) (2024-09-25) 73 | 74 | ### Trivial Changes 75 | 76 | * **deps:** bump actions/setup-node from 3.5.1 to 4.0.4 ([479fa2a](https://github.com/rvagg/polendina/commit/479fa2a7c5809d19fd5f0e0611307fe228ce2313)) 77 | * update deps ([488fd9c](https://github.com/rvagg/polendina/commit/488fd9caca279a8c07d6a17cde2f5b81d8066d78)) 78 | 79 | ## [3.2.1](https://github.com/rvagg/polendina/compare/v3.2.0...v3.2.1) (2023-05-16) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * properly handle subdirectories & relative directory tests ([4ea4a50](https://github.com/rvagg/polendina/commit/4ea4a5011385abdff614f2602e9be6be8cdd1d47)) 85 | 86 | ## [3.2.0](https://github.com/rvagg/polendina/compare/v3.1.0...v3.2.0) (2023-05-15) 87 | 88 | 89 | ### Features 90 | 91 | * big 'ol dependency update & associated fixes ([7e7bfc1](https://github.com/rvagg/polendina/commit/7e7bfc148c97384cd7c5f05d4b1a2894d0eb90d1)) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * ./ for registry loading, even on windows ([70d4c1f](https://github.com/rvagg/polendina/commit/70d4c1ff50616ad79def41c0d3b55f2668716d80)) 97 | * debug nonzero exit codes (windows, CI) ([362f6c5](https://github.com/rvagg/polendina/commit/362f6c55437dc11a164f821ef31f9f1fe130a10b)) 98 | * defer final console.log() ([b524822](https://github.com/rvagg/polendina/commit/b524822ea77ce72236190a2bffa2cf147a8f7c97)) 99 | * extend timeouts, add debugging (for CI) ([f71ad1a](https://github.com/rvagg/polendina/commit/f71ad1a0bb497898082a55823647061ab696e4ca)) 100 | * remove extraneous 'console' event ([02b884a](https://github.com/rvagg/polendina/commit/02b884a7504079ee1b9f27643f53653bafa0f017)) 101 | * sequence console.log() calls ([839fceb](https://github.com/rvagg/polendina/commit/839fceb045b60ef9b8e27e9a7b41ec26e37f898c)) 102 | * update node versions in CI ([aa2f616](https://github.com/rvagg/polendina/commit/aa2f616a1e2e92589b05c618e873758ab0aa6d46)) 103 | 104 | 105 | ### Trivial Changes 106 | 107 | * **no-release:** bump actions/setup-node from 3.1.1 to 3.2.0 ([#58](https://github.com/rvagg/polendina/issues/58)) ([115db86](https://github.com/rvagg/polendina/commit/115db867ce096bae7dcb34c94eed86c94738409e)) 108 | * **no-release:** bump actions/setup-node from 3.2.0 to 3.3.0 ([#59](https://github.com/rvagg/polendina/issues/59)) ([313dffd](https://github.com/rvagg/polendina/commit/313dffd40fb58a19bc528d4a31b937e34cab7a1f)) 109 | * **no-release:** bump actions/setup-node from 3.3.0 to 3.4.0 ([#67](https://github.com/rvagg/polendina/issues/67)) ([6d1aa17](https://github.com/rvagg/polendina/commit/6d1aa176013ab76e9370df360a4a39ad87644bdd)) 110 | * **no-release:** bump actions/setup-node from 3.4.0 to 3.4.1 ([#70](https://github.com/rvagg/polendina/issues/70)) ([9de3e55](https://github.com/rvagg/polendina/commit/9de3e554bbc2eab075cd8bf6a31e2ad1838f17ab)) 111 | * **no-release:** bump actions/setup-node from 3.4.1 to 3.5.0 ([#85](https://github.com/rvagg/polendina/issues/85)) ([3bc465c](https://github.com/rvagg/polendina/commit/3bc465cc3c7853036020e70db170e4d76327d610)) 112 | * **no-release:** bump actions/setup-node from 3.5.0 to 3.5.1 ([#88](https://github.com/rvagg/polendina/issues/88)) ([113e5e4](https://github.com/rvagg/polendina/commit/113e5e4bf3cab293084f6ef6c3d2206b164da7cf)) 113 | 114 | ## [3.1.0](https://github.com/rvagg/polendina/compare/v3.0.0...v3.1.0) (2022-05-10) 115 | 116 | 117 | ### Features 118 | 119 | * bump puppeteer from 13.7.0 to 14.0.0 ([#57](https://github.com/rvagg/polendina/issues/57)) ([8ab4886](https://github.com/rvagg/polendina/commit/8ab48869f24984025aa3d38e90502c02729a1008)) 120 | 121 | ## [3.0.0](https://github.com/rvagg/polendina/compare/v2.0.15...v3.0.0) (2022-05-02) 122 | 123 | 124 | ### ⚠ BREAKING CHANGES 125 | 126 | * update deps (incl mocha@10) & silence mocha warning 127 | 128 | ### Bug Fixes 129 | 130 | * update deps (incl mocha@10) & silence mocha warning ([c631362](https://github.com/rvagg/polendina/commit/c63136264d1663641c0ff5e92d003361a312ce7a)) 131 | 132 | 133 | ### Trivial Changes 134 | 135 | * **deps:** bump glob from 7.2.0 to 8.0.1 ([3e96580](https://github.com/rvagg/polendina/commit/3e96580427e685f3dece4226df42eae9670d1515)) 136 | * **deps:** bump puppeteer from 13.5.2 to 13.7.0 ([5489ebb](https://github.com/rvagg/polendina/commit/5489ebb84d7b30e28783d61011c57e46a9681402)) 137 | * **deps:** bump webpack from 5.70.0 to 5.72.0 ([ff7f9dd](https://github.com/rvagg/polendina/commit/ff7f9ddaa661683121ab6b4178a9a9678b854be6)) 138 | * **no-release:** bump actions/setup-node from 3.0.0 to 3.1.0 ([#50](https://github.com/rvagg/polendina/issues/50)) ([b59e0c1](https://github.com/rvagg/polendina/commit/b59e0c19fc72c45bbddadf560ba8cc78b71ce856)) 139 | * **no-release:** bump actions/setup-node from 3.1.0 to 3.1.1 ([#52](https://github.com/rvagg/polendina/issues/52)) ([0dd2cea](https://github.com/rvagg/polendina/commit/0dd2cea8a9ff43926db45db1b02c356e3eb56926)) 140 | 141 | ### [2.0.15](https://github.com/rvagg/polendina/compare/v2.0.14...v2.0.15) (2022-03-08) 142 | 143 | 144 | ### Trivial Changes 145 | 146 | * **deps:** bump puppeteer from 13.4.1 to 13.5.0 ([bb52f19](https://github.com/rvagg/polendina/commit/bb52f1987f9a686a81ec9b273a51e2b03ecb0a32)) 147 | 148 | ### [2.0.14](https://github.com/rvagg/polendina/compare/v2.0.13...v2.0.14) (2022-03-04) 149 | 150 | 151 | ### Trivial Changes 152 | 153 | * **deps:** bump webpack from 5.69.1 to 5.70.0 ([fb30b7b](https://github.com/rvagg/polendina/commit/fb30b7b75656d59afaf812e335e55cfec0033aba)) 154 | * **no-release:** bump actions/checkout from 2.4.0 to 3 ([#46](https://github.com/rvagg/polendina/issues/46)) ([6beb163](https://github.com/rvagg/polendina/commit/6beb163d51707a158374c304e40df0fc2bcd1a0c)) 155 | 156 | ### [2.0.13](https://github.com/rvagg/polendina/compare/v2.0.12...v2.0.13) (2022-02-25) 157 | 158 | 159 | ### Trivial Changes 160 | 161 | * **deps:** bump puppeteer from 13.3.2 to 13.4.0 ([b786a7e](https://github.com/rvagg/polendina/commit/b786a7eb637b9046b54473135e2ccce53b7d6d9b)) 162 | * **no-release:** bump actions/setup-node from 2.5.1 to 3.0.0 ([#45](https://github.com/rvagg/polendina/issues/45)) ([3f60aba](https://github.com/rvagg/polendina/commit/3f60aba983d7706aeff9fc955d161344ed9cb72f)) 163 | 164 | ### [2.0.12](https://github.com/rvagg/polendina/compare/v2.0.11...v2.0.12) (2022-02-16) 165 | 166 | 167 | ### Trivial Changes 168 | 169 | * **deps:** bump webpack from 5.68.0 to 5.69.0 ([76c656d](https://github.com/rvagg/polendina/commit/76c656dbffd076811f964b3792600ae238325eae)) 170 | 171 | ### [2.0.11](https://github.com/rvagg/polendina/compare/v2.0.10...v2.0.11) (2022-02-10) 172 | 173 | 174 | ### Trivial Changes 175 | 176 | * **deps:** bump puppeteer from 13.2.0 to 13.3.0 ([d0edab3](https://github.com/rvagg/polendina/commit/d0edab380271d915e8340c72fd76c0bfec35c23d)) 177 | 178 | ### [2.0.10](https://github.com/rvagg/polendina/compare/v2.0.9...v2.0.10) (2022-02-09) 179 | 180 | 181 | ### Trivial Changes 182 | 183 | * **deps:** bump puppeteer from 13.1.3 to 13.2.0 ([1583a26](https://github.com/rvagg/polendina/commit/1583a261f243b238a227afb4f7f5588e1760384c)) 184 | 185 | ### [2.0.9](https://github.com/rvagg/polendina/compare/v2.0.8...v2.0.9) (2022-02-01) 186 | 187 | 188 | ### Trivial Changes 189 | 190 | * **deps:** bump webpack from 5.67.0 to 5.68.0 ([3f796b4](https://github.com/rvagg/polendina/commit/3f796b44adf09fdbc32bd53f51c54a17cac469dc)) 191 | 192 | ### [2.0.8](https://github.com/rvagg/polendina/compare/v2.0.7...v2.0.8) (2022-01-28) 193 | 194 | 195 | ### Trivial Changes 196 | 197 | * **deps-dev:** bump tape from 5.4.1 to 5.5.0 in /test/fixtures/tape ([b564b82](https://github.com/rvagg/polendina/commit/b564b82aee325d58184ab2a0941352c482208e10)) 198 | * **deps-dev:** bump tape in /test/fixtures/tape-esm ([3f5db65](https://github.com/rvagg/polendina/commit/3f5db65286ca661cb0045f72752849c66fb04fb6)) 199 | * **deps-dev:** bump tape in /test/fixtures/tape-failure ([a928b0a](https://github.com/rvagg/polendina/commit/a928b0a0a0e381c06f6a27976bd9213c5d5b23f9)) 200 | * **deps-dev:** bump tape in /test/fixtures/tape-failure-esm ([4eba814](https://github.com/rvagg/polendina/commit/4eba81491603f947d778c22b48aa62b4a0585259)) 201 | 202 | ### [2.0.7](https://github.com/rvagg/polendina/compare/v2.0.6...v2.0.7) (2022-01-25) 203 | 204 | 205 | ### Trivial Changes 206 | 207 | * **deps-dev:** bump mocha from 9.1.4 to 9.2.0 in /test/fixtures/mocha ([600cd7c](https://github.com/rvagg/polendina/commit/600cd7c651b8d47cb9b99a296554c0fbe7eac605)) 208 | * **deps-dev:** bump mocha in /test/fixtures/mocha-esm ([9b3e67e](https://github.com/rvagg/polendina/commit/9b3e67e5f5cdafbcd6c5bd1b0b5b3b2addea4e6b)) 209 | * **deps-dev:** bump mocha in /test/fixtures/mocha-failure ([1e63d87](https://github.com/rvagg/polendina/commit/1e63d87bb156a6e6654843d0d60c3516f34e0a2c)) 210 | * **deps-dev:** bump mocha in /test/fixtures/mocha-failure-esm ([6efe0ee](https://github.com/rvagg/polendina/commit/6efe0ee4be480b2172717259735c8d9e36c7f2fd)) 211 | 212 | ### [2.0.6](https://github.com/rvagg/polendina/compare/v2.0.5...v2.0.6) (2022-01-24) 213 | 214 | 215 | ### Trivial Changes 216 | 217 | * **deps:** bump webpack from 5.66.0 to 5.67.0 ([#31](https://github.com/rvagg/polendina/issues/31)) ([e4b90f0](https://github.com/rvagg/polendina/commit/e4b90f059b1e35da553a343c9b1156330a653add)) 218 | 219 | ### [2.0.5](https://github.com/rvagg/polendina/compare/v2.0.4...v2.0.5) (2022-01-18) 220 | 221 | 222 | ### Trivial Changes 223 | 224 | * **deps:** bump puppeteer from 13.0.1 to 13.1.0 ([#30](https://github.com/rvagg/polendina/issues/30)) ([bbc956c](https://github.com/rvagg/polendina/commit/bbc956c9fae046cfdc67247e7d46042c329964cf)) 225 | 226 | ### [2.0.4](https://github.com/rvagg/polendina/compare/v2.0.3...v2.0.4) (2022-01-13) 227 | 228 | 229 | ### Trivial Changes 230 | 231 | * **deps:** bump webpack from 5.65.0 to 5.66.0 ([#29](https://github.com/rvagg/polendina/issues/29)) ([58a81f9](https://github.com/rvagg/polendina/commit/58a81f91fe326ab9a155d8339bab7fe42001a410)) 232 | 233 | ### [2.0.3](https://github.com/rvagg/polendina/compare/v2.0.2...v2.0.3) (2022-01-04) 234 | 235 | 236 | ### Trivial Changes 237 | 238 | * **deps-dev:** bump tape from 5.3.2 to 5.4.0 in /test/fixtures/tape ([c229ba3](https://github.com/rvagg/polendina/commit/c229ba3425c85a2dd050673da3321e329d745ab2)) 239 | * **deps-dev:** bump tape in /test/fixtures/tape-esm ([00d0e36](https://github.com/rvagg/polendina/commit/00d0e36b2850482e041fe61291cefe4d8a54d0b8)) 240 | * **deps-dev:** bump tape in /test/fixtures/tape-failure ([9517259](https://github.com/rvagg/polendina/commit/9517259f5f5ea05dae1a04c1f1a606afda467476)) 241 | * **deps-dev:** bump tape in /test/fixtures/tape-failure-esm ([f70b2d9](https://github.com/rvagg/polendina/commit/f70b2d97157545f89f61eed5363ccbda1b22257e)) 242 | * **deps:** bump actions/setup-node from 2.5.0 to 2.5.1 ([5bd1e5e](https://github.com/rvagg/polendina/commit/5bd1e5e6e668e1a66ebadfbf936b04bd1b5a82a7)) 243 | 244 | ### [2.0.2](https://github.com/rvagg/polendina/compare/v2.0.1...v2.0.2) (2021-12-13) 245 | 246 | 247 | ### Trivial Changes 248 | 249 | * **deps:** bump puppeteer from 12.0.1 to 13.0.0 ([4d82a04](https://github.com/rvagg/polendina/commit/4d82a04733e5adbd692e2c7ee5b8d91e7a275457)) 250 | 251 | ### [2.0.1](https://github.com/rvagg/polendina/compare/v2.0.0...v2.0.1) (2021-12-09) 252 | 253 | 254 | ### Trivial Changes 255 | 256 | * **deps-dev:** bump tape in /test/fixtures/tape-failure ([8e6bd35](https://github.com/rvagg/polendina/commit/8e6bd3575b9fe8cd3e3f252865aa7322dd724b5d)) 257 | * **deps-dev:** bump tape in /test/fixtures/tape-failure-esm ([325e8d4](https://github.com/rvagg/polendina/commit/325e8d4b5fb35de6b950044173b733e9233114ec)) 258 | * **deps:** bump actions/checkout from 2.3.4 to 2.4.0 ([0ff4939](https://github.com/rvagg/polendina/commit/0ff49399120f162955512827b427f5336e951280)) 259 | * **deps:** bump actions/setup-node from 2.4.0 to 2.5.0 ([cf1d48f](https://github.com/rvagg/polendina/commit/cf1d48f01b105f945b7a393703625cbd60348b18)) 260 | * **deps:** bump strip-ansi from 6.0.1 to 7.0.1 ([feeba0d](https://github.com/rvagg/polendina/commit/feeba0db755a83ee1d5b1e0d2730a8f9ae90a635)) 261 | 262 | ## [2.0.0](https://github.com/rvagg/polendina/compare/v1.1.1...v2.0.0) (2021-12-08) 263 | 264 | 265 | ### ⚠ BREAKING CHANGES 266 | 267 | * upgrade to webpack5 268 | 269 | ### Features 270 | 271 | * support both ESM and CJS ([4de7f38](https://github.com/rvagg/polendina/commit/4de7f38ae789463c5907e8e3fcab05052b441730)) 272 | * upgrade to webpack5 ([4e99018](https://github.com/rvagg/polendina/commit/4e99018837d51cc125324804755593a50205a28d)) 273 | 274 | 275 | ### Trivial Changes 276 | 277 | * add dependabot config, also for fixtures ([d074252](https://github.com/rvagg/polendina/commit/d0742522961100022597f9e92512164ade788bee)) 278 | * update deps (those that can be updated without code changes) ([7cca22c](https://github.com/rvagg/polendina/commit/7cca22cbc3847757ca887cfc632cceb99ff6439f)) 279 | 280 | ### [1.1.1](https://github.com/rvagg/polendina/compare/v1.1.0...v1.1.1) (2021-12-07) 281 | 282 | 283 | ### Bug Fixes 284 | 285 | * no build step for release ([b91faf0](https://github.com/rvagg/polendina/commit/b91faf08395e6f8bc44b21d02d94c2136bcac942)) 286 | 287 | 288 | ### Trivial Changes 289 | 290 | * add auto-releases ([#17](https://github.com/rvagg/polendina/issues/17)) ([9e71246](https://github.com/rvagg/polendina/commit/9e71246667ba96d8253aa180271728b35e700b45)) 291 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Rod Vagg 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # polendina 2 | 3 | **Non-UI browser testing for JavaScript libraries from the command-line** 4 | 5 | > At the words, the door opened and a dapper little old man came in. His name was Geppetto, but to the boys of the neighborhood he was Polendina, on account of the wig he always wore which was just the color of yellow corn. 6 | > 7 | > Geppetto had a very bad temper. Woe to the one who called him Polendina! He became as wild as a beast and no one could soothe him. 8 | 9 | ![CI](https://github.com/rvagg/polendina/workflows/CI/badge.svg?branch=master) 10 | 11 | [![NPM](https://nodei.co/npm/polendina.svg)](https://nodei.co/npm/polendina/) 12 | 13 | * [What and why?](#what-and-why) 14 | * [Usage](#usage) 15 | * [Options](#options) 16 | * [Examples](#examples) 17 | * [Test runners](#test-runners) 18 | * [Mocha](#mocha) 19 | * [Tape](#tape) 20 | * [bare-sync](#bare-sync) 21 | * [bare-async](#bare-async) 22 | * [TypeScript support (and other Webpack extensions)](#typescript-support-and-other-webpack-extensions) 23 | * [polendina-node: the minimal Node.js test runner](#polendina-node-the-minimal-nodejs-test-runner) 24 | * [Minimising Puppeteer's size](#minimising-puppeteers-size) 25 | * [Global and npx](#global-and-npx) 26 | * [License and Copyright](#license-and-copyright) 27 | 28 | ## What and why? 29 | 30 | Browser testing _still_ sucks, especially if you just want to make sure your JavaScript libraries work in a browser environment and don't need the UI testing overhead. Thankfully there's [Puppeteer](https://pptr.dev/) that can run headless Chrome with a Node.js API. 31 | 32 | **polendina is a frontend to Puppeteer that simplifies the testing pipeline for JavaScript libraries.** A typical use-case is a library that uses a browser-friendly Node.js test runner, such as Mocha but is designed for both Node.js and the browser. polendina can be inserted into npm scripts to run the test suite in a browser environment. `npm test` can then trigger both Node.js _and_ browser tests without dramatically extending execution time. 33 | 34 | ***Example package.json:*** 35 | 36 | ```json 37 | "scripts": { 38 | "lint": "standard", 39 | "test:node": "mocha tests/test-*.js", 40 | "test:browser": "polendina --cleanup tests/test-*.js", 41 | "test": "npm run lint && npm run test:node && npm run test:browser" 42 | } 43 | ``` 44 | 45 | ***Example execution:*** 46 | 47 | Using `mocha test/*.spec.js && polendina --cleanup test/*.spec.js` as the test script. The first block is natively via Node.js using the Mocha CLI. The second block is within a browser page, using the Mocha browser bundle reporting back to the command-line. Total time for this simple test suite across Node.js and the browser was 2.5 seconds. 48 | 49 | ![demo.png](demo.png) 50 | 51 | Even though polendina is intended for "non-UI" testing, your tests run in a standard browser environment so the UI isn't out of bounds if need it. 52 | 53 | ## Usage 54 | 55 | `polendina ` 56 | 57 | ### Options 58 | 59 | * `--runner`, `-r`: The test runner to use (choices: "mocha", "tape", "bare-sync", "bare-async") (default: "mocha") 60 | * `--output-dir`, `-o`: Location for temporary build resources, if `--cleanup` isn't specified you will need to clean this up yourself (default: "./build") 61 | * `--page`: Run tests in standard browser page (default: "true") 62 | * `--worker`: Run tests in a WebWorker (default: "false") 63 | * `--serviceworker`: Run tests in a ServiceWorker (default: "false") 64 | * `--stats`: Write webpack-stats.json with bundle; written to the `output-dir` so will be removed if `--cleanup` is specified (default: "false") 65 | * `--cleanup`: Remove the `output-dir` after execution (default: "false") 66 | * `--timeout`: Number of seconds to wait before auto-failing the test suite (default: "30") 67 | * `--webpack-config`: Supply a path to a webpack.config.js to merge into Polendina's Webpack config (use with caution) 68 | * `--mocha-reporter`: Specify the Mocha reporter if the test runner is Mocha (default: "spec") 69 | 70 | ## Examples 71 | 72 | Run Mocha test suite in Page, a WebWorker and a ServiceWorker, then clean up: 73 | 74 | ``` 75 | polendina --worker --serviceworker --cleanup tests/**/test-*.js 76 | ``` 77 | 78 | Run a Tape test suite in a WebWorker only and write a webpack-stats.json file: 79 | 80 | ``` 81 | polendina --runner=tape --page=false --worker --stats tests/*-test.js 82 | ``` 83 | 84 | Run a bare synchronous test file (tests are executed as soon as the file is loaded with no asynchronous activity), then clean up: 85 | 86 | ``` 87 | polendina --runner=bare-sync test.js 88 | ``` 89 | 90 | ## Test runners 91 | 92 | Tests should be run with the correct test runner. Tests written against Mocha and Tape require dependencies be loaded in the browser along with some additional utilities to ensure output and test end is properly handled. 93 | 94 | ### [Mocha](https://mochajs.org) 95 | 96 | The default test runner (no need to supply a `--runner`). Standard Mocha reporters may be used, with `--mocha-reporter`. However, the default reporter, `spec` should be preferred as it is the best behaved when printing output. The `tap` reporter is usable. Other reporters, that combine `process.stdout.write()` and `console.log()` have some trouble with interleaved output. 97 | 98 | ### [Tape](https://ghub.io/tape) 99 | 100 | Use with `--runner=tape` 101 | 102 | ### bare-sync 103 | 104 | Use with `--runner=bare-sync` 105 | 106 | The `bare-sync` runner assumes either a custom, synchronous test runner is being used, or the fule itself is raw test commands without any asynchronous activity. The `bare-sync` runner will simply load your test files and assume that it will execute sequentially and that the tests are complete when the file has ceased loading. 107 | 108 | Test failure for a `bare-sync` runner is signalled by a thrown exception. Most assertion libraries, including the Node.js `assert` module, throw exceptions on failure so may be used. 109 | 110 | Simple progress is printed to stdout showing the names of the test files being executed and whether they succeeded or failed. The first failure will end the entire test suite. 111 | 112 | **Example `bare-sync` test file:** 113 | 114 | ```js 115 | const assert = require('assert') 116 | const mymod = require('./') 117 | 118 | module.exports = () => { 119 | assert.strictEquals(typeof mymod.multiply, 'function', 'exports `multiply()`) 120 | assert.strictEquals(mymod.multiply(10, 5), 50, '`multiply()` works properly) 121 | } 122 | ``` 123 | 124 | ### bare-async 125 | 126 | Use with `--runner=bare-async` 127 | 128 | The `bare-async` runner assumes that test files _export_ the tests in the form of `async` (or `Promise` returning) functions. A test file may either export a single test function, e.g. `module.exports = async () => { ... }`. Alternatively a test file may export multiple test functions, e.g. `module.exports.testOne = async () => { ... }` etc. 129 | 130 | Test functions will be run sequentually, waiting for each asynchronous function to complete. 131 | 132 | Test failure for a `bare-async` runner is signalled by a thrown exception (or `Promise` rejection). Most assertion libraries, including the Node.js `assert` module, throw exceptions on failure so may be used. 133 | 134 | Simple progress is printed to stdout showing both the names of the test files being executed any any constituent test functions within those files if `module.exports` is not a function itself. The first failure will end the entire test suite. 135 | 136 | **Example `bare-async` test file:** 137 | 138 | ```js 139 | const assert = require('assert') 140 | const mymod = require('./') 141 | const mockServer = require('./mock-server') 142 | 143 | module.exports.testExport = () => { // non-async function works too as `await` is used 144 | assert.strictEquals(typeof mymod.fetch, 'function', 'exports `fetch()`) 145 | } 146 | 147 | module.exports.testFetch = async () => { 148 | const server = await mockServer() 149 | const thing = await mymod('localhost', server.port).fetch('/foo/bar/plip') 150 | assert.strictEquals(thing, { plip: 'plop' }, '`fetch()` works properly) 151 | } 152 | 153 | module.exports.testFail = async () => { 154 | await assert.rejects(async () => { 155 | await mymod('localhost', '8888').fetch('/foo/bar/plip') 156 | }, { 157 | name: 'Error', 158 | message: 'Could not connect to server' 159 | }) 160 | } 161 | ``` 162 | 163 | ## TypeScript support (and other Webpack extensions) 164 | 165 | Among other things, the `--webpack-config` option can be used to enable TypeScript support by mixing in the [ts-loader](https://ghub.io/ts-loader) package: 166 | 167 | **webpack.config.ts.js** 168 | 169 | ```js 170 | module.exports = { 171 | module: { 172 | rules: [ 173 | { 174 | test: /\.tsx?$/, 175 | use: 'ts-loader', 176 | exclude: /node_modules/ 177 | } 178 | ] 179 | } 180 | resolve: { 181 | extensions: ['.tsx', '.ts', '.js'] 182 | } 183 | } 184 | ``` 185 | 186 | ```sh 187 | $ polendina --cleanup test/*.spec.ts --webpack-config webpack.config.ts.js 188 | ``` 189 | 190 | 191 | ## `polendina-node`: the minimal Node.js test runner 192 | 193 | polendina's `bare-sync` and `bare-async` modes run plain Node.js modules in the browser without the need for a test runner. But if you want to run these types of files in Node.js you may find it difficult to scale when you have many modules. 194 | 195 | A `bare-sync` test file is a simple matter of `node file.js` and checking the exit code, but as you increase the number of files, your package.json `"test"` script starts to get out of hand. 196 | 197 | A `bare-async` test file requires a custom runner since it uses exported `async` functions. A simple `require('./file')().catch((err) { console.error(err); process.exit(1) })` would suffice for a single function exported on a single file but this obviously doesn't scale well and your before long your test runner will become a beast of its own. 198 | 199 | To deal with this, polendina also ships with a `polendina-node` command-line test runner to manage these for you: 200 | 201 | * `polendina-node bare-sync `: Run synchronous tests using a plain require(file) on each test file, with simple progress reporting. 202 | * `polendina-node bare-async `: Run tests by executing exported functions from files as async, with simple progress reporting. 203 | 204 | The `bare-sync` runner doesn't use anything exported from the files listed, simply `require()`ing them and checking for `throw`n errors is assumed enough to run them. 205 | 206 | The `bare-async` runner will look for `module.exports` as a function or its child properties to be function and will run them as `async` functions (they don't need to actualy be `async` or return a function, they could be exported sync functions). See [bare-async](#bare-async) above for examples. 207 | 208 | ## Minimising Puppeteer's size 209 | 210 | During install, Puppeteer will download a version of Chromium designed to work with it. This can be quite large (200 - 300 Mb depending on platform). This can be avoided, but you should note the following [caveat from the Puppeteer folks](https://github.com/puppeteer/puppeteer#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy): 211 | 212 | > We see Puppeteer as an indivisible entity with Chromium. Each version of Puppeteer bundles a specific version of Chromium – the only version it is guaranteed to work with. 213 | 214 | If you are willing to own this risk, you could install a Canary / Unstable version of Chrome on your system and set the following environment variables in your `~/.profile` (or similar): 215 | 216 | ```sh 217 | export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true 218 | # path to Chrome Canary / Chromium Unstable 219 | export PUPPETEER_EXECUTABLE_PATH="/usr/bin/google-chrome-unstable" 220 | # on macOS you could use: "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary" 221 | ``` 222 | 223 | The first environment variable will tell Puppeteer to not bother downloading its own Chromium, and the second tells it which executable to use instead. 224 | 225 | ### Global and `npx` 226 | 227 | Running polendina with npm's [`npx`](https://github.com/npm/npx) tool (i.e. run as `npx polendina ...`) will avoid bloating your node_modules directories. Instead, `npx` will install a copy in your npm cache and run it from there. This can be handy if you have multiple projects using polendina. 228 | 229 | If you install polendina as a global (`npm install polendina -g`) then `npx` will even find it from there, avoiding the need to install a copy in the npm cache and you get a `polendina` executable as a bonus. 230 | 231 | ## License and Copyright 232 | 233 | Copyright 2019 Rod Vagg 234 | 235 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 236 | 237 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 238 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvagg/polendina/192ebb8a416efabac2d48d148017088a0bdef7bc/demo.png -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | import process from 'process' 2 | import colors from 'ansi-colors' 3 | import stripAnsi from 'strip-ansi' 4 | 5 | const tty = process.stdout.isTTY && process.stderr.isTTY 6 | let logQueue = [] 7 | let logSeq = -1 8 | 9 | function brand (error) { 10 | const brand = 'polendina ☞' 11 | return tty ? `${colors[error ? 'red' : 'green'](brand)}` : brand 12 | } 13 | 14 | export function log (msg) { 15 | if (msg === undefined) { 16 | return console.log() 17 | } 18 | console.log(` ${brand()} ${tty ? colors.gray.italic(msg) : msg}`) 19 | } 20 | 21 | export function error (msg) { 22 | console.log(` ${brand(true)} ${tty ? colors.gray.italic(msg) : msg}`) 23 | } 24 | 25 | export function logRaw (type, seq, args) { 26 | if (!tty) { 27 | args = args.map((a) => typeof a === 'string' ? stripAnsi(a) : a) 28 | } 29 | logQueue.push({ 30 | type: type === 'info' ? 'log' : type, 31 | args, 32 | seq 33 | }) 34 | flushLogs(false) 35 | } 36 | 37 | export function flushLogs (force) { 38 | while (true) { 39 | const lastLen = logQueue.length 40 | logQueue = logQueue.filter((le) => { 41 | const next = le.seq === -1 || le.seq === logSeq + 1 42 | if (force || next) { 43 | console[le.type].apply(null, le.args) 44 | if (le.seq !== -1) { 45 | logSeq++ 46 | } 47 | } else if (le.seq < logSeq) { 48 | throw new Error(`Unexpected log output sequencing (${le.seq}<${logSeq})`) 49 | } 50 | return !next 51 | }) 52 | if (lastLen === logQueue.length) { 53 | break 54 | } 55 | } 56 | if (force) { 57 | logSeq = -1 58 | } 59 | } 60 | 61 | export function logWrite (args) { 62 | args = Array.isArray(args) ? args : [args] 63 | process.stdout.write.apply(process.stdout, args) 64 | } 65 | -------------------------------------------------------------------------------- /lib/puppeteer.js: -------------------------------------------------------------------------------- 1 | import puppeteer from 'puppeteer' 2 | // TODO 3 | // const pti = require('puppeteer-to-istanbul') 4 | // const { copyFile } = require('fs').promises 5 | import { log, error, logRaw, logWrite, flushLogs } from './log.js' 6 | 7 | // wrap our convoluted _run() function in a pure Promise that can handle 8 | // both standard throws and the callback that ends it. _run() needs to handle 9 | // ends in a few different ways, hence the hack. 10 | export function run (outputDir, port, timeout, mode, runner, coverage) { 11 | return new Promise((resolve, reject) => { 12 | _run(outputDir, port, timeout, mode, runner, coverage, (err, errors) => { 13 | if (err) { 14 | return reject(err) 15 | } 16 | resolve(errors) 17 | }) 18 | }) 19 | } 20 | 21 | // this can throw, or it can end via `callback()` which may or may not contain 22 | // a runtime-error argument and a "number of errors from tests" argument. 23 | // the callback may be triggered by proper test end or failure, or a timeout. 24 | async function _run (outputDir, port, timeout, mode, runner, coverage, callback) { 25 | let executionQueue = Promise.resolve() 26 | 27 | const browser = await puppeteer.launch({ args: ['--no-sandbox'], headless: 'new' }) 28 | const [page] = await browser.pages() 29 | 30 | // this works as a debounce exit method, when process.stdout.write() is used 31 | // instead of console.log, the output goes through the stream handling which 32 | // inserts delays so log output comes later than an end() signal. If we get 33 | // an end(), we delay for a short time and then keep delaying if we get more 34 | // output—it should come in steady increments after an end() as we're only 35 | // waiting for stream delays, not test delays at that point. 36 | let lastCall 37 | 38 | function end (errors) { 39 | lastCall = setTimeout(() => { 40 | if (!executionQueue) { 41 | error('end after end') 42 | return 43 | } 44 | executionQueue.then(async () => { 45 | flushLogs(true) 46 | executionQueue = null 47 | /* TODO 48 | if (coverage) { 49 | const jsCoverage = await page.coverage.stopJSCoverage() 50 | pti.write([...jsCoverage]) 51 | await copyFile('build/bundle.js.map', '.nyc_output/js/bundle.js.map') 52 | } 53 | */ 54 | await browser.close() 55 | }).catch(callback) 56 | .then(() => { callback(null, errors) }) 57 | }, 100) 58 | } 59 | 60 | function maybeEnd () { 61 | if (lastCall) { 62 | clearTimeout(lastCall) 63 | end() 64 | } 65 | } 66 | 67 | // this should be a rare, or impossible event since we intercept console.log 68 | /* 69 | page.on('console', (msg) => { 70 | if (!executionQueue) { 71 | error(`log after end: ${msg.text()}`) 72 | return 73 | } 74 | const args = [] 75 | executionQueue = executionQueue.then(async () => { 76 | logRaw('info', -1, await Promise.all(args)) 77 | }) 78 | for (const arg of msg.args()) { 79 | args.push(arg.evaluate(n => n)) 80 | } 81 | maybeEnd() 82 | }) 83 | */ 84 | 85 | page.on('error', (error) => console.error(error)) 86 | page.on('pageerror', (error) => console.error(error)) 87 | 88 | await page.exposeFunction('polendinaEnd', (errors) => { 89 | end(errors) 90 | }) 91 | await page.exposeFunction('polendinaLog', (args) => { 92 | logRaw(args.shift(), args.shift(), args) 93 | maybeEnd() 94 | }) 95 | await page.exposeFunction('polendinaWrite', (args) => { 96 | logWrite(args) 97 | maybeEnd() 98 | }) 99 | 100 | if (coverage) { 101 | await page.coverage.startJSCoverage() 102 | } 103 | 104 | const url = `http://localhost:${port}/?mode=${mode}&runner=${runner}` 105 | log(`Running ${runner} ${mode} tests with Puppeteer via\n ${url}`) 106 | if (runner !== 'mocha') { 107 | log() 108 | } 109 | 110 | await page.goto(url) 111 | 112 | setTimeout(() => { 113 | const err = new Error(`timeout: tests did not finish cleanly after ${timeout} seconds`) 114 | const cb = () => callback(err) 115 | browser.close().then(cb).catch(cb) 116 | }, timeout * 1000).unref() 117 | } 118 | -------------------------------------------------------------------------------- /lib/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { glob } from 'glob' 3 | import { fileURLToPath } from 'url' 4 | 5 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 6 | 7 | export function webpackConfig (env, options, runner) { 8 | const bundleRun = path.normalize(path.resolve(options.outputDir, runner)) 9 | const testFiles = options._.reduce((p, c) => { 10 | return p.concat(glob.sync(c, { absolute: true }).sort()) 11 | }, []).map(path.normalize) 12 | 13 | // our globs may be fruitless 14 | if (!testFiles.length) { 15 | throw new Error(`No test files found: '${options._.join(' ')}'`) 16 | } 17 | 18 | testFiles.push(bundleRun) 19 | 20 | return { 21 | mode: 'development', 22 | context: process.cwd(), 23 | entry: testFiles, 24 | output: { 25 | path: path.resolve(process.cwd(), options.outputDir), 26 | filename: 'bundle.js' 27 | }, 28 | devtool: 'cheap-module-source-map', 29 | optimization: { 30 | minimize: false 31 | }, 32 | resolve: { 33 | modules: [ 34 | path.join(process.cwd(), 'node_modules'), 35 | path.join(__dirname, '../node_modules') 36 | ] 37 | }, 38 | resolveLoader: { 39 | modules: [ 40 | path.join(process.cwd(), 'node_modules'), 41 | path.join(__dirname, '../node_modules') 42 | ] 43 | }, 44 | module: { 45 | exprContextCritical: false, // mocha stil has a require.resolve resulting in a noisy warning 46 | rules: [ 47 | { 48 | test: testFiles, 49 | exclude: [/node_modules/, bundleRun], 50 | type: 'javascript/auto', // needed so the wrap-loader can get a sync require() in 51 | use: [{ 52 | loader: path.resolve(__dirname, 'wrap-loader.cjs'), 53 | options 54 | }] 55 | } 56 | ] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/wrap-loader.cjs: -------------------------------------------------------------------------------- 1 | // webpack loader for test files, wrap them in a function and "register" them with 2 | // a central registry so they can be pulled out by the test runner and loaded at 3 | // an appropriate time. Some tests will execute straight away if we don't defer with 4 | // a function wrap but we need better control over timing. 5 | 6 | const path = require('path') 7 | 8 | function wrapLoader (src) { 9 | return src 10 | } 11 | 12 | wrapLoader.pitch = function pitch (remainingRequest) { 13 | const argv = this.query 14 | 15 | // probably redundant 16 | if (remainingRequest === path.resolve(argv.outputDir, 'bundle-run.js')) { 17 | return 18 | } 19 | 20 | // wrap the test in a function that is only called when we want it called 21 | return ` 22 | const { registry } = require('./${path.relative(path.dirname(remainingRequest), path.resolve(argv.outputDir, 'test-registry.cjs')).replace(/\\/g, '/')}') 23 | registry.argv = JSON.parse(${JSON.stringify(JSON.stringify(argv))}) 24 | registry.tests.push({ 25 | name: JSON.parse('${JSON.stringify(path.normalize(path.relative(process.cwd(), remainingRequest))).replace(/\\+/g, '/')}'), 26 | load: () => { return require(${JSON.stringify(`!!./${path.basename(remainingRequest)}`)}) } 27 | }) 28 | module.exports = {} 29 | ` 30 | } 31 | 32 | module.exports = wrapLoader 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polendina", 3 | "version": "3.2.11", 4 | "description": "Non-UI browser testing for JavaScript libraries from the command-line", 5 | "main": "polendina.js", 6 | "type": "module", 7 | "dependencies": { 8 | "ansi-colors": "^4.1.3", 9 | "glob": "^11.0.0", 10 | "path-browserify": "^1.0.1", 11 | "process": "^0.11.10", 12 | "puppeteer": "^24.0.0", 13 | "raw-loader": "^4.0.2", 14 | "readable-stream": "^4.5.2", 15 | "rimraf": "^6.0.1", 16 | "st": "^3.0.2", 17 | "stream-browserify": "^3.0.0", 18 | "strip-ansi": "^7.1.0", 19 | "webpack": "^5.95.0", 20 | "webpack-merge": "^6.0.1", 21 | "yargs": "^18.0.0" 22 | }, 23 | "bin": { 24 | "polendina": "./polendina-cli.js", 25 | "polendina-node": "./polendina-node-cli.js" 26 | }, 27 | "scripts": { 28 | "lint": "standard", 29 | "test:install": "for f in $(cd test/fixtures/; ls); do (cd test/fixtures/$f && grep devDependencies package.json > /dev/null && npm i --no-audit --no-fund --no-package-lock) || true; done", 30 | "test:run": "npm_config_yes=true npx mocha test/test-*.js", 31 | "test": "npm run lint && npm run test:install && npm run test:run" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/rvagg/polendina.git" 36 | }, 37 | "keywords": [ 38 | "puppeteer", 39 | "chrome", 40 | "chromium", 41 | "mocha", 42 | "tape", 43 | "tests", 44 | "testing", 45 | "test" 46 | ], 47 | "author": "Rod (http://r.va.gg/)", 48 | "license": "Apache-2.0", 49 | "release": { 50 | "branches": [ 51 | "master" 52 | ], 53 | "plugins": [ 54 | [ 55 | "@semantic-release/commit-analyzer", 56 | { 57 | "preset": "conventionalcommits", 58 | "releaseRules": [ 59 | { 60 | "breaking": true, 61 | "release": "major" 62 | }, 63 | { 64 | "revert": true, 65 | "release": "patch" 66 | }, 67 | { 68 | "type": "feat", 69 | "release": "minor" 70 | }, 71 | { 72 | "type": "fix", 73 | "release": "patch" 74 | }, 75 | { 76 | "type": "chore", 77 | "release": "patch" 78 | }, 79 | { 80 | "type": "docs", 81 | "release": "patch" 82 | }, 83 | { 84 | "type": "test", 85 | "release": "patch" 86 | }, 87 | { 88 | "scope": "no-release", 89 | "release": false 90 | } 91 | ] 92 | } 93 | ], 94 | [ 95 | "@semantic-release/release-notes-generator", 96 | { 97 | "preset": "conventionalcommits", 98 | "presetConfig": { 99 | "types": [ 100 | { 101 | "type": "feat", 102 | "section": "Features" 103 | }, 104 | { 105 | "type": "fix", 106 | "section": "Bug Fixes" 107 | }, 108 | { 109 | "type": "chore", 110 | "section": "Trivial Changes" 111 | }, 112 | { 113 | "type": "docs", 114 | "section": "Trivial Changes" 115 | }, 116 | { 117 | "type": "test", 118 | "section": "Tests" 119 | } 120 | ] 121 | } 122 | } 123 | ], 124 | "@semantic-release/changelog", 125 | "@semantic-release/npm", 126 | "@semantic-release/github", 127 | "@semantic-release/git" 128 | ] 129 | }, 130 | "devDependencies": { 131 | "standard": "^17.1.2" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /polendina-cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import path from 'path' 4 | import yargs from 'yargs' 5 | import { hideBin } from 'yargs/helpers' 6 | import { log } from './lib/log.js' 7 | import { Polendina } from './polendina.js' 8 | 9 | const start = Date.now() 10 | const argv = yargs(hideBin(process.argv)) 11 | .usage('$0 testfile.js [testfile2.js [tests/**/test*.js ...] ]') 12 | .option('runner', { 13 | alias: 'r', 14 | type: 'string', 15 | describe: 'The test runner to use', 16 | choices: ['mocha', 'tape', 'bare-sync', 'bare-async'], 17 | default: 'mocha' 18 | }) 19 | .option('output-dir', { 20 | alias: 'o', 21 | type: 'string', 22 | describe: 'Location for temporary build resources', 23 | default: path.join(process.cwd(), 'build') 24 | }) 25 | .option('page', { 26 | type: 'boolean', 27 | describe: 'Run tests in standard browser page', 28 | default: true 29 | }) 30 | .option('worker', { 31 | type: 'boolean', 32 | describe: 'Run tests in a WebWorker', 33 | default: false 34 | }) 35 | .option('serviceworker', { 36 | type: 'boolean', 37 | describe: 'Run tests in a ServiceWorker', 38 | default: false 39 | }) 40 | .option('stats', { 41 | type: 'boolean', 42 | describe: 'Write webpack-stats.json with bundle', 43 | default: false 44 | }) 45 | .option('cleanup', { 46 | type: 'boolean', 47 | describe: 'Remove the output-dir after execution', 48 | default: false 49 | }) 50 | .option('timeout', { 51 | type: 'number', 52 | describe: 'Number of seconds to wait before auto-failing the test suite', 53 | default: 30 54 | }) 55 | .option('coverage', { 56 | type: 'boolean', 57 | describe: 'Enable coverage reporting', 58 | default: false, 59 | hidden: true 60 | }) 61 | .option('webpack-config', { 62 | type: 'string', 63 | describe: 'Supply a path to a webpack.config.js to merge into Polendina\'s Webpack config (use with caution)', 64 | requiresArg: true 65 | }) 66 | .option('mocha-reporter', { 67 | type: 'string', 68 | describe: 'Specify the Mocha reporter', 69 | default: 'spec', 70 | requiresArg: true 71 | }) 72 | .help('help') 73 | .demandCommand(1, 'You must supply at least one test file') 74 | .check((argv) => { 75 | if (!argv.page && !argv.worker && !argv.serviceworker) { 76 | throw new Error('No mode specified, use one or more of `--page`, `--worker`, `--serviceworker`') 77 | } 78 | if (argv.timeout <= 0) { 79 | throw new Error(`Invalid timeout value (${argv.timeout})`) 80 | } 81 | if (!argv.outputDir) { 82 | throw new Error('--output-dir required') 83 | } 84 | return true 85 | }) 86 | .argv 87 | 88 | ;(async () => { 89 | const polendina = new Polendina(argv) 90 | 91 | log(`Setting up output directory: ${polendina.outputDir} ...`) 92 | await polendina.build() 93 | log(`Created bundle: ${path.join(polendina.outputDir, polendina.bundleFile)} ...`) 94 | if (polendina.statsFile) { 95 | log(`Created stats: ${polendina.statsFile} ...`) 96 | } 97 | 98 | const errors = await polendina.run() 99 | 100 | if (argv.cleanup) { 101 | log(`Removing output directory: ${polendina.outputDir}`) 102 | await polendina.cleanup() 103 | } 104 | 105 | let time = (Date.now() - start) / 1000 106 | if (time > 10) { 107 | time = Math.round(time) 108 | } else { 109 | time = Math.round(time * 10) / 10 110 | } 111 | 112 | log(`Took ${time} second${time === 1 ? '' : 's'}`) 113 | 114 | if (errors) { 115 | return process.exit(errors) 116 | } 117 | })() 118 | .catch((err) => { 119 | console.error(err.stack || err) 120 | if (err.details) { 121 | console.error(err.details) 122 | } 123 | process.exit(1) 124 | }) 125 | -------------------------------------------------------------------------------- /polendina-node-cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from 'process' 4 | import path from 'path' 5 | import { glob } from 'glob' 6 | import stripAnsi from 'strip-ansi' 7 | import yargs from 'yargs' 8 | import { hideBin } from 'yargs/helpers' 9 | import { BareRunner } from './resources/bare.js' 10 | 11 | const start = Date.now() 12 | const tty = process.stdout.isTTY && process.stderr.isTTY 13 | const argv = yargs(hideBin(process.argv)) 14 | .command('bare-sync ', 'Run synchronous tests using a plain require(file)') 15 | .command('bare-async ', 'Run tests by executing exported functions from files as async') 16 | .demandCommand() 17 | .help('help') 18 | .check((argv) => { 19 | if (argv._[0] !== 'bare-sync' && argv._[0] !== 'bare-async') { 20 | throw new Error('Run with either `bare-sync` or `bare-async`') 21 | } 22 | return true 23 | }) 24 | .argv 25 | 26 | function cleanLog (to) { 27 | return (...args) => { 28 | if (!tty) { 29 | args = args.map((a) => typeof a === 'string' ? stripAnsi(a) : a) 30 | } 31 | to.apply(null, args) 32 | } 33 | } 34 | 35 | async function run () { 36 | const mode = argv._[0] 37 | const testFiles = argv.testfiles.reduce((p, c) => { 38 | return p.concat(glob.sync(c, { absolute: true }).sort()) 39 | }, []) 40 | 41 | // our globs may be fruitless 42 | if (!testFiles.length) { 43 | throw new Error(`No test files found: '${argv._.join(' ')}'`) 44 | } 45 | 46 | const tests = testFiles.map((f) => { 47 | return { 48 | name: path.relative(process.cwd(), f), 49 | load: async () => { 50 | const mod = await import(new URL(`file://${path.resolve(process.cwd(), f)}`)) 51 | // Node.js 23 ESM CJS wrapper 52 | return 'module.exports' in mod ? mod['module.exports'] : mod 53 | } 54 | } 55 | }) 56 | const log = { 57 | error: cleanLog(console.error), 58 | info: cleanLog(console.log) 59 | } 60 | const runner = new BareRunner(log, tests) 61 | const errors = await runner[mode === 'bare-sync' ? 'runBareSync' : 'runBareAsync']() 62 | return errors 63 | } 64 | 65 | run() 66 | .catch((err) => { 67 | console.error(err.stack || err) 68 | if (err.details) { 69 | console.error(err.details) 70 | } 71 | process.exit(1) 72 | }) 73 | .then((errors) => { 74 | let time = (Date.now() - start) / 1000 75 | if (time > 10) { 76 | time = Math.round(time) 77 | } else { 78 | time = Math.round(time * 10) / 10 79 | } 80 | 81 | console.log(`Took ${time} second${time === 1 ? '' : 's'}`) 82 | 83 | process.exit(errors ? 1 : 0) 84 | }) 85 | -------------------------------------------------------------------------------- /polendina.js: -------------------------------------------------------------------------------- 1 | import { promisify } from 'util' 2 | import path from 'path' 3 | import fs from 'fs' 4 | import http from 'http' 5 | import { fileURLToPath } from 'url' 6 | import st from 'st' 7 | import { rimraf } from 'rimraf' 8 | import _webpack from 'webpack' 9 | import { merge } from 'webpack-merge' 10 | import { run as puppeteer } from './lib/puppeteer.js' 11 | import { webpackConfig } from './lib/webpack.config.js' 12 | 13 | const webpack = promisify(_webpack) 14 | 15 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 16 | 17 | export class Polendina { 18 | constructor (options) { 19 | this._options = options 20 | 21 | if (this._options.runner === 'mocha') { 22 | this._runnerModule = 'mocha-run.js' 23 | } else if (this._options.runner === 'tape') { 24 | this._runnerModule = 'tape-run.js' 25 | } else if (this._options.runner.startsWith('bare-')) { 26 | this._runnerModule = 'bare-run.js' 27 | } else { 28 | throw new Error(`Unknown runner ${this._options.runner}`) 29 | } 30 | 31 | this._mode = { 32 | page: this._options.page, 33 | worker: this._options.worker, 34 | serviceworker: this._options.serviceworker 35 | } 36 | 37 | this.outputDir = path.resolve(process.cwd(), this._options.outputDir) 38 | } 39 | 40 | async build () { 41 | let config = webpackConfig(process.env, this._options, this._runnerModule) 42 | 43 | if (this._options.runner === 'tape') { 44 | config = merge(config, { 45 | resolve: { 46 | fallback: { 47 | stream: path.join(__dirname, 'node_modules', 'stream-browserify'), 48 | path: path.join(__dirname, 'node_modules', 'path-browserify'), 49 | fs: false 50 | } 51 | }, 52 | plugins: [ 53 | new webpack.ProvidePlugin({ 54 | process: 'process/browser' 55 | }) 56 | ] 57 | }) 58 | } 59 | 60 | if (this._options.webpackConfig) { 61 | const userConfig = (await import(`file://${path.join(process.cwd(), this._options.webpackConfig)}`)).default 62 | config = merge(config, userConfig) 63 | } 64 | 65 | await fs.promises.mkdir(this.outputDir, { recursive: true }) 66 | const copyFiles = ['index.html', 'test-registry.cjs', 'page-run.js', 'common-run.js', this._runnerModule] 67 | if (this._options.runner.startsWith('bare-')) { 68 | copyFiles.push('bare.js') 69 | } 70 | await Promise.all(copyFiles.map((file) => { 71 | return fs.promises.copyFile(path.join(__dirname, 'resources', file), path.join(this.outputDir, file)) 72 | })) 73 | 74 | const stats = await webpack(config) 75 | const info = stats.toJson() 76 | 77 | if (stats.hasErrors()) { 78 | console.error(info.errors) 79 | process.exit(1) 80 | } 81 | 82 | if (stats.hasWarnings()) { 83 | for (const warning of info.warnings) { 84 | console.warn('Bundling warning: ', warning) 85 | } 86 | } 87 | 88 | this.bundleFile = info.assetsByChunkName.main[0] 89 | 90 | if (this._options.stats) { 91 | this.statsFile = path.join(config.output.path, 'webpack-stats.json') 92 | await fs.promises.writeFile(this.statsFile, JSON.stringify(info), 'utf8') 93 | } 94 | } 95 | 96 | async run () { 97 | let errors 98 | for (const m of ['page', 'worker', 'serviceworker']) { 99 | if (this._mode[m]) { 100 | errors = await this._executeMode(m) 101 | if (errors) { 102 | break 103 | } 104 | } 105 | } 106 | return errors 107 | } 108 | 109 | async cleanup () { 110 | // TODO: let's be more careful .. only delete dir if it contains just the files we created 111 | return rimraf(this.outputDir) 112 | } 113 | 114 | _executeMode (mode) { 115 | const mount = st({ 116 | path: this.outputDir, 117 | index: 'index.html', 118 | url: '/', 119 | cache: false 120 | }) 121 | return new Promise((resolve, reject) => { 122 | const server = http.createServer((req, res) => { 123 | mount(req, res, () => { 124 | res.statusCode = 404 125 | res.end('Nope') 126 | }) 127 | }) 128 | server.on('error', reject) 129 | server.listen(() => { 130 | const port = server.address().port 131 | puppeteer(this.outputDir, port, this._options.timeout, mode, this._options.runner, this._options.coverage) 132 | .then((errors) => { 133 | server.close(() => { 134 | if (this._options.runner !== 'mocha') { 135 | console.log() // whitespace pls 136 | } 137 | resolve(errors) 138 | }) 139 | }) 140 | .catch(reject) 141 | }) 142 | }) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /resources/bare-run.js: -------------------------------------------------------------------------------- 1 | // in-browser test runner for bare-sync and bare-async, at end of bundle 2 | 3 | import { registry, executionQueue, log, setup } from './common-run.js' 4 | import { BareRunner } from './bare.js' 5 | 6 | setup().then(async () => { 7 | const runner = new BareRunner(log, registry.tests) 8 | const errors = await runner[registry.argv.runner === 'bare-sync' ? 'runBareSync' : 'runBareAsync']() 9 | executionQueue(() => globalThis.polendinaEnd(errors ? 1 : 0)) 10 | }) 11 | -------------------------------------------------------------------------------- /resources/bare.js: -------------------------------------------------------------------------------- 1 | export class BareRunner { 2 | constructor (log, tests) { 3 | this.log = log 4 | this.tests = tests 5 | } 6 | 7 | async runBare (exec) { 8 | for (const mod of this.tests) { 9 | try { 10 | await exec(mod) 11 | } catch (err) { 12 | await this.log.error(err.stack || String(err)) 13 | if (err.details) { 14 | await this.log.error(String(err.details)) 15 | } 16 | return true 17 | } 18 | } 19 | 20 | return false 21 | } 22 | 23 | async execBare (name, fn, indent) { 24 | try { 25 | await fn() 26 | await this.log.info(`${indent ? ' ' : ''} \u001b[32m✔\u001b[39m ${name}`) 27 | } catch (err) { 28 | this.log.info(`${indent ? ' ' : ''} \u001b[31m✘\u001b[39m ${name}`) 29 | throw err 30 | } 31 | } 32 | 33 | async runBareSync () { 34 | return this.runBare((mod) => this.execBare(mod.name, mod.load)) 35 | } 36 | 37 | async runBareAsync () { 38 | return this.runBare(async (mod) => { 39 | const exp = await mod.load() 40 | if (typeof exp === 'function') { 41 | return this.execBare(mod.name, exp) 42 | } 43 | const expn = Object.keys(exp) 44 | if (expn.length === 1 && expn[0] === 'default' && typeof exp.default === 'function') { 45 | return this.execBare(mod.name, exp.default) 46 | } 47 | await this.log.info(` ${mod.name}`) 48 | let found = false 49 | for (const name of expn) { 50 | const fn = exp[name] 51 | if (typeof fn === 'function') { 52 | found = true 53 | await this.execBare(name, fn, true) 54 | } 55 | } 56 | if (!found) { 57 | throw new Error(`Did not find any async test function exports in ${mod.name}`) 58 | } 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /resources/common-run.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope clients */ 2 | 3 | import { registry } from './test-registry.cjs' 4 | 5 | const inPage = 6 | typeof window !== 'undefined' && 7 | window.location && 8 | typeof WorkerGlobalScope === 'undefined' 9 | const inServiceWorker = !inPage && 10 | typeof ServiceWorkerGlobalScope !== 'undefined' && 11 | globalThis instanceof ServiceWorkerGlobalScope 12 | const inWorker = !inPage && !inServiceWorker && 13 | typeof WorkerGlobalScope !== 'undefined' && 14 | globalThis instanceof WorkerGlobalScope 15 | let _executionQueue = Promise.resolve() 16 | let logSeq = 0 17 | 18 | export { registry } 19 | export function executionQueue (fn) { 20 | _executionQueue = _executionQueue.then(fn) 21 | return _executionQueue 22 | } 23 | 24 | export const log = { 25 | info: (...args) => { 26 | executionQueue(() => globalThis.polendinaLog(['info', logSeq++].concat(args))) 27 | return executionQueue 28 | }, 29 | // TODO 30 | warn: (...args) => { 31 | executionQueue(() => globalThis.polendinaLog(['warn', logSeq++].concat(args))) 32 | return executionQueue 33 | }, 34 | // TODO 35 | error: (...args) => { 36 | executionQueue(() => globalThis.polendinaLog(['error', logSeq++].concat(args))) 37 | return executionQueue 38 | } 39 | } 40 | 41 | function setupWorkerGlobals () { 42 | globalThis.polendinaLog = async function (...args) { 43 | globalThis.postMessage(['polendinaLog'].concat(args)) 44 | } 45 | 46 | globalThis.polendinaWrite = async function (...args) { 47 | globalThis.postMessage(['polendinaWrite'].concat(args)) 48 | } 49 | 50 | globalThis.polendinaEnd = async function (...args) { 51 | globalThis.postMessage(['polendinaEnd'].concat(args)) 52 | } 53 | } 54 | 55 | function setupServiceWorkerGlobals () { 56 | async function _postMessage (msg) { 57 | for (const client of await clients.matchAll()) { 58 | client.postMessage(msg) 59 | } 60 | } 61 | 62 | globalThis.polendinaLog = async function (...args) { 63 | _postMessage(['polendinaLog'].concat(args)) 64 | } 65 | 66 | globalThis.polendinaWrite = async function (...args) { 67 | _postMessage(['polendinaWrite'].concat(args)) 68 | } 69 | 70 | globalThis.polendinaEnd = async function (...args) { 71 | _postMessage(['polendinaEnd'].concat(args)) 72 | } 73 | } 74 | 75 | function setupLogging () { 76 | console.log = function (...args) { 77 | try { 78 | if (/BrowserStdout.*write/.test(new Error().stack)) { 79 | // the BrowserStdout polyfill (Mocha ships with among others) that converts 80 | // process.stdout.write() to console.log() 81 | // so we strip out the extra \n that necessarily inserts 82 | // args[0] = args[0].replace(/\n$/, '') 83 | executionQueue(() => globalThis.polendinaWrite(args)) 84 | return 85 | } 86 | } catch (err) {} 87 | log.info.apply(null, args) 88 | } 89 | 90 | // TODO: differentiate 91 | console.warn = log.warn 92 | console.error = log.error 93 | } 94 | 95 | export async function setup () { 96 | if (inWorker) { 97 | setupWorkerGlobals() 98 | } else if (inServiceWorker) { 99 | await new Promise((resolve, reject) => { 100 | globalThis.addEventListener('activate', (event) => { 101 | event.waitUntil(globalThis.clients.claim()) 102 | setupServiceWorkerGlobals() 103 | resolve() 104 | }) 105 | }) 106 | } 107 | 108 | setupLogging() 109 | } 110 | -------------------------------------------------------------------------------- /resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/mocha-run.js: -------------------------------------------------------------------------------- 1 | // in-browser setup and runner for Mocha, at end of bundle 2 | 3 | import mochaExport from 'mocha/mocha.js' 4 | import { registry, executionQueue, log, setup } from './common-run.js' 5 | 6 | async function runMocha () { 7 | // mocha@8 exports what we want, mocha@7 sets a global 8 | const mochaLocal = mochaExport 9 | mochaLocal.setup({ reporter: registry.argv.mochaReporter, ui: 'bdd' }) 10 | // mocha@7 deprecated useColors() 11 | if (typeof mochaLocal.color === 'function') { 12 | mochaLocal.color(true) 13 | } else if (typeof mochaLocal.useColors === 'function') { 14 | mochaLocal.useColors(true) 15 | } 16 | 17 | // the well-behaved reporters, like spec, are easy to intercept 18 | mochaLocal.constructor.reporters.Base.consoleLog = log.info 19 | 20 | for (const mod of registry.tests) { 21 | await mod.load() 22 | } 23 | 24 | let errors = 0 25 | mochaLocal 26 | .run((_errors) => { errors = _errors }) 27 | .on('end', (...args) => { 28 | executionQueue(() => globalThis.polendinaEnd(errors)) 29 | }) 30 | } 31 | 32 | setup().then(runMocha) 33 | -------------------------------------------------------------------------------- /resources/page-run.js: -------------------------------------------------------------------------------- 1 | /* globals self Worker */ 2 | 3 | // loaded as script file in index.html, determines how to load the bundle 4 | 5 | const isWorkerMode = /mode=worker/.test(window.location.search) 6 | const isServiceWorkerMode = /mode=serviceworker/.test(window.location.search) 7 | const _consoleLog = console ? console.log : () => {} 8 | 9 | function runPage () { 10 | const script = document.createElement('script') 11 | script.type = 'text/javascript' 12 | script.src = 'bundle.js' 13 | document.head.appendChild(script) 14 | } 15 | 16 | function onWorkerMessage (msg) { 17 | if (!Array.isArray(msg.data)) { 18 | return 19 | } 20 | 21 | if (!self[msg.data[0]]) { 22 | _consoleLog.apply(console, msg.data) 23 | } else { 24 | self[msg.data[0]].apply(null, msg.data.slice(1)) 25 | } 26 | } 27 | 28 | function runWorker () { 29 | const worker = new Worker('bundle.js') 30 | worker.addEventListener('message', onWorkerMessage, false) 31 | } 32 | 33 | function runServiceWorker () { 34 | navigator.serviceWorker.register('bundle.js', { scope: './' }) 35 | navigator.serviceWorker.addEventListener('message', onWorkerMessage, false) 36 | } 37 | 38 | window.addEventListener('load', () => { 39 | if (isWorkerMode) { 40 | runWorker() 41 | } else if (isServiceWorkerMode) { 42 | runServiceWorker() 43 | } else { 44 | runPage() 45 | } 46 | }) 47 | -------------------------------------------------------------------------------- /resources/tape-run.js: -------------------------------------------------------------------------------- 1 | // in-browser setup and runner for Tape, at end of bundle 2 | 3 | import tape from 'tape' 4 | import { registry, setup, executionQueue } from './common-run.js' 5 | import { Transform } from 'stream' 6 | 7 | async function runTape () { 8 | let failures = 0 9 | 10 | const stream = new Transform({ 11 | transform (chunk, encoding, callback) { 12 | executionQueue(() => { 13 | globalThis.polendinaWrite(chunk.toString()) 14 | .catch(callback) 15 | .then(callback) 16 | }) 17 | }, 18 | 19 | flush (callback) { 20 | executionQueue(() => { 21 | globalThis.polendinaEnd(failures) 22 | .catch(callback) 23 | .then(callback) 24 | }) 25 | } 26 | }) 27 | 28 | tape.getHarness({ stream }) 29 | 30 | tape.getHarness().onFailure((...args) => { 31 | failures = 1 32 | }) 33 | 34 | for (const mod of registry.tests) { 35 | await mod.load() 36 | } 37 | } 38 | 39 | setup().then(runTape) 40 | -------------------------------------------------------------------------------- /resources/test-registry.cjs: -------------------------------------------------------------------------------- 1 | // see wrap-loader.cjs 2 | 3 | module.exports.registry = { 4 | argv: null, 5 | tests: [] 6 | } 7 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import assert from 'assert' 3 | import { execFile } from 'child_process' 4 | import { fileURLToPath } from 'url' 5 | 6 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 7 | const cli = path.join(__dirname, '../polendina-cli.js') 8 | 9 | export async function runCli (cwd, args) { 10 | return runCommand(`${cli} test*.js test/test*.js --cleanup ${args || ''}`, cwd) 11 | } 12 | 13 | export async function runCommand (command, cwd) { 14 | return new Promise((resolve, reject) => { 15 | const args = command.split(' ').filter(Boolean) 16 | execFile(process.execPath, args, { cwd }, (err, stdout, stderr) => { 17 | const code = err ? err.code : 0 18 | if (!code) { 19 | try { 20 | assert.strictEqual(stderr.toString(), '', 'no stderr') 21 | } catch (e) { 22 | return reject(e) 23 | } 24 | } 25 | const out = { stdout: stdout.toString(), stderr: stderr.toString(), code } 26 | resolve(out) 27 | }) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-esm/index.js: -------------------------------------------------------------------------------- 1 | export default function test () { 2 | return new Promise((resolve, reject) => setTimeout(() => resolve('bare test fixture'), 500)) 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-async", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node test-1.js && node test-2.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "license": "Apache-2.0", 16 | "devDependencies": { 17 | "chai": "^5.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-esm/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import { assert } from 'chai' 4 | 5 | export default async function () { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | console.log('testing is in serviceworker') 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | console.log('testing is in worker') 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | } else { 13 | console.log('testing is not in worker') 14 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-esm/test-2.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from './index.js' 3 | 4 | export async function test1 () { 5 | console.log('testing bare fixture') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | export async function test2 () { 10 | assert.ok(true, 'yep') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-esm/test/test-3.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from '../index.js' 3 | 4 | export async function test1 () { 5 | console.log('testing bare fixture subdir') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | export async function test2 () { 10 | assert.ok(true, 'yep') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure-esm/index.js: -------------------------------------------------------------------------------- 1 | export default function test () { 2 | return new Promise((resolve, reject) => setTimeout(() => resolve('bare test fixture'), 500)) 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-async-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node test-1.js && node test-2.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "license": "Apache-2.0", 16 | "devDependencies": { 17 | "chai": "^5.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure-esm/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import { assert } from 'chai' 4 | 5 | export default async function () { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | console.log('testing is in serviceworker') 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | console.log('testing is in worker') 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | } else { 13 | console.log('testing is not in worker') 14 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure-esm/test-2.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from './index.js' 3 | 4 | export async function test1 () { 5 | console.log('testing bare fixture') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | export async function test2 () { 10 | assert.fail('faily mcfailface') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure/index.js: -------------------------------------------------------------------------------- 1 | module.exports = () => new Promise((resolve, reject) => setTimeout(() => resolve('bare test fixture'), 500)) 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-async-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test-1.js && node test-2.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rvagg/polendina.git" 13 | }, 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "chai": "^4.3.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const { assert } = require('chai') 4 | 5 | module.exports = async function () { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | console.log('testing is in serviceworker') 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | console.log('testing is in worker') 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | } else { 13 | console.log('testing is not in worker') 14 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/bare-async-failure/test-2.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('./') 3 | 4 | module.exports.test1 = async function () { 5 | console.log('testing bare fixture') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | module.exports.test2 = async function () { 10 | assert.fail('faily mcfailface') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-async/index.js: -------------------------------------------------------------------------------- 1 | module.exports = () => new Promise((resolve, reject) => setTimeout(() => resolve('bare test fixture'), 500)) 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-async", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test-1.js && node test-2.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rvagg/polendina.git" 13 | }, 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "chai": "^4.3.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/bare-async/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const { assert } = require('chai') 4 | 5 | module.exports = async function () { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | console.log('testing is in serviceworker') 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | console.log('testing is in worker') 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | } else { 13 | console.log('testing is not in worker') 14 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/bare-async/test-2.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('./') 3 | 4 | module.exports.test1 = async function () { 5 | console.log('testing bare fixture') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | module.exports.test2 = async function () { 10 | assert.ok(true, 'yep') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-async/test/test-3.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('../') 3 | 4 | module.exports.test1 = async function () { 5 | console.log('testing bare fixture subdir') 6 | assert.strictEqual(await index(), 'bare test fixture') 7 | } 8 | 9 | module.exports.test2 = async function () { 10 | assert.ok(true, 'yep') 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-esm/index.js: -------------------------------------------------------------------------------- 1 | export default 'bare test fixture' 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-sync", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node test-1.js && node test-2.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "license": "Apache-2.0", 16 | "devDependencies": { 17 | "chai": "^5.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-esm/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import { assert } from 'chai' 4 | 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | console.log('testing is in serviceworker') 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 9 | console.log('testing is in worker') 10 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 11 | } else { 12 | console.log('testing is not in worker') 13 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-esm/test-2.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from './index.js' 3 | 4 | console.log('testing bare fixture') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.ok(true, 'yep') 7 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-esm/test/test-3.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from '../index.js' 3 | 4 | console.log('testing bare fixture subdir') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.ok(true, 'yep') 7 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure-esm/index.js: -------------------------------------------------------------------------------- 1 | export default 'bare test fixture' 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-sync-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "node test-1.js && node test-2.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "license": "Apache-2.0", 16 | "devDependencies": { 17 | "chai": "^5.1.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure-esm/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import { assert } from 'chai' 4 | 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | console.log('testing is in serviceworker') 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 9 | console.log('testing is in worker') 10 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 11 | } else { 12 | console.log('testing is not in worker') 13 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure-esm/test-2.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import index from './index.js' 3 | 4 | console.log('testing bare fixture') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.fail('faily mcfailface') 7 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'bare test fixture' 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-sync-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test-1.js && node test-2.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rvagg/polendina.git" 13 | }, 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "chai": "^4.3.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const { assert } = require('chai') 4 | 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | console.log('testing is in serviceworker') 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 9 | console.log('testing is in worker') 10 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 11 | } else { 12 | console.log('testing is not in worker') 13 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync-failure/test-2.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('.') 3 | 4 | console.log('testing bare fixture') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.fail('faily mcfailface') 7 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'bare test fixture' 2 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-bare-sync", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test-1.js && node test-2.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rvagg/polendina.git" 13 | }, 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "chai": "^4.3.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const { assert } = require('chai') 4 | 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | console.log('testing is in serviceworker') 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 9 | console.log('testing is in worker') 10 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 11 | } else { 12 | console.log('testing is not in worker') 13 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync/test-2.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('./') 3 | 4 | console.log('testing bare fixture') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.ok(true, 'yep') 7 | -------------------------------------------------------------------------------- /test/fixtures/bare-sync/test/test-3.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | const index = require('../') 3 | 4 | console.log('testing bare fixture subdir') 5 | assert.strictEqual(index, 'bare test fixture') 6 | assert.ok(true, 'yep') 7 | -------------------------------------------------------------------------------- /test/fixtures/mocha-esm/index.js: -------------------------------------------------------------------------------- 1 | export default 'polendina test' 2 | -------------------------------------------------------------------------------- /test/fixtures/mocha-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-mocha", 3 | "version": "1.0.0", 4 | "description": "test-fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "mocha test-*.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "license": "Apache-2.0", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rvagg/polendina.git" 15 | }, 16 | "devDependencies": { 17 | "chai": "^5.1.1", 18 | "mocha": "^11.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/mocha-esm/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import fixture from './index.js' 4 | import { assert } from 'chai' 5 | 6 | describe('test suite 1', () => { 7 | it('test case 1', () => { 8 | assert.strictEqual(fixture, 'polendina test') 9 | }) 10 | 11 | it('test case 2', () => { 12 | assert.ok(true, 'all good') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/mocha-esm/test-2.js: -------------------------------------------------------------------------------- 1 | /* globals describe it WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import { assert } from 'chai' 4 | 5 | describe('test suite 2 - worker', () => { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | it('is in serviceworker', () => { 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | }) 10 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 11 | it('is in worker', () => { 12 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 13 | }) 14 | } else { 15 | it('is not in worker', () => { 16 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 17 | }) 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /test/fixtures/mocha-esm/test-3.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import fixture from './index.js' 4 | import { assert } from 'chai' 5 | 6 | describe('test suite 3', () => { 7 | it('test case 1', () => { 8 | assert.strictEqual(fixture, 'polendina test') 9 | }) 10 | 11 | it('test case 2', () => { 12 | assert.ok(true, 'all good') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/mocha-failure-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-mocha-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "mocha test-*.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "license": "Apache-2.0", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rvagg/polendina.git" 15 | }, 16 | "devDependencies": { 17 | "chai": "^5.1.1", 18 | "mocha": "^11.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/mocha-failure-esm/test.js: -------------------------------------------------------------------------------- 1 | /* globals describe it WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | import { assert } from 'chai' 3 | 4 | describe('test suite 1 - worker', () => { 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | it('is in serviceworker', () => { 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | }) 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | it('is in worker', () => { 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | }) 13 | } else { 14 | it('is not in worker', () => { 15 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 16 | }) 17 | } 18 | }) 19 | 20 | describe('test suite 2 - failing', () => { 21 | it('should fail', () => { 22 | throw new Error('failing test') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/fixtures/mocha-failure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-mocha-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test-*.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "license": "Apache-2.0", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "devDependencies": { 16 | "chai": "^5.1.1", 17 | "mocha": "^11.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/mocha-failure/test.js: -------------------------------------------------------------------------------- 1 | /* globals describe it WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | const { assert } = require('chai') 3 | 4 | describe('test suite 1 - worker', () => { 5 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 6 | it('is in serviceworker', () => { 7 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 8 | }) 9 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 10 | it('is in worker', () => { 11 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 12 | }) 13 | } else { 14 | it('is not in worker', () => { 15 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 16 | }) 17 | } 18 | }) 19 | 20 | describe('test suite 2 - failing', () => { 21 | it('should fail', () => { 22 | throw new Error('failing test') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/fixtures/mocha/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'polendina test' 2 | -------------------------------------------------------------------------------- /test/fixtures/mocha/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-mocha", 3 | "version": "1.0.0", 4 | "description": "test-fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test-*.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "license": "Apache-2.0", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "devDependencies": { 16 | "chai": "^5.1.1", 17 | "mocha": "^11.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/mocha/test-1.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | const fixture = require('./') 4 | const { assert } = require('chai') 5 | 6 | describe('test suite 1', () => { 7 | it('test case 1', () => { 8 | assert.strictEqual(fixture, 'polendina test') 9 | }) 10 | 11 | it('test case 2', () => { 12 | assert.ok(true, 'all good') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/mocha/test-2.js: -------------------------------------------------------------------------------- 1 | /* globals describe it WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const { assert } = require('chai') 4 | 5 | describe('test suite 2 - worker', () => { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | it('is in serviceworker', () => { 8 | assert.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | }) 10 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 11 | it('is in worker', () => { 12 | assert.strictEqual(typeof WorkerGlobalScope, 'function') 13 | }) 14 | } else { 15 | it('is not in worker', () => { 16 | assert.strictEqual(typeof WorkerGlobalScope, 'undefined') 17 | }) 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /test/fixtures/mocha/test-3.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | const fixture = require('./') 4 | const { assert } = require('chai') 5 | 6 | describe('test suite 3', () => { 7 | it('test case 1', () => { 8 | assert.strictEqual(fixture, 'polendina test') 9 | }) 10 | 11 | it('test case 2', () => { 12 | assert.ok(true, 'all good') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/tape-esm/index.js: -------------------------------------------------------------------------------- 1 | export default 'polendina test' 2 | -------------------------------------------------------------------------------- /test/fixtures/tape-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-tape", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "tape test.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "license": "Apache-2.0", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rvagg/polendina.git" 15 | }, 16 | "devDependencies": { 17 | "tape": "^5.6.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/tape-esm/test.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import tape from 'tape' 4 | import fixture from './index.js' 5 | 6 | tape('test suite 1', (t) => { 7 | t.test('test case 1', (t) => { 8 | t.strictEqual(fixture, 'polendina test') 9 | t.end() 10 | }) 11 | 12 | t.test('test case 2', (t) => { 13 | t.ok(true, 'all good') 14 | t.end() 15 | }) 16 | }) 17 | 18 | tape('test suite 2 - worker', (t) => { 19 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 20 | t.test('is in serviceworker', (t) => { 21 | t.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 22 | t.end() 23 | }) 24 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 25 | t.test('is in worker', (t) => { 26 | t.strictEqual(typeof WorkerGlobalScope, 'function') 27 | t.end() 28 | }) 29 | } else { 30 | t.test('is not in worker', (t) => { 31 | t.strictEqual(typeof WorkerGlobalScope, 'undefined') 32 | t.end() 33 | }) 34 | } 35 | }) 36 | 37 | tape('test suite 3', (t) => { 38 | t.test('test case 1', (t) => { 39 | t.strictEqual(fixture, 'polendina test') 40 | t.end() 41 | }) 42 | 43 | t.test('test case 2', (t) => { 44 | t.ok(true, 'all good') 45 | t.end() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /test/fixtures/tape-failure-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-tape-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "tape test.js" 9 | }, 10 | "author": "Rod (http://r.va.gg/)", 11 | "license": "Apache-2.0", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rvagg/polendina.git" 15 | }, 16 | "devDependencies": { 17 | "tape": "^5.6.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/tape-failure-esm/test.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | import tape from 'tape' 4 | 5 | tape('test suite 1 - worker', (t) => { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | t.test('is in serviceworker', (t) => { 8 | t.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | t.end() 10 | }) 11 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 12 | t.test('is in worker', (t) => { 13 | t.strictEqual(typeof WorkerGlobalScope, 'function') 14 | t.end() 15 | }) 16 | } else { 17 | t.test('is not in worker', (t) => { 18 | t.strictEqual(typeof WorkerGlobalScope, 'undefined') 19 | t.end() 20 | }) 21 | } 22 | }) 23 | 24 | tape('test suite 2 - failure', (t) => { 25 | t.test('test case 1', (t) => { 26 | t.fail('bork') 27 | t.end() 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/fixtures/tape-failure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-tape-failure", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape test.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "license": "Apache-2.0", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "devDependencies": { 16 | "tape": "^5.6.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/tape-failure/test.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const tape = require('tape') 4 | 5 | tape('test suite 1 - worker', (t) => { 6 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 7 | t.test('is in serviceworker', (t) => { 8 | t.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 9 | t.end() 10 | }) 11 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 12 | t.test('is in worker', (t) => { 13 | t.strictEqual(typeof WorkerGlobalScope, 'function') 14 | t.end() 15 | }) 16 | } else { 17 | t.test('is not in worker', (t) => { 18 | t.strictEqual(typeof WorkerGlobalScope, 'undefined') 19 | t.end() 20 | }) 21 | } 22 | }) 23 | 24 | tape('test suite 2 - failure', (t) => { 25 | t.test('test case 1', (t) => { 26 | t.fail('bork') 27 | t.end() 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/fixtures/tape/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'polendina test' 2 | -------------------------------------------------------------------------------- /test/fixtures/tape/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-tape", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape test.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "license": "Apache-2.0", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/rvagg/polendina.git" 14 | }, 15 | "devDependencies": { 16 | "tape": "^5.6.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/tape/test.js: -------------------------------------------------------------------------------- 1 | /* globals WorkerGlobalScope ServiceWorkerGlobalScope */ 2 | 3 | const tape = require('tape') 4 | const fixture = require('./') 5 | 6 | tape('test suite 1', (t) => { 7 | t.test('test case 1', (t) => { 8 | t.strictEqual(fixture, 'polendina test') 9 | t.end() 10 | }) 11 | 12 | t.test('test case 2', (t) => { 13 | t.ok(true, 'all good') 14 | t.end() 15 | }) 16 | }) 17 | 18 | tape('test suite 2 - worker', (t) => { 19 | if (typeof ServiceWorkerGlobalScope !== 'undefined' && global instanceof ServiceWorkerGlobalScope) { 20 | t.test('is in serviceworker', (t) => { 21 | t.strictEqual(typeof ServiceWorkerGlobalScope, 'function') 22 | t.end() 23 | }) 24 | } else if (typeof WorkerGlobalScope !== 'undefined' && global instanceof WorkerGlobalScope) { 25 | t.test('is in worker', (t) => { 26 | t.strictEqual(typeof WorkerGlobalScope, 'function') 27 | t.end() 28 | }) 29 | } else { 30 | t.test('is not in worker', (t) => { 31 | t.strictEqual(typeof WorkerGlobalScope, 'undefined') 32 | t.end() 33 | }) 34 | } 35 | }) 36 | 37 | tape('test suite 3', (t) => { 38 | t.test('test case 1', (t) => { 39 | t.strictEqual(fixture, 'polendina test') 40 | t.end() 41 | }) 42 | 43 | t.test('test case 2', (t) => { 44 | t.ok(true, 'all good') 45 | t.end() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /test/fixtures/webpack-merge/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'bare test fixture' 2 | -------------------------------------------------------------------------------- /test/fixtures/webpack-merge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixture-webpack-merge", 3 | "version": "1.0.0", 4 | "description": "test fixture", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test.js" 8 | }, 9 | "author": "Rod (http://r.va.gg/)", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rvagg/polendina.git" 13 | }, 14 | "license": "Apache-2.0", 15 | "devDependencies": { 16 | "chai": "^5.1.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/webpack-merge/test.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai') 2 | 3 | assert.strictEqual(typeof assert.ok, 'function') 4 | console.log('assert.ok() is a function') 5 | assert.strictEqual(WOOP, 'woop') // eslint-disable-line 6 | console.log('WOOP is set') 7 | -------------------------------------------------------------------------------- /test/fixtures/webpack-merge/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | module.exports = { 4 | plugins: [new webpack.DefinePlugin({ WOOP: '"woop"' })] 5 | } 6 | -------------------------------------------------------------------------------- /test/test-bare-async.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCli } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | 10 | for (const type of ['cjs', 'esm']) { 11 | const bareAsyncFixture = path.join(__dirname, `fixtures/bare-async${type === 'esm' ? '-esm' : ''}`) 12 | const bareAsyncFailureFixture = path.join(__dirname, `fixtures/bare-async-failure${type === 'esm' ? '-esm' : ''}`) 13 | 14 | describe(`basic bare-async (${type})`, function () { 15 | this.timeout(60000) 16 | const expectedTemplate = ` 17 | testing is WORKER 18 | ✔ test-1.js 19 | test-2.js 20 | testing bare fixture 21 | ✔ test1 22 | ✔ test2 23 | test/test-3.js 24 | testing bare fixture subdir 25 | ✔ test1 26 | ✔ test2 27 | ` 28 | 29 | it('should run in page', async () => { 30 | const { stdout, stderr, code } = await runCli(bareAsyncFixture, '--runner=bare-async') 31 | if (code !== 0) { 32 | console.error(stderr) 33 | } 34 | assert.strictEqual(code, 0, 'exited with zero exit code') 35 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 36 | if (!stdout.includes(expected)) { 37 | console.error(stdout) 38 | } 39 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 40 | assert.ok(stdout.includes('Running bare-async page tests with Puppeteer'), 'stdout contains expected output for running in page') 41 | }) 42 | 43 | it('should run in worker', async () => { 44 | const { stdout, stderr, code } = await runCli(bareAsyncFixture, '--runner=bare-async --worker --page=false') 45 | if (code !== 0) { 46 | console.error(stderr) 47 | } 48 | assert.strictEqual(code, 0, 'exited with zero exit code') 49 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 50 | if (!stdout.includes(expected)) { 51 | console.error(stdout) 52 | } 53 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 54 | assert.ok(stdout.includes('Running bare-async worker tests with Puppeteer'), 'stdout contains expected output for running in page') 55 | }) 56 | 57 | it('should run in serviceworker', async () => { 58 | const { stdout, stderr, code } = await runCli(bareAsyncFixture, '--runner=bare-async --serviceworker --page=false') 59 | if (code !== 0) { 60 | console.error(stderr) 61 | } 62 | assert.strictEqual(code, 0, 'exited with zero exit code') 63 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 64 | if (!stdout.includes(expected)) { 65 | console.error(stdout) 66 | } 67 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 68 | assert.ok(stdout.includes('Running bare-async serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 69 | }) 70 | 71 | it('should run in page, worker and serviceworker', async () => { 72 | const { stdout, stderr, code } = await runCli(bareAsyncFixture, '--runner=bare-async --worker --serviceworker') 73 | if (code !== 0) { 74 | console.error(stderr) 75 | } 76 | assert.strictEqual(code, 0, 'exited with zero exit code') 77 | 78 | let expected = expectedTemplate.replace(/WORKER/, 'not in worker') 79 | if (!stdout.includes(expected)) { 80 | console.error(stdout) 81 | } 82 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 83 | assert.ok(stdout.includes('Running bare-async serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 84 | 85 | expected = expectedTemplate.replace(/WORKER/, 'in worker') 86 | if (!stdout.includes(expected)) { 87 | console.error(stdout) 88 | } 89 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 90 | assert.ok(stdout.includes('Running bare-async serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 91 | 92 | expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 93 | if (!stdout.includes(expected)) { 94 | console.error(stdout) 95 | } 96 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 97 | assert.ok(stdout.includes('Running bare-async serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 98 | }) 99 | }) 100 | 101 | describe(`failing bare-async (${type})`, function () { 102 | this.timeout(60000) 103 | const expectedTemplate = ` 104 | testing is WORKER 105 | ✔ test-1.js 106 | test-2.js 107 | testing bare fixture 108 | ✔ test1 109 | ✘ test2 110 | ` 111 | const expectedStderr = 'AssertionError: faily mcfailface' 112 | 113 | it('should fail in page', async () => { 114 | let { stdout, stderr, code } = await runCli(bareAsyncFailureFixture, '--runner=bare-async') 115 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 116 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 117 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 118 | if (!stdout.includes(expected)) { 119 | console.error(stdout) 120 | } 121 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 122 | assert.ok(stdout.includes('Running bare-async page tests with Puppeteer'), 'stdout contains expected output for running in worker') 123 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 124 | }) 125 | 126 | it('should fail in worker', async () => { 127 | let { stdout, stderr, code } = await runCli(bareAsyncFailureFixture, '--runner=bare-async --worker --page=false') 128 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 129 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 130 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 131 | if (!stdout.includes(expected)) { 132 | console.error(stdout) 133 | } 134 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 135 | assert.ok(stdout.includes('Running bare-async worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 136 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 137 | }) 138 | 139 | it('should fail in serviceworker', async () => { 140 | let { stdout, stderr, code } = await runCli(bareAsyncFailureFixture, '--runner=bare-async --serviceworker --page=false') 141 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 142 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 143 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 144 | if (!stdout.includes(expected)) { 145 | console.error(stdout) 146 | } 147 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 148 | assert.ok(stdout.includes('Running bare-async serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 149 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 150 | }) 151 | 152 | it('should fail in page and not run in worker', async () => { 153 | let { stdout, stderr, code } = await runCli(bareAsyncFailureFixture, '--runner=bare-async --worker') 154 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 155 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 156 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 157 | let found = stdout.indexOf(expected) 158 | assert.ok(found > -1, 'stdout contains expected test output') 159 | found = stdout.indexOf(expected, found + 1) 160 | assert.ok(found === -1, 'stdout doesn\'t contain second instance of expected test output') 161 | assert.ok(stdout.includes('Running bare-async page tests with Puppeteer'), 'stdout contains expected output for running in page') 162 | assert.ok(!stdout.includes('Running bare-async worker tests with Puppeteer'), 'stdout doesn\'t contain expected output for running in worker') 163 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 164 | }) 165 | }) 166 | } 167 | -------------------------------------------------------------------------------- /test/test-bare-sync.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCli } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | 10 | for (const type of ['cjs', 'esm']) { 11 | const bareSyncFixture = path.join(__dirname, `fixtures/bare-sync${type === 'esm' ? '-esm' : ''}`) 12 | const bareSyncFailureFixture = path.join(__dirname, `fixtures/bare-sync-failure${type === 'esm' ? '-esm' : ''}`) 13 | 14 | describe(`basic bare-sync (${type})`, function () { 15 | this.timeout(60000) 16 | const expectedTemplate = ` 17 | testing is WORKER 18 | ✔ test-1.js 19 | testing bare fixture 20 | ✔ test-2.js 21 | testing bare fixture subdir 22 | ✔ test/test-3.js 23 | ` 24 | 25 | it('should run in page', async () => { 26 | const { stdout, stderr, code } = await runCli(bareSyncFixture, '--runner=bare-sync') 27 | if (code !== 0) { 28 | console.error(stderr) 29 | } 30 | assert.strictEqual(code, 0, 'exited with zero exit code') 31 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 32 | if (!stdout.includes(expected)) { 33 | console.error(stdout) 34 | } 35 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 36 | assert.ok(stdout.includes('Running bare-sync page tests with Puppeteer'), 'stdout contains expected output for running in page') 37 | }) 38 | 39 | it('should run in worker', async () => { 40 | const { stdout, stderr, code } = await runCli(bareSyncFixture, '--runner=bare-sync --worker --page=false') 41 | if (code !== 0) { 42 | console.error(stderr) 43 | } 44 | assert.strictEqual(code, 0, 'exited with zero exit code') 45 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 46 | if (!stdout.includes(expected)) { 47 | console.error(stdout) 48 | } 49 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 50 | assert.ok(stdout.includes('Running bare-sync worker tests with Puppeteer'), 'stdout contains expected output for running in page') 51 | }) 52 | 53 | it('should run in serviceworker', async () => { 54 | const { stdout, stderr, code } = await runCli(bareSyncFixture, '--runner=bare-sync --serviceworker --page=false') 55 | if (code !== 0) { 56 | console.error(stderr) 57 | } 58 | assert.strictEqual(code, 0, 'exited with zero exit code') 59 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 60 | if (!stdout.includes(expected)) { 61 | console.error(stdout) 62 | } 63 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 64 | assert.ok(stdout.includes('Running bare-sync serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 65 | }) 66 | 67 | it('should run in page, worker and serviceworker', async () => { 68 | const { stdout, stderr, code } = await runCli(bareSyncFixture, '--runner=bare-sync --worker --serviceworker') 69 | if (code !== 0) { 70 | console.error(stderr) 71 | } 72 | assert.strictEqual(code, 0, 'exited with zero exit code') 73 | 74 | let expected = expectedTemplate.replace(/WORKER/, 'not in worker') 75 | if (!stdout.includes(expected)) { 76 | console.error(stdout) 77 | } 78 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 79 | assert.ok(stdout.includes('Running bare-sync serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 80 | 81 | expected = expectedTemplate.replace(/WORKER/, 'in worker') 82 | if (!stdout.includes(expected)) { 83 | console.error(stdout) 84 | } 85 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 86 | assert.ok(stdout.includes('Running bare-sync serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 87 | 88 | expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 89 | if (!stdout.includes(expected)) { 90 | console.error(stdout) 91 | } 92 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 93 | assert.ok(stdout.includes('Running bare-sync serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 94 | }) 95 | }) 96 | 97 | describe(`failing bare-sync (${type})`, function () { 98 | this.timeout(60000) 99 | const expectedTemplate = ` 100 | testing is WORKER 101 | ✔ test-1.js 102 | testing bare fixture 103 | ✘ test-2.js 104 | ` 105 | const expectedStderr = 'AssertionError: faily mcfailface' 106 | 107 | it('should fail in page', async () => { 108 | let { stdout, stderr, code } = await runCli(bareSyncFailureFixture, '--runner=bare-sync') 109 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 110 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 111 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 112 | if (!stdout.includes(expected)) { 113 | console.error(stdout) 114 | } 115 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 116 | assert.ok(stdout.includes('Running bare-sync page tests with Puppeteer'), 'stdout contains expected output for running in worker') 117 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 118 | }) 119 | 120 | it('should fail in worker', async () => { 121 | let { stdout, stderr, code } = await runCli(bareSyncFailureFixture, '--runner=bare-sync --worker --page=false') 122 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 123 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 124 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 125 | if (!stdout.includes(expected)) { 126 | console.error(stdout) 127 | } 128 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 129 | assert.ok(stdout.includes('Running bare-sync worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 130 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 131 | }) 132 | 133 | it('should fail in serviceworker', async () => { 134 | let { stdout, stderr, code } = await runCli(bareSyncFailureFixture, '--runner=bare-sync --serviceworker --page=false') 135 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 136 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 137 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 138 | if (!stdout.includes(expected)) { 139 | console.error(stdout) 140 | } 141 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 142 | assert.ok(stdout.includes('Running bare-sync serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 143 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 144 | }) 145 | 146 | it('should fail in page and not run in worker', async () => { 147 | let { stdout, stderr, code } = await runCli(bareSyncFailureFixture, '--runner=bare-sync --worker') 148 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 149 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 150 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 151 | let found = stdout.indexOf(expected) 152 | if (found === -1) { 153 | console.error(stdout) 154 | } 155 | assert.ok(found > -1, 'stdout contains expected test output') 156 | found = stdout.indexOf(expected, found + 1) 157 | assert.ok(found === -1, 'stdout doesn\'t contain second instance of expected test output') 158 | assert.ok(stdout.includes('Running bare-sync page tests with Puppeteer'), 'stdout contains expected output for running in page') 159 | assert.ok(!stdout.includes('Running bare-sync worker tests with Puppeteer'), 'stdout doesn\'t contain expected output for running in worker') 160 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected output') 161 | }) 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /test/test-mocha.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCli } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | 10 | function removeTimings (stdout) { 11 | return stdout.replace(/\(\d+ms\)/g, '(Xms)') 12 | } 13 | 14 | for (const type of ['cjs', 'esm']) { 15 | const mochaFixture = path.join(__dirname, `fixtures/mocha${type === 'esm' ? '-esm' : ''}`) 16 | const mochaFailureFixture = path.join(__dirname, `fixtures/mocha-failure${type === 'esm' ? '-esm' : ''}`) 17 | 18 | describe(`basic mocha (${type})`, function () { 19 | this.timeout(60000) 20 | const expectedTemplate = ` 21 | test suite 1 22 | ✅ test case 1 23 | ✅ test case 2 24 | 25 | test suite 2 - worker 26 | ✅ is WORKER 27 | 28 | test suite 3 29 | ✅ test case 1 30 | ✅ test case 2 31 | ` 32 | 33 | it('should run in page', async () => { 34 | const { stdout, stderr, code } = await runCli(mochaFixture) 35 | if (code !== 0) { 36 | console.error(stderr) 37 | } 38 | assert.strictEqual(code, 0, 'exited with zero exit code') 39 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 40 | if (!stdout.includes(expected)) { 41 | console.error(stdout) 42 | } 43 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 44 | assert.ok(stdout.includes('Running mocha page tests with Puppeteer'), 'stdout contains expected output for running in page') 45 | }) 46 | 47 | it('should run in worker', async () => { 48 | const { stdout, stderr, code } = await runCli(mochaFixture, '--worker --page=false') 49 | if (code !== 0) { 50 | console.error(stderr) 51 | } 52 | assert.strictEqual(code, 0, 'exited with zero exit code') 53 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 54 | if (!stdout.includes(expected)) { 55 | console.error(stdout) 56 | } 57 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 58 | assert.ok(stdout.includes('Running mocha worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 59 | }) 60 | 61 | it('should run in serviceworker', async () => { 62 | const { stdout, stderr, code } = await runCli(mochaFixture, '--serviceworker --page=false') 63 | if (code !== 0) { 64 | console.error(stderr) 65 | } 66 | assert.strictEqual(code, 0, 'exited with zero exit code') 67 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 68 | if (!stdout.includes(expected)) { 69 | console.error(stdout) 70 | } 71 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 72 | assert.ok(stdout.includes('Running mocha serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 73 | }) 74 | 75 | it('should run in page, worker and serviceworker', async () => { 76 | const { stdout, stderr, code } = await runCli(mochaFixture, '--worker --serviceworker') 77 | if (code !== 0) { 78 | console.error(stderr) 79 | } 80 | assert.strictEqual(code, 0, 'exited with zero exit code') 81 | 82 | let expected = expectedTemplate.replace(/WORKER/, 'not in worker') 83 | if (!stdout.includes(expected)) { 84 | console.error(stdout) 85 | } 86 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 87 | assert.ok(stdout.includes('Running mocha page tests with Puppeteer'), 'stdout contains expected output for running in page') 88 | 89 | expected = expectedTemplate.replace(/WORKER/, 'in worker') 90 | if (!stdout.includes(expected)) { 91 | console.error(stdout) 92 | } 93 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 94 | assert.ok(stdout.includes('Running mocha worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 95 | 96 | expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 97 | if (!stdout.includes(expected)) { 98 | console.error(stdout) 99 | } 100 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 101 | assert.ok(stdout.includes('Running mocha serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 102 | }) 103 | }) 104 | 105 | describe(`failing mocha (${type})`, function () { 106 | this.timeout(60000) 107 | const expectedTemplate = ` 108 | test suite 1 - worker 109 | ✅ is WORKER 110 | 111 | test suite 2 - failing 112 | 1) should fail 113 | 114 | 115 | 1 passing (Xms) 116 | 1 failing 117 | 118 | 1) test suite 2 - failing 119 | should fail: 120 | Error: failing test 121 | ` 122 | 123 | it('should fail in page', async () => { 124 | let { stdout, code } = await runCli(mochaFailureFixture) 125 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 126 | stdout = removeTimings(stdout) 127 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 128 | if (!stdout.includes(expected)) { 129 | console.error(stdout) 130 | } 131 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 132 | assert.ok(stdout.includes('Running mocha page tests with Puppeteer'), 'stdout contains expected output for running in worker') 133 | }) 134 | 135 | it('should fail in worker', async () => { 136 | let { stdout, code } = await runCli(mochaFailureFixture, '--worker --page=false') 137 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 138 | stdout = removeTimings(stdout) 139 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 140 | if (!stdout.includes(expected)) { 141 | console.error(stdout) 142 | } 143 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 144 | assert.ok(stdout.includes('Running mocha worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 145 | }) 146 | 147 | it('should fail in serviceworker', async () => { 148 | let { stdout, code } = await runCli(mochaFailureFixture, '--serviceworker --page=false') 149 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 150 | stdout = removeTimings(stdout) 151 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 152 | if (!stdout.includes(expected)) { 153 | console.error(stdout) 154 | } 155 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 156 | assert.ok(stdout.includes('Running mocha serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 157 | }) 158 | 159 | it('should fail in page and not run in worker', async () => { 160 | let { stdout, code } = await runCli(mochaFailureFixture, '--worker') 161 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 162 | stdout = removeTimings(stdout) 163 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 164 | let found = stdout.indexOf(expected) 165 | assert.ok(found > -1, 'stdout contains expected test output') 166 | found = stdout.indexOf(expected, found + 1) 167 | assert.ok(found === -1, 'stdout doesn\'t contain second instance of expected test output') 168 | assert.ok(stdout.includes('Running mocha page tests with Puppeteer'), 'stdout contains expected output for running in page') 169 | assert.ok(!stdout.includes('Running mocha worker tests with Puppeteer'), 'stdout doesn\'t contain expected output for running in worker') 170 | }) 171 | }) 172 | } 173 | -------------------------------------------------------------------------------- /test/test-polendina-node.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCommand } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | 10 | const cli = path.join(__dirname, '../polendina-node-cli.js') 11 | 12 | function runCli (mode, cwd) { 13 | return runCommand(`${cli} ${mode} test*.js`, cwd) 14 | } 15 | 16 | function removeTiming (stdout) { 17 | return stdout.replace(/Took [\d.]+ seconds/, 'Took X seconds') 18 | } 19 | 20 | for (const type of ['cjs', 'esm']) { 21 | const bareSyncFixture = path.join(__dirname, `fixtures/bare-sync${type === 'esm' ? '-esm' : ''}`) 22 | const bareSyncFailureFixture = path.join(__dirname, `fixtures/bare-sync-failure${type === 'esm' ? '-esm' : ''}`) 23 | const bareAsyncFixture = path.join(__dirname, `fixtures/bare-async${type === 'esm' ? '-esm' : ''}`) 24 | const bareAsyncFailureFixture = path.join(__dirname, `fixtures/bare-async-failure${type === 'esm' ? '-esm' : ''}`) 25 | 26 | describe(`polendina-node bare-sync (${type})`, function () { 27 | this.timeout(60000) 28 | it('pass', async () => { 29 | const expected = 30 | `testing is not in worker 31 | ✔ test-1.js 32 | testing bare fixture 33 | ✔ test-2.js 34 | Took X seconds 35 | ` 36 | const { stdout, stderr, code } = await runCli('bare-sync', bareSyncFixture) 37 | if (code !== 0) { 38 | console.error(stderr) 39 | } 40 | assert.strictEqual(code, 0, 'exited with zero exit code') 41 | assert.strictEqual(removeTiming(stdout), expected) 42 | }) 43 | 44 | it('failure', async () => { 45 | const expected = 46 | `testing is not in worker 47 | ✔ test-1.js 48 | testing bare fixture 49 | ✘ test-2.js 50 | Took X seconds 51 | ` 52 | const { stdout, stderr, code } = await runCli('bare-sync', bareSyncFailureFixture) 53 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 54 | assert.strictEqual(removeTiming(stdout), expected) 55 | assert.ok(stderr.includes('AssertionError: faily mcfailface'), 'stderr contains expected output') 56 | }) 57 | }) 58 | 59 | describe(`polendina-node bare-async (${type})`, function () { 60 | this.timeout(60000) 61 | it('pass', async () => { 62 | const expected = 63 | `testing is not in worker 64 | ✔ test-1.js 65 | test-2.js 66 | testing bare fixture 67 | ✔ test1 68 | ✔ test2 69 | Took X seconds 70 | ` 71 | const { stdout, stderr, code } = await runCli('bare-async', bareAsyncFixture) 72 | if (code !== 0) { 73 | console.error(stderr) 74 | } 75 | assert.strictEqual(code, 0, 'exited with zero exit code') 76 | assert.strictEqual(removeTiming(stdout), expected) 77 | }) 78 | 79 | it('failure', async () => { 80 | const expected = 81 | `testing is not in worker 82 | ✔ test-1.js 83 | test-2.js 84 | testing bare fixture 85 | ✔ test1 86 | ✘ test2 87 | Took X seconds 88 | ` 89 | const { stdout, stderr, code } = await runCli('bare-async', bareAsyncFailureFixture) 90 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 91 | assert.strictEqual(removeTiming(stdout), expected) 92 | assert.ok(stderr.includes('AssertionError: faily mcfailface'), 'stderr contains expected output') 93 | }) 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /test/test-tape.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCli } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | 10 | for (const type of ['cjs', 'esm']) { 11 | const tapeFixture = path.join(__dirname, `fixtures/tape${type === 'esm' ? '-esm' : ''}`) 12 | const tapeFailureFixture = path.join(__dirname, `fixtures/tape-failure${type === 'esm' ? '-esm' : ''}`) 13 | 14 | describe(`basic tape (${type})`, function () { 15 | this.timeout(60000) 16 | const expectedTemplate = ` 17 | TAP version 13 18 | # test suite 1 19 | # test case 1 20 | ok 1 should be strictly equal 21 | # test case 2 22 | ok 2 all good 23 | # test suite 2 - worker 24 | # is WORKER 25 | ok 3 should be strictly equal 26 | # test suite 3 27 | # test case 1 28 | ok 4 should be strictly equal 29 | # test case 2 30 | ok 5 all good 31 | 32 | 1..5 33 | # tests 5 34 | # pass 5 35 | 36 | # ok 37 | ` 38 | 39 | it('should run in page', async () => { 40 | const { stdout, stderr, code } = await runCli(tapeFixture, '--runner=tape') 41 | if (code !== 0) { 42 | console.error(stderr) 43 | } 44 | assert.strictEqual(code, 0, 'exited with zero exit code') 45 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 46 | if (!stdout.includes(expected)) { 47 | console.error(stdout) 48 | } 49 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 50 | assert.ok(stdout.includes('Running tape page tests with Puppeteer'), 'stdout contains expected output for running in page') 51 | }) 52 | 53 | it('should run in worker', async () => { 54 | const { stdout, stderr, code } = await runCli(tapeFixture, '--runner=tape --worker --page=false') 55 | if (code !== 0) { 56 | console.error(stderr) 57 | } 58 | assert.strictEqual(code, 0, 'exited with zero exit code') 59 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 60 | if (!stdout.includes(expected)) { 61 | console.error(stdout) 62 | } 63 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 64 | assert.ok(stdout.includes('Running tape worker tests with Puppeteer'), 'stdout contains expected output for running in page') 65 | }) 66 | 67 | it('should run in serviceworker', async () => { 68 | const { stdout, stderr, code } = await runCli(tapeFixture, '--runner=tape --serviceworker --page=false') 69 | if (code !== 0) { 70 | console.error(stderr) 71 | } 72 | assert.strictEqual(code, 0, 'exited with zero exit code') 73 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 74 | if (!stdout.includes(expected)) { 75 | console.error(stdout) 76 | } 77 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 78 | assert.ok(stdout.includes('Running tape serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 79 | }) 80 | 81 | it('should run in page, worker and serviceworker', async () => { 82 | const { stdout, stderr, code } = await runCli(tapeFixture, '--runner=tape --worker --serviceworker') 83 | if (code !== 0) { 84 | console.error(stderr) 85 | } 86 | assert.strictEqual(code, 0, 'exited with zero exit code') 87 | 88 | let expected = expectedTemplate.replace(/WORKER/, 'not in worker') 89 | if (!stdout.includes(expected)) { 90 | console.error(stdout) 91 | } 92 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 93 | assert.ok(stdout.includes('Running tape serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 94 | 95 | expected = expectedTemplate.replace(/WORKER/, 'in worker') 96 | if (!stdout.includes(expected)) { 97 | console.error(stdout) 98 | } 99 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 100 | assert.ok(stdout.includes('Running tape serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 101 | 102 | expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 103 | if (!stdout.includes(expected)) { 104 | console.error(stdout) 105 | } 106 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 107 | assert.ok(stdout.includes('Running tape serviceworker tests with Puppeteer'), 'stdout contains expected output for running in page') 108 | }) 109 | }) 110 | 111 | describe(`failing tape (${type})`, function () { 112 | this.timeout(60000) 113 | const expectedTemplate = ` 114 | TAP version 13 115 | # test suite 1 - worker 116 | # is WORKER 117 | ok 1 should be strictly equal 118 | # test suite 2 - failure 119 | # test case 1 120 | not ok 2 bork 121 | --- 122 | operator: fail 123 | stack: |- 124 | Error: bork 125 | ... 126 | 127 | 1..2 128 | # tests 2 129 | # pass 1 130 | # fail 1 131 | ` 132 | 133 | it('should fail in page', async () => { 134 | let { stdout, code } = await runCli(tapeFailureFixture, '--runner=tape') 135 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 136 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 137 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 138 | if (!stdout.includes(expected)) { 139 | console.error(stdout) 140 | } 141 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 142 | assert.ok(stdout.includes('Running tape page tests with Puppeteer'), 'stdout contains expected output for running in worker') 143 | }) 144 | 145 | it('should fail in worker', async () => { 146 | let { stdout, code } = await runCli(tapeFailureFixture, '--runner=tape --worker --page=false') 147 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 148 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 149 | const expected = expectedTemplate.replace(/WORKER/, 'in worker') 150 | if (!stdout.includes(expected)) { 151 | console.error(stdout) 152 | } 153 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 154 | assert.ok(stdout.includes('Running tape worker tests with Puppeteer'), 'stdout contains expected output for running in worker') 155 | }) 156 | 157 | it('should fail in serviceworker', async () => { 158 | let { stdout, code } = await runCli(tapeFailureFixture, '--runner=tape --serviceworker --page=false') 159 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 160 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 161 | const expected = expectedTemplate.replace(/WORKER/, 'in serviceworker') 162 | if (!stdout.includes(expected)) { 163 | console.error(stdout) 164 | } 165 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 166 | assert.ok(stdout.includes('Running tape serviceworker tests with Puppeteer'), 'stdout contains expected output for running in worker') 167 | }) 168 | 169 | it('should fail in page and not run in worker', async () => { 170 | let { stdout, code } = await runCli(tapeFailureFixture, '--runner=tape --worker') 171 | assert.strictEqual(code, 1, 'exited with non-zero exit code') 172 | stdout = stdout.replace(/^ +at .*\n/gm, '') // stack traces 173 | const expected = expectedTemplate.replace(/WORKER/, 'not in worker') 174 | let found = stdout.indexOf(expected) 175 | assert.ok(found > -1, 'stdout contains expected test output') 176 | found = stdout.indexOf(expected, found + 1) 177 | assert.ok(found === -1, 'stdout doesn\'t contain second instance of expected test output') 178 | assert.ok(stdout.includes('Running tape page tests with Puppeteer'), 'stdout contains expected output for running in page') 179 | assert.ok(!stdout.includes('Running tape worker tests with Puppeteer'), 'stdout doesn\'t contain expected output for running in worker') 180 | }) 181 | }) 182 | } 183 | -------------------------------------------------------------------------------- /test/test-webpack-merge.js: -------------------------------------------------------------------------------- 1 | /* globals describe it */ 2 | 3 | import assert from 'assert' 4 | import path from 'path' 5 | import { fileURLToPath } from 'url' 6 | import { runCli } from './common.js' 7 | 8 | const __dirname = fileURLToPath(path.dirname(import.meta.url)) 9 | const webpackMergeFixture = path.join(__dirname, 'fixtures/webpack-merge') 10 | 11 | describe('basic webpack-merge', function () { 12 | this.timeout(60000) 13 | it('should run with a custom config', async () => { 14 | const expected = ` 15 | assert.ok() is a function 16 | WOOP is set 17 | ✔ test.js 18 | ` 19 | const { stdout, stderr, code } = await runCli(webpackMergeFixture, '--runner=bare-sync --webpack-config webpack.config.js') 20 | if (code !== 0) { 21 | console.error(stderr) 22 | } 23 | assert.strictEqual(code, 0, 'exited with zero exit code') 24 | if (!stdout.includes(expected)) { 25 | console.error(stdout) 26 | } 27 | assert.ok(stdout.includes(expected), 'stdout contains expected test output') 28 | assert.ok(stdout.includes('Running bare-sync page tests with Puppeteer'), 'stdout contains expected output for running in page') 29 | }) 30 | 31 | it('should fail without a custom config', async () => { 32 | const expectedStdout = ` 33 | assert.ok() is a function 34 | ✘ test.js 35 | ` 36 | const expectedStderr = 'WOOP is not defined' 37 | 38 | const { stdout, stderr, code } = await runCli(webpackMergeFixture, '--runner=bare-sync') 39 | assert.strictEqual(code, 1, 'exited with zero exit code') 40 | assert.ok(stdout.includes(expectedStdout), 'stdout contains expected test output') 41 | assert.ok(stderr.includes(expectedStderr), 'stderr contains expected test output') 42 | assert.ok(stdout.includes('Running bare-sync page tests with Puppeteer'), 'stdout contains expected output for running in page') 43 | }) 44 | }) 45 | --------------------------------------------------------------------------------