From 6bd0143a3613dfb712499314f7bc868acde03c0b Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Sat, 14 Dec 2019 22:53:56 +0400 Subject: [PATCH 01/18] Add flash message with info after login - Add new types for flash message - Show info type flash message after login via github - Add missed tests Small initial step to address #19 --- app/components/flash-message.js | 7 ++++- app/routes/login.js | 7 +++++ app/services/flash-messages.js | 3 +- app/styles/app.scss | 18 ++++++++++-- tests/unit/components/flash-message-test.js | 31 +++++++++++++++++++++ 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/unit/components/flash-message-test.js diff --git a/app/components/flash-message.js b/app/components/flash-message.js index ef807af025a..3fcac9cf3fe 100644 --- a/app/components/flash-message.js +++ b/app/components/flash-message.js @@ -1,12 +1,17 @@ import Component from '@ember/component'; +import { computed } from '@ember/object'; import { readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; export default Component.extend({ flashMessages: service(), message: readOnly('flashMessages.message'), + options: readOnly('flashMessages.options'), + type: computed('flashMessages.options', function () { + return this.get('flashMessages.options.type') || 'warning'; + }), elementId: 'flash', tagName: 'p', - classNameBindings: ['message:shown'], + classNameBindings: ['message:shown', 'type'], }); diff --git a/app/routes/login.js b/app/routes/login.js index bed6ee39b3e..2de32c588e2 100644 --- a/app/routes/login.js +++ b/app/routes/login.js @@ -1,5 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { htmlSafe } from '@ember/string'; /** * This route will open a popup window directed at the `github-login` route. @@ -67,6 +68,12 @@ export default Route.extend({ let user = this.store.push(this.store.normalize('user', data.user)); let transition = this.get('session.savedTransition'); this.session.loginUser(user); + + const messageAfterLogin = htmlSafe( + "Welcome to crates.io! Visit account settings to verify your email address and create an API token!", + ); + this.flashMessages.show(messageAfterLogin, { type: 'info' }); + if (transition) { transition.retry(); } diff --git a/app/services/flash-messages.js b/app/services/flash-messages.js index e8f2568fe54..0cfa65549ec 100644 --- a/app/services/flash-messages.js +++ b/app/services/flash-messages.js @@ -4,8 +4,9 @@ export default Service.extend({ message: null, _nextMessage: null, - show(message) { + show(message, options = { type: 'warning' }) { this.set('message', message); + this.set('options', options); }, queue(message) { diff --git a/app/styles/app.scss b/app/styles/app.scss index 141047e82ee..b3f422f45e4 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -202,8 +202,6 @@ span.loading { display: none; font-weight: bold; font-size: 110%; - background-color: rgb(255, 213, 213); - border: 2px solid rgb(228, 136, 136); text-align: center; margin: 0 0 10px 0; padding: 10px; @@ -212,6 +210,22 @@ span.loading { &.shown { display: block; } + + &.warning { + background-color: rgb(255, 213, 213); + border: 2px solid rgb(228, 136, 136); + } + + &.info { + background-color: $main-bg-dark; + border: 2px solid #62865f; + } + + &.success { + color: darken($html-bg, 20%); + background-color: $html-bg; + border: 2px solid darken($html-bg, 20%); + } } a { diff --git a/tests/unit/components/flash-message-test.js b/tests/unit/components/flash-message-test.js new file mode 100644 index 00000000000..3e021d161b4 --- /dev/null +++ b/tests/unit/components/flash-message-test.js @@ -0,0 +1,31 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Component | flash-message', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + assert.expect(2); + + this.flashMessages = this.owner.lookup('service:flashMessages'); + this.flashMessages.show('test text'); + + await render(hbs`{{flash-message}}`); + + assert.equal(this.element.textContent.trim(), 'test text', 'should show right message'); + assert.equal(this.element.querySelector('#flash').className, 'shown warning ember-view', 'should have right class'); + }); + + test('it renders with right passed type', async function (assert) { + assert.expect(1); + + this.flashMessages = this.owner.lookup('service:flashMessages'); + this.flashMessages.show('test', { type: 'info' }); + + await render(hbs`{{flash-message}}`); + + assert.equal(this.element.querySelector('#flash').className, 'shown info ember-view', 'should have right class'); + }); +}); From 93d8dbbac27cd9c30de46bcc2b5b39f5898a56da Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Sun, 15 Dec 2019 22:41:05 +0400 Subject: [PATCH 02/18] Fix prettier errors --- app/components/flash-message.js | 2 +- tests/unit/components/flash-message-test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/components/flash-message.js b/app/components/flash-message.js index 3fcac9cf3fe..cd0b78d3ac1 100644 --- a/app/components/flash-message.js +++ b/app/components/flash-message.js @@ -7,7 +7,7 @@ export default Component.extend({ flashMessages: service(), message: readOnly('flashMessages.message'), options: readOnly('flashMessages.options'), - type: computed('flashMessages.options', function () { + type: computed('flashMessages.options', function() { return this.get('flashMessages.options.type') || 'warning'; }), diff --git a/tests/unit/components/flash-message-test.js b/tests/unit/components/flash-message-test.js index 3e021d161b4..e3ac35e7aa0 100644 --- a/tests/unit/components/flash-message-test.js +++ b/tests/unit/components/flash-message-test.js @@ -3,10 +3,10 @@ import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; -module('Integration | Component | flash-message', function (hooks) { +module('Integration | Component | flash-message', function(hooks) { setupRenderingTest(hooks); - test('it renders', async function (assert) { + test('it renders', async function(assert) { assert.expect(2); this.flashMessages = this.owner.lookup('service:flashMessages'); @@ -18,7 +18,7 @@ module('Integration | Component | flash-message', function (hooks) { assert.equal(this.element.querySelector('#flash').className, 'shown warning ember-view', 'should have right class'); }); - test('it renders with right passed type', async function (assert) { + test('it renders with right passed type', async function(assert) { assert.expect(1); this.flashMessages = this.owner.lookup('service:flashMessages'); From 93105e76755ea8c50c73f8c6687a3f13453904d7 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 16:19:47 +0400 Subject: [PATCH 03/18] fix: Remove clean to fix error in console --- app/routes/me/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/routes/me/index.js b/app/routes/me/index.js index b35a921b122..11794e29ffc 100644 --- a/app/routes/me/index.js +++ b/app/routes/me/index.js @@ -5,12 +5,10 @@ import AuthenticatedRoute from '../../mixins/authenticated-route'; export default Route.extend(AuthenticatedRoute, { actions: { willTransition: function() { - this.controller - .setProperties({ - emailNotificationsSuccess: false, - emailNotificationsError: false, - }) - .clear(); + this.controller.setProperties({ + emailNotificationsSuccess: false, + emailNotificationsError: false, + }); }, }, model() { From d7ac6d79ae1849cf46d0ec76b885710d8aa3efda Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 16:37:43 +0400 Subject: [PATCH 04/18] feat: Add has_token field to user --- src/controllers/user/me.rs | 11 ++++++++--- src/models/user.rs | 2 ++ src/tests/user.rs | 18 +++++++++++++++++- src/views.rs | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index a67681f17d3..49d52587cb4 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -7,8 +7,8 @@ use crate::email; use crate::util::bad_request; use crate::util::errors::AppError; -use crate::models::{CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version}; -use crate::schema::{crate_owners, crates, emails, follows, users, versions}; +use crate::models::{CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, ApiToken}; +use crate::schema::{crate_owners, crates, emails, follows, users, versions, api_tokens}; use crate::views::{EncodableMe, EncodableVersion, OwnedCrate}; /// Handles the `GET /me` route. @@ -37,6 +37,11 @@ pub fn me(req: &mut dyn Request) -> AppResult { emails::token_generated_at.nullable().is_not_null(), )) .first::<(User, Option, Option, bool)>(&*conn)?; + + let tokens: Vec = ApiToken::belonging_to(req.user()?) + .filter(api_tokens::revoked.eq(false)) + .load(&*conn)?; + let has_tokens = tokens.len() > 0; let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User) .inner_join(crates::table) @@ -55,7 +60,7 @@ pub fn me(req: &mut dyn Request) -> AppResult { let verified = verified.unwrap_or(false); let verification_sent = verified || verification_sent; Ok(req.json(&EncodableMe { - user: user.encodable_private(email, verified, verification_sent), + user: user.encodable_private(email, verified, verification_sent, has_tokens), owned_crates, })) } diff --git a/src/models/user.rs b/src/models/user.rs index c3be5158335..0fd4093c315 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -171,6 +171,7 @@ impl User { email: Option, email_verified: bool, email_verification_sent: bool, + has_tokens: bool, ) -> EncodablePrivateUser { let User { id, @@ -186,6 +187,7 @@ impl User { email, email_verified, email_verification_sent, + has_tokens, avatar: gh_avatar, login: gh_login, name, diff --git a/src/tests/user.rs b/src/tests/user.rs index 1ca0e95771a..8ca8882c834 100644 --- a/src/tests/user.rs +++ b/src/tests/user.rs @@ -5,7 +5,7 @@ use crate::{ OkBool, TestApp, }; use cargo_registry::{ - models::{Email, NewUser, User}, + models::{Email, NewUser, User, ApiToken}, schema::crate_owners, views::{EncodablePrivateUser, EncodablePublicUser, EncodableVersion, OwnedCrate}, }; @@ -745,3 +745,19 @@ fn test_update_email_notifications_not_owned() { // There should be no change to the `email_notifications` value for a crate not belonging to me assert!(email_notifications); } + +#[test] +fn shows_that_user_has_tokens() { + let (app, _, user) = TestApp::init().with_user(); + + let user_id = user.as_model().id; + app.db(|conn| { + vec![ + t!(ApiToken::insert(conn, user_id, "bar")), + t!(ApiToken::insert(conn, user_id, "baz")), + ] + }); + + let json = user.show_me(); + assert!(json.user.has_tokens); +} diff --git a/src/views.rs b/src/views.rs index cf29c4f2a61..eee959f226e 100644 --- a/src/views.rs +++ b/src/views.rs @@ -175,6 +175,7 @@ pub struct EncodablePrivateUser { pub email: Option, pub avatar: Option, pub url: Option, + pub has_tokens: bool, } /// The serialization format for the `User` model. From e6403a904c1b3499ac1cd98318b7c35738cfb69f Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 18:30:53 +0400 Subject: [PATCH 05/18] fix(component): Move test to right folder --- .../components/flesh-message-test.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{unit/components/flash-message-test.js => integration/components/flesh-message-test.js} (94%) diff --git a/tests/unit/components/flash-message-test.js b/tests/integration/components/flesh-message-test.js similarity index 94% rename from tests/unit/components/flash-message-test.js rename to tests/integration/components/flesh-message-test.js index e3ac35e7aa0..799b4eeaf46 100644 --- a/tests/unit/components/flash-message-test.js +++ b/tests/integration/components/flesh-message-test.js @@ -3,7 +3,7 @@ import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; -module('Integration | Component | flash-message', function(hooks) { +module('Integration | Component | flesh-message', function(hooks) { setupRenderingTest(hooks); test('it renders', async function(assert) { From d006ee910c62b5fc8429c945a6e8687f0b3ad19d Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 18:32:31 +0400 Subject: [PATCH 06/18] chore: Add welcome-message component --- app/components/welcome-message.js | 29 ++++++ app/models/user.js | 1 + app/styles/app.scss | 3 +- app/templates/components/welcome-message.hbs | 1 + app/templates/index.hbs | 1 + mirage/factories/user.js | 20 +++++ mirage/models/user.js | 3 + .../components/welcome-message-test.js | 89 +++++++++++++++++++ 8 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 app/components/welcome-message.js create mode 100644 app/templates/components/welcome-message.hbs create mode 100644 mirage/factories/user.js create mode 100644 mirage/models/user.js create mode 100644 tests/integration/components/welcome-message-test.js diff --git a/app/components/welcome-message.js b/app/components/welcome-message.js new file mode 100644 index 00000000000..3eb7ac55000 --- /dev/null +++ b/app/components/welcome-message.js @@ -0,0 +1,29 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import { notEmpty } from '@ember/object/computed'; + +export default Component.extend({ + session: service(), + + text: computed('session.currentUser.{email_verified,has_tokens}', function() { + const user = this.get('session.currentUser'); + if (!user || (user.email_verified && user.has_tokens)) return ''; + + const textArray = [ + !user.email_verified && 'verify your email address', + !user.email_verified && !user.has_tokens && ' and ', + !user.has_tokens && 'create an API token', + '!', + ].filter(e => !!e); + + return textArray.join(''); + }), + + showMessage: notEmpty('text').readOnly(), + + type: 'info', + elementId: 'welcome-message', + tagName: 'p', + classNameBindings: ['showMessage:shown', 'type'], +}); diff --git a/app/models/user.js b/app/models/user.js index 0bcbdcbce71..3d870306faa 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -9,6 +9,7 @@ export default DS.Model.extend({ avatar: DS.attr('string'), url: DS.attr('string'), kind: DS.attr('string'), + has_tokens: DS.attr('boolean'), stats() { return this.store.adapterFor('user').stats(this.id); diff --git a/app/styles/app.scss b/app/styles/app.scss index b3f422f45e4..619e7fa757a 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -198,7 +198,8 @@ span.loading { background-image: url(/assets/ajax-loader.gif); } -#flash { +#flash, +#welcome-message { display: none; font-weight: bold; font-size: 110%; diff --git a/app/templates/components/welcome-message.hbs b/app/templates/components/welcome-message.hbs new file mode 100644 index 00000000000..967c29c9ceb --- /dev/null +++ b/app/templates/components/welcome-message.hbs @@ -0,0 +1 @@ +Welcome to crates.io! Visit account settings to {{text}} \ No newline at end of file diff --git a/app/templates/index.hbs b/app/templates/index.hbs index c11be7b8882..b952150120d 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -1,3 +1,4 @@ +{{welcome-message}}

