{"id":15643199,"url":"https://github.com/kettanaito/page-with","last_synced_at":"2025-04-14T06:50:35.921Z","repository":{"id":43472096,"uuid":"336499287","full_name":"kettanaito/page-with","owner":"kettanaito","description":"A library for usage example-driven in-browser testing of your code.","archived":false,"fork":false,"pushed_at":"2023-03-30T20:36:20.000Z","size":340,"stargazers_count":59,"open_issues_count":2,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T22:22:44.045Z","etag":null,"topics":["testing"],"latest_commit_sha":null,"homepage":"https://npm.im/page-with","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kettanaito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-02-06T09:19:17.000Z","updated_at":"2024-02-13T12:23:26.000Z","dependencies_parsed_at":"2024-03-17T02:05:21.181Z","dependency_job_id":"608c0275-f392-4a22-a20a-04a82674c528","html_url":"https://github.com/kettanaito/page-with","commit_stats":{"total_commits":69,"total_committers":3,"mean_commits":23.0,"dds":0.08695652173913049,"last_synced_commit":"144040dad79ad64486012fe4bb9f2cd0b311bf63"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":"kettanaito/create-typescript-library","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kettanaito%2Fpage-with","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kettanaito%2Fpage-with/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kettanaito%2Fpage-with/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kettanaito%2Fpage-with/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kettanaito","download_url":"https://codeload.github.com/kettanaito/page-with/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248837274,"owners_count":21169373,"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":["testing"],"created_at":"2024-10-03T11:59:27.224Z","updated_at":"2025-04-14T06:50:35.889Z","avatar_url":"https://github.com/kettanaito.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `page-with`\n\nA library for usage example-driven in-browser testing of your own libraries.\n\n## Motivation\n\nThis library empowers example-based testing. That is a testing approach when you write a bunch of actual usage example modules of your own library and wish to run tests against them.\n\n### Why not JSDOM?\n\nJSDOM is designed to emulate browser environment, not substitute it. The code you test in JSDOM still runs in NodeJS and there is no actual browser context involved.\n\n### Why not Cypress?\n\nTools like Cypress give you a benefit of executing your tests in a real browser. However, the setup of such tools is often verbose and may be an overkill for usage-based in-browser testing of a _library_. Cypress also lacks a low-level browser automation API (i.e. creating and performing actions across multiple tabs, Service Worker access), which makes it not suitable for a versatile yet plain usage testing.\n\n### Why not Puppeteer/Playwrigth/Selenium/etc.?\n\nLow-level browser automation software like Puppeteer gives you a great control over the browser. However, you still need to load your usage example into it, which may involve optional compilation step in case you wish to illustrate usage examples in TypeScript, React, or any other format that cannot run directly in a browser.\n\n## How does this work?\n\n1. Creates a single browser process for the entire test run.\n1. Spawns a single server that compiles usage examples on-demand.\n1. Gives you an API to compile and load a given usage example as a part of a test.\n1. Cleans up afterwards.\n\n## Getting started\n\n### Install\n\n```bash\n$ npm install page-with --save-dev\n```\n\n### Configure your test framework\n\nHere's an example how to use `page-with` with Jest:\n\n```js\n// jest.setup.js\nimport { createBrowser } from 'page-with'\n\nlet browser\n\nbeforeAll(async () =\u003e {\n  browser = await createBrowser()\n})\n\nafterAll(async () =\u003e {\n  await browser.cleanup()\n})\n```\n\n\u003e Specify the `jest.setup.js` file as the value for the [`setupFilesAfterEnv`](https://jestjs.io/docs/en/configuration.html#setupfilesafterenv-array) option in your Jest configuration file.\n\n### Create a usage scenario\n\n```js\n// test/getValue.usage.js\nimport { getValue } from 'my-library'\n\n// My library hydrates the value by the key from sessionStorage\n// if it's present, otherwise it returns undefined.\nwindow.value = getValue('key')\n```\n\n\u003e Use [webpack `resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) to import the source code of your library from its published namespace (i.e. `my-library`) instead of relative imports. Let your usage examples look exactly how your library is used.\n\n### Test your library\n\n```js\n// test/getValue.test.js\nimport { pageWith } from 'page-with'\n\nit('hydrates the value from the sessionStorage', async () =\u003e {\n  const scenario = await pageWith({\n    // Provide the usage example we've created earlier.\n    example: './getValue.usage.ts',\n  })\n\n  const initialValue = await scenario.page.evaluate(() =\u003e {\n    return window.value\n  })\n  expect(initialValue).toBeUndefined()\n\n  await scenario.page.evaluate(() =\u003e {\n    sessionStorage.setItem('key', 'abc-123')\n  })\n  await scenario.page.reload()\n\n  const hydratedValue = await scenario.page.evaluate(() =\u003e {\n    return window.value\n  })\n  expect(hydratedValue).toBe('abc-123')\n})\n```\n\n## Options\n\n### `example`\n\n(_Required_) A relative path to the example module to compile and load in the browser.\n\n```js\npageWith({\n  example: path.resolve(__dirname, 'example.js'),\n})\n```\n\n### `title`\n\nA custom title of the page. Useful to discern pages when loading multiple scenarios in the same browser.\n\n```js\npageWith({\n  title: 'My app',\n})\n```\n\n### `markup`\n\nA custom HTML markup of the loaded example.\n\n```js\npageWith({\n  markup: `\n\u003cbody\u003e\n  \u003cbutton\u003eCLick me\u003c/button\u003e\n\u003c/body\u003e\n  `,\n})\n```\n\n\u003e Note that the compiled example module will be appended to the markup automatically.\n\nYou can also provide a relative path to the HTML file to use as the custom markup:\n\n```js\npageWith({\n  markup: path.resolve(__dirname, 'markup.html'),\n})\n```\n\n### `contentBase`\n\nA relative path to a directory to use to resolve page's resources. Useful to load static resources (i.e. images) on the runtime.\n\n```js\npageWith({\n  contentBase: path.resolve(__dirname, 'public'),\n})\n```\n\n### `routes`\n\nA function to customize the Express server instance that runs the local preview of the compiled example.\n\n```js\npageWith({\n  routes(app) {\n    app.get('/user', (res, res) =\u003e {\n      res.status(200).json({ firstName: 'John' })\n    })\n  },\n})\n```\n\n\u003e Making a `GET /user` request in your example module now returns the defined JSON response.\n\n### `env`\n\nEnvironmental variables to propagate to the browser's `window`.\n\n```js\npageWith({\n  env: {\n    serverUrl: 'http://localhost:3000',\n  },\n})\n```\n\n\u003e The `serverUrl` variable will be available under `window.serverUrl` in the browser (and your example).\n\n## Recipes\n\n### Debug mode\n\nDebugging headless automated browsers is not an easy task. That's why `page-with` supports a debug mode in which it will open the browser for you to see and log out all the steps that your test performs into the terminal.\n\nTo enable the debug mode pass the `DEBUG` environmental variable to your testing command and scope it down to `pageWith`:\n\n```bash\n$ DEBUG=pageWith npm test\n```\n\n\u003e If necessary, replace `npm test` with the command that runs your automated tests.\n\nSince you see the same browser instance that runs in your test, you will also see all the steps your test makes live.\n\n### Debug breakpoints\n\nYou can use the `debug` utility to create a breakpoint at any point of your test.\n\n```js\nimport { pageWith, debug } from 'page-with'\n\nit('automates the browser', async () =\u003e {\n  const { page } = await pageWith({ example: 'function.usage.js' })\n  // Pause the execution when the page is created.\n  await debug(page)\n\n  await page.evaluate(() =\u003e {\n    console.log('Hey, some action!')\n  })\n\n  // Pause the execution after some actions in the test.\n  // See the result of those actions in the opened browser.\n  await debug(page)\n})\n```\n\n\u003e Note that you need to run your test [in debug mode](#debug-mode) to see the automated browser open.\n\n### Custom webpack configuration\n\nThis library compiles your usage example in the local server. To extend the webpack configuration used to compile your example pass the partial webpack config to the `serverOptions.webpackConfig` option of `createBrowser`.\n\n```js\nimport path from 'path'\nimport { createBrowser } from 'page-with'\n\nconst browser = createBrowser({\n  serverOptions: {\n    webpackConfig: {\n      resolve: {\n        alias: {\n          'my-lib': path.resolve(__dirname, '../lib'),\n        },\n      },\n    },\n  },\n})\n```\n\n## FAQ\n\n### Why choose `playwright`?\n\nPlaywright comes with a browser context feature that allows to spawn a single browser instance and execute various scenarios independently without having to create a new browser process per test. This decreases the testing time tremendously.\n\n### Why not `webpack-dev-server`?\n\nAlthough `webpack-dev-server` can perform webpack compilations and serve static HTML with the compilation assets injected, it needs to know the entry point(s) prior to compilation. To prevent each test from spawning a new dev server, this library creates a single instance of an Express server that compiles given entry points on-demand on runtime.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkettanaito%2Fpage-with","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkettanaito%2Fpage-with","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkettanaito%2Fpage-with/lists"}