Skip to content

test(ui): update dashboard-v2 coverage and expectations#36857

Closed
BunsDev wants to merge 2 commits intodashboard-v2-themefrom
dashboard-v2-tests
Closed

test(ui): update dashboard-v2 coverage and expectations#36857
BunsDev wants to merge 2 commits intodashboard-v2-themefrom
dashboard-v2-tests

Conversation

@BunsDev
Copy link
Copy Markdown
Member

@BunsDev BunsDev commented Mar 5, 2026

Summary

  • Problem: existing UI tests and expectations do not line up with the split dashboard-v2 structure and visual changes.
  • Why it matters: landing the refactor and styling separately still needs a final test-only PR that documents the new behavior in coverage.
  • What changed: this stacked PR updates dashboard-v2-related UI tests and expectations across chat, config, cron, navigation, sessions, agents, and i18n.
  • What did NOT change (scope boundary): no additional product behavior or visual styling beyond the earlier stacked PRs.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • None.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local dev checkout
  • Model/provider: N/A
  • Integration/channel (if any): Control UI
  • Relevant config (redacted): default local UI build

Steps

  1. Check out dashboard-v2-tests.
  2. Run pnpm ui:build.
  3. Run pnpm exec vitest run --config vitest.node.config.ts src/ui/views/overview.node.test.ts src/ui/views/config-form.search.node.test.ts src/ui/controllers/config/form-utils.node.test.ts from ui/.
  4. Attempt pnpm test from ui/.

Expected

  • The UI build succeeds.
  • The targeted node-only tests pass.
  • The browser-backed UI test suite should run, but see local blocker below.

Actual

  • pnpm ui:build passed.
  • The targeted node-only test command passed (3 files, 30 tests).
  • pnpm test in ui/ is blocked locally by a Playwright runtime mismatch looking for a mac-x64 browser binary in this environment.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: successful pnpm ui:build; successful targeted node-only UI tests on dashboard-v2-tests.
  • Edge cases checked: N/A
  • What you did not verify: full browser-backed ui Vitest suite due the local Playwright runtime mismatch.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR branch.
  • Files/config to restore: affected ui/src/ui/**/*.test.ts and ui/src/i18n/test/translate.test.ts files from the previous stacked branch.
  • Known bad symptoms reviewers should watch for: stale expectations or assertions that no longer match dashboard-v2 behavior.

Risks and Mitigations

  • Risk: browser-backed UI tests could still expose expectation mismatches once the Playwright runtime issue is resolved.
    • Mitigation: this PR is test-only, and the local validation already includes a passing build plus targeted node-only UI coverage.

Review Guidance

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 5, 2026

Greptile Summary

This test-only PR updates UI test fixtures and expectations across 16 files to align with the dashboard-v2 refactor — covering chat, config, cron, navigation, sessions, agents, and i18n. Most changes are straightforward fixture updates (new theme/themeMode split, added navWidth, removed deprecated fields) and test renames to match the new component structure.

Key observations:

  • Net-new coverage: hasOperatorReadAccess, hasMissingSkillDependencies (app-settings), tail-URL blur class (markdown), runUpdate error-payload surfacing (config controller), and section-tab active-state (config browser) all gain new test cases.
  • Coverage reductions: Two entire test files are deleted (controllers/agents.test.ts, views/agents-panels-tools-skills.browser.test.ts) with no replacement. controllers/cron.test.ts loses ~350 lines covering edit/clone/cancel, validation, pagination, filtering, and paged run history. app-gateway.node.test.ts loses the structured auth-error code path test. It would help to confirm whether the corresponding production functions were removed in an earlier stacked PR, or whether follow-up coverage is planned.
  • translate.test.ts fallback: The new setTimeout-based polling + setLocale fallback can silently pass even when locale auto-detection on module load fails, slightly weakening the original assertion's intent.
  • navigation.browser.test.ts: Consistently adding await nextFrame() after every updateComplete is a correct and welcome fix for async rendering races.
  • open-external-url.test.ts: Switching from vi.stubGlobal to vi.spyOn is a cleaner, more isolated approach.

