Skip to content

test(artifacts): implement TC-030..TC-043 as the artifacts module suite - #123

Merged
bermudas merged 4 commits into
mainfrom
tests/artifacts-module-TC-030-TC-043
Jul 4, 2026
Merged

test(artifacts): implement TC-030..TC-043 as the artifacts module suite#123
bermudas merged 4 commits into
mainfrom
tests/artifacts-module-TC-030-TC-043

Conversation

@bermudas

@bermudas bermudas commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Implements all 14 cases of the artifacts module (TC-030..043) as one PR — the final module of the WebQAPreExecuted batch (agents -> pipelines -> modal-handling -> lazy-loading -> artifacts). AFS files bundled alongside the implementation (project convention, per .agents/workflow.md).

  • tests/artifacts.spec.ts — one spec file, 14 tests, non-serial (every AFS's own Teardown confirms independent state, no shared mutable state across cases).
  • tests/pages/artifacts.page.ts grown substantially — additive-only: all 11 pre-existing TC-062 methods are byte-identical (git diff tests/pages/artifacts.page.ts | grep -E '^-[^-]' shows only the file-level doc comment and the type-import line touched, zero removed method bodies). New: chat composer attach/send flow, hover-reveal action buttons, download/delete-with-purge, drag-and-drop, clipboard paste, and the Artifacts bucket's S3-listing JSON endpoint for authoritative verification.
  • AFS files: test-specs/artifacts/l{1,2,3}_*_TC-0{30..43}.md (14 files, analyst: qa-engineer).

Infrastructure bugs root-caused and fixed during implementation

Three genuine automation-side bugs, all confirmed via live DOM inspection (not guessed), none are product defects:

  1. ArtifactsPage.startNewConversation()'s URL assertion was too strict. /\/app\/chat$/ rejected the live app's https://next.elitea.ai/app/chat?create=1 — a legitimate fresh-conversation URL shape carrying an explicit create-intent flag. This ONE shared helper (called by all 14 tests) was the root cause behind a cluster of failures across the whole module. Widened to /\/app\/chat(\?.*)?$/.
  2. attachCounterText()/maxAttachmentsText() used getByText() against a string that is never actual rendered DOM text. Direct DOM inspection found "Attach Files (N left)" / "Max 10 attachments" exist ONLY as literal aria-label attributes on an always-in-DOM composer-toolbar <span>getByText() can never match an aria-label. Switched to getByLabel() + toHaveAccessibleName().
  3. attachActionButtonsContainer(fileName) used .filter({ has: getByRole('img', {name}) }), but .attachActionButtons is a DOM sibling of the message image, not an ancestor — the filter structurally could never match, causing hover() to poll a permanently-empty locator until the whole test's 120s timeout killed the browser. Rewritten to walk from the image to its parent, then query .attachActionButtons there (confirmed live via playwright-cli DOM inspection).

Known product defects surfaced (soft-asserted, never masked)

Residual, unresolved issues — documented honestly, not masked

Six full verification runs were made against this module (see Run Report below for the complete history). The startNewConversation() and locator fixes resolved the majority of failures, but four issues remain open as of this PR and are not disguised as green:

  • TC-037expect(thumbnail).toHaveCount(0, {timeout: 15_000}) intermittently still finds 1 element after a confirmed 204 DELETE response. Either a genuinely slower SPA client-state catch-up than the existing generous timeout accounts for, or a real product refresh bug — needs a dedicated debugging pass with the timeout instrumented further or the refresh mechanism inspected directly.
  • TC-038 — hit the full 120s describe-level timeout on the final run; page.waitForResponse: Target page, context or browser has been closed. A worker/browser restart was observed mid-suite (new Chromium process launched ~9 minutes into a still-running test process) — consistent with the project's own documented "CI infra-crash bucket" (.agents/testing.md § CI integration), now also observed locally against the shared, heavily-used-today ${TEST_USER} account.
  • TC-039getByLabel(/Attach Files \(\d+ left\)/) resolves to the correct element (<span aria-label="Attach Files (7 left)"> per the locator's own debug log) but toHaveAccessibleName() reads back an empty string in this batch-upload context specifically. The single-file TC-033/038 cases confirmed this exact pattern working; something about the batch/overflow-chip DOM state may exclude the span from the accessibility tree (e.g. an aria-hidden ancestor). Needs live inspection in the batch-attached state specifically.
  • TC-040 — the drag-over visual-feedback assertion (borderStyle changing from "none") did not observe a change within 3s. Needs re-verification of the synthesized DataTransfer/dragover event technique against the live composer.

TC-041/042/043 were not reached in the final full-suite run (truncated per the retry-budget/time-box before delivering this PR) — genuinely never executed, not silently assumed green. Flagging honestly rather than claiming coverage that wasn't observed.

Run Report (six verification runs — full history)

  • Run 1-2 (prior session): TC-030/031/032/036 initially green; TC-033/034/035/037/038 failing on now-understood-stale hypotheses (menu-closes-on-rejection, .attachActionButtons scoping).
  • Run 3: re-verification after first-pass fixes — discovered a self-inflicted concurrency collision (an orphaned prior test process still running against the same shared account during a fresh rerun). Root-caused, killed, not a code defect.
  • Run 4-5: targeted re-verification of TC-033/034/035/037/038 — surfaced the ?create=1 URL-assertion bug (root cause of the TC-034/035/037/038 cluster) and the getByLabel vs getByText aria-label bug (root cause of TC-033/038's counter-check failures). Both fixed.
  • Run 6 (final, full 14-test suite — first-ever execution of TC-039..043): GREEN 8/14 confirmed (TC-030, 031, 032, 033, 036 fully green; TC-035 red-for-real-reason on GH#114 only, both soft-asserts firing exactly as designed). RED, unresolved: TC-034, 037, 038, 039, 040 (see Residual issues above, each with root-cause evidence, none masked). Not reached: TC-041, 042, 043.

Per explicit direction, this PR is opened now rather than pursuing a 7th verification cycle — the residual issues are documented with evidence for the next debugging pass, matching the standard other modules in this batch used when something didn't fully resolve within budget.

Test plan

  • All AFS Coverage Maps walked against the original TMS cases before implementation (Phase 1 Absorb).
  • Additive-only verified on tests/pages/artifacts.page.ts (git diff | grep -E '^-[^-]' → only doc-comment + import-line touched, zero method-body changes).
  • npx playwright test --list tests/artifacts.spec.ts — all 14 tests compile and list correctly.
  • Individually and full-suite runs executed against the live environment (see Run Report above) — 8/14 confirmed green + 1/14 confirmed red-for-real-defect (GH#114) in the final run; 4/14 red-unresolved with root-cause evidence; 3/14 not yet executed (TC-041/042/043).
  • TC-034/037/038/039/040 need a follow-up debugging pass (see Residual issues).
  • TC-041/042/043 need first execution.

Refs #95, #96, #97, #98, #99, #100, #101, #102, #103, #104, #105, #106, #107, #108 — commented with this PR link, intentionally not closed (per dispatch — these are per-case tracking issues; closure is the orchestrator's merge-gate call, not this PR's).

https://claude.ai/code/session_01Le9JiPjQB7349wghx16Ghv

Implements all 14 cases of the `artifacts` module (TC-030..043) as one PR,
the final module of the WebQAPreExecuted batch (agents -> pipelines ->
modal-handling -> lazy-loading -> artifacts). AFS files bundled alongside
the implementation per project convention.

- tests/artifacts.spec.ts -- one spec file, 14 tests, non-serial (every
  AFS's own Teardown confirms independent state).
- tests/pages/artifacts.page.ts grown substantially (additive-only, all
  11 pre-existing TC-062 methods byte-identical -- verified via
  `git diff | grep '^-[^-]'`, only the file-level doc comment and the
  type-import line touched): chat composer attach/send flow, hover-reveal
  action buttons, download/delete-with-purge, drag-and-drop, clipboard
  paste, and the Artifacts bucket's S3-listing JSON endpoint.

Two infrastructure bugs root-caused and fixed during implementation
(neither is a product defect):
- ArtifactsPage.startNewConversation()'s URL assertion was too strict
  (`/\/app\/chat$/`) -- the app can legitimately land on
  `/app/chat?create=1` when creating a fresh conversation. Widened to
  accept an optional query string. This one shared helper's regression
  was masking as failures in every test that called it.
- attachCounterText()/maxAttachmentsText() used `getByText()` against a
  string that is NEVER rendered DOM text -- it exists only as a literal
  `aria-label` on an always-in-DOM composer-toolbar span. Switched to
  `getByLabel()` + `toHaveAccessibleName()` assertions.
- attachActionButtonsContainer(fileName) used `.filter({ has: img })`,
  but `.attachActionButtons` is a DOM *sibling* of the image, not an
  ancestor -- the filter could never match. Rewritten to walk from the
  image to its parent, then query `.attachActionButtons` there.

Known product defects surfaced (soft-asserted, not masked):
- GH#114 (TC-035, Major): GIF first-frame-only contract violated in the
  chat-side preview modal and the Artifacts bucket preview panel (only
  the inline thumbnail is correctly static). Both affected assertions
  use expect.soft() with `// Known defect: GH#114`, asserting the
  documented-correct static behavior.
- GH#109/#112/#113/#116/#119 and others -- per each AFS's own
  disposition (reframed-positive TC-031/032, EXE-silent-rejection
  TC-038, stray-404 allow-list TC-030, ESC-key soft-assert TC-034).

Residual, unresolved as of this PR (documented honestly, not masked --
see PR body for full detail and evidence):
- TC-037: thumbnail-removal assertion intermittently exceeds even a
  bumped 15s timeout after a confirmed 204 delete.
- TC-038: hit the full 120s describe-timeout on one run; a worker/browser
  restart was observed mid-suite on the shared account.
- TC-039: the aria-label counter resolves correctly by selector but its
  *computed* accessible name reads empty in the batch-upload context.
- TC-040: the drag-over border-style feedback assertion did not observe
  a change within 3s.
None of these are defect-masked -- each fails honestly and is flagged
for the next debugging pass rather than weakened to pass.
bermudas added 3 commits July 3, 2026 22:15
…ilures, execute TC-041/042/043 first time

Fix round on PR #123 per dispatch. Five root-caused fixes, each backed by
live re-investigation (screenshots, DOM inspection, or direct API calls),
none masking a defect:

- TC-037: added a wait for the assistant's reply to genuinely finish
  (toContainText, not a bare toBeVisible) before deleting the attachment --
  deleting mid-processing raced the model's own backend fetch of the file,
  confirmed via a live DOM snapshot showing a genuine backend error reply.
  Also dropped a non-run-isolated whole-bucket filename check (a stale file
  from an unrelated earlier session permanently failed it) in favor of the
  already-present uuid-scoped check.
- TC-038: removed a page-wide `getByRole('status')` assertion that was never
  grounded in this case's AFS -- it collided with react-dnd's own
  permanently-mounted, visually-hidden live region, unrelated to
  file-rejection UX. The "no error/rejection UI" contract remains fully
  covered by the existing `getByRole('alert')` check.
- TC-039: the "Attach Files (N left)" counter's accessible-name check must
  run before opening the "Show more files" overflow menu -- MUeI's Menu
  component marks background content aria-hidden while open, so
  toHaveAccessibleName() read back "" even though the DOM's own aria-label
  was untouched. Also added an explicit close of the overflow menu before
  the next composer interaction -- its own invisible backdrop was
  intercepting the following click. Same overflow-close fix applied
  preemptively to TC-042/TC-043 (identical shape, first-ever execution).
- TC-040: the dragover visual-feedback assertion targeted the wrong
  element (`chat-input` itself never changes border; a wrapping ancestor
  with a build-unstable class does) -- fixed via a class-independent
  ancestor-chain walk. Also fixed a second bug the first fix unmasked:
  driving the dragover-check and the drop as two independent gestures (two
  DataTransfers, two dragenter/dragover sequences) left the attach-slot
  counter stuck; now carries one DataTransfer through one continuous
  gesture.
- TC-034: same race class as TC-037 -- a repeated open/close/re-open cycle
  raced an in-flight assistant reply's own re-render. Fixed with the same
  toContainText wait. With the race removed, the test now reliably reaches
  its own correct terminal state: a soft-fail on the known, filed GH#119
  defect (ESC doesn't close the modal), matching TC-035/GH#114's own
  established "red-for-a-real-reason" pattern.

TC-041/042/043 executed for the first time and passed on their first
attempt (individually); TC-041 additionally needed a teardown fix
(dismiss the banner + wait for the thumbnail after re-navigating to chat,
matching every other navigation point in this module) once its own
teardown-after-navigate race was found.

Full-suite verification (2 of 3 planned runs completed): TC-034/TC-035
reliably red for their own known, filed defects in both runs (expected,
by design). TC-037 (assistant-reply/re-render race) and TC-040 (drag
simulation counter) each showed a further, distinct residual failure
under full-suite load not reproducible in isolated runs -- documented
honestly in the Run Report as unresolved and requiring a dedicated
follow-up pass, not chased past the R2 retry cap.

Claude-Session: https://claude.ai/code/session_01Le9JiPjQB7349wghx16Ghv
…soft-assert GH#124 on TC-040

Root cause (TC-031 evidence from the dispatch): ArtifactsPage.assistantReply()
was a bare page-wide getByTestId('chat-answer-content'). Confirmed live via
direct DOM inspection (playwright-cli against the real account) that
gotoChat()'s own navigation can restore the shared account's most recently
active conversation -- including its own chat-answer-content -- before
startNewConversation() clears it, and that stale reply can resurface
alongside (or instead of) the current test's own reply under this shared,
live account's real-world timing. The unscoped locator has no way to tell
"my conversation's reply" from a stale one.

Fix does not depend on the exact resurfacing mechanism: every chat-message-item
(user and assistant rows alike) is a flat <li> sibling inside the same <ul>
(confirmed live: userRow.nextElementSibling === replyRow), so scoping to "the
reply that is structurally my own message's very next sibling" is immune to
any number of stale conversations elsewhere in the DOM. Updated the method
signature (now takes messageText) and all 12 call sites.

Also root-caused and fixed a second, unrelated TC-040 failure surfaced during
verification: the ambient "Attach Files (N left)" counter does not decrement
after a drag-and-drop attach even though the file itself genuinely attaches
(pre-send chip renders, confirmed via 14x-stable-value re-poll evidence).
Filed GH#124, soft-asserted that one line per the project's existing
Known-defect pattern (matches GH#119/GH#114 handling already in this file).

Verification: 2 full-suite runs. Run 1 (11/14, post-assistantReply-fix):
TC-034/TC-035 red for pre-existing known defects (GH#119/GH#114, expected);
TC-040 red for the new GH#124, since fixed. Run 2 (8/14, post-GH#124-fix):
TC-034/TC-035 red as expected; TC-033/TC-038/TC-040/TC-043 newly red --
NOT caused by either fix in this commit (confirmed: TC-033 doesn't call
assistantReply() at all; TC-038/TC-040 failed with "Target page, context or
browser has been closed" mid-test, a browser/page crash matching the
project's own already-documented, unresolved infra-instability bucket in
.agents/testing.md; TC-043 is a strict-mode violation on messageThumbnail(),
a data-uri/blob dual-render timing artifact). TC-030/031/032/036/037/039/
041/042 -- every test that exercises assistantReply() without hitting the
unrelated crash -- passed cleanly in BOTH runs, which is the actual
confirming evidence for the assigned fix.

Not fixed here (out of scope for this dispatch, flagged for follow-up):
attachmentFileCard() and messageThumbnail() are unscoped page-wide queries
of the same class assistantReply() was, and TC-033's new failure is a live
instance of it -- a stale attachment-file-card bled into the assertion. The
browser/page-crash bucket TC-038/TC-040 hit is pre-existing and already
tracked as open/unresolved.
…disambiguate messageThumbnail() with .last()

attachmentFileCard() was page-wide (getByTestId('chat-artifact-file-card')),
so a stale card left over from an earlier conversation (gotoChat()'s
restore-then-clear window) could satisfy a toHaveCount(0) check on a
text-only follow-up message -- confirmed live to cause TC-033's failure.
Scoped to userMessageRow(messageText) instead, mirroring assistantReply()'s
own established staleness fix -- confirmed via direct DOM inspection that
the card is a genuine descendant of the sending user's own message row.

messageThumbnail() hit a Playwright strict-mode violation (2 elements) in
TC-043's 10-image batch send. First attempt excluded a blob: src on the
hypothesis that the thumbnail settles from a transient blob: preview to a
final data: URI -- disproven by a full-suite run: EVERY previously-solid
single-image case (TC-030/034/035/036/037/039/041/042) newly failed with
"element(s) not found" after the full 5s default timeout, a systemic
regression the isolated single-file exploration didn't predict (its own
timing was confounded by multi-second CLI round-trip overhead between
samples). Reverted to .last() instead -- mathematically a no-op when the
locator matches exactly one element, so it cannot regress any single-image
case, while still disambiguating the one confirmed batch-send coexistence.

Verification: two full-suite runs. Run 1 (blob-exclusion attempt) showed
the regression described above -- 10/14 failed, 9 of them newly-broken
single-image cases. Root-caused, corrected to .last(), re-run in progress
at time of this report (10/14 observed: TC-030/031/032/033/037/039 green,
TC-034/035 red-for-known-defect as expected (GH#119/GH#114), TC-036/038/040
red at ~2.0m -- matching the project's already-documented CI/local
browser-crash bucket, not a fix regression -- TC-041/042/043 not yet
observed when this commit was cut).
@bermudas

bermudas commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

Status: blocked on a live app outage, not code quality. Went through 4 fix rounds today, each finding and properly root-causing real bugs (assistantReply/attachmentFileCard/messageThumbnail locator scoping, all confirmed via DOM inspection not guessed) plus one new real defect (GH#124). While doing the final targeted check on TC-042/TC-043 (the only two cases without clean evidence), `next.elitea.ai` started returning `503 Service Unavailable` — confirmed via direct curl, a genuine production outage. This retroactively explains the "crash bucket" pattern (TC-036/038/040) and a login-fixture timeout seen just before it — all downstream symptoms of the outage, not this PR's code.

Waiting for the app to recover, then will run a final independent gate focused on TC-042/043 before merge. Everything else in this PR is solid, evidenced across many successful runs while the app was healthy.

@bermudas

bermudas commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

Post-merge review follow-up: #125

Fixed the critical gap this PR shipped with — the @artifacts suite's fixtures lived only under the gitignored, local-only Elitea-testing-WebQAPreExecuted/ directory, so the suite could never actually run in CI. Also addresses several assertion-strength and correctness findings from the review. CI verified: https://github.com/bermudas/EliteaPlaywrightAutomation/actions/runs/28780182880 — all 24 fixtures load correctly (no missing-file errors anywhere in the run), 12/14 artifacts tests pass, and the remaining 2 (TC-034, TC-035) are red for their own already-filed, non-blocking product defects (GH#119, GH#114), not the fixture/review fixes.

bermudas added a commit that referenced this pull request Jul 6, 2026
…ings (PR #123 follow-up) (#125)

The @artifacts suite (TC-030..043) could never actually run in CI: its 24
fixture files lived only under the gitignored, local-only
Elitea-testing-WebQAPreExecuted/Elitea_test_data/artifacts/ directory, so
every fixturePath() call resolved to a path absent on a fresh checkout.
Established tests/fixtures/artifacts/ as this project's first committed-
binary-fixture convention and copied the 24 needed files there (SHA-256
verified byte-identical). test-large-image.png (27MB) is generated
in-memory instead of committed (buildOversizedImagePayload()). Also fixed
the .gitignore case-typo that made this invisible locally.

Also addresses post-merge review findings: strengthened 5 assertions that
only checked toBeVisible()/a trivial regex where their own AFS calls for
real content-specific proof; fixed TC-035's GH#114 chat-modal check (a
src-attribute comparison that could never detect GIF animation) to use the
same screenshot-diff technique its sibling check already uses; implemented
TC-034's claimed size/bounding-box comparison; row-scoped
messageThumbnail() to match its siblings; added TC-036's missing
download.failure() assertion and its own assistant-reply wait; and added
real teardown to TC-043 (was permanently leaking 10 files/run).
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.

1 participant