Skip to content

Commit a813159

Browse files
Merge pull request #1430 from NullVoxPopuli/nvp/vite-support
Support vite
2 parents cbac72e + 4495b36 commit a813159

48 files changed

Lines changed: 2003 additions & 987 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
- { name: "Broccoli (v1 Addon)", dir: ".", cmd: "pnpm test:ember" }
5555
- { name: "Broccoli (v1 App)", dir: './test-apps/broccoli', cmd: 'pnpm test:ember' }
5656
- { name: "Webpack + Embroider 3 ", dir: './test-apps/embroider3-webpack', cmd: 'pnpm test:ember' }
57+
- { name: "Vite + Compat", dir: './test-apps/vite-with-compat', cmd: 'pnpm build:tests && pnpm test:exam' }
58+
5759
steps:
5860
- uses: actions/checkout@v4
5961
- uses: pnpm/action-setup@v4

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# compiled output
22
/dist/
33
/acceptance-dist/
4+
dist-*/
5+
test-execution-*.json
46
/declarations/
57

68
# dependencies

CONTRIBUTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,14 @@
2525
- Visit the dummy application at [http://localhost:4200](http://localhost:4200).
2626

2727
For more information on using ember-cli, visit [https://cli.emberjs.com/release/](https://cli.emberjs.com/release/).
28+
29+
## Debugging testem
30+
31+
Terminal 1
32+
```bash
33+
pnpm ember exam --load-balance --path ./dist --parallel 2 --testem-debug testem.log
34+
```
35+
Terminal 2
36+
```bash
37+
tail -f testem.log
38+
```

README.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,54 @@ Update your test-helper.js or test-helper.ts, to have add the ember-exam `start`
9393
+ import { setupEmberOnerrorValidation } from 'ember-qunit';
9494
+ import { start as startEmberExam } from 'ember-exam/test-support';
9595

96-
export function start() {
96+
- export function start() {
97+
+ export async function start({ availableModules }) {
9798
setApplication(Application.create(config.APP));
9899

99100
setup(QUnit.assert);
100101
setupEmberOnerrorValidation();
101102

102103
- qunitStart();
103104
+ // Options passed to `start` will be passed-through to ember-qunit
104-
+ startEmberExam();
105+
+ await startEmberExam({ availableModules });
105106
}
106107
```
107108

109+
Then, update your tests/index.html to pass availableModules to start:
110+
```html
111+
<script type="module">
112+
import { start } from './test-helper.js';
113+
114+
const availableModules = {
115+
...import.meta.glob('./application/**/*-test.{js,ts,gjs,gts}'),
116+
...import.meta.glob('./rendering/**/*-test.{js,ts,gjs,gts}'),
117+
...import.meta.glob('./unit/**/*-test.{js,ts,gjs,gts}'),
118+
};
119+
120+
start({ availableModules });
121+
</script>
122+
```
123+
124+
108125
Testing development:
109126
```bash
110-
NODE_ENV=development vite build --mode test
111-
ember exam --path dist
127+
NODE_ENV=development vite build --mode development
128+
ember exam --path dist --config-file ./testem.cjs
112129
```
113130

114131
Testing production:
115132
```bash
116133
vite build --mode test
117-
ember exam --path dist
134+
ember exam --path dist --config-file ./testem.cjs
118135
```
119136

137+
> [!NOTE]
138+
> Specifying the `--path` is important because otherwise ember-cli will try to build your vite app, and it will error.
139+
140+
> [!NOTE]
141+
> Specifying the `--config-path` is important because ember-cli (what backs ember-exam) doesn't know about cjs files.
142+
143+
120144
### Version < `3.0.0`
121145

122146

addon-test-support/-private/ember-exam-test-loader.js

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { assert } from '@ember/debug';
12
import getUrlParams from './get-url-params';
23
import splitTestModules from './split-test-modules';
34
import weightTestModules from './weight-test-modules';
@@ -59,7 +60,7 @@ export default class EmberExamTestLoader extends TestLoader {
5960
*
6061
* @method loadModules
6162
*/
62-
loadModules() {
63+
async loadModules({ availableModules } = {}) {
6364
const loadBalance = this._urlParams.get('loadBalance');
6465
const browserId = this._urlParams.get('browser');
6566
const modulePath = this._urlParams.get('modulePath');
@@ -75,7 +76,18 @@ export default class EmberExamTestLoader extends TestLoader {
7576
partitions = [partitions];
7677
}
7778

78-
super.loadModules();
79+
if (!availableModules) {
80+
super.loadModules();
81+
} else {
82+
assert(
83+
`Available modules must be an object.`,
84+
typeof availableModules === 'object',
85+
);
86+
87+
this._availableModules = availableModules;
88+
this._testModules = Object.keys(availableModules);
89+
}
90+
7991
this.setupModuleMetadataHandler();
8092

8193
if (modulePath || filePath) {
@@ -93,6 +105,7 @@ export default class EmberExamTestLoader extends TestLoader {
93105
split,
94106
partitions,
95107
);
108+
96109
this._testem.emit(
97110
'testem:set-modules-queue',
98111
this._testModules,
@@ -104,25 +117,70 @@ export default class EmberExamTestLoader extends TestLoader {
104117
split,
105118
partitions,
106119
);
120+
121+
if (this._availableModules) {
122+
await this.loadAvailableModules();
123+
return;
124+
}
125+
126+
/**
127+
* Legacy support
128+
*/
107129
this._testModules.forEach((moduleName) => {
108130
super.require(moduleName);
109131
super.unsee(moduleName);
110132
});
111133
}
112134
}
113135

136+
/**
137+
* availableModules are passed in from loadModules
138+
* from loadEmberExam
139+
* from start
140+
*/
141+
async loadAvailableModules() {
142+
if (this._availableModules) {
143+
await Promise.all(
144+
this._testModules.map(async (moduleName) => {
145+
let loader = this._availableModules[moduleName];
146+
147+
/**
148+
* If it's not a function, it's already loaded
149+
*/
150+
if (typeof loader === 'function') {
151+
await loader();
152+
}
153+
}),
154+
);
155+
}
156+
}
157+
114158
/**
115159
* Allow loading one module at a time.
116160
*
117161
* @method loadIndividualModule
118162
* @param {string} moduleName
119163
*/
120-
loadIndividualModule(moduleName) {
164+
async loadIndividualModule(moduleName) {
121165
if (moduleName === undefined) {
122166
throw new Error(
123167
'Failed to load a test module. `moduleName` is undefined in `loadIndividualModule`.',
124168
);
125169
}
170+
171+
if (this._availableModules) {
172+
let loader = this._availableModules[moduleName];
173+
174+
/**
175+
* If it's not a function, it's already loaded
176+
*/
177+
if (typeof loader === 'function') {
178+
await loader();
179+
}
180+
181+
return;
182+
}
183+
126184
super.require(moduleName);
127185
super.unsee(moduleName);
128186
}
@@ -168,10 +226,10 @@ export default class EmberExamTestLoader extends TestLoader {
168226

169227
return nextModuleAsyncIterator
170228
.next()
171-
.then((response) => {
229+
.then(async (response) => {
172230
if (!response.done) {
173231
const moduleName = response.value;
174-
this.loadIndividualModule(moduleName);
232+
await this.loadIndividualModule(moduleName);
175233

176234
// if no tests were added, request the next module
177235
if (this._qunit.config.queue.length === 0) {

addon-test-support/start.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ import { start as qunitStart } from 'ember-qunit';
66
*
77
* @function loadTests
88
* @param {*} testLoader
9+
* @param {*} loaderOptions
910
*/
10-
function loadTests(testLoader) {
11+
async function loadTests(testLoader, loaderOptions = {}) {
1112
if (testLoader === undefined) {
1213
throw new Error(
1314
'A testLoader instance has not been created. You must call `loadEmberExam()` before calling `loadTest()`.',
1415
);
1516
}
1617

17-
testLoader.loadModules();
18+
await testLoader.loadModules(loaderOptions);
1819
}
1920

2021
/**
@@ -24,11 +25,13 @@ function loadTests(testLoader) {
2425
* @function start
2526
* @param {*} qunitOptions
2627
*/
27-
export default function start(qunitOptions) {
28-
const modifiedOptions = qunitOptions || Object.create(null);
28+
export default async function start(qunitOptions = {}) {
29+
const { availableModules, ...modifiedOptions } =
30+
qunitOptions || Object.create(null);
31+
2932
modifiedOptions.loadTests = false;
3033

3134
const testLoader = loadEmberExam();
32-
loadTests(testLoader);
35+
await loadTests(testLoader, { availableModules });
3336
qunitStart(modifiedOptions);
3437
}

docs-app/filtering.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,25 @@ Ember Exam provides options to filter test suites by two types - module path and
66
$ ember exam --module-path=<module-path>
77
```
88

9-
The `module-path` option allows you to filter module paths by a given value. Module paths are mapped by test files and they are generated during `ember build`. After the build, `tests.js` file is created and it resides under [build-directory]/assets. The file is combined of all tests in an application and it has a form of `define("<module-path>", others..`.
9+
#### For Vite Apps
10+
11+
The `file-path` option allows you to filter modules by the given relative path that is generated from `import.meta.glob(...)` in your `tests/index.html`.
12+
13+
```bash
14+
# This will run tests that are defined in `/my-application/tests/unit/my-test.js`
15+
$ ember exam --file-path='/my-application/tests/unit/my-test.js'
16+
17+
# This will run all test files that are under `/my-application/tests/unit/`
18+
$ ember exam --file-path='/my-application/tests/unit/*.js'
19+
```
20+
21+
22+
#### For non-Vite Apps
23+
24+
25+
The `module-path` option allows you to filter module paths by a given value. Module paths are mapped by test files and they are generated during `ember build`. After the build, `tests.js` file is created and it resides under [build-directory]/assets.
26+
27+
The file is combined of all tests in an application and it has a form of `define("<module-path>", others..`.
1028

1129
The value for `module-path` can have either string or regular expression, for instance:
1230

docs-app/quickstart.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,84 @@ ember exam --load-balance --parallel --server --no-launch
3535

3636
The idea is that you can replace `ember test` with `ember exam` and never look back.
3737

38+
To get the unique features of Ember Exam (described in-depth below), you will need to **replace** the use of `start()` from `ember-qunit` in `test-helper.js` with `start()` from `ember-exam`:
39+
40+
41+
## Setup
42+
43+
### With Vite
44+
45+
46+
Update your test-helper.js or test-helper.ts, to have add the ember-exam `start` function:
47+
```diff
48+
// ...
49+
import { setApplication } from '@ember/test-helpers';
50+
import { setup } from 'qunit-dom';
51+
- import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit';
52+
+ import { setupEmberOnerrorValidation } from 'ember-qunit';
53+
+ import { start as startEmberExam } from 'ember-exam/test-support';
54+
55+
- export function start() {
56+
+ export async function start({ availableModules }) {
57+
setApplication(Application.create(config.APP));
58+
59+
setup(QUnit.assert);
60+
setupEmberOnerrorValidation();
61+
62+
- qunitStart();
63+
+ // Options passed to `start` will be passed-through to ember-qunit
64+
+ await startEmberExam({ availableModules });
65+
}
66+
```
67+
68+
Then, update your tests/index.html to pass availableModules to start:
69+
```html
70+
<script type="module">
71+
import { start } from './test-helper.js';
72+
73+
const availableModules = {
74+
...import.meta.glob('./application/**/*-test.{js,ts,gjs,gts}'),
75+
...import.meta.glob('./rendering/**/*-test.{js,ts,gjs,gts}'),
76+
...import.meta.glob('./unit/**/*-test.{js,ts,gjs,gts}'),
77+
};
78+
79+
start({ availableModules });
80+
</script>
81+
```
82+
83+
We need to tell vite to build the app before telling ember/exam to run tests on that output.
84+
85+
Testing development:
86+
```bash
87+
NODE_ENV=development vite build --mode development
88+
ember exam --path dist --config-file ./testem.cjs
89+
```
90+
91+
Testing production:
92+
```bash
93+
vite build --mode test
94+
ember exam --path dist --config-file ./testem.cjs
95+
```
96+
97+
> [!NOTE]
98+
> Specifying the `--path` is important because otherwise ember-cli will try to build your vite app, and it will error.
99+
100+
> [!NOTE]
101+
> Specifying the `--config-path` is important because ember-cli (what backs ember-exam) doesn't know about cjs files.
102+
103+
104+
### broccoli / ember-cli
105+
106+
38107
To get the unique features of Ember Exam (described in-depth below), you will need to **replace** the use of `start()` from `ember-qunit` in `test-helper.js` with `start()` from `ember-exam`:
39108

40109
```js
41110
// test-helper.js
42-
import start from 'ember-exam/test-support/start';
111+
- import { start, setupEmberOnerrorValidation } from 'ember-qunit';
112+
+ import { setupEmberOnerrorValidation } from 'ember-qunit';
113+
+ import { start } from 'ember-exam/test-support';
43114

44-
// start() triggers qunit start after instantiating qunit test-loader instance and loading test modules.
115+
// Options passed to `start` will be passed-through to ember-qunit
45116
start();
46117
```
47118

lib/commands/task/test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ module.exports = TestTask.extend({
77
options.custom_browser_socket_events;
88
transformOptions.browser_module_mapping = options.browser_module_mapping;
99

10+
if (options.loadBalance) {
11+
/**
12+
* the parallel option is how testem knows to boot browsers simultaneously.
13+
* setting testPage to an array isn't enough.
14+
* default behavior is 1 browser at a time, which defeats the purpose of loadBalance.
15+
*/
16+
transformOptions.parallel = options.testPage.length;
17+
}
18+
1019
return transformOptions;
1120
},
1221
});

0 commit comments

Comments
 (0)