Skip to content

Commit 8363da4

Browse files
committed
Auto merge of #3304 - plaets:issue-3294, r=Turbo87
Improve invalid semver string handling * Add a tooltip message and a new release track indicator (`?`) to components/version-list/row * Handle null returned from `semverParse` in models/version * Add `loose` option to `semverParse` (https://github.com/npm/node-semver#functions) I placed the checks so that "invalid version" has higher priority than "first version" but lower than "yanked". I also modified css of the version list so that long versions do not overflow from tooltips. Before ![image](https://user-images.githubusercontent.com/49844988/108100395-272b2680-7086-11eb-838b-7341c16ee4b1.png) After ![image](https://user-images.githubusercontent.com/49844988/108100455-37db9c80-7086-11eb-9159-2565a90440df.png) Fixes #3294
2 parents e64886a + aebe476 commit 8363da4

File tree

8 files changed

+99
-9
lines changed

8 files changed

+99
-9
lines changed

app/components/download-graph.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export default class DownloadGraph extends Component {
108108

109109
let versionsList = [...versions];
110110
try {
111-
semverSort(versionsList);
111+
semverSort(versionsList, { loose: true });
112112
} catch {
113113
// Catches exceptions thrown when a version number is invalid
114114
// see issue #3295

app/components/version-list/row.hbs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@
99
...attributes
1010
>
1111
<div local-class="version">
12-
<div local-class="release-track">
12+
<div local-class="release-track" data-test-release-track>
1313
{{#if @version.yanked}}
1414
{{svg-jar "trash"}}
15+
{{else if @version.invalidSemver}}
16+
?
1517
{{else if @version.isFirst}}
1618
{{svg-jar "star"}}
1719
{{else}}
1820
{{@version.releaseTrack}}
1921
{{/if}}
2022

21-
<EmberTooltip @text={{this.releaseTrackTitle}} @side="right" />
23+
<EmberTooltip @text={{this.releaseTrackTitle}} @side="right" data-test-release-track-title/>
2224
</div>
2325

2426
<LinkTo
@@ -27,6 +29,7 @@
2729
local-class="num-link"
2830
{{on "focusin" (fn this.setFocused true)}}
2931
{{on "focusout" (fn this.setFocused false)}}
32+
data-test-release-track-link
3033
>
3134
{{@version.num}}
3235
</LinkTo>

app/components/version-list/row.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export default class VersionRow extends Component {
1313
if (version.yanked) {
1414
return 'This version was yanked';
1515
}
16+
if (version.invalidSemver) {
17+
return `Failed to parse version ${version.num}`;
18+
}
1619
if (version.isFirst) {
1720
return 'This is the first version that was released';
1821
}

app/components/version-list/row.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
z-index: 1;
4747
cursor: help;
4848
}
49+
50+
:global(.ember-tooltip) {
51+
word-break: break-all;
52+
}
4953
}
5054

5155
.version {

app/models/version.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,29 +44,41 @@ export default class Version extends Model {
4444
return oldestVersion === this;
4545
}
4646

47-
@cached get semver() {
48-
return semverParse(this.num);
47+
get semver() {
48+
return semverParse(this.num, { loose: true });
49+
}
50+
51+
get invalidSemver() {
52+
return this.semver === null;
4953
}
5054

5155
get isPrerelease() {
56+
if (this.invalidSemver) {
57+
return false;
58+
}
59+
5260
return this.semver.prerelease.length !== 0;
5361
}
5462

5563
get releaseTrack() {
64+
if (this.invalidSemver) {
65+
return null;
66+
}
67+
5668
let { semver } = this;
5769
return `${semver.major}.${semver.major === 0 ? semver.minor : 'x'}`;
5870
}
5971

6072
@cached get isHighestOfReleaseTrack() {
61-
if (this.isPrerelease) {
73+
if (this.isPrerelease || this.invalidSemver) {
6274
return false;
6375
}
6476

6577
let { crate, semver, releaseTrack } = this;
6678
let { versions } = crate;
6779
// find all other non-prerelease versions on the same release track
6880
let sameTrackVersions = versions.filter(
69-
it => it !== this && !it.yanked && !it.isPrerelease && it.releaseTrack === releaseTrack,
81+
it => it !== this && !it.yanked && !it.isPrerelease && !it.invalidSemver && it.releaseTrack === releaseTrack,
7082
);
7183
// check if we're the "highest"
7284
return sameTrackVersions.every(it => it.semver.compare(semver) === -1);

mirage/serializers/crate.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ export default BaseSerializer.extend({
5656
versions = versions.filter(it => !it.yanked);
5757

5858
let versionNums = versions.models.map(it => it.num);
59-
semverSort(versionNums);
59+
semverSort(versionNums, { loose: true });
6060
hash.max_version = versionNums[0] ?? '0.0.0';
61-
hash.max_stable_version = versionNums.find(it => !prerelease(it)) ?? null;
61+
hash.max_stable_version = versionNums.find(it => !prerelease(it, { loose: true })) ?? null;
6262

6363
let newestVersions = versions.models.sort((a, b) => compareIsoDates(b.updated_at, a.updated_at));
6464
hash.newest_version = newestVersions[0]?.num ?? '0.0.0';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { render } from '@ember/test-helpers';
2+
import { module, test } from 'qunit';
3+
4+
import { hbs } from 'ember-cli-htmlbars';
5+
6+
import { setupRenderingTest } from 'cargo/tests/helpers';
7+
8+
import setupMirage from '../helpers/setup-mirage';
9+
10+
module('Component | VersionList::Row', function (hooks) {
11+
setupRenderingTest(hooks);
12+
setupMirage(hooks);
13+
14+
test('handle non-standard semver strings', async function (assert) {
15+
let crate = this.server.create('crate', { name: 'foo' });
16+
this.server.create('version', { crate, num: '0.4.0-alpha.01', created_at: Date.now(), updated_at: Date.now() });
17+
this.server.create('version', { crate, num: '0.3.0-alpha.01', created_at: Date.now(), updated_at: Date.now() });
18+
19+
let store = this.owner.lookup('service:store');
20+
let crateRecord = await store.findRecord('crate', crate.name);
21+
let versions = (await crateRecord.versions).toArray();
22+
this.firstVersion = versions[0];
23+
this.secondVersion = versions[1];
24+
25+
await render(hbs`<VersionList::Row @version={{this.firstVersion}} />`);
26+
assert.dom('[data-test-release-track] svg').exists();
27+
assert.dom('[data-test-release-track-link]').hasText('0.4.0-alpha.01');
28+
29+
await render(hbs`<VersionList::Row @version={{this.secondVersion}} />`);
30+
assert.dom('[data-test-release-track]').hasText('0.3');
31+
assert.dom('[data-test-release-track-link]').hasText('0.3.0-alpha.01');
32+
});
33+
34+
test('handle node-semver parsing errors', async function (assert) {
35+
let crate = this.server.create('crate', { name: 'foo' });
36+
let version = '18446744073709551615.18446744073709551615.18446744073709551615';
37+
this.server.create('version', { crate, num: version });
38+
39+
let store = this.owner.lookup('service:store');
40+
let crateRecord = await store.findRecord('crate', crate.name);
41+
this.version = (await crateRecord.versions).toArray()[0];
42+
43+
await render(hbs`<VersionList::Row @version={{this.version}} />`);
44+
assert.dom('[data-test-release-track]').hasText('?');
45+
assert.dom('[data-test-release-track-link]').hasText(version);
46+
});
47+
});

tests/models/version-test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,38 @@ module('Model | Version', function (hooks) {
7878
assert.true(isPrerelease);
7979
assert.strictEqual(releaseTrack, '0.0');
8080
});
81+
82+
test('parses 0.3.0-alpha.01 (non-standard) correctly', async function (assert) {
83+
let { semver, releaseTrack, isPrerelease } = await prepare(this, { num: '0.3.0-alpha.01' });
84+
assert.strictEqual(semver.major, 0);
85+
assert.strictEqual(semver.minor, 3);
86+
assert.strictEqual(semver.patch, 0);
87+
assert.deepEqual(semver.prerelease, ['alpha', 1]);
88+
assert.true(isPrerelease);
89+
assert.strictEqual(releaseTrack, '0.3');
90+
});
91+
92+
test('invalidSemver is true for unparseable versions', async function (assert) {
93+
let { invalidSemver } = await prepare(this, {
94+
num: '18446744073709551615.18446744073709551615.1844674407370955161',
95+
});
96+
assert.true(invalidSemver);
97+
});
8198
});
8299

83100
module('isHighestOfReleaseTrack', function () {
84101
test('happy path', async function (assert) {
85102
let nums = [
86103
'0.4.0-rc.1',
104+
'0.3.24-alpha.02',
87105
'0.3.23',
88106
'0.3.22',
89107
'0.3.21-pre.0',
90108
'0.3.20',
91109
'0.3.3',
92110
'0.3.2',
93111
'0.3.1',
112+
'0.3.0-alpha.01',
94113
'0.3.0',
95114
'0.2.1',
96115
'0.2.0',
@@ -110,13 +129,15 @@ module('Model | Version', function (hooks) {
110129
versions.map(it => ({ num: it.num, isHighestOfReleaseTrack: it.isHighestOfReleaseTrack })),
111130
[
112131
{ num: '0.4.0-rc.1', isHighestOfReleaseTrack: false },
132+
{ num: '0.3.24-alpha.02', isHighestOfReleaseTrack: false },
113133
{ num: '0.3.23', isHighestOfReleaseTrack: true },
114134
{ num: '0.3.22', isHighestOfReleaseTrack: false },
115135
{ num: '0.3.21-pre.0', isHighestOfReleaseTrack: false },
116136
{ num: '0.3.20', isHighestOfReleaseTrack: false },
117137
{ num: '0.3.3', isHighestOfReleaseTrack: false },
118138
{ num: '0.3.2', isHighestOfReleaseTrack: false },
119139
{ num: '0.3.1', isHighestOfReleaseTrack: false },
140+
{ num: '0.3.0-alpha.01', isHighestOfReleaseTrack: false },
120141
{ num: '0.3.0', isHighestOfReleaseTrack: false },
121142
{ num: '0.2.1', isHighestOfReleaseTrack: true },
122143
{ num: '0.2.0', isHighestOfReleaseTrack: false },

0 commit comments

Comments
 (0)