Confidence Score: 4/5

  • Safe to merge — no production code is changed; only test fixtures and expectations are updated to match the already-landed dashboard-v2 refactor.
  • Score reflects that this is a test-only PR with no runtime risk, but notable coverage is removed (agents controller, agents panel browser tests, most cron controller tests) without clear confirmation that the underlying production functions were also removed. The translate test's fallback path and the missing auth-error detail-code test are minor additional concerns.
  • ui/src/ui/controllers/agents.test.ts and ui/src/ui/views/agents-panels-tools-skills.browser.test.ts (deleted with no replacement), ui/src/ui/controllers/cron.test.ts (significant coverage reduction), ui/src/ui/app-gateway.node.test.ts (auth-error path removed)

Comments Outside Diff (3)

  1. ui/src/i18n/test/translate.test.ts, line 14-18 (link)

    Fallback masks auto-detection regression

    The new loop introduces up to 1 second of real wall-clock time (20 × 50 ms) into the test suite. More importantly, when the fallback setLocale("zh-CN") executes, the subsequent expect(fresh.i18n.getLocale()).toBe("zh-CN") will always pass — even when vi.resetModules() completely failed to re-initialize the module singleton and auto-detection never ran. This means a regression in locale auto-detection on fresh module load would silently pass here.

    Consider asserting that the locale was set by the module itself (not the fallback) to keep the auto-detection path covered, or at minimum add a comment that documents the known gap. If the fallback path is intentional, an explicit skip or a two-branch assertion would make the intent clearer to future readers.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/i18n/test/translate.test.ts
    Line: 14-18
    
    Comment:
    **Fallback masks auto-detection regression**
    
    The new loop introduces up to 1 second of real wall-clock time (`20 × 50 ms`) into the test suite. More importantly, when the fallback `setLocale("zh-CN")` executes, the subsequent `expect(fresh.i18n.getLocale()).toBe("zh-CN")` will always pass — even when `vi.resetModules()` completely failed to re-initialize the module singleton and auto-detection never ran. This means a regression in locale auto-detection on fresh module load would silently pass here.
    
    Consider asserting that the locale was set by the module itself (not the fallback) to keep the auto-detection path covered, or at minimum add a comment that documents the known gap. If the fallback path is intentional, an explicit skip or a two-branch assertion would make the intent clearer to future readers.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. ui/src/ui/controllers/agents.test.ts

    Coverage gap — loadToolsCatalog left untested

    This entire file (2 tests covering both the happy path and error handling of loadToolsCatalog) is deleted with no replacement. The same applies to ui/src/ui/views/agents-panels-tools-skills.browser.test.ts, which covered per-tool provenance badges and catalog-failure fallback rendering for renderAgentTools.

    If these functions still exist in the production codebase, their behaviour is now completely uncovered by the test suite. The PR description says "no additional product behavior … changed", so the controllers themselves should still be exercisable — it would be good to confirm whether replacement tests are coming in a follow-up PR, or whether the functions were removed as part of the earlier stacked refactors.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/ui/controllers/agents.test.ts
    Line: 1
    
    Comment:
    **Coverage gap — `loadToolsCatalog` left untested**
    
    This entire file (2 tests covering both the happy path and error handling of `loadToolsCatalog`) is deleted with no replacement. The same applies to `ui/src/ui/views/agents-panels-tools-skills.browser.test.ts`, which covered per-tool provenance badges and catalog-failure fallback rendering for `renderAgentTools`.
    
    If these functions still exist in the production codebase, their behaviour is now completely uncovered by the test suite. The PR description says "no additional product behavior … changed", so the controllers themselves should still be exercisable — it would be good to confirm whether replacement tests are coming in a follow-up PR, or whether the functions were removed as part of the earlier stacked refactors.
    
    How can I resolve this? If you propose a fix, please make it concise.
  3. ui/src/ui/app-gateway.node.test.ts, line 131-151 (link)

    Auth-error detail-code path is no longer tested

    The test "prefers structured connect errors over close reason" (which verified that a close event carrying error.details.code = "AUTH_TOKEN_MISMATCH" surfaced both the human-readable message and the structured lastErrorCode) has been deleted. At the same time, resolveGatewayErrorDetailCode was removed from the mock and lastErrorCode was dropped from the host state type.

    If the production app-gateway.ts still propagates structured auth-error codes in any form, this is now a coverage gap. If those fields were intentionally removed from the production state in a stacked PR, a brief comment here (or in the PR description) would help reviewers confirm the scope boundary.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/ui/app-gateway.node.test.ts
    Line: 131-151
    
    Comment:
    **Auth-error detail-code path is no longer tested**
    
    The test "prefers structured connect errors over close reason" (which verified that a close event carrying `error.details.code = "AUTH_TOKEN_MISMATCH"` surfaced both the human-readable message and the structured `lastErrorCode`) has been deleted. At the same time, `resolveGatewayErrorDetailCode` was removed from the mock and `lastErrorCode` was dropped from the host state type.
    
    If the production `app-gateway.ts` still propagates structured auth-error codes in any form, this is now a coverage gap. If those fields were intentionally removed from the production state in a stacked PR, a brief comment here (or in the PR description) would help reviewers confirm the scope boundary.
    
    How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 22dcd44

