{"id":17293086,"url":"https://github.com/benchristel/taste","last_synced_at":"2026-03-04T01:14:10.981Z","repository":{"id":63096393,"uuid":"403148672","full_name":"benchristel/taste","owner":"benchristel","description":"🏍 A fast JavaScript test framework","archived":false,"fork":false,"pushed_at":"2024-12-11T20:32:24.000Z","size":309,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T01:04:20.175Z","etag":null,"topics":["javascript","test","testing"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@benchristel/taste","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benchristel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-04T20:17:31.000Z","updated_at":"2025-02-18T22:53:18.000Z","dependencies_parsed_at":"2024-01-10T05:28:17.612Z","dependency_job_id":"9afb23c0-e722-4b86-967f-bfa77af405a3","html_url":"https://github.com/benchristel/taste","commit_stats":{"total_commits":74,"total_committers":3,"mean_commits":"24.666666666666668","dds":0.1216216216216216,"last_synced_commit":"9019df6c80161508b67ddffbd3fbbd8cd5731e81"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benchristel%2Ftaste","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benchristel%2Ftaste/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benchristel%2Ftaste/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benchristel%2Ftaste/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benchristel","download_url":"https://codeload.github.com/benchristel/taste/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248878021,"owners_count":21176241,"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":["javascript","test","testing"],"created_at":"2024-10-15T10:45:13.309Z","updated_at":"2026-03-04T01:14:05.936Z","avatar_url":"https://github.com/benchristel.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @benchristel/taste\n\nA **fast** JavaScript test framework, for browser apps, NPM packages, and Node.js\nscripts.\n\n```js\nimport {test, expect, is} from \"@benchristel/taste\"\n\ntest(\"greet\", {\n  \"greets the world\"() {\n    expect(greet(\"world\"), is, \"Hello, world!\")\n  },\n\n  \"defaults to a generic greeting\"() {\n    expect(greet(null), is, \"Hello, you person, you!\")\n  },\n})\n\nfunction greet(name) {\n  return `Hello, ${name || \"you person, you\"}!`\n}\n```\n\nTaste's design philosophy is that **a test is just a function**. Testing doesn't have to be more complicated than that! Some nice consequences of this idea:\n\n- Running a test suite is just calling a function.\n- A matcher is just a function that returns a boolean.\n- An assertion is just a function that throws an exception.\n\nThe result of all this is that **your tests can run anywhere JS can.** You can easily run them in the browser, or in NodeJS, Bun, or Deno. Heck, your smart TV could probably run Taste, though I haven't tried this (let me know if you do).\n\nTaste achieves this simplicity without major sacrifices to ergonomics or aesthetics. Just look at the example above!\n\n## Fast?\n\nOh, yes. Here are the results of running Taste's own test suite 768 times in Firefox on a 2.4 GHz Intel CPU:\n\n![134400 tests ran, and found no issues. 521 ms elapsed.](https://github.com/user-attachments/assets/0d3a40f0-4970-43b6-b805-aba6b60272b1)\n\n134400 tests in about half a second! I'm going to bet your program doesn't have that many tests.\n\nYou should take this measurement with a grain of salt, though, because when the same tests run many times, the JIT has more time to optimize the code. Still, _I_ think this is pretty cool.\n\n## Build Status\n\nTaste's own tests are written using Taste. [You can run the tests in your browser here](https://benchristel.github.io/taste).\n\n## Try it!\n\nhttps://benchristel.github.io/try-taste/\n\nThere is also a set of [downloadable koans/tutorials](https://github.com/benchristel/taste-koans) that walk you through\nTaste's features from basic to advanced, and serve as a reference for how to integrate Taste into a project.\n\n## Node.js Quick Start\n\n```js\n// copy-paste this into test.mjs\nimport {test, expect, is} from \"@benchristel/taste\"\nimport {getAllTests, runTests, formatTestResultsAsText} from \"@benchristel/taste\"\n\n// add a test to the global suite\ntest(\"my first Taste test\", {\n  \"runs\"() {\n    expect(1 + 1, is, 2)\n  },\n})\n\n// run the tests and print the results\nrunTests(getAllTests())\n  .then(formatTestResultsAsText)\n  .then(console.log)\n```\n\nThen run `time node test.mjs`. Replace `node` with `bun` to go even faster! You can also use `bun --hot test.mjs` to run your tests on every code change.\n\n## Project Templates\n\n- [Preact + TypeScript + Vite + Taste](https://github.com/benchristel/preact-typescript-vite-taste) (recommended)\n- [React + Flow + Snowpack + Taste](https://github.com/benchristel/react-flow-snowpack-taste) (deprecated)\n\n## Installation\n\n```\nyarn add @benchristel/taste\n```\n\n## Features\n\n- **Tests can run in the browser.** You can integrate test\n  results into the dev UI of your app!\n- **Tests can live in the same files as production code.**\n  Or you can use any other organization scheme. The tests\n  are automatically stripped out of production builds of\n  your app.\n- **The tests are fast**—up to 50x faster than Jest tests.\n  Taste can run tens of thousands of tests per second.\n- **Custom matchers couldn't be simpler.** Any function\n  that returns a boolean can be used in a test assertion.\n  Test failures still get pretty-formatted as you'd expect.\n\n## Non-features\n\n- `async` tests are not run in parallel. Thus, if you\n  `await` a 100ms timer in a test, your suite will take\n  100ms longer to run. It's up to you to design your code so\n  promises can resolve quickly in tests. The ideal is to\n  resolve all promises in [microtasks](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth),\n  which will allow your async tests to run as fast as\n  synchronous code.\n- By default, there is no timeout for `async` tests. If you\n  accidentally `await` a promise that never resolves, your\n  tests will hang with no indication of what's wrong. Reduce\n  the pain of this eventuality by running your tests on\n  every code change so you can quickly revert mistakes. You\n  can add timeouts to your test suite with\n  [@benchristel/taste-timeout](https://www.npmjs.com/package/@benchristel/taste-timeout).\n- There are no equivalents of Jest's `beforeEach` etc.,\n  nor are there nested `describe` or `context` blocks. You\n  can de-duplicate repeated setup by simply extracting\n  functions and calling them, as you would for duplicated\n  production code. In my experience, this makes for more\n  readable tests in the long run. However, Taste is\n  flexible enough that you could build your own Jest-like\n  test-definition syntax on top of it, if you wanted to.\n- Taste has no facilities for mocking. It's clearer, and\n  easy enough, to roll your own test doubles if you need to.\n  Or use another mocking library; [there are plenty to\n  choose from](https://www.npmjs.com/search?q=mock).\n\n## Recommended Integrations\n\n- Add [Vite](https://vitejs.dev/) for\n  auto-refresh, and you can get near-instantaneous test\n  feedback whenever you change a JavaScript file.\n- To run browser tests in CI or as a pre-push hook, you can set up\n  [Puppeteer](https://developers.google.com/web/tools/puppeteer/),\n  which runs the tests in a headless Chrome browser. That\n  might sound like a lot of overhead, but it's _still\n  slightly faster than using Jest_ (1.2s vs. 1.3s startup\n  time on my machine).\n\n## API Documentation\n\n### Writing Tests\n\n```\nimport {\n  test,\n  expect,\n  is,\n  equals,\n  not,\n  which,\n} from \"@benchristel/taste\"\n```\n\n#### `test(subject: string, TestDefinitions): void`\n\nThe `test` method is the usual way of writing tests in\nTaste. The `TestDefinitions` type is a map of strings\n(test scenario names) to test functions:\n\n```ts\ntype TestDefinitions = {\n  [scenario: string]: () =\u003e void | Promise\u003cvoid\u003e,\n}\n```\n\nTypical usage looks like this:\n\n```js\nimport {test, expect, is} from \"@benchristel/taste\"\n\ntest(\"a Counter\", {\n  \"starts at zero\"() {\n    const c = new Counter()\n    expect(c.value(), is, 0)\n  },\n\n  \"increments once\"() {\n    const c = new Counter()\n    c.increment()\n    expect(c.value(), is, 1)\n  },\n})\n```\n\nWhen the test results are formatted using Taste's built-in\nformatter (`formatTestResultsAsText`), the subject name\nand scenario name will be concatenated for each test. So\nin the above example, you'd see failure messages like:\n\n```\na Counter increments once\n  expect(\n    0,\n    is,\n    1\n  )\n```\n\n#### `expect(actual, predicate: () =\u003e boolean, ...expected): void`\n\nThrows an error if the given `predicate` returns a falsey\nvalue when passed the `expected` and `actual` values.\n\nNote that the `subject` is passed as the _last_ argument\nto the predicate function. This is to accommodate predicates\nthat are designed to be curried.\n\nHere's an example of how you might define a custom\n`isGreaterThan` predicate:\n\n```js\nimport {test, expect, is} from \"@benchristel/taste\"\n\nfunction isGreaterThan(reference, n) {\n  return n \u003e reference\n}\n\ntest(\"rolling a die\", {\n  \"produces a positive number\"() {\n    expect(rollDie(), isGreaterThan, 0)\n  },\n})\n```\n\n#### `is(expected, actual): boolean`\n\nReturns whether its arguments are `===`.\n\n`is` is curried, so the following are equivalent:\n\n```js\nexpect(1 + 1, is, 2)\nexpect(1 + 1, is(2))\n```\n\n#### `equals(expected, actual): boolean`\n\nReturns whether two objects are deeply equal. The rules\nfor comparison are complicated, but work similarly to the\nones in Jest, Jasmine, and other test frameworks you might\nbe familiar with. For examples of behavior, see\n[the tests](https://github.com/benchristel/taste/blob/main/src/predicates.js).\n\n```js\nequals({a: 1}, {a: 1}) // true\nequals({a: 1}, {a: 2}) // false\n```\n\n`equals` is curried, so the following are equivalent:\n\n```js\nexpect(1 + 1, equals, 2)\nexpect(1 + 1, equals(2))\n```\n\n#### `not(p: () =\u003e boolean): () =\u003e boolean`\n\nReturns a negated version of the given predicate `p`; i.e.\n`not(p)` returns true for some arguments iff `p` returns\nfalsey for those arguments.\n\nThe following are equivalent:\n\n```\nexpect(2 + 2, not(is), 5)\nexpect(2 + 2, not(is(5)))\n```\n\n#### `curry(func, name)`\n\nReturns a curried version of the given function.\nPartially-applied functions generated from the curried\nfunction will be pretty-printed by\n`formatTestResultsAsText` in a format that includes the\ngiven `name`.\n\n#### `which(predicate): magic`\n\nGiven a predicate, returns a magical object that `equals`\nany value for which the predicate returns truthy. You can\nuse `which` to customize how objects should be compared when\nusing `equals`—usually to relax some constraint.\n\n```js\nconst isBetween = curry((min, max, n) =\u003e {\n  return n \u003e= min \u0026\u0026 n \u003c= max\n}, \"isBetween\")\n\ntest(\"randomDndCharacter\", {\n  \"generates reasonable ability scores\"() {\n    expect(randomDndCharacter(), equals, {\n      str: which(isBetween(7, 18)),\n      dex: which(isBetween(7, 18)),\n      con: which(isBetween(7, 18)),\n      int: which(isBetween(7, 18)),\n      wis: which(isBetween(7, 18)),\n      cha: which(isBetween(7, 18)),\n    })\n  },\n})\n```\n\n### Running Tests\n\n```\nimport {\n  getAllTests,\n  runTests,\n  formatTestResultsAsText,\n} from \"@benchristel/taste\"\n```\n\n#### `getAllTests(): Array\u003cTest\u003e`\n\nReturns all tests that have been registered via `test()`.\nNilpotent, but may return different results on subsequent\ncalls if there are intervening calls to `test()`.\n\n#### `runTests(Array\u003cTest\u003e): Promise\u003cSuiteResults\u003e`\n\nRuns tests asynchronously and returns their results as a\nmachine-readable object.\n\n#### `formatTestResultsAsText(SuiteResults): string`\n\nFormats test results from `runTests` as a human-readable\nstring.\n\n## Integrating with build tools\n\n### Snowpack\n\nTaste works with [Snowpack](https://www.snowpack.dev/) with\nno special configuration.\n\nI recommend bundling your code for production with\n`@snowpack/plugin-webpack`. This will automatically remove\nany `taste` tests from your production build.\n\nNon-Taste-specific note: if you use snowpack+webpack, do not\nput any JavaScript\ndirectly in your `index.html`; as of this writing, the\nwebpack plugin will not build it correctly and it will not\nbe run in the bundled version of your site. Instead, do\nsomething like this to include your root `index.js` module:\n\n```html\n\u003cscript type=\"module\" src=\"./index.js\"\u003e\u003c/script\u003e\n```\n\n### Vite + React\n\nTaste works great with Vite! I recommend it.\n\nTo ensure your tests run automatically and you do not see\nduplicate test results when components get hot-reloaded, I\nrecommend turning hot module replacement (HMR) **off** for\nfiles that contain tests. You can do that like this:\n\n```js\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react({include: [\"**/*.impl.js\", \"**/*.impl.jsx\"]})],\n  build: {\n    target: \"chrome91\",\n  },\n})\n```\n\nUsing this config, you'd place all of your non-test code\nin `*.impl.js` or `*.impl.jsx` files, and only those files\nwould get hot-reloaded. An alternative would be to use\nsomething like `exclude: [\"**/*.test.js\"]` if you use that\nnaming convention.\n\n### Removing tests from production builds\n\nIf you're using Webpack, Rollup, or most other module\nbundlers, you shouldn't have to do anything special to\nremove Taste tests from the production build of your app.\nIf you find that tests are showing up in bundled code,\nensure that `process.env.NODE_ENV === \"production\"` and your\noptimizer is configured for tree-shaking / dead code\nelimination.\n\n## Development\n\nThis section contains instructions for working on Taste\nitself.\n\nFast test runner:\n\n```\nyarn dev\nopen http://localhost:8080\n```\n\nRobot-friendly test runner:\n\n```\nyarn test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenchristel%2Ftaste","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenchristel%2Ftaste","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenchristel%2Ftaste/lists"}