{"id":15415870,"url":"https://github.com/davidje13/lean-test","last_synced_at":"2026-03-09T20:06:27.967Z","repository":{"id":48914405,"uuid":"425403650","full_name":"davidje13/lean-test","owner":"davidje13","description":"Small plugin-based testing library","archived":false,"fork":false,"pushed_at":"2025-06-07T07:32:10.000Z","size":692,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-12T18:52:09.505Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidje13.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-07T03:28:30.000Z","updated_at":"2025-05-30T12:02:23.000Z","dependencies_parsed_at":"2022-09-09T21:12:16.752Z","dependency_job_id":"77ff9397-643c-4267-8dc1-0ebc06e7b693","html_url":"https://github.com/davidje13/lean-test","commit_stats":{"total_commits":179,"total_committers":1,"mean_commits":179.0,"dds":0.0,"last_synced_commit":"2caa3650c2152903bf73f995e9da73a895c4bbd6"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/davidje13/lean-test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Flean-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Flean-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Flean-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Flean-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidje13","download_url":"https://codeload.github.com/davidje13/lean-test/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Flean-test/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30310055,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T17:10:03.644Z","updated_at":"2026-03-09T20:06:27.950Z","avatar_url":"https://github.com/davidje13.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lean-Test\n\nA testing framework for when you want to test without adding hundreds of dependencies.\n\nRuns tests in NodeJS and/or in browsers.\n\n### Running in NodeJS\n\n```sh\nnpx lean-test\n```\n\n### Running in browsers\n\n```sh\nnpx lean-test --target chrome --target firefox\n```\n\n## Features\n\n- Run tests from the commandline against NodeJS and/or browsers;\n- Natively supports ES6 modules and promises (`async` tests);\n- Transpile source with your chosen tooling before running;\n- Fluent and matcher-style expectations, easy to add custom matchers;\n- Parallel test running;\n- Low overhead (fast tests);\n- No dependencies;\n- Highly extensible plugin and reporter architecture:\n\t- `stdout` / `stderr` / `console` capturing;\n\t- lifecycle methods (`beforeAll` / `beforeEach` / `afterEach` / `afterAll`);\n\t- repeated tests, failure tolerance;\n\t- auto retry failing tests;\n\t- parameterised tests;\n\t- sequential test execution with stop at first failure (for flow testing);\n\t- optional pseudo-random test execution ordering (with a seed to allow repetition);\n\t- configurable test timeout.\n\n## Usage\n\nInstall with:\n\n```sh\nnpm install --save-dev lean-test\n```\n\nOr run without installing:\n\n```sh\nnpx lean-test\n```\n\nAutomatically discovers all `.test.js` / `.spec.js` (and `.mjs`) files by default.\n\nTests can be writen in a variety of ways; the classic \"globals\" approach:\n\n```javascript\n// myThing.spec.mjs\n\ndescribe('my thing', () =\u003e {\n\tit('does something', () =\u003e {\n\t\texpect(3 * 3).equals(9); // fluent-style\n\t});\n\n\tit('does another thing', () =\u003e {\n\t\texpect(2 + 2, equals(4)); // matcher-style\n\t});\n\n\tit('does a thing with promises', async () =\u003e {\n\t\tconst value = await Promise.resolve(7);\n\t\texpect(value, equals(7));\n\t});\n});\n```\n\nOr with shorthand `describe` syntax:\n\n```javascript\n// myThing.spec.mjs\n\ndescribe('my other thing', {\n\t'does stuff'() {\n\t\texpect(2 + 2, equals(4));\n\t},\n\n\tasync 'does async stuff'() {\n\t\tconst value = await Promise.resolve(7);\n\t\texpect(value, equals(7));\n\t},\n\n\t'sub-block': {\n\t\t'more stuff'() {\n\t\t\texpect(1 * 2, isGreaterThan(1));\n\t\t},\n\t},\n});\n```\n\nOr if you prefer, you can avoid globals by using the `export` approach:\n\n```javascript\n// myThing.spec.mjs\n\nexport default ({ describe, it, expect, equals }) =\u003e {\n\tdescribe('my thing', () =\u003e {\n\t\tit('does a thing', () =\u003e {\n\t\t\texpect(3 * 3, equals(9));\n\t\t});\n\t});\n};\n```\n\nOr with shorthand `describe` syntax:\n\n```javascript\n// myThing.spec.mjs\n\nexport default ({ describe, expect }) =\u003e describe('my thing', {\n\t'does a thing'() {\n\t\texpect(3 * 3).equals(9);\n\t},\n});\n```\n\n## Features\n\nMost features are provided by plugins. The standard plugins are enabled by default, and\noffer the following features. You can also create your own plugins if you have bespoke\nneeds.\n\n### fail\n\n```javascript\nfail();\nfail('message');\n```\n\nThrows a `TestAssertionError` (marking the test as failed).\n\n### skip\n\n```javascript\nskip();\nskip('message');\n```\n\nThrows a `TestAssumptionError` (marking the test as skipped).\n\n### expect\n\n```javascript\nexpect(2, equals(2));\nexpect(2).equals(2);\n```\n\nChecks a condition, throwing a `TestAssertionError` if it fails (marking the test as\nfailed).\n\n### assume\n\n```javascript\nassume(2, equals(2));\nassume(2).equals(2);\n```\n\nChecks a condition, throwing a `TestAssumptionError` if it fails (marking the test as\nskipped). Can use all the same matchers as `expect`.\n\n### expect.extend\n\n```javascript\nconst isSeven = () =\u003e (actual) =\u003e {\n\tif (actual === 7) {\n\t\treturn {\n\t\t\tpass: true,\n\t\t\tmessage: 'Expected value not to be 7, but was.',\n\t\t};\n\t} else {\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: `Expected 7, but got ${actual}.`,\n\t\t};\n\t}\n};\n\n// can be used matcher-style immediately:\nexpect(7, isSeven());\n\n// use expect.extend to allow use fluent-style:\nexpect.extend({ isSeven });\n// ...\nexpect(7).isSeven();\n```\n\nGlobally registers new fluent checks.\n\n### expect.poll\n\n```javascript\nawait expect.poll(() =\u003e getThing(), equals(6));\n\nawait expect.poll(() =\u003e getThing(), equals(6), {\n\ttimeout: 1000,\n\tinterval: 100,\n});\n```\n\nPolls a condition repeatedly until it passes, throwing a `TestAssertionError` if the\ntimeout is reached before the condition passes (marking the test as failed).\n\nThe first parameter should be a function with no side-effects (as it will be invoked\nseveral times). The second parameter can be any matcher, including custom matchers.\nNote that `expect.poll` does not support fluent matcher syntax.\n\nYou can optionally provide configuration for the polling behaviour. By default, it\nwill poll every 50 milliseconds for up to 5 seconds.\n\n### mock\n\n```javascript\n// create a mocked function\nconst mockedFunc = mock();\nconst namedMockFunc = mock('my mock');\n\n// spy on existing methods\nconst spyLog = mock(console, 'log');\n\n// configure behaviour\nconst myMock = mock()\n\t.whenCalledWith(1, 'foo').thenReturn(10)\n\t.whenCalledWith(greaterThan(5)).thenThrow(new Error('too much!'));\n```\n\nCreates a mock function, or spies on an existing method.\n\nMocked functions can be configured to return specific values when invoked,\nand can be checked to see if they were called with particular arguments (see\n`hasBeenCalled` / `hasBeenCalledWith` below).\n\nThe extra methods available on mocks and spies are:\n\n- `whenCalled()`:\u003cbr\u003e\n\tBegins a context for configuring behaviour when the function is called.\n\tThe returned object has several fluent-API methods:\n\n\t- `with(...arguments)`:\u003cbr\u003e\n\t\tFilters for invocations with matching arguments (can be literal values,\n\t\tmatchers, or a combination). By default, the arguments are not checked.\n\n\t- `times(n)`:\u003cbr\u003e\n\t\tLimits the current configuration to a fixed number of invocations, after\n\t\twhich it is removed. This can be useful for configuring return values which\n\t\tchange in subsequent invocations. By default, there is no limit.\n\n\t- `once()`:\u003cbr\u003e\n\t\tShorthand for `.times(1)`.\n\n\t- `then(func)`:\u003cbr\u003e\n\t\tConfigures the mock to invoke the given function when an invocation matches\n\t\tthe current configuration. The function will be called with all provided\n\t\targuments, and its return value will be returned, so this acts as a\n\t\tpass-through.\n\t\tAs a convenience, this returns the original mock function, so multiple\n\t\tconfigurations can be chained easily.\n\n\t- `thenReturn(value)`:\u003cbr\u003e\n\t\tShorthand for `.then(() =\u003e value)`\n\n\t- `thenThrow(error)`:\u003cbr\u003e\n\t\tShorthand for `.then(() =\u003e { throw error; })`\n\n\t- `thenResolve(value)`:\u003cbr\u003e\n\t\tShorthand for `.thenReturn(Promise.resolve(value))`\n\n\t- `thenReject(error)`:\u003cbr\u003e\n\t\tShorthand for `.thenReturn(Promise.reject(error))`\n\n\t- `thenCallThrough()`:\u003cbr\u003e\n\t\tConfigures the spy to invoke the original method when an invocation matches\n\t\tthe current configuration. This is the default for spies.\n\t\tAs a convenience, this returns the original mock function, so multiple\n\t\tconfigurations can be chained easily.\n\n- `whenCalledWith(...arguments)`:\u003cbr\u003e\n\tShorthand for `.whenCalled().with(...arguments)`.\n\n- `whenCalledNext()`:\u003cbr\u003e\n\tShorthand for `.whenCalled().times(1)`.\n\n- `returning(value)`:\u003cbr\u003e\n\tShorthand for `.whenCalled().thenReturn(value)`.\n\n- `throwing(error)`:\u003cbr\u003e\n\tShorthand for `.whenCalled().thenThrow(error)`.\n\n- `reset()`:\u003cbr\u003e\n\tResets the mock configuration and recorded invocations.\n\n- `revert()`:\u003cbr\u003e\n\tRemoves the spy, returning the original function (note that this only exists\n\tfor spies; it does not exist for mock functions).\n\n- `getInvocation(index?)` and `getLatestInvocation()`:\u003cbr\u003e\n  Returns an object containing the parameters the mock has been called with.\n\tThe object contains:\n\t- `arguments`: a list of arguments passed when the function was called\n\t- `stack`: a stacktrace for the invocation which can be used for debugging\n\t   (currently generated by `new Error().stack`, but this could change in\n\t\t future versions, and the exact format can change between Node / browser\n\t\t versions) - can be `undefined` if the platform does not support stack\n\t\t traces.\n\nIf multiple `whenCalled*` configurations match an invocation, the first one is\nchosen. For example:\n\n```javascript\nconst fn = mock('my mocked function')\n\t.whenCalledWith(greaterThan(2)).once().thenReturn('a')\n\t.whenCalledWith(lessThan(6)).thenReturn('b')\n\t.whenCalled().thenReturn('c');\n\nfn(1); // b ('b' and 'c' match, so first is chosen)\nfn(4); // a (all match, so first is chosen)\nfn(4); // b ('a' has been used and was configured to only apply once)\nfn(8); // c\n```\n\n### getStdout / getStderr / getOutput\n\n```javascript\nconst textStdout = getStdout();\nconst textStderr = getStderr();\nconst binaryValue = getStdout(true);\n\nconst allOutput = getOutput();\n```\n\nReturns the content of `stdout` / `stderr` captured from the current test so far.\n`getOutput` returns all content to both `stdout` and `stderr` in the order it was\nwritten.\n\nIn the browser, only `getOutput()` is available, which returns all content printed\nto the console as a string. Note that the exact format of logged content is not\nguaranteed (in particular, the format of printed objects may vary and the output\nmay include ANSI escape sequences for setting colours).\n\nAlso note that these may not capture all content; the capturing relies on inspecting\nstack traces, which will not work inside event callbacks such as `setTimeout` etc.\n\nIn NodeJS, `console.*` will produce content in `stdout`.\n\n### ignore\n\n```javascript\nit.ignore('will not run', () =\u003e { /* ... */ });\n\ndescribe.ignore('will not run', () =\u003e { /* ... */ });\n\nit('will not run', { ignore: true }, () =\u003e { /* ... */ });\n\ndescribe('will not run', { ignore: true }, () =\u003e { /* ... */ });\n\nit('will not run', () =\u003e { /* ... */ }, { ignore: true });\n\ndescribe('will not run', () =\u003e { /* ... */ }, { ignore: true });\n```\n\nIgnores a test or block. This will be reported as a skipped test.\n\n### focus\n\n```javascript\nit.focus('only this will run', () =\u003e { /* ... */ });\n\ndescribe.focus('only this will run', () =\u003e { /* ... */ });\n\nit('only this will run', { focus: true }, () =\u003e { /* ... */ });\n\ndescribe('only this will run', { focus: true }, () =\u003e { /* ... */ });\n\nit('only this will run', () =\u003e { /* ... */ }, { focus: true });\n\ndescribe('only this will run', () =\u003e { /* ... */ }, { focus: true });\n```\n\nFocuses a test or block. If any tests or blocks are focused, only the marked tests\nwill run, and the rest will be reported as skipped.\n\n### repeat\n\n```javascript\nit('will run multiple times', { repeat: 3 }, () =\u003e { /* ... */ });\n\nit('will run multiple times', () =\u003e { /* ... */ }, { repeat: 3 });\n```\n\nRuns a test multiple times, expecting every run to succeed. Can also be configured with\na failure tolerance:\n\n```javascript\nit('will run multiple times', { repeat: { total: 3, maxFailures: 1 } }, () =\u003e {\n\t// ...\n});\n```\n\n### retry\n\n```javascript\nit('will retry on failure', { retry: 3 }, () =\u003e { /* ... */ });\n\nit('will retry on failure', () =\u003e { /* ... */ }, { retry: 3 });\n```\n\nRuns a test multiple times until it succeeds. If any attempt succeeds, the test is\nconsidered a success.\n\n### parameters\n\n```javascript\nit('will run with multiple parameters', { parameters: [1, 2] }, (v) =\u003e { /* ... */ });\n\nit('will run with multiple parameters', (v) =\u003e { /* ... */ }, { parameters: [1, 2] });\n```\n\nRuns a test multiple times with different parameters. There are a variety of ways\nto set parameters:\n\n```javascript\n// call with (1), (2):\n{ parameters: [1, 2] }\n\n// multiple parameters:\n// call with (1, 2), (3, 4):\n{ parameters: [[1, 2], [3, 4]] }\n\n// parameter matrix:\n// call with (1, 3), (1, 4), (2, 3), (2, 4):\n{ parameters: [new Set([1, 2]), new Set([3, 4])] }\n\n// parameter matrix with multiple parameters:\n// call with (1, 'a', 3), (1, 'a', 4), (2, 'b', 3), (2, 'b', 4):\n{ parameters: [new Set([[1, 'a'], [2, 'b']]), new Set([3, 4])] }\n```\n\nYou can also set a `parameterFilter` to exclude specific combinations of\nparameters:\n\n```javascript\n// parameter matrix with filter:\n// call with (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2):\n{\n\tparameters: [new Set([1, 2, 3]), new Set([1, 2, 3])],\n\t// do not allow both parameters set to the same value\n\tparameterFilter: (a, b) =\u003e (a !== b),\n}\n```\n\nBy default, the tests will be named using a stringified version of all the\nparameters, but if you provide an object with a `name` property, that name\nwill be used instead:\n\n```javascript\n{ parameters: [\n\t{ name: 'my first test', v1: 1, v2: 2 },\n\t{ name: 'my second test', v1: 1, v2: 2 },\n] }\n```\n\n### timeout\n\n```javascript\nit('will time out', { timeout: 1000 }, () =\u003e { /* ... */ });\n\nit('will time out', () =\u003e { /* ... */ }, { timeout: 1000 });\n```\n\nFails the test if it takes longer than the configured time (in milliseconds) to run.\n\nNote that this will not be able to prevent \"busy loops\" such as `while (true) {}`,\nand will not terminate tasks which are running (so the test code may continue to\nexecute even though the timeout has triggered), but any further exceptions will be\nignored.\n\n### stopAtFirstFailure\n\n```javascript\ndescribe('my flow test', { stopAtFirstFailure: true }, () =\u003e {\n\t// tests here\n});\n\ndescribe('my flow test', () =\u003e {\n\t// tests here\n}, { stopAtFirstFailure: true });\n```\n\nStops executing tests within the current block if one fails (subsequent tests will be\nmarked as skipped).\n\n### Lifecycle Hooks\n\n```javascript\ndescribe('lifecycle', () =\u003e {\n\tbeforeAll('optional name', () =\u003e {\n\t\t// ...\n\t});\n\n\tbeforeEach('optional name', () =\u003e {\n\t\t// ...\n\t});\n\n\tafterEach('optional name', () =\u003e {\n\t\t// ...\n\t});\n\n\tafterAll('optional name', () =\u003e {\n\t\t// ...\n\t});\n\n\t// tests here\n});\n```\n\nRegisters execution listeners which will run before and after the whole block, or before\nand after each test within the block. Multiple hooks will be executed in the order they\nare defined. Nested blocks will be executed from outermost to innermost for `before`, and\ninnermost to outermost for `after`.\n\nAll methods can be asynchronous.\n\n`before` hooks can also return a function which will act like a corresponding `after`\nhook:\n\n```javascript\ndescribe('lifecycle', () =\u003e {\n\tlet server;\n\n\tbeforeAll('launch server', async () =\u003e {\n\t\tserver = await runServer();\n\n\t\t// teardown:\n\t\treturn async () =\u003e {\n\t\t\tawait server.close();\n\t\t};\n\t});\n\n\t// tests here\n});\n```\n\nYou can also set test parameters from a `beforeAll` or `beforeEach` hook.\nThese parameters will be available to all tests which are inside the hook's\nscope.\n\n```javascript\ndescribe('lifecycle', () =\u003e {\n\tconst SERVER = beforeEach('launch server', async ({ setParameter }) =\u003e {\n\t\tconst server = await runServer();\n\t\tsetParameter(server);\n\n\t\treturn () =\u003e server.close();\n\t});\n\n\tit('does a thing', ({ [SERVER]: server }) =\u003e {\n\t\tserver.get('foobar');\n\t\t// ...\n\t});\n});\n```\n\nThis pattern can be useful for fully decoupling tests from global state, allowing\nthem to run in parallel.\n\nThese parameters are available in the first argument passed to the tests (see the\ndestructuring example above). They are also available to other lifecycle hooks in\nthe same way.\n\nTo make parameters type-safe in TypeScript, you can use:\n\n```typescript\ndescribe('lifecycle', () =\u003e {\n\tconst SERVER = beforeEach\u003cServer\u003e('launch server', async ({ setParameter }) =\u003e {\n\t\tconst server = await runServer();\n\t\tsetParameter(server);\n\n\t\treturn () =\u003e server.close();\n\t});\n\n\tit('does a thing', ({ getTyped }) =\u003e {\n\t\t// getTyped is always available and just retrieves the corresponding parameter,\n\t\t// but typed according to the type of the key. Functionally this is the same as\n\t\t// the destructuring in the example above.\n\t\tconst server = getTyped(SERVER);\n\t\tserver.get('foobar');\n\t\t// ...\n\t});\n});\n```\n\nThe legacy method `addTestParameter` is also available, but should be avoided as\nit is not possible to make type-safe, is not made available to other lifecycle\nhooks, and will be removed in the future.\n\nFinally, it is possible to get the current test path in `beforeEach` and\n`afterEach`, and as much of the path as is known in `beforeAll` and `afterAll`:\n\n```javascript\ndescribe('lifecycle', () =\u003e {\n\tbeforeEach('record info', ({ testPath }) =\u003e {\n\t\t// records: \"path/mytest.spec.mjs \u003e lifecycle \u003e my test\"\n\t\tmyLog.append(testPath.join(' \u003e '));\n\t});\n\n\tit('my test', () =\u003e { /* ... */ });\n});\n```\n\n## Standard Matchers\n\n- `equals(value)`:\u003cbr\u003e\n\tRecursively checks for equality.\n\n- `same(value)`:\u003cbr\u003e\n\tChecks strict (`===`) identity.\n\n- `isInstanceOf(class)`:\u003cbr\u003e\n\tChecks `instanceof`.\n\n- `not(expectation)`:\u003cbr\u003e\n\tNegates another matcher.\u003cbr\u003e\n\te.g. `expect(7, not(equals(4)))`\n\n- `any()`:\u003cbr\u003e\n\tAlways matches. The negation `not(any())` always fails. Useful as a sub-matcher.\n\n- `matches(regexp)`:\u003cbr\u003e\n\tChecks if a string matches the given regular expression.\n\n- `withMessage(message, expectation)`:\u003cbr\u003e\n\tCustomises the error message of another matcher.\u003cbr\u003e\n\te.g. `expect(7, withMessage('hmm, not 7', equals(7)))`\n\n- `isTrue()`:\u003cbr\u003e\n\tChecks if `=== true`.\n\n- `isFalse()`:\u003cbr\u003e\n\tChecks if `=== false`.\n\n- `isTruthy()`:\u003cbr\u003e\n\tChecks if the value is truthy (`Boolean(value) === true`).\n\n- `isFalsy()`:\u003cbr\u003e\n\tChecks if the value is falsy (`Boolean(value) === false`).\n\n- `isNull()`:\u003cbr\u003e\n\tChecks if `=== null`.\n\n- `isUndefined()`:\u003cbr\u003e\n\tChecks if `=== undefined`.\n\n- `isNullish()`:\u003cbr\u003e\n\tChecks if the value is nullish `value === null || value === undefined`.\n\n- `isGreaterThan(value)`:\u003cbr\u003e\n\tChecks if `\u003e value`.\n\n- `isLessThan(value)`:\u003cbr\u003e\n\tChecks if `\u003c value`.\n\n- `isGreaterThanOrEqual(value)`:\u003cbr\u003e\n\tChecks if `\u003e= value`.\n\n- `isLessThanOrEqual(value)`:\u003cbr\u003e\n\tChecks if `\u003c= value`.\n\n- `isNear(value[, precision])`:\u003cbr\u003e\n\tChecks if near `value`. By default, the comparison checks to 2 decimal places, but\n\tyou can configure this by providing an explicit precision. The types of precision\n\tsupported are:\n\t- `{ tolerance: n }` sets an explicit permitted range (+/- `n`)\n\t- `{ decimalPlaces: n }` sets an explicit number of decimal places to check\n\t\t(+/- `0.5 * 10^-n`)\n\n- `resolves(expectation)`:\u003cbr\u003e\n\tChecks if the given function or promise returns a value which matches the given\n\texpectation (sub-matcher). `expectation` can also be a literal value, in which case\n\tit behaves as if `equals(expectation)` were used. If no `expectation` is given, this\n\tjust checks that the funtion returns (does not throw).\n\n\tNote that if promises are involved, the `expect` call should be awaited:\n\n\t```javascript\n\tawait expect(myPromise, resolves(equals(7)));\n\t```\n\n- `throws(expectation)`:\u003cbr\u003e\n\tChecks if the given function or promise throws a value which matches the given\n\texpectation (sub-matcher). `expectation` can also be a literal string, in which case\n\tit checks if the thrown `Error` message contains the given string. If no `expectation`\n\tis given, this just checks that the funtion throws.\n\n\tNote that if promises are involved, the `expect` call should be awaited:\n\n\t```javascript\n\tawait expect(myPromise, throws('oops'));\n\t```\n\n- `hasLength(expectation)`:\u003cbr\u003e\n\tChecks if the value (an array, `Set`, `Map`, etc.) has a length matching the given\n\texpectation (sub-matcher). `expectation` can also be a literal number, in which case\n\tit behaves as if `equals(expectation)` were used. If no `expectation` is given, this\n\tjust checks that the value has a `length` or `size` property.\n\n- `isEmpty()`:\u003cbr\u003e\n\tChecks if the value (an array, `Set`, `Map`, etc.) has no items.\n\n- `contains(sub)`:\u003cbr\u003e\n\tChecks if the value (a string, array, or `Set`) contains the given substring or\n\tsub-element.\n\n- `startsWith(sub)`:\u003cbr\u003e\n\tChecks if a string starts with the given substring.\n\n- `endsWith(sub)`:\u003cbr\u003e\n\tChecks if a string ends with the given substring.\n\n- `isListOf(...elements)`:\u003cbr\u003e\n\tChecks if the value contains the given elements in the listed order. Elements can be\n\tliteral or matchers (these can be mixed).\n\n- `hasProperty(name[, expectation])`:\u003cbr\u003e\n\tChecks if the value (any type) contains a property of the given name, optionally\n\tmatching the given expectation. If no `expectation` is given, this just checks that\n\tthe property exists on the object (using `hasOwnProperty`).\n\n- `hasBeenCalled()`:\u003cbr\u003e\n\tChecks that a mocked function has been invoked since being mocked.\n\n- `hasBeenCalledWith(...arguments)`:\u003cbr\u003e\n\tChecks that a mocked function has been invoked since being mocked, with the given\n\targuments (which can be literal, matchers, or a mix).\n\n## CLI flags\n\nThe `lean-test` executable can be configured in various ways:\n\n- `--preprocess \u003ctool\u003e` / `-c \u003ctool\u003e`:\u003cbr\u003e\n\tApplies the specified tool as a preprocessor for all source files (excluding\n\t`node_modules` sources).\n\n\tCurrent supported tooling:\n\t- `babel`:\u003cbr\u003e\n\t\tThe Babel transpiler. Looks for a `babel.config.*` or `.babelrc.*` file, or the\n\t\t`babel` section of a `package.json` for configuration.\n\t\tRequires `babel` (`npm install --save-dev @babel/core`).\n\n\t- `rollup`:\u003cbr\u003e\n\t\tThe Rollup bundler. Looks for a `rollup.config.js` file for configuration (uses\n\t\tthe first entry if multiple configurations are defined).\n\t\tRequires `rollup` (`npm install --save-dev rollup`).\n\n\t- `tsc`:\u003cbr\u003e\n\t\tThe Typescript transpiler. Looks for a `tsconfig.json` file for configuration.\n\t\tRequires `typescript` (`npm install --save-dev typescript`).\n\n\t- `webpack`:\u003cbr\u003e\n\t\tThe webpack bundler. Looks for a `.webpack/webpackfile`,\n\t\t`.webpack/webpack.config.*`, or `webpack.config.*` file for configuration (uses\n\t\tthe first entry if multiple configurations are defined).\n\t\tRequires `webpack` (`npm install --save-dev webpack`).\n\n- `--target \u003cname\u003e` / `-t \u003cname\u003e` / environment `TARGET=\u003cname\u003e`:\u003cbr\u003e\n\tRuns the tests in the chosen target. Currently `node`, `chrome` and `firefox` are\n\tsupported, or use `url` then open the printed URL in any browser to start the tests.\n\n\tYou can also use a WebDriver-compatible server (e.g. Selenium) by setting the\n\t`WEBDRIVER_HOST` environment variable, or `WEBDRIVER_HOST_\u003cBROWSER\u003e` to set it for\n\ta specific browser. For example:\n\n\t```sh\n\t# alternatively this could be a grid, for example\n\tdocker run -d -p 4444:4444 --shm-size=\"2g\" selenium/standalone-chrome\n\texport WEBDRIVER_HOST=localhost:4444\n\tlean-test --target=chrome\n\t```\n\n\t```sh\n\tdocker run -d -p 4444:4444 --shm-size=\"2g\" selenium/standalone-chrome\n\tdocker run -d -p 4445:4444 --shm-size=\"2g\" selenium/standalone-firefox\n\texport WEBDRIVER_HOST_CHROME=localhost:4444\n\texport WEBDRIVER_HOST_FIREFOX=localhost:4445\n\tlean-test --target=chrome --target=firefox\n\t```\n\n\tIf you are using a remote browser, you will also need to set\n\t`--host 0.0.0.0` (or equivalently `TESTRUNNER_HOST=0.0.0.0`) so that the test server\n\tis accessible to the browser.\n\n\tIf you specify more than one target, the tests will be run in all targets in\n\tparallel, with the results containing one section per target:\n\n\t```sh\n\tlean-test --target=node,chrome,firefox\n\t# or\n\tlean-test --target node --target chrome --target firefox\n\t```\n\n- `--port \u003cnumber\u003e` / environment `TESTRUNNER_PORT=\u003cnumber\u003e`:\u003cbr\u003e\n\tSets an explicit port number for the browser-based tests to use. By default this is\n\t`0` (pick random available port). This only takes effect if the tests are running in a\n\tbrowser.\n\n- `--host \u003cname\u003e` / environment `TESTRUNNER_HOST=\u003cname\u003e`:\u003cbr\u003e\n\tSets an explicit host name for the browser-based tests to use. By default this is\n\t`127.0.0.1` (local loopback). This only takes effect if the tests are running in a\n\tbrowser.\n\tYou may want to change this setting if you need to run tests in a browser running on a\n\tdifferent computer on the same network (e.g. by specifying `0.0.0.0` to make it\n\tavailable over the network).\n\n- `--import-map` / environment `IMPORT_MAP=true`:\u003cbr\u003e\n\tGenerates an [import map](https://github.com/WICG/import-maps) for `node_modules`\n\timports. This only takes effect if the tests are running in a browser.\n\tThis allows non-relative imports like `import foo from 'foo';`, which will be\n\tresolved by looking in `node_modules`, which means some projects can be tested\n\twithout needing a compilation / transpilation stage.\n\tNote that import maps are currently supported by Chrome, Firefox, and Edge.\n\n- `--parallel-suites` / `--parallel` / `-p` / environment `PARALLEL_SUITES=true`:\u003cbr\u003e\n\tRuns test suites in parallel. This is generally recommended unless the code being\n\ttested may cause tests in different files to interfere with each other (e.g. uses\n\tsingletons or global state).\n\n- `--random-seed \u003cseed\u003e` / `-s \u003cseed\u003e` / environment `RANDOM_SEED=\u003cseed\u003e`:\u003cbr\u003e\n\tRandomise the order of the test in a deterministic (repeatable) way. The seed can\n\tbe a 32-character hexadecimal string, or the word `random` to pick a random seed\n\t(the chosen seed will be printed at the end of the test run). If you want to\n\tensure that your tests do not depend on execution order, you can add\n\t`--random-seed=random` to your standard test run command, then if an error appears,\n\tyou can use the seed it prints to re-run the tests in that order during debugging.\n\n- `--include \u003cpattern\u003e` / `-i \u003cpattern\u003e`:\u003cbr\u003e\n\tConfigures the search pattern glob. Can be set multiple times. By default, this is\n\t`**/*.{spec|test}.*`.\n\n- `--exclude \u003cpattern\u003e` / `-x \u003cpattern\u003e`:\u003cbr\u003e\n\tConfigures the exclusion pattern glob. Can be set multiple times.\n\tNote that `**/node_modules` and `**/.*` will always be excluded unless\n\t`--no-default-exclude` is specified.\n\n- `--no-default-exclude`:\u003cbr\u003e\n\tBy default, `**/node_modules` and `**/.*` are always excluded. Setting this flag\n\tallows them.\n\n- `--parallel-discovery` / `-P` / environment `PARALLEL_DISCOVERY=true`:\u003cbr\u003e\n\tRuns test discovery in parallel. This may be slightly faster than the default\n\t(synchronous) discovery, but may fail with an error depending on the environment\n\tand the test complexity.\n\nAfter the flags, you can provide one or more directories which will be used as starting\npoints for scanning for tests (by default the current working directory is used).\n\nExample:\n\n```sh\n# Run all js/mjs files in the 'tests' folder, using Chrome:\nlean-test --parallel --target chrome -i '**/*.{js|mjs}' tests\n```\n\n## Troubleshooting\n\n### Chrome crashes with \"session deleted because of page crash\"\n\nThis typically means that Chrome is running in a docker instance which has limited\nshared memory (\"shm\") available. If possible, configure the container with more space:\n\n```sh\ndocker run -d -p 4444:4444 --shm-size=\"2g\" selenium/standalone-chrome\n```\n\nIf this is not possible (e.g. when using GitLab CI), you can set the\n`WEBDRIVER_DISABLE_SHM` environment variable, which will add `--disable-dev-shm-usage`\nto the requested Chrome capabilities.\n\n## CI Examples for Browser testing\n\nThese examples assume that `package.json` contains something like:\n\n```json\n{\n  \"scripts\": {\n    \"test\": \"lean-test --target=chrome,firefox\"\n  }\n}\n```\n\n### GitLab CI/CD\n\n```yaml\nbuild_and_test:\n  image: node:16\n  services:\n  - name: selenium/standalone-firefox\n    alias: firefox\n  - name: selenium/standalone-chrome\n    alias: chrome\n  variables:\n    WEBDRIVER_DISABLE_SHM: 'true'\n    WEBDRIVER_HOST_CHROME: chrome:4444\n    WEBDRIVER_HOST_FIREFOX: firefox:4444\n    TESTRUNNER_HOST: '0.0.0.0'\n  script:\n  - npm install-test\n```\n\n### GitHub Actions\n\n```yaml\nname: Test\non: [push]\n\njobs:\n  build_and_test:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Install Node\n      uses: actions/setup-node@v2\n      with:\n        node-version: '16'\n    - name: Test\n      run: npm install-test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidje13%2Flean-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidje13%2Flean-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidje13%2Flean-test/lists"}