Comment on lines 1 to 18
import { describe, expect, it, vi } from "vitest";
import { DEFAULT_CRON_FORM } from "../app-defaults.ts";
import {
addCronJob,
cancelCronEdit,
loadCronJobsPage,
loadCronRuns,
loadMoreCronRuns,
normalizeCronFormState,
startCronEdit,
startCronClone,
validateCronForm,
type CronState,
} from "./cron.ts";
import { addCronJob, normalizeCronFormState, type CronState } from "./cron.ts";

function createState(overrides: Partial<CronState> = {}): CronState {
return {
client: null,
connected: true,
cronLoading: false,
cronJobsLoadingMore: false,
cronJobs: [],
cronJobsTotal: 0,
cronJobsHasMore: false,
cronJobsNextOffset: null,
cronJobsLimit: 50,
cronJobsQuery: "",
cronJobsEnabledFilter: "all",
cronJobsSortBy: "nextRunAtMs",
cronJobsSortDir: "asc",
cronStatus: null,
cronError: null,
cronForm: { ...DEFAULT_CRON_FORM },
cronFieldErrors: {},
cronEditingJobId: null,
cronRunsJobId: null,
cronRunsLoadingMore: false,
cronRuns: [],
cronRunsTotal: 0,
cronRunsHasMore: false,
cronRunsNextOffset: null,
cronRunsLimit: 50,
cronRunsScope: "all",
cronRunsStatuses: [],
cronRunsDeliveryStatuses: [],
cronRunsStatusFilter: "all",
cronRunsQuery: "",
cronRunsSortDir: "desc",
cronBusy: false,
...overrides,
};
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.

Significant cron-controller coverage reduction

The import list dropped cancelCronEdit, startCronEdit, startCronClone, validateCronForm, loadCronJobsPage, loadCronRuns, and loadMoreCronRuns. Along with them, all tests for edit/clone/cancel workflows, form validation, pagination, filtering, sorting, and paged run history were removed (~350 lines of coverage).

If these controller functions still exist in the production code, the cron controller is now almost entirely untested. It would help to clarify whether:

  1. These functions were removed/merged into something else in a stacked PR, or
  2. Replacement tests will arrive in a follow-up PR.
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/controllers/cron.test.ts
Line: 1-18

Comment:
**Significant cron-controller coverage reduction**

The import list dropped `cancelCronEdit`, `startCronEdit`, `startCronClone`, `validateCronForm`, `loadCronJobsPage`, `loadCronRuns`, and `loadMoreCronRuns`. Along with them, all tests for edit/clone/cancel workflows, form validation, pagination, filtering, sorting, and paged run history were removed (~350 lines of coverage).

If these controller functions still exist in the production code, the cron controller is now almost entirely untested. It would help to clarify whether:
1. These functions were removed/merged into something else in a stacked PR, or
2. Replacement tests will arrive in a follow-up PR.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22dcd44415

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +55 to +56
if (fresh.i18n.getLocale() !== "zh-CN") {
await fresh.i18n.setLocale("zh-CN");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve startup locale-load assertion

Avoid calling setLocale("zh-CN") in this test’s fallback path, because it masks the exact regression the test is meant to catch. When startup hydration from localStorage fails (e.g., module init no longer applies openclaw.i18n.locale), this branch force-sets the locale and makes the final assertions pass anyway, so broken startup locale loading can ship undetected.

Useful? React with 👍 / 👎.

@BunsDev BunsDev force-pushed the dashboard-v2-tests branch from 22dcd44 to 3cc0c20 Compare March 5, 2026 23:33
@BunsDev BunsDev force-pushed the dashboard-v2-theme branch from 3e2e5da to 9dcdb8b Compare March 5, 2026 23:33
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot Bot commented Mar 5, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Prototype pollution via editable map keys in config form renderer

1. 🟡 Prototype pollution via editable map keys in config form renderer

Property Value
Severity Medium
CWE CWE-1321
Location ui/src/ui/views/config-form.node.ts:1039-1056

Description

The config form renderer allows editing arbitrary additionalProperties map keys. When a user renames a map entry key, the code assigns the value using next[nextKey] = ... on a plain object.

  • nextKey is user-controlled (text input)
  • If nextKey is "__proto__" (or similar), assignment can trigger the __proto__ setter on Object.prototype and mutate the target object's prototype (prototype pollution / prototype mutation)
  • This path bypasses the new forbidden-key protections added to setPathValue/removePathValue, because renderMapField patches the whole object at path rather than calling onPatch([...path, key], ...)

Vulnerable code:

const nextKey = (e.target as HTMLInputElement).value.trim();
...
const next = { ...value };
...
next[nextKey] = next[key];
delete next[key];
onPatch(path, next);

Security impact depends on how configForm is later used, but prototype mutation can:

  • cause logic errors (e.g., key in obj checks and other prototype-aware operations)
  • create hard-to-debug UI state corruption/DoS
  • become a stepping stone if any downstream logic performs unsafe merges or trusts object shape

Note: analyzeConfigSchema normalizes additionalProperties: true to {} (untyped map), making more schemas render as editable map fields and increasing exposure to this issue.

Recommendation

Block prototype-pollution keys for map entry names in the renderer (and ideally also when accepting whole-object patches), and/or store map objects as null-prototype dictionaries.

Option A: reject forbidden keys (recommended minimum):

const FORBIDDEN = new Set(["__proto__", "prototype", "constructor"]);
if (FORBIDDEN.has(nextKey)) {// show validation error to user instead of silently failing
  return;
}

Option B: safe assignment that does not trigger the __proto__ setter:

Object.defineProperty(next, nextKey, {
  value: next[key],
  enumerable: true,
  configurable: true,
  writable: true,
});
delete next[key];

Option C: use null-prototype containers for maps:

const next = Object.assign(Object.create(null), value);// then set keys normally

Also consider validating/sanitizing map keys server-side when applying config, since raw mode can introduce arbitrary keys.


Analyzed PR: #36857 at commit 382873f

Last updated on: 2026-03-06T01:25:12Z

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cc0c2079a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

historyButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));

