npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ember-vitest

v0.3.3

Published

integration for testing Ember with Vitest

Readme

ember-vitest

ember <-> vitest integration

  • browser testing is first class
  • pause test execution for UI debugging purposes (and without pausing JS execution)
  • continue using familiar helpers as you would in qunit

Install

npm add --save-dev ember-vitest vitest @vitest/browser @vitest/browser-webdriverio

Usage

After #setup, run:

pnpm vitest
# or
npm exec vitest

Using vitest's test

[!NOTE] We use expect.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.

Rendering Tests

A basic test can be written like:

import { describe, test, expect as hardExpect } from "vitest";
import { setupRenderingContext } from "ember-vitest";

const expect = hardExpect.soft;

describe("example", () => {
  test("it works", async () => {
    await using ctx = setupRenderingContext();

    await ctx.render(<template>hello there</template>);

    expect(ctx.element.textContent).contains("hello there");
  });

And interactivity can be done via @testing-library/dom and 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.)

import { trackedObject } from "@ember/reactive/collections";
import { describe, test, expect as hardExpect } from "vitest";
import { screen } from "@testing-library/dom";
import { fireEvent } from "testing-library-ember";

import { setupRenderingContext } from "ember-vitest";

const expect = hardExpect.soft;

describe("example", () => {
  test("has interactivity", async () => {
    await using ctx = setupRenderingContext();

    const state = trackedObject({ value: 0 });
    const increment = () => state.value++;

    await ctx.render(
      <template>
        <button role="button" onclick={{increment}}>click me</button>
        <output>{{state.value}}</output>
      </template>,
    );

    let btn = screen.getByText(/click me/);
    let out = ctx.element.querySelector("output");

    expect(btn).toBeTruthy();
    expect(out.textContent).toBe("0");

    await fireEvent.click(btn);
    expect(out.textContent).toBe("1");
  });
});

The returned ctx from the setupRenderingContext has the following APIs:

  • element
  • owner
  • find(selector)
  • findAll(selector)
  • click(selector or element)
  • render(componet)

Using extended test

[!NOTE] 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)

Application Tests

These tests are generally for when you visit specific pages and simulate user flows.

// tests/application/sample-test.gjs
import { describe, it, expect } from "vitest";
import { applicationTest } from "ember-vitest";
import { visit, pauseTest } from "@ember/test-helpers";

import App from "./your/app/location";

describe("Home", () => {
  applicationTest.scoped({ app: ({}, use) => use(App) });

  applicationTest("can visit the home screen", async ({ element }) => {
    await visit("/");

    expect(element.textContent).toBe("hello there");
  });
});

Rendering Tests

These sorts of tests are very versatile, as they enable you to test not just components, but reactivity, DOM, modifiers, and more!

import { describe, it, expect } from "vitest";
import { renderingTest } from "ember-vitest";
import { find, click, render } from "@ember/test-helpers";

import { Counter } from "#src/components/counter";

describe("Counter", () => {
  // Optional: only needed if your component needs access to application state
  // renderingTest.scoped({ app: ({}, use) => use(App) });

  renderingTest("can interact", async () => {
    await render(<template><Counter /></template>);

    expect(find("output").textContent).toBe("0");

    await click("button");

    expect(find("output").textContent).toBe("1");
  });
});

Container Tests

These tests are sort of like unit tests, but when you need your application owner present.

import { describe, it, expect } from "vitest";
import { test } from "ember-vitest";

describe("Container test", () => {
  test.scoped({ app: ({}, use) => use(App) });

  test("can interact", async ({ context }) => {
    let foo = context.owner.lookup("service:foo");

    expect(foo.count).toBe(0);
    foo.count++;
    expect(foo.count).toBe(1);
  });
});

Pausing Test Execution

you 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.

To get around this, you'll probably want to bump the testTimeout in the vite config to a few minutes.

export default defineConfig({
  test: {
    testTimeout: 120_000_000, // ms
    // ...
});

Test Context

The test callback has some extra data available in it that you may find useful

`renderingTest|applicationTest|test`(
  "name of your test here",
  ({
    /**
            The element the component or application is rendered in to
        */
    element,
    /**
            The test context. This only has the owner
        */
    context: { owner },
    /**
            the env is where most of the test setup is interanally.
            the env.owner here is === context.owner, and the env.element is === element
        */
    env: { owner, element, pauseTest },
  }) => {},
);

Setup

In 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.:

// vite.config.js
import { webdriverio } from "@vitest/browser-webdriverio";
import { defineConfig } from "vite";

import { ember, extensions } from "@embroider/vite";
import { babel } from "@rollup/plugin-babel";

export default defineConfig({
  // Add this config
  test: {
    include: ["tests/**/*-test.{gjs,gts}"],
    maxConcurrency: 1,
    browser: {
      provider: webdriverio(),
      enabled: true,
      headless: true,
      // at least one instance is required
      instances: [
        { browser: "chrome" },
        // { browser: 'firefox' },
        // { browser: 'edge' },
        // { browser: 'safari' },
      ],
    },
  },
  // Existing config:
  plugins: [
    ember(),
    babel({
      babelHelpers: "runtime",
      extensions,
    }),
  ],
});

Your actual vite config may vary.