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

feat: update email preferences copy #3162

Merged
merged 1 commit into from
Jun 13, 2024

Conversation

masimons
Copy link
Contributor

@masimons masimons commented Jun 11, 2024

Ticket #3064

Description

This change is part of the Assign-to-Share project. We want the copy within the Email Notifications settings to change if the feature flag shareTerminologyEnabled is set to true.

Screenshots / Demo Video

"Assign" copy:
image

"Share" copy:
image

Testing

I verified this change locally. MyProfileView.vue doesn't seem to have a corresponding test file yet. I've been on engineering teams where the process in this case would be to create a separate chore to write tests for MyProfileView.vue (especially since the change made in this PR is small). But if the process here is to commit a test without exception, then I may ask to pair briefly with someone on a unit test for this code (I'm not super familiar with Vue.js, and I hit my timebox for getting a test working on my own).

Manual steps for reviewer:
Navigate to My Profile -> Email Notifications to verify the copy change (the shareTerminologyEnabledFlag will need to be manually set to false/true in the code to see the change).

Automated and Unit Tests

  • Added Unit tests

Manual tests for Reviewer

  • Added steps to test feature/functionality manually

Checklist

  • Provided ticket and description
  • Provided screenshots/demo
  • Provided testing information
  • Provided adequate test coverage for all new code
  • Added PR reviewers

@github-actions github-actions bot added enhancement New feature or request javascript Pull requests that update Javascript code labels Jun 11, 2024
Copy link

github-actions bot commented Jun 11, 2024

QA Summary

QA Check Result
🌐 Client Tests
🔗 Server Tests
🤝 E2E Tests
📏 ESLint
🧹 TFLint

Test Coverage

Coverage report for `packages/client`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🔴 All files 14.66 11.53 16.18 14.84
🔴  src 0 100 100 0
🔴   App.vue 0 100 100 0 9
🔴  src/arpa_reporter 0 100 100 0
🔴   App.vue 0 100 100 0 13
🟡  ...ter/components 53.96 31.81 58.97 53.96
🔴   AlertBox.vue 33.33 0 0 33.33 35-36
🟡   ...oadButton.vue 57.14 50 42.85 57.14 60-67
🟢   ...ileButton.vue 100 100 100 100
🔴   ...ttonSmall.vue 0 100 0 0 13-23
🟢   ...mplateBtn.vue 100 100 100 100
🟡   ...avigation.vue 65 100 62.5 65 213-219,228-235
🔴   StandardForm.vue 37.5 25 55.55 37.5 124-128,135-157
🟢  ...porter/helpers 84.61 79.48 87.5 84.61
🟢   form-helpers.js 84.21 79.48 85.71 84.21 7,16,25,81-83
🟢   short-uuid.js 100 100 100 100
🔴  ...eporter/router 0 0 0 0
🔴   index.js 0 0 0 0 21-135
🔴  ...reporter/store 4.85 0 2.17 5.1
🔴   index.js 4.85 0 2.17 5.1 13-16,34-263
🔴  ...reporter/views 17.03 11.51 25.98 17.03
🟢   AgenciesView.vue 100 100 100 100
🔴   AgencyView.vue 0 0 0 0 30-96
🔴   HomeView.vue 16.66 5 50 16.66 113,137-207
🔴   LoginView.vue 0 0 0 0 49-100
🔴   ...plateView.vue 0 0 0 0 47-113
🔴   ...ploadView.vue 0 0 0 0 24-144
🔴   ...eriodView.vue 0 0 0 0 30-90
🔴   ...riodsView.vue 0 0 0 0 124-174
🔴   ...pientView.vue 0 0 0 0 56-152
🔴   ...ientsView.vue 0 0 0 0 99-206
🔴   UploadView.vue 49.12 47.61 78.94 49.12 ...41-442,448-449
🔴   UploadsView.vue 44.44 40.9 66.66 44.44 ...61-264,272-287
🔴   UserView.vue 40.62 20 68.75 40.62 84,97-137
🔴   UsersView.vue 0 0 0 0 58-126
🔴   ...ationView.vue 0 0 0 0 109-270
🔴  src/components 6.38 1.49 5.71 6.38
🔴   ...vityTable.vue 3.84 0 0 3.84 110-179
🔴   BaseLayout.vue 45.45 100 44.44 45.45 220-232
🔴   ...atesTable.vue 11.11 0 0 11.11 56-88
🔴   CopyButton.vue 0 100 0 0 29-49
🔴   GrantsTable.vue 3.84 0 0 3.84 187-543
🔴   ...dUploader.vue 3.7 0 0 3.7 55-111
🔴   SearchFilter.vue 5.55 0 0 5.55 40-82
🔴   UserAvatar.vue 12.5 0 0 12.5 29-40
🔴  ...ponents/Modals 9.09 1.6 10.93 9.12
🔴   ...anization.vue 18.75 0 22.22 18.75 143-178
🔴   AddTeam.vue 45 25 58.33 45 204,210,216-245
🔴   AddUser.vue 0 0 0 0 74-176
🔴   ...anization.vue 20 0 16.66 20 58-78
🔴   EditTeam.vue 12.19 0 30.76 12.19 208-301
🔴   EditUser.vue 6.25 0 0 6.25 72-128
🔴   ...ilsLegacy.vue 2.5 0 0 2.5 205-369
🔴   ImportTeams.vue 10 0 0 10 56-82
🔴   ImportUsers.vue 0 0 0 0 49-80
🔴   ...archPanel.vue 3.03 0 0 3.03 146-255
🔴   SearchPanel.vue 3.44 0 0 3.5 301-486
🔴  src/helpers 16.96 20 17.14 17.43
🟢   constants.js 100 100 100 100
🟢   currency.js 100 100 100 100
🟢   dates.js 100 100 100 100
🔴   fetchApi.js 5.71 16.66 5.88 5.71 9-97
🔴   filters.js 4 0 0 4.54 19-51
🔴   form-helpers.js 0 0 0 0 5-82
🟡   gtag.js 77.77 90 75 77.77 12,51
🟢  ...s/featureFlags 90.9 100 80 90.9
🟡   index.js 66.66 100 66.66 66.66 12
🟢   utils.js 100 100 100 100
🔴  src/mixin 20 0 28.57 20
🔴   ...zableTable.js 20 0 28.57 20 16-31,36-37,42
🔴  src/router 18.91 12.5 15.78 18.91
🔴   index.js 18.91 12.5 15.78 18.91 ...78-179,183-201
🟢  src/store 100 100 100 100
🟢   index.js 100 100 100 100
🔴  src/store/modules 3.58 0 4.72 3.77
🔴   agencies.js 5.26 100 8.33 5.55 13-70
🔴   alerts.js 20 100 20 20 10-24
🔴   grants.js 1.41 0 1.05 1.49 58-352
🔴   organization.js 33.33 100 33.33 33.33 21-25
🔴   roles.js 20 100 20 25 13-22
🔴   tenants.js 11.11 100 14.28 12.5 13-32
🔴   users.js 2.43 0 4.76 2.5 17-100
🔴  src/views 12.39 4.44 11.67 12.44
🔴   ...orterView.vue 0 0 0 0 96-151
🔴   ...boardView.vue 30 0 36.36 30 ...30-140,158-169
🔴   ...tailsView.vue 0 0 0 0 239-526
🟢   GrantsView.vue 100 100 100 100
🔴   LoginView.vue 5.88 0 0 5.88 87-134
🔴   MyGrantsView.vue 0 100 0 0 46-65
🟡   ...ofileView.vue 77.77 66.66 75 77.77 130-134
🟡   NotFoundView.vue 80 100 0 100
🔴   ...tionsView.vue 45.45 100 40 45.45 84,94-97,111-115
🔴   ...ivityView.vue 0 0 0 0 63-134
🟡   TeamsView.vue 54.54 100 50 54.54 142,156-163
🔴   ...DatesView.vue 0 0 0 0 49-119
🔴   UsersView.vue 0 0 0 0 62-139
Coverage report for `packages/server`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟡 All files 57.97 50.27 53.56 58.08
🟢  src 81.63 33.33 60 81.63
🟢   configure.js 81.63 33.33 60 81.63 42,61-68,97-99
🟢  src/arpa_reporter 98.75 66.66 100 98.75
🟢   configure.js 97.36 40 100 97.36 36
🟢   environment.js 100 100 100 100
🟢   use-request.js 100 100 100 100
🔴  src/arpa_reporter/db 38.58 32.92 44.44 40.16
🔴   arpa-subrecipients.js 13.15 4.34 15.38 14.28 23-92
🔴   reporting-periods.js 37.2 46.87 40 38.09 46,77-156
🟢   settings.js 100 83.33 100 100 13
🟡   uploads.js 50 28.57 52.38 51.42 18-29,84,99-124,141-150
🔴  src/arpa_reporter/lib 29.57 33.08 34.56 28.46
🟢   arpa-ec-codes.js 100 100 100 100
🔴   audit-report.js 21.44 19.35 24.19 21.32 ...28-529,554-684,732-758
🟡   ensure-async-context.js 75 100 50 100
🟢   format.js 90.62 90 90 91.3 41-42
🟡   log.js 75 50 50 75 13,25
🟡   preconditions.js 66.66 33.33 100 66.66 3
🔴   spreadsheet.js 9.09 0 0 9.09 15-32
🟢   validation-error.js 85.71 100 50 85.71 16
🔴  src/arpa_reporter/routes 40 14.92 14.28 40.6
🔴   agencies.js 22.58 0 0 23.33 13-21,26-53
🟡   application_settings.js 75 100 0 75 10-11
🟡   audit-report.js 68.91 58.33 100 68.91 57-58,64-78,100-116
🟢   exports.js 81.42 83.33 100 81.42 61-75,98-99
🔴   reporting-periods.js 20 0 0 20.43 ...25-137,143-149,154-180
🔴   subrecipients.js 23.8 0 0 23.8 12-13,17-27,31-48,52-63
🔴   uploads.js 28.28 7.89 9.09 29.16 ...33-154,164-166,173-180
🔴   users.js 19.6 0 0 20 15-35,39-44,48-81
🔴  src/arpa_reporter/services 42.83 30.41 45.71 43.22
🔴   generate-arpa-report.js 36.86 2.79 50 37.24 ...-974,983-996,1070-1137
🔴   get-template.js 21.62 0 0 21.62 18-79
🟡   persist-upload.js 68.6 90 69.56 68.67 ...58-200,221-235,273-295
🔴   records.js 20.75 0 11.11 21.15 38-204,221-276
🔴   revalidate-uploads.js 37.5 100 0 37.5 5-14
🔴   validate-upload.js 38.86 50.6 33.33 39.81 ...20,339,361,379-656,671
🟢   validation-rules.js 98.18 90 90.9 100 157,173
🟡  src/db 74.45 71.31 68.78 74.49
🟢   connection.js 100 50 100 100 6
🟢   constants.js 100 100 100 100
🟡   helpers.js 75 83.33 50 75 5,21-22
🟢   index.js 82.57 78.36 82.35 82.53 ...62-1428,1610-1611,1618
🟢   saved_search_migration.js 92 88.23 71.42 93.61 5,69,134
🔴   tenant_creation.js 10.58 2.7 0 11.11 15-40,48-210,220
🔴  src/db/arpa_reporter_db_shims 23.68 0 0 23.68
🔴   agencies.js 22.22 100 0 22.22 11-51
🔴   users.js 25 0 0 25 12-62
🟢  src/lib 85.32 78.75 91.04 85.28
🟢   access-helpers.js 93.54 89.18 100 93.54 96-97,102-103
🟢   agencyImporter.js 90.38 88.46 100 90.19 26,29,35,93-94
🟢   email.js 92.85 79.24 100 92.76 ...38,160-164,211,357-360
🔴   gost-aws.js 47.82 37.5 42.85 47.72 13-58,94,104,114-134
🟢   grants-ingest.js 83.33 97.5 90 83.33 ...28-131,138-140,155-159
🟡   logging.js 77.77 85.71 100 77.77 11,13
🟢   redirect_validation.js 100 100 100 100
🟢   userImporter.js 82.27 58.33 88.88 81.57 32,47,53,62,73-81,143-152
🔴  src/lib/annualReports 27.38 0 0 27.38
🔴   doc-builder.js 7.69 0 0 7.69 19-352
🟡   index.js 80 100 0 80 6
🟢   placeholderTextStrings.js 100 100 100 100
🔴   reportBuilder.js 17.24 0 0 17.24 21-33,50-62,86-103
🟢  src/lib/arpa_reporter_shims 100 100 100 100
🟢   email.js 100 100 100 100
🟢  src/lib/email 93.1 87.5 100 92.59
🟢   constants.js 100 100 100 100
🟢   email-nodemailer.js 88.23 83.33 100 86.66 33,64
🟢   service-email.js 100 100 100 100
🟢  src/lib/fieldConfigs 100 100 100 100
🟢   fundingActivityCategories.js 100 100 100 100
🟡  src/routes 70.59 62.2 62.82 70.53
🔴   agencies.js 42.39 30 33.33 42.39 ...13-121,125-146,154-160
🔴   annualReports.js 47.05 100 0 47.05 15-27
🟢   eligibilityCodes.js 100 100 100 100
🟢   grants.js 80.97 71.02 76.47 81.25 ...80-381,396-399,[412-413](https://github.com/...*[Comment body truncated]*

Copy link

github-actions bot commented Jun 11, 2024

Terraform Summary

Step Result
🖌 Terraform Format & Style
⚙️ Terraform Initialization
🤖 Terraform Validation
📖 Terraform Plan

Hint: If "Terraform Format & Style" failed, run terraform fmt -recursive from the terraform/ directory and commit the results.

Output

Validation Output
stdout:
Success! The configuration is valid.


-------------------------------------
stderr:

Plan Summary
CHANGE RESOURCE
add module.website.aws_s3_object.origin_dist_artifact["assets/ClosingDatesTable-GMhh2UV-.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/ClosingDatesTable-GMhh2UV-.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/DashboardView-BJ_dQ8jP.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/DashboardView-BJ_dQ8jP.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsLegacy-C3Cix7oi.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsLegacy-C3Cix7oi.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsView-BqjBzmE4.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsView-BqjBzmE4.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsTable-CpqgYTBi.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsTable-CpqgYTBi.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsView-CLP6xTdv.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsView-CLP6xTdv.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyGrantsView-Bd8JsgtX.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyGrantsView-Bd8JsgtX.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyProfileView-48vp0pYc.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyProfileView-48vp0pYc.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/OrganizationsView-C-9S8cJt.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/OrganizationsView-C-9S8cJt.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/RecentActivityView-CTiyqq0e.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/RecentActivityView-CTiyqq0e.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/TeamsView-CoR1Ig_v.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/TeamsView-CoR1Ig_v.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/UpcomingClosingDatesView-B0B3F7xn.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/UpcomingClosingDatesView-B0B3F7xn.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/UsersView-Du2SK8T8.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/UsersView-Du2SK8T8.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/main-2OrICFIB.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/main-2OrICFIB.js.map"]
update module.api.aws_ecs_service.default[0]
module.api.module.grant_digest_scheduled_task.aws_iam_role_policy.default[0]
module.api.module.grant_digest_scheduled_task.aws_scheduler_schedule.default[0]
module.arpa_audit_report.aws_ecs_service.default
module.arpa_treasury_report.aws_ecs_service.default
module.consume_grants.aws_ecs_service.default
module.website.aws_s3_object.deploy-config[0]
module.website.aws_s3_object.origin_dist_artifact["index.html"]
recreate module.api.aws_ecs_task_definition.default[0]
module.arpa_audit_report.aws_ecs_task_definition.consumer
module.arpa_treasury_report.aws_ecs_task_definition.consumer
module.consume_grants.aws_ecs_task_definition.consume_grants
delete module.website.aws_s3_object.origin_dist_artifact["assets/ClosingDatesTable-DayK_7Ek.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/ClosingDatesTable-DayK_7Ek.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/DashboardView-DHd73FV2.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/DashboardView-DHd73FV2.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsLegacy-BLGBksZR.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsLegacy-BLGBksZR.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsView-BVL-jSRI.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantDetailsView-BVL-jSRI.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsTable-B9AgGLus.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsTable-B9AgGLus.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsView-CVWLma-K.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/GrantsView-CVWLma-K.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyGrantsView-Cbjig0oW.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyGrantsView-Cbjig0oW.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyProfileView-CxcFvpCG.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/MyProfileView-CxcFvpCG.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/OrganizationsView-D6h1Satw.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/OrganizationsView-D6h1Satw.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/RecentActivityView-ChBn964y.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/RecentActivityView-ChBn964y.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/TeamsView-_RqcLgPA.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/TeamsView-_RqcLgPA.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/UpcomingClosingDatesView-CdhREasN.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/UpcomingClosingDatesView-CdhREasN.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/UsersView-CbXVob8T.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/UsersView-CbXVob8T.js.map"]
module.website.aws_s3_object.origin_dist_artifact["assets/main-DYEElho_.js"]
module.website.aws_s3_object.origin_dist_artifact["assets/main-DYEElho_.js.map"]

Pusher: @masimons, Action: pull_request_target, Workflow: Continuous Integration

@masimons masimons force-pushed the ms-email-preference-assign-to-share branch from 689aec9 to 66ad49b Compare June 11, 2024 23:17
@masimons masimons changed the title feature: Update email preferences copy feat: update email preferences copy Jun 11, 2024
@masimons masimons force-pushed the ms-email-preference-assign-to-share branch 3 times, most recently from 5365db6 to e484cee Compare June 12, 2024 16:45
@masimons masimons marked this pull request as ready for review June 12, 2024 18:07
Copy link
Member

@TylerHendrickson TylerHendrickson left a comment

Choose a reason for hiding this comment

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

@masimons TL;DR Approving this because I think the change looks good overall and it's working for me in manual testing. I do think there's some work to do around testing (see below) but I don't know that it needs to hold up this PR.

Regarding tests: we don't currently have any hard rule that says all Vue template changes need automated tests to be accepted. However, I think it's a good idea to try adding them as we go. Ideally, this change would be tested in a MyProfileView.spec.js file.

I checked out this PR branch and tried my hand at writing some tests; a few observations to share (some or all of which you no doubt also encountered):

  • Because the shareTerminologyEnabled() function from the featureFlags module is called within the Vue template's data() export, we don't have a way to configure a flag value when mounting a Vuex.Store during test setup. By contrast, in TeamsView.spec.js, the newTerminologyEnabled feature flag value is set by configuring a computed method for the view.
  • Simply creating a computed helper method like shareTerminologyEnabled in MyProfileView.vue wouldn't be all that useful, since this.shareTerminologyEnabled() wouldn't be callable from the data() export.
  • I'm wondering if the "correct" way to do this would be to perform a more intensive refactoring of the MyProfileView.vue template, i.e. that utilizes a custom component for the email toggles or else removes the <v-for="pref in prefs"> loop in favor of templating each preference toggle explicitly, which would allow us to evaluate the feature flag in the template logic rather than when data() is executed.
  • All that said, I was (almost) able to get tests working without making any changes to MyProfileView.vue by calling localStorage.set('featureFlags', 'Mock JSON-serialized feature flag here') (see below). I also think there's a similar approach here that could work by mocking the @/helpers/featureFlag module's exported shareTerminologyEnabled() function, but I wasn't quite able to figure out Vitest's mocking system.
    • Note: I say "almost" because I wasn't able to identify a good selector to use when defining the prefName variable. Presumably this could be fixed by tweaking the id attributes in the template a bit, e.g. by adding something like :id="pref.key" in the <b-row> element that loops on v-for="pref in prefs".
Expand for example test file
import {
  describe, beforeEach, afterEach, it, expect,
} from 'vitest';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import MyProfileView from '@/views/MyProfileView.vue';

const localVue = createLocalVue();
localVue.use(Vuex);

let store;
let wrapper;

const stubs = [];
const noOpGetters = {
  'users/loggedInUser': () => undefined,
};
const noOpActions = {
  'users/updateEmailSubscriptionPreferencesForLoggedInUser': () => {},
};
afterEach(() => {
  store = undefined;
  wrapper = undefined;
});

describe('MyProfileView.vue', () => {
  describe('shareTerminologyEnabled flag is enabled', () => {
    const originalSessionStorageFeatureFlags = sessionStorage.getItem('featureFlags');
    beforeEach(() => {
      sessionStorage.setItem(
        'featureFlags',
        JSON.stringify({
          ...(originalSessionStorageFeatureFlags || {}),
          shareTerminologyEnabled: true,
        }),
      );
      store = new Vuex.Store({
        getters: {
          ...noOpGetters,
          'users/loggedInUser': () => ({
            name: 'Someone',
            emailPreferences: { GRANT_ASSIGNMENT: 'SUBSCRIBED' },
          }),
        },
        actions: {
          ...noOpActions,
        },
      });
      afterEach(() => {
        sessionStorage.setItem(
          'featureFlags',
          originalSessionStorageFeatureFlags,
        );
      });
      wrapper = shallowMount(MyProfileView, {
        store,
        localVue,
        stubs,
        computed: {
          shareTerminologyEnabled: () => true,
        },
      });
    });
    it('should present GRANT_ASSIGNMENT email preferences with share terminology', () => {
      expect(JSON.parse(window.sessionStorage.getItem('featureFlags'))).toEqual(
        { shareTerminologyEnabled: true },
      );
      const prefName = wrapper.find('!!!Could not figure out a selector!!!');
      expect(prefName.text()).toEqual('Shared Grants');
    });
  });
});

At this point, my feeling is that it is worth having tests, but there's enough to unpack here that it makes more sense to (as you suggested) create a separate chore issue to discuss a path forward and encapsulate this work. Before I totally commit to that, I do want to see if @jeffsmohan has anything to add here, because he's much more familiar with Vue and Vitest than I am.

@jeffsmohan
Copy link
Contributor

Oh this brings up more feelings for me than expected, lol. 😝 (To be clear, absolutely nothing to do with your approach here, @masimons  — I love the timebox and then check in tactic!)

The categories of my feelings:

  1. Feature flags. I'd love if we had a more robust ecosystem around our feature flags. (Ability to flip them without a code deploy, nice helpers to toggle them in tests, etc.) That's obviously a much bigger topic, but it sure would be nice to have a simple helper method for tests here.
  2. Writing tests. Totally agree that we'd benefit from better frontend test coverage! That said, a lot of kinds of frontend changes really don't lend themselves to good tests (changing a background color, reordering stuff on the page, etc.), so I tend to favor a bias toward testing rather than an iron-clad rule that all PRs must come with tests. In specific for something like the change in this PR, it's on the edge but I personally don't tend to think tests are worth it — given the simplicity of the logic involved, it verges on writing a test for expect(1 + 1).toEqual(2), where you're kind of just testing whether core language functionality works, as opposed to testing your own business logic. (This is just my personal viewpoint, fwiw... there's lots of room for other opinions on testing, and I'd definitely defer to USDR's norms here!)
  3. Non-reactive data in data. The definition of prefs and breadcrumb_items in the data option for the MyProfileView component is not really the cleanest. Ideally, the data option is where you define reactive data for your component, but here it's just data structures. Vue 3 has some better idioms for this, but in Vue 2, the best options are:
    • Write it out in the template (and factor out a new component if it's getting too repetitive) like @TylerHendrickson suggested:
      // For prefs
      <CheckboxInput name="New Grant Digest" key="GRANT_DIGEST" description="Send me a daily..." />
      <CehckboxInput name="Grants Assignment" key="GRANT_ASSIGNMENT" description="Send me notifications..." />
      // For breadcrumbs
      <b-breadcrumb :items="[
        { text: 'Home', to: 'grants' },
        { text: 'My Profile', href: '#' },
      ]" />
    • Stash it directly in the component options (bit awkward in Vue 2, but for very large/complex values this can be preferable to the above):
      // define the data directly on the component options
      {
        data() { ... },
        prefs: [ ... ], // the existing prefs value currently defined in data
        ...
      }
      // use the data in the template via `$options`
      <b-row v-for="pref in $options.prefs>...</b-row>
  4. Testing feature flags. For now, the cleanest way to test functionality behind a feature flag is to mock the flag at the helper level. Here's a working rough draft of tests for the functionality in this PR, for example:
    import {
      describe, it, expect, vi,
    } from 'vitest';
    import { createLocalVue, shallowMount } from '@vue/test-utils';
    import Vuex from 'vuex';
    import MyProfileView from '@/views/MyProfileView.vue';
    import { shareTerminologyEnabled } from '@/helpers/featureFlags';
    
    describe('MyProfileView', () => {
      // Set up a mock store for testing
      const localVue = createLocalVue();
      localVue.use(Vuex);
      const store = new Vuex.Store({
        getters: {
          'users/loggedInUser': () => ({
            emailPreferences: {
              GRANT_ASSIGNMENT: 'SUBSCRIBED',
              GRANT_INTEREST: 'SUBSCRIBED',
              GRANT_DIGEST: 'SUBSCRIBED',
              GRANT_FINDER_UPDATES: 'SUBSCRIBED',
            },
          }),
        },
      });
    
      // Mock the relevant flag
      vi.mock('@/helpers/featureFlags', async (importOriginal) => ({
        ...await importOriginal(),
        shareTerminologyEnabled: vi.fn(),
      }));
    
      it('uses assign terminology when flag is off', () => {
        vi.mocked(shareTerminologyEnabled).mockReturnValue(false);
        const wrapper = shallowMount(MyProfileView, { store, localVue });
        expect(wrapper.text()).toContain('Grants Assignment');
      });
    
      it('uses share terminology when flag is on', () => {
        vi.mocked(shareTerminologyEnabled).mockReturnValue(true);
        const wrapper = shallowMount(MyProfileView, { store, localVue });
        expect(wrapper.text()).toContain('Shared Grants');
      });
    });

Sorry for the essay! Upshot is, if you wanted to add tests in this PR, I don't think it's too onerous, fft grab the test code above and finesse it just a bit, and you should be good to go. :)

