{"id":15012861,"url":"https://github.com/paperstrike/wrightplay","last_synced_at":"2025-04-12T03:31:56.921Z","repository":{"id":39623975,"uuid":"505103454","full_name":"PaperStrike/wrightplay","owner":"PaperStrike","description":"Playwright, but unit test","archived":false,"fork":false,"pushed_at":"2024-08-31T18:27:10.000Z","size":690,"stargazers_count":3,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-07T10:04:23.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PaperStrike.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-06-19T12:27:59.000Z","updated_at":"2024-04-28T15:12:38.000Z","dependencies_parsed_at":"2023-02-08T08:32:01.581Z","dependency_job_id":"2d56b6e0-f779-4076-a7e5-be33ceb92b64","html_url":"https://github.com/PaperStrike/wrightplay","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2Fwrightplay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2Fwrightplay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2Fwrightplay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PaperStrike%2Fwrightplay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PaperStrike","download_url":"https://codeload.github.com/PaperStrike/wrightplay/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223494315,"owners_count":17154526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:43:20.479Z","updated_at":"2024-11-07T10:04:26.904Z","avatar_url":"https://github.com/PaperStrike.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wrightplay\n\n[![Build Status](https://github.com/PaperStrike/wrightplay/actions/workflows/test.yml/badge.svg)](https://github.com/PaperStrike/wrightplay/actions/workflows/test.yml)\n[![npm Package](https://img.shields.io/npm/v/wrightplay?logo=npm \"wrightplay\")](https://www.npmjs.com/package/wrightplay)\n\nExperience the seamless synergy of Playwright with wrightplay: while Playwright takes the center stage for Node.js (e2e testing), wrightplay puts the spotlight back on browsers (unit testing).\n\nFor a complete example showing how to use wrightplay, check out [wrightplay-demo](https://github.com/PaperStrike/wrightplay-demo).\n\n**Project Under Development**\n\nBut feel free to try! All APIs should work, they just lack test cases and documentations.\n\n## When should I choose wrightplay?\n\n* You want Node.js native coverage reports\n* You want full TypeScript supports\n* You want NET interceptor that intercepts all page requests, with in-page control\n* You want source mapped error stack traces\n* You don't want the error stack mapping to happen inside the browser\n* You don't want the interceptor to occupy Service Worker\n* You don't want to find, choose, and install a “loader” dependency for each browser\n\nThe key features come from:\n\n* It converts chromium coverage output to Node.js format\n* The source mapping of error traces happens outside the browser, affecting no page script but only the Node.js console output\n* Everything written in TypeScript\n* Browsers from [Playwright](https://playwright.dev)\n* Proxies [`page.route`](https://playwright.dev/docs/api/class-page#page-route) through WebSocket to a specific module within the page to intercept page requests with in-page control and without occupying Service Worker \u003c!-- Moreover, you may have heard that `page.route` has supported (“likely” as of now) Service Worker interception on Chromium since Playwright 1.24 --\u003e\n\n## Installation\n\n```shell\nnpm i -D wrightplay\n```\n\nInstall test browsers with Playwright's cli.\n\n```shell\n# Default browsers (chromium, firefox, and webkit)\nnpx playwright install\n\n# Specific browser(s)\n# browser: one of: chromium, chrome, chrome-beta, msedge, msedge-beta, msedge-dev, firefox, webkit\n# E.g., npx playwright install chromium\nnpx playwright install \u003cbrowser...\u003e\n```\n\nTo use browsers available on the machine, use [`channel`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server-option-channel) via [`browserServerOptions`](#browserserveroptions).\n\nFor CI environments, check out\n* [Install system dependencies · Command line tools | Playwright](https://playwright.dev/docs/cli#install-system-dependencies).\n\nFor more available options and descriptions, check out\n* [Installing browsers · Browsers | Playwright](https://playwright.dev/docs/browsers#installing-browsers)\n* [Install browsers · Command line tools | Playwright](https://playwright.dev/docs/cli#install-browsers)\n\n## Basic\n\n### Write a test setup\n\n#### Listen to test ready\n\n```ts\nimport { onInit } from 'wrightplay';\n\nonInit(() =\u003e {\n  startTesting();\n});\n```\n\nPass a callback to `onInit` to act when all the test files got successfully imported. If called multiple times, `onInit` will call the callbacks in order. If a callback returns a promise, it will wait until the promise get fulfilled before calling the next callback. If a callback throws an error, the process will exit unsuccessfully.\n\n#### Indicate the result of your tests\n\n```ts\nimport { done } from 'wrightplay';\n\nonTestEnd((failures) =\u003e {\n  // Pass the desired process exit number to `done`.\n  done(failures \u003e 0 ? 1 : 0);\n});\n```\n\nThe process may never exit if you don't call this function.\n\nSome test runners like Mocha require additional steps to run in browsers, see [Working with...](#working-with) part for examples.\n\n### Start\n\nIf the tests inject the setup on their own,\n\n```shell\nwrightplay test/**/*.spec.ts\n```\n\nIf the setup is separate and the tests don't inject it themselves,\n\n```shell\nwrightplay -s test/setup.ts test/**/*.spec.ts\n```\n\nIf you want Node.js API,\n\n```ts\nimport { Runner } from 'wrightplay/node';\n\nconst runner = new Runner({\n  setup: 'test/setup.ts',\n  tests: 'test/**/*.spec.ts',\n});\n\nprocess.exit(await runner.runTests());\n```\n\nCheck [Options](#options) for full option list.\n\n## HostHandle\n\nAPIs similar to [`JSHandle` in Playwright](https://playwright.dev/docs/api/class-jshandle).\n\nJust like you can pass a function from node to browser to run via `page.evaluate` in Playwright, you can pass a function from browser to node via `pageHandle.evaluate` in wrightplay.\n\n`pageHandle` and `contextHandle` represent the [`Page`](https://playwright.dev/docs/api/class-page) and [`BrowserContext`](https://playwright.dev/docs/api/class-browsercontext) Playwright instance that controls the current page respectively.\n\n### Evaluate\n\nSimilar to [`JSHandle.evaluate` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-evaluate).\n\n```ts\nimport { pageHandle } from 'wrightplay';\n\nconst screenshotPath = 'screenshots/1.png';\nawait pageHandle.evaluate(async (page, path) =\u003e {\n  await page.screenshot({ path });\n}, screenshotPath);\n```\n\n### EvaluateHandle\n\nSimilar to [`JSHandle.evaluateHandle` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-evaluate-handle).\n\n```ts\nimport { pageHandle } from 'wrightplay';\n\nconst browserHandle = await pageHandle\n  .evaluateHandle((page) =\u003e page.context().browser());\n\n// \"103.0.5060.42\" on chromium as of writing\nawait browserHandle.evaluate((b) =\u003e b.version());\n```\n\n### Dispose\n\nSimilar to [`JSHandle.dispose` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-dispose).\n\n### getProperties\n\nSimilar to [`JSHandle.getProperties` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-get-properties).\n\n### getProperty\n\nSimilar to [`JSHandle.getProperty` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-get-property).\n\n### jsonValue\n\nSimilar to [`JSHandle.jsonValue` in Playwright](https://playwright.dev/docs/api/class-jshandle#js-handle-json-value).\n\n## Route\n\nDedicated API faster than wrapping `contextRoute.evaluate` for routing, uses `ArrayBuffer` and `Blob` for binary data. The handler callback stays in the browser and has access to all the scopes like a normal function has. \n\nSimilar to [`browserContext.route` in Playwright](https://playwright.dev/docs/api/class-browsercontext#browser-context-route).\n\n```ts\nimport { contextRoute } from 'wrightplay';\n\nconst body = new Blob(['routed!']);\nawait contextRoute('hello', (r) =\u003e {\n  r.fulfill({ body });\n}, { times: 1 });\n\n// \"routed!\"\nawait (await fetch('hello')).text();\n```\n\nAll the routes by this API will auto “unroute” on page unload.\n\n### Unroute\n\nSimilar to [`browserContext.unroute` in Playwright](https://playwright.dev/docs/api/class-browsercontext#browser-context-unroute).\n\n## Coverage\n\nUse [`NODE_V8_COVERAGE`](https://nodejs.org/api/cli.html#node_v8_coveragedir) environment variable to get coverage results. Tools like [`c8`](https://www.npmjs.com/package/c8) that use `NODE_V8_COVERAGE` internally work as well.\n\nNote that firefox and webkit don't support coverage recording.\n\n```shell\n# Generate Node.js format coverage output to ./coverage/tmp/\ncross-env NODE_V8_COVERAGE=coverage/tmp wrightplay test/*.spec.*\n\n# Or use c8 coverage reports\nc8 -a wrightplay test/*.spec.*\n```\n\n`-a`, `--exclude-after-remap` option enables `c8` to properly parse 1:many source maps for wrightplay. `c8` should enable this option by default, but they haven't yet.\n\n## Configuration file\n\nYou can put the test options (see [Options](#options)) in a config file, and wrightplay will read it as the option base. See [`config`](#config) option for how the CLI resolves the config file path.\n\nYou can use an array of option objects to represent multiple test runs that should run in order. \u003c!-- Parallel run may be available if one day I come out with a way to organize the output messages. Probably in another package like `@playwright/test` from `playwright-core`. --\u003e\n\n### JS\n\n```js\nexport default {\n  tests: 'test/**/*.spec.*',\n};\n```\n\n### JSON\n\n```json\n{\n  \"tests\": \"test/**/*.spec.*\"\n}\n```\n\n### TS\n\n```ts\nimport { ConfigOptions } from 'wrightplay/node';\n\nconst config: ConfigOptions = {\n  tests: 'test/**/*.spec.*',\n};\n\nexport default config;\n```\n\n## Options\n\n### config\n\n```shell\nwrightplay --config path/to/config/file\n\n# Omit to use default\nwrightplay\n```\n\nCLI-only option.\n\nPath to config file. The CLI checks these files by default:\n\n```ts\n[\n  'package.json', // \"wrightplay\" property\n  '.wrightplayrc',\n  '.wrightplayrc.json',\n  '.wrightplayrc.ts',\n  '.wrightplayrc.mts',\n  '.wrightplayrc.cts',\n  '.wrightplayrc.js',\n  '.wrightplayrc.mjs',\n  '.wrightplayrc.cjs',\n  '.config/wrightplayrc',\n  '.config/wrightplayrc.json',\n  '.config/wrightplayrc.ts',\n  '.config/wrightplayrc.mts',\n  '.config/wrightplayrc.cts',\n  '.config/wrightplayrc.js',\n  '.config/wrightplayrc.mjs',\n  '.config/wrightplayrc.cjs',\n  'wrightplay.config.ts',\n  'wrightplay.config.mts',\n  'wrightplay.config.cts',\n  'wrightplay.config.js',\n  'wrightplay.config.mjs',\n  'wrightplay.config.cjs',\n]\n```\n\n### setup\n\n```shell\nwrightplay -s \u003cpath/to/setup\u003e\nwrightplay --setup \u003cpath/to/setup\u003e\n```\n\nFile to run before the test files.\n\n### tests\n\n```shell\nwrightplay [pattern...]\n```\n\nPatterns for the target test files. Check out [`globby`](https://www.npmjs.com/package/globby) for supported patterns.\n\n### entryPoints\n\n```shell\nwrightplay [entry...]\n```\n\nAdditional entry points to build. You can use this option to build workers.\n\nIn CLI, use format `name=path/to/entry`. For example,\n\n```shell\nwrightplay worker=test/web-worker-helper.ts\n```\n\nor config file\n\n```json\n{\n  \"entryPoints\": {\n    \"worker\": \"test/web-worker.ts\"\n  }\n}\n```\n\nwill make this available:\n\n```ts\nconst worker = new Worker('/worker.js');\n// ...\n```\n\n### watch\n\n```shell\nwrightplay -w\nwrightplay --watch\n```\n\nMonitor test file changes and trigger automatic test reruns. Defaults to `false`.\n\nPlease be aware that on certain platforms, particularly in the context of large-scale projects, this feature might silently fail or raise some errors.\n\n### browser\n\n```shell\nwrightplay -b \u003cbrowser\u003e\nwrightplay --browser \u003cbrowser\u003e\n```\n\nBrowser type. One of: `chromium`, `firefox`, `webkit`. Defaults to `chromium`.\n\n### browserServerOptions\n\n```shell\nwrightplay --browser-server-options \u003cjson\u003e\n```\n\nOptions used to launch the browser server. See [`browserType.launchServer([options])` in Playwright](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server) for details.\n\n### headless\n\nIn CLI, use [`--debug`](#debug).\n\nRun the browser in headless mode. Defaults to `true` unless the\n`devtools` option (in [`browserServerOptions`](#browserserveroptions)) is `true`.\n\n### debug\n\n```shell\nwrightplay -d\nwrightplay --debug\n```\n\nCLI-only option.\n\nThis sets `devtools` (in [`browserServerOptions`](#browserserveroptions)) to `true` and [`headless`](#headless) to `false`.\n\n### noCov\n\n```shell\nwrightplay --no-cov\n```\n\nDisable coverage file output. This only matters when `NODE_V8_COVERAGE` is set. Defaults to `false` on chromium, `true` on firefox and webkit.\n\n### cwd\n\nCurrent working directory. Defaults to `process.cwd()`.\n\n## Working with...\n\n### Mocha\n\nReference:\n* [RUNNING MOCHA IN THE BROWSER | Mocha](https://mochajs.org/#running-mocha-in-the-browser)\n* [mocha.run | Mocha - Documentation](https://mochajs.org/api/mocha#run)\n\n#### Use mocha.js\n\nIn your `package.json`, add:\n\n```json\n{\n  \"browser\": {\n    \"mocha\": \"mocha/mocha.js\"\n  }\n}\n```\n\n#### Write mocha setup like\n\n```ts\nimport 'mocha';\nimport { onInit, done } from 'wrightplay';\n\nmocha.setup({\n  color: true,\n  fullTrace: true,\n  reporter: 'spec',\n  ui: 'bdd',\n});\n\nonInit(() =\u003e {\n  mocha.run((failures) =\u003e {\n    done(failures \u003e 0 ? 1 : 0);\n  });\n});\n```\n\n### uvu\n\n`uvu` has no reliable or future-proof way to run and get the test results programmatically. Track [lukeed/uvu · Issue #113](https://github.com/lukeed/uvu/issues/113).\n\nClick [here](test/third-party/uvu/setup.ts) for an example setup that reads the test results by proxying the console messages.\n\n### tape\n\n`tape` needs some node-specific modules to work: `path`, `stream`, `events`, and `process`. So we need polyfills to get it to work in browsers.\n\nSteps below may differ if you choose different providers.\n\n#### Install polyfills\n\n```shell\nnpm i -D path stream events process\n```\n\nIn `package.json`, add:\n\n```json\n{\n  \"browser\": {\n    \"process\": \"process/browser.js\"\n  }\n}\n```\n\n#### Write tape setup like\n\n```ts\nimport process from 'process';\nimport { done } from 'wrightplay';\n\nglobalThis.process = process;\n\nconst { onFailure, onFinish } = await import('tape');\n\nlet exitCode = 0;\nonFailure(() =\u003e {\n  exitCode = 1;\n});\nonFinish(() =\u003e {\n  done(exitCode);\n});\n```\n\n### Zora\n\n#### Process zora messages\n\nTo setup zora, pipe zora reporters to read test results:\n\n```ts\nimport { hold, report, createTAPReporter } from 'zora';\nimport { done, onInit } from 'wrightplay';\n\n// Hold zora default run\nhold();\n\n// Record failed assertion\nconst tapReporter = createTAPReporter();\nasync function* record(stream: Parameters\u003ctypeof tapReporter\u003e[0]) {\n  let exitCode = 0;\n  for await (const msg of stream) {\n    if (msg.type === 'ASSERTION' \u0026\u0026 !msg.data.pass) {\n      exitCode = 1;\n    } else if (msg.type === 'ERROR') {\n      done(1);\n    } if (msg.type === 'TEST_END') {\n      done(exitCode);\n    }\n    yield msg;\n  }\n}\n\nonInit(async () =\u003e {\n  // Run zora with piped reporter\n  await report({\n    reporter: (stream) =\u003e tapReporter(record(stream)),\n  });\n});\n```\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperstrike%2Fwrightplay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaperstrike%2Fwrightplay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperstrike%2Fwrightplay/lists"}