The Rust community’s crate registry

diff --git a/mirage/factories/user.js b/mirage/factories/user.js new file mode 100644 index 00000000000..fa7448c60fb --- /dev/null +++ b/mirage/factories/user.js @@ -0,0 +1,20 @@ +import { Factory, faker, trait } from 'ember-cli-mirage'; + +export default Factory.extend({ + email_verified: false, + email_verification_sent: true, + name: () => faker.name.firstName(), + login: () => faker.internet.userName(), + avatar: () => faker.image.imageUrl(), + url: () => faker.internet.url(), + kind: () => faker.lorem.words(), + has_tokens: false, + + withVerifiedEmail: trait({ + email_verified: true, + }), + + withTokens: trait({ + has_tokens: true, + }), +}); diff --git a/mirage/models/user.js b/mirage/models/user.js new file mode 100644 index 00000000000..581e5e978fd --- /dev/null +++ b/mirage/models/user.js @@ -0,0 +1,3 @@ +import { Model } from 'ember-cli-mirage'; + +export default Model; diff --git a/tests/integration/components/welcome-message-test.js b/tests/integration/components/welcome-message-test.js new file mode 100644 index 00000000000..4d9a723b142 --- /dev/null +++ b/tests/integration/components/welcome-message-test.js @@ -0,0 +1,89 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Integration | Component | welcome-message', function(hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + + test('it renders', async function(assert) { + assert.expect(2); + const user = this.server.create('user', {}); + + this.session = this.owner.lookup('service:session'); + this.session.loginUser(user); + + await render(hbs`{{welcome-message}}`); + + assert.equal( + this.element.textContent.trim(), + 'Welcome to crates.io! Visit account settings to verify your email address and create an API token!', + 'should show right message', + ); + assert.equal( + this.element.querySelector('#welcome-message').className, + 'shown info ember-view', + 'should have right class', + ); + }); + + test('it show reminder about email only if user has tokens', async function(assert) { + assert.expect(2); + const user = this.server.create('user', 'withTokens'); + + this.session = this.owner.lookup('service:session'); + this.session.loginUser(user); + + await render(hbs`{{welcome-message}}`); + + assert.equal( + this.element.textContent.trim(), + 'Welcome to crates.io! Visit account settings to verify your email address!', + 'should show right message', + ); + assert.equal( + this.element.querySelector('#welcome-message').className, + 'shown info ember-view', + 'should have right class', + ); + }); + + test('it show reminder about tokens only if user has verified email', async function(assert) { + assert.expect(2); + const user = this.server.create('user', 'withVerifiedEmail'); + + this.session = this.owner.lookup('service:session'); + this.session.loginUser(user); + + await render(hbs`{{welcome-message}}`); + + assert.equal( + this.element.textContent.trim(), + 'Welcome to crates.io! Visit account settings to create an API token!', + 'should show right message', + ); + assert.equal( + this.element.querySelector('#welcome-message').className, + 'shown info ember-view', + 'should have right class', + ); + }); + + test('it not shows if user has tokens and verified email', async function(assert) { + assert.expect(1); + const user = this.server.create('user', 'withTokens', 'withVerifiedEmail'); + + this.session = this.owner.lookup('service:session'); + this.session.loginUser(user); + + await render(hbs`{{welcome-message}}`); + + assert.equal( + this.element.querySelector('#welcome-message').className, + 'info ember-view', + 'should have right class', + ); + }); +}); From 56ed394e0e3478289096f3b25f2469dad61d25e2 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 18:32:58 +0400 Subject: [PATCH 07/18] fix: Remove flash message after user login --- app/routes/login.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/routes/login.js b/app/routes/login.js index 2de32c588e2..ab96c1d09bc 100644 --- a/app/routes/login.js +++ b/app/routes/login.js @@ -1,6 +1,5 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; -import { htmlSafe } from '@ember/string'; /** * This route will open a popup window directed at the `github-login` route. @@ -69,11 +68,6 @@ export default Route.extend({ let transition = this.get('session.savedTransition'); this.session.loginUser(user); - const messageAfterLogin = htmlSafe( - "Welcome to crates.io! Visit account settings to verify your email address and create an API token!", - ); - this.flashMessages.show(messageAfterLogin, { type: 'info' }); - if (transition) { transition.retry(); } From 220e90fc66b6e809becd27fb88aac224b47c4f5c Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 18:33:58 +0400 Subject: [PATCH 08/18] fix: Update user fields after add/remove api tokens Trying to avoid over fetching on current user model change. --- app/components/api-token-row.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/components/api-token-row.js b/app/components/api-token-row.js index 280d1abad60..686c10ada0e 100644 --- a/app/components/api-token-row.js +++ b/app/components/api-token-row.js @@ -1,10 +1,13 @@ import Component from '@ember/component'; +import { inject as service } from '@ember/service'; import { empty, or } from '@ember/object/computed'; export default Component.extend({ emptyName: empty('api_token.name'), disableCreate: or('api_token.isSaving', 'emptyName'), serverError: null, + session: service(), + store: service(), didInsertElement() { let input = this.element.querySelector('input'); @@ -17,6 +20,7 @@ export default Component.extend({ async saveToken() { try { await this.api_token.save(); + this.set('session.currentUser.has_tokens', true); this.set('serverError', null); } catch (err) { let msg; @@ -31,6 +35,11 @@ export default Component.extend({ async revokeToken() { try { + // To avoid error on destroy we need to set before destroying of api-token + // that's why we need to set length of api-tokens to 1 in check + if ((await this.store.query('api-token', {})).length == 1) { + this.set('session.currentUser.has_tokens', false); + } await this.api_token.destroyRecord(); } catch (err) { let msg; From 9117b74ce209a761a4364a8ff349ed4812196475 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 20:08:28 +0400 Subject: [PATCH 09/18] fix: Fixes after merge from current master --- app/templates/index.hbs | 2 +- mirage/factories/user.js | 5 +++-- tests/integration/components/welcome-message-test.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/templates/index.hbs b/app/templates/index.hbs index 3ce7db38c78..f162bbfa5cb 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -1,4 +1,4 @@ -{{welcome-message}} +

