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

Update display name front-end validation to match back-end #14873

Merged
merged 19 commits into from
Feb 14, 2023

Conversation

puneetlath
Copy link
Contributor

@puneetlath puneetlath commented Feb 6, 2023

Details

Updates the display name validation to match the back-end. These are the changes:

  1. Added maxLength prop to first and last name fields
  2. Added validation that "Expensify" or "Concierge" aren't used in the first name field
  3. Updated some of the personal details related error message copy
  4. Did some general refactoring of the display name validation to make it simpler and more straightforward

Fixed Issues

$ #13779

Tests

  1. Go to Settings > Profile > Display name
  2. Keep typing in the first name or last name fields
  3. Verify that the input stops adding more after 50 characters
  4. Add a semicolon or a comma anywhere in either of the fields
  5. Verify that you get a validation error
  6. Add the word "concierge" or the word "expensify" anywhere in either field
  7. Verify that you get a validation error only for the first name field
  8. Use a name that doesn't result in any errors
  9. Verify that you're able to update the name successfully
  • Verify that no errors appear in the JS console

Offline tests

Same tests. Everything here should work while offline.

QA Steps

  1. Go to Settings > Profile > Display name
  2. Keep typing in the first name or last name fields
  3. Verify that the input stops adding more after 50 characters
  4. Add a semicolon or a comma anywhere in either of the fields
  5. Verify that you get a validation error
  6. Add the word "concierge" or the word "expensify" anywhere in either field
  7. Verify that you get a validation error only for the first name field
  8. Use a name that doesn't result in any errors
  9. Verify that you're able to update the name successfully
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android / native
    • Android / Chrome
    • iOS / native
    • iOS / Safari
    • MacOS / Chrome / Safari
    • MacOS / Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I verified the translation was requested/reviewed in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

Screenshots/Videos

Web image image
Mobile Web - Chrome Screenshot 2023-02-06 at 4 51 55 PM Screenshot 2023-02-06 at 4 51 47 PM
Mobile Web - Safari Screenshot 2023-02-06 at 5 16 52 PM Screenshot 2023-02-06 at 5 16 45 PM
Desktop Screenshot 2023-02-06 at 4 42 35 PM
iOS
Android Screenshot 2023-02-06 at 4 49 31 PM Screenshot 2023-02-06 at 4 49 02 PM

@puneetlath puneetlath self-assigned this Feb 6, 2023
@puneetlath puneetlath marked this pull request as ready for review February 6, 2023 22:03
@puneetlath puneetlath requested a review from a team as a code owner February 6, 2023 22:03
@melvin-bot melvin-bot bot requested review from mollfpr and youssef-lr and removed request for a team February 6, 2023 22:04
@MelvinBot
Copy link

@mollfpr @youssef-lr One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@puneetlath
Copy link
Contributor Author

I'm having trouble getting my iOS simulator to work, but otherwise everything should be ready for review.

@mollfpr
Copy link
Contributor

mollfpr commented Feb 7, 2023

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • Android / native
    • Android / Chrome
    • iOS / native
    • iOS / Safari
    • MacOS / Chrome / Safari
    • MacOS / Desktop
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Web
14873.Web.mov
Mobile Web - Chrome
14873.mWeb.Chrome.mov
Mobile Web - Safari
14873.mWeb.Safari.mp4
Desktop
14873.Desktop.mov
iOS
14873.iOS.mp4
Android
14873.Android.mov

@mollfpr
Copy link
Contributor

mollfpr commented Feb 7, 2023

Input 0 only on the first name, and submitting the form will reset the user display name to the email, but the first name in personalDetails remains 0. No error was returned from the API. I guess the user should be expected to have the first name 0 displayed if we allow it.

Screen.Recording.2023-02-07.at.14.13.13.mov

Screen Shot 2023-02-07 at 14 17 19

@puneetlath
Copy link
Contributor Author

Nice catch @mollfpr. I'll look into it.

@neil-marcellini
Copy link
Contributor

I'm going to bow out of this PR review for #focus.

@neil-marcellini neil-marcellini removed their request for review February 7, 2023 18:50
@puneetlath
Copy link
Contributor Author

Thanks for the thorough review so far @mollfpr. Ready for another round!

mollfpr
mollfpr previously approved these changes Feb 9, 2023
Copy link
Contributor

@mollfpr mollfpr left a comment

Choose a reason for hiding this comment

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

Looks good to me and test well on the RequestCallPage, LegalNamePage, and DisplayNamePage 👍

Here's the checklist!

@puneetlath
Copy link
Contributor Author

Thank you @mollfpr!

@youssef-lr could you also give this a review please?

@neil-marcellini
Copy link
Contributor

I can take a look today.

@neil-marcellini neil-marcellini self-requested a review February 13, 2023 18:22
Copy link
Contributor

@neil-marcellini neil-marcellini left a comment

