From 976f0b2272d5fda60320cf65ec5f5771fd5ff1b9 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Tue, 9 Feb 2021 23:41:26 +0100 Subject: [PATCH 1/2] Implement `/crates/:crate_id/range/:range` route ... that redirects the user to the highest version that satisfies the specified semver range --- app/router.js | 1 + app/routes/crate/range.js | 26 +++++++++++ tests/routes/crate/range-test.js | 78 ++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 app/routes/crate/range.js create mode 100644 tests/routes/crate/range-test.js diff --git a/app/router.js b/app/router.js index 1358ca01bee..d1cb61f263b 100644 --- a/app/router.js +++ b/app/router.js @@ -15,6 +15,7 @@ Router.map(function () { this.route('dependencies'); this.route('version', { path: '/:version_num' }); this.route('version-dependencies', { path: '/:version_num/dependencies' }); + this.route('range', { path: '/range/:range' }); this.route('reverse-dependencies', { path: 'reverse_dependencies' }); diff --git a/app/routes/crate/range.js b/app/routes/crate/range.js new file mode 100644 index 00000000000..c18532e0b0c --- /dev/null +++ b/app/routes/crate/range.js @@ -0,0 +1,26 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; + +import maxSatisfying from 'semver/ranges/max-satisfying'; + +export default class VersionRoute extends Route { + @service notifications; + @service router; + + async model({ range }) { + let crate = this.modelFor('crate'); + + let versions = await crate.get('versions'); + let allVersionNums = versions.map(it => it.num); + let unyankedVersionNums = versions.filter(it => !it.yanked).map(it => it.num); + + // find a version that matches the specified range + let versionNum = maxSatisfying(unyankedVersionNums, range) ?? maxSatisfying(allVersionNums, range); + if (!versionNum) { + this.notifications.error(`No matching version of crate '${crate.name}' found for: ${range}`); + this.replaceWith('crate.index'); + } + + this.router.replaceWith('crate.version', versionNum); + } +} diff --git a/tests/routes/crate/range-test.js b/tests/routes/crate/range-test.js new file mode 100644 index 00000000000..eec13335869 --- /dev/null +++ b/tests/routes/crate/range-test.js @@ -0,0 +1,78 @@ +import { currentURL, visit } from '@ember/test-helpers'; +import { module, test } from 'qunit'; + +import { setupApplicationTest } from 'cargo/tests/helpers'; + +module('Route | crate.range', function (hooks) { + setupApplicationTest(hooks); + + test('happy path', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0' }); + this.server.create('version', { crate, num: '1.2.0' }); + this.server.create('version', { crate, num: '1.2.3' }); + + await visit('/crates/foo/range/^1.1.0'); + assert.equal(currentURL(), `/crates/foo/1.2.3`); + assert.dom('[data-test-crate-name]').hasText('foo'); + assert.dom('[data-test-crate-version]').hasText('1.2.3'); + assert.dom('[data-test-notification-message]').doesNotExist(); + }); + + test('happy path with tilde range', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0' }); + this.server.create('version', { crate, num: '1.1.1' }); + this.server.create('version', { crate, num: '1.2.0' }); + + await visit('/crates/foo/range/~1.1.0'); + assert.equal(currentURL(), `/crates/foo/1.1.1`); + assert.dom('[data-test-crate-name]').hasText('foo'); + assert.dom('[data-test-crate-version]').hasText('1.1.1'); + assert.dom('[data-test-notification-message]').doesNotExist(); + }); + + test('ignores yanked versions if possible', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0' }); + this.server.create('version', { crate, num: '1.1.1' }); + this.server.create('version', { crate, num: '1.2.0', yanked: true }); + + await visit('/crates/foo/range/^1.0.0'); + assert.equal(currentURL(), `/crates/foo/1.1.1`); + assert.dom('[data-test-crate-name]').hasText('foo'); + assert.dom('[data-test-crate-version]').hasText('1.1.1'); + assert.dom('[data-test-notification-message]').doesNotExist(); + }); + + test('falls back to yanked version if necessary', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate, num: '1.0.0', yanked: true }); + this.server.create('version', { crate, num: '1.1.0', yanked: true }); + this.server.create('version', { crate, num: '1.1.1', yanked: true }); + this.server.create('version', { crate, num: '2.0.0' }); + + await visit('/crates/foo/range/^1.0.0'); + assert.equal(currentURL(), `/crates/foo/1.1.1`); + assert.dom('[data-test-crate-name]').hasText('foo'); + assert.dom('[data-test-crate-version]').hasText('1.1.1'); + assert.dom('[data-test-notification-message]').doesNotExist(); + }); + + test('redirects to main crate page if no match found', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0' }); + this.server.create('version', { crate, num: '1.1.1' }); + this.server.create('version', { crate, num: '2.0.0' }); + + await visit('/crates/foo/range/^3'); + assert.equal(currentURL(), `/crates/foo`); + assert.dom('[data-test-crate-name]').hasText('foo'); + assert.dom('[data-test-crate-version]').hasText('2.0.0'); + assert.dom('[data-test-notification-message="error"]').hasText("No matching version of crate 'foo' found for: ^3"); + }); +}); From 9bfefad17b6d85964f7ab9578078ec66925ee687 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Tue, 9 Feb 2021 23:41:44 +0100 Subject: [PATCH 2/2] LinkToDep: Use new `crate.range` route --- app/components/dependency-list/row.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/dependency-list/row.hbs b/app/components/dependency-list/row.hbs index ad561c7f26f..546d38eb72a 100644 --- a/app/components/dependency-list/row.hbs +++ b/app/components/dependency-list/row.hbs @@ -12,8 +12,8 @@