The Rust community’s crate registry

diff --git a/mirage/factories/user.js b/mirage/factories/user.js index fa7448c60fb..a9af94d165e 100644 --- a/mirage/factories/user.js +++ b/mirage/factories/user.js @@ -1,9 +1,10 @@ -import { Factory, faker, trait } from 'ember-cli-mirage'; +import { Factory, trait } from 'ember-cli-mirage'; +import faker from 'faker'; export default Factory.extend({ email_verified: false, email_verification_sent: true, - name: () => faker.name.firstName(), + name: () => faker.name.findName(), login: () => faker.internet.userName(), avatar: () => faker.image.imageUrl(), url: () => faker.internet.url(), diff --git a/tests/integration/components/welcome-message-test.js b/tests/integration/components/welcome-message-test.js index b7b77d0bc69..e7c0d57c476 100644 --- a/tests/integration/components/welcome-message-test.js +++ b/tests/integration/components/welcome-message-test.js @@ -68,6 +68,6 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.equal(this.element.querySelector('#welcome-message').className, 'info', 'should have right class'); + assert.equal(this.element.querySelector('#welcome-message').className, '', 'should have right class'); }); }); From 9656756a82ae67a480278483acfa802d7385447c Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 20:13:38 +0400 Subject: [PATCH 10/18] fix: Fix linter errors --- app/templates/components/welcome-message.hbs | 2 +- src/controllers/user/me.rs | 4 ++-- src/tests/user.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/components/welcome-message.hbs b/app/templates/components/welcome-message.hbs index d077d88f9e5..92211c708e0 100644 --- a/app/templates/components/welcome-message.hbs +++ b/app/templates/components/welcome-message.hbs @@ -1,3 +1,3 @@

