Skip to content

UN-3797 [MISC] Add a browser-based frontend layout test tier#2196

Open
athul-rs wants to merge 4 commits into
mainfrom
UN-3741-fe-layout-tests
Open

UN-3797 [MISC] Add a browser-based frontend layout test tier#2196
athul-rs wants to merge 4 commits into
mainfrom
UN-3741-fe-layout-tests

Conversation

@athul-rs

@athul-rs athul-rs commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Stacked on #2193. This branch contains that PR's two commits as a dependency —
the layout suite is red against main without them, which is the point. Merge #2193
first; this branch then gets rebased onto main before merging, leaving only the
test commit.

What

Adds a second vitest project that runs the UI in real Chromium and asserts the
geometric contracts of the shared ListView row: that its columns line up
across rows, that the metadata cluster stays inside its own box and clear of the
action icons, that rows never overflow, and that an over-long owner label
truncates instead of displacing the layout.

Also wires the frontend tests into ci-test.yaml as a frontend job. This is
the first time any vitest test runs in CI — the existing ones have only ever run
on dev machines.

Why

Two CSS regressions reached staging in the last week and were both caught by
human QA rather than by CI:

  1. Row columns were switched from fixed widths to flex. The row's left column
    grows to absorb slack, so the metadata cluster is anchored on the row's right
    edge — which means a content-sized owner column starts at a different x on
    every row. Measured drift between a row owned by "Me" and one owned by a long
    email address: 181–222px.
  2. The follow-up pinned those columns to fixed widths but left their wrapper
    shrinkable. A shrinkable box with rigid children lets the children spill out
    of it and print on top of the action icons: +26px at 1440, +66px at 1024.

Neither is visible when reading the CSS diff, and neither is catchable by the
existing test setup — vitest.config.mjs runs on happy-dom, which has no layout
engine, so every getBoundingClientRect() returns zeros. No amount of unit
testing in that environment can see either bug.

How

vitest.config.mjs now defines two projects:

  • unit — happy-dom, the existing tests, unchanged.
  • layout — real Chromium via @vitest/browser + playwright, running only
    *.layout.test.jsx.

src/test-utils/layout/ holds component-agnostic helpers: expectAlignedX,
expectContained, expectNoHorizontalOverlap, expectNoHorizontalOverflow,
expectTruncated, and a mountAtWidth harness that renders a component at a
requested width and waits for fonts and antd's runtime styles to settle.

Every assertion is relational, never absolute. Nothing asserts a pixel
position, a padding, a gap, or a CSS property value — a redesign that moves the
columns keeps passing, and only a column that stops being a column fails. This
is deliberate: absolute-pixel layout tests get deleted within a month of the
first innocuous design tweak.

The production build's optionalPluginImports plugin moved to
frontend/vite-plugins/ so vitest.config.mjs can reuse it. Tests need the
same treatment the build gives absent cloud plugins — importantly, it makes a
missing plugin throw, which is the signal pluginRegistry and ~30 other
runtime fallbacks match on. A lookalike stub that resolved to an empty module
instead would silently push every one of those fallbacks down the wrong branch.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

No product code changes — the diff is tests, test config, CI, and one
build-config extraction.

The extraction is the only line of risk: optionalPluginImports moved out of
vite.config.js into vite-plugins/optional-plugin-imports.js and is imported
back. The function body is byte-identical apart from added braces on three
single-line returns. npx vite build was run before and after and succeeds.

