Skip to content

Commit 10008ea

Browse files
committed
CrateSidebar: Show "Try on Rust Playground" button for crates that are available there
1 parent e4d37d9 commit 10008ea

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

app/components/crate-sidebar.hbs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,22 @@
130130
{{/if}}
131131
{{/unless}}
132132
</div>
133+
134+
{{#if this.playgroundLink}}
135+
<div>
136+
<a
137+
href={{this.playgroundLink}}
138+
target="_blank"
139+
rel="noopener noreferrer"
140+
local-class="playground-button"
141+
data-test-playground-button
142+
>
143+
Try on Rust Playground
144+
</a>
145+
<p local-class="playground-help" data-test-playground-help>
146+
The top 100 crates are available on the Rust Playground for you to
147+
try out directly in your browser.
148+
</p>
149+
</div>
150+
{{/if}}
133151
</section>

app/components/crate-sidebar.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { computed } from '@ember/object';
22
import { gt, readOnly } from '@ember/object/computed';
3+
import { inject as service } from '@ember/service';
34
import Component from '@glimmer/component';
45

6+
import * as Sentry from '@sentry/browser';
7+
import { didCancel } from 'ember-concurrency';
8+
59
const NUM_VERSIONS = 5;
610

711
export default class DownloadGraph extends Component {
12+
@service playground;
13+
814
@readOnly('args.crate.versions') sortedVersions;
915

1016
@computed('sortedVersions')
@@ -17,4 +23,28 @@ export default class DownloadGraph extends Component {
1723
get tomlSnippet() {
1824
return `${this.args.crate.name} = "${this.args.version.num}"`;
1925
}
26+
27+
get playgroundLink() {
28+
let playgroundCrates = this.playground.crates;
29+
if (!playgroundCrates) return;
30+
31+
let playgroundCrate = playgroundCrates.find(it => it.name === this.args.crate.name);
32+
if (!playgroundCrate) return;
33+
34+
return `https://play.rust-lang.org/?code=use%20${playgroundCrate.id}%3B%0A%0Afn%20main()%20%7B%0A%20%20%20%20%2F%2F%20try%20using%20the%20%60${playgroundCrate.id}%60%20crate%20here%0A%7D`;
35+
}
36+
37+
constructor() {
38+
super(...arguments);
39+
40+
// load Rust Playground crates list, if necessary
41+
if (!this.playground.crates) {
42+
this.playground.loadCratesTask.perform().catch(error => {
43+
if (!(didCancel(error) || error.isServerError || error.isNetworkError)) {
44+
// report unexpected errors to Sentry
45+
Sentry.captureException(error);
46+
}
47+
});
48+
}
49+
}
2050
}

app/components/crate-sidebar.module.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,17 @@ ul.owners, ul.keywords {
119119
.reverse-deps-link {
120120
composes: small from '../styles/shared/typography.module.css';
121121
}
122+
123+
.playground-button {
124+
composes: yellow-button small from '../styles/shared/buttons.module.css';
125+
justify-content: center;
126+
width: 220px;
127+
margin-top: 20px;
128+
}
129+
130+
.playground-help {
131+
composes: small from '../styles/shared/typography.module.css';
132+
max-width: 220px;
133+
text-align: justify;
134+
line-height: 1.3em;
135+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { render, settled, waitFor } from '@ember/test-helpers';
2+
import { module, test } from 'qunit';
3+
4+
import { defer } from 'rsvp';
5+
6+
import { hbs } from 'ember-cli-htmlbars';
7+
8+
import { setupRenderingTest } from 'cargo/tests/helpers';
9+
10+
import setupMirage from '../../helpers/setup-mirage';
11+
12+
module('Component | CrateSidebar | Playground Button', function (hooks) {
13+
setupRenderingTest(hooks);
14+
setupMirage(hooks);
15+
16+
hooks.beforeEach(function () {
17+
let crates = [
18+
{ name: 'addr2line', version: '0.14.1', id: 'addr2line' },
19+
{ name: 'adler', version: '0.2.3', id: 'adler' },
20+
{ name: 'adler32', version: '1.2.0', id: 'adler32' },
21+
{ name: 'ahash', version: '0.4.7', id: 'ahash' },
22+
{ name: 'aho-corasick', version: '0.7.15', id: 'aho_corasick' },
23+
{ name: 'ansi_term', version: '0.12.1', id: 'ansi_term' },
24+
{ name: 'ansi_term', version: '0.11.0', id: 'ansi_term_0_11_0' },
25+
];
26+
27+
this.server.get('https://play.rust-lang.org/meta/crates', { crates });
28+
});
29+
30+
test('button is hidden for unavailable crates', async function (assert) {
31+
let crate = this.server.create('crate', { name: 'foo' });
32+
this.server.create('version', { crate, num: '1.0.0' });
33+
34+
let store = this.owner.lookup('service:store');
35+
this.crate = await store.findRecord('crate', crate.name);
36+
this.version = (await this.crate.versions).firstObject;
37+
38+
await render(hbs`<CrateSidebar @crate={{this.crate}} @version={{this.version}} />`);
39+
assert.dom('[data-test-playground-button]').doesNotExist();
40+
assert.dom('[data-test-playground-help]').doesNotExist();
41+
});
42+
43+
test('button is visible for available crates', async function (assert) {
44+
let crate = this.server.create('crate', { name: 'aho-corasick' });
45+
this.server.create('version', { crate, num: '1.0.0' });
46+
47+
let store = this.owner.lookup('service:store');
48+
this.crate = await store.findRecord('crate', crate.name);
49+
this.version = (await this.crate.versions).firstObject;
50+
51+
let expectedHref =
52+
'https://play.rust-lang.org/?code=use%20aho_corasick%3B%0A%0Afn%20main()%20%7B%0A%20%20%20%20%2F%2F%20try%20using%20the%20%60aho_corasick%60%20crate%20here%0A%7D';
53+
54+
await render(hbs`<CrateSidebar @crate={{this.crate}} @version={{this.version}} />`);
55+
assert.dom('[data-test-playground-button]').hasAttribute('href', expectedHref);
56+
assert.dom('[data-test-playground-help]').exists();
57+
});
58+
59+
test('button is hidden while Playground request is pending', async function (assert) {
60+
let crate = this.server.create('crate', { name: 'aho-corasick' });
61+
this.server.create('version', { crate, num: '1.0.0' });
62+
63+
let deferred = defer();
64+
this.server.get('https://play.rust-lang.org/meta/crates', deferred.promise);
65+
66+
let store = this.owner.lookup('service:store');
67+
this.crate = await store.findRecord('crate', crate.name);
68+
this.version = (await this.crate.versions).firstObject;
69+
70+
render(hbs`<CrateSidebar @crate={{this.crate}} @version={{this.version}} />`);
71+
await waitFor('[data-test-versions]');
72+
assert.dom('[data-test-playground-button]').doesNotExist();
73+
assert.dom('[data-test-playground-help]').doesNotExist();
74+
75+
deferred.resolve({ crates: [] });
76+
await settled();
77+
});
78+
79+
test('button is hidden if the Playground request fails', async function (assert) {
80+
let crate = this.server.create('crate', { name: 'aho-corasick' });
81+
this.server.create('version', { crate, num: '1.0.0' });
82+
83+
this.server.get('https://play.rust-lang.org/meta/crates', {}, 500);
84+
85+
let store = this.owner.lookup('service:store');
86+
this.crate = await store.findRecord('crate', crate.name);
87+
this.version = (await this.crate.versions).firstObject;
88+
89+
await render(hbs`<CrateSidebar @crate={{this.crate}} @version={{this.version}} />`);
90+
assert.dom('[data-test-playground-button]').doesNotExist();
91+
assert.dom('[data-test-playground-help]').doesNotExist();
92+
});
93+
});

0 commit comments

Comments
 (0)