{"id":14965757,"url":"https://github.com/chase-moskal/cynic","last_synced_at":"2025-10-25T12:30:43.231Z","repository":{"id":91093442,"uuid":"45376252","full_name":"chase-moskal/cynic","owner":"chase-moskal","description":"simple async run-anywhere js testing framework","archived":false,"fork":false,"pushed_at":"2023-03-25T21:03:03.000Z","size":298,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-31T07:11:11.645Z","etag":null,"topics":["browser","commonjs","esm","esmodules","javascript","node","puppeteer","test-automation","test-runner","testing","testing-framework","testing-tools","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/chase-moskal.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}},"created_at":"2015-11-02T06:31:04.000Z","updated_at":"2024-05-12T09:43:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"e2d9f0ab-8ae5-4c0a-8526-083776e108bd","html_url":"https://github.com/chase-moskal/cynic","commit_stats":{"total_commits":96,"total_committers":1,"mean_commits":96.0,"dds":0.0,"last_synced_commit":"178f2050989a47246b772653fe57699bf1776613"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chase-moskal%2Fcynic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chase-moskal%2Fcynic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chase-moskal%2Fcynic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chase-moskal%2Fcynic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chase-moskal","download_url":"https://codeload.github.com/chase-moskal/cynic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238137862,"owners_count":19422715,"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":["browser","commonjs","esm","esmodules","javascript","node","puppeteer","test-automation","test-runner","testing","testing-framework","testing-tools","typescript"],"created_at":"2024-09-24T13:35:15.288Z","updated_at":"2025-10-25T12:30:37.933Z","avatar_url":"https://github.com/chase-moskal.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# 🧐 cynic\n\n## async testing framework for es modules\n\n- cynic is designed to be dirt-simple, because i'm sick of overcomplicated testing frameworks\n- the test suites are just nested async functions\n- the whole framework is just simple es modules that run anywhere: node, browser, puppeteer, deno\n- no magic assumptions are made about or foisted onto the environment: the assertion library and everything else is just simply imported like from any other module\n- examples here are shown in typescript, but of course you can use vanilla js\n\n## let's get cynical, and make a damn test suite!\n\n1. install cynic into your project\n\n    ```sh\n    npm install --save-dev cynic\n    ```\n\n2. write a test suite, `example.test.ts`\n\n    ```ts\n    import {Suite, assert, expect} from \"cynic\"\n\n    export default \u003cSuite\u003e{\n      \"alpha system\": {\n        \"can sum two numbers (boolean return)\": async() =\u003e {\n          const a = 1\n          const b = 2\n          // no assertion library required:\n          // simply returning false, or throwing, will fail a test\n          return (a + b) === 3\n        },\n        \"can sum three numbers (assert)\": async() =\u003e {\n          const a = 1\n          const b = 2\n          const c = 3\n          // benefits of 'assert'\n          //  - you get a stack trace\n          //  - you can provide a custom message for each failure\n          assert((a + b + c) === 6, `sum is wrong`)\n        }\n      },\n      \"bravo system\": {\n        \"can multiply numbers (expect)\": async() =\u003e {\n          const a = 2\n          const b = 3\n          // benefits of 'expect'\n          //  - you get a stack trace\n          //  - cynic tries to invent a message about the failure\n          expect(a * b).equals(6)\n          expect(a * b * a).equals(12)\n        }\n      }\n    }\n    ```\n\n## now run it!\n\n- ### **you can run the suite file through the cynic cli**\n\n    ```sh\n    # run your tests in node\n    cynic node example.test.js\n\n    # run your tests in browser\n    cynic browser example.test.js\n\n    # run your tests in puppeteer (headless browser)\n    cynic puppeteer example.test.js\n\n    # use node debugger\n    node inspect node_modules/cynic/dist/cli.js node example.test.js\n    ```\n\n    cynic executes the default export as a test suite\n\n    optional arguments for *all* runtimes:\n    - `--label=\"test suite\"` — the report title\n\n    optional arguments for *browser* and *puppeteer* runtimes:\n    - `--open=false` — true to prompt open your default browser\n    - `--port=8021` — run the server on a different port\n    - `--origin=\"http://localhost:8021\"` — connect to the server via an alternative url (mind the port number!)\n    - `--cynic-path=node_modules/cynic` — use an alternative path to the cynic library's root\n    - `--importmap-path=./dist/importmap.json` — provide an import map for your test suites\n\n    if puppeteer isn't running properly, see puppeteer's [troubleshooting.md](https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md)\n\n- ### **or you can just execute your test suite, manually, anywhere**\n\n    this should work anywhere you can import an es module\n\n    ```ts\n    import {test} from \"cynic\"\n    import suite from \"./example.test.js\"\n\n    ;(async() =\u003e {\n\n      // run the test suite\n      const {report, ...stats} = await test(\"example suite\", suite)\n\n      // emit the report text to console\n      console.log(report)\n\n      // handle results programmatically\n      if (stats.failed === 0) console.log(\"done\")\n      else console.log(\"failed!\")\n\n      // returns stats about the test run results\n      console.log(stats)\n\n    })()\n    ```\n\n    see which stats are available in the `Stats` interface in [types.ts](./source/types.ts)\n\n## so what do the console reports look like?\n\n- **report: successful run**\n\n    ```\n    cynic example suite\n    \n      ▽ examples\n        ▽ alpha system\n          ✓ can sum two numbers (boolean return)\n          ✓ can sum three numbers (assertion)\n        ▽ bravo system\n          ✓ can multiply numbers (expectation)\n    \n    0 failed tests\n    0 thrown errors\n    3 passed tests\n    3 total tests\n    0.00 seconds\n    ```\n\n- **report: a test returns false**  \n    return false to indicate a failed test\n\n    ```\n    cynic example suite\n    \n      ▽ examples\n        ▽ alpha system\n    \n    ═════ ✘ can sum two numbers (boolean return)\n    \n        ▽ bravo system\n\n    ✘ can sum two numbers (boolean return) — failed\n\n    1 FAILED tests\n    0 thrown errors\n    2 passed tests\n    3 total tests\n    0.00 seconds\n    ```\n\n- **report: a test throws**  \n    a thrown string or error will be shown as the failure reason\n\n    ```\n    cynic example suite\n\n      ▽ examples\n        ▽ alpha system\n\n    ═════ ✘ can sum two numbers (boolean return)\n    ――――――― arithmetic failed for interesting reasons\n\n        ▽ bravo system\n\n    ✘ can sum two numbers (boolean return) — arithmetic failed for interesting reasons\n\n    1 FAILED tests\n    1 thrown errors\n    2 passed tests\n    3 total tests\n    0.00 seconds\n    ```\n\n- **report: a test fails an assertion**  \n    assertions will display a stack trace, and optional custom message\n\n    ```\n    cynic example suite\n\n      ▽ examples\n        ▽ alpha system\n    \n    ═════ ✘ can sum three numbers (assertion)\n    ――――――― CynicBrokenAssertion: sum is wrong\n              at assert (file:///work/cynic/dist/assert.js:7:15)\n              at can sum three numbers (assertion) (file:///work/cynic/dist/internals/example.test.js:13:20)\n              at execute (file:///work/cynic/dist/internals/execute.js:13:34)\n              [...]\n\n        ▽ bravo system\n\n    ✘ can sum three numbers (assertion) — CynicBrokenAssertion: sum is wrong\n\n    1 FAILED tests\n    1 thrown errors\n    2 passed tests\n    3 total tests\n    0.00 seconds\n    ```\n\n- **report: a test fails an expectation**  \n    stack trace is provided, and a failure reason is generated automatically\n\n    ```\n    cynic example suite\n    \n      ▽ examples\n        ▽ alpha system\n        ▽ bravo system\n\n    ═════ ✘ can multiply numbers (expectation)\n    ――――――― CynicBrokenExpectation: expect(7).equals(6): not equal, should be\n              at composite (file:///work/cynic/dist/expect.js:46:19)\n              at Object.equals (file:///work/cynic/dist/expect.js:25:125)\n              at can multiply numbers (expectation) (file:///work/cynic/dist/internals/example.test.js:20:39)\n              at execute (file:///work/cynic/dist/internals/execute.js:13:34)\n              [...]\n\n    ✘ can multiply numbers (expectation) — CynicBrokenExpectation: expect(7).equals(6): not equal, should be\n\n    1 FAILED tests\n    1 thrown errors\n    2 passed tests\n    3 total tests\n    0.00 seconds\n    ```\n\n## hot tips for big brains\n\n- use object nesting to group and organize tests arbitrarily\n\n    ```ts\n    import {Suite} from \"cynic\"\n    export default \u003cSuite\u003e{\n      \"nested tests\": {\n        \"more nested\": {\n          \"exceedingly nested\": {\n            \"it works\": async() =\u003e true\n          }\n        }\n      }\n    }\n    ```\n\n- you can just throw strings as assertions\n\n    ```ts\n    import {Suite} from \"cynic\"\n    export default \u003cSuite\u003e{\n      \"assertions and expectations\": async() =\u003e {\n        const example = \"abc\"\n\n        // let's call it \"the spartan assertion\"\n        if (!example.includes(\"b\"))\n          throw `expected example to include \"b\"`\n\n        return true\n      }\n    }\n    ```\n\n- or you can use the handy `assert` function to do that, you get stack traces\n\n    ```ts\n    import {Suite, assert} from \"cynic\"\n    export default \u003cSuite\u003e{\n      \"using 'assert'\": async() =\u003e {\n        const example = \"abc\"\n        assert(example === \"abc\", `example must equal \"abc\"`)\n        assert(example.includes(\"b\"), `example should include \"b\"`)\n      }\n    }\n    ```\n\n- or you can also use the experimental new `expect` api, you get auto-generated messages and stack traces\n\n    ```ts\n    import {Suite, expect} from \"cynic\"\n    export default \u003cSuite\u003e{\n      \"using 'expect'\": async() =\u003e {\n        const example = \"abc\"\n        expect(example).defined()\n        expect(example).equals(\"abc\")\n      }\n    }\n    ```\n\n- a suite or test can return another suite or test — *easy setups!*\n\n    ```ts\n    export default \u003cSuite\u003e(async() =\u003e {\n\n      // doing some async setup\n      const myFile = await loadFile(\"myfile.json\")\n\n      // returning more tests\n      return {\n        \"group of tests\": {\n          \"my file exists\": async() =\u003e {\n            return !!myFile\n          }\n        }\n      }\n    })\n    ```\n\n## food for thought\n\n- 🥃 chase moskal made this with open source love. please contribute!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchase-moskal%2Fcynic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchase-moskal%2Fcynic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchase-moskal%2Fcynic/lists"}