CI risk is one new job. It is gated on frontend/**, so non-frontend PRs skip
it (skip reads as pass, per the existing filter convention), and it runs in
parallel with the python tiers — no added wall-clock on a PR that also runs e2e.

Database Migrations

None.

Env Config

None.

Relevant Docs

None.

Related Issues or PRs

Depends on #2193 (the CSS fix these tests hold in place, UN-3741). Follows #2182.
Tracked under UN-3797, a sub-task of UN-3635 alongside the python rig work.

Dependencies Versions

Two new frontend devDependencies: @vitest/browser@3.2.6 (matches the pinned
vitest) and playwright@^1.61.1. CI caches ~/.cache/ms-playwright and installs
only Chromium.

bun.lock is regenerated. @vitest/browser is pinned exactly rather than with
a caret: its peer dependency on vitest is an exact version, so a caret would
resolve to 3.2.7 against the repo's vitest 3.2.6 and warn on every install.

Notes on Testing

The suite was validated against three states of the CSS it protects, to prove it
fails for the right reason rather than merely passing:

CSS state Result
main (drift bug live) 7 fail — "expected one straight column, got 180.5px of drift"
main + first fix commit (columns pinned, wrapper still shrinkable) 1 fail — "child escapes its container by 89.7px" at 800px
main + complete #2193 13 pass

Each bug is caught by the assertion aimed at it, and neither masks the other.

Harness fidelity was checked against a deployed build in a dev namespace: at a
900px list the harness reproduces owner 240px / updated 170px / meta 430px / 24px clearance to the actions — the same numbers measured live in the browser
on the running app.

Runtime: 29 tests (16 existing unit + 13 layout) in ~3s locally. The CI job is
~2–4 minutes, dominated by dependency install and the Chromium fetch on a cold
cache.

Widths tested are 800 / 1100 / 1400 — the min and max .list-view-wrapper
itself declares, plus the midpoint. The harness asserts it actually achieved the
requested width, so a clamp can't silently collapse three cases into one.

What this does not cover

  • Colors, font weights, icon swaps, spacing — anything non-geometric.
  • Page composition: components are mounted in a harness, not inside the app
    shell, so a page-level stylesheet override or a sidebar squeezing the content
    area is still QA's to catch.
  • src/plugins/** (gitignored cloud content, absent in CI) and non-Chromium
    browsers.
  • Any component other than ListView. The helpers are reusable; add a
    *.layout.test.jsx beside a component when it earns one.

Screenshots

Failure output is the interesting artifact here — an assertion reports the
measured drift and the offending rows, and vitest writes a screenshot of the
failing render, uploaded as a CI artifact:

AssertionError: owner column: expected one straight column, got 180.5px of drift
  [0] 1180px  Owned By:Me
  [1] 1000px  Owned By:a-really-long-service-account-n…
  [2] 1015px  Owned By:someone.else@example.com
  [3] 1160px  Owned By:Me +2

Checklist

I have read and understood the Contribution Guidelines.

athul-rs and others added 3 commits July 22, 2026 12:17
The meta cluster is anchored on the row's right edge, so content-sized
columns shifted "Owned By" sideways row by row depending on the length of
the owner label. Pin the owner and updated columns; bold the title to match
the agentic Prompt Studio project list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The meta wrapper was still shrinkable, so once its columns became fixed
they overflowed the box and overlapped the action icons (26px at 1440,
66px at 1024). Pin the wrapper too and let the left column, which
ellipsizes, absorb the shortfall.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…TS (#2188)

* UN-3636 [FEAT] Merge overlay manifests via UNSTRACT_RIG_EXTRA_MANIFESTS

load_groups() now merges extra group manifests listed in the env var
(os.pathsep-separated, REPO_ROOT-relative) onto the base tests/groups.yaml
before validation, so cross-manifest depends_on and the platform-gate
invariant are checked over the union. Name collisions are an error.

Lets a downstream repo (the cloud build) contribute its own test groups by
copying a groups.cloud.yaml into the merged tree, without editing the OSS
manifest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Skip org lookup when no org in context

UserContext.get_organization() ran Organization.objects.get() even with no
org id in StateStore (import time, or management commands with no request),
catching only DoesNotExist/ProgrammingError — a DB-less/unmigrated setup hit
an uncaught OperationalError. Short-circuit when there's no org id: no query,
so serializers/managers that reference org-scoped querysets at class-def can
be imported during DB-free test collection.

* UN-3636 [FIX] Scope overlay manifests to the default manifest

Address review feedback on the UNSTRACT_RIG_EXTRA_MANIFESTS overlay:

- Overlays now apply only when loading the default manifest, so an
  explicit `load_groups(path)` (test fixture, ad-hoc manifest) can no
  longer absorb a downstream repo's ambient overlay.
- `_merge_manifest` returns the merged defaults so an overlay can rename
  `platform_gate_group` instead of having it silently ignored.
- A bad path in the env var raises a ValueError naming the variable
  rather than a bare FileNotFoundError/IsADirectoryError.
- Extract `_load_manifest_dict` to single-source manifest parsing and its
  error message.
- Tests: drive the real default-manifest path; cover overlay isolation,
  overlay defaults, malformed overlay, and a missing overlay path.
- Pin the truthy branch of UserContext.get_organization so inverting the
  guard can't pass unnoticed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Stop re-running cross-tier deps in every tier leg

`--tier X` expanded each selected group's `depends_on` transitively, with
no tier bound. `integration-workflow-execution` and `e2e-smoke` both
declare `depends_on: [unit-sdk1, unit-workers]`, so those two unit groups
ran again in the integration leg and a third time in the e2e leg.

Tiers run as separate CI legs and the unit leg already covers them, so
dep expansion is now bounded to the requested tier. Explicitly named
groups are never dropped, and intra-tier deps (e2e-smoke -> e2e-login)
still expand and order as before. Unrun deps do not weaken gating:
`blocked_by` intersects with groups that failed in the same run.

Measured on the last main run: ~88s of unit-workers and ~48s of
unit-sdk1 re-executed per run. On the cloud CI runner the same
duplication costs ~350s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [MISC] Drop the ENVIRONMENT gate on the LLM mock (#2191)

* UN-3636 [FIX] Drop the ENVIRONMENT gate on the LLM mock

It did not defend the case it was added for. The threat was a worker env block
copied out of the test overlay into a real deployment, but the gate was written
into that same block, so a copy carries it. Base compose also sets
ENVIRONMENT=development on both workers that run the injection, so any
deployment derived from it satisfied the gate regardless. That left one real
case -- the mock var set alone somewhere that sets no ENVIRONMENT at all --
which holds by accident rather than design, in exchange for depending on a
variable nothing else in the codebase reads.

What actually guards the hatch is unchanged: it is off unless someone sets
UNSTRACT_LLM_MOCK_RESPONSE, and it warns once per process while active. Making
mocked spend distinguishable downstream is the defence worth having, and it
belongs on the usage record rather than in a config check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3636 [MISC] Drop the unread ENVIRONMENT variable from compose

Nothing reads it: no service, worker, frontend or plugin looks the variable
up, and the one consumer it ever had — the LLM mock gate — was removed in the
previous commit. Dropping it everywhere keeps a dead knob from looking
load-bearing to the next reader.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* UN-3636 [MISC] Tighten code comments

Drop lines that restate the code, trim session-specific detail, and merge
comments that duplicated each other across a module and its test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

* UN-3636 [FIX] Gate overlay merging on an omitted path, not its value

`load_groups(DEFAULT_MANIFEST)` merged overlays even though the caller named a
manifest explicitly, because the check compared path values. Path equality is
also spelling-sensitive, so the same file relative and absolute behaved
differently. Key on `path is None` instead: an explicit path loads exactly what
it names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3

* UN-3636 [FIX] Stop ide_prompt_complete tests from hitting the network

TestIdePromptComplete drives the full success path, which reaches
client_plugin_registry.get_client_plugin("subscription_usage"). In OSS no
such plugin is installed, so the lookup returns None instantly. In a tree
with the cloud plugins copied in, it resolves to a real plugin that POSTs
to the backend; with no backend running the call only fails after a
multi-second connect timeout, and _track_subscription_usage swallows the
error so the tests still pass. That accounted for ~190s of the cloud
unit-workers run.

The file already declared _PATCH_GET_PLUGIN but never applied it outside
the dedicated subscription-usage classes. Apply it as a class-scoped
fixture so the lookup is pinned to the OSS answer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The pull request adds browser-based frontend layout testing and CI gating, extracts optional plugin resolution, guards organization lookups without context, supports rig manifest overlays and tier-aware selection, removes environment-based LLM mock restrictions, and isolates plugin-dependent callback tests.

Changes

Frontend validation

Layer / File(s) Summary
Frontend test stack
frontend/package.json, frontend/vitest.config.mjs, frontend/src/test-utils/layout/*
Splits unit and Playwright layout tests and adds deterministic mounting, geometry assertions, animation suppression, and browser dependencies.
Optional plugin resolution
frontend/vite-plugins/*, frontend/vite.config.js, frontend/vitest.config.mjs
Extracts optional plugin import handling and registers it for production and Vitest configurations.
ListView layout contract
frontend/src/components/widgets/list-view/*
Updates metadata sizing and tests alignment, containment, truncation, overlap, and overflow across widths.
Frontend CI gate
.github/workflows/ci-test.yaml, .gitignore
Adds frontend path filtering, test execution, Chromium setup, failure screenshots, reporting, and screenshot exclusion.

Runtime guards and test isolation

Layer / File(s) Summary
Organization lookup guard
backend/utils/user_context.py, backend/utils/tests/test_user_context.py
Missing organization identifiers now return None without an ORM lookup, with regression coverage.
LLM mock activation
unstract/sdk1/src/unstract/sdk1/llm.py, unstract/sdk1/tests/test_mock_response.py, tests/README.md
Mock injection no longer depends on ENVIRONMENT; refusal tests and related documentation are removed or updated.
Compose and callback test isolation
docker/docker-compose.yaml, tests/compose/docker-compose.test.yaml, workers/tests/test_ide_callback.py
Removes explicit environment assignments from compose services and disables client plugin lookup in callback tests.

Rig manifest and selection

Layer / File(s) Summary
Manifest overlay loading
tests/rig/groups.py, tests/rig/tests/test_groups.py
Merges environment-specified manifests for default loads, including defaults, dependency resolution, collision checks, and validation tests.
Tier-aware dependency selection
tests/rig/selection.py, tests/rig/tests/test_selection.py
Retains explicit selections while excluding dependency-expanded groups from other tiers when a tier filter is active.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant PathFilters
  participant FrontendJob
  participant Vitest
  participant Playwright
  participant ReportJob
  GitHubActions->>PathFilters: evaluate frontend/** changes
  PathFilters-->>GitHubActions: frontend relevance output
  GitHubActions->>FrontendJob: run frontend tests
  FrontendJob->>Vitest: run unit suite
  FrontendJob->>Playwright: run Chromium layout suite
  Playwright-->>FrontendJob: upload failure screenshots
  FrontendJob-->>ReportJob: frontend result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: adding a browser-based frontend layout test tier.
Description check ✅ Passed The description follows the required template and fills the major sections with relevant details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch UN-3741-fe-layout-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/workflows/ci-test.yaml Fixed
Comment thread .github/workflows/ci-test.yaml Fixed
Comment thread .github/workflows/ci-test.yaml Fixed
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a browser-based layout test tier for the ListView component using vitest's Playwright/Chromium project alongside the existing happy-dom unit project, and wires both into CI as a new frontend job. It also ships four orthogonal housekeeping changes: extracting optionalPluginImports into its own file so the test config can share it, removing the two-condition ENVIRONMENT gate from the LLM mock hatch (with thorough documentation of why), adding a short-circuit guard in UserContext.get_organization, and supporting overlay manifests in the test rig.

  • Layout test suite (ListView.layout.test.jsx, test-utils/layout/): 13 relational assertions at 800/1100/1400 px verify column alignment, containment, non-overlap, overflow, and truncation — validated against three CSS states to prove the right assertions fail for the right regressions.
  • CI wiring (.github/workflows/ci-test.yaml): New frontend job caches the Playwright browser, runs unit and layout tiers separately, uploads failure screenshots, and participates in the branch-protection report job. One action (actions/cache@v5) is not pinned to a commit SHA, inconsistent with every other action in the file.
  • Mock-response simplification: The ENVIRONMENT variable is removed from all compose services and its guard in _inject_mock_response is dropped; UNSTRACT_LLM_MOCK_RESPONSE being set is now the sole condition, with the rationale documented in tests/README.md.

Confidence Score: 4/5

Safe to merge after pinning actions/cache to a commit SHA; no product code is changed and all other changes are well-tested.

The change is tests, CI scaffolding, and one build-config extraction — no product logic is altered. The only actionable finding is actions/cache@v5 using a floating tag while every other action in the same job is pinned to a commit SHA, which is inconsistent with the repo's established convention.

.github/workflows/ci-test.yaml — the actions/cache@v5 step should be pinned to a commit SHA to match every other action in the file.

Important Files Changed

Filename Overview
.github/workflows/ci-test.yaml New frontend job wires vitest unit + layout tiers into CI; all actions except actions/cache are pinned to commit SHAs, inconsistent with the repo's pinning convention.
frontend/vitest.config.mjs Splits vitest into unit (happy-dom) and layout (real Chromium/playwright) projects; reuses optionalPluginImports so cloud-plugin stubs throw on import — the correct signal for runtime fallbacks.
frontend/src/test-utils/layout/mount.jsx Width-convergence harness with proper onTestFinished cleanup; iterative proportional correction handles percentage-based sizing, and the post-loop clamp assertion prevents false-broad coverage.
frontend/src/test-utils/layout/assertions.js Five relational geometry assertions using a shared 1.5px epsilon; all include coordinates and text content in failure messages for fast diagnosis.
frontend/src/components/widgets/list-view/ListView.layout.test.jsx 13 relational layout tests covering column alignment, containment, overlap, overflow and truncation across 800/1100/1400px widths; fixture guard prevents alignment assertions from silently passing on identical labels.
frontend/vite-plugins/optional-plugin-imports.js Byte-identical extraction of optionalPluginImports from vite.config.js into its own module so vitest.config.mjs can share it.
unstract/sdk1/src/unstract/sdk1/llm.py Removes the two-condition ENVIRONMENT gate for mock responses; UNSTRACT_LLM_MOCK_RESPONSE being set is now the only condition, with documented rationale.
docker/docker-compose.yaml Removes ENVIRONMENT=development from all 11 services now that no code reads the variable.
tests/rig/groups.py Adds overlay manifest support via UNSTRACT_RIG_EXTRA_MANIFESTS; overlays are merged before validation so cross-manifest depends_on is checked over the full union; name collisions are rejected.
tests/rig/selection.py Adds tier-scoped dep expansion: dep-injected groups from other tiers are filtered out while explicit positional requests always survive.
backend/utils/user_context.py Adds an early-return guard for falsy organization_id to avoid a DB query on DB-less/unmigrated setups; pinned by new regression tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Push / PR] --> B[changes job\npaths-filter]
    B -->|frontend/**| C[frontend job]
    B -->|backend/**| D[test job\nPython unit/integration]
    B -->|any non-docs| E[e2e job]

    C --> C1[bun install --frozen-lockfile]
    C1 --> C2[Cache Playwright browsers]
    C2 --> C3[playwright install chromium --with-deps]
    C3 --> C4[bun run test:unit\nvitest · happy-dom]
    C4 --> C5[bun run test:layout\nvitest · real Chromium]
    C5 -->|on failure| C6[Upload __screenshots__]

    D --> F[report job]
    E --> F
    C --> F
    F --> G{All pass/skip?}
    G -->|yes| H[Branch protection: green]
    G -->|no| I[Branch protection: red]
Loading

Reviews (2): Last reviewed commit: "UN-3797 [MISC] Add a browser-based front..." | Re-trigger Greptile

Comment thread frontend/src/test-utils/layout/mount.jsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci-test.yaml:
- Around line 161-181: Regenerate and commit frontend/bun.lock so it includes
the declared `@vitest/browser` and playwright versions, then update the Install
dependencies step to run bun install --frozen-lockfile. Keep the existing
Playwright cache key based on the updated lockfile.

In `@tests/compose/docker-compose.test.yaml`:
- Around line 20-27: Update the UNSTRACT_LLM_MOCK_RESPONSE environment entries
for both worker-executor-v2 and worker-file-processing-v2 to fail closed when
the variable is absent, using MOCK_LLM_OK as the default or enforcing startup
failure for an empty value. Preserve the guarantee that execute-path E2E runs
cannot reach a real provider.

In `@tests/rig/groups.py`:
- Around line 171-178: Update _load_manifest_dict to validate that raw["groups"]
is a mapping in addition to validating the top-level manifest and groups key.
Raise the existing manifest-specific ValueError for non-mapping groups values,
preserving consistent validation for base and overlay manifests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f9a127f-ee0e-475d-87c2-a38cac16d795

📥 Commits

Reviewing files that changed from the base of the PR and between a03fcf8 and cd8ef15.

📒 Files selected for processing (23)
  • .github/workflows/ci-test.yaml
  • .gitignore
  • backend/utils/tests/test_user_context.py
  • backend/utils/user_context.py
  • docker/docker-compose.yaml
  • frontend/package.json
  • frontend/src/components/widgets/list-view/ListView.css
  • frontend/src/components/widgets/list-view/ListView.layout.test.jsx
  • frontend/src/test-utils/layout/assertions.js
  • frontend/src/test-utils/layout/mount.jsx
  • frontend/src/test-utils/layout/setup.js
  • frontend/vite-plugins/optional-plugin-imports.js
  • frontend/vite.config.js
  • frontend/vitest.config.mjs
  • tests/README.md
  • tests/compose/docker-compose.test.yaml
  • tests/rig/groups.py
  • tests/rig/selection.py
  • tests/rig/tests/test_groups.py
  • tests/rig/tests/test_selection.py
  • unstract/sdk1/src/unstract/sdk1/llm.py
  • unstract/sdk1/tests/test_mock_response.py
  • workers/tests/test_ide_callback.py
💤 Files with no reviewable changes (3)
  • unstract/sdk1/src/unstract/sdk1/llm.py
  • unstract/sdk1/tests/test_mock_response.py
  • docker/docker-compose.yaml

Comment thread .github/workflows/ci-test.yaml Outdated
Comment on lines +20 to 27
# Execute-path e2e must never reach a real provider.
worker-executor-v2:
environment:
- ENVIRONMENT=test
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-}

worker-file-processing-v2:
environment:
- ENVIRONMENT=test
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fail closed when the mock variable is absent.

${UNSTRACT_LLM_MOCK_RESPONSE:-} expands to an empty value, which the LLM adapter treats as unset; a compose run outside the rig can therefore call a real provider despite the comment’s “must never” guarantee. Default both worker services to MOCK_LLM_OK or fail startup when the variable is empty.

Proposed fix
-      - UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-}
+      - UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-MOCK_LLM_OK}

Apply the same change to worker-file-processing-v2.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Execute-path e2e must never reach a real provider.
worker-executor-v2:
environment:
- ENVIRONMENT=test
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-}
worker-file-processing-v2:
environment:
- ENVIRONMENT=test
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-}
# Execute-path e2e must never reach a real provider.
worker-executor-v2:
environment:
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-MOCK_LLM_OK}
worker-file-processing-v2:
environment:
- UNSTRACT_LLM_MOCK_RESPONSE=${UNSTRACT_LLM_MOCK_RESPONSE:-MOCK_LLM_OK}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/compose/docker-compose.test.yaml` around lines 20 - 27, Update the
UNSTRACT_LLM_MOCK_RESPONSE environment entries for both worker-executor-v2 and
worker-file-processing-v2 to fail closed when the variable is absent, using
MOCK_LLM_OK as the default or enforcing startup failure for an empty value.
Preserve the guarantee that execute-path E2E runs cannot reach a real provider.

Comment thread tests/rig/groups.py
Comment on lines +171 to +178
def _load_manifest_dict(manifest_path: Path) -> dict[str, Any]:
"""Parse a manifest YAML, rejecting anything that is not a ``groups:`` mapping."""
if not manifest_path.is_file():
raise ValueError(f"{manifest_path}: manifest file not found")
raw = yaml.safe_load(manifest_path.read_text())
if not isinstance(raw, dict) or "groups" not in raw:
raise ValueError(f"{manifest_path}: expected top-level `groups:` mapping")
return raw

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate that groups is a mapping.

groups: [] passes this check and later raises AttributeError at Line 130. Reject non-mapping values here so base and overlay manifests consistently raise the intended ValueError.

Proposed fix
-    if not isinstance(raw, dict) or "groups" not in raw:
+    if not isinstance(raw, dict) or not isinstance(raw.get("groups"), dict):
         raise ValueError(f"{manifest_path}: expected top-level `groups:` mapping")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _load_manifest_dict(manifest_path: Path) -> dict[str, Any]:
"""Parse a manifest YAML, rejecting anything that is not a ``groups:`` mapping."""
if not manifest_path.is_file():
raise ValueError(f"{manifest_path}: manifest file not found")
raw = yaml.safe_load(manifest_path.read_text())
if not isinstance(raw, dict) or "groups" not in raw:
raise ValueError(f"{manifest_path}: expected top-level `groups:` mapping")
return raw
def _load_manifest_dict(manifest_path: Path) -> dict[str, Any]:
"""Parse a manifest YAML, rejecting anything that is not a ``groups:`` mapping."""
if not manifest_path.is_file():
raise ValueError(f"{manifest_path}: manifest file not found")
raw = yaml.safe_load(manifest_path.read_text())
if not isinstance(raw, dict) or not isinstance(raw.get("groups"), dict):
raise ValueError(f"{manifest_path}: expected top-level `groups:` mapping")
return raw
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rig/groups.py` around lines 171 - 178, Update _load_manifest_dict to
validate that raw["groups"] is a mapping in addition to validating the top-level
manifest and groups key. Raise the existing manifest-specific ValueError for
non-mapping groups values, preserving consistent validation for base and overlay
manifests.

List rows are a table drawn with flexbox: columns only line up across rows
because their widths are pinned, and a wrapper that shrinks lets pinned
children spill onto the row actions. Both failure modes shipped recently and
were caught by human QA — neither is visible in a CSS diff, and neither can be
caught by the existing vitest setup, whose happy-dom environment measures
every rectangle as zero.

Adds a second vitest project running real Chromium via playwright, asserting
the geometric contracts of ListView at the widths its own CSS claims to
support: columns are straight, metadata stays inside its box and clear of the
action icons, rows never overflow, over-long owner labels truncate instead of
displacing the layout.

Assertions are relational, never absolute pixels — a redesign that moves the
columns keeps passing, only a column that stops being a column fails. The
shared helpers in test-utils/layout are component-agnostic.

Wires both projects into the existing ci-test workflow as a `frontend` job,
gated by a frontend/** paths filter and joined to the report job's merge gate,
so every PR touching the frontend runs them and a red assertion blocks. This
is also the first CI execution of the pre-existing unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@athul-rs
athul-rs force-pushed the UN-3741-fe-layout-tests branch from cd8ef15 to 71195c9 Compare July 22, 2026 20:47
@athul-rs athul-rs changed the title UN-3741 [MISC] Add a browser-based frontend layout test tier UN-3797 [MISC] Add a browser-based frontend layout test tier Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 14.6
e2e-coowners e2e 1 0 0 0 1.1
e2e-etl e2e 1 0 0 0 7.8
e2e-login e2e 2 0 0 0 0.9
e2e-prompt-studio e2e 1 0 0 0 4.3
e2e-smoke e2e 2 0 0 0 0.8
e2e-workflow e2e 1 0 0 0 23.0
integration-backend integration 124 0 0 27 66.8
integration-connectors integration 1 0 0 7 8.4
unit-backend unit 163 0 0 0 21.8
unit-connectors unit 63 0 0 0 9.8
unit-core unit 27 0 0 0 1.3
unit-platform-service unit 15 0 0 0 2.6
unit-rig unit 86 0 0 0 4.4
unit-sdk1 unit 480 0 0 0 24.0
unit-workers unit 723 0 0 0 47.2
TOTAL 1693 0 0 34 238.7

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

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.

3 participants