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 +3 -0
- package/README.md +148 -0
- package/babel.config.js +37 -0
- package/ember-addon-main.cjs +3 -0
- package/index.js +97 -0
- package/package.json +55 -0
- package/pnpm-workspace.yaml +7 -0
- package/tests/application/sample-test.gjs +28 -0
- package/tests/container/sample-test.gjs +28 -0
- package/tests/rendering/sample-test.gjs +27 -0
- package/vite.config.js +28 -0
package/.prettierrc.js
ADDED
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.
|
package/babel.config.js
ADDED
|
@@ -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
|
+
};
|
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,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
|
+
});
|