- Welcome to crates.io! Visit account settings to {{text}} + Welcome to crates.io! Visit account settings to {{this.text}}

\ No newline at end of file diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index 1599ae9d0af..1be751ca55e 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -7,9 +7,9 @@ use crate::email; use crate::util::errors::AppError; use crate::models::{ - CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction, ApiToken, + ApiToken, CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction, }; -use crate::schema::{crate_owners, crates, emails, follows, users, versions, api_tokens}; +use crate::schema::{api_tokens, crate_owners, crates, emails, follows, users, versions}; use crate::views::{EncodableMe, EncodableVersion, OwnedCrate}; /// Handles the `GET /me` route. diff --git a/src/tests/user.rs b/src/tests/user.rs index 03cda0298a8..dab72dfc3cb 100644 --- a/src/tests/user.rs +++ b/src/tests/user.rs @@ -5,7 +5,7 @@ use crate::{ OkBool, TestApp, }; use cargo_registry::{ - models::{Email, NewUser, User, ApiToken}, + models::{ApiToken, Email, NewUser, User}, schema::crate_owners, views::{EncodablePrivateUser, EncodablePublicUser, EncodableVersion, OwnedCrate}, }; From b9c2856e7ee776931fea73697d1542fc8eb43002 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Thu, 26 Dec 2019 20:21:12 +0400 Subject: [PATCH 11/18] fix: Fix Rust linter errors --- src/controllers/user/me.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index 1be751ca55e..a95105b0833 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -42,7 +42,7 @@ pub fn me(req: &mut dyn Request) -> AppResult { let tokens: Vec = ApiToken::belonging_to(req.user()?) .filter(api_tokens::revoked.eq(false)) .load(&*conn)?; - let has_tokens = tokens.len() > 0; + let has_tokens = !tokens.is_empty(); let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User) .inner_join(crates::table) From 4eb0e95cab5ab19b40f250f37ca3694ed7dd3ef8 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Sat, 28 Dec 2019 22:29:19 +0400 Subject: [PATCH 12/18] fix: Change class name in test after rebase --- tests/acceptance/login-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/login-test.js b/tests/acceptance/login-test.js index 8a26d6f9cb6..c4aa88867ec 100644 --- a/tests/acceptance/login-test.js +++ b/tests/acceptance/login-test.js @@ -83,7 +83,7 @@ module('Acceptance | Login', function(hooks) { fakeWindow.closed = true; // wait for the error message to show up after the failed login - await waitFor('[data-test-flash-message].shown'); + await waitFor('[data-test-flash-message].show'); assert.dom('[data-test-flash-message]').hasText('Failed to log in: Forbidden'); }); From ff50263b890651cb99d46c286b5978a92df12aab Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Tue, 31 Dec 2019 12:36:56 +0400 Subject: [PATCH 13/18] fix: Remove unneeded space --- app/routes/login.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/routes/login.js b/app/routes/login.js index 81a6c73d191..f4f15387120 100644 --- a/app/routes/login.js +++ b/app/routes/login.js @@ -68,7 +68,6 @@ export default Route.extend({ let user = this.store.push(this.store.normalize('user', data.user)); let transition = this.get('session.savedTransition'); this.session.loginUser(user); - if (transition) { transition.retry(); } From 46ab6eee7d5ba1fe31a7350deba5653edfe8ba92 Mon Sep 17 00:00:00 2001 From: Denis Savchuk Date: Tue, 31 Dec 2019 12:49:13 +0400 Subject: [PATCH 14/18] fix: Change tests to use qunit-dom --- app/templates/components/welcome-message.hbs | 2 +- .../components/welcome-message-test.js | 41 +++++++++---------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/app/templates/components/welcome-message.hbs b/app/templates/components/welcome-message.hbs index 92211c708e0..47a1f280606 100644 --- a/app/templates/components/welcome-message.hbs +++ b/app/templates/components/welcome-message.hbs @@ -1,3 +1,3 @@ -

+

Welcome to crates.io! Visit account settings to {{this.text}}

\ No newline at end of file diff --git a/tests/integration/components/welcome-message-test.js b/tests/integration/components/welcome-message-test.js index e7c0d57c476..05a7fa564f2 100644 --- a/tests/integration/components/welcome-message-test.js +++ b/tests/integration/components/welcome-message-test.js @@ -9,7 +9,7 @@ module('Integration | Component | welcome-message', function(hooks) { setupMirage(hooks); test('it renders', async function(assert) { - assert.expect(2); + assert.expect(3); const user = this.server.create('user', {}); this.session = this.owner.lookup('service:session'); @@ -17,16 +17,15 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.equal( - this.element.textContent.trim(), - 'Welcome to crates.io! Visit account settings to verify your email address and create an API token!', - 'should show right message', - ); - assert.equal(this.element.querySelector('#welcome-message').className, 'show info', 'should have right class'); + assert + .dom('[data-test-welcome-message]') + .hasText('Welcome to crates.io! Visit account settings to verify your email address and create an API token!'); + assert.dom('[data-test-welcome-message]').hasClass('show'); + assert.dom('[data-test-welcome-message]').hasClass('info'); }); test('it show reminder about email only if user has tokens', async function(assert) { - assert.expect(2); + assert.expect(3); const user = this.server.create('user', 'withTokens'); this.session = this.owner.lookup('service:session'); @@ -34,16 +33,15 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.equal( - this.element.textContent.trim(), - 'Welcome to crates.io! Visit account settings to verify your email address!', - 'should show right message', - ); - assert.equal(this.element.querySelector('#welcome-message').className, 'show info', 'should have right class'); + assert + .dom('[data-test-welcome-message]') + .hasText('Welcome to crates.io! Visit account settings to verify your email address!'); + assert.dom('[data-test-welcome-message]').hasClass('show'); + assert.dom('[data-test-welcome-message]').hasClass('info'); }); test('it show reminder about tokens only if user has verified email', async function(assert) { - assert.expect(2); + assert.expect(3); const user = this.server.create('user', 'withVerifiedEmail'); this.session = this.owner.lookup('service:session'); @@ -51,12 +49,11 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.equal( - this.element.textContent.trim(), - 'Welcome to crates.io! Visit account settings to create an API token!', - 'should show right message', - ); - assert.equal(this.element.querySelector('#welcome-message').className, 'show info', 'should have right class'); + assert + .dom('[data-test-welcome-message]') + .hasText('Welcome to crates.io! Visit account settings to create an API token!'); + assert.dom('[data-test-welcome-message]').hasClass('show'); + assert.dom('[data-test-welcome-message]').hasClass('info'); }); test('it not shows if user has tokens and verified email', async function(assert) { @@ -68,6 +65,6 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.equal(this.element.querySelector('#welcome-message').className, '', 'should have right class'); + assert.dom('[data-test-welcome-message]').doesNotHaveClass('show'); }); }); From a64fc2a7d7c4c1f6736c9cf72920c089744142ef Mon Sep 17 00:00:00 2001 From: "denis_savchuk@Deniss-MacBook-Pro.local (RSA)" Date: Tue, 4 Feb 2020 14:07:58 -0800 Subject: [PATCH 15/18] fix: Return flash message component to default state --- app/components/flash-message.hbs | 2 +- app/components/flash-message.js | 13 ------------- app/components/flash-message.module.scss | 22 ++++------------------ 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/app/components/flash-message.hbs b/app/components/flash-message.hbs index b8b574fa1e5..476e1edede3 100644 --- a/app/components/flash-message.hbs +++ b/app/components/flash-message.hbs @@ -1,3 +1,3 @@ -

