{"id":43741635,"url":"https://github.com/NullVoxPopuli/ember-vitest","last_synced_at":"2026-02-17T00:00:44.608Z","repository":{"id":315812566,"uuid":"1060458016","full_name":"NullVoxPopuli/ember-vitest","owner":"NullVoxPopuli","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-09T02:31:05.000Z","size":362,"stargazers_count":5,"open_issues_count":6,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-09T02:54:54.465Z","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/NullVoxPopuli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-20T00:33:58.000Z","updated_at":"2026-01-08T13:38:57.000Z","dependencies_parsed_at":"2025-09-20T23:19:21.279Z","dependency_job_id":"cacc8819-618f-4aa0-9840-5664d1848388","html_url":"https://github.com/NullVoxPopuli/ember-vitest","commit_stats":null,"previous_names":["nullvoxpopuli/ember-vitest"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/NullVoxPopuli/ember-vitest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-vitest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-vitest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-vitest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-vitest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NullVoxPopuli","download_url":"https://codeload.github.com/NullVoxPopuli/ember-vitest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fember-vitest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29525435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T21:45:09.491Z","status":"ssl_error","status_checked_at":"2026-02-16T21:44:58.452Z","response_time":115,"last_error":"SSL_read: 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":"2026-02-05T12:00:31.886Z","updated_at":"2026-02-17T00:00:44.602Z","avatar_url":"https://github.com/NullVoxPopuli.png","language":"JavaScript","funding_links":[],"categories":["Testing"],"sub_categories":["Runners"],"readme":"# ember-vitest\n\nember \u003c-\u003e vitest integration\n\n- browser testing is first class\n- pause test execution for UI debugging purposes (and without pausing JS execution)\n- continue using familiar helpers as you would in qunit\n\n## Install\n\n```\nnpm add --save-dev ember-vitest vitest @vitest/browser @vitest/browser-webdriverio\n```\n\n## Usage\n\nAfter [#setup](#setup), run:\n\n```bash\npnpm vitest\n# or\nnpm exec vitest\n```\n\n### Using vitest's `test`\n\n\u003e [!NOTE]\n\u003e We use [`expect.soft`](https://vitest.dev/api/expect.html#soft) for better ergonomics in test reporting so when a test starts failing we can get the whole picture of the test at once, rather than have to address one failure at a time.\n\n#### Rendering Tests\n\nA basic test can be written like:\n\n```gjs\nimport { describe, test, expect as hardExpect } from \"vitest\";\nimport { setupRenderingContext } from \"ember-vitest\";\n\nconst expect = hardExpect.soft;\n\ndescribe(\"example\", () =\u003e {\n  test(\"it works\", async () =\u003e {\n    await using ctx = setupRenderingContext();\n\n    await ctx.render(\u003ctemplate\u003ehello there\u003c/template\u003e);\n\n    expect(ctx.element.textContent).contains(\"hello there\");\n  });\n```\n\nAnd interactivity can be done via [`@testing-library/dom`](https://testing-library.com/docs/queries/about) and [`testing-library-ember`](https://github.com/nullvoxpopuli/testing-library-ember/) (which provides settled-state integration with testing-library's `fireEvent` utility, so you don't have to even \"wait\" or check for things in a loop. This cleans up tests significantly when async rendering is involved.)\n\n```gjs\nimport { trackedObject } from \"@ember/reactive/collections\";\nimport { describe, test, expect as hardExpect } from \"vitest\";\nimport { screen } from \"@testing-library/dom\";\nimport { fireEvent } from \"testing-library-ember\";\n\nimport { setupRenderingContext } from \"ember-vitest\";\n\nconst expect = hardExpect.soft;\n\ndescribe(\"example\", () =\u003e {\n  test(\"has interactivity\", async () =\u003e {\n    await using ctx = setupRenderingContext();\n\n    const state = trackedObject({ value: 0 });\n    const increment = () =\u003e state.value++;\n\n    await ctx.render(\n      \u003ctemplate\u003e\n        \u003cbutton role=\"button\" onclick={{increment}}\u003eclick me\u003c/button\u003e\n        \u003coutput\u003e{{state.value}}\u003c/output\u003e\n      \u003c/template\u003e,\n    );\n\n    let btn = screen.getByText(/click me/);\n    let out = ctx.element.querySelector(\"output\");\n\n    expect(btn).toBeTruthy();\n    expect(out.textContent).toBe(\"0\");\n\n    await fireEvent.click(btn);\n    expect(out.textContent).toBe(\"1\");\n  });\n});\n```\n\nThe returned `ctx` from the `setupRenderingContext` has the following APIs:\n\n- `element`\n- `owner`\n- `find(selector)`\n- `findAll(selector)`\n- `click(selector or element)`\n- `render(componet)`\n\n### Using extended `test`\n\n\u003e [!NOTE]\n\u003e These utilities are an experiment and will not be covered un semver, and unfortunately, use of these utilities prevents the ability to run tests in parallel (this is a limitation of `@ember/test-helpers`'s `setApplication`)\n\n#### Application Tests\n\nThese tests are generally for when you visit specific pages and simulate user flows.\n\n```gjs\n// tests/application/sample-test.gjs\nimport { describe, it, expect } from \"vitest\";\nimport { applicationTest } from \"ember-vitest\";\nimport { visit, pauseTest } from \"@ember/test-helpers\";\n\nimport App from \"./your/app/location\";\n\ndescribe(\"Home\", () =\u003e {\n  applicationTest.scoped({ app: ({}, use) =\u003e use(App) });\n\n  applicationTest(\"can visit the home screen\", async ({ element }) =\u003e {\n    await visit(\"/\");\n\n    expect(element.textContent).toBe(\"hello there\");\n  });\n});\n```\n\n#### Rendering Tests\n\nThese sorts of tests are very versatile, as they enable you to test not just components, but reactivity, DOM, modifiers, and more!\n\n```gjs\nimport { describe, it, expect } from \"vitest\";\nimport { renderingTest } from \"ember-vitest\";\nimport { find, click, render } from \"@ember/test-helpers\";\n\nimport { Counter } from \"#src/components/counter\";\n\ndescribe(\"Counter\", () =\u003e {\n  // Optional: only needed if your component needs access to application state\n  // renderingTest.scoped({ app: ({}, use) =\u003e use(App) });\n\n  renderingTest(\"can interact\", async () =\u003e {\n    await render(\u003ctemplate\u003e\u003cCounter /\u003e\u003c/template\u003e);\n\n    expect(find(\"output\").textContent).toBe(\"0\");\n\n    await click(\"button\");\n\n    expect(find(\"output\").textContent).toBe(\"1\");\n  });\n});\n```\n\n#### Container Tests\n\nThese tests are sort of like unit tests, but when you need your application owner present.\n\n```gjs\nimport { describe, it, expect } from \"vitest\";\nimport { test } from \"ember-vitest\";\n\ndescribe(\"Container test\", () =\u003e {\n  test.scoped({ app: ({}, use) =\u003e use(App) });\n\n  test(\"can interact\", async ({ context }) =\u003e {\n    let foo = context.owner.lookup(\"service:foo\");\n\n    expect(foo.count).toBe(0);\n    foo.count++;\n    expect(foo.count).toBe(1);\n  });\n});\n```\n\n### Pausing Test Execution\n\nyou may use `pauseTest` and `resumeTest` just as you would in qunit, but vitest does not allow changing the test timeout within a test, so when paused, you only have until your test timeout to debug.\n\nTo get around this, you'll probably want to bump the testTimeout in the vite config to a few minutes.\n\n```js\nexport default defineConfig({\n  test: {\n    testTimeout: 120_000_000, // ms\n    // ...\n});\n```\n\n### Test Context\n\nThe test callback has some extra data available in it that you may find useful\n\n```js\n`renderingTest|applicationTest|test`(\n  \"name of your test here\",\n  ({\n    /**\n            The element the component or application is rendered in to\n        */\n    element,\n    /**\n            The test context. This only has the owner\n        */\n    context: { owner },\n    /**\n            the env is where most of the test setup is interanally.\n            the env.owner here is === context.owner, and the env.element is === element\n        */\n    env: { owner, element, pauseTest },\n  }) =\u003e {},\n);\n```\n\n## Setup\n\nIn order to use ember-vitest, you must have a vite config with plugins configured for compiling ember, as well as telling `test.include` to include the gjs / gts files.:\n\n```js\n// vite.config.js\nimport { webdriverio } from \"@vitest/browser-webdriverio\";\nimport { defineConfig } from \"vite\";\n\nimport { ember, extensions } from \"@embroider/vite\";\nimport { babel } from \"@rollup/plugin-babel\";\n\nexport default defineConfig({\n  // Add this config\n  test: {\n    include: [\"tests/**/*-test.{gjs,gts}\"],\n    maxConcurrency: 1,\n    browser: {\n      provider: webdriverio(),\n      enabled: true,\n      headless: true,\n      // at least one instance is required\n      instances: [\n        { browser: \"chrome\" },\n        // { browser: 'firefox' },\n        // { browser: 'edge' },\n        // { browser: 'safari' },\n      ],\n    },\n  },\n  // Existing config:\n  plugins: [\n    ember(),\n    babel({\n      babelHelpers: \"runtime\",\n      extensions,\n    }),\n  ],\n});\n```\n\nYour actual vite config may vary.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNullVoxPopuli%2Fember-vitest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNullVoxPopuli%2Fember-vitest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNullVoxPopuli%2Fember-vitest/lists"}