Choose a reason for hiding this comment

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

Looking pretty great overall. I like how you have simplified this a lot. There's a few small changes I would like to see. Also I think it would be good to include tests for each page that was modified here, just in case.

Comment on lines 63 to 66
if (doesFirstNameHaveInvalidCharacters) {
errors.firstName = this.props.translate('personalDetails.error.hasInvalidCharacter');
} else if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_FIRST_NAMES)) {
errors.firstName = this.props.translate('personalDetails.error.containsReservedWord');
Copy link
Contributor

Choose a reason for hiding this comment

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

:issue We should display multiple error messages at the same time if multiple validations fail, as we state in the form guidelines. For example, if I set my first name to Concierge; I should see First name cannot contain the words Expensify or Concierge. Name cannot contain a comma or semicolon..

suggestion: We can easily join the error messages together.

Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: We can easily join the error messages together.

I'm not sure we do this in any of our other forms?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, could you give an example of a form that does this? Because that would require copy variations and conditions for each combination of errors. Or I guess appending the error to errors.firstName instead of replacing it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we currently do this in any of our forms. I'll ask in Slack if we want to do this. If not then I'm happy with what we have and we should update the form guidelines.

Copy link
Contributor

Choose a reason for hiding this comment

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

Here's the Slack discussion.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll make this a NAB comment. If we decide to make the change I'll create a refactor issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright sounds good. I'm going to go ahead and merge then. Thanks for starting the slack discussion @neil-marcellini.

src/libs/ValidationUtils.js Outdated Show resolved Hide resolved
@youssef-lr
Copy link
Contributor

youssef-lr commented Feb 14, 2023

Looks good to me and tested well! Just a question @puneetlath, Keeping the first name empty and last name as 'Concierge' will still result in a display name that says 'Concierge'. Is this fine?

Screenshot 2023-02-14 at 10 48 28

Screenshot 2023-02-14 at 10 48 38

@mollfpr
Copy link
Contributor

mollfpr commented Feb 14, 2023

@youssef-lr Yup, we already addressed that here.

@puneetlath puneetlath merged commit 023e59e into main Feb 14, 2023
@puneetlath puneetlath deleted the reserved-first-names branch February 14, 2023 19:43
@OSBotify
Copy link
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@github-actions
Copy link
Contributor

Performance Comparison Report 📊

Significant Changes To Duration

There are no entries

Meaningless Changes To Duration

Show entries
Name Duration
App start TTI 701.139 ms → 721.851 ms (+20.712 ms, +3.0%)
App start runJsBundle 191.194 ms → 196.258 ms (+5.065 ms, +2.6%)
Open Search Page TTI 611.937 ms → 612.790 ms (+0.853 ms, ±0.0%)
App start nativeLaunch 19.871 ms → 19.935 ms (+0.065 ms, ±0.0%)
App start regularAppStart 0.014 ms → 0.015 ms (+0.001 ms, +4.8%)
Show details
Name Duration
App start TTI Baseline
Mean: 701.139 ms
Stdev: 38.170 ms (5.4%)
Runs: 627.5586509997956 648.6231269999407 653.5004900000058 662.5699780001305 665.4013450001366 666.4061839999631 666.8986269999295 668.6453499998897 670.9554849998094 671.256490000058 677.8653839998879 678.2209310000762 687.1833009999245 690.8923229998909 694.5230479999445 696.1947940001264 697.4001290001906 701.8032349999994 709.5908050001599 712.7879579998553 719.0251009999774 720.601090000011 721.3308080001734 726.8798219999298 727.1316539999098 733.215299999807 733.8502839999273 737.9294909997843 737.9422789998353 750.5055470000952 766.626021000091 813.1427620002069

Current
Mean: 721.851 ms
Stdev: 25.363 ms (3.5%)
Runs: 676.1864249999635 689.1270480002277 689.9225849998184 691.8273860001937 694.0429980000481 698.4690399998799 701.8609640002251 702.1158429998904 702.5430870000273 703.4250659998506 704.3568790000863 709.327219999861 713.0752389999107 713.5576530001126 713.8197630001232 719.089668000117 721.7701909998432 722.2383909998462 724.2163680000231 724.4607489998452 729.046875 734.0915850000456 740.5075980001129 741.2544169998728 741.436015999876 741.7652830001898 742.8596310000867 744.8739490001462 750.1877689999528 757.2173469997942 765.5318700000644 795.0211029998027
App start runJsBundle Baseline
Mean: 191.194 ms
Stdev: 22.405 ms (11.7%)
Runs: 159 161 161 165 168 170 173 174 177 178 178 178 179 184 184 186 187 194 197 198 198 200 201 207 210 214 215 220 229 232 250

Current
Mean: 196.258 ms
Stdev: 16.130 ms (8.2%)
Runs: 170 173 175 179 179 180 183 183 184 184 186 191 191 192 195 196 196 196 200 201 203 203 205 208 208 210 212 215 216 231 239
Open Search Page TTI Baseline
Mean: 611.937 ms
Stdev: 19.276 ms (3.2%)
Runs: 580.804525999818 581.6920980000868 584.5612800000235 586.8015949996188 589.1717530000024 589.4432379999198 590.7444659997709 595.9230549996719 598.8281660000794 601.9893390000798 602.0983080002479 603.65677900007 606.7478439998813 607.0749109997414 609.1887210002169 609.885050999932 610.6902669998817 610.8963219998404 612.3330899998546 612.8577069998719 613.0437830002047 619.4685060000047 620.7049160003662 620.8798839999363 622.3158780001104 622.6413579997607 632.7620040001348 635.4674889999442 637.6037189997733 640.1894939998165 645.2804370000958 646.7051190002821 651.472453000024

Current
Mean: 612.790 ms
Stdev: 18.036 ms (2.9%)
Runs: 581.8181969998404 585.6286619999446 586.8728849999607 587.4799810000695 589.7980150002986 593.7728679999709 594.4903569999151 595.3375249998644 600.0264889998361 603.0832520001568 603.9464520001784 606.2943939999677 608.3620609999634 608.9814860001206 609.1951910001226 614.9837650000118 616.0409750002436 617.6699620001018 618.2917479998432 619.9393719998188 623.035279000178 625.1116140000522 625.9863289999776 626.6190590001643 627.4076340002939 627.5345060001127 628.8662509997375 631.7473960001953 644.6124269999564 644.8991299998015 648.6457929997705
App start nativeLaunch Baseline
Mean: 19.871 ms
Stdev: 1.755 ms (8.8%)
Runs: 17 17 18 18 18 18 18 18 19 19 19 19 19 19 20 20 20 20 20 20 20 21 21 21 21 22 22 23 23 23 23

Current
Mean: 19.935 ms
Stdev: 2.139 ms (10.7%)
Runs: 18 18 18 18 18 18 18 18 18 18 18 19 19 19 19 19 19 20 20 20 20 21 21 21 21 22 23 23 24 25 25
App start regularAppStart Baseline
Mean: 0.014 ms
Stdev: 0.001 ms (5.6%)
Runs: 0.012410999741405249 0.012653999961912632 0.012817000038921833 0.01310200011357665 0.013183000031858683 0.013305999804288149 0.013427000027149916 0.013509000185877085 0.013591000344604254 0.013671000022441149 0.01383400009945035 0.013915999792516232 0.013915999792516232 0.013916000258177519 0.013956999871879816 0.014078999869525433 0.01448499970138073 0.014526000246405602 0.014526000246405602 0.014607999939471483 0.014648000244051218 0.014689000323414803 0.014689000323414803 0.014771000016480684 0.014852000400424004 0.014892000239342451 0.0148930000141263 0.015015000011771917 0.01509599993005395 0.015136000234633684 0.015381000004708767

Current
Mean: 0.015 ms
Stdev: 0.001 ms (6.3%)
Runs: 0.013427000027149916 0.013590000104159117 0.013753000181168318 0.013793999794870615 0.013794000260531902 0.013833999633789062 0.013874999713152647 0.013916000258177519 0.014200999867171049 0.01436399994418025 0.014403999783098698 0.014444999862462282 0.014485999941825867 0.014525999780744314 0.014526000246405602 0.014607999939471483 0.01464799977838993 0.014771000016480684 0.014810999855399132 0.014811000321060419 0.0148930000141263 0.014934000093489885 0.01509599993005395 0.015461999922990799 0.01554399961605668 0.015666000079363585 0.0157880000770092 0.0163569999858737 0.0163569999858737 0.016885999590158463 0.0170499999076128

@OSBotify
Copy link
Contributor

🚀 Deployed to staging by https://github.com/puneetlath in version: 1.2.72-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@OSBotify
Copy link
Contributor

🚀 Deployed to production by https://github.com/francoisl in version: 1.2.72-1 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@PauloGasparSv
Copy link
Contributor

Hey everyone, following the BugZero checklist for #15741 here.

We implemented the same validation rules for both the "Display name" and the "Legal Name" in this P.R. but I think the latter has different more strict rules (that don't allow special characters for example).

Because of that, newDot allowed some special characters in the "Legal Name" fields but after saving it the API was stripping them away in the Onyx response.

Video of the bug
Bug5968608_Gravar__2078.mp4

// Then we validate the last name field
if (!ValidationUtils.isValidDisplayName(values.lastName)) {
errors.lastName = this.props.translate('personalDetails.error.hasInvalidCharacter');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We missed out a case here, we shouldn't had allowed the user to submit empty first name, this caused #44167.

Though i agree this is not actually a bug from this PR, but rather a missed logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants