Skip to content

Commit ae8195c

Browse files
authored
Merge pull request #10393 from Turbo87/msw
Migrate from `ember-cli-mirage` to `msw` and `@mswjs/data`
2 parents 64c6940 + a328a12 commit ae8195c

File tree

368 files changed

+9397
-7595
lines changed

Some content is hidden

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

368 files changed

+9397
-7595
lines changed

.eslintrc.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,6 @@ module.exports = {
107107
},
108108
},
109109

110-
// mirage files
111-
{
112-
files: ['mirage/**/*.js'],
113-
rules: {
114-
// disabled because of different `.find()` meaning
115-
'unicorn/no-array-callback-reference': 'off',
116-
},
117-
},
118-
119110
// node files
120111
{
121112
files: [

.github/workflows/ci.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
files_ignore: |
4040
app/**
4141
e2e/**
42-
mirage/**
42+
packages/**
4343
public/**
4444
tests/**
4545
.eslintrc
@@ -239,6 +239,30 @@ jobs:
239239
- if: github.repository != 'rust-lang/crates.io'
240240
run: pnpm test-coverage
241241

242+
msw-test:
243+
name: Frontend / Test (@crates-io/msw)
244+
runs-on: ubuntu-24.04
245+
needs: [changed-files]
246+
if: needs.changed-files.outputs.non-rust == 'true'
247+
248+
steps:
249+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
250+
with:
251+
persist-credentials: false
252+
253+
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
254+
with:
255+
version: ${{ env.PNPM_VERSION }}
256+
257+
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
258+
with:
259+
cache: pnpm
260+
node-version-file: package.json
261+
262+
- run: pnpm install
263+
264+
- run: pnpm --filter "@crates-io/msw" test
265+
242266
e2e-test:
243267
name: Frontend / Test (playwright)
244268
runs-on: ubuntu-24.04

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/tmp
77

88
# dependencies
9-
/node_modules
9+
node_modules/
1010
/bower_components
1111
package-lock.json
1212
yarn.lock

app/templates/dashboard.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
</h2>
4646

4747
<div local-class="feed">
48-
<ul local-class="feed-list">
48+
<ul local-class="feed-list" data-test-feed-list>
4949
{{#each this.myFeed as |version|}}
5050
<li local-class="feed-row">
5151
<LinkTo @route="crate.version" @models={{array version.crateName version.num}}>

config/environment.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ module.exports = function (environment) {
6363

6464
if (environment === 'production') {
6565
// here you can enable a production-specific feature
66-
delete ENV['ember-cli-mirage'];
67-
6866
ENV.sentry = {
6967
dsn: process.env.SENTRY_DSN_WEB,
7068
};

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ These files have to do with the frontend:
5151
- `.ember-cli` - Settings for the `ember` command line interface
5252
- `ember-cli-build.js` - Contains the build specification for Broccoli
5353
- `.eslintrc.js` - Defines Javascript coding style guidelines (enforced during CI???)
54-
- `mirage/` - A mock backend used during development and testing
5554
- `node_modules/` - npm dependencies - (ignored in `.gitignore`)
55+
- `packages/crates-io-msw` - A mock backend used for testing
5656
- `package.json` - Defines the npm package and its dependencies
5757
- `package-lock.json` - Locks dependencies to specific versions providing consistency across
5858
development and deployment

docs/CONTRIBUTING.md

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,18 +134,12 @@ To build and serve the frontend assets, use the command `pnpm start`. There
134134
are variations on this command that change which backend your frontend tries to
135135
talk to:
136136

137-
| Command | Backend | Use case |
138-
| ----------------------------------------- | --------------------------------------------- | ------------------------------------------------------- |
139-
| `pnpm start:live` | <https://crates.io> | Testing UI changes with the full live site's data |
140-
| `pnpm start:staging` | <https://staging-crates-io.herokuapp.com> | Testing UI changes with a smaller set of realistic data |
141-
| `pnpm start` | Static fixture test data in `mirage/fixtures` | Setting up particular situations, see note |
142-
| `pnpm start:local` | Backend server running locally | See the Working on the backend section for setup |
143-
| `pnpm start -- --proxy https://crates.io` | Whatever is specified in `--proxy` arg | If your use case is not covered here |
144-
145-
> Note: If you want to set up a particular situation, you can edit the fixture
146-
> data used for tests in `mirage/fixtures`. The fixture data does not currently
147-
> contain JSON needed to support every page, so some pages might not load
148-
> correctly.
137+
| Command | Backend | Use case |
138+
| ----------------------------------------- | ----------------------------------------- | ------------------------------------------------------- |
139+
| `pnpm start:live` | <https://crates.io> | Testing UI changes with the full live site's data |
140+
| `pnpm start:staging` | <https://staging-crates-io.herokuapp.com> | Testing UI changes with a smaller set of realistic data |
141+
| `pnpm start:local` | Backend server running locally | See the Working on the backend section for setup |
142+
| `pnpm start -- --proxy https://crates.io` | Whatever is specified in `--proxy` arg | If your use case is not covered here |
149143

150144
#### Running the frontend tests
151145

e2e/acceptance/api-token.spec.ts

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,37 @@
1-
import { test, expect } from '@/e2e/helper';
1+
import { expect, test } from '@/e2e/helper';
2+
import { http, HttpResponse } from 'msw';
23

34
test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
4-
test.beforeEach(async ({ mirage }) => {
5-
await mirage.addHook(server => {
6-
let user = server.create('user', {
7-
login: 'johnnydee',
8-
name: 'John Doe',
9-
10-
avatar: 'https://avatars2.githubusercontent.com/u/1234567?v=4',
11-
});
12-
server.create('api-token', {
13-
user,
14-
name: 'BAR',
15-
createdAt: '2017-11-19T17:59:22',
16-
lastUsedAt: null,
17-
expiredAt: '2017-12-19T17:59:22',
18-
});
19-
20-
server.create('api-token', {
21-
user,
22-
name: 'recently expired',
23-
createdAt: '2017-08-01T12:34:56',
24-
lastUsedAt: '2017-11-02T01:45:14',
25-
expiredAt: '2017-11-19T17:59:22',
26-
});
27-
server.create('api-token', {
28-
user,
29-
name: 'foo',
30-
createdAt: '2017-08-01T12:34:56',
31-
lastUsedAt: '2017-11-02T01:45:14',
32-
});
33-
34-
globalThis.authenticateAs(user);
5+
test.beforeEach(async ({ msw }) => {
6+
let user = msw.db.user.create({
7+
login: 'johnnydee',
8+
name: 'John Doe',
9+
10+
avatar: 'https://avatars2.githubusercontent.com/u/1234567?v=4',
3511
});
12+
msw.db.apiToken.create({
13+
user,
14+
name: 'BAR',
15+
createdAt: '2017-11-19T17:59:22',
16+
lastUsedAt: null,
17+
expiredAt: '2017-12-19T17:59:22',
18+
});
19+
20+
msw.db.apiToken.create({
21+
user,
22+
name: 'recently expired',
23+
createdAt: '2017-08-01T12:34:56',
24+
lastUsedAt: '2017-11-02T01:45:14',
25+
expiredAt: '2017-11-19T17:59:22',
26+
});
27+
msw.db.apiToken.create({
28+
user,
29+
name: 'foo',
30+
createdAt: '2017-08-01T12:34:56',
31+
lastUsedAt: '2017-11-02T01:45:14',
32+
});
33+
34+
await msw.authenticateAs(user);
3635
});
3736

3837
test('/me is showing the list of active API tokens', async ({ page }) => {
@@ -72,16 +71,13 @@ test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
7271
await expect(row3.locator('[data-test-token]')).toHaveCount(0);
7372
});
7473

75-
test('API tokens can be revoked', async ({ page }) => {
74+
test('API tokens can be revoked', async ({ page, msw }) => {
7675
await page.goto('/settings/tokens');
7776
await expect(page).toHaveURL('/settings/tokens');
7877
await expect(page.locator('[data-test-api-token]')).toHaveCount(3);
7978

8079
await page.click('[data-test-api-token="1"] [data-test-revoke-token-button]');
81-
expect(
82-
await page.evaluate(() => server.schema['apiTokens'].all().length),
83-
'API token has been deleted from the backend database',
84-
).toBe(2);
80+
expect(msw.db.apiToken.findMany({}).length, 'API token has been deleted from the backend database').toBe(2);
8581

8682
await expect(page.locator('[data-test-api-token]')).toHaveCount(2);
8783
await expect(page.locator('[data-test-api-token="2"]')).toBeVisible();
@@ -97,12 +93,10 @@ test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
9793
await expect(page).toHaveURL('/settings/tokens/new?from=1');
9894
});
9995

100-
test('failed API tokens revocation shows an error', async ({ page, mirage }) => {
101-
await mirage.addHook(server => {
102-
server.delete('/api/v1/me/tokens/:id', {}, 500);
103-
});
96+
test('failed API tokens revocation shows an error', async ({ page, msw }) => {
97+
await msw.worker.use(http.delete('/api/v1/me/tokens/:id', () => HttpResponse.json({}, { status: 500 })));
10498

105-
await mirage.page.goto('/settings/tokens');
99+
await page.goto('/settings/tokens');
106100
await expect(page).toHaveURL('/settings/tokens');
107101
await expect(page.locator('[data-test-api-token]')).toHaveCount(3);
108102

@@ -115,7 +109,7 @@ test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
115109
);
116110
});
117111

118-
test('new API tokens can be created', async ({ page, percy }) => {
112+
test('new API tokens can be created', async ({ page, percy, msw }) => {
119113
await page.goto('/settings/tokens');
120114
await expect(page).toHaveURL('/settings/tokens');
121115
await expect(page.locator('[data-test-api-token]')).toHaveCount(3);
@@ -129,7 +123,7 @@ test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
129123

130124
await page.click('[data-test-generate]');
131125

132-
let token = await page.evaluate(() => server.schema['apiTokens'].findBy({ name: 'the new token' })?.token);
126+
let token = msw.db.apiToken.findFirst({ where: { name: { equals: 'the new token' } } })?.token;
133127
expect(token, 'API token has been created in the backend database').toBeTruthy();
134128

135129
await expect(page.locator('[data-test-api-token="4"] [data-test-name]')).toHaveText('the new token');
@@ -140,14 +134,14 @@ test.describe('Acceptance | api-tokens', { tag: '@acceptance' }, () => {
140134
await expect(page.locator('[data-test-token]')).toHaveText(token);
141135
});
142136

143-
test('API tokens are only visible in plaintext until the page is left', async ({ page }) => {
137+
test('API tokens are only visible in plaintext until the page is left', async ({ page, msw }) => {
144138
await page.goto('/settings/tokens');
145139
await page.click('[data-test-new-token-button]');
146140
await page.fill('[data-test-name]', 'the new token');
147141
await page.click('[data-test-scope="publish-update"]');
148142
await page.click('[data-test-generate]');
149143

150-
let token = await page.evaluate(() => server.schema['apiTokens'].findBy({ name: 'the new token' })?.token);
144+
let token = msw.db.apiToken.findFirst({ where: { name: { equals: 'the new token' } } })?.token;
151145
await expect(page.locator('[data-test-token]')).toHaveText(token);
152146

153147
// leave the API tokens page

e2e/acceptance/categories.spec.ts

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { test, expect } from '@/e2e/helper';
1+
import { expect, test } from '@/e2e/helper';
22

33
test.describe('Acceptance | categories', { tag: '@acceptance' }, () => {
4-
test('listing categories', async ({ page, mirage, percy, a11y }) => {
5-
await mirage.addHook(server => {
6-
server.create('category', { category: 'API bindings' });
7-
server.create('category', { category: 'Algorithms' });
8-
server.createList('crate', 1, { categoryIds: ['algorithms'] });
9-
server.create('category', { category: 'Asynchronous' });
10-
server.createList('crate', 15, { categoryIds: ['asynchronous'] });
11-
server.create('category', { category: 'Everything', crates_cnt: 1234 });
12-
});
4+
test('listing categories', async ({ page, msw, percy, a11y }) => {
5+
msw.db.category.create({ category: 'API bindings' });
6+
let algos = msw.db.category.create({ category: 'Algorithms' });
7+
msw.db.crate.create({ categories: [algos] });
8+
let async = msw.db.category.create({ category: 'Asynchronous' });
9+
Array.from({ length: 15 }).forEach(() => msw.db.crate.create({ categories: [async] }));
10+
msw.db.category.create({ category: 'Everything', crates_cnt: 1234 });
1311

1412
await page.goto('/categories');
1513

@@ -22,10 +20,8 @@ test.describe('Acceptance | categories', { tag: '@acceptance' }, () => {
2220
await a11y.audit();
2321
});
2422

25-
test('category/:category_id index default sort is recent-downloads', async ({ page, mirage, percy, a11y }) => {
26-
await mirage.addHook(server => {
27-
server.create('category', { category: 'Algorithms' });
28-
});
23+
test('category/:category_id index default sort is recent-downloads', async ({ page, msw, percy, a11y }) => {
24+
msw.db.category.create({ category: 'Algorithms' });
2925
await page.goto('/categories/algorithms');
3026

3127
await expect(page.locator('[data-test-category-sort] [data-test-current-order]')).toHaveText('Recent Downloads');
@@ -34,11 +30,9 @@ test.describe('Acceptance | categories', { tag: '@acceptance' }, () => {
3430
await a11y.audit();
3531
});
3632

37-
test('listing category slugs', async ({ page, mirage }) => {
38-
await mirage.addHook(server => {
39-
server.create('category', { category: 'Algorithms', description: 'Crates for algorithms' });
40-
server.create('category', { category: 'Asynchronous', description: 'Async crates' });
41-
});
33+
test('listing category slugs', async ({ page, msw }) => {
34+
msw.db.category.create({ category: 'Algorithms', description: 'Crates for algorithms' });
35+
msw.db.category.create({ category: 'Asynchronous', description: 'Async crates' });
4236
await page.goto('/category_slugs');
4337

4438
await expect(page.locator('[data-test-category-slug="algorithms"]')).toHaveText('algorithms');
@@ -50,10 +44,8 @@ test.describe('Acceptance | categories', { tag: '@acceptance' }, () => {
5044

5145
test.describe('Acceptance | categories (locale: de)', { tag: '@acceptance' }, () => {
5246
test.use({ locale: 'de' });
53-
test('listing categories', async ({ page, mirage }) => {
54-
await mirage.addHook(server => {
55-
server.create('category', { category: 'Everything', crates_cnt: 1234 });
56-
});
47+
test('listing categories', async ({ page, msw }) => {
48+
msw.db.category.create({ category: 'Everything', crates_cnt: 1234 });
5749
await page.goto('categories');
5850

5951
await expect(page.locator('[data-test-category="everything"] [data-test-crate-count]')).toHaveText('1.234 crates');

e2e/acceptance/crate-deletion.spec.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { expect, test } from '@/e2e/helper';
22

33
test.describe('Acceptance | crate deletion', { tag: '@acceptance' }, () => {
4-
test('happy path', async ({ page, mirage }) => {
5-
await mirage.addHook(server => {
6-
let user = server.create('user');
7-
authenticateAs(user);
4+
test('happy path', async ({ page, msw }) => {
5+
let user = msw.db.user.create();
6+
await msw.authenticateAs(user);
87

9-
let crate = server.create('crate', { name: 'foo' });
10-
server.create('version', { crate });
11-
server.create('crate-ownership', { crate, user });
12-
});
8+
let crate = msw.db.crate.create({ name: 'foo' });
9+
msw.db.version.create({ crate });
10+
msw.db.crateOwnership.create({ crate, user });
1311

1412
await page.goto('/crates/foo');
1513
await expect(page).toHaveURL('/crates/foo');
@@ -34,7 +32,7 @@ test.describe('Acceptance | crate deletion', { tag: '@acceptance' }, () => {
3432
let message = 'Crate foo has been successfully deleted.';
3533
await expect(page.locator('[data-test-notification-message="success"]')).toHaveText(message);
3634

37-
let crate = await page.evaluate(() => server.schema.crates.findBy({ name: 'foo' }));
35+
crate = msw.db.crate.findFirst({ where: { name: { equals: 'foo' } } });
3836
expect(crate).toBeNull();
3937
});
4038
});

0 commit comments

Comments
 (0)