Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ filetime = "0.2"
# beat without real-time waits. Test-only — the runtime feature set in
# `[dependencies]` (`full`) intentionally excludes it.
tokio = { version = "1", features = ["test-util"] }
# Property-based testing for the adversarial-input surfaces (command classifier,
# encryption round-trip) — plan.md §6.3. Version already resolved in Cargo.lock.
proptest = "1"

[features]
default = ["tokenjuice-treesitter"]
Expand Down
2 changes: 2 additions & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/d3-force": "^3.0.10",
"@types/debug": "^4.1.12",
"@types/jest-axe": "^3.5.9",
"@types/node": "^25.0.10",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
Expand All @@ -158,6 +159,7 @@
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"husky": "^9.1.7",
"jest-axe": "^10.0.0",
"jsdom": "^28.0.0",
"knip": "^6.3.1",
"postcss": "^8.5.6",
Expand Down
80 changes: 80 additions & 0 deletions app/src/components/__tests__/a11y.smoke.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Accessibility smoke lane (plan.md §7 — the frontend had zero a11y assertions).
* Renders a few high-traffic, self-contained components and runs axe-core over
* the output, asserting no violations. jsdom can't compute layout, so
* color-contrast is auto-skipped; this catches the structural a11y bugs that
* matter — missing roles/labels, invalid ARIA, unlabelled controls.
*
* Kept deliberately small and provider-light so it stays fast and stable; grow
* it screen-by-screen rather than pulling in the full app shell.
*/
import { configureStore } from '@reduxjs/toolkit';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import { Provider } from 'react-redux';
import { describe, expect, it, vi } from 'vitest';

import chatRuntimeReducer, {
type ArtifactSnapshot,
type PendingApproval,
setPendingApprovalForThread,
} from '../../store/chatRuntimeSlice';
import ApprovalRequestCard from '../chat/ApprovalRequestCard';
import ArtifactCard from '../chat/ArtifactCard';

vi.mock('../../services/artifactDownloadService', () => ({
saveArtifactViaDialog: vi.fn(),
revealArtifactInFileManager: vi.fn(),
}));
vi.mock('../../services/coreRpcClient', () => ({ callCoreRpc: vi.fn() }));

async function expectNoViolations(container: HTMLElement) {
const results = await axe(container);
expect(results.violations).toEqual([]);
}

describe('accessibility smoke', () => {
it('ArtifactCard (ready) has no axe violations', async () => {
const artifact: ArtifactSnapshot = {
artifactId: 'a-1',
kind: 'presentation',
title: 'Quarterly Deck',
status: 'ready',
sizeBytes: 4096,
path: 'a-1/deck.pptx',
updatedAt: 0,
};
const { container } = render(<ArtifactCard artifact={artifact} />);
await expectNoViolations(container);
});

it('ArtifactCard (failed with error) has no axe violations', async () => {
const artifact: ArtifactSnapshot = {
artifactId: 'a-2',
kind: 'document',
title: 'Report',
status: 'failed',
error: 'producer crashed',
updatedAt: 0,
};
const { container } = render(<ArtifactCard artifact={artifact} onRetry={vi.fn()} />);
await expectNoViolations(container);
});

it('ApprovalRequestCard has no axe violations', async () => {
const approval: PendingApproval = {
requestId: 'req-1',
toolName: 'shell',
message: 'Run `shell` — shell (18 bytes of arguments)',
command: 'pip show yfinance',
};
const store = configureStore({ reducer: { chatRuntime: chatRuntimeReducer } });
store.dispatch(setPendingApprovalForThread({ threadId: 't1', approval }));
const { container } = render(
<Provider store={store}>
<ApprovalRequestCard threadId="t1" approval={approval} />
</Provider>
);
await expectNoViolations(container);
});
});
19 changes: 16 additions & 3 deletions plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,9 +434,22 @@ P0, most P1 "untested" claims were **inaccurate against current `main`**. Verifi
the ~20 controller domains with no `tests/` reference (the Phase 0 inventory allowlist to burn
down). Approval×turn integration, journey spec, and the Playwright approval mirror need WDIO/PW.

**Phase 4 — new dimensions (ongoing)**
- proptest + cargo-fuzz targets; scoped cargo-mutants; jest-axe lane; migration fixture;
duration report; mock chaos modes + contract tests.
**Phase 4 — new dimensions** — ⏳ partial (PR: `test/phase4-new-dimensions`). Unlike the §4 backlog,
these are genuinely greenfield (the suite had zero of them), so they were built and validated:
- [x] **proptest** (was absent): added as a dev-dep; property suite for the command classifier +
path check (never-panic on arbitrary/unicode input; fail-closed redirect floor). NOTE: an
encryption round-trip property was trialled but dropped — under coverage Argon2id is ~2.5s/case,
so it held the lib-test binary ~60s and deterministically exposed a pre-existing env-var race in
the unrelated `config::schema::load` env-overlay tests; the round-trip stays covered by the
Phase 1 fixed-input tests.
- [x] **Mock chaos modes** (§5.3): `httpFaultRules` gains `mode: "reset"` (connection reset) and
`mode: "malformed"` (non-JSON 200) beyond clean statuses; `scripts/mock-api/chaos.test.mjs`
drives the real server (auto-run by the Phase 0 `test:scripts` job).
- [x] **jest-axe a11y smoke lane** (§7 — the frontend had zero a11y assertions): axe-core over
ArtifactCard + ApprovalRequestCard asserting no violations.
- [ ] Still open (larger tooling / own PRs): cargo-fuzz targets, scoped cargo-mutants on the P0
zones, criterion micro-benches, migration snapshot fixture, per-suite duration report,
mock↔backend contract test.

---

Expand Down
Loading
Loading