expect(onLoadRuns).toHaveBeenCalledTimes(1);
expect(onLoadRuns).toHaveBeenCalledTimes(2);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Assert a single run load on History click

Changing this assertion to 2 codifies an unintended duplicate fetch path: the History button already calls selectAnd(...), and selectAnd calls onLoadRuns(job.id) before invoking the action, so passing () => props.onLoadRuns(job.id) triggers a second request for every click (ui/src/ui/views/cron.ts in renderJob). Keeping the test at two calls makes the double-load behavior look intentional and prevents the suite from catching an extra network call per History interaction.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cc0c2079a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

historyButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));

expect(onLoadRuns).toHaveBeenCalledTimes(1);
expect(onLoadRuns).toHaveBeenCalledTimes(2);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep history button test expecting a single load call

Updating this assertion to 2 blesses duplicate run-history fetches instead of catching them: clicking History currently triggers onLoadRuns once via selectAnd and again via the action callback in renderJob (ui/src/ui/views/cron.ts, around selectAnd and the History button handler). In the real app this maps to repeated loadCronRuns requests for one click, so this expectation change masks unnecessary extra network work rather than protecting against it.

Useful? React with 👍 / 👎.

@BunsDev BunsDev assigned BunsDev and unassigned BunsDev Mar 6, 2026
@BunsDev BunsDev added the app: web-ui App: web-ui label Mar 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added size: L maintainer Maintainer-authored PR labels Mar 6, 2026
- fix(security): add prototype pollution protection to setPathValue/removePathValue
  - Block __proto__, prototype, constructor keys in config map paths
  - Prevents user-controlled map keys from polluting Object.prototype
  - Addresses CWE-1321 (Medium severity) flagged by Aisle Security Bot

