Skip to content

Commit b12ecc7

Browse files
committed
Merge branch 'codegen-bot/persist-todos-localstorage-STA-2-1754688724' of github.com:lambda-curry/react-router-starter into codegen/sta-10-testing-add-storage-hydration-tests-utils-todo-context
2 parents 3a52810 + bdf695d commit b12ecc7

File tree

7 files changed

+124
-11
lines changed

7 files changed

+124
-11
lines changed

apps/todo-app/app/components/__tests__/add-todo.test.tsx

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,97 @@
11
import { render, screen, fireEvent } from '@testing-library/react';
2-
import { describe, it, expect, vi } from 'vitest';
2+
import { describe, it, expect, vi, beforeEach } from 'vitest';
33
import { AddTodo } from '../add-todo';
44
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
5+
import type { ReactElement, ReactNode, ChangeEvent, FormEvent } from 'react';
56

6-
function renderWithRouter(ui: React.ReactElement) {
7+
// Create a stateful mock for the input field
8+
let testInputValue = '';
9+
10+
// Mock lucide-react icons
11+
vi.mock('lucide-react', () => ({
12+
Plus: () => null,
13+
}));
14+
15+
// Mock the @lambdacurry/forms components
16+
interface TextFieldProps {
17+
name: string;
18+
placeholder: string;
19+
className: string;
20+
}
21+
22+
vi.mock('@lambdacurry/forms', () => ({
23+
TextField: ({ name, placeholder, className }: TextFieldProps) => (
24+
<input
25+
name={name}
26+
placeholder={placeholder}
27+
className={className}
28+
type="text"
29+
value={testInputValue}
30+
onChange={(e) => { testInputValue = e.target.value; }}
31+
/>
32+
),
33+
FormError: () => null,
34+
}));
35+
36+
interface ButtonProps {
37+
children: ReactNode;
38+
onClick: () => void;
39+
type: 'button' | 'submit' | 'reset';
40+
}
41+
42+
vi.mock('@lambdacurry/forms/ui', () => ({
43+
Button: ({ children, onClick, type }: ButtonProps) => (
44+
<button type={type} onClick={onClick}>
45+
{children}
46+
</button>
47+
),
48+
}));
49+
50+
// Mock the remix-hook-form module
51+
interface RemixFormConfig {
52+
submitHandlers?: {
53+
onValid: (data: { text: string }) => void;
54+
};
55+
[key: string]: unknown;
56+
}
57+
58+
vi.mock('remix-hook-form', () => ({
59+
RemixFormProvider: ({ children }: { children: ReactNode }) => children,
60+
useRemixForm: (config: RemixFormConfig) => {
61+
return {
62+
...config,
63+
getValues: (_name: string) => testInputValue,
64+
reset: vi.fn(() => {
65+
testInputValue = '';
66+
// Force re-render by dispatching a custom event
67+
const inputs = document.querySelectorAll('input[name="text"]');
68+
inputs.forEach(input => {
69+
(input as HTMLInputElement).value = '';
70+
});
71+
}),
72+
setValue: vi.fn((_name: string, value: string) => {
73+
testInputValue = value;
74+
}),
75+
register: vi.fn((name: string) => ({
76+
name,
77+
onChange: (e: ChangeEvent<HTMLInputElement>) => {
78+
testInputValue = e.target.value;
79+
},
80+
value: testInputValue
81+
})),
82+
handleSubmit: vi.fn((onValid: (data: { text: string }) => void) => (e: FormEvent) => {
83+
e.preventDefault();
84+
if (testInputValue?.trim()) {
85+
onValid({ text: testInputValue.trim() });
86+
}
87+
}),
88+
formState: { errors: {} },
89+
watch: vi.fn((_name: string) => testInputValue),
90+
};
91+
}
92+
}));
93+
94+
function renderWithRouter(ui: ReactElement) {
795
const router = createMemoryRouter([
896
{ path: '/', element: ui }
997
], { initialEntries: ['/'] });
@@ -13,10 +101,12 @@ function renderWithRouter(ui: React.ReactElement) {
13101
// hoist regex literals to top-level to satisfy biome's useTopLevelRegex
14102
const ADD_REGEX = /add/i;
15103

16-
// hoist regex literals to top-level to satisfy biome's useTopLevelRegex
17-
const ADD_REGEX = /add/i;
18-
19104
describe('AddTodo', () => {
105+
beforeEach(() => {
106+
// Reset the test state before each test
107+
testInputValue = '';
108+
});
109+
20110
it('renders input and button', () => {
21111
const mockOnAdd = vi.fn();
22112
renderWithRouter(<AddTodo onAdd={mockOnAdd} />);
@@ -53,7 +143,7 @@ describe('AddTodo', () => {
53143

54144
it('does not call onAdd with empty text', () => {
55145
const mockOnAdd = vi.fn();
56-
render(<AddTodo onAdd={mockOnAdd} />);
146+
renderWithRouter(<AddTodo onAdd={mockOnAdd} />);
57147

58148
const button = screen.getByRole('button', { name: ADD_REGEX });
59149
fireEvent.click(button);

apps/todo-app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"typecheck": "tsc --noEmit",
1818
"lint": "biome lint .",
1919
"format": "biome format --write .",
20-
"test": "vitest",
20+
"test": "vitest run",
21+
"test:watch": "vitest",
2122
"test:ui": "vitest --ui",
2223
"test:run": "vitest run",
2324
"test:ci": "vitest run"

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"typecheck": "bun run turbo run typecheck",
1313
"biome-fix": "bun run biome check --fix",
1414
"test": "bun run turbo run test",
15+
"test:watch": "bun run turbo run test:watch",
1516
"test:ci": "bun run turbo run test:ci"
1617
},
1718
"dependencies": {

packages/ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"lint": "biome lint .",
1717
"format": "biome format --write .",
1818
"typecheck": "tsc --noEmit",
19-
"test": "vitest",
19+
"test": "vitest run --passWithNoTests",
20+
"test:watch": "vitest",
2021
"test:ci": "vitest run --passWithNoTests"
2122
},
2223
"devDependencies": {

packages/utils/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"lint": "biome lint .",
1717
"format": "biome format --write .",
1818
"typecheck": "tsc --noEmit",
19-
"test": "vitest",
19+
"test": "vitest run --passWithNoTests",
20+
"test:watch": "vitest",
2021
"test:ci": "vitest run --passWithNoTests"
2122
},
2223
"devDependencies": {

turbo.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,18 @@
2929
},
3030
"test": {
3131
"dependsOn": ["^build"],
32-
"inputs": ["$TURBO_DEFAULT$", ".env*"],
32+
"inputs": ["$TURBO_DEFAULT$", ".env*", "vitest.config.*"],
3333
"outputs": []
3434
},
35+
"test:watch": {
36+
"dependsOn": ["^build"],
37+
"inputs": ["$TURBO_DEFAULT$", ".env*", "vitest.config.*"],
38+
"outputs": [],
39+
"cache": false,
40+
"persistent": true
41+
},
3542
"test:ci": {
36-
"inputs": ["$TURBO_DEFAULT$", ".env*"],
43+
"inputs": ["$TURBO_DEFAULT$", ".env*", "vitest.config.*"],
3744
"outputs": []
3845
}
3946
}

vitest.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
// Root Vitest configuration for monorepo
4+
// This enables running tests from the root with proper workspace support
5+
export default defineConfig({
6+
test: {
7+
projects: [
8+
'apps/*/vitest.config.{ts,js}',
9+
'packages/*/vitest.config.{ts,js}',
10+
],
11+
},
12+
});

0 commit comments

Comments
 (0)