Project

General

Profile

Feature #40 » qa40-parent-child-tickets-proof.txt

Redmine Admin, 06/11/2026 06:50 PM

 
================================================================================
QA PROOF — Redmine #40 "Implement parent-child ticket relationships in ticketing module"
Project: Pipeliner Client (id 9) | Tracker: Feature | Status entering QA: QA
Branch: feat/40-parent-child-tickets | PR #22 -> dev
HEAD: 0a59ec3 fix(ticketing): address #40 review — dedup subtitle, derive sort order, ICU plural, parent fallback
Date: 2026-06-11 | Agent: manual QA (#25)
Scope: Frontend-only (Next.js 16, Atomic Design, next-intl). No backend change.
================================================================================

SMOKE TEST STRATEGY
-------------------
A live authenticated screenshot of the ticketing module was attempted via the
gstack browse binary. The pipeliner login page (https://pipeliner.omdev.tech/login)
was reachable (HTTP 200) but rendered blank/unauthenticated (no test credentials,
flapping auth, no guaranteed project carrying parent-child tickets). Per the QA
fallback policy, the component/render test suites that exercise the feature are
used as the smoke proof. These tests render the real TaskStageGroup, TaskRow,
TaskDetailPane and ticketing page components and assert the exact user-visible
behaviour described by each acceptance criterion.

FULL FRONTEND GATE (project CLAUDE.md)
--------------------------------------
npx vitest run -> 83 files, 424 tests PASSED
npx tsc --noEmit -> exit 0 (clean)
npx eslint . -> exit 0 (clean)
npm run build -> exit 0 (success; all routes emitted incl. /projects/[id]/ticketing)
The single "ENVIRONMENT_FALLBACK" log on /admin prerender is the
documented pre-existing, non-fatal log — build still completes.

AC-RELEVANT SUITES (35 tests, all PASSED)
-----------------------------------------
src/features/tasks/hierarchy.test.ts ............ 9 tests
src/components/molecules/TaskStageGroup.test.tsx 15 tests
src/components/organisms/TaskDetailPane.test.tsx . 9 tests
app/projects/[id]/ticketing/page.test.tsx ....... 2 tests

ACCEPTANCE CRITERIA -> EVIDENCE
--------------------------------
[OK] List View: parent shows expandable arrow; expanding reveals indented subtickets (title/status/preview)
- TaskStageGroup > shows an expand toggle on a parent with subtickets
- TaskStageGroup > hides subtickets until the parent is expanded
- TaskStageGroup > reveals indented subtickets (with status preview, not a duplicate subject) when expanded

[OK] List View: clicking a subticket opens its detail view with a "Back to parent" button
- TaskStageGroup > opens a subticket's detail via onSelect when its row is clicked
- TaskStageGroup > calls onToggleExpand (not onSelect) when the chevron is clicked
- Ticketing page > shows a Back-to-parent affordance for a deep-linked subticket whose parent isn't on the loaded board (#40)

[OK] Detail View: clickable "Parent Ticket" section when a parent exists, navigates to parent
- TaskDetailPane > shows a clickable Parent ticket section that navigates to the parent
- hierarchy > parentSummary resolves the parent TaskSummary when present
- hierarchy > parentSummary returns null for a top-level task and for an orphan parent_id

[OK] Detail View: collapsible "Subtickets" section (title/status/preview) when subtickets exist
- TaskDetailPane > lists subtickets in a collapsible section and opens one on click
- TaskDetailPane > collapses the subtickets section via its toggle
- TaskDetailPane > renders nothing parent/subtickets-related when there are none

[OK] Detail View: clicking a subticket replaces the view + shows "Back to parent"
- TaskDetailPane > lists subtickets in a collapsible section and opens one on click
- Ticketing page > shows a Back-to-parent affordance for a deep-linked subticket (#40)

[OK] Sorting: subtickets sorted by status (e.g. "In Development" first)
- hierarchy > sortByStatus orders by pipeline rank with in_development first
- hierarchy > sortByStatus is stable for equal statuses (preserves input order)

[OK] Nesting: only one level of subtickets shown (no subtickets of subtickets)
- hierarchy > enforces one level of nesting: a child's own children are not nested
- TaskStageGroup > renders a child that is itself a parent with NO expand chevron at depth 1 (one-level guarantee enforced at render)

[OK] Visual Design: expandable arrows, status tags, sections match existing styling
- TaskStageGroup > renders rows with an accessible name of subject + status
- TaskStageGroup > derives the stage label from the catalog (status_id), not server status_name
- TaskStageGroup > falls back to status_name for an unknown status id
- Reuses existing StatusPill/Badge (no new status color logic per spec_ref)

[OK] Navigation: "Back to parent" returns user to the parent ticket's detail view
- Ticketing page > shows a Back-to-parent affordance for a deep-linked subticket whose parent isn't on the loaded board (#40)
- TaskDetailPane > shows a clickable Parent ticket section that navigates to the parent

EDGE CASES / ROBUSTNESS (covered by tests)
------------------------------------------
[OK] Orphan parent_id (parent not on the loaded board) renders as a normal top-level row (no crash)
- hierarchy > treats an orphan parent_id as a top-level row
- TaskStageGroup > renders an orphan parent_id as a normal top-level row
[OK] Dedup: a task appearing in two stage groups produces no duplicate child rows
- hierarchy > dedups a task that appears in two stage groups
[OK] A real child is NOT rendered as a standalone row in its own stage
- TaskStageGroup > does NOT render a child as a standalone row in its own stage

VERBATIM TEST RESULTS (ANSI-stripped; no secrets)
-------------------------------------------------
✓ src/features/tasks/hierarchy.test.ts > buildTaskHierarchy > groups children under their parent across stage groups
✓ src/features/tasks/hierarchy.test.ts > buildTaskHierarchy > treats an orphan parent_id (parent not in the board) as a top-level row
✓ src/features/tasks/hierarchy.test.ts > buildTaskHierarchy > marks a real child as NOT a top-level row
✓ src/features/tasks/hierarchy.test.ts > buildTaskHierarchy > dedups a task that appears in two stage groups (no duplicate child rows)
✓ src/features/tasks/hierarchy.test.ts > buildTaskHierarchy > enforces one level of nesting: a child's own children are not nested
✓ src/features/tasks/hierarchy.test.ts > sortByStatus > orders by pipeline rank with in_development first
✓ src/features/tasks/hierarchy.test.ts > sortByStatus > is stable for equal statuses (preserves input order)
✓ src/features/tasks/hierarchy.test.ts > parentSummary > resolves the parent TaskSummary when present
✓ src/features/tasks/hierarchy.test.ts > parentSummary > returns null for a top-level task and for an orphan parent_id
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > renders the stage header, count, and a labelled group
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > renders rows with an accessible name of subject + status
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > marks the selected row with aria-current
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > fires onSelect with the issue id when a row is clicked
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > renders the empty-stage line for a stage with no tasks
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > derives the stage label from the catalog (status_id), not the server status_name
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > falls back to status_name for an unknown status id
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > shows an expand toggle on a parent with subtickets
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > hides subtickets until the parent is expanded
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > reveals indented subtickets (with status preview, not a duplicate subject) when expanded
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > does NOT render a child as a standalone row in its own stage
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > calls onToggleExpand (not onSelect) when the chevron is clicked
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > opens a subticket's detail via onSelect when its row is clicked
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > renders a child that is itself a parent with NO expand chevron at depth 1 (one-level guarantee enforced at render)
✓ src/components/molecules/TaskStageGroup.test.tsx > TaskStageGroup > parent/child (#40) > renders an orphan parent_id as a normal top-level row
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — read-only status (no client transitions) > renders the status as a read-only Badge with no transition control
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — gated edit / delete / attachments > shows Edit + Delete when mutable (status 7) and reveals the edit form
✓ app/projects/[id]/ticketing/page.test.tsx > Ticketing workspace (/projects/[id]/ticketing) > renders the assistant composer and the project context (active module tab + name)
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — gated edit / delete / attachments > saves an edit via PATCH
✓ app/projects/[id]/ticketing/page.test.tsx > Ticketing workspace (/projects/[id]/ticketing) > shows a Back-to-parent affordance for a deep-linked subticket whose parent isn't on the loaded board (#40)
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — gated edit / delete / attachments > deletes via the confirm dialog and navigates back
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — gated edit / delete / attachments > renders nothing parent/subtickets-related when there are none
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — gated edit / delete / attachments > locks edit/delete/add and shows lock notes when not mutable (status 9)
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — parent/child relationships (#40) > shows a clickable Parent ticket section that navigates to the parent
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — parent/child relationships (#40) > lists subtickets in a collapsible section and opens one on click
✓ src/components/organisms/TaskDetailPane.test.tsx > TaskDetailPane — parent/child relationships (#40) > collapses the subtickets section via its toggle
Test Files 4 passed (4)
Tests 35 passed (35)

================================================================================
VERDICT: PASS — all 9 acceptance criteria OK. Ticket stays in QA (preprod
promotion is a separate agent's responsibility).
SECRETS: none included (no env values, tokens, DB URLs, cookies, or bearer tokens).
================================================================================
(2-2/2)