Skip to content

Commit 9b763ce

Browse files
authored
Merge pull request #9866 from Turbo87/readme-loading
Show "Retry" button for README loading errors
2 parents e22c684 + b25f4e3 commit 9b763ce

File tree

8 files changed

+81
-8
lines changed

8 files changed

+81
-8
lines changed

app/controllers/crate/version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default class CrateVersionController extends Controller {
4343
: await version.loadReadmeTask.perform();
4444

4545
// If the README contains `language-mermaid` we ensure that the `mermaid` library has loaded before we continue
46-
if (readme.includes('language-mermaid') && !this.mermaid.loadTask.lastSuccessful?.value) {
46+
if (readme && readme.includes('language-mermaid') && !this.mermaid.loadTask.lastSuccessful?.value) {
4747
try {
4848
await this.mermaid.loadTask.perform();
4949
} catch (error) {

app/models/version.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ export default class Version extends Model {
122122
loadReadmeTask = keepLatestTask(async () => {
123123
if (this.readme_path) {
124124
let response = await fetch(this.readme_path);
125+
if (response.status === 404 || response.status === 403) {
126+
return;
127+
}
128+
125129
if (!response.ok) {
126130
throw new Error(`README request for ${this.crateName} v${this.num} failed`);
127131
}

app/styles/crate/version.module.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
}
2626
}
2727

28-
.no-readme {
28+
.no-readme, .readme-error {
2929
padding: var(--space-l) var(--space-s);
3030
text-align: center;
3131
font-size: 20px;
@@ -39,6 +39,13 @@
3939
}
4040
}
4141

42+
.retry-button {
43+
composes: yellow-button from '../shared/buttons.module.css';
44+
display: block;
45+
text-align: center;
46+
margin: var(--space-s) auto 0;
47+
}
48+
4249
.placeholder-title {
4350
width: 30%;
4451
height: 25px;

app/templates/crate/version.hbs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
<article aria-label="Readme" data-test-readme>
2626
<RenderedHtml @html={{this.readme}} local-class="readme" />
2727
</article>
28+
{{else if this.loadReadmeTask.last.error}}
29+
<div local-class="readme-error" data-test-readme-error>
30+
Failed to load <code>README</code> file for {{this.crate.name}} v{{this.currentVersion.num}}
31+
32+
<button
33+
type="button"
34+
local-class="retry-button"
35+
data-test-retry-button
36+
{{on "click" (perform this.loadReadmeTask)}}
37+
>
38+
Retry
39+
</button>
40+
</div>
2841
{{else}}
2942
<div local-class="no-readme" data-test-no-readme>
3043
{{this.crate.name}} v{{this.currentVersion.num}} appears to have no <code>README.md</code> file

e2e/acceptance/readme-rendering.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { test, expect } from '@/e2e/helper';
1+
import { expect, test } from '@/e2e/helper';
2+
import { Response } from 'miragejs';
23

34
const README_HTML = `
45
<p><strong>Serde is a framework for <em>ser</em>ializing and <em>de</em>serializing Rust data structures efficiently and generically.</strong></p>
@@ -112,4 +113,29 @@ test.describe('Acceptance | README rendering', { tag: '@acceptance' }, () => {
112113
await page.goto('/crates/serde');
113114
await expect(page.locator('[data-test-no-readme]')).toBeVisible();
114115
});
116+
117+
test('it shows an error message and retry button if loading fails', async ({ page, mirage }) => {
118+
await page.exposeBinding('resp200', () => new Response(200, { 'Content-Type': 'text/html' }, 'foo'));
119+
120+
await mirage.addHook(server => {
121+
let crate = server.create('crate', { name: 'serde' });
122+
server.create('version', { crate, num: '1.0.0' });
123+
124+
server.logging = true;
125+
// Simulate a server error when fetching the README
126+
server.get('/api/v1/crates/:name/:version/readme', {}, 500);
127+
});
128+
129+
await page.goto('/crates/serde');
130+
await expect(page.locator('[data-test-readme-error]')).toBeVisible();
131+
await expect(page.locator('[data-test-retry-button]')).toBeVisible();
132+
133+
await page.evaluate(() => {
134+
// Simulate a successful response when fetching the README
135+
server.get('/api/v1/crates/:name/:version/readme', {});
136+
});
137+
138+
await page.click('[data-test-retry-button]');
139+
await expect(page.locator('[data-test-readme]')).toHaveText('{}');
140+
});
115141
});

mirage/route-handlers/crates.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,12 @@ export function register(server) {
374374
const { name, version: versionNum } = request.params;
375375
const crate = schema.crates.findBy({ name });
376376
if (!crate) {
377-
return new Response(404, { 'Content-Type': 'text/html' }, '');
377+
return new Response(403, { 'Content-Type': 'text/html' }, '');
378378
}
379379

380380
const version = schema.versions.findBy({ crateId: crate.id, num: versionNum });
381381
if (!version || !version.readme) {
382-
return new Response(404, { 'Content-Type': 'text/html' }, '');
382+
return new Response(403, { 'Content-Type': 'text/html' }, '');
383383
}
384384

385385
return new Response(200, { 'Content-Type': 'text/html' }, version.readme);

tests/acceptance/readme-rendering-test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { click } from '@ember/test-helpers';
12
import { module, test } from 'qunit';
23

34
import percySnapshot from '@percy/ember';
5+
import { Response } from 'miragejs';
46

57
import { setupApplicationTest } from 'crates-io/tests/helpers';
68

@@ -112,4 +114,25 @@ module('Acceptance | README rendering', function (hooks) {
112114
await visit('/crates/serde');
113115
assert.dom('[data-test-no-readme]').exists();
114116
});
117+
118+
test('it shows an error message and retry button if loading fails', async function (assert) {
119+
let crate = this.server.create('crate', { name: 'serde' });
120+
this.server.create('version', { crate, num: '1.0.0' });
121+
122+
// Simulate a server error when fetching the README
123+
this.server.get('/api/v1/crates/:name/:version/readme', {}, 500);
124+
125+
await visit('/crates/serde');
126+
assert.dom('[data-test-readme-error]').exists();
127+
assert.dom('[data-test-retry-button]').exists();
128+
129+
// Simulate a successful response when fetching the README
130+
this.server.get(
131+
'/api/v1/crates/:name/:version/readme',
132+
() => new Response(200, { 'Content-Type': 'text/html' }, 'foo'),
133+
);
134+
135+
await click('[data-test-retry-button]');
136+
assert.dom('[data-test-readme]').hasText('foo');
137+
});
115138
});

tests/mirage/crates/versions/readme-test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ module('Mirage | GET /api/v1/crates/:id/:version/readme', function (hooks) {
1111

1212
test('returns 404 for unknown crates', async function (assert) {
1313
let response = await fetch('/api/v1/crates/foo/1.0.0/readme');
14-
assert.strictEqual(response.status, 404);
14+
assert.strictEqual(response.status, 403);
1515
assert.strictEqual(await response.text(), '');
1616
});
1717

1818
test('returns 404 for unknown versions', async function (assert) {
1919
this.server.create('crate', { name: 'rand' });
2020

2121
let response = await fetch('/api/v1/crates/rand/1.0.0/readme');
22-
assert.strictEqual(response.status, 404);
22+
assert.strictEqual(response.status, 403);
2323
assert.strictEqual(await response.text(), '');
2424
});
2525

@@ -28,7 +28,7 @@ module('Mirage | GET /api/v1/crates/:id/:version/readme', function (hooks) {
2828
this.server.create('version', { crate, num: '1.0.0' });
2929

3030
let response = await fetch('/api/v1/crates/rand/1.0.0/readme');
31-
assert.strictEqual(response.status, 404);
31+
assert.strictEqual(response.status, 403);
3232
assert.strictEqual(await response.text(), '');
3333
});
3434

0 commit comments

Comments
 (0)