+

{{this.message}}

\ No newline at end of file diff --git a/app/components/flash-message.js b/app/components/flash-message.js index b7a25cb7099..4798652642b 100644 --- a/app/components/flash-message.js +++ b/app/components/flash-message.js @@ -1,18 +1,5 @@ import Component from '@ember/component'; -import { computed } from '@ember/object'; -import { readOnly } from '@ember/object/computed'; -import { inject as service } from '@ember/service'; export default Component.extend({ - flashMessages: service(), - message: readOnly('flashMessages.message'), - options: readOnly('flashMessages.options'), - type: computed('flashMessages.options', function() { - return this.get('flashMessages.options.type') || 'warning'; - }), - classes: computed('message', 'type', function() { - return `${this.message ? 'show' : ''} ${this.type}`; - }), - tagName: '', }); diff --git a/app/components/flash-message.module.scss b/app/components/flash-message.module.scss index 8492b444697..5fd887cd082 100644 --- a/app/components/flash-message.module.scss +++ b/app/components/flash-message.module.scss @@ -2,28 +2,14 @@ display: none; font-weight: bold; font-size: 110%; + background-color: rgb(255, 213, 213); + border: 2px solid rgb(228, 136, 136); text-align: center; margin: 0 0 10px 0; padding: 10px; - @include border-radius(5px); + border-radius: 5px; - &.show { + &.shown { display: block; } - - &.warning { - background-color: rgb(255, 213, 213); - border: 2px solid rgb(228, 136, 136); - } - - &.info { - background-color: $main-bg-dark; - border: 2px solid #62865f; - } - - &.success { - color: darken($html-bg, 20%); - background-color: $html-bg; - border: 2px solid darken($html-bg, 20%); - } } From b8c9d25fa1af4de3f81729a2bd64c3e5effe57cc Mon Sep 17 00:00:00 2001 From: "denis_savchuk@Deniss-MacBook-Pro.local (RSA)" Date: Tue, 4 Feb 2020 14:08:43 -0800 Subject: [PATCH 16/18] fix: Update welcome message component to be align after rebase --- app/components/welcome-message.hbs | 3 +++ app/components/welcome-message.module.scss | 18 ++++++++++++++++++ app/templates/components/welcome-message.hbs | 3 --- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 app/components/welcome-message.hbs create mode 100644 app/components/welcome-message.module.scss delete mode 100644 app/templates/components/welcome-message.hbs diff --git a/app/components/welcome-message.hbs b/app/components/welcome-message.hbs new file mode 100644 index 00000000000..136497a371b --- /dev/null +++ b/app/components/welcome-message.hbs @@ -0,0 +1,3 @@ +

+ Welcome to crates.io! Visit account settings to {{this.text}} +

\ No newline at end of file diff --git a/app/components/welcome-message.module.scss b/app/components/welcome-message.module.scss new file mode 100644 index 00000000000..3359c2bfeb5 --- /dev/null +++ b/app/components/welcome-message.module.scss @@ -0,0 +1,18 @@ +.welcome-message { + display: none; + font-weight: bold; + font-size: 110%; + text-align: center; + margin: 0 0 10px 0; + padding: 10px; + border-radius: 5px; + + &.show { + display: block; + } + + &.info { + background-color: $main-bg-dark; + border: 2px solid #62865f; + } +} diff --git a/app/templates/components/welcome-message.hbs b/app/templates/components/welcome-message.hbs deleted file mode 100644 index 47a1f280606..00000000000 --- a/app/templates/components/welcome-message.hbs +++ /dev/null @@ -1,3 +0,0 @@ -

- Welcome to crates.io! Visit account settings to {{this.text}} -

\ No newline at end of file From 938393f96fe608c19df0a15050f07a05a473bf0c Mon Sep 17 00:00:00 2001 From: "denis_savchuk@Deniss-MacBook-Pro.local (RSA)" Date: Tue, 4 Feb 2020 17:26:21 -0800 Subject: [PATCH 17/18] fix: Refactor tests after rebase - Faker in model the best way to generate random values for better testing. - Remove check by classes because css module generate random class name now and there no good ways how to use that names in tests. - Refactor some asserts to check real value instead of custom one. --- app/components/welcome-message.hbs | 2 +- app/components/welcome-message.module.scss | 2 +- mirage/factories/user.js | 24 ++++++++++++------- package-lock.json | 6 +++++ package.json | 1 + .../components/flesh-message-test.js | 20 +++------------- .../components/welcome-message-test.js | 19 +++++++-------- tests/mirage/crates-test.js | 15 ++---------- tests/mirage/users-test.js | 13 +++------- 9 files changed, 41 insertions(+), 61 deletions(-) diff --git a/app/components/welcome-message.hbs b/app/components/welcome-message.hbs index 136497a371b..8c91120f20a 100644 --- a/app/components/welcome-message.hbs +++ b/app/components/welcome-message.hbs @@ -1,3 +1,3 @@ -

+

Welcome to crates.io! Visit account settings to {{this.text}}

\ No newline at end of file diff --git a/app/components/welcome-message.module.scss b/app/components/welcome-message.module.scss index 3359c2bfeb5..6c7dce346ab 100644 --- a/app/components/welcome-message.module.scss +++ b/app/components/welcome-message.module.scss @@ -7,7 +7,7 @@ padding: 10px; border-radius: 5px; - &.show { + &.shown { display: block; } diff --git a/mirage/factories/user.js b/mirage/factories/user.js index 0ff0fe72530..d494b1dde31 100644 --- a/mirage/factories/user.js +++ b/mirage/factories/user.js @@ -1,15 +1,23 @@ -import { Factory } from 'ember-cli-mirage'; -import { dasherize } from '@ember/string'; +import { Factory, trait } from 'ember-cli-mirage'; +import faker from 'faker'; export default Factory.extend({ - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - email_verification_sent: true, email_verified: false, + email_verification_sent: true, + name() { + return faker.name.findName(); + }, + login() { + return faker.internet.userName(); + }, + avatar() { + return faker.image.imageUrl(); + }, + url() { + return faker.internet.url(); + }, + kind: 'user', has_tokens: false, - kind: "Test text", - login: () => dasherize(this.name), - name: i => `User ${i + 1}`, - url: () => `https://github.com/${this.login}`, withVerifiedEmail: trait({ email_verified: true, diff --git a/package-lock.json b/package-lock.json index b54f4dbdd0c..4fc8ef677e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41301,6 +41301,12 @@ "integrity": "sha512-Kn2WYYS6cDBS5jq/voOfSGCA0TafOYAUPbEp8mUVpD/DVV5bQIDjlq+MLLvNUokkbTpjBVlLDaM5PnX+PwZMlw==", "dev": true }, + "faker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", + "dev": true + }, "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", diff --git a/package.json b/package.json index 14791f0a0bf..f57f8bac28f 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "eslint-config-prettier": "^6.9.0", "eslint-plugin-ember": "^7.7.2", "eslint-plugin-prettier": "^3.1.2", + "faker": "^4.1.0", "loader.js": "^4.7.0", "normalize.css": "^8.0.1", "nyc": "^15.0.0", diff --git a/tests/integration/components/flesh-message-test.js b/tests/integration/components/flesh-message-test.js index 45dc264964a..4ce750b8033 100644 --- a/tests/integration/components/flesh-message-test.js +++ b/tests/integration/components/flesh-message-test.js @@ -9,23 +9,9 @@ module('Integration | Component | flesh-message', function(hooks) { test('it renders', async function(assert) { assert.expect(2); - this.flashMessages = this.owner.lookup('service:flashMessages'); - this.flashMessages.show('test text'); + await render(hbs``); - await render(hbs`{{flash-message}}`); - - assert.equal(this.element.textContent.trim(), 'test text', 'should show right message'); - assert.equal(this.element.querySelector('#flash').className, 'show warning', 'should have right class'); - }); - - test('it renders with right passed type', async function(assert) { - assert.expect(1); - - this.flashMessages = this.owner.lookup('service:flashMessages'); - this.flashMessages.show('test', { type: 'info' }); - - await render(hbs`{{flash-message}}`); - - assert.equal(this.element.querySelector('#flash').className, 'show info', 'should have right class'); + assert.dom('[data-test-flash-message]').hasText('test text'); + assert.dom('[data-test-flash-message]').isVisible(); }); }); diff --git a/tests/integration/components/welcome-message-test.js b/tests/integration/components/welcome-message-test.js index 05a7fa564f2..c6077b5f5ed 100644 --- a/tests/integration/components/welcome-message-test.js +++ b/tests/integration/components/welcome-message-test.js @@ -9,8 +9,8 @@ module('Integration | Component | welcome-message', function(hooks) { setupMirage(hooks); test('it renders', async function(assert) { - assert.expect(3); - const user = this.server.create('user', {}); + assert.expect(2); + const user = this.server.create('user'); this.session = this.owner.lookup('service:session'); this.session.loginUser(user); @@ -20,12 +20,11 @@ module('Integration | Component | welcome-message', function(hooks) { assert .dom('[data-test-welcome-message]') .hasText('Welcome to crates.io! Visit account settings to verify your email address and create an API token!'); - assert.dom('[data-test-welcome-message]').hasClass('show'); - assert.dom('[data-test-welcome-message]').hasClass('info'); + assert.dom('[data-test-welcome-message]').isVisible(); }); test('it show reminder about email only if user has tokens', async function(assert) { - assert.expect(3); + assert.expect(2); const user = this.server.create('user', 'withTokens'); this.session = this.owner.lookup('service:session'); @@ -36,12 +35,11 @@ module('Integration | Component | welcome-message', function(hooks) { assert .dom('[data-test-welcome-message]') .hasText('Welcome to crates.io! Visit account settings to verify your email address!'); - assert.dom('[data-test-welcome-message]').hasClass('show'); - assert.dom('[data-test-welcome-message]').hasClass('info'); + assert.dom('[data-test-welcome-message]').isVisible(); }); test('it show reminder about tokens only if user has verified email', async function(assert) { - assert.expect(3); + assert.expect(2); const user = this.server.create('user', 'withVerifiedEmail'); this.session = this.owner.lookup('service:session'); @@ -52,8 +50,7 @@ module('Integration | Component | welcome-message', function(hooks) { assert .dom('[data-test-welcome-message]') .hasText('Welcome to crates.io! Visit account settings to create an API token!'); - assert.dom('[data-test-welcome-message]').hasClass('show'); - assert.dom('[data-test-welcome-message]').hasClass('info'); + assert.dom('[data-test-welcome-message]').isVisible(); }); test('it not shows if user has tokens and verified email', async function(assert) { @@ -65,6 +62,6 @@ module('Integration | Component | welcome-message', function(hooks) { await render(hbs`{{welcome-message}}`); - assert.dom('[data-test-welcome-message]').doesNotHaveClass('show'); + assert.dom('[data-test-welcome-message]').isNotVisible(); }); }); diff --git a/tests/mirage/crates-test.js b/tests/mirage/crates-test.js index 78f51d5a279..df767e2e906 100644 --- a/tests/mirage/crates-test.js +++ b/tests/mirage/crates-test.js @@ -684,25 +684,14 @@ module('Mirage | Crates', function(hooks) { }); test('returns the list of users that own the specified crate', async function(assert) { - let user = this.server.create('user', { name: 'John Doe' }); + let user = this.server.create('user'); this.server.create('crate', { name: 'rand', userOwners: [user] }); let response = await fetch('/api/v1/crates/rand/owner_user'); assert.equal(response.status, 200); let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - users: [ - { - id: '1', - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - kind: 'user', - login: 'john-doe', - name: 'John Doe', - url: 'https://github.com/john-doe', - }, - ], - }); + assert.deepEqual(JSON.stringify(responsePayload), JSON.stringify({ users: [user] })); }); }); diff --git a/tests/mirage/users-test.js b/tests/mirage/users-test.js index 8766a5c98a3..a698ec7ad31 100644 --- a/tests/mirage/users-test.js +++ b/tests/mirage/users-test.js @@ -18,21 +18,14 @@ module('Mirage | Users', function(hooks) { }); test('returns a user object for known users', async function(assert) { - let user = this.server.create('user', { name: 'John Doe' }); + let user = this.server.create('user'); let response = await fetch(`/api/v1/users/${user.login}`); assert.equal(response.status, 200); let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - user: { - id: '1', - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - login: 'john-doe', - name: 'John Doe', - url: 'https://github.com/john-doe', - }, - }); + + assert.deepEqual(JSON.stringify(responsePayload), JSON.stringify({ user })); }); }); }); From d7b5a543246e592c682889fe6b3686e6636e3601 Mon Sep 17 00:00:00 2001 From: "denis_savchuk@Deniss-MacBook-Pro.local (RSA)" Date: Tue, 4 Feb 2020 17:30:12 -0800 Subject: [PATCH 18/18] fix: Fix linter errors --- tests/acceptance/login-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/login-test.js b/tests/acceptance/login-test.js index 87b132c3da0..f4aa3973a36 100644 --- a/tests/acceptance/login-test.js +++ b/tests/acceptance/login-test.js @@ -7,12 +7,12 @@ import window, { setupWindowMock } from 'ember-window-mock'; import flashStyles from 'cargo/components/flash-message.module.scss'; import setupMirage from '../helpers/setup-mirage'; -module('Acceptance | Login', function (hooks) { +module('Acceptance | Login', function(hooks) { setupApplicationTest(hooks); setupWindowMock(hooks); setupMirage(hooks); - test('successful login', async function (assert) { + test('successful login', async function(assert) { let deferred = defer(); let fakeWindow = { closed: false }; window.open = (url, target, features) => { @@ -56,7 +56,7 @@ module('Acceptance | Login', function (hooks) { assert.dom('[data-test-user-menu] [data-test-toggle]').hasText('John Doe'); }); - test('failed login', async function (assert) { + test('failed login', async function(assert) { let deferred = defer(); let fakeWindow = { closed: false }; window.open = () => {