Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PASSWORD]: reset and forgot WIP #1231

Merged
merged 2 commits into from
May 31, 2017
Merged

[PASSWORD]: reset and forgot WIP #1231

merged 2 commits into from
May 31, 2017

Conversation

snewcomer
Copy link
Contributor

References

Progress on: #1126 #1125

@snewcomer snewcomer self-assigned this Apr 5, 2017
@snewcomer snewcomer requested a review from joshsmith April 5, 2017 03:21
Copy link
Contributor

@joshsmith joshsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per some discussion outside this PR, we're going to limit the scope to unauthenticated users who forgot their password. This means there won't be anything in the user's settings, and everything can work without the authenticated route mixin.

method: 'POST',
data: {
password,
passwordConfirmation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to send the confirmation, as well, or can we just use that on the client-side? I don't believe we have a concept on the server of a confirmation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshsmith so on the backend we do have a check validate_confirmation check

https://github.com/code-corps/code-corps-api/blob/develop/web/models/user.ex#L101

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed that this was added. My mistake. Is it added to the user model in Ember here, too?

<div class="container">
<form>

{{input name="email" autocapitalize="off" type="text" placeholder='Your email' value=email key-down="keyDown"}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above this we can have a p that says:

Enter your email and we'll send you a link to reset your password

If the user's authenticated, this could even be auto-populated with the user's email!

Don't think keyDown is needed here.

{{input name="email" autocapitalize="off" type="text" placeholder='Your email' value=email key-down="keyDown"}}

<div class="input-group">
<input class="button default {{if hasError "has-error"}}" name="forgot-password" type="submit" value='Forgot Password' {{action forgotPassword email}} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this or the text input have the error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. If the ajax request hit a 500 or something...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm asking which one would have it. I think the button having it is a little weird, but the text having it makes more sense.

<form>
{{input autofocus="true" class="has-progress" name="password" type="password" placeholder='Enter new password' autocomplete="off" value=password}}

{{input class="has-progress" name="password" type="password" placeholder='Confirm password' autocomplete="off" value=passwordConfirmation}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't do placeholder on either of these.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably have a label of "Confirm password".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this applies to forgot pwd as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think passwords should typically have a placeholder since it's a little confusing UX.

@@ -0,0 +1,15 @@
<div class="container">
<form>
{{input autofocus="true" class="has-progress" name="password" type="password" placeholder='Enter new password' autocomplete="off" value=password}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably have a label of "Password".

{{input class="has-progress" name="password" type="password" placeholder='Confirm password' autocomplete="off" value=passwordConfirmation}}

<div class="input-group">
<input class="button default {{if hasError "has-error"}}" name="reset-password" type="submit" value='Reset Password' {{action resetPassword password passwordConfirmation}} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would change value to "Change password".

@@ -0,0 +1,3 @@
{{password/reset-password
resetPassword=(route-action "resetPassword")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think route-action's are less testable in component tests, though you can correct me if I'm wrong on this. Also fairly certain that you don't need it here if the action is on the route, and similarly wonder if it shouldn't be in a controller.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe route-action is for discoverability. If I change to action, it errors. I can create a controller for this action if you would like.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is at a route level, so no need to generate a controller just for the sake of not using route-action, I think. The component is just as easily testable this way as it would be with a controller, but the boilerplate is reduced.

Also, thank you for that input, @snewcomer. I was in a mental place where I wasn't sure how route actions are useful exactly, since, in my experience, using them in nested components makes everything harder to test.

This example is where using a route action is useful for reducing boilerplate with no (to me) apparent negatives.

@snewcomer
Copy link
Contributor Author

@joshsmith I'm guessing a Forgot Password link below the sign-in button? Text underline, smaller font, etc?

@joshsmith
Copy link
Contributor

@snewcomer I think actually if we can do it just below the password field, that might be ideal. I'd like to have a "Don't have an account? Sign up." below the button on Sign In, and a "Have an account? Log in." below the button on Sign Up.

error: null,

/**
* @property resetPasswordTask
Copy link
Contributor Author

@snewcomer snewcomer Apr 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@begedin Could you review this file? Was wondering what you thought of it as it follows a bit diff pattern than I have seen so far. Also, how should I handle errors here? This displays the error as you mentioned in the slack a while back. But have no way of getting rid of it.

Copy link
Contributor

@begedin begedin Apr 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it looks nice and clean. There isn't anything that seriously clashes with our usual approach.

@@ -1,3 +1,5 @@
import Ember from 'ember';
const { RSVP } = Ember;
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the associated component test.

Copy link
Contributor Author

@snewcomer snewcomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshsmith forgot password for your review. Once this is reviewed and g2g, will cleanup reset password w/ page objects and such and checkoff items in #1125

@@ -9,6 +9,9 @@
<div class="input-group">
{{input name="password" id="password" placeholder="Password" type="password" value=password}}
</div>
{{#link-to 'password.forgot' classNames="t-forgot-password"}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshsmith this forgot-password link could use some styling when you get a chance.

@@ -1,13 +1,13 @@
<div class="container">
<form>

<p>Enter your email and we'll send you a link to reset your password</p>
<p data-test-id="forgot-password-header">Enter your email and we'll send you a link to reset your password</p>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data test ids if you want to use them to target for tests. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been discussing the usage of https://github.com/simplabs/ember-test-selectors in the past, but I'm nut sure where we're with it now. The addon makes it so these test-only attributes are not present in the final build.

I think, let's not introduce it now, but what you could do is create an issue where we can discuss it and, if we opt into it, create a bunch of other issues. It's a tasks that would be very suitable for newbie volunteer.

*/
resetPasswordTask: task(function* (password, passwordConfirmation) {
try {
yield get(this, 'resetPassword')(password, passwordConfirmation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will get(this, 'resetPassword') actually return the function here? Isn't it defined under this.actions.resetPassword?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this is where I would clear the error. Simply do a set(this, 'error', null) before the yield.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah to it is passed in as closure action, so just need to get it off the props and call that function. This way also allows me to patch that action in the integration test.

*/
resetPasswordTask: task(function* (password, passwordConfirmation) {
try {
yield get(this, 'resetPassword')(password, passwordConfirmation);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah to it is passed in as closure action, so just need to get it off the props and call that function. This way also allows me to patch that action in the integration test.

password,
passwordConfirmation
}
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to handle success/error cases still.

Copy link
Contributor

@begedin begedin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love your comment on the merging of page objects. Otherwise looks great. Nicely written and well-tested.

form,
resetPasswordForm,
errorFormatter
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe a page object should match with a route 1 to 1. Otherwise, we risk confusing people. Is there any particular reason why you chose to merge the two password pages?

Split up, we would not have visitReset and visitForgot. The two pages would simply have a visit, a form and an errorFormatter.

Copy link
Contributor Author

@snewcomer snewcomer Apr 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I see your point. Well the components are based on the components and the password page b/c of the parent password route. Also because I have an acceptance/password-test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snewcomer
Copy link
Contributor Author

@joshsmith all checks have passed. Probably a good idea to style some pieces here. I can tackle, but didn't know if you wanted to do that.

mirage/config.js Outdated
let passwordConfirmation = params.split('&')[1].split('=')[1];
let [first, second] = params.split('&');
let password = first.split('=')[1];
let passwordConfirmation = second.split('=')[1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a this._getAttrsForFormRequest() which returns a POJO containing the form data, doing this job for you. It's undocumented, but it is supported by mirage.

However, you are using an arrow function, so you do not have access to this.

I believe something like

this.post('/password/reset', function() {
  let { password, passwordConfirmation } = this._getAttrsForFormRequest();

  // continue with the rest of your code here
));

ought to do the trick.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome trick!

@snewcomer
Copy link
Contributor Author

@joshsmith fyi this is failing only on phantom. That is why I put in PR #1253


ajax: service(),
session: service(),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I need here is a model hook that extracts the token from params and used to send up with the POST request.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the same functionality you proposed by changing the route in API, you could do something like:

queryParams: {
  token: { refreshModel: true }
}

If you add that code into the route, the params hash in the model hook will contain a token key.

The standard way to specify these query parameters which are not part of the dynamic route URL would be to specify them in the controller:

export default Controller.extend({
  queryParams: ['token']
});

but the way I described above allows us to skip unnecessarily defining a controller file.

@snewcomer
Copy link
Contributor Author

@joshsmith @begedin So I tried this exact same code out on a simple app I just spun up and it works. Just need the email created part.

@snewcomer snewcomer force-pushed the password-work branch 3 times, most recently from d6b23d0 to c335d4d Compare May 18, 2017 15:55
@joshsmith joshsmith force-pushed the password-work branch 2 times, most recently from fa5d7a6 to 21f22fc Compare May 30, 2017 21:52
Copy link
Contributor Author

@snewcomer snewcomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! No real comments. I def forgot about the reset password url reflecting the token_id.

@@ -24,10 +24,10 @@ export default Route.extend({
data: {
token: get(this, 'controller.token'),
password,
passwordConfirmation
"password-confirmation": passwordConfirmation
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious about this change. Is this a stylistic thing? Just asking for future ref.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because most of the keys in JSON API are dasherized by default. There's some lack of consistency in some places at present that are not standard CRUD (like here), but would like to bring at least that consistency to bear here, even if the resource itself isn't a JSON API resource.


andThen(() => {
assert.equal(currentURL(), '/password/reset');
assert.equal(currentURL(), '/password/reset?token=abc123');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yep, I forgot about those changes.

@@ -53,7 +59,9 @@ test('visiting /password/forgot', function(assert) {
assert.equal(currentURL(), '/password/forgot');
});

passwordPage.forgotPasswordForm.sendForgotPasswordSuccessfully('admin@gmail.com');
andThen(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surprised this has to be wrapped in an andThen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because the Mirage response is a promise and we don't know when it will return.

Copy link
Contributor

@joshsmith joshsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Merging as soon as build passes. Thanks for all the hard work on this @snewcomer! 👍

@joshsmith joshsmith merged commit 0436aec into develop May 31, 2017
@joshsmith joshsmith deleted the password-work branch May 31, 2017 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants