ember-vitest 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc.js ADDED
@@ -0,0 +1,3 @@
1
+ export default {
2
+ plugins: ["prettier-plugin-ember-template-tag"],
3
+ };
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # ember-vitest
2
+
3
+ Ember<->Vitest integration
4
+
5
+ - pause test execution for UI debugging purposes (and without pausing JS execution)
6
+ - continue using familiar helpers as you would in qunit
7
+ -
8
+
9
+ ## Install
10
+
11
+ ```
12
+ npm add --save-dev ember-vitest vitest @vitest/browser webdriverio
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ After [#setup](#setup), run:
18
+
19
+ ```bash
20
+ pnpm vitest
21
+ # or
22
+ npm exec vitest
23
+ ```
24
+
25
+ ### Application Tests
26
+
27
+ These tests are generally for when you visit specific pages and simulate user flows.
28
+
29
+ ```gjs
30
+ // tests/application/sample-test.gjs
31
+ import { describe, it, expect } from "vitest";
32
+ import { applicationTest } from "ember-vitest";
33
+ import { visit, pauseTest } from "@ember/test-helpers";
34
+
35
+ import App from "./your/app/location";
36
+
37
+ describe("Home", () => {
38
+ applicationTest.scoped({ app: ({}, use) => use(App) });
39
+
40
+ applicationTest("can visit the home screen", async ({ element }) => {
41
+ await visit("/");
42
+
43
+ expect(element.textContent).toBe("hello there");
44
+ });
45
+ });
46
+ ```
47
+
48
+ ### Rendering Tests
49
+
50
+ These sorts of tests are very versatile, as they enable you to test not just components, but reactivity, DOM, modifiers, and more!
51
+
52
+ ```gjs
53
+ import { describe, it, expect } from "vitest";
54
+ import { renderingTest } from "ember-vitest";
55
+ import { find, click, render } from "@ember/test-helpers";
56
+
57
+ import { Counter } from "#src/components/counter";
58
+
59
+ describe("Counter", () => {
60
+ // Optional: only needed if your component needs access to application state
61
+ // renderingTest.scoped({ app: ({}, use) => use(App) });
62
+
63
+ renderingTest("can interact", async () => {
64
+ await render(<template><Counter /></template>);
65
+
66
+ expect(find("output").textContent).toBe("0");
67
+
68
+ await click("button");
69
+
70
+ expect(find("output").textContent).toBe("1");
71
+ });
72
+ });
73
+ ```
74
+
75
+ ### Container Tests
76
+
77
+ These tests are sort of like unit tests, but when you need your application owner present.
78
+
79
+ ```gjs
80
+ import { describe, it, expect } from "vitest";
81
+ import { test } from "ember-vitest";
82
+
83
+ describe("Container test", () => {
84
+ test.scoped({ app: ({}, use) => use(App) });
85
+
86
+ test("can interact", async ({ context }) => {
87
+ let foo = context.owner.lookup("service:foo");
88
+
89
+ expect(foo.count).toBe(0);
90
+ foo.count++;
91
+ expect(foo.count).toBe(1);
92
+ });
93
+ });
94
+ ```
95
+
96
+ ### Pausing Test Execution
97
+
98
+ 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.
99
+
100
+ To get around this, you'll probably want to bump the testTimeout in the vite config to a few minutes.
101
+
102
+ ```js
103
+ export default defineConfig({
104
+ test: {
105
+ testTimeout: 120_000_000, // ms
106
+ // ...
107
+ });
108
+ ```
109
+
110
+ ## Setup
111
+
112
+ 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.:
113
+
114
+ ```js
115
+ // vite.config.js
116
+ import { defineConfig } from "vite";
117
+
118
+ import { ember, extensions } from "@embroider/vite";
119
+ import { babel } from "@rollup/plugin-babel";
120
+
121
+ export default defineConfig({
122
+ // Add this config
123
+ test: {
124
+ include: ["tests/**/*-test.{gjs,gts}"],
125
+ browser: {
126
+ provider: "webdriverio",
127
+ enabled: true,
128
+ // at least one instance is required
129
+ instances: [
130
+ { browser: "chrome" },
131
+ // { browser: 'firefox' },
132
+ // { browser: 'edge' },
133
+ // { browser: 'safari' },
134
+ ],
135
+ },
136
+ },
137
+ // Existing config:
138
+ plugins: [
139
+ ember(),
140
+ babel({
141
+ babelHelpers: "runtime",
142
+ extensions,
143
+ }),
144
+ ],
145
+ });
146
+ ```
147
+
148
+ Your actual vite config may vary.
@@ -0,0 +1,37 @@
1
+ import { buildMacros } from "@embroider/macros/babel";
2
+
3
+ const macros = buildMacros();
4
+
5
+ export default {
6
+ plugins: [
7
+ [
8
+ "babel-plugin-ember-template-compilation",
9
+ {
10
+ compilerPath: "ember-source/dist/ember-template-compiler.js",
11
+ enableLegacyModules: [],
12
+ transforms: [...macros.templateMacros],
13
+ },
14
+ ],
15
+ [
16
+ "module:decorator-transforms",
17
+ {
18
+ runtime: {
19
+ import: import.meta.resolve("decorator-transforms/runtime-esm"),
20
+ },
21
+ },
22
+ ],
23
+ [
24
+ "@babel/plugin-transform-runtime",
25
+ {
26
+ absoluteRuntime: import.meta.dirname,
27
+ useESModules: true,
28
+ regenerator: false,
29
+ },
30
+ ],
31
+ ...macros.babelMacros,
32
+ ],
33
+
34
+ generatorOpts: {
35
+ compact: false,
36
+ },
37
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ name: "ember-vitest",
3
+ };
package/index.js ADDED
@@ -0,0 +1,97 @@
1
+ import { setApplication, resumeTest, pauseTest } from "@ember/test-helpers";
2
+
3
+ // https://vitest.dev/guide/test-context.html#extend-test-context
4
+ import { test as baseTest } from "vitest";
5
+ import {
6
+ setupContext,
7
+ teardownContext,
8
+ setupRenderingContext,
9
+ setupApplicationContext,
10
+ } from "@ember/test-helpers";
11
+
12
+ import Application from "ember-strict-application-resolver";
13
+
14
+ globalThis.resumeTest = resumeTest;
15
+
16
+ class App extends Application {
17
+ modules = {};
18
+ }
19
+
20
+ const waitForSettled = true;
21
+ const options = { waitForSettled };
22
+
23
+ export const test = baseTest.extend({
24
+ app: ({}, use) => use(App),
25
+ element: ({}, use) => use(document.createElement("div")),
26
+ context: ({}, use) => use({}),
27
+ env: [
28
+ async ({ app, element, context }, use) => {
29
+ document.body.append(element);
30
+
31
+ setApplication(app.create({ autoboot: false, rootElement: element }));
32
+ await setupContext(context, options);
33
+
34
+ await use({
35
+ owner: context.owner,
36
+ element,
37
+ pauseTest,
38
+ });
39
+
40
+ await teardownContext(context, options);
41
+ element.remove();
42
+ },
43
+ { auto: true },
44
+ ],
45
+ });
46
+
47
+ export const renderingTest = baseTest.extend({
48
+ app: ({}, use) => use(App),
49
+ element: ({}, use) => use(document.createElement("div")),
50
+ context: ({}, use) => use({}),
51
+ env: [
52
+ async ({ app, element, context }, use) => {
53
+ document.body.append(element);
54
+
55
+ setApplication(app.create({ autoboot: false, rootElement: element }));
56
+ await setupContext(context, options);
57
+ await setupRenderingContext(context, options);
58
+
59
+ await use({
60
+ owner: context.owner,
61
+ element,
62
+ pauseTest,
63
+ });
64
+
65
+ await teardownContext(context, options);
66
+ element.remove();
67
+ },
68
+ { auto: true },
69
+ ],
70
+ });
71
+
72
+ export const applicationTest = baseTest.extend({
73
+ app: ({}, use) => use(App),
74
+ element: ({}, use) => use(document.createElement("div")),
75
+ context: ({}, use) => use({}),
76
+ env: [
77
+ async ({ app, element, context }, use) => {
78
+ document.body.append(element);
79
+
80
+ console.log(app);
81
+
82
+ setApplication(app.create({ autoboot: false, rootElement: element }));
83
+ await setupContext(context, options);
84
+ await setupApplicationContext(context, options);
85
+
86
+ await use({
87
+ owner: context.owner,
88
+ element,
89
+ pauseTest,
90
+ });
91
+
92
+ await teardownContext(context, options);
93
+ element.remove();
94
+ },
95
+ { auto: true },
96
+ ],
97
+ });
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "ember-vitest",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "integration for testing Ember with Vitest",
6
+ "main": "index.js",
7
+ "module": "index.js",
8
+ "exports": "index.js",
9
+ "keywords": [
10
+ "ember",
11
+ "ember-addon",
12
+ "vite",
13
+ "vitest"
14
+ ],
15
+ "author": "NullVoxPopuli",
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "@babel/core": "^7.28.4",
19
+ "@babel/plugin-transform-runtime": "^7.28.3",
20
+ "@ember/test-helpers": "^5.3.0",
21
+ "@embroider/core": "^4.2.0",
22
+ "@embroider/macros": "^1.18.1",
23
+ "@embroider/vite": "^1.2.0",
24
+ "@glimmer/component": "^2.0.0",
25
+ "@rollup/plugin-babel": "^6.0.4",
26
+ "@testing-library/dom": "^10.4.1",
27
+ "@vitest/browser": "^3.2.4",
28
+ "babel-plugin-ember-template-compilation": "^3.0.1",
29
+ "decorator-transforms": "^2.3.0",
30
+ "ember-source": "^6.7.0",
31
+ "prettier": "^3.6.2",
32
+ "prettier-plugin-ember-template-tag": "^2.1.0",
33
+ "vite": "^7.1.6",
34
+ "vitest": "^3.2.4",
35
+ "webdriverio": "^9.19.2"
36
+ },
37
+ "ember-addon": {
38
+ "version": 2,
39
+ "type": "addon",
40
+ "main": "./ember-addon-main.cjs"
41
+ },
42
+ "peerDependencies": {
43
+ "vitest": "^3.2.4"
44
+ },
45
+ "engines": {
46
+ "node": "^20.11.0 || ^22 || >=24"
47
+ },
48
+ "dependencies": {
49
+ "ember-strict-application-resolver": "^0.1.0"
50
+ },
51
+ "scripts": {
52
+ "test": "vitest run",
53
+ "test:dev": "vitest"
54
+ }
55
+ }
@@ -0,0 +1,7 @@
1
+ minimumReleaseAge: 1440
2
+ ignoreScripts: true
3
+
4
+ peersSuffixMaxLength: 40
5
+ autoInstallPeers: false
6
+ dedupeInjectedDeps: true
7
+ dedupePeerDependents: true
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { applicationTest } from "ember-vitest";
3
+ import { visit, pauseTest } from "@ember/test-helpers";
4
+
5
+ import EmberRouter from "@ember/routing/router";
6
+ import Application from "ember-strict-application-resolver";
7
+
8
+ class Router extends EmberRouter {
9
+ location = "none";
10
+ rootUrl = "/";
11
+ }
12
+
13
+ class App extends Application {
14
+ modules = {
15
+ "./router": Router,
16
+ "./templates/application": <template>hello there</template>,
17
+ };
18
+ }
19
+
20
+ describe("Home", () => {
21
+ applicationTest.scoped({ app: ({}, use) => use(App) });
22
+
23
+ applicationTest("can visit the home screen", async ({ element, env }) => {
24
+ await visit("/");
25
+
26
+ expect(element.textContent).toBe("hello there");
27
+ });
28
+ });
@@ -0,0 +1,28 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { test } from "ember-vitest";
3
+ import Service from "@ember/service";
4
+ import { tracked } from "@glimmer/tracking";
5
+
6
+ class Foo extends Service {
7
+ @tracked count = 0;
8
+ }
9
+
10
+ import Application from "ember-strict-application-resolver";
11
+
12
+ class App extends Application {
13
+ modules = {
14
+ "./services/foo": Foo,
15
+ };
16
+ }
17
+
18
+ describe("Container", () => {
19
+ test.scoped({ app: ({}, use) => use(App) });
20
+
21
+ test("works", async ({ context }) => {
22
+ let foo = context.owner.lookup("service:foo");
23
+
24
+ expect(foo.count).toBe(0);
25
+ foo.count++;
26
+ expect(foo.count).toBe(1);
27
+ });
28
+ });
@@ -0,0 +1,27 @@
1
+ import Component from "@glimmer/component";
2
+ import { tracked } from "@glimmer/tracking";
3
+ import { describe, it, expect } from "vitest";
4
+ import { renderingTest } from "ember-vitest";
5
+ import { find, click, render, pauseTest } from "@ember/test-helpers";
6
+
7
+ class Counter extends Component {
8
+ @tracked count = 0;
9
+ increment = () => this.count++;
10
+
11
+ <template>
12
+ <output>{{this.count}}</output>
13
+ <button onclick={{this.increment}}>++</button>
14
+ </template>
15
+ }
16
+
17
+ describe("Counter", () => {
18
+ renderingTest("can interact", async () => {
19
+ await render(<template><Counter /></template>);
20
+
21
+ expect(find("output").textContent).toBe("0");
22
+
23
+ await click("button");
24
+
25
+ expect(find("output").textContent).toBe("1");
26
+ });
27
+ });
package/vite.config.js ADDED
@@ -0,0 +1,28 @@
1
+ import { defineConfig } from "vite";
2
+
3
+ import { ember, extensions } from "@embroider/vite";
4
+ import { babel } from "@rollup/plugin-babel";
5
+
6
+ export default defineConfig({
7
+ test: {
8
+ include: ["tests/**/*-test.{gjs,gts}"],
9
+ browser: {
10
+ provider: "webdriverio",
11
+ enabled: true,
12
+ // at least one instance is required
13
+ instances: [
14
+ { browser: "chrome" },
15
+ // { browser: 'firefox' },
16
+ // { browser: 'edge' },
17
+ // { browser: 'safari' },
18
+ ],
19
+ },
20
+ },
21
+ plugins: [
22
+ ember(),
23
+ babel({
24
+ babelHelpers: "runtime",
25
+ extensions,
26
+ }),
27
+ ],
28
+ });