Conversation
WalkthroughMigrates legacy JS test tooling to a new TypeScript-based test UI and server. Removes old JS runners/controllers, adds test registry, TypeScript controllers, an Express+WebSocket backend, a React+Redux frontend, CLI entry (run.mts), and supporting docs/configs. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as React Client
participant WS as WebSocket
participant Server as Express Server
participant Queue as Test Queue
participant Controller as Npm/Yarn Controller
participant FS as File System
Client->>Server: POST /api/tests/run (testName, version)
Server->>Queue: enqueue(test)
Server->>WS: broadcast "test-queued"
WS-->>Client: "test-queued"
Queue->>Server: dequeue -> start test
Server->>WS: broadcast "test-started"
WS-->>Client: "test-started"
loop phases: clean,setVersion,install,test,build,verify
Server->>Controller: execute phase command
Controller->>FS: read/write/pack files
FS-->>Controller: output
Controller-->>Server: TestOutcome (ok|fail) + output
Server->>WS: broadcast "test-output" (phase, status, output, duration)
WS-->>Client: "test-output"
end
alt all phases passed
Server->>WS: broadcast "test-completed" (passed)
else any failed
Server->>WS: broadcast "test-completed" (failed)
end
WS-->>Client: "test-completed"
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (9)
.github/workflows/integ.yml-61-61 (1)
61-61: Update checkout action to v4 for consistency with thelist_integration_testsjob.The
build-and-testjob usesactions/checkout@v3(line 61), which is inconsistent with thelist_integration_testsjob that usesactions/checkout@v4(line 28). Using the same version across jobs in the workflow is recommended for consistency and stability.Apply this diff:
- - uses: actions/checkout@v3 + - uses: actions/checkout@v4integrations/tanstack-start-basic/src/routeTree.gen.ts-366-374 (1)
366-374: Unused import and risk of modification in auto-generated file.
createStart(line 368) is imported but never used in the module augmentation.- This file is marked as auto-generated (lines 7-9 warn against making changes). Manual additions may be overwritten when TanStack Router regenerates this file.
Consider moving this module augmentation to a separate declaration file (e.g.,
tanstack-start.d.ts) to avoid losing changes on regeneration.import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' declare module '@tanstack/react-start' {scripts/run.mts-88-100 (1)
88-100: Missing return statement and type narrowing issue.
Line 90 assigns
getTestMetadata(testName)(which returnsTestMetadata | undefined) to a variable typed asTestMetadata. Theundefinedcase is checked on line 92, but the type annotation is misleading.If
metadata.typeis neither'library'nor'direct', the function implicitly returnsundefined, violating thePromise<TestOutcome[]>return type.export async function runTest(testName: string, rechartsVersion: string): Promise<TestOutcome[]> { // First try to get metadata from registry - const metadata: TestMetadata = getTestMetadata(testName); + const metadata = getTestMetadata(testName); if (!metadata) { throw new Error(`Test "${testName}" not found in registry.`); } if (metadata.type === 'library') { return await runLibraryTest(metadata, rechartsVersion); } else if (metadata.type === 'direct') { return await runDirectDependencyTest(metadata, rechartsVersion); + } else { + throw new Error(`Unknown test type: ${metadata.type}`); } }test-ui/README.md-75-83 (1)
75-83: Documentation references incorrect filename.The architecture section references
server.js, but the actual file isserver.ts(TypeScript). This should be updated for consistency.-### Backend (server.js) +### Backend (server/server.ts)test-ui/test/store/exampleTestResults.ts-4-1615 (1)
4-1615: Consider sanitizing local filesystem paths in fixture outputMany
outputstrings embed absolute paths from a specific machine (e.g.,/Users/pavelvanecek/...). While harmless for functionality, this:
- Leaks local environment details, and
- Makes future fixture updates noisy.
If feasible, consider replacing those paths with normalized placeholders (e.g.,
/path/to/project/...) or trimming logs to just the parts needed by the UI/tests.test-ui/server/scripts/TestOutcome.ts-1-15 (1)
1-15: Fix array join separator instringifyThe array branch currently joins with the literal string
"/n"instead of a newline, which will produce unexpected output formatting. Line 9 has"/n"(forward-slash + n) when it should be"\n"(newline escape).- if (Array.isArray(output)) { - return output.map((item) => stringify(item)).join("/n"); - } + if (Array.isArray(output)) { + return output.map((item) => stringify(item)).join("\n"); + }test-ui/server/server.ts-83-84 (1)
83-84: Abort controller is declared but never used.
currentTestAbortControlleris declared and checked in the cancel endpoint (lines 566-569), but it's never assigned when starting a test. The cancellation mechanism won't actually abort running tests.If you want test cancellation to work, you'll need to:
- Create an
AbortControllerwhen starting a test inexecuteTest- Pass its signal to the controller commands
- Handle abort signals in the controller methods
Would you like me to help design this cancellation flow?
test-ui/server/server.ts-349-357 (1)
349-357: Missing return for unexpected test type.If
metadata.typeis neither"library"nor"direct", the function implicitly returnsundefined. This will cause a runtime error when destructuring at line 430:const { fn } = getController(...).} else if (metadata.type === "direct") { const absolutePath = path.join(rootDir, metadata.integrationPath!); const controller = factory(absolutePath); return { controller, fn: runDirectDependencyAppTest(controller, version), }; + } else { + throw new Error(`Unknown test type: ${metadata.type}`); } }test-ui/server/server.ts-157-172 (1)
157-172: Unreachable code branch.Line 163 (
else if (typeof result === "string")) is unreachable because it's inside theif (typeof result === 'object')block. A result cannot be both an object and a string.if (result && typeof result === "object") { if (result.success !== undefined) { phase.status = result.success ? "passed" : "failed"; if (result.error) { phase.output = String(result.error); } - } else if (typeof result === "string") { - phase.output = result; - phase.status = "passed"; + } else { + phase.status = "passed"; } } else if (typeof result === "string") { phase.output = result; phase.status = "passed"; } else { phase.status = "passed"; }
🧹 Nitpick comments (26)
.run/Template Vitest.run.xml (1)
7-7: Consider documenting environment variable requirements (if any).The
<envs />element is empty. If the Vitest tests require environment variables (e.g., for API URLs, test databases, or feature flags), document this in the PR description or aCONTRIBUTING.mdfile so team members know to set them up when using this template.test-ui/DEVELOPMENT.md (1)
5-16: Minor: Add language specifier to fenced code blocks.Lines 5-16 and other code blocks in this file don't specify a language for syntax highlighting. While this is a minor formatting issue, adding language identifiers improves readability. For the directory structure at line 5, you could use
```treeor```textinstead of```.Example fix for lines 5-16:
-``` +```tree test-ui/ ├── src/This is optional and doesn't affect functionality.
test-ui/.gitignore (1)
1-28: Consider adding TypeScript build artifacts.The
.gitignorecovers standard Node.js artifacts well. However, it may be missing TypeScript incremental build artifacts (*.tsbuildinfo). Iftest-ui/server/tsconfig.tsbuildinfo(added in this PR) should not be committed, add a pattern to exclude these files.Add this line to exclude TypeScript build info files:
server.js + +# TypeScript incremental build info +*.tsbuildinfoVerify with your team's build process before applying—some teams commit tsbuildinfo for faster CI builds.
test-ui/SCREENSHOTS.md (2)
92-92: Minor: Use hyphen in compound adjective.Line 92 reads "Full width gradient background" but should be "Full-width gradient background" per standard English punctuation for compound adjectives modifying a noun.
-- Full width gradient background +- Full-width gradient backgroundThis is a minor grammar improvement that improves readability.
5-5: Minor: Add language specifiers to fenced code blocks.Lines 5, 16, 62, and others have fenced code blocks without language identifiers. Adding language specs enables syntax highlighting and follows Markdown best practices.
Examples:
-``` +```text ┌─────────────────────────────────────────────────────────────────────────┐ │ 🧪 Recharts Integration Test Runner │This improves documentation rendering without changing content.
Also applies to: 16-16, 62-62
test-ui/UI_STRUCTURE.md (1)
5-5: Minor: Add language specifiers to fenced code blocks.Lines 5, 16, and 62 have fenced code blocks without language identifiers. Adding language specs improves rendering and aligns with Markdown best practices.
Add language identifiers like
```textor```yaml:-``` +```text ┌────────────────────────────────────────────────────────────────────┐ │ [✓] npm-react18 ✓ Stable ✅ passed │This is optional but improves documentation quality.
Also applies to: 16-16, 62-62
scripts/run.mts (1)
76-86: Non-null assertions on optional metadata fields without validation.Lines 77-78 and 85 use non-null assertions (
!) onmetadata.libraryName,metadata.appName, andmetadata.integrationPath. If these fields areundefinedfor a given test type,path.join()will produce invalid paths like/path/to/libraries/undefined.Consider validating required fields before use:
async function runLibraryTest(metadata: TestMetadata, rechartsVersion: string): Promise<TestOutcome[]> { + if (!metadata.libraryName || !metadata.appName) { + throw new Error(`Library test "${metadata.name}" missing required libraryName or appName`); + } - const libPath = path.join(__dirname, "../libraries", metadata.libraryName!); - const appPath = path.join(__dirname, "../apps-3rd-party", metadata.appName!); + const libPath = path.join(__dirname, "../libraries", metadata.libraryName); + const appPath = path.join(__dirname, "../apps-3rd-party", metadata.appName); const Controller = getControllerConstructor(metadata.packageManager); return await runLibraryInLibraryTest(new Controller(libPath), new Controller(appPath), rechartsVersion); } async function runDirectDependencyTest(metadata: TestMetadata, rechartsVersion: string): Promise<TestOutcome[]> { + if (!metadata.integrationPath) { + throw new Error(`Direct test "${metadata.name}" missing required integrationPath`); + } const Controller = getControllerConstructor(metadata.packageManager); - return await runDirectDependencyAppTest(new Controller(metadata.integrationPath!), rechartsVersion); + return await runDirectDependencyAppTest(new Controller(metadata.integrationPath), rechartsVersion); }test-ui/nodemon.json (1)
1-8: Ensure nodemon can actually run TS and watch all relevant filesAs written,
nodemonwill:
- Try to execute
server/server.tswith plainnode.- Only restart on
.tschanges, not.mtsor.jsunder../scripts.Unless you have a TS loader wired in elsewhere, this is likely to fail or miss restarts.
Consider something along these lines (adjusting to whatever you already use —
ts-node,tsx, or compiled JS):- "watch": ["server", "../scripts"], - "ext": "ts", - "exec": "node server/server.ts", + "watch": ["server", "../scripts"], + "ext": "ts,mts,js", + "exec": "tsx server/server.ts"If you instead compile to JS first, point
execat the built JS file and setwatch/extaccordingly. Please double‑check this matches your actual dev workflow.README.md (1)
14-14: Wrap bare URL to satisfy markdownlint (optional)Markdownlint flags the bare
http://localhost:3000. Wrapping it as a link/autolink will clear that without changing meaning.-Then open http://localhost:3000 in your browser. +Then open <http://localhost:3000> in your browser.test-ui/src/PhaseOutput.tsx (2)
82-90: Consider adding ESLint disable comment for intentional missing dependency.The
expandedPhasesdependency is intentionally omitted to prevent an infinite loop (expand triggers re-render, re-render triggers effect). Adding a comment clarifies this is deliberate.// Auto-expand current phase when running useEffect(() => { if (currentPhase && !expandedPhases[currentPhase]) { setExpandedPhases(prev => ({ ...prev, [currentPhase]: true })); } + // eslint-disable-next-line react-hooks/exhaustive-deps -- expandedPhases intentionally omitted to prevent infinite loop }, [currentPhase]);
72-80: Edge case: negative duration could show unexpected results.If
durationis negative (clock skew, incorrect timestamps), this would display a negative value. Consider adding a guard.const formatDuration = (duration: Phase['duration']) => { - if (!duration) return '-'; + if (!duration || duration < 0) return '-'; const seconds = Math.floor(duration / 1000);test-ui/src/App.css (1)
1-575: Add focus styles for interactive elementsButtons and clickable phase items have hover styles but no explicit
:focus/:focus-visiblerules. Adding clear focus outlines (e.g., for.btn,.btn-clear,button.phase-summary-item) would improve keyboard accessibility without changing layout.test-ui/test/store/testDurationSelectors.spec.ts (2)
99-108: Use array.lengthinstead ofObject.keyson results
selectAllTestResultsreturns an array, soObject.keys(testResults).lengthis indirect. UsingtestResults.lengthis simpler and conveys intent more clearly:- expect(Object.keys(testResults).length).toBe( - Object.keys(exampleTestResults).length, - ); + expect(testResults.length).toBe( + Object.keys(exampleTestResults).length, + );
140-157: Stabilize time‑dependent ETA expectations
selectRunningTestsETAusesDate.now()(via the selector), but the test asserts an exact numeric ETA (53682). This can become brittle if:
- The fixture start time changes, or
- The implementation tweaks elapsed/remaining calculations.
Consider either mocking the clock (e.g., fixing
Date.now()/Vitest system time) or asserting with a tolerance (toBeCloseTo) so these ETAs remain stable across small changes.test-ui/CHANGES.md (1)
8-30: Fix Markdown list indentation to satisfy markdownlint (MD007)
markdownlintis complaining about unordered list indentation (MD007) for the bullets under sections 1–3. You can fix this by removing the leading spaces before-and using standard nested indentation, for example:-### 1. **App.tsx - TestItem Component** - - Updated the phase summary to display phase names alongside status icons - - Added duration display in tooltips for each phase - - Improved the one-line summary to be more informative with phase labels - - Reordered buttons for better UX (Run, Show/Hide, Clear) - - Phase summary now always visible (not dependent on expanded state) - - Made expand/collapse more intuitive with "Show"/"Hide" labels +### 1. **App.tsx - TestItem Component** +- Updated the phase summary to display phase names alongside status icons +- Added duration display in tooltips for each phase +- Improved the one-line summary to be more informative with phase labels +- Reordered buttons for better UX (Run, Show/Hide, Clear) +- Phase summary now always visible (not dependent on expanded state) +- Made expand/collapse more intuitive with "Show"/"Hide" labelsApply the same pattern to the bullets under sections 2 and 3 to clear the lints.
test-ui/SUMMARY.md (1)
9-26: Align SUMMARY with current TS structure and clean up markdownlint issues
- The “Files Created” tree still lists
App.jsx,main.jsx,server.js, andvite.config.js, while the refactor doc usesApp.tsx,main.tsx,server.ts?, andvite.config.ts. Consider updating the tree to match the actual TypeScript filenames so newcomers don’t get lost.- To satisfy
markdownlint:
- Add a language to fenced blocks that are currently bare (e.g. use
```textfor the directory tree and data-flow diagram).- Wrap bare URLs (like
http://localhost:3000) as<http://localhost:3000>or[link](http://localhost:3000).Also applies to: 61-76, 77-96
test-ui/src/store/testsSlice.ts (1)
4-21: Consider keying queued/running tests byidand reusing shared typesRight now
queuedTestsandrunningTestsare keyed bytestName, and the queued shape{ id, position }is duplicated inline instead of using theQueuedTesttype fromtypes.ts. This makes it harder to support multiple concurrent runs of the same test and slightly increases maintenance cost.Non-blocking, but consider:
- Using
idas the primary key inqueuedTests/runningTestsand storingtestNameas a field.- Replacing
Record<string, { id: string; position: number }>withRecord<string, QueuedTest>to keep shapes in sync withtypes.ts.Also applies to: 76-90, 91-107
test-ui/REFACTORING.md (1)
75-97: Minor markdown polish for tooling friendlinessA few tiny doc tweaks would make linters happier:
- Add a language to the fenced blocks showing the project tree and WebSocket message flow (e.g.
```text).- Optionally tighten the closing sentence by replacing “remains exactly the same” with “remains unchanged” or similar.
Purely cosmetic; safe to defer.
Also applies to: 185-199, 379-390
test-ui/server/scripts/YarnController.ts (1)
27-33: Preferawait super.clean()for clarity and future safety
clean()callssuper.clean()but doesn’tawaitthe returned promise. Today the base implementation is fully synchronous, but if it ever gains async work, that call will no longer be sequenced with the Yarn cache clean.Non-blocking, but it’s safer and clearer to await it:
- async clean(): Promise<TestOutcome> { - super.clean(); - await this.execAsync('yarn cache clean'); - return TestOutcome.ok('clean'); - } + async clean(): Promise<TestOutcome> { + await super.clean(); + await this.execAsync('yarn cache clean'); + return TestOutcome.ok('clean'); + }test-ui/src/store/testDurationSelectors.ts (1)
146-155: Phase order assumption is fragile.The code relies on
Object.keys(test.phases)returning phases in execution order, but object key order isn't guaranteed to match the logical phase sequence. The comment states "the order is not interesting," but it actually matters for calculating remaining phases correctly.Consider using an explicit phase order array for reliability:
+const PHASE_ORDER: PhaseName[] = [ + "clean", + "setVersion", + "install", + "test", + "build", + "verify", +]; + runningTests.forEach((test) => { if (!test.currentPhase || !test.phases) { return; } - const phaseNames = Object.keys(test.phases) as PhaseName[]; + const phaseNames = PHASE_ORDER; const currentPhaseIndex = phaseNames.indexOf(test.currentPhase);test-ui/server/server.ts (1)
47-64: Consider sharing type definitions with the client.
Phase,Phases, andPhaseNameare duplicated here and intest-ui/src/types.ts. If these drift apart, it could cause runtime mismatches between server responses and client expectations.Consider extracting shared types to a common module (e.g.,
test-ui/shared/types.ts) that both server and client can import.test-ui/src/App.tsx (5)
356-379: Potential stale closure in interval callback.The interval callback captures
globalStartTimein its closure. WhensetGlobalStartTime(Date.now())is called on line 369, the interval from the previous render still uses the oldnullvalue until the effect re-runs.Consider using a ref to avoid the stale closure:
+ const globalStartTimeRef = useRef<number | null>(null); const [globalElapsedTime, setGlobalElapsedTime] = useState(0); - const [globalStartTime, setGlobalStartTime] = useState<number | null>(null); useEffect(() => { const hasRunningOrQueued = runningTestsList.length > 0 || Object.keys(queuedTests).length > 0; if (!hasRunningOrQueued) { setGlobalElapsedTime(0); - setGlobalStartTime(null); + globalStartTimeRef.current = null; return; } - if (globalStartTime === null) { - setGlobalStartTime(Date.now()); + if (globalStartTimeRef.current === null) { + globalStartTimeRef.current = Date.now(); } const interval = setInterval(() => { - if (globalStartTime !== null) { - setGlobalElapsedTime(Date.now() - globalStartTime); + if (globalStartTimeRef.current !== null) { + setGlobalElapsedTime(Date.now() - globalStartTimeRef.current); } }, 100); return () => clearInterval(interval); - }, [runningTestsList.length, queuedTests, globalStartTime]); + }, [runningTestsList.length, queuedTests]);
381-386: Missingdispatchin effect dependencies.The effect calls functions that use
dispatch, but it's not listed in the dependency array. While this works becausedispatchis stable, ESLint'sreact-hooks/exhaustive-depsrule may warn about this.useEffect(() => { loadTests(); loadPersistedResultsFromStorage(); loadRechartsVersions(); loadPersistedPackingDirectory(); - }, []); + }, [dispatch]);Or suppress the warning if intentional:
// eslint-disable-next-line react-hooks/exhaustive-deps
542-576: Consider improving the directory selection UX.The File System Access API's
showDirectoryPickerdoesn't provide the full path, so prompting the user to manually enter it defeats much of the convenience.Since the server runs locally, consider:
- Just using a text input (as already implemented as a fallback)
- Or implementing a server-side directory browser endpoint
The current approach works but may confuse users who expect the picker to populate the path automatically.
243-246: Avoidanytype assertion.Using
(status as any).positionbypasses type safety. Since this is a queued status, use the proper type:- {status.status === "queued" - ? `Queued (#${(status as any).position})` + {status.status === "queued" && "position" in status + ? `Queued (#${status.position})` : status.status}Or use a type guard function to narrow the type properly.
325-870: App component is functional but consider splitting.The App component is quite large (~550 lines). Consider extracting:
- Version/package selection controls into a separate component
- The queue info section into a separate component
This would improve maintainability without being urgent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (60)
.github/workflows/integ.yml(2 hunks).run/Template Vitest.run.xml(1 hunks)README.md(1 hunks)apps-3rd-party/app-react16/package.json(1 hunks)apps-3rd-party/app-react17/package.json(2 hunks)apps-3rd-party/app-react18/package.json(1 hunks)apps-3rd-party/app-react19/package.json(1 hunks)integrations/tanstack-start-basic/package.json(2 hunks)integrations/tanstack-start-basic/src/routeTree.gen.ts(1 hunks)list.js(1 hunks)run-test.sh(1 hunks)scripts/Controller.js(0 hunks)scripts/NpmController.js(0 hunks)scripts/TestResult.js(0 hunks)scripts/YarnController.js(0 hunks)scripts/replacePackageVersion.js(0 hunks)scripts/run.js(0 hunks)scripts/run.mts(1 hunks)scripts/tgzFileNameToPackageJsonReference.js(0 hunks)start-ui.sh(1 hunks)test-ui/.gitignore(1 hunks)test-ui/CHANGES.md(1 hunks)test-ui/CHECKLIST.md(1 hunks)test-ui/DEVELOPMENT.md(1 hunks)test-ui/QUICKSTART.md(1 hunks)test-ui/README.md(1 hunks)test-ui/REFACTORING.md(1 hunks)test-ui/SCREENSHOTS.md(1 hunks)test-ui/SUMMARY.md(1 hunks)test-ui/UI_STRUCTURE.md(1 hunks)test-ui/index.html(1 hunks)test-ui/nodemon.json(1 hunks)test-ui/package.json(1 hunks)test-ui/server/scripts/Controller.ts(1 hunks)test-ui/server/scripts/NpmController.ts(1 hunks)test-ui/server/scripts/TestOutcome.ts(1 hunks)test-ui/server/scripts/YarnController.ts(1 hunks)test-ui/server/scripts/replacePackageVersion.ts(1 hunks)test-ui/server/scripts/test-registry.ts(1 hunks)test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts(1 hunks)test-ui/server/server.ts(1 hunks)test-ui/server/tsconfig.json(1 hunks)test-ui/server/tsconfig.tsbuildinfo(1 hunks)test-ui/src/App.css(1 hunks)test-ui/src/App.tsx(1 hunks)test-ui/src/PhaseOutput.css(1 hunks)test-ui/src/PhaseOutput.tsx(1 hunks)test-ui/src/index.css(1 hunks)test-ui/src/main.tsx(1 hunks)test-ui/src/store/hooks.ts(1 hunks)test-ui/src/store/store.ts(1 hunks)test-ui/src/store/testDurationSelectors.ts(1 hunks)test-ui/src/store/testsSlice.ts(1 hunks)test-ui/src/store/websocketMiddleware.ts(1 hunks)test-ui/src/types.ts(1 hunks)test-ui/test/store/exampleTestResults.ts(1 hunks)test-ui/test/store/testDurationSelectors.spec.ts(1 hunks)test-ui/test/store/testsSlice.spec.ts(1 hunks)test-ui/tsconfig.json(1 hunks)test-ui/vite.config.ts(1 hunks)
💤 Files with no reviewable changes (7)
- scripts/tgzFileNameToPackageJsonReference.js
- scripts/Controller.js
- scripts/run.js
- scripts/replacePackageVersion.js
- scripts/YarnController.js
- scripts/NpmController.js
- scripts/TestResult.js
🧰 Additional context used
🧬 Code graph analysis (16)
test-ui/src/main.tsx (1)
test-ui/src/store/store.ts (1)
store(9-18)
test-ui/test/store/testsSlice.spec.ts (2)
test-ui/src/types.ts (2)
Phase(17-23)TestRun(36-45)test-ui/src/store/store.ts (3)
store(9-18)RootState(20-20)rootReducer(5-7)
test-ui/src/PhaseOutput.tsx (1)
test-ui/src/types.ts (3)
PhaseName(34-34)Phases(25-32)Phase(17-23)
test-ui/server/scripts/replacePackageVersion.ts (2)
scripts/run-everything.js (2)
version(11-11)fs(3-3)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)
scripts/run.mts (4)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)test-ui/server/scripts/NpmController.ts (1)
NpmController(4-70)test-ui/server/scripts/YarnController.ts (1)
YarnController(27-172)test-ui/server/scripts/test-registry.ts (2)
TestMetadata(12-32)getTestMetadata(204-206)
test-ui/server/scripts/NpmController.ts (2)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)scripts/run-everything.js (1)
output(14-31)
test-ui/src/store/testsSlice.ts (1)
test-ui/src/types.ts (4)
Test(1-10)TestRun(36-45)Phases(25-32)PhaseName(34-34)
test-ui/src/store/websocketMiddleware.ts (1)
test-ui/src/store/store.ts (1)
store(9-18)
test-ui/src/App.tsx (3)
test-ui/src/types.ts (5)
Test(1-10)TestStatus(52-52)PhaseName(34-34)TestRun(36-45)Phases(25-32)test-ui/src/store/hooks.ts (2)
useAppDispatch(4-4)useAppSelector(5-5)test-ui/src/store/testDurationSelectors.ts (4)
selectQueuedTestsETA(100-116)selectRunningTestsETA(123-188)selectElapsedTimeForRunningTestPhases(44-93)selectAllRunningTests(13-18)
test-ui/server/scripts/Controller.ts (4)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)scripts/run-everything.js (2)
fs(3-3)version(11-11)test-ui/server/scripts/replacePackageVersion.ts (1)
replacePackageVersion(4-32)test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts (1)
tgzFileNameToPackageJsonReference(3-5)
test-ui/test/store/testDurationSelectors.spec.ts (3)
test-ui/src/types.ts (1)
Phase(17-23)test-ui/src/store/store.ts (3)
RootState(20-20)store(9-18)rootReducer(5-7)test-ui/src/store/testDurationSelectors.ts (5)
selectAllTestResults(23-28)selectElapsedTimeForRunningTestPhases(44-93)selectAllRunningTests(13-18)selectQueuedTestsETA(100-116)selectRunningTestsETA(123-188)
test-ui/server/server.ts (5)
test-ui/src/types.ts (3)
Phases(25-32)PhaseName(34-34)Phase(17-23)test-ui/server/scripts/test-registry.ts (2)
getAllTests(223-241)getTestMetadata(204-206)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)test-ui/server/scripts/NpmController.ts (1)
NpmController(4-70)test-ui/server/scripts/YarnController.ts (1)
YarnController(27-172)
test-ui/server/scripts/YarnController.ts (2)
test-ui/server/scripts/Controller.ts (1)
execAsync(36-45)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)
test-ui/src/store/hooks.ts (1)
test-ui/src/store/store.ts (2)
AppDispatch(21-21)RootState(20-20)
test-ui/src/store/testDurationSelectors.ts (2)
test-ui/src/store/store.ts (1)
RootState(20-20)test-ui/src/types.ts (2)
TestRun(36-45)PhaseName(34-34)
test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts (1)
test-ui/server/scripts/Controller.ts (1)
tgzFileNameToPackageJsonReference(64-66)
🪛 actionlint (1.7.9)
.github/workflows/integ.yml
61-61: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🪛 LanguageTool
test-ui/SCREENSHOTS.md
[grammar] ~92-~92: Use a hyphen to join words.
Context: ...────────────────────────────┘ ``` - Full width gradient background - Large center...
(QB_NEW_EN_HYPHEN)
README.md
[grammar] ~50-~50: Ensure spelling is correct
Context: ...ersion ### Pack and run with local recharts bash ./pack-and-run.sh ```
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
test-ui/REFACTORING.md
[style] ~390-~390: ‘exactly the same’ might be wordy. Consider a shorter alternative.
Context: ...codebase The app functionality remains exactly the same - all features work as before, now with...
(EN_WORDINESS_PREMIUM_EXACTLY_THE_SAME)
🪛 markdownlint-cli2 (0.18.1)
test-ui/DEVELOPMENT.md
5-5: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/UI_STRUCTURE.md
5-5: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
62-62: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/CHANGES.md
9-9: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
10-10: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
17-17: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
18-18: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
19-19: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
20-20: Unordered list indentation
Expected: 2; Actual: 5
(MD007, ul-indent)
21-21: Unordered list indentation
Expected: 2; Actual: 5
(MD007, ul-indent)
22-22: Unordered list indentation
Expected: 2; Actual: 5
(MD007, ul-indent)
23-23: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
26-26: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
27-27: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
28-28: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
29-29: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
30-30: Unordered list indentation
Expected: 0; Actual: 3
(MD007, ul-indent)
test-ui/CHECKLIST.md
7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
87-87: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
97-97: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
110-110: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
135-135: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
README.md
14-14: Bare URL used
(MD034, no-bare-urls)
test-ui/REFACTORING.md
77-77: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
185-185: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/SUMMARY.md
10-10: Bare URL used
(MD034, no-bare-urls)
75-75: Bare URL used
(MD034, no-bare-urls)
76-76: Bare URL used
(MD034, no-bare-urls)
🔇 Additional comments (38)
.run/Template Vitest.run.xml (1)
3-6: Template paths are correctly configured and verified.All referenced paths in the Vitest run configuration exist and are properly configured:
- ✓
test-ui/vite.config.tsexists- ✓
vitestis declared intest-ui/package.jsondependencies- ✓ Directory structure matches template expectations
The template will work consistently across the team after running
npm installorpnpm installin the test-ui directory to populate node_modules..github/workflows/integ.yml (1)
57-57: Verify Node.js version matrix change.Line 57 updates the matrix from
[20.x, 22.x, 24.x]to[22.18.x, 24.x, 25.x], removing Node 20.x support. Confirm this is intentional, as it may be a breaking change for users running the tests on older Node versions.If this removal is intentional, consider documenting it in a migration guide or PR description. If accidental, restore the 20.x version or update to a newer patch of 20.x (e.g.,
20.14.x).test-ui/server/tsconfig.json (1)
1-12: LGTM!The TypeScript composite configuration is properly set up for a Node.js/ESM server environment. The compiler options (
nodenextmodule,esnexttarget, verbatim module syntax) align well with the modern TypeScript/ESM setup. The inclusion of../../scripts/allows sharing types across the test infrastructure.test-ui/server/tsconfig.tsbuildinfo (1)
1-1: Verify generated artifact handling.The
tsconfig.tsbuildinfofile is a generated TypeScript incremental build artifact. Consider whether this should be committed to the repository or added to.gitignore. Generally, build artifacts are excluded, but for composite builds, some teams prefer committing them for faster CI builds.Confirm the intended treatment:
- If committing: Ensure it's regenerated locally before commits to avoid stale references.
- If ignoring: Add
test-ui/server/tsconfig.tsbuildinfoto the root.gitignore(if not already covered by a pattern).Current
test-ui/.gitignoreat line 28 hasserver.js, so build artifacts may already be intended to be ignored.test-ui/tsconfig.json (1)
1-24: LGTM!The TypeScript configuration is well-tuned for a modern React 19 + Vite project. Strict mode is enabled, JSX is configured for React 17+, and the bundler module resolution aligns with Vite. The linting options (
noUnusedLocals,noUnusedParameters,noFallthroughCasesInSwitch) will catch common issues early.test-ui/CHECKLIST.md (1)
1-142: Documentation looks comprehensive and well-organized.The checklist clearly documents implementation status, known limitations, and future considerations. The unchecked items under "Testing Verification" serve as a useful reminder for manual QA before finalizing the feature.
list.js (1)
30-39: I'll help verify the concerns raised in this review comment about requiring a.tsfile directly from CommonJS. Let me investigate howlist.jsis executed and whether there's a TypeScript loader configured.
<function_calls>
#!/bin/bashFind list.js and understand its location
fd -type f -name "list.js" | head -20
</function_calls>Now let me search for how
list.jsis invoked in the repository:
<function_calls>
#!/bin/bashSearch for references to list.js in package.json, shell scripts, and CI configs
echo "=== In package.json ==="
rg -n "list.js" package.json 2>/dev/null || echo "No matches in package.json"echo -e "\n=== In .github workflows ==="
find .github -type f ( -name ".yml" -o -name ".yaml" ) -exec grep -l "list.js" {} ; 2>/dev/null || echo "No matches found"echo -e "\n=== In shell/bash scripts ==="
rg -n "list.js" --type sh --type bash 2>/dev/null || echo "No matches found"echo -e "\n=== Direct node invocations ==="
rg -n "node.*list" --type sh --type yaml --type json 2>/dev/null || echo "No matches found"
</function_calls>Now let me check for TypeScript configuration and loaders:
<function_calls>
#!/bin/bashCheck for TypeScript configuration
echo "=== tsconfig.json ==="
cat tsconfig.json 2>/dev/null || echo "No tsconfig.json found"echo -e "\n=== Check for ts-node or tsx configuration ==="
rg -n "ts-node|tsx|register" package.json tsconfig.json .mocharc.* 2>/dev/null || echo "No TS loader config found"echo -e "\n=== Check for node --loader or --require setup ==="
rg -n "--loader|--require" package.json .github/ 2>/dev/null || echo "No loader/require flags found"
</function_calls>Let me find and examine the test-registry.ts file:
<function_calls>
#!/bin/bashFind test-registry.ts
echo "=== Looking for test-registry.ts ==="
fd -type f -name "test-registry.ts"echo -e "\n=== Looking for test-registry in any form ==="
fd -type f "test-registry"
</function_calls>Let me check the actual list.js file to see its full context:
<function_calls>
#!/bin/bashFind list.js and check its content
fd -type f -name "list.js" -exec wc -l {} ;
echo -e "\n=== list.js content ==="
fd -type f -name "list.js" -exec cat -n {} ;
</function_calls>Let me search for how this list.js is being used:
<function_calls>
#!/bin/bashSearch for references to list.js more broadly
echo "=== All references to list.js ==="
rg "list.js" -n 2>/dev/null | head -30echo -e "\n=== References to require paths with test-registry ==="
rg "test-registry" -n 2>/dev/null | head -30
</function_calls>Let me check if there's a mocha or test configuration that might be relevant:
<function_calls>
#!/bin/bashCheck mocha configuration
echo "=== Mocha config files ==="
fd -type f -name ".mocharc*" -o -name "mocha.opts"echo -e "\n=== Test scripts in package.json ==="
rg -A 5 '"test":|"mocha":' package.json 2>/dev/null || echo "No test scripts found"
</function_calls><function_calls>
#!/bin/bashMore aggressive search - look at all mocha config
cat .mocharc.* 2>/dev/null || echo "No .mocharc files"
echo -e "\n=== Check package.json for mocha/test config ==="
python3 << 'PYEOF'
import json
try:
with open('package.json', 'r') as f:
data = json.load(f)
if 'mocha' in data:
print("Mocha config:", json.dumps(data['mocha'], indent=2))
if 'scripts' in data and 'test' in data['scripts']:
print("Test script:", data['scripts']['test'])
except Exception as e:
print(f"Error: {e}")
PYEOF
</function_calls><function_calls>
#!/bin/bashFind where test-ui/server/scripts/test-registry might be
echo "=== Looking for test-ui directory ==="
fd -type d "test-ui"echo -e "\n=== Looking for test-registry in any form ==="
fd "test-registry"echo -e "\n=== Looking for server/scripts directory ==="
fd -type d "scripts" | grep server
</function_calls><function_calls>
#!/bin/bashCheck directory structure around test files
echo "=== Repository structure ==="
git ls-files | grep -E "(test|spec)" | head -30
</function_calls><function_calls>
#!/bin/bashLook for all .ts files to understand TypeScript presence
echo "=== TypeScript files in repo ==="
fd -type f -name "*.ts" | head -30echo -e "\n=== Check if tsx or ts-node is a dependency ==="
rg '"tsx"|"ts-node"' package.json package-lock.json yarn.lock 2>/dev/null || echo "Not found in lock files"
</function_calls><function_calls>
#!/bin/bashSearch for getAllTestNames function to understand what it is
echo "=== Looking for getAllTestNames ==="
rg -n "getAllTestNames" --type ts --type js 2>/dev/nullecho -e "\n=== Broader search ==="
rg -n "getAllTestNames" 2>/dev/null
</function_calls><function_calls>
#!/bin/bashCheck the actual mocha configuration to see if it uses require hooks
echo "=== Searching for register hooks or require patterns ==="
rg -n "register|require.*tsx|require.*ts-node" . --type js --type json 2>/dev/null | head -20
</function_calls><function_calls>
#!/bin/bashLook at the dev dependencies for TS support
echo "=== Check devDependencies for TS tools ==="
python3 << 'PYEOF'
import json
try:
with open('package.json', 'r') as f:
data = json.load(f)
dev_deps = data.get('devDependencies', {})
ts_related = {k: v for k, v in dev_deps.items() if any(x in k.lower() for x in ['tsx', 'ts-node', 'typescript', '@types'])}
if ts_related:
print("TypeScript-related devDependencies:")
for k, v in sorted(ts_related.items()):
print(f" {k}: {v}")
else:
print("No TypeScript-related devDependencies found")# Also show all dependencies print("\nAll dependencies:") for k, v in sorted(data.get('dependencies', {}).items()): print(f" {k}: {v}")except Exception as e:
print(f"Error: {e}")
PYEOF
</function_calls><function_calls>
#!/bin/bashSearch for how Node is invoked in CI
echo "=== CI configurations ==="
find .github/workflows -name ".yml" -o -name ".yaml" | xargs grep -l "node" 2>/dev/null | head -5 | xargs cat 2>/dev/null | grep -A 5 -B 5 "node " | head -50
</function_calls><function_calls>
#!/bin/bashGet a comprehensive view - search in all files for how list.js or test scripts are used
echo "=== All mentions of 'list' in config files ==="
rg "list" .github/ package.json --type json --type yaml -n 2>/dev/null | head -30
</function_calls><function_calls>
#!/bin/bashLook at the directory containing list.js
echo "=== Finding list.js and its directory ==="
LIST_FILE=$(fd -type f -name "list.js" | head -1)
if [ -n "$LIST_FILE" ]; then
DIR=$(dirname "$LIST_FILE")
echo "list.js location: $LIST_FILE"
echo "Directory contents:"
ls -la "$DIR" 2>/dev/null | head -20echo -e "\n=== list.js full content ===" cat -n "$LIST_FILE"else
echo "list.js not found"
fi
</function_calls><function_calls>
#!/bin/bashSearch for mocha configuration that might use ts-node or tsx
echo "=== Full mocha configuration (if any) ==="
ls -la | grep mochaecho -e "\n=== Check if mocha uses require option ==="
cat .mocharc.json 2>/dev/null || cat .mocharc.js 2>/dev/null || cat .mocharc.yaml 2>/dev/null || echo "No .mocharc file found"echo -e "\n=== Check test script ==="
grep -A 2 '"test"' package.json | head -10
</function_calls>Let me try a different approach - look at git info to understand the PR context:
<function_calls>
#!/bin/bashCheck git log for recent changes to list.js
echo "=== Recent changes to list.js ==="
git log --oneline -10 -- "*list.js" 2>/dev/null || echo "No git history available"echo -e "\n=== Git diff for list.js ==="
git diff HEAD~1 HEAD -- "*list.js" 2>/dev/null | head -100 || echo "No diff available"echo -e "\n=== Current branch ==="
git branch --show-current 2>/dev/null || echo "Cannot determine branch"
</function_calls><function_calls>
#!/bin/bashTry to find any .ts or .js file that might have been added/modified
echo "=== Modified TypeScript files ==="
git status --short 2>/dev/null | grep -E ".(ts|js)$" || echo "No modified files"echo -e "\n=== Show all TypeScript/JS files in repo ==="
git ls-files 2>/dev/null | grep -E ".(ts|js)$" | sort | head -50
</function_calls><function_calls>
#!/bin/bashGet a sense of what's actually in this repository
echo "=== Repository root contents ==="
ls -la | head -30echo -e "\n=== All JavaScript/TypeScript files ==="
find . -maxdepth 3 -type f ( -name ".js" -o -name ".ts" ) 2>/dev/null | head -40echo -e "\n=== package.json name and type ==="
cat package.json | python3 -c "import json, sys; data=json.load(sys.stdin); print('Name:', data.get('name')); print('Type:', data.get('type')); print('Main:', data.get('main'))"
</function_calls>I'm encountering limitations in accessing the repository files needed to verify this review comment. While the review references specific code in
list.js(lines 30-39) that attempts torequire()a TypeScript file, I cannot locate these files in the current sandbox environment.Let me try one more comprehensive search:
<function_calls>
#!/bin/bashComprehensive search for any file containing "test-registry" or "getAllTestNames"
echo "=== Searching entire repo for test-registry ==="
rg "test-registry" . 2>/dev/null | head -20 || echo "No matches found"echo -e "\n=== Searching for getAllTestNames ==="
rg "getAllTestNames" . 2>/dev/null | head -20 || echo "No matches found"echo -e "\n=== All .js files in repo ==="
find . -name "*.js" -type f 2>/dev/null | head -40
</function_calls>Based on my investigation, I cannot locate the
list.jsfile ortest-registry.tsfile mentioned in the review comment within the accessible repository structure. This prevents me from verifying the actual concern about requiring a TypeScript file from CommonJS.However, I can still provide an assessment based on the technical concerns raised:
The core concern is technically valid: Requiring
.tsfiles directly from CommonJS does require a TypeScript loader (tsx, ts-node, etc.) to be configured. Without verification of the actual runtime configuration, I cannot definitively determine if the implementation will work.**Cannot verify—requires manual review of runtime environment configuration** The concern about requiring `.ts` files directly from CommonJS is technically sound. Standard Node.js cannot load TypeScript without a loader. This requires verification that: 1. The repository has `tsx`, `ts-node`, or equivalent configured 2. The Mocha or test runner invokes `list.js` with appropriate TS support 3. The `test-registry.ts` file exists at the expected path Without access to the actual files in this PR, manual verification of the runtime setup is necessary. Check: - Mocha configuration (`.mocharc.*` or `package.json`) - Test script invocation in `package.json` - Dependencies for TypeScript loaders - Whether `test-ui/server/scripts/test-registry.ts` exists and exports `getAllTestNames`run-test.sh (1)
30-30: Node.js 22.18.x natively supports running.mtsfiles with type-stripping enabled by default.The CI workflow tests on Node.js 22.18.x, 24.x, and 25.x—all versions that have built-in TypeScript type-stripping support. The
run.mtsfile uses only erasable type annotations (import type, type annotations) that do not require additional TypeScript transformation. No--experimental-strip-typesflag,tsx, orts-nodeis needed.test-ui/src/index.css (1)
1-23: Global styling baseline looks solidReset, font stack, and layout defaults are clean and appropriate for the new UI. No changes needed.
test-ui/src/main.tsx (1)
1-17: React/Redux bootstrap is correct and defensiveEntrypoint correctly uses
createRoot, wraps the app inProviderandStrictMode, and guards against a missing#rootelement. With the HTML entry fixed to referencemain.tsx, this should work as expected.start-ui.sh (1)
1-26: Startup script is straightforward and fits the workflowThe script cleanly handles
cdintotest-ui, lazy installs dependencies, and delegates tonpm startwithset -efor safety. Looks good as-is.test-ui/test/store/testsSlice.spec.ts (1)
1-53: testsSlice persistence/clear behavior is well coveredThe spec cleanly asserts initial state, loading of persisted results, and clearing a specific test result via
clearTestResult. Store setup viaconfigureStore({ reducer: rootReducer })is appropriate for this unit test.test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts (1)
1-5: Tarball reference helper is concise and appropriateHelper cleanly constructs a
file:reference from an absolute path and file name, which aligns with its usage inController.tgzFileNameToPackageJsonReference.test-ui/vite.config.ts (1)
8-12: Consider adding WebSocket proxy support.The README and QUICKSTART mention WebSocket connections to
ws://localhost:3001for real-time updates. If the frontend connects to WebSocket via the Vite dev server (e.g.,/api/ws), you'll needws: truein the proxy config. If the frontend connects directly tows://localhost:3001, this is fine as-is.proxy: { '/api': { target: 'http://localhost:3001', changeOrigin: true, + ws: true, }, },test-ui/QUICKSTART.md (1)
52-57: LGTM!The quick-start guide is comprehensive and well-organized. The troubleshooting table, keyboard shortcuts, and example workflows provide excellent developer guidance.
test-ui/package.json (1)
9-9: Verify Node.js version requirement for native TypeScript execution.Running
node server/server.tsdirectly requires Node.js 22+ with native TypeScript support (via--experimental-strip-types). Consider adding anenginesfield to enforce this requirement and prevent confusion for contributors using older Node versions."license": "ISC", "description": "", + "engines": { + "node": ">=22.0.0" + },test-ui/src/PhaseOutput.css (1)
1-90: LGTM!Well-structured CSS with clear state classes, appropriate transitions, and good contrast for accessibility. The dark theme for output content and light theme for headers provide good visual separation.
test-ui/src/PhaseOutput.tsx (2)
23-41: LGTM on initialization logic.The cascading logic for determining initial expanded phase (explicit prop → current phase → last non-pending) is well thought out and handles all states appropriately.
138-167: LGTM on render logic.Good use of conditional rendering, dynamic class composition, and inline styles for the progress gradient. The accessibility of clickable elements is appropriate with cursor changes based on
hasOutput.test-ui/src/store/hooks.ts (1)
1-5: Typed hooks look good
useAppDispatch/useAppSelectorare correctly typed againstAppDispatch/RootStateand will keep component usage type-safe. No changes needed.test-ui/src/store/store.ts (1)
1-21: Store configuration is idiomatic
configureStoresetup,rootReducershape, serializableCheck overrides, andRootState/AppDispatchexports are all consistent and should work well with the rest of the test UI.test-ui/src/types.ts (1)
1-52: Type definitions look consistent and usable across the UIThe
Test,Phase*, andTestRun/QueuedTestshapes are coherent and match how the slice and UI are modeled; nothing blocking here.test-ui/server/scripts/Controller.ts (1)
11-73: Controller base class looks solid and cohesiveThe shared
clean,execAsync, env shaping, and package.json helpers provide a clear, reusable foundation for NPM/Yarn controllers. The abstraction and API surface look appropriate.test-ui/server/scripts/test-registry.ts (1)
38-155: Test registry structure and helpers look correctThe direct and library test definitions plus
getTestMetadata/getAllTestNames/getAllTests/isStableTestcover the expected queries and are much clearer than ad‑hoc string parsing. No blocking issues seen.Also applies to: 169-249
test-ui/src/store/testDurationSelectors.ts (5)
1-6: LGTM!Imports and the private
selectQueueLengthselector are correctly defined.
13-28: LGTM!Clean memoized selectors with proper readonly typing to prevent accidental mutations.
30-93: LGTM!The selector correctly calculates average phase durations from completed tests and gracefully falls back to educated guesses when historical data is unavailable.
100-116: LGTM!Clean ETA calculation for queued tests.
159-187: LGTM!The remaining time calculation logic correctly handles the current phase (subtracting elapsed time) and future phases (using full estimates), with proper fallbacks for missing
startTime.test-ui/server/server.ts (3)
1-28: LGTM!Server setup with Express, WebSocket, and necessary middleware is properly configured.
89-103: LGTM!WebSocket connection management and broadcast function are properly implemented with readyState checks.
469-495: LGTM!Queue processing logic with
setImmediateis well-designed for non-blocking serial execution.test-ui/src/App.tsx (6)
1-29: LGTM!Imports are well-organized and correctly reference the store hooks and selectors.
33-45: LGTM!Clean duration formatting utility.
91-123: LGTM!The elapsed time tracking effect properly cleans up the interval and handles the running test state correctly.
450-463: LGTM!Version sorting correctly handles semantic versioning with numeric comparison.
501-506: LGTM!Clean implementation that queues selected tests and clears selection.
60-323: TestItem component is well-structured.The component handles multiple states (running, completed, queued) cleanly with proper conditional rendering and progress tracking.
| }, | ||
| "dependencies": { | ||
| "my-charts": "file:../my-charts-replace-with-tgz-file-before-running", | ||
| "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-v0.1.0.tgz", |
There was a problem hiding this comment.
Remove hardcoded absolute paths — repository is not portable.
Same issue as the other app-react packages: the absolute path is machine-specific. Additionally, app-react17 points to my-charts-react19 instead of react17, suggesting a copy-paste error.
Replace with a portable reference:
- "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-v0.1.0.tgz",
+ "my-charts": "file:../my-charts",Verify the correct my-charts version mapping for React 17.
📝 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.
| "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-v0.1.0.tgz", | |
| "my-charts": "file:../my-charts", |
🤖 Prompt for AI Agents
In apps-3rd-party/app-react17/package.json around line 14, the dependency
"my-charts" uses a hardcoded absolute file path and incorrectly points to
my-charts-react19; replace the absolute path with a portable reference (e.g., a
versioned semver entry or a relative path inside the repo such as
"../libraries/my-charts-react17/...") and ensure the package points to the React
17 build (my-charts-react17) with the correct version number; update the
dependency string accordingly and verify the mapped version matches the
published/package.json version for the React 17 artifact.
| }, | ||
| "dependencies": { | ||
| "my-charts": "file:../my-charts-replace-with-tgz-file-before-running", | ||
| "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-0.1.0.tgz", |
There was a problem hiding this comment.
Remove hardcoded absolute paths — repository is not portable.
The dependency uses an absolute file path specific to your machine (/Users/pavelvanecek/...). Additionally, app-react18 references my-charts-react19 instead of the React 18 equivalent, which may be unintentional.
Replace with a portable reference:
- "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-0.1.0.tgz",
+ "my-charts": "file:../my-charts",Verify that the correct my-charts variant for React 18 is being referenced.
📝 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.
| "my-charts": "file:/Users/pavelvanecek/github/recharts-integ/libraries/my-charts-react19/my-charts-react19-0.1.0.tgz", | |
| "my-charts": "file:../my-charts", |
🤖 Prompt for AI Agents
In apps-3rd-party/app-react18/package.json around line 14, replace the hardcoded
absolute file dependency "file:/Users/pavelvanecek/..." with a portable
reference (either a relative file path to the packaged tarball inside the repo,
e.g. "file:../libraries/my-charts-react18/my-charts-react18-0.1.0.tgz", or
preferably a published semver dependency like "my-charts": "0.1.0"), and ensure
the dependency points to the React 18 build (my-charts-react18) instead of
my-charts-react19.
| async pack(): Promise<string> { | ||
| const output = await this.execAsync('yarn pack --json'); | ||
| const trimmed = output.trim(); | ||
| try { | ||
| const jsonOutput: YarnPackJson = JSON.parse(trimmed); | ||
| if (jsonOutput.type === 'success' && jsonOutput.data) { | ||
| const match = jsonOutput.data.match(/"(.+\.tgz)"/); | ||
| if (match) { | ||
| return `file:${match[1]}`; | ||
| } | ||
| throw new Error(`Could not extract tgz path from: ${jsonOutput.data}`); | ||
| } else { | ||
| throw new Error(`Unexpected output format from yarn pack: ${trimmed}`); | ||
| } | ||
| } catch (error: any) { | ||
| console.error(`Error parsing yarn pack output: ${error.message}`); | ||
| throw error; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the full YarnController.ts file to understand context and imports
cat test-ui/server/scripts/YarnController.tsRepository: recharts/recharts-integ
Length of output: 6220
🏁 Script executed:
# Search for pack() method usage and tests
rg -n "pack\(\)" test-ui/server/scripts/ -A 3 -B 3
# Also search for any test files related to YarnController
fd -name "*test*" -o -name "*spec*" | grep -i yarnRepository: recharts/recharts-integ
Length of output: 1633
🏁 Script executed:
# Let's check the yarn documentation and actual output format by searching the codebase
# for any comments or documentation about yarn pack
rg -i "yarn.*pack" test-ui/ -n -B 2 -A 2Repository: recharts/recharts-integ
Length of output: 3301
🏁 Script executed:
# Search for YarnPackJson usage to understand how it's used
rg "YarnPackJson" test-ui/ -n -B 3 -A 3Repository: recharts/recharts-integ
Length of output: 1131
🌐 Web query:
yarn pack --json output format multi-line
💡 Result:
Do you mean (A) how Yarn's yarn pack --json emits JSON events that can contain multi-line fields, (B) how to produce/format multi-line JSON output from yarn pack --json, or (C) how to parse those multi-line fields reliably? Which one do you want?
🌐 Web query:
yarn pack --json documentation output format examples
💡 Result:
Short answer
- Yarn's --json mode emits a JSON-stream (NDJSON): one JSON object per line with a "type" and "data" payload (same pattern used by other yarn commands). [1][2]
Typical / example lines you may see from yarn pack --json
-
informational progress or warnings:
{"type":"info","data":"Running prepack script"}
{"type":"warning","data":"package.json: No license field"} [3] -
pack success (example — actual field names vary by command/version):
{"type":"success","data":{"filename":"package.tgz","size":12345,"hash":"sha1:..."}} -
table-style output (some yarn commands use this shape):
{"type":"table","data":{"head":["Name","Version"],"body":[["mypkg","1.2.3"]]}} [3]
Notes
- Yarn v1 (Classic) docs show pack behaviour but not a formal JSON schema; Yarn v2+ documents that --json produces an NDJSON stream. Interpretations of "data" vary by command/version, so inspect a real run or pipe through jq to confirm fields for your yarn version. [1][2][3]
Sources
- Yarn Classic docs: yarn pack. [1]
- Yarn v2+ source/docs: --json emits NDJSON / JSON-stream. [2]
- Examples of yarn --json objects (table/info/warning) from tooling docs and wrappers. [3]
References
[1] https://classic.yarnpkg.com/lang/en/docs/cli/pack/
[2] (Yarn v2+ CLI source/discussion showing --json => NDJSON).
[3] Examples of yarn --json objects (info/table/warning) in tooling/docs.
🌐 Web query:
yarn pack --json success data structure filename field
💡 Result:
Short answer: yarn pack --json emits NDJSON "success" events whose data is the human-readable message (e.g. "Wrote tarball to "...""); there is not a separate parsed filename field in the JSON — you must parse the message string to get the path. [1][2]
Sources:
- yarn pack docs (describes --json NDJSON output). [1]
- Yarn messages (packWroteTarball → "Wrote tarball to $0.") — the pack message is a formatted string, not a JSON object with a filename property. [2]
🏁 Script executed:
# Let's search for any existing usage or tests that might show actual yarn output
rg "Wrote tarball" test-ui/ -B 2 -A 2Repository: recharts/recharts-integ
Length of output: 49
🏁 Script executed:
# Also check if there are any version specifications for yarn in the repo
find test-ui -name "package.json" -o -name ".yarnrc" -o -name "yarn.lock" | head -5Repository: recharts/recharts-integ
Length of output: 87
🏁 Script executed:
# Check yarn version in package.json
cat test-ui/package.json | head -50Repository: recharts/recharts-integ
Length of output: 1077
Based on my verification, the review comment is accurate and identifies a critical bug. Here's what I confirmed:
- Yarn's
--jsonoutput is NDJSON (one JSON object per line), not a single JSON blob - The success event's
datafield is a formatted string message like"Wrote tarball to \"path.tgz\""with quotes around the path - The current implementation will fail because:
JSON.parse(trimmed)on multi-line output will throw or only parse the first line- Even if it gets the success line, the regex pattern and logic expect a different format
- The proposed fix correctly addresses these issues by:
- Splitting output by newlines and parsing each line individually
- Iterating to find the success entry (skipping info/warning lines)
- Using the appropriate regex pattern for the actual message format
Fix yarn pack --json parsing to handle real NDJSON output
The current pack() implementation assumes yarn pack --json returns a single JSON object, but Yarn emits NDJSON (one JSON object per line with type and data fields). Additionally, the success event's data field is a formatted string message (e.g., "Wrote tarball to \"path.tgz\""), not a bare path. As written, JSON.parse(trimmed) will fail on multi-line output, and the regex pattern will not match the actual message format.
- async pack(): Promise<string> {
- const output = await this.execAsync('yarn pack --json');
- const trimmed = output.trim();
- try {
- const jsonOutput: YarnPackJson = JSON.parse(trimmed);
- if (jsonOutput.type === 'success' && jsonOutput.data) {
- const match = jsonOutput.data.match(/"(.+\.tgz)"/);
- if (match) {
- return `file:${match[1]}`;
- }
- throw new Error(`Could not extract tgz path from: ${jsonOutput.data}`);
- } else {
- throw new Error(`Unexpected output format from yarn pack: ${trimmed}`);
- }
- } catch (error: any) {
- console.error(`Error parsing yarn pack output: ${error.message}`);
- throw error;
- }
- }
+ async pack(): Promise<string> {
+ const output = await this.execAsync('yarn pack --json');
+ const lines = output.trim().split('\n').filter(Boolean);
+
+ for (const line of lines) {
+ try {
+ const entry: YarnPackJson = JSON.parse(line);
+ if (entry.type === 'success' && entry.data) {
+ const match = entry.data.match(/(\S+\.tgz)/);
+ if (match) {
+ // If Yarn returns a relative path, resolve it from the project root
+ const tgzPath = path.isAbsolute(match[1])
+ ? match[1]
+ : path.join(this.absolutePath, match[1]);
+ return `file:${tgzPath}`;
+ }
+ throw new Error(`Could not extract tgz path from: ${entry.data}`);
+ }
+ } catch {
+ // Ignore non‑JSON or irrelevant lines
+ }
+ }
+
+ throw new Error(`Could not find success entry in 'yarn pack' output:\n${output}`);
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In test-ui/server/scripts/YarnController.ts around lines 79 to 97, the code
treats yarn pack --json as a single JSON blob and uses an incorrect regex,
causing parsing failures; update pack() to split the output by newline, parse
each non-empty line as JSON, iterate to find the entry with type === 'success',
extract its data string (which is a quoted/formatted message like "Wrote tarball
to \"path.tgz\""), unquote or unescape the string and then extract the .tgz
filename (e.g. with a regex matching Wrote tarball to \"(.+\.tgz)\" or by
removing surrounding quotes and searching for .tgz), return it as file:${path},
and throw a clear error if no success entry or no path is found while preserving
the original error handling.
| const packProcess = spawn( | ||
| "bash", | ||
| [ | ||
| "-c", | ||
| ` | ||
| cd "${expandedDirectory}" && \ | ||
| npm run build && \ | ||
| npm pack | tail -n 1 | ||
| `, | ||
| ], | ||
| { | ||
| cwd: rootDir, | ||
| env: { ...process.env }, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Command injection vulnerability in directory path.
The expandedDirectory is interpolated directly into a shell command without sanitization. A malicious directory path like "; rm -rf / # or $(malicious_command) could execute arbitrary commands.
Consider using execFile with an array of arguments instead of spawn("bash", ["-c", ...]), or validate/sanitize the path:
+const sanitizePath = (dir: string): string => {
+ // Only allow alphanumeric, slashes, dashes, underscores, dots, and tildes
+ if (!/^[a-zA-Z0-9/_\-.\s~]+$/.test(dir)) {
+ throw new Error("Invalid directory path characters");
+ }
+ return dir;
+};
+
app.post("/api/pack", (req: Request, res: Response) => {
const { directory } = req.body;
if (!directory) {
return res.status(400).json({ error: "Directory path is required" });
}
+ try {
+ sanitizePath(directory);
+ } catch (err) {
+ return res.status(400).json({ error: (err as Error).message });
+ }Alternatively, use child_process.execFile with explicit arguments to avoid shell interpretation.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In test-ui/server/server.ts around lines 604 to 618, the code injects
expandedDirectory directly into a shell string which allows command injection;
replace the shell invocation with a non-shell child process call (use
child_process.execFile or spawn with shell: false) and pass the command and
arguments as an array, set cwd to the expandedDirectory (not interpolated into a
shell command), and capture stdout to get the last line of npm pack;
additionally validate or normalize expandedDirectory (e.g. ensure it is an
absolute path inside the expected workspace and does not contain shell
metacharacters) before using it as cwd.
There was a problem hiding this comment.
This application only runs on one's own machine so the only thing an attacker can attack is their own computer. We don't deploy this anywhere.
There was a problem hiding this comment.
@PavelVanecek, that's a fair point—since this is a local development tool running only on your own machine, the security risk is indeed minimal. The primary concern with command injection is when untrusted users can interact with the service, which isn't the case here.
I understand and accept your decision to keep the code as-is given this context. If the tool's scope ever changes or the code is adapted for other uses, it would be worth revisiting, but for now it's reasonable to prioritize development velocity for internal tooling.
| const websocketMiddleware: Middleware = (store) => { | ||
| let ws: WebSocket | null = null; | ||
|
|
||
| const connect = () => { | ||
| ws = new WebSocket(WS_URL); | ||
|
|
||
| ws.onopen = () => { | ||
| console.log('WebSocket connected'); | ||
| }; | ||
|
|
||
| ws.onmessage = (event) => { | ||
| try { | ||
| const message: WebSocketMessage = JSON.parse(event.data); | ||
| handleMessage(message, store.dispatch); | ||
| } catch (error) { | ||
| console.error('Failed to parse WebSocket message:', error); | ||
| } | ||
| }; | ||
|
|
||
| ws.onerror = (error) => { | ||
| console.error('WebSocket error:', error); | ||
| }; | ||
|
|
||
| ws.onclose = () => { | ||
| console.log('WebSocket disconnected, reconnecting...'); | ||
| setTimeout(connect, 3000); | ||
| }; | ||
| }; | ||
|
|
||
| const handleMessage = (message: WebSocketMessage, dispatch: any) => { | ||
| const { type, data } = message; | ||
|
|
||
| switch (type) { | ||
| case 'test-queued': | ||
| dispatch(testQueued({ | ||
| testName: data.testName, | ||
| id: data.id, | ||
| position: data.position, | ||
| })); | ||
| break; | ||
|
|
||
| case 'test-started': | ||
| dispatch(testStarted({ | ||
| testName: data.testName, | ||
| id: data.id, | ||
| })); | ||
| break; | ||
|
|
||
| case 'test-output': | ||
| dispatch(testOutput({ | ||
| id: data.id, | ||
| output: data.output, | ||
| phases: data.phases, | ||
| currentPhase: data.currentPhase, | ||
| })); | ||
| break; | ||
|
|
||
| case 'test-error': | ||
| dispatch(testError({ | ||
| id: data.id, | ||
| error: data.error, | ||
| })); | ||
| break; | ||
|
|
||
| case 'test-completed': | ||
| dispatch(testCompleted({ | ||
| id: data.id, | ||
| status: data.status, | ||
| exitCode: data.exitCode, | ||
| })); | ||
| // Move to results after delay | ||
| setTimeout(() => { | ||
| const state = store.getState(); | ||
| const testName = Object.keys(state.tests.runningTests).find( | ||
| (name: string) => state.tests.runningTests[name].id === data.id | ||
| ); | ||
| if (testName) { | ||
| dispatch(moveToResults(testName)); | ||
| } | ||
| }, 1000); | ||
| break; | ||
|
|
||
| case 'queue-cleared': | ||
| dispatch(queueCleared()); | ||
| break; | ||
|
|
||
| default: | ||
| console.log('Unknown WebSocket message type:', type); | ||
| } | ||
| }; | ||
|
|
||
| // Initialize connection | ||
| connect(); | ||
|
|
||
| return (next) => (action) => { | ||
| return next(action); | ||
| }; | ||
| }; |
There was a problem hiding this comment.
Guard WebSocket usage and harden test-completed handling
Two robustness concerns here:
- Environment guard
connect()is invoked when the middleware is created, which happens during store creation. Ifstoreis imported in a non‑browser environment whereWebSocketis undefined (e.g., node/Vitest without a DOM env or SSR), this will throw atnew WebSocket(WS_URL). Consider guarding:
- const connect = () => {
- ws = new WebSocket(WS_URL);
+ const connect = () => {
+ if (typeof WebSocket === 'undefined') {
+ console.warn('WebSocket is not available in this environment; skipping connection.');
+ return;
+ }
+ ws = new WebSocket(WS_URL);- Safer lookup in
test-completedpath
If the tests slice ever stops keeping completed tests inrunningTests, this lookup can throw onstate.tests.runningTests[name].id. Using optional chaining avoids a hard crash:
- const testName = Object.keys(state.tests.runningTests).find(
- (name: string) => state.tests.runningTests[name].id === data.id
- );
+ const testName = Object.keys(state.tests.runningTests).find(
+ (name: string) =>
+ state.tests.runningTests[name]?.id === data.id,
+ );With these tweaks the middleware will fail gracefully in unsupported environments and be more resilient to future slice changes.
🤖 Prompt for AI Agents
In test-ui/src/store/websocketMiddleware.ts around lines 19 to 116, the
middleware calls connect() unconditionally and assumes WebSocket and
runningTests entries exist; change it to only initialize the socket when
WebSocket is available (e.g., if (typeof globalThis !== 'undefined' && typeof
globalThis.WebSocket !== 'undefined') connect();), and inside the
'test-completed' handler make the runningTests lookup defensive using optional
chaining when comparing ids and guard the moveToResults dispatch (e.g., derive
testName by checking state.tests?.runningTests?.[name]?.id === data.id and only
dispatch if testName is truthy). Ensure no WebSocket construction occurs in
non-browser environments.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
test-ui/src/store/testsSlice.ts (1)
109-134: Consider a secondary index for id-to-testName lookups.The
testOutput,testError, andtestCompletedreducers all iterate throughObject.keys(state.runningTests)to find a test by id. This is O(n) per event, and output events can be frequent during test execution.For a modest number of concurrent tests this is acceptable, but if scalability becomes a concern, consider maintaining a
runningTestsById: Record<string, string>(id → testName) index.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps-3rd-party/app-react17/package.json(1 hunks)integrations/tanstack-start-basic/package.json(2 hunks)test-ui/.vite/deps/_metadata.json(1 hunks)test-ui/.vite/deps/package.json(1 hunks)test-ui/index.html(1 hunks)test-ui/server/scripts/NpmController.ts(1 hunks)test-ui/server/scripts/replacePackageVersion.ts(1 hunks)test-ui/server/server.ts(1 hunks)test-ui/src/store/testsSlice.ts(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- apps-3rd-party/app-react17/package.json
- test-ui/.vite/deps/package.json
- test-ui/.vite/deps/_metadata.json
🚧 Files skipped from review as they are similar to previous changes (3)
- test-ui/index.html
- test-ui/server/scripts/NpmController.ts
- test-ui/server/scripts/replacePackageVersion.ts
🧰 Additional context used
🧬 Code graph analysis (1)
test-ui/src/store/testsSlice.ts (1)
test-ui/src/types.ts (4)
Test(1-10)TestRun(36-45)Phases(25-32)PhaseName(34-34)
🪛 GitHub Actions: Recharts integration tests
integrations/tanstack-start-basic/package.json
[error] Process completed with exit code 1.
🔇 Additional comments (3)
integrations/tanstack-start-basic/package.json (1)
18-18: Recharts version "^3.0.0" is correct and consistent across all integrations—no issue found.The dependency uses a pinned caret range
"^3.0.0"(not "latest"), which is consistent with all other integrations in the repository (tanstack-start-basic, ts-react16, ts-react18, ts-react19, ts-react17, and ts4-react17 all specify the same version). The NPM registry confirms both 3.0.0 and 3.0.1 exist, and^3.0.0will accept compatible updates. The past review's recommendation for"^3.0.1"does not align with the codebase's standardization strategy. The pipeline failure mentioned is unrelated to this version constraint and requires separate investigation.test-ui/src/store/testsSlice.ts (1)
1-223: Well-structured Redux slice with comprehensive test lifecycle management.The slice correctly handles the full test lifecycle (queued → running → completed → results) with proper phase tracking. Good use of Immer for mutable-style updates and clean separation of concerns across reducers. The queue size tracking logic in
testQueuedand reset logic intestCompletedproperly handles batch tracking across multiple test additions.test-ui/server/server.ts (1)
89-103: Well-designed WebSocket broadcast pattern and queue architecture.The broadcast function correctly checks
readyStatebefore sending, and the overall architecture with serial queue processing viasetImmediate(line 494) properly yields to the event loop between tests. The cancellation flow withshouldCancelQueueflag is cleanly integrated acrossrunPhase,processQueue, andexecuteTest.
| const runLibraryInLibraryTest = ( | ||
| libController: Controller, | ||
| appController: Controller, | ||
| version: string, | ||
| ) => { | ||
| return async (testData: TestData) => { | ||
| await runPhase("clean", testData, async () => { | ||
| await libController.clean(); | ||
| return await appController.clean(); | ||
| }); | ||
|
|
||
| await runPhase("setVersion", testData, async () => { | ||
| libController.replacePackageJsonVersion("recharts", version); | ||
| await libController.install(); | ||
| await libController.test(); | ||
| await libController.build(); | ||
| await verifyAllSingleDependencyVersions(libController, testData); | ||
| const myChartsTgzFile = await libController.pack(); | ||
| appController.replacePackageJsonVersion("my-charts", myChartsTgzFile); | ||
| return TestOutcome.ok("setVersion"); | ||
| }); | ||
|
|
||
| await runPhase( | ||
| "install", | ||
| testData, | ||
| async () => await appController.install(), | ||
| ); | ||
| if (testData.phases.install.status === "failed") { | ||
| return; | ||
| } | ||
|
|
||
| await runPhase("test", testData, async () => await appController.test()); | ||
| await runPhase( | ||
| "build", | ||
| testData, | ||
| async () => await appController.build(), | ||
| ); | ||
| await runPhase( | ||
| "verify", | ||
| testData, | ||
| async () => | ||
| await verifyAllSingleDependencyVersions(appController, testData), | ||
| ); | ||
| }; |
There was a problem hiding this comment.
runLibraryInLibraryTest swallows errors during setVersion phase.
The setVersion phase (lines 299-308) runs multiple operations (install, test, build, verifyAllSingleDependencyVersions, pack) but always returns TestOutcome.ok("setVersion") regardless of whether intermediate steps failed. Errors thrown by these calls will propagate and fail the phase, but if they return failure outcomes (like verifyAllSingleDependencyVersions can), those are silently ignored.
Consider checking return values:
await runPhase("setVersion", testData, async () => {
- libController.replacePackageJsonVersion("recharts", version);
+ await libController.replacePackageJsonVersion("recharts", version);
await libController.install();
await libController.test();
await libController.build();
- await verifyAllSingleDependencyVersions(libController, testData);
+ const verifyResult = await verifyAllSingleDependencyVersions(libController, testData);
+ if (!verifyResult.success) {
+ return verifyResult;
+ }
const myChartsTgzFile = await libController.pack();
appController.replacePackageJsonVersion("my-charts", myChartsTgzFile);
return TestOutcome.ok("setVersion");
});📝 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.
| const runLibraryInLibraryTest = ( | |
| libController: Controller, | |
| appController: Controller, | |
| version: string, | |
| ) => { | |
| return async (testData: TestData) => { | |
| await runPhase("clean", testData, async () => { | |
| await libController.clean(); | |
| return await appController.clean(); | |
| }); | |
| await runPhase("setVersion", testData, async () => { | |
| libController.replacePackageJsonVersion("recharts", version); | |
| await libController.install(); | |
| await libController.test(); | |
| await libController.build(); | |
| await verifyAllSingleDependencyVersions(libController, testData); | |
| const myChartsTgzFile = await libController.pack(); | |
| appController.replacePackageJsonVersion("my-charts", myChartsTgzFile); | |
| return TestOutcome.ok("setVersion"); | |
| }); | |
| await runPhase( | |
| "install", | |
| testData, | |
| async () => await appController.install(), | |
| ); | |
| if (testData.phases.install.status === "failed") { | |
| return; | |
| } | |
| await runPhase("test", testData, async () => await appController.test()); | |
| await runPhase( | |
| "build", | |
| testData, | |
| async () => await appController.build(), | |
| ); | |
| await runPhase( | |
| "verify", | |
| testData, | |
| async () => | |
| await verifyAllSingleDependencyVersions(appController, testData), | |
| ); | |
| }; | |
| const runLibraryInLibraryTest = ( | |
| libController: Controller, | |
| appController: Controller, | |
| version: string, | |
| ) => { | |
| return async (testData: TestData) => { | |
| await runPhase("clean", testData, async () => { | |
| await libController.clean(); | |
| return await appController.clean(); | |
| }); | |
| await runPhase("setVersion", testData, async () => { | |
| await libController.replacePackageJsonVersion("recharts", version); | |
| await libController.install(); | |
| await libController.test(); | |
| await libController.build(); | |
| const verifyResult = await verifyAllSingleDependencyVersions(libController, testData); | |
| if (!verifyResult.success) { | |
| return verifyResult; | |
| } | |
| const myChartsTgzFile = await libController.pack(); | |
| appController.replacePackageJsonVersion("my-charts", myChartsTgzFile); | |
| return TestOutcome.ok("setVersion"); | |
| }); | |
| await runPhase( | |
| "install", | |
| testData, | |
| async () => await appController.install(), | |
| ); | |
| if (testData.phases.install.status === "failed") { | |
| return; | |
| } | |
| await runPhase("test", testData, async () => await appController.test()); | |
| await runPhase( | |
| "build", | |
| testData, | |
| async () => await appController.build(), | |
| ); | |
| await runPhase( | |
| "verify", | |
| testData, | |
| async () => | |
| await verifyAllSingleDependencyVersions(appController, testData), | |
| ); | |
| }; |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
scripts/run.mts (1)
40-64: Library controller outcomes are discarded; only app controller results are returned.This issue was flagged in a previous review and remains unaddressed. Lines 41-46 execute operations on
libController, but outcomes are not captured inresults. If any library step fails, the failure is silently ignored.test-ui/server/scripts/YarnController.ts (1)
79-97:yarn pack --jsonparsing is broken - outputs NDJSON, not single JSON.This issue was flagged in a previous review and remains unaddressed. Yarn's
--jsonmode emits NDJSON (one JSON object per line), not a single JSON blob.JSON.parse(trimmed)will fail on multi-line output.test-ui/server/server.ts (1)
303-311: Verification failure is ignored insetVersionphase.The result from
verifyAllSingleDependencyVersionsat line 308 is not checked. If verification fails, the phase still returnsTestOutcome.ok("setVersion"), masking the failure.Apply this diff:
await runPhase("setVersion", testData, async () => { libController.replacePackageJsonVersion("recharts", version); await libController.install(); await libController.test(); await libController.build(); - await verifyAllSingleDependencyVersions(libController, testData); + const verifyResult = await verifyAllSingleDependencyVersions(libController, testData); + if (!verifyResult.success) { + return verifyResult; + } const myChartsTgzFile = await libController.pack(); appController.replacePackageJsonVersion("my-charts", myChartsTgzFile); return TestOutcome.ok("setVersion"); });
🧹 Nitpick comments (13)
.github/workflows/integ.yml (1)
23-26: Setup node before checkout may cause issues.The
actions/setup-nodestep runs beforeactions/checkout. While this works forlist.jsexecution, it's unconventional. Typically, checkout comes first. Consider reordering for consistency with thebuild-and-testjob.steps: - - name: Setup node.js - uses: actions/setup-node@v6 - with: - node-version: 24.x - name: Checkout Integration Tests Repository uses: actions/checkout@v4 with: repository: recharts/recharts-integ + - name: Setup node.js + uses: actions/setup-node@v6 + with: + node-version: 24.xtest-ui/SCREENSHOTS.md (1)
87-91: Add language specifier to fenced code blocks for consistency.Several ASCII diagram code blocks are missing the
textlanguage specifier, unlike the first block at line 7. This affects lines 87, 97, 110, 135, 225, 236, 249, and 262. Addingtextensures consistent rendering and resolves markdownlint warnings.-``` +```text ┌─────────────────────────────────────────┐ │ 🧪 Recharts Integration Test Runner │ └─────────────────────────────────────────┘Apply the same fix to the other blocks at lines 97, 110, 135, 225, 236, 249, and 262.
test-ui/UI_STRUCTURE.md (1)
16-58: Addtextlanguage specifier to ASCII diagram blocks.Code blocks at lines 16 and 62 are ASCII art diagrams but lack the
textspecifier used at line 5. This causes markdownlint warnings.-``` +```text ┌────────────────────────────────────────────────────────────────────┐test-ui/server/scripts/replacePackageVersion.ts (1)
12-12: Consider adding type safety for package.json structure.The
packageJsonis typed asRecord<string, unknown>, butdependenciesandpeerDependenciesare accessed without type narrowing. While this works at runtime, adding type guards would improve type safety.- let packageJson: Record<string, unknown>; + interface PackageJsonDeps { + dependencies?: Record<string, string>; + peerDependencies?: Record<string, string>; + } + let packageJson: PackageJsonDeps;test-ui/src/PhaseOutput.tsx (2)
94-120: Consider reducing interval frequency for better performance.The interval runs every 100ms to update elapsed times. With many tests visible, this could cause frequent re-renders. Consider using 250ms or 500ms for a balance between responsiveness and performance.
- }, 100); + }, 250);
154-156: Duration display uses final duration, not live elapsed time.Line 156 displays
phase.duration(from server) rather thanphaseElapsedTimes[phaseName]. For running phases, this will show-until the phase completes. If live elapsed time display is desired during running phases, consider:- <span className="phase-duration">{formatDuration(phase.duration)}</span> + <span className="phase-duration"> + {formatDuration(phase.status === 'running' ? phaseElapsedTimes[phaseName] : phase.duration)} + </span>test-ui/REFACTORING.md (1)
185-199: Consider adding a language specifier to the fenced code block.The message flow diagram code block at line 185 lacks a language specifier. While this is a diagram rather than code, adding
textas the language would satisfy linters and improve consistency.-``` +```text WebSocket Server ↓ onmessagetest-ui/SUMMARY.md (2)
110-120: Add language specifier to the data flow diagram code block.The data flow diagram at line 110 lacks a language specifier.
-``` +```text User Action → React Component → API Request
243-243: Format bare URL as a proper Markdown link.The bare URL should be formatted as a clickable link for better rendering across Markdown viewers.
-2. Open browser to http://localhost:3000 +2. Open browser to <http://localhost:3000>test-ui/server/scripts/YarnController.ts (1)
99-110: Inconsistent use ofexecAsync- uses local instead of inherited method.Lines 104-109 create a new
execAsynccall with manually specified options, but the baseControllerclass already providesthis.execAsync()with the same configuration (cwd, encoding, env, maxBuffer). Consider using the inherited method for consistency.Note: The current approach works but duplicates the execution setup logic.
try { - const result = await execAsync(command, { - encoding: 'utf-8', - cwd: this.absolutePath, - env: this.getEnv(), - maxBuffer: 10 * 1024 * 1024, - }); - rawOutput = result.stdout; + rawOutput = await this.execAsync(command); } catch (error: any) {test-ui/src/App.tsx (1)
288-288: Replaceas anywith proper type assertion.The
as anytype assertion bypasses type checking. SinceexpandedPhaseisstring | nullandPhaseOutputexpectsPhaseName | null, use a proper type guard or assertion.Apply this diff:
- initialExpandedPhase={expandedPhase as any} + initialExpandedPhase={expandedPhase as PhaseName | null}Also applies to: 308-308
test-ui/server/server.ts (2)
85-89: Consider implementing AbortSignal support for cleaner cancellation.The
currentTestAbortControlleris declared but unused because controllers don't yet acceptAbortSignal. Implementing proper abort signal propagation would allow more immediate cancellation than relying on theshouldCancelQueueflag checked between phases.
47-64: Extract duplicated types to a shared module.The types
Phase,Phases, andPhaseNameare duplicated intest-ui/src/types.ts. Consider creating a shared types module (e.g.,test-ui/shared/types.ts) to ensure consistency and reduce maintenance burden.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
.github/workflows/integ.yml(2 hunks).run/Template Vitest.run.xml(1 hunks)README.md(1 hunks)integrations/tanstack-start-basic/src/routeTree.gen.ts(1 hunks)scripts/run.mts(1 hunks)test-ui/.gitignore(1 hunks)test-ui/CHANGES.md(1 hunks)test-ui/DEVELOPMENT.md(1 hunks)test-ui/README.md(1 hunks)test-ui/REFACTORING.md(1 hunks)test-ui/SCREENSHOTS.md(1 hunks)test-ui/SUMMARY.md(1 hunks)test-ui/UI_STRUCTURE.md(1 hunks)test-ui/nodemon.json(1 hunks)test-ui/server/scripts/TestOutcome.ts(1 hunks)test-ui/server/scripts/YarnController.ts(1 hunks)test-ui/server/scripts/replacePackageVersion.ts(1 hunks)test-ui/server/server.ts(1 hunks)test-ui/src/App.css(1 hunks)test-ui/src/App.tsx(1 hunks)test-ui/src/PhaseOutput.tsx(1 hunks)test-ui/src/store/testDurationSelectors.ts(1 hunks)test-ui/src/store/testsSlice.ts(1 hunks)test-ui/test/store/exampleTestResults.ts(1 hunks)test-ui/test/store/testDurationSelectors.spec.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
- test-ui/test/store/testDurationSelectors.spec.ts
- test-ui/nodemon.json
- test-ui/README.md
- test-ui/server/scripts/TestOutcome.ts
- test-ui/src/store/testDurationSelectors.ts
- test-ui/.gitignore
- .run/Template Vitest.run.xml
- test-ui/test/store/exampleTestResults.ts
- test-ui/DEVELOPMENT.md
🧰 Additional context used
🧬 Code graph analysis (7)
test-ui/server/scripts/replacePackageVersion.ts (2)
scripts/run-everything.js (2)
version(11-11)fs(3-3)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)
test-ui/src/App.tsx (3)
test-ui/src/types.ts (5)
Test(1-10)TestStatus(52-52)PhaseName(34-34)TestRun(36-45)Phases(25-32)test-ui/src/store/hooks.ts (2)
useAppDispatch(4-4)useAppSelector(5-5)test-ui/src/store/testDurationSelectors.ts (4)
selectQueuedTestsETA(100-116)selectRunningTestsETA(123-192)selectElapsedTimeForRunningTestPhases(44-93)selectAllRunningTests(13-18)
scripts/run.mts (4)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)test-ui/server/scripts/NpmController.ts (1)
NpmController(4-99)test-ui/server/scripts/YarnController.ts (1)
YarnController(27-172)test-ui/server/scripts/test-registry.ts (2)
TestMetadata(12-32)getTestMetadata(204-206)
test-ui/src/PhaseOutput.tsx (1)
test-ui/src/types.ts (3)
PhaseName(34-34)Phases(25-32)Phase(17-23)
test-ui/src/store/testsSlice.ts (2)
test-ui/src/types.ts (4)
Test(1-10)TestRun(36-45)Phases(25-32)PhaseName(34-34)test-ui/server/scripts/NpmController.ts (1)
test(14-21)
test-ui/server/server.ts (5)
test-ui/src/types.ts (3)
Phases(25-32)PhaseName(34-34)Phase(17-23)test-ui/server/scripts/test-registry.ts (2)
getAllTests(223-241)getTestMetadata(204-206)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)test-ui/server/scripts/NpmController.ts (1)
NpmController(4-99)test-ui/server/scripts/YarnController.ts (1)
YarnController(27-172)
test-ui/server/scripts/YarnController.ts (2)
test-ui/server/scripts/Controller.ts (1)
execAsync(36-45)test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-46)
🪛 LanguageTool
README.md
[grammar] ~50-~50: Ensure spelling is correct
Context: ...ersion ### Pack and run with local recharts bash ./pack-and-run.sh ```
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
test-ui/REFACTORING.md
[style] ~390-~390: ‘exactly the same’ might be wordy. Consider a shorter alternative.
Context: ...codebase The app functionality remains exactly the same - all features work as before, now with...
(EN_WORDINESS_PREMIUM_EXACTLY_THE_SAME)
🪛 markdownlint-cli2 (0.18.1)
test-ui/UI_STRUCTURE.md
16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
62-62: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/SUMMARY.md
110-110: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
243-243: Bare URL used
(MD034, no-bare-urls)
test-ui/REFACTORING.md
185-185: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/SCREENSHOTS.md
87-87: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
97-97: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
110-110: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
135-135: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
225-225: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
236-236: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
249-249: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
262-262: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (14)
integrations/tanstack-start-basic/src/routeTree.gen.ts (1)
1-9: This file was correctly auto-generated by the TanStack Router vite plugin—no manual editing occurred.The
routeTree.gen.tsfile is properly regenerated as part of the PR's TypeScript/ESM migration. The vite.config.ts includes thetanstackStart()plugin from@tanstack/react-start, which automatically generates this file based on your route structure. The changes at lines 366-373 add module augmentation for@tanstack/react-startwith SSR and router type registration—standard for TanStack Start projects. No action needed; this regeneration is expected and correct.README.md (1)
3-54: Well-structured documentation for the new Test UI.The README provides clear quick start instructions, a comprehensive feature list, and maintains backward compatibility by documenting existing CLI usage. The emoji usage for visual categorization is helpful.
test-ui/UI_STRUCTURE.md (1)
97-103: Clear and actionable documentation of UI behaviors.The key behaviors section accurately describes the state management logic implemented in
PhaseOutput.tsx, including auto-expand behavior and session persistence. This aligns well with the implementation.test-ui/server/scripts/replacePackageVersion.ts (2)
14-21: Error handling for file read operations is properly implemented.The try-catch block correctly captures read/parse failures and returns a structured
TestOutcome.failwith descriptive error messages.
53-61: Error handling for file write operations is properly implemented.The write operation is wrapped in try-catch and returns appropriate
TestOutcomeresults, addressing the concerns from previous reviews.test-ui/src/PhaseOutput.tsx (3)
23-41: Well-structured initialization logic for expanded phases.The lazy initialization handles three cases appropriately: explicit initial phase, current running phase, and completed tests (last non-pending). The use of
slice().reverse()avoids mutating the constant array.
104-114: Elapsed time only updates for currentPhase when running.For phases that have
startTimebut noendTimeand aren't thecurrentPhase, the elapsed time won't be tracked. This seems intentional (only animate the current phase), but worth noting for clarity.
132-137: Progress calculation correctly caps at 100%.Good use of
Math.minto prevent the progress bar from exceeding 100% when actual duration exceeds the estimate..github/workflows/integ.yml (1)
63-63:actions/setup-node@v6is a valid released version and is currently the latest major version.Verification confirms that v6.0.0 and v6 tags exist in the actions/setup-node repository. Your knowledge cutoff (March 2025) predates the v6 release. The code uses the current latest stable version, so no action is needed.
test-ui/src/App.css (1)
1-585: LGTM!The stylesheet is well-structured with logical grouping, good use of transitions, and proper focus-visible states for accessibility. The color-coded status indicators and phase styling provide clear visual feedback.
test-ui/src/store/testsSlice.ts (1)
1-226: LGTM!The Redux slice is well-structured with proper TypeScript types, clear action names, and appropriate state management patterns. The queue tracking logic correctly handles initialQueueSize and completedTestsCount. The previous setTimeout issue has been addressed.
test-ui/CHANGES.md (1)
1-60: LGTM!Clear and well-organized documentation of the UI changes. The feature descriptions align with the implementation details in the related component files.
test-ui/server/scripts/YarnController.ts (1)
27-77: LGTM for clean, install, test, and build methods.These methods have proper error handling, appropriate use of
TestOutcome.ok()andTestOutcome.fail(), and handle edge cases (e.g., missing test script gracefully returns success instead of failure).test-ui/src/App.tsx (1)
543-579: LGTM! Pragmatic approach for directory selection.The fallback to
prompt()for obtaining the full path is a reasonable workaround for the File System Access API's security limitations in a local development context.
| matrix: | ||
| # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | ||
| node-version: [ 20.x, 22.x, 24.x ] | ||
| node-version: [ 22.18.x, 24.x, 25.x ] |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Node.js version 25.x stability release status November 2025
💡 Result:
Short answer: Node.js 25.x is a Current (non‑LTS) release — first published Oct 15, 2025 — actively receiving Current‑line updates; latest patch in the 25.x stream is v25.2.1 (Nov 17, 2025). Its Current branch end-of-bugfix and end-of-security dates are listed as Apr 1, 2026 and Jun 1, 2026 respectively. [1][2][3]
Sources:
- Node.js v25.0.0 release notes (Oct 15, 2025). [1]
- nodejs/node GitHub releases (v25.2.1, Nov 17, 2025). [2]
- Node.js Release WG schedule (25.x = Current; EoBF Apr 1, 2026; EoSF Jun 1, 2026). [3]
🌐 Web query:
Node.js LTS versions 22.x 24.x support timeline November 2025
💡 Result:
Summary (as of Nov 30, 2025)
- Node.js 22.x (codename "Jod"): Maintenance LTS (moved to Maintenance in Oct 2025). End-of-life: April 2027. [1][2]
- Node.js 24.x (codename "Krypton"): Active LTS (entered LTS in Nov 2025). Supported through end of April 2028. [1][3]
Sources:
[1] Node.js Releases (official release status table).
[2] Node.js v22.11.0 LTS blog (notes about 22.x LTS → Maintenance and EoL).
[3] Node.js migration notes (v22 → v24) — states v24 LTS updates continue through end of April 2028.
🌐 Web query:
Node.js 22.18.x current version patch available
💡 Result:
The current patch in the v22.18.x line is v22.18.0 (released July 31, 2025). [1][2]
Sources:
[1] Node.js v22.18.0 download/archive.
[2] Node.js v22.18.0 release blog post.
Use 22.x and clarify that 25.x is the Current release track, not nightly.
The matrix includes 25.x, which is Node.js's Current (non-LTS) release track, not a nightly build. While actively maintained (latest patch v25.2.1 as of November 2025), Current releases end security support June 1, 2026, making them unsuitable as primary CI targets. For 22.18.x, use the major-minor format 22.x instead; pinning to a specific patch defeats the purpose of CI testing against the release line and prevents critical patch updates.
node-version: [ 22.x, 24.x, 25.x ]
🤖 Prompt for AI Agents
In .github/workflows/integ.yml around line 57, update the matrix entry so the
first item uses the major-minor form "22.x" instead of "22.18.x" (to test the
release line rather than a specific patch); keep "24.x" and "25.x" if you want
to include Node's Current track, otherwise remove "25.x"; optionally add a brief
inline comment noting that "25.x" represents the Current (non-LTS) release track
so it is not a nightly build.
There was a problem hiding this comment.
22.18 is the version with typescript support so we need that as a minimum. 25 is the latest, I will switch it to 26 once that is current.
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Node.js 22.18 TypeScript support new feature
💡 Result:
Short summary — what’s new in Node.js 22.18 regarding TypeScript
-
Node.js v22.18.0 enables the built‑in "type stripping" TypeScript support by default (no experimental warning). This lets Node execute .ts files that use only erasable TypeScript syntax by removing type annotations at runtime. [1][2]
-
Key runtime flags and controls:
- --no-strip-types (disable built‑in stripping). [1]
- --experimental-transform-types (enable transforming non‑erasable TS features like enums/namespaces). [1][2]
-
Important limitations to know:
- No type checking — Node only strips types; use tsc or other tools for checking. [1]
- Features that require code generation (enums, namespaces, some parameter properties) need the transform flag or a transpiler. [1]
- Node ignores tsconfig.json for runtime behavior (so path aliases, emit targets, etc. are not applied). [1][2]
- Imports must use explicit extensions and type-only imports should use the type keyword to avoid runtime errors. [1]
Sources
- Node.js v22.18.0 TypeScript docs (type stripping enabled by default; flags & limitations). [1]
- Node.js API docs / TypeScript module page (history and stability notes). [2]
@PavelVanecek, thank you for the clarification! Node.js v22.18.0 indeed enables built-in TypeScript type stripping by default, making it the minimum version for native TS support without experimental warnings.
Your version strategy makes sense:
- 22.18.x: minimum version for stable TypeScript support
- 24.x: Active LTS
- 25.x: Current release track (updated to 26.x when available)
Consider adding an inline comment in the workflow to document this reasoning for future maintainers:
node-version: [ 22.18.x, 24.x, 25.x ] # 22.18+ required for native TS support✏️ Learnings added
Learnt from: PavelVanecek
Repo: recharts/recharts-integ PR: 80
File: .github/workflows/integ.yml:57-57
Timestamp: 2025-11-30T04:39:58.861Z
Learning: In .github/workflows/integ.yml, Node.js version 22.18.x is the minimum required version because it enables built-in TypeScript type stripping by default, which is essential for the test-ui TypeScript/ESM implementation.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| import type { getRouter } from './router.tsx' | ||
| declare module '@tanstack/react-start' { | ||
| interface Register { | ||
| ssr: true | ||
| router: Awaited<ReturnType<typeof getRouter>> | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify getRouter export and check for usage of removed server route exports
# 1. Check if getRouter is exported from router.tsx
echo "=== Checking for getRouter export ==="
rg -n "export.*getRouter" integrations/tanstack-start-basic/src/router.tsx
# 2. Search for any remaining references to removed server route entities
echo -e "\n=== Checking for references to removed server route entities ==="
rg -n "serverRouteTree|FileServerRoutes|RootServerRouteChildren|ServerFileRoutesByPath" \
integrations/tanstack-start-basic/src/ \
-g '!routeTree.gen.ts'
# 3. Check if @tanstack/react-start is in dependencies
echo -e "\n=== Checking @tanstack/react-start dependency ==="
cat integrations/tanstack-start-basic/package.json | jq '.dependencies["@tanstack/react-start"], .devDependencies["@tanstack/react-start"]'
# 4. Check the actual type signature of getRouter function
echo -e "\n=== Checking getRouter function signature ==="
rg -n -A 5 "export.*getRouter\s*\(" integrations/tanstack-start-basic/src/router.tsxRepository: recharts/recharts-integ
Length of output: 285
🏁 Script executed:
#!/bin/bash
# Check what's actually exported from router.tsx
echo "=== Contents of router.tsx ==="
wc -l integrations/tanstack-start-basic/src/router.tsx
# Show all exports from router.tsx
echo -e "\n=== Exports from router.tsx ==="
rg -n "^export" integrations/tanstack-start-basic/src/router.tsx
# Search for getRouter anywhere in the project
echo -e "\n=== Searching for getRouter in entire project ==="
rg -n "getRouter" integrations/tanstack-start-basic/
# Show first 50 lines of router.tsx to understand structure
echo -e "\n=== First 50 lines of router.tsx ==="
head -50 integrations/tanstack-start-basic/src/router.tsxRepository: recharts/recharts-integ
Length of output: 1151
Fix import: getRouter does not exist in router.tsx—use createRouter instead.
The module augmentation imports a non-existent getRouter function. The file exports createRouter, not getRouter. The import statement on line 367 will fail at compile time. Change:
import type { getRouter } from './router.tsx'to:
import type { createRouter } from './router.tsx'Also update line 371 to use createRouter:
router: Awaited<ReturnType<typeof createRouter>>🤖 Prompt for AI Agents
In integrations/tanstack-start-basic/src/routeTree.gen.ts around lines 367 to
373, the module augmentation imports a non-existent getRouter; replace the
import to reference the actual exported function createRouter from
'./router.tsx' and update the Register.router type to use
Awaited<ReturnType<typeof createRouter>> so the type resolves correctly at
compile time.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
597137b to
95694a1
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (3)
.github/workflows/integ.yml (1)
23-27: CI Node version matrix and setup steps look consistent with the TS/ESM toolingThe added
setup-node@v6steps and updated matrix[22.18.x, 24.x, 25.x]align with the TypeScript/ESM setup and give good coverage across minimum+LTS+current. One small suggestion: add an inline comment on the22.18.xentry explaining it’s the minimum version with built‑in TS type stripping required by the new test‑ui tooling, so future maintainers understand why that exact minor is pinned.Based on learnings, this pin is intentional due to Node 22.18’s TS support.
Also applies to: 57-65
integrations/tanstack-start-basic/src/routeTree.gen.ts (1)
367-372: Double-check thatrouter.tsxactually exportsgetRouterto match this Register typeThe ambient augmentation here assumes a
getRouterfunction exported from./router.tsx:import type { getRouter } from './router.tsx' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited<ReturnType<typeof getRouter>> } }On a previous revision,
router.tsxexportedcreateRouter, notgetRouter, which would make this type import inaccurate. If you haven’t already updatedrouter.tsx, consider either exportinggetRouterthere or switching this to use whatever factory you actually export (e.g.createRouter). Otherwise your registeredroutertype may not line up with the real router instance.scripts/run.mts (1)
40-64: Library controller outcomes are still discarded.This was flagged in a previous review but remains unaddressed. Lines 41-46 execute library steps (
clean,replacePackageJsonVersion,install,test,build,verifyAllSingleDependencyVersions) but never capture their results. If any library step fails, the failure is silently ignored and execution continues to the app phase, masking root causes.Apply the fix from the previous review to capture library outcomes:
async function runLibraryInLibraryTest(libController: Controller, appController: Controller, rechartsVersion: string): Promise<TestOutcome[]> { - await libController.clean(); - await libController.replacePackageJsonVersion("recharts", rechartsVersion); - await libController.install(); - await libController.test(); - await libController.build(); - await verifyAllSingleDependencyVersions(libController); + const results: TestOutcome[] = []; + + results.push(await libController.clean()); + results.push(await libController.replacePackageJsonVersion("recharts", rechartsVersion)); + const libInstallResult = await libController.install(); + results.push(libInstallResult); + if (!libInstallResult.success) { + console.warn('Failed to install the library. Skipping remaining steps.'); + return results; + } + results.push(await libController.test()); + results.push(await libController.build()); + results.push(...await verifyAllSingleDependencyVersions(libController)); const myChartsTgzFile = await libController.pack(); - const results: TestOutcome[] = []; - results.push(await appController.clean());
🧹 Nitpick comments (13)
README.md (1)
8-15: Optionally call out the minimum Node.js version in Quick StartGiven
list.jsand the new test UI rely on Node’s built‑in TypeScript support, consider adding a brief note here that local runs expect at least Node 22.18.x (or newer) so developers don’t hit confusing runtime errors on older Node versions.Based on learnings, this repo already standardizes on 22.18.x+ in CI for that reason.
test-ui/SCREENSHOTS.md (1)
87-266: Add a language (text) to ASCII-art code fences for markdownlintA few of the fenced blocks showing UI boxes don’t specify a language (markdownlint MD040). To keep docs lint‑clean and consistent with the earlier
```textblock, you could annotate these as```textas well.test-ui/UI_STRUCTURE.md (1)
16-82: Align fenced diagrams withtextlanguage for markdownlintThese UI layout diagrams are in fenced blocks without a language. For consistency with your other diagrams and to appease markdownlint (MD040), consider changing them to
```text.test-ui/src/store/testsSlice.ts (1)
79-158: Queue progress tracking assumes at most one running test—document or revisit if you ever add parallelismThe
initialQueueSize/completedTestsCountlogic intestQueuedandtestCompletedlooks correct for the current serial runner:
initialQueueSizeis derived fromqueuedTests + runningTestsand updated if the queue grows mid‑run.testCompletedincrementscompletedTestsCountand resets both counters whenremainingTests === 1(i.e. this is the last running test).That
remainingTests === 1reset implicitly relies on there never being more than one running test at a time. If you later introduce parallel execution for multiple tests, this heuristic will start to misfire and the progress counters will reset prematurely.I’d suggest either (a) adding a brief comment here noting the “serial only” assumption, or (b) when/if you add parallelism, switching the reset condition to something like “no queued or running tests remain after this test is moved to results.”
test-ui/src/PhaseOutput.tsx (1)
94-120: Interval cleanup is correctly implemented, but consider optimization.The interval runs every 100ms which could cause frequent re-renders. Since you're tracking elapsed time for running phases, this is reasonable for smooth progress display. The cleanup function properly clears the interval.
However, the effect has
phasesin its dependency array, which means the interval restarts whenever any phase data changes (including output strings). Consider extracting only the needed phase timing data to reduce unnecessary interval restarts.test-ui/test/store/testDurationSelectors.spec.ts (1)
119-127: Hardcoded floating-point expectations are fragile.The test uses precise floating-point values like
2682.0689655172414. If the fixture data or calculation logic changes slightly, these tests will fail even if the selector logic is correct. Consider usingtoBeCloseTo()for floating-point comparisons or computing expected values programmatically from the fixture.expect(elapsedTimes).toEqual({ - build: 2682.0689655172414, - clean: 2359.766666666667, - install: 18898.3, - setVersion: 3918.266666666667, - test: 1764.0689655172414, - verify: 357.17241379310343, + build: expect.closeTo(2682, 1), + clean: expect.closeTo(2360, 1), + install: expect.closeTo(18898, 1), + setVersion: expect.closeTo(3918, 1), + test: expect.closeTo(1764, 1), + verify: expect.closeTo(357, 1), });test-ui/src/App.css (1)
475-478: Pulse animation provides good visual feedback for running state.The animation is subtle (opacity 0.5-1) and won't cause accessibility issues for users sensitive to motion. Consider adding a
prefers-reduced-motionmedia query for users who prefer minimal animations.@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @media (prefers-reduced-motion: reduce) { .phase-summary-item.running { animation: none; } }test-ui/REFACTORING.md (1)
185-199: Add language identifier to fenced code block.The message flow diagram code block is missing a language identifier. While it's a text diagram, adding a language hint improves rendering in some markdown processors.
-``` +```text WebSocket Server ↓ onmessagetest-ui/src/store/testDurationSelectors.ts (1)
30-37: Avoid duplicating phase lists across objects
educatedGuess,phaseDurations, andaverageDurationsall hard‑code the same phase keys. Combined with thePHASE_ORDERarrays in this file and inApp.tsx, this creates several places that must be updated in lockstep when adding/removing phases.Consider extracting a single
PHASE_ORDER: PhaseName[](or similar) in a shared module and deriving these records via.reduce, so phase changes are compiler‑guided and less error‑prone.Also applies to: 52-59, 72-79
test-ui/server/scripts/test-registry.ts (1)
157-199: Consider centralizing registry sources and avoiding magic stringsThe library registry construction is clear, but a couple of small refactors could improve maintainability:
['npm', 'yarn'] as PackageManager[]and hard‑coded library/app IDs could be moved to shared constants, so consumers (e.g. server scripts, UI filters) don’t risk spelling drift.allTestsis the single source forgetAllTestNames,getAllTests, andtestRegistry; documenting that (or freezing it) would clarify that it’s not meant to be mutated at runtime.These are non‑blocking but would make future expansions (new package managers or libraries) safer.
Also applies to: 211-241
test-ui/src/App.tsx (2)
518-523: Optional: prevent duplicaterunTestcalls for already queued/running tests
runSelectedTestsunconditionally callsrunTestfor each selected test, and the per‑row “Run” button is only disabled while a test is queued (disabled={isQueued}), not while it’s already running. This makes it easy to fire multiple/tests/runrequests for the same test if a user double‑clicks or re‑selects it while it’s in flight.If the backend already deduplicates safely, this is just a UX concern; otherwise, you might consider:
- Disabling “Run” when
isRunningorisQueued, or- Filtering
selectedTestsinrunSelectedTeststo exclude tests currently inqueuedTestsorrunningTests.Non‑blocking, but it would make the UI behavior more predictable.
Also applies to: 825-831, 869-892
90-123: Centralize phase order to avoid duplicationThe phase traversal logic is duplicated across the codebase: hardcoded in App.tsx (lines 99–106 and 144–151), defined locally in testDurationSelectors.ts (line 147), and defined in PhaseOutput.tsx (line 14). To prevent drift and reduce maintenance burden, extract this to a shared constant—either export it from PhaseOutput.tsx or create a dedicated constants module—and import it in App.tsx and testDurationSelectors.ts.
test-ui/src/types.ts (1)
36-52: Optional: simplifyTestStatusdiscriminantRight now:
TestRunhas astatusfield including'queued'.QueuedTesthas nostatus.TestStatus = TestRun | QueuedTest, and consumers discriminate with"status" in status.This works, but you could simplify future usage by adding a literal
status: 'queued'toQueuedTestand narrowingTestRun['status']to omit'queued'. That would let you use a simple discriminated union onstatuswithout additional"in"checks.Non‑blocking, just a potential ergonomic improvement.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (58)
.github/workflows/integ.yml(2 hunks).run/Template Vitest.run.xml(1 hunks)README.md(1 hunks)apps-3rd-party/app-react17/package.json(1 hunks)integrations/tanstack-start-basic/package.json(2 hunks)integrations/tanstack-start-basic/src/routeTree.gen.ts(1 hunks)list.js(1 hunks)run-test.sh(1 hunks)scripts/Controller.js(0 hunks)scripts/NpmController.js(0 hunks)scripts/TestResult.js(0 hunks)scripts/YarnController.js(0 hunks)scripts/replacePackageVersion.js(0 hunks)scripts/run.js(0 hunks)scripts/run.mts(1 hunks)scripts/tgzFileNameToPackageJsonReference.js(0 hunks)start-ui.sh(1 hunks)test-ui/.gitignore(1 hunks)test-ui/.vite/deps/_metadata.json(1 hunks)test-ui/.vite/deps/package.json(1 hunks)test-ui/CHANGES.md(1 hunks)test-ui/CHECKLIST.md(1 hunks)test-ui/DEVELOPMENT.md(1 hunks)test-ui/QUICKSTART.md(1 hunks)test-ui/README.md(1 hunks)test-ui/REFACTORING.md(1 hunks)test-ui/SCREENSHOTS.md(1 hunks)test-ui/SUMMARY.md(1 hunks)test-ui/UI_STRUCTURE.md(1 hunks)test-ui/index.html(1 hunks)test-ui/nodemon.json(1 hunks)test-ui/package.json(1 hunks)test-ui/server/scripts/Controller.ts(1 hunks)test-ui/server/scripts/NpmController.ts(1 hunks)test-ui/server/scripts/TestOutcome.ts(1 hunks)test-ui/server/scripts/YarnController.ts(1 hunks)test-ui/server/scripts/replacePackageVersion.ts(1 hunks)test-ui/server/scripts/test-registry.ts(1 hunks)test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts(1 hunks)test-ui/server/server.ts(1 hunks)test-ui/server/tsconfig.json(1 hunks)test-ui/src/App.css(1 hunks)test-ui/src/App.tsx(1 hunks)test-ui/src/PhaseOutput.css(1 hunks)test-ui/src/PhaseOutput.tsx(1 hunks)test-ui/src/index.css(1 hunks)test-ui/src/main.tsx(1 hunks)test-ui/src/store/hooks.ts(1 hunks)test-ui/src/store/store.ts(1 hunks)test-ui/src/store/testDurationSelectors.ts(1 hunks)test-ui/src/store/testsSlice.ts(1 hunks)test-ui/src/store/websocketMiddleware.ts(1 hunks)test-ui/src/types.ts(1 hunks)test-ui/test/store/exampleTestResults.ts(1 hunks)test-ui/test/store/testDurationSelectors.spec.ts(1 hunks)test-ui/test/store/testsSlice.spec.ts(1 hunks)test-ui/tsconfig.json(1 hunks)test-ui/vite.config.ts(1 hunks)
💤 Files with no reviewable changes (7)
- scripts/TestResult.js
- scripts/NpmController.js
- scripts/Controller.js
- scripts/YarnController.js
- scripts/tgzFileNameToPackageJsonReference.js
- scripts/run.js
- scripts/replacePackageVersion.js
✅ Files skipped from review due to trivial changes (2)
- test-ui/QUICKSTART.md
- test-ui/src/PhaseOutput.css
🚧 Files skipped from review as they are similar to previous changes (29)
- test-ui/.gitignore
- test-ui/package.json
- run-test.sh
- test-ui/test/store/testsSlice.spec.ts
- .run/Template Vitest.run.xml
- test-ui/src/store/websocketMiddleware.ts
- test-ui/nodemon.json
- start-ui.sh
- integrations/tanstack-start-basic/package.json
- test-ui/src/store/hooks.ts
- test-ui/src/store/store.ts
- apps-3rd-party/app-react17/package.json
- test-ui/server/scripts/replacePackageVersion.ts
- test-ui/.vite/deps/_metadata.json
- test-ui/DEVELOPMENT.md
- test-ui/tsconfig.json
- test-ui/CHECKLIST.md
- test-ui/src/main.tsx
- test-ui/src/index.css
- test-ui/vite.config.ts
- test-ui/README.md
- test-ui/CHANGES.md
- test-ui/server/scripts/tgzFileNameToPackageJsonReference.ts
- test-ui/.vite/deps/package.json
- test-ui/server/tsconfig.json
- test-ui/server/server.ts
- test-ui/server/scripts/Controller.ts
- test-ui/index.html
- test-ui/server/scripts/YarnController.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: PavelVanecek
Repo: recharts/recharts-integ PR: 80
File: .github/workflows/integ.yml:57-57
Timestamp: 2025-11-30T04:39:58.861Z
Learning: In .github/workflows/integ.yml, Node.js version 22.18.x is the minimum required version because it enables built-in TypeScript type stripping by default, which is essential for the test-ui TypeScript/ESM implementation.
📚 Learning: 2025-11-30T04:39:58.861Z
Learnt from: PavelVanecek
Repo: recharts/recharts-integ PR: 80
File: .github/workflows/integ.yml:57-57
Timestamp: 2025-11-30T04:39:58.861Z
Learning: In .github/workflows/integ.yml, Node.js version 22.18.x is the minimum required version because it enables built-in TypeScript type stripping by default, which is essential for the test-ui TypeScript/ESM implementation.
Applied to files:
.github/workflows/integ.yml
🧬 Code graph analysis (8)
test-ui/test/store/testDurationSelectors.spec.ts (3)
test-ui/src/types.ts (1)
Phase(17-23)test-ui/src/store/store.ts (3)
RootState(20-20)store(9-18)rootReducer(5-7)test-ui/src/store/testDurationSelectors.ts (4)
selectAllTestResults(23-28)selectElapsedTimeForRunningTestPhases(44-93)selectAllRunningTests(13-18)selectRunningTestsETA(123-192)
test-ui/src/PhaseOutput.tsx (1)
test-ui/src/types.ts (3)
PhaseName(34-34)Phases(25-32)Phase(17-23)
test-ui/server/scripts/NpmController.ts (2)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-52)scripts/run-everything.js (1)
output(14-31)
list.js (2)
scripts/run-everything.js (2)
require(1-1)require(2-2)test-ui/server/scripts/test-registry.ts (1)
getAllTestNames(211-218)
test-ui/src/store/testDurationSelectors.ts (2)
test-ui/src/store/store.ts (1)
RootState(20-20)test-ui/src/types.ts (2)
TestRun(36-45)PhaseName(34-34)
scripts/run.mts (4)
test-ui/server/scripts/TestOutcome.ts (1)
TestOutcome(18-52)test-ui/server/scripts/NpmController.ts (1)
NpmController(4-99)test-ui/server/scripts/YarnController.ts (1)
YarnController(27-172)test-ui/server/scripts/test-registry.ts (2)
TestMetadata(12-32)getTestMetadata(204-206)
test-ui/src/App.tsx (3)
test-ui/src/types.ts (5)
Test(1-10)TestStatus(52-52)PhaseName(34-34)TestRun(36-45)Phases(25-32)test-ui/src/store/hooks.ts (2)
useAppDispatch(4-4)useAppSelector(5-5)test-ui/src/store/testDurationSelectors.ts (4)
selectQueuedTestsETA(100-116)selectRunningTestsETA(123-192)selectElapsedTimeForRunningTestPhases(44-93)selectAllRunningTests(13-18)
test-ui/src/store/testsSlice.ts (2)
test-ui/src/types.ts (4)
Test(1-10)TestRun(36-45)Phases(25-32)PhaseName(34-34)test-ui/server/scripts/NpmController.ts (1)
test(14-21)
🪛 LanguageTool
README.md
[grammar] ~50-~50: Ensure spelling is correct
Context: ...ersion ### Pack and run with local recharts bash ./pack-and-run.sh ```
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
test-ui/REFACTORING.md
[style] ~390-~390: ‘exactly the same’ might be wordy. Consider a shorter alternative.
Context: ...codebase The app functionality remains exactly the same - all features work as before, now with...
(EN_WORDINESS_PREMIUM_EXACTLY_THE_SAME)
🪛 markdownlint-cli2 (0.18.1)
test-ui/REFACTORING.md
185-185: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/SCREENSHOTS.md
87-87: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
97-97: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
110-110: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
135-135: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
225-225: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
236-236: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
249-249: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
262-262: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
test-ui/UI_STRUCTURE.md
16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
62-62: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: npm:integrations/ts-react16-overrides on node 25.x
- GitHub Check: yarn:my-charts-react19:app-react18 on node 24.x
- GitHub Check: npm:integrations/ts4-react17 on node 25.x
- GitHub Check: yarn:my-charts-react19:app-react19 on node 22.18.x
🔇 Additional comments (14)
test-ui/server/scripts/TestOutcome.ts (1)
1-51: TestOutcome and stringify implementation look solidThe
stringifyhelper plusTestOutcome.ok/failcover the expected error shapes (output/stdout/stderr) and fall back safely when needed;successaligns with the factories’ usage. No changes needed from my side.list.js (1)
20-39: CommonJS bridge to the TS test registry looks goodThe dynamic
requireoftest-ui/server/scripts/test-registry.tsand use ofgetAllTestNames('stable')vsgetAllTestNames()keeps this script backwards‑compatible for existing callers while centralizing test definitions in the registry. The path calculation via__dirnameandpath.joinis solid; I don’t see any issues here.test-ui/test/store/exampleTestResults.ts (1)
1-1615: Well-structured test fixture data.The fixture provides comprehensive coverage of test scenarios including passed, failed, and in-progress tests with realistic phase data and timing information. This will enable thorough testing of duration selectors and state management.
test-ui/src/PhaseOutput.tsx (2)
24-41: State initialization logic is well-structured.The lazy initializer handles three scenarios appropriately: explicit initial phase, current running phase, and completed tests (last non-pending phase). Good use of functional initialization to avoid unnecessary computation on re-renders.
131-137: Progress calculation handles edge cases correctly.The fallback to 1000ms when
estimatedPhaseDurations[phaseName]is falsy and capping progress at 100% prevents visual glitches from unexpected data. Note that a duration of0would also trigger the fallback, which is reasonable behavior.test-ui/src/App.css (1)
149-154: Focus states are well-implemented for accessibility.Good use of
:focus-visiblefor keyboard navigation without affecting mouse users. The outline styling is clear and consistent across interactive elements.test-ui/SUMMARY.md (1)
159-166: Security considerations are appropriately documented.Good transparency about security limitations (no auth, CORS enabled, no input validation). The clear note about local development use sets appropriate expectations.
test-ui/server/scripts/NpmController.ts (2)
5-12: Install method has proper error handling.Good pattern of wrapping the async operation in try/catch and returning appropriate
TestOutcomefor both success and failure cases.
38-98: Robust dependency verification implementation.The
verifySingleDependencyVersionmethod properly handles all edge cases: command failure, JSON parse errors, missing dependencies, and multiple versions. The recursivewalkDependenciesfunction correctly traverses the dependency tree. This addresses the concerns from the previous review.test-ui/server/scripts/test-registry.ts (2)
38-155: Verify direct test stability flags against legacy list.jsThe
stabilitysettings for the direct dependency tests (npm vs yarn variants) differ in several places (e.g.ts-react16is experimental for npm but stable for yarn,ts4-react17is stable for npm but experimental for yarn).If this is intentional, a brief comment noting the rationale would help future maintainers. If it’s meant to mirror the old
list.jsbehavior exactly, it’s worth re‑checking those flags to avoid silently changing which tests run in CI.
243-249:isStableTestcorrectly delegates to metadataThe
isStableTesthelper cleanly reusesgetTestMetadataand avoids re‑encoding stability rules. No issues from a correctness standpoint.test-ui/src/App.tsx (2)
360-385: Global timer and progress calculation look correctThe global elapsed timer and progress bar logic correctly:
- Start timing only when there are running or queued tests.
- Reset and clear intervals when the queue is empty.
- Base progress on
completedTestsCount / initialQueueSizeand ETA onrunningETA + queuedETA.No functional issues spotted here; the dependency array (
runningTestsList.length,queuedTests) is sufficient to keep the timer in sync with queue changes.Also applies to: 666-671, 693-713
395-400: Session/localStorage persistence flows are straightforwardPersisting
testResultsintosessionStorageand packing directory/local package paths intolocalStorageis implemented cleanly and guarded with try/catch where needed. The single‑item and “clear all” flows correctly keep storage in sync with Redux state.No changes needed here.
Also applies to: 543-552, 554-557
test-ui/src/types.ts (1)
1-10: Type modelling for tests and phases aligns with usage
Test,Phase,Phases, andPhaseNamematch how the selectors and UI components (e.g.PhaseOutput,TestItem) consume them. UsingPhaseName = keyof Phasesis a good way to keep the union and object shape in sync.No issues here.
Also applies to: 17-35
| if (passed.length === 0 && skipped.length === 0) { | ||
| console.error('No tests were run.'); | ||
| process.exit(1); | ||
| } |
There was a problem hiding this comment.
Reference to undefined variable skipped will cause runtime error.
Line 136 references skipped.length but skipped is never defined in this file. This will throw a ReferenceError at runtime when executed via CLI. The past review indicated this was addressed, but the variable reference was left behind.
if (errors.length > 0) {
errors.forEach(result => {
console.error(`❌ ${result.name}: ${result.error}`);
});
process.exit(1);
}
- if (passed.length === 0 && skipped.length === 0) {
+ if (passed.length === 0) {
console.error('No tests were run.');
process.exit(1);
}
process.exit(0);📝 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.
| if (passed.length === 0 && skipped.length === 0) { | |
| console.error('No tests were run.'); | |
| process.exit(1); | |
| } | |
| if (passed.length === 0) { | |
| console.error('No tests were run.'); | |
| process.exit(1); | |
| } |
🤖 Prompt for AI Agents
In scripts/run.mts around lines 136–139, the code references an undefined
variable `skipped`, causing a ReferenceError; either (A) if a skipped-tests
collection exists elsewhere, replace `skipped` with the correct variable name
(e.g., `skippedTests`) or ensure `skipped` is declared and populated where test
results are collected, or (B) if there are only `passed`/`failed` counters,
remove `skipped` from the condition and adjust the check to use the correct
variables (for example `if (passed.length === 0 && failed.length === 0)`) so the
CLI no longer throws at runtime.
| async pack(): Promise<string> { | ||
| const output = await this.execAsync("npm pack"); | ||
| return this.tgzFileNameToPackageJsonReference(output.trim()); | ||
| } |
There was a problem hiding this comment.
Missing error handling in pack() method.
Unlike other methods that return TestOutcome, pack() returns a string and has no try/catch. If npm pack fails or produces unexpected output, the error will propagate uncaught. This inconsistency could cause unexpected crashes in the test orchestration.
Consider wrapping in try/catch or changing the return type to handle failures:
async pack(): Promise<string> {
+ try {
const output = await this.execAsync("npm pack");
return this.tgzFileNameToPackageJsonReference(output.trim());
+ } catch (ex) {
+ throw new Error(`npm pack failed: ${ex instanceof Error ? ex.message : String(ex)}`);
+ }
}Alternatively, if failures should be handled gracefully, consider returning TestOutcome | string or a result object.
📝 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.
| async pack(): Promise<string> { | |
| const output = await this.execAsync("npm pack"); | |
| return this.tgzFileNameToPackageJsonReference(output.trim()); | |
| } | |
| async pack(): Promise<string> { | |
| try { | |
| const output = await this.execAsync("npm pack"); | |
| return this.tgzFileNameToPackageJsonReference(output.trim()); | |
| } catch (ex) { | |
| throw new Error(`npm pack failed: ${ex instanceof Error ? ex.message : String(ex)}`); | |
| } | |
| } |
🤖 Prompt for AI Agents
In test-ui/server/scripts/NpmController.ts around lines 33–36, the pack() method
calls npm pack and returns a string but lacks error handling; wrap the execAsync
call in a try/catch and change the method signature to Promise<TestOutcome |
string> (or return TestOutcome on failure) so failures are represented
consistently with other methods—on success return the package string, on error
return a TestOutcome failure object containing the error message and any
relevant context.
| const loadRechartsVersions = async () => { | ||
| try { | ||
| dispatch(setLoadingVersions(true)); | ||
| // Fetch from npm registry | ||
| const response = await fetch("https://registry.npmjs.org/recharts"); | ||
| const data = await response.json(); | ||
|
|
||
| // Get versions and sort from latest to oldest | ||
| const versions = Object.keys(data.versions || {}) | ||
| .filter((v) => !v.includes("-")) // Filter out pre-release versions | ||
| .sort((a, b) => { | ||
| // Compare version numbers (simple sort by version string works for semver) | ||
| const aParts = a.split(".").map(Number); | ||
| const bParts = b.split(".").map(Number); | ||
|
|
||
| for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { | ||
| const aVal = aParts[i] || 0; | ||
| const bVal = bParts[i] || 0; | ||
| if (aVal !== bVal) { | ||
| return bVal - aVal; // Descending order | ||
| } | ||
| } | ||
| return 0; | ||
| }) | ||
| .slice(0, 50); // Limit to 50 most recent versions | ||
|
|
||
| dispatch(setAvailableVersions(versions)); | ||
| } catch (err) { | ||
| console.error("Failed to load Recharts versions:", err); | ||
| dispatch(setLoadingVersions(false)); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Bug: loadingVersions is never reset to false on success
loadRechartsVersions sets setLoadingVersions(true) before the fetch and only calls setLoadingVersions(false) in the catch block. On a successful response, loadingVersions remains true, which means the Recharts version <select> stays disabled due to disabled={loadingVersions || !!localPackagePath}.
This effectively prevents users from ever choosing a specific version once the registry call succeeds.
Consider mirroring the pattern from loadTests and always clearing the flag in a finally block, e.g.:
const loadRechartsVersions = async () => {
try {
dispatch(setLoadingVersions(true));
// Fetch from npm registry
const response = await fetch("https://registry.npmjs.org/recharts");
const data = await response.json();
// ...
dispatch(setAvailableVersions(versions));
- } catch (err) {
- console.error("Failed to load Recharts versions:", err);
- dispatch(setLoadingVersions(false));
- }
+ } catch (err) {
+ console.error("Failed to load Recharts versions:", err);
+ } finally {
+ dispatch(setLoadingVersions(false));
+ }
};Also applies to: 733-755
🤖 Prompt for AI Agents
In test-ui/src/App.tsx around lines 457 to 488 (and similarly at 733 to 755),
the function sets loadingVersions to true before the fetch but only clears it in
the catch block so the flag remains true on success; move the
dispatch(setLoadingVersions(false)) into a finally block (or add a finally that
dispatches setLoadingVersions(false)) so loadingVersions is always reset
regardless of success or failure, ensuring the version <select> becomes enabled
after the fetch completes.
| export const selectElapsedTimeForRunningTestPhases: ( | ||
| state: RootState, | ||
| ) => Record<PhaseName, number> = createSelector( | ||
| [selectAllTestResults], | ||
| (testResults: ReadonlyArray<TestRun>) => { | ||
| if (testResults.length === 0) { | ||
| return educatedGuess; | ||
| } |
There was a problem hiding this comment.
Guard against invalid timestamps causing NaN ETAs
Both selectElapsedTimeForRunningTestPhases and especially selectRunningTestsETA assume that all phase timestamps and durations are valid. If phase.startTime is a malformed string, new Date(phase.startTime).getTime() becomes NaN, which then propagates through elapsed, remaining, and totalETA, ultimately yielding NaN from Math.ceil(totalETA) and breaking UI formatting.
A light defensive check (e.g. ignoring phases where Number.isFinite(startTimestamp) or Number.isFinite(remaining) fails) would make the ETA computation robust against bad data without changing the happy path.
Also applies to: 81-89, 100-115, 123-191
| describe("selectRunningTestsETA", () => { | ||
| it("should estimate the remaining time for currently running tests", () => { | ||
| const store = prepareStore(); | ||
| const state = store.getState(); | ||
| const eta = selectRunningTestsETA(state); | ||
|
|
||
| // Based on the remaining durations of the two running tests | ||
| // Note: This expectation uses snapshot values from fixture and may be brittle | ||
| // if fixture timestamps change. Consider using toBeCloseTo or mocking Date.now(). | ||
| expect(eta).toBe(53682); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Time-dependent test will be flaky.
The selectRunningTestsETA selector uses Date.now() internally, but the test compares against a hardcoded value. Since the fixture timestamps are from 2025-11-28 and tests run at different times, this test will produce inconsistent results. The comment acknowledges this but doesn't address it.
Consider mocking Date.now() to ensure deterministic results:
it("should estimate the remaining time for currently running tests", () => {
const mockNow = new Date("2025-11-28T02:19:50.000Z").getTime();
vi.spyOn(Date, 'now').mockReturnValue(mockNow);
const store = prepareStore();
const state = store.getState();
const eta = selectRunningTestsETA(state);
expect(eta).toBeCloseTo(expectedValue, -2); // Allow some variance
vi.restoreAllMocks();
});🤖 Prompt for AI Agents
In test-ui/test/store/testDurationSelectors.spec.ts around lines 150 to 161, the
test uses a hardcoded ETA that depends on Date.now() and will be flaky; mock
Date.now() to return the fixture timestamp (e.g. the 2025-11-28 fixture time)
before calling prepareStore()/selectRunningTestsETA, assert deterministically
(use toBe or toBeCloseTo with an appropriate tolerance), and restore the mock
after the assertion (or use a test framework helper to auto-restore) so the test
is stable and deterministic.
|
OK All the tests are passing, the app is not quite 100% perfect but it's good enough to be useful. |
|
Figured it's not as important to review 😅 |
New UI for running tests, rewrote many things to typescript and ESM
https://github.com/recharts/recharts/blob/main/test/helper/userEventSetup.ts
Summary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.