{"id":15945739,"url":"https://github.com/ig3/test","last_synced_at":"2026-02-20T23:39:12.384Z","repository":{"id":248831544,"uuid":"829898569","full_name":"ig3/test","owner":"ig3","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-24T16:52:33.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-24T07:49:18.519Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/ig3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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,"publiccode":null,"codemeta":null}},"created_at":"2024-07-17T08:01:40.000Z","updated_at":"2024-07-24T16:52:37.000Z","dependencies_parsed_at":"2024-07-17T10:52:08.357Z","dependency_job_id":"c921855b-7c56-4fcb-ae41-646d18fd7156","html_url":"https://github.com/ig3/test","commit_stats":null,"previous_names":["ig3/test"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ig3/test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Ftest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Ftest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Ftest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Ftest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ig3","download_url":"https://codeload.github.com/ig3/test/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ig3%2Ftest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667860,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T23:24:07.480Z","status":"ssl_error","status_checked_at":"2026-02-20T23:24:06.202Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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-07T09:06:01.663Z","updated_at":"2026-02-20T23:39:12.368Z","avatar_url":"https://github.com/ig3.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @ig3/test\n\nA minimal TAP producing module for performing tests.\n\n## Installation\n\n```\n$ npm install -D @ig3/test\n```\n\n## Example\n\n```\n'use strict';\n\nconst t = require('@ig3/test');\n\nt.test('timing test', t =\u003e {\n  t.equal(typeof Date.now, 'function', 'Date.now is a function');\n  const start = Date.now();\n\n  setTimeout(() =\u003e {\n    t.equal(Date.now() - start, 100, 'It is now 100ms after start');\n  }, 100);\n});\n```\n\n## Description\n\nThis module runs one or more tests and produces TAP output according to the\nresults.\n\nA test script may contain one or more tests and test may be nested.\n\nFor example:\n\n```\n'use strict';\n\nconst t = require('@ig3/test');\n\nt.test('test one', t =\u003e {\n  t.pass('test one runs');\n  t.test('sub-test one', t =\u003e {\n    t.pass('sub-test one runs');\n    t.end();\n  });\n  t.test('sub-test two', t =\u003e {\n    t.pass('sub-test two runs');\n    t.end();\n  });\n  t.end();\n});\n\nt.test('test two', t =\u003e {\n  t.pass('test two runs');\n  t.end();\n});\n```\n\n## Methods\n\n### test(desc, cb, opts)\n\nCreate a new test.\n\ndesc: a string description of the test.\n\ncb: the callback function that implements the test. This function will\nbe called synchronously and will receive a single argument: a new test\ncontext with all the methods of the API. Its return value will be ignored,\nexcept that it will be passed to Promise.resolve() and the resulting\npromise will be the return value of the test method. It must call the end\nmethod exactly once.\n\nopts: ignored.\n\nReturns a promise which resolves to the return from the callback.\nTherefore, if the callback itself returns a promise, then the promise\nreturned from the test method will not resolve until after the promise\nreturned from the callback resolves. This feature can be used to run tests\nsequentially.\n\nFor example:\n```\n'use strict';\n\nconst t = require('@ig3/test');\n\n(async () =\u003e {\n  console.log('start');\n  await t.test('test one', t =\u003e {\n    console.log('start test one');\n    return new Promise(fulfill =\u003e {\n      setTimeout(\n        () =\u003e {\n          fulfill();\n          t.end();\n          console.log('end test one');\n        },\n        10\n      );\n    });\n    t.end();\n  });\n  await t.test('test two', t =\u003e {\n    console.log('start test two');\n    return new Promise(fulfill =\u003e {\n      setTimeout(\n        () =\u003e {\n          fulfill();\n          t.end();\n          console.log('end test two');\n        },\n        10\n      );\n    });\n    t.end();\n  });\n  console.log('end');\n})();\n```\n\nProduces the following output:\n\n```\nstart\nstart test one\nend test one\nstart test two\nend test two\nend\nTAP version 13\n# test one\nok 1 test: test one\n# test two\nok 2 test: test two\n\n1..2\n# tests 2\n# pass  2\n\n# ok\n```\n\nThe callback of test two is not called until after the promise returned\nfrom the callback of test one settles.\n\nRemove the awaits and the order of execution and output changes to:\n\n```\nstart\nstart test one\nstart test two\nend\nend test one\nend test two\nTAP version 13\n# test one\nok 1 test: test one\n# test two\nok 2 test: test two\n\n1..2\n# tests 2\n# pass  2\n\n# ok\n```\n\nIn this case, the callback of test two is called after the callback of test\none is called (i.e. in the order of the calls to the test method) but\nbefore the promise returned by the callback of test one settles.\n\nIn both cases, the TAP output is not produced until all tests are complete.\n\n@ig3/test methods are all synchronous. The only deferred proessing is\nproduction of the TAP output, which happens in a callback on the process\nexit event.\n\n### skip(desc, cb, opts)\n\nLike test except that the callback will not be called and the test will be\ntreated as a passing test.\n\n### plan(n)\n\nSet the expected number of tests to be performed.\n\nn: the number of tests expected.\n\nThis can be set for each test context.\n\nFor the global context, set it before calling test or skip. For example:\n\n```\n'use strict';\n\nconst t = require('@ig3/test');\n\nt.plan(3);\n\nt.test('example', t =\u003e {\n  t.pass('it works');\n  t.end();\n});\n```\n\nThis will produce:\n```\nTAP version 13\n# example\nok 1 it works\nok 2 test: example\nnot ok 3 plan 3 != actual 2\n\n1..3\n# tests 3\n# pass  2\n# fail  1\n```\n\nNote that the test itself is evaluated for success or failure, reported in\nthe TAP output and included in the count but the report that actual did not\nmatch the plan is not counted.\n\nIt also works inside tests:\n\n```\n'use strict';\n\nconst t = require('..');\n\nt.test('example', t =\u003e {\n  t.plan(4);\n  t.pass('it works');\n  t.end();\n});\n```\n\nThis produces:\n\n```\nTAP version 13\n# example\nok 1 it works\nnot ok 2 plan 4 != actual 1\nnot ok 3 test: example\n\n1..3\n# tests 3\n# pass  1\n# fail  2\n```\n\nNote that the plan at the end of the TAP output (here `1..3`) is not the\nplan of the single test. The test script could have multiple tests, nested\nor not. The plan within a test is a plan only for that test, including\nnested subtests. It is not written to the TAP output because TAP allows\nonly a single plan, not a plan for each test. But it is evaluated and TAP\noutput reporting a failure is produced if the actual number of tests does\nnot equal the planned number.\n\n\n### pass(desc)\n\nAn assertion that always passes.\n\ndesc: a description of the condition.\n\nThis assertion always passes. It is useful for recording in the TAP output\nthat a block of code was executed as expected.\n\n\n### fail(desc)\n\nAn assertion that always fails.\n\ndesc: a description of the condition.\n\nThis assertion always fails. It is useful in a block of code that should\nnot be reached. For example, the catch of a try or promise, if failure is\nnot expected.\n\n\n### ok(actual, desc)\n\nAn assertion that passes if actual is truthy.\n\nactual: the actual value to be tested.\n\ndesc: a description of the condition.\n\nThis assertion passes if actual is truthy and fails otherwise.\n\n\n### equal(actual, expected, desc)\n\nAn assertion that compares two values using Object.is() and passes or fails\naccording to the result.\n\nactual: the actual value to be tested\n\nexpected: the expected value\n\ndesc: a description of the test condition\n\n\n### deepEqual(actual, expected, desc)\n\nAn assertion that compares two values using  and passes or fails\naccording to the result.\n\nactual: the actual value to be tested\n\nexpected: the expected value\n\ndesc: a description of the test condition\n\n\n### throws(cb[, expected[, desc]])\n\nAssertion passes if the function cf, called with no arguments, throws an exception and expected is not provided or expected matches the thrown exception.\n\ncb: a callback function that will be called with no arguments. \n\nexpected: a string, regular expression or object to be matched against the thrown exception.\n\ndescr: a description of the test condition.\n\n\n### after(cb) / teardown(cb)\n\nRegister a callback to be called when end() is called.\n\ncb: a function that will be called with no arguments.\n\nteardown is an alias for after.\n\nIf multiple callbacks are registered, they will be called in the reverse of\nthe order they were registered (i.e. last in first out).\n\n### end()\n\nThis method indicates the end of the test. It must be called exactly once\nper test. If a test finishes and end() has not been called, an error will\nbe produced and the test will be treated as a failing test. If end() is\ncalled more than once, an error is produced and the test will be treated as\na failing test.\n\n## Motivation\n\nI used [tape](https://www.npmjs.com/package/tape) and\n[multi-tape](https://www.npmjs.com/package/multi-tape) for many years to\ngood effect but recently at installation npm was reporting deprecated and\nunsupported dependencies. This prompted me to review my test tools again.\n\nI tried [node's test runner](https://nodejs.org/api/test.html) and\n[assert](https://nodejs.org/api/assert.html). It was very appealing that\nthey are bundled with node which, among other things, gave me confidence\nthat they would be well designed, well implemented and well supported. They\nare very competent tools but after using them I decided that I don't like\nassertions that throw exceptions and produce no output when they pass. It\nmakes it difficult to distinguished assertions that passed from assertions\nthat were never evaluated due to errors in or misunderstanding of the test\nscripts.\n\nI missed faults and relased code more than once, with all tests passing,\nonly to find later that the tests weren't testing what I thought they were.\nI missed the verboseness of tape, with which every assertion is itself a\ntest reported in the TAP output, making it easy to determine which and how\nmany assertions were evaluated.\n\nSo I returned to reviewing available packages and came across \n[zora](https://www.npmjs.com/package/zora) which appealed due to\nits small size, lack of dependencies and similarity to tape. I tried it and\nit works well. Like tape, its assertions don't throw exceptions and each\nassertion that is evaluated is reported in the TAP output. The\ndocumentation is a bit scant so I spent some time reviewing the code to\nunderstand how it works and what it does and doesn't do. It was easy to\nrewrite existing tests to work with zora.\n\nI might have just used zora but by this time I had spent quite some time\nreviewing various frameworks and how they operated. I was curious about the\npossibility of a framework that worked like tape and zora, but allowed\ncommonly avaailable assertion libraries to be used, like chai or\nnode:assert: wrapping them to catch their exceptions and carry on testing,\nand reporting them all, pass or fail. And I missed the 'pass' assertion of\ntape and a few other very minor details that I hadn't gotten used to with\nzora.\n\nSo I wrote this, as much an exercise in understanding how a test framework\nmight work as an attempt to write the next best thing. It is not yet very\ndeveloped, with only a handful of assertions. It wraps a couple of\nnode:assert assertions but nothing in the API supports doing this with\nexception throwing assertions generally. And maybe it never will: I have a\nbetter appreciation for the diversity of assertions and failure reports.\nReporting pass/fail would be trivial but more helpful diagnostics are not\nso easy to generalize.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fig3%2Ftest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fig3%2Ftest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fig3%2Ftest/lists"}