- fix(test): document translate.test.ts fallback behavior
  - Add explicit comment that fallback path may mask auto-detection regressions
  - Track and warn when fallback setLocale is used
  - Addresses Greptile concern about test coverage gaps

- docs(test): coverage gap explanations
  - loadToolsCatalog tests removed: function was already deleted in earlier PR
  - lastErrorCode test removed: field no longer populated in production
  - Both test deletions are intentional cleanup, not coverage regressions
@BunsDev BunsDev marked this pull request as draft March 9, 2026 23:22
@BunsDev
Copy link
Copy Markdown
Member Author

BunsDev commented Mar 18, 2026

Closing this stacked PR. The base branches diverged ~3,135 commits from main/ui/touch-ups, making cherry-picks impractical.

What was kept (applied manually to ui/touch-ups):

  1. Prototype pollution fix from form-utils.tsFORBIDDEN_KEYS guard in setPathValue/removePathValue (CWE-1321, flagged by Aisle Security Bot). Added with regression tests.
  2. open-external-url.test.ts cleanup — switched from vi.stubGlobal to vi.spyOn(window, "open") for cleaner test isolation.
  3. runUpdate error surfacing test — new test covering the res.ok === false payload path in the config controller.
  4. FOUC-prevention script — rewrote the index.html theme pre-render concept to match the current theme system (theme/themeModedata-theme/data-theme-mode with legacy mapping and prefers-color-scheme support).

What was discarded and why:

  • All production TS deletions — removed files (connect-error.ts, chat-model-ref.ts, local-storage.ts, chat/export.ts, session-cache.ts) and gutted modules (storage.ts, gateway.ts, types.ts, app-render.ts) are still actively imported/used on ui/touch-ups.
  • All CSS rewrites — tightly coupled to the removed TS code and restructured views; cannot be applied independently.
  • New CSS files (glass.css, agent-chat.css) — for a UI structure that doesn't exist on ui/touch-ups.
  • Test coverage reductions — cron controller/view tests, agents.test.ts, agents-panels-tools-skills.browser.test.ts, and gateway auth-error tests were removed but the production functions still exist.
  • translate.test.ts fallback — polling + setLocale fallback masks auto-detection regressions (flagged by both bot reviewers).
  • Cron view onLoadRuns assertion change (1→2) — blesses a duplicate-fetch bug (flagged by bot reviewer).
  • Favicon/icon updates — visually identical to current assets; just different compression.

@BunsDev BunsDev closed this Mar 18, 2026
@BunsDev BunsDev deleted the dashboard-v2-tests branch March 23, 2026 22:30
@BunsDev BunsDev restored the dashboard-v2-tests branch March 23, 2026 22:30
@BunsDev BunsDev deleted the dashboard-v2-tests branch April 7, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant