Skip to content

Commit ec6bacf

Browse files
committed
Auto merge of #4476 - Turbo87:crate-range-errors, r=Turbo87
crate.range: Show error page if data fails to load Similar to the crate and version pages. This ensures that the URL stays the same so it can be fixed if there was a typo. <img width="753" alt="Bildschirmfoto 2022-01-22 um 21 21 09" src="https://user-images.githubusercontent.com/141300/150654244-0825e7b5-f844-451b-bbf1-2b53d3708d22.png">
2 parents 8869cab + ee7658d commit ec6bacf

File tree

2 files changed

+66
-18
lines changed

2 files changed

+66
-18
lines changed

app/routes/crate/range.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,26 @@ export default class VersionRoute extends Route {
1111
@service notifications;
1212
@service router;
1313

14-
async model({ range }) {
14+
async model({ range }, transition) {
1515
let crate = this.modelFor('crate');
1616

17-
let versions = await crate.get('versions');
18-
let allVersionNums = versions.map(it => it.num);
19-
let unyankedVersionNums = versions.filter(it => !it.yanked).map(it => it.num);
17+
try {
18+
let versions = await crate.hasMany('versions').load();
19+
let allVersionNums = versions.map(it => it.num);
20+
let unyankedVersionNums = versions.filter(it => !it.yanked).map(it => it.num);
2021

21-
let npmRange = cargoRangeToNpm(range);
22-
// find a version that matches the specified range
23-
let versionNum = maxSatisfying(unyankedVersionNums, npmRange) ?? maxSatisfying(allVersionNums, npmRange);
24-
if (!versionNum) {
25-
this.notifications.error(`No matching version of crate '${crate.name}' found for: ${range}`);
26-
this.router.replaceWith('crate.index');
22+
let npmRange = cargoRangeToNpm(range);
23+
// find a version that matches the specified range
24+
let versionNum = maxSatisfying(unyankedVersionNums, npmRange) ?? maxSatisfying(allVersionNums, npmRange);
25+
if (versionNum) {
26+
this.router.replaceWith('crate.version', versionNum);
27+
} else {
28+
let title = `${crate.name}: No matching version found for ${range}`;
29+
this.router.replaceWith('catch-all', { transition, title });
30+
}
31+
} catch (error) {
32+
let title = `${crate.name}: Failed to load version data`;
33+
this.router.replaceWith('catch-all', { transition, error, title, tryAgain: true });
2734
}
28-
29-
this.router.replaceWith('crate.version', versionNum);
3035
}
3136
}

tests/routes/crate/range-test.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { currentURL, visit } from '@ember/test-helpers';
1+
import { currentURL } from '@ember/test-helpers';
22
import { module, test } from 'qunit';
33

44
import { setupApplicationTest } from 'cargo/tests/helpers';
55

6+
import { visit } from '../../helpers/visit-ignoring-abort';
7+
68
module('Route | crate.range', function (hooks) {
79
setupApplicationTest(hooks);
810

@@ -76,17 +78,58 @@ module('Route | crate.range', function (hooks) {
7678
assert.dom('[data-test-notification-message]').doesNotExist();
7779
});
7880

79-
test('redirects to main crate page if no match found', async function (assert) {
81+
test('shows an error page if crate not found', async function (assert) {
82+
await visit('/crates/foo/range/^3');
83+
assert.equal(currentURL(), '/crates/foo/range/%5E3');
84+
assert.dom('[data-test-404-page]').exists();
85+
assert.dom('[data-test-title]').hasText('Crate not found');
86+
assert.dom('[data-test-go-back]').exists();
87+
assert.dom('[data-test-try-again]').doesNotExist();
88+
});
89+
90+
test('shows an error page if crate fails to load', async function (assert) {
91+
this.server.get('/api/v1/crates/:crate_name', {}, 500);
92+
93+
await visit('/crates/foo/range/^3');
94+
assert.equal(currentURL(), '/crates/foo/range/%5E3');
95+
assert.dom('[data-test-404-page]').exists();
96+
assert.dom('[data-test-title]').hasText('Crate failed to load');
97+
assert.dom('[data-test-go-back]').doesNotExist();
98+
assert.dom('[data-test-try-again]').exists();
99+
});
100+
101+
test('shows an error page if no match found', async function (assert) {
80102
let crate = this.server.create('crate', { name: 'foo' });
81103
this.server.create('version', { crate, num: '1.0.0' });
82104
this.server.create('version', { crate, num: '1.1.0' });
83105
this.server.create('version', { crate, num: '1.1.1' });
84106
this.server.create('version', { crate, num: '2.0.0' });
85107

86108
await visit('/crates/foo/range/^3');
87-
assert.equal(currentURL(), `/crates/foo`);
88-
assert.dom('[data-test-crate-name]').hasText('foo');
89-
assert.dom('[data-test-crate-version]').hasText('2.0.0');
90-
assert.dom('[data-test-notification-message="error"]').hasText("No matching version of crate 'foo' found for: ^3");
109+
assert.equal(currentURL(), '/crates/foo/range/%5E3');
110+
assert.dom('[data-test-404-page]').exists();
111+
assert.dom('[data-test-title]').hasText('foo: No matching version found for ^3');
112+
assert.dom('[data-test-go-back]').exists();
113+
assert.dom('[data-test-try-again]').doesNotExist();
114+
});
115+
116+
test('shows an error page if versions fail to load', async function (assert) {
117+
let crate = this.server.create('crate', { name: 'foo' });
118+
this.server.create('version', { crate, num: '3.2.1' });
119+
120+
this.server.get('/api/v1/crates/:crate_name/versions', {}, 500);
121+
122+
// Load `crate` and then explicitly unload the side-loaded `versions`.
123+
let store = this.owner.lookup('service:store');
124+
let crateRecord = await store.findRecord('crate', 'foo');
125+
let versions = crateRecord.hasMany('versions').value();
126+
versions.forEach(record => record.unloadRecord());
127+
128+
await visit('/crates/foo/range/^3');
129+
assert.equal(currentURL(), '/crates/foo/range/%5E3');
130+
assert.dom('[data-test-404-page]').exists();
131+
assert.dom('[data-test-title]').hasText('foo: Failed to load version data');
132+
assert.dom('[data-test-go-back]').doesNotExist();
133+
assert.dom('[data-test-try-again]').exists();
91134
});
92135
});

0 commit comments

Comments
 (0)