Repository navigation
[Enumerators] Propagate the backfilled initial question to other programs that use the list set - #14344
[Enumerators] Propagate the backfilled initial question to other programs that use the list set#14344chelseakr-exygy wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #14344 +/- ##
============================================
- Coverage 82.60% 82.59% -0.01%
- Complexity 5767 5776 +9
============================================
Files 617 617
Lines 26350 26416 +66
Branches 2123 2132 +9
============================================
+ Hits 21766 21818 +52
- Misses 3851 3859 +8
- Partials 733 739 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
1cfeddc to
24958a7
Compare
38f1b69 to
ccf5fce
Compare
24958a7 to
9f92ca9
Compare
…rams that use the list set
9f92ca9 to
013dca2
Compare
| # Warning shown on the list set screen after an admin picks an initial question for a list set that does not have one yet, before they save it | ||
| alert.repeatedSet.backfillSaveWarning=The initial question will be applied to any uses of this repeated set across programs. You will not be able to change the initial question after you save it. | ||
| # Added after the save warning when exactly one other program also uses this list set | ||
| alert.repeatedSet.backfillSaveWarningOneProgram=This list set is also used in 1 other program, which will get the initial question too. |
There was a problem hiding this comment.
issue (blocking): I don't think this wording was reviewed by UX or Product since I don't see it in the mocks. I just sent the wording to UX for review. Here's is Hannan's recommendation: First sentence visibility depends if programs > 0:
"This will also update X program(s) [bolded] that use(s) this list set. You will not be able to remove the initial question after you save it."
| @@ -1259,6 +1259,10 @@ description.repeatedSet.initialQuestionAdminId=This is the question that the app | |||
| description.repeatedSet.backfillInitialQuestion=Add an initial question that accompanies the enumerator selected above. For example if your enumerator asks users to list members of their household, you might choose a name question. | |||
| # Warning shown on the list set screen after an admin picks an initial question for a list set that does not have one yet, before they save it | |||
| alert.repeatedSet.backfillSaveWarning=The initial question will be applied to any uses of this repeated set across programs. You will not be able to change the initial question after you save it. | |||
There was a problem hiding this comment.
suggestion (non-blocking): Sorry I didn't catch this last time, but the wording here should be "list set" rather than "repeated set". The term was updated since the mocks were created and it seems the mocks weren't updated.
| ) | ||
| }) | ||
|
|
||
| test('warns how many other programs will also get the initial question', async ({ |
There was a problem hiding this comment.
suggestion (non-blocking): This test could be merged into the one above it. Since our browser tests take so long to run and we don't use beforeAll, the eng team decided that a good solution was to merge tests rather than perform the same setup for separate tests.
| saved.initialQuestion().getId(), | ||
| fileUploadQuestionImprovementsEnabled, | ||
| addedTo); | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
suggestion (non-blocking): When propagation fails, the checked exceptions (e.g. CantAddQuestionToBlockException when another program's block already has the question) are turned into a 404 by the outer catch without being logged. That leaves no server-side record of the failure.
Could you log before reverting? Something like:
| } catch (Exception e) { | |
| } catch (Exception e) { | |
| logger.error( | |
| "Could not add initial question {} to every block that uses enumerator {}; reverting", | |
| saved.initialQuestion().getId(), | |
| saved.enumeratorQuestion().getId(), | |
| e); |
Also, right now the user will not know that there was an error because HTMX swallows errors and doesn't display an error page. We don't need to take care of that in this PR. The user-facing error can be handled in #13587.
| blockId); | ||
|
|
||
| assertThat(result.status()).isNotEqualTo(OK); | ||
| // The bank stays open so the admin can retry. |
There was a problem hiding this comment.
issue (blocking): The question bank is already closed by the time Save can be clicked (hxSelectInitialQuestion closes it via HX-Trigger-After-Swap, and the success test at line 555 says so), so this comment isn't accurate. What stays on screen is the staged card with its Save button.
The next assertion doesn't distinguish failure from success either: it checks HX-Trigger rather than HX-Trigger-After-Swap, and the success path has no HX-Trigger too. I think these two lines can be removed. The checks below already verify the revert.
Description
Step 4 of the backfill flow. When an admin saves an initial question on a legacy enumerator (#14328), the question is now also added to every other draft program block that uses the same enumerator. Saving already makes drafts of the published programs that use it, so this covers drafts and published programs alike. The initial question is shared between them, not copied again. The staged card's warning also says how many other programs will get it ("This list set is also used in 2 other programs, which will get the initial question too.").
TDD Step 4 says to run the save and propagation in one transaction so a failure rolls everything back. I tried that, and it breaks the save:
addQuestionsToBlockand the program lookups read on other threads, which can't see uncommitted changes, so adding the initial question to the current block is refused. The TDD allows for this and suggests reverting what was done instead, so that is what this does. If anything fails after the initial question is linked, it removes the question from the blocks it was added to and unlinks it from the enumerator, then returns the error and leaves the bank open so the admin can retry. The copied initial question stays in the question bank, as the TDD notes, and so do any drafts made for published programs. Each undo step is logged if it fails.For review: is the revert approach OK in place of the transaction? The alternative is passing the transaction through the async lookups, which touches a lot of code.
This change was AI-drafted (Claude Code) under my direction.
Checklist
General
civiform/developers, or a more specific round-robin listciviform/eng-adminas FYI (if this PR includes functionality changes and neither you nor the primary reviewer is an admin)isDevOrStagingInstructions for manual testing
ENUMERATOR_IMPROVEMENTS_ENABLED./dev/seedand log in as an admin. The comprehensive sample program already uses the "Sample Enumerator Question", which has no initial question./admin/export?programId=<id>) shows it on the enumerator block's questions.I ran the full
end_to_end_enumerators.test.tsin Chromium against a local app on this branch (25 passed, 7 already skipped), including a new test for the count. In Firefox, a session driven by Claude Code ran the steps above and checked that both programs' enumerator blocks have the initial question. I also clicked through this by hand in Safari: picked the initial question from the bank (using the search so only one question was listed), checked that the card showed the pick and the warning with "also used in 1 other program", checked that leaving the screen discarded the pick, saved it, and checked the initial question on the edit forms of the enumerator in both programs. This also covers the backfill flow from #14328. The unit tests for the touched classes pass (272). The tests that fail without the change arehxBackfillInitialQuestion_propagatesInitialQuestionToOtherDraftProgramsUsingEnumerator,hxBackfillInitialQuestion_propagatesInitialQuestionToOtherActiveProgramsUsingEnumerator,hxBackfillInitialQuestion_whenPropagationFails_savesNothing, andhxSelectInitialQuestion_onLegacyEnumeratorBlock_warningSaysHowManyOtherProgramsUseIt. Not covered here: the browser test only checks the count, and propagation is checked by the unit tests and the Claude Code-driven Firefox session. Accessibility of the longer warning text was not checked.Issue(s) this completes
Fixes #13419