Copy link
Contributor

@jeffsmohan jeffsmohan left a comment

Choose a reason for hiding this comment

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

Nice work, change also lgtm as-is, fwiw! Though I have a small optional improvement, and I left a giganto comment that ends with how to test this change if you choose to. 😸

key: 'GRANT_ASSIGNMENT',
description: 'Send me notifications if a grant has been assigned to my USDR Grants team.',
description: `${shareTerminologyEnabledFlag ? grantShareDescription : grantAssignmentDescription}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

(minor nit, optional and non-blocking) The js format string with the backticks are unnecessary for the name and description here, since we're not really interpolating anything. You can just do e.g. name: shareTerminologyEnabledFlag ? 'Shared Grants' : 'Grants Assignment', ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OOPS you are so right. Missed that during cleanup. Thanks for catching.

@masimons masimons force-pushed the ms-email-preference-assign-to-share branch from e484cee to 0f90b07 Compare June 13, 2024 20:25
Copy link
Contributor

@jeffsmohan jeffsmohan left a comment

Choose a reason for hiding this comment

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

Updates lgtm! You'll just need to get things linted so the check passes. Have you got auto-formatting set up for your editor? A bit of info can be found in our docs here, but I'm happy to talk you through it as the docs aren't exactly extensive.

@masimons masimons force-pushed the ms-email-preference-assign-to-share branch from 0f90b07 to dda3551 Compare June 13, 2024 20:46
@masimons masimons force-pushed the ms-email-preference-assign-to-share branch from dda3551 to 1b27fe2 Compare June 13, 2024 20:54
@masimons
Copy link
Contributor Author

masimons commented Jun 13, 2024

@jeffsmohan a) Thank you SO MUCH for the time you invested in your response, and for sharing working tests and great insight. Really appreciate it 🙏
b) I just rebased main, and ran the linter, so I'm pretty sure this is the final version for reals and that once you re-re-approve this PR, it will be the last time haha
c) Indeed I have not set up the auto-formatting for vs code yet, thank you for sharing that doc 👍
d) Since I've got you here, from what I understand, merging a PR will deploy to the staging env, NOT production. Just want to make sure this is the case, because I have NOT tested this on staging (in the case that deploys to staging do need to be done before merge)

@jeffsmohan
Copy link
Contributor

No problem, I just hope it was more helpful than overwhelming! :P

VSCode should be a pretty straightforward linting setup, hopefully. If you install all the recommended workspace extensions, it should Just Work when you save files. But lmk if you run into trouble.

And you're correct — merged PRs in this repo get auto-deployed to staging (you can see the GH Action for this here). But it will not deploy to production automatically. The USDR staff run a process, usually on Tue and Thu, to cut a release, do some manual QA, and then trigger a deploy to production. So if your checks are green and GH allows you to merge the PR, you should feel free to merge it whenever!

Copy link
Contributor

@jeffsmohan jeffsmohan left a comment

Choose a reason for hiding this comment

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

lgtm! Thanks for asking good questions and reading through the very long answers :)

@masimons masimons merged commit 1132656 into main Jun 13, 2024
19 checks passed
@masimons masimons deleted the ms-email-preference-assign-to-share branch June 13, 2024 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants