Skip to content

Commit 9857ef0

Browse files
authored
Fix unintentional crate.newest_version reset (#11795)
The `GET /summary` call correctly initialized the `newest_version` fields of the crates, but when navigating to the crate page, the `GET /crate/foo` call by default uses an `include` query parameter that excludes the `newest_version` field from being loaded on the backend, which causes it to be returned with a value of `0.0.0` (for legacy reasons). This then reset the field on the model, causing it to be displayed incorrectly when navigating back to the front page. This commit fixes the issue by adjusting the crate serializer to ignore `0.0.0` values for `max_version` and `newest_version`, so that they won't override previously loaded values.
1 parent 2891d6e commit 9857ef0

File tree

3 files changed

+150
-9
lines changed

3 files changed

+150
-9
lines changed

app/serializers/crate.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ApplicationSerializer from './application';
22

3-
const SKIP_NULL_FIELDS = new Set(['categories', 'keywords']);
3+
const SKIP_NULL_FIELDS = new Set(['categories', 'keywords', 'max_stable_version']);
44

55
export default class CrateSerializer extends ApplicationSerializer {
66
isNewSerializerAPI = true;
@@ -17,15 +17,33 @@ export default class CrateSerializer extends ApplicationSerializer {
1717
// We don't want existing relationships overwritten by results with null values.
1818
// See: https://github.com/rust-lang/crates.io/issues/10711
1919
if (payload.crates) {
20-
payload.crates = payload.crates.map(crate => {
21-
for (const rel of SKIP_NULL_FIELDS) {
22-
if (crate[rel] === null) {
23-
delete crate[rel];
24-
}
25-
}
26-
return crate;
27-
});
20+
payload.crates.forEach(crate => removeNullFields(crate));
2821
}
22+
2923
return super.normalizeQueryResponse(...arguments);
3024
}
25+
26+
normalizeQueryRecordResponse(_store, _modelClass, payload) {
27+
if (payload.crate) {
28+
removeNullFields(payload.crate);
29+
}
30+
31+
return super.normalizeQueryResponse(...arguments);
32+
}
33+
}
34+
35+
function removeNullFields(crate) {
36+
for (let rel of SKIP_NULL_FIELDS) {
37+
if (crate[rel] === null) {
38+
delete crate[rel];
39+
}
40+
}
41+
42+
if (crate.max_version == '0.0.0') {
43+
delete crate.max_version;
44+
}
45+
46+
if (crate.newest_version == '0.0.0') {
47+
delete crate.newest_version;
48+
}
3149
}

e2e/bugs/11772.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { test, expect } from '@/e2e/helper';
2+
3+
test.describe('Bug #11772', { tag: '@bugs' }, () => {
4+
function prepare(msw: any) {
5+
// Create a crate that will appear in "New Crates" section
6+
let newCrate = msw.db.crate.create({ name: 'test-crate' });
7+
msw.db.version.create({ crate: newCrate, num: '1.2.3' });
8+
}
9+
10+
test('crate versions should remain correct after navigating back from crate details', async ({ page, msw }) => {
11+
prepare(msw);
12+
13+
// Visit homepage
14+
await page.goto('/');
15+
await expect(page).toHaveURL('/');
16+
17+
// Verify initial correct version displays
18+
await expect(page.locator('[data-test-new-crates] [data-test-crate-link]')).toContainText('test-crate v1.2.3');
19+
await expect(page.locator('[data-test-just-updated] [data-test-crate-link]')).toContainText('test-crate v1.2.3');
20+
21+
// Click on a crate to navigate to its details page
22+
await page.click('[data-test-new-crates] [data-test-crate-link]');
23+
24+
// Verify we're on the crate details page
25+
await expect(page).toHaveURL('/crates/test-crate');
26+
27+
await page.goto('/'); // Re-visit to simulate the back navigation
28+
29+
// Versions should still be displayed correctly, not v0.0.0
30+
await expect(page.locator('[data-test-new-crates] [data-test-crate-link]')).toContainText('test-crate v1.2.3');
31+
await expect(page.locator('[data-test-just-updated] [data-test-crate-link]')).toContainText('test-crate v1.2.3');
32+
});
33+
34+
test('crates with actual v0.0.0 versions should display correctly', async ({ page, msw }) => {
35+
// Create a crate with an actual v0.0.0 version
36+
let zeroCrate = msw.db.crate.create({ name: 'test-zero-crate' });
37+
msw.db.version.create({ crate: zeroCrate, num: '0.0.0' });
38+
39+
// Visit homepage
40+
await page.goto('/');
41+
await expect(page).toHaveURL('/');
42+
43+
// Should correctly display v0.0.0 for crates that actually have that version
44+
await expect(page.locator('[data-test-new-crates] [data-test-crate-link]')).toContainText('test-zero-crate v0.0.0');
45+
46+
// Click on the crate to navigate to its details page
47+
await page.click('[data-test-new-crates] [data-test-crate-link]');
48+
49+
// Verify we're on the crate details page
50+
await expect(page).toHaveURL('/crates/test-zero-crate');
51+
52+
await page.goto('/'); // Re-visit to simulate the back navigation
53+
54+
// Should still display v0.0.0 correctly (this is the intended behavior)
55+
await expect(page.locator('[data-test-new-crates] [data-test-crate-link]')).toContainText('test-zero-crate v0.0.0');
56+
});
57+
});

tests/bugs/11772-test.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { click, currentURL, visit } from '@ember/test-helpers';
2+
import { module, test } from 'qunit';
3+
4+
import { setupApplicationTest } from 'crates-io/tests/helpers';
5+
6+
module('Bug #11772', function (hooks) {
7+
setupApplicationTest(hooks);
8+
9+
function prepare(context) {
10+
let { db } = context;
11+
12+
// Create a crate that will appear in "New Crates" section
13+
let newCrate = db.crate.create({ name: 'test-crate' });
14+
db.version.create({ crate: newCrate, num: '1.2.3' });
15+
}
16+
17+
test('crate versions should remain correct after navigating back from crate details', async function (assert) {
18+
prepare(this);
19+
20+
// Visit homepage
21+
await visit('/');
22+
assert.strictEqual(currentURL(), '/');
23+
24+
// Verify initial correct version displays
25+
assert.dom('[data-test-new-crates] [data-test-crate-link]').containsText('test-crate v1.2.3');
26+
assert.dom('[data-test-just-updated] [data-test-crate-link]').containsText('test-crate v1.2.3');
27+
28+
// Click on a crate to navigate to its details page
29+
await click('[data-test-new-crates] [data-test-crate-link]');
30+
31+
// Verify we're on the crate details page
32+
assert.strictEqual(currentURL(), '/crates/test-crate');
33+
34+
await visit('/'); // Re-visit to simulate the back navigation
35+
36+
// Versions should still be displayed correctly, not v0.0.0
37+
assert.dom('[data-test-new-crates] [data-test-crate-link]').containsText('test-crate v1.2.3');
38+
assert.dom('[data-test-just-updated] [data-test-crate-link]').containsText('test-crate v1.2.3');
39+
});
40+
41+
test('crates with actual v0.0.0 versions should display correctly', async function (assert) {
42+
let { db } = this;
43+
44+
// Create a crate with an actual v0.0.0 version
45+
let zeroCrate = db.crate.create({ name: 'test-zero-crate' });
46+
db.version.create({ crate: zeroCrate, num: '0.0.0' });
47+
48+
// Visit homepage
49+
await visit('/');
50+
assert.strictEqual(currentURL(), '/');
51+
52+
// Should correctly display v0.0.0 for crates that actually have that version
53+
assert.dom('[data-test-new-crates] [data-test-crate-link]').containsText('test-zero-crate v0.0.0');
54+
55+
// Click on the crate to navigate to its details page
56+
await click('[data-test-new-crates] [data-test-crate-link]');
57+
58+
// Verify we're on the crate details page
59+
assert.strictEqual(currentURL(), '/crates/test-zero-crate');
60+
61+
await visit('/'); // Re-visit to simulate the back navigation
62+
63+
// Should still display v0.0.0 correctly (this is the intended behavior)
64+
assert.dom('[data-test-new-crates] [data-test-crate-link]').containsText('test-zero-crate v0.0.0');
65+
});
66+
});

0 commit comments

Comments
 (0)