test(frontend): render the project list item's permission and description rules - #7415
Conversation
…tion rules The list item decides in its template what a viewer may touch and how much of a description to show, and none of it was rendered: the existing specs call the save and colour methods directly. Adds 8 tests. The one that matters most is the editable gating — a project the viewer only has READ on must not be shown the rename, description, share or delete controls, and that decision lives entirely in two *ngIf="editable" guards. Also covers the name/edit-input swap, the description starting collapsed and expanding on request, the trim() guard that keeps a whitespace-only description from rendering an empty expander, the character counter, the save icon appearing only once the text actually changed, and the creation-date format. MarkdownModule.forRoot() joins the TestBed: an expanded description renders a <markdown> element, which no existing test reached. No production file is touched.
Automated Reviewer SuggestionsBased on the
|
There was a problem hiding this comment.
Pull request overview
This PR strengthens frontend unit coverage for UserProjectListItemComponent by adding rendered-template tests that verify permission-gated controls (editable) and description rendering/editing behavior that previously wasn’t exercised by the spec suite.
Changes:
- Adds a new “rendered item” test suite that asserts UI gating for read-only vs write-access viewers (icons/actions visibility).
- Adds tests for description collapse/expand behavior, whitespace-only description suppression, character counting, and save-icon visibility.
- Updates the TestBed setup to include
MarkdownModule.forRoot()so expanded descriptions rendering<markdown>can be tested.
Suppressed comments (2)
frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.spec.ts:258
- This test only asserts the rename control and the existence of the actions container; it doesn’t verify the add/edit-description control (or that share/delete actions actually render). Adding a couple of targeted assertions will better pin the
editablegating behavior described in the PR.
it("offers the editing controls to a viewer with write access", () => {
const el = render({}, true);
expect(el.querySelector(".edit-name-icon")).not.toBeNull();
expect(el.querySelector("ul[nz-list-item-actions]")).not.toBeNull();
frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.spec.ts:300
- The character-count test verifies the initial count but doesn’t assert that the counter updates as the user types. Simulating an input change here would prevent regressions where the count becomes disconnected from the textarea’s current value.
const count = hostFixture.nativeElement.querySelector(".character-count")!;
expect(count.textContent?.trim()).toBe(`3/${component.MAX_PROJECT_DESCRIPTION_CHAR_COUNT}`);
});
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7415 +/- ##
============================================
+ Coverage 84.70% 84.98% +0.28%
Complexity 4150 4150
============================================
Files 1169 1169
Lines 46740 46740
Branches 5202 5202
============================================
+ Hits 39589 39723 +134
+ Misses 5433 5302 -131
+ Partials 1718 1715 -3
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ter checks The creation-date assertion looked for the 1970-01-01 substring, which neither pinned the yyyy-MM-dd HH:mm format nor held up outside the runner's timezone; it now matches the rendered line as a shape. The editable case asserted only the rename control, so it now also covers the add-description control and counts the two action buttons. The character counter was checked once at its initial value; it now types into the textarea and confirms the count follows the box rather than the saved description.
|
Both suppressed comments were worth acting on — fixed in e44b491. The One implementation note in case it saves someone time: The character counter. Right — it only saw the initial value, so a counter wired to |
The regex introduced in e44b491 fixed the format and timezone concerns but matched only the shape, so the assertion would have stayed green if the template rendered a different timestamp entirely. Formats the expected string with the same pipe and format literal instead, which pins the value too and still holds in any timezone. Rendering a different timestamp now fails; it did not before.
…apache#7418) ### What changes were proposed in this PR? Rebased onto `main` after apache#7415 landed on the same file, and reduced to what that PR left uncovered. apache#7415 pins what each template branch *looks like* by putting the component into the state directly; this PR pins the wiring that produces those states, so a control losing its handler fails here. Together they take `user-project-list-item.component.html` to 100% (110/110 statements, no uncovered branches — it was 77/110 after apache#7415). 8 new tests: - **colour panel** — the `[(colorPicker)]` / `(colorPickerSelect)` outputs, and the `cpExtraTemplate` menu, whose markup exists only while the picker is open: its Save action, and its Delete action in both states (disabled while the project has no colour, enabled and wired once one is set). None of this was previously rendered — it is the bulk of the gap apache#7410 describes. - **name** — the edit button opens the input, `keyup.enter` saves, `focusout` closes. - **description** — the expand/collapse controls, the edit button, `focusout` saving, and the suffix save icon closing the editor. - **actions** — the share button and the delete popconfirm's `nzOnConfirm`. - three class-level gaps: the `entry` getter's guard, `ngOnInit` adopting a stored colour, and `updateProjectColor` skipping the service when the colour is unchanged (class 63/67 -> 66/67). This block queries with `By.css` + `triggerEventHandler` rather than `querySelector`: `nzOnConfirm`, `keyup.enter` and `colorPickerSelect` are directive outputs, not DOM events, so a native dispatch cannot reach them. apache#7415's `MarkdownModule.forRoot()` is kept as-is. No production code was changed. Tests that apache#7415 already covers (the creation date, the `editable` gating, the read-only delete branch) were dropped from this PR rather than duplicated. One statement stays uncovered: the `if (!this.entry) throw` guard inside `saveProjectName`'s subscribe. The `entry` getter already throws when no entry was provided, so that branch cannot be reached — it is dead code rather than a coverage gap, and removing it felt out of scope for a test-only PR. ### Any related issues, documentation, discussions? Closes apache#7410. Builds on apache#7415 (apache#7412), which covers the same template from the rendering side. ### How was this PR tested? `ng test --watch=false --include src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.spec.ts` — 28 passed (20 existing + 8 new), run 3x for determinism. Coverage (`--coverage`) confirms `user-project-list-item.component.html` at 110/110 statements with no uncovered branches, and the class at 66/67. The failure path was verified by breaking an assertion (red, non-zero exit); eslint and prettier are clean. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context])
What changes were proposed in this PR?
UserProjectListItemComponentdecides in its template what a viewer may touch, and none of it was rendered — the existing specs call the save and colour methods directly.Adds 8 tests. The one that matters most is the
editablegating: a project the viewer only holds READ on must not be offered the rename, add-description, share or delete controls, and that decision lives entirely in two*ngIf="editable"guards plus one on the action list.Also covered: the name/edit-input swap, the description starting collapsed and expanding on request, the
trim()guard that stops a whitespace-only description rendering an empty expander, the character counter, the save icon appearing only once the text actually differs, and the creation-date format.Verified by mutation, all reverted (template diff empty):
trim()guardTwo things worth recording:
MarkdownModule.forRoot()joins the TestBed. An expanded description renders a<markdown>element, and no existing test reached that path, soMarkdownServicehad never been needed.descriptionCollapseddefaults to true. My first version of the collapse test asserted the opposite and failed, which also revealed that the whitespace test would have passed vacuously — collapsed hides the block regardless. It now expands first, so thetrim()guard is the only thing left doing the work.No production file is touched.
Any related issues, documentation, discussions?
Closes #7412
How was this PR tested?
8 new on top of the existing 10.
yarn format:cipasses.Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)