Skip to content

Commit 361f0e3

Browse files
committed
test: add Vitest + React Testing Library, initial tests for resolveImageUrl, extractErrorMessages, PasswordStrength
1 parent f7bf245 commit 361f0e3

8 files changed

Lines changed: 1231 additions & 3 deletions

File tree

apps/web/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"scripts": {
66
"dev": "next dev",
77
"build": "next build",
8-
"start": "next start"
8+
"start": "next start",
9+
"test": "vitest run"
910
},
1011
"dependencies": {
1112
"next": "16.2.10",
@@ -14,10 +15,16 @@
1415
},
1516
"devDependencies": {
1617
"@tailwindcss/postcss": "^4",
18+
"@testing-library/jest-dom": "^7.0.0",
19+
"@testing-library/react": "^16.3.2",
20+
"@testing-library/user-event": "^14.6.1",
1721
"@types/node": "^20",
1822
"@types/react": "^19",
1923
"@types/react-dom": "^19",
24+
"@vitejs/plugin-react": "^6.0.5",
25+
"jsdom": "^30.0.1",
2026
"tailwindcss": "^4",
21-
"typescript": "^5"
27+
"typescript": "^5",
28+
"vitest": "^4.1.10"
2229
}
2330
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { render, screen } from '@testing-library/react';
3+
import { PasswordStrength } from './password-strength';
4+
5+
describe('PasswordStrength', () => {
6+
it('renders nothing when password is empty', () => {
7+
const { container } = render(<PasswordStrength password="" />);
8+
expect(container).toBeEmptyDOMElement();
9+
});
10+
11+
it('shows "Too short" for a password under 8 characters', () => {
12+
render(<PasswordStrength password="abc123" />);
13+
expect(screen.getByText('Too short')).toBeInTheDocument();
14+
});
15+
16+
it('shows a stronger label for a long, mixed-character password', () => {
17+
render(<PasswordStrength password="Str0ng!Password99" />);
18+
expect(screen.getByText('Strong')).toBeInTheDocument();
19+
});
20+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { extractErrorMessages } from './error-message';
3+
4+
describe('extractErrorMessages', () => {
5+
it('returns a single message wrapped in an array', () => {
6+
expect(extractErrorMessages({ message: 'Invalid credentials' })).toEqual([
7+
'Invalid credentials',
8+
]);
9+
});
10+
11+
it('returns an array of messages as-is', () => {
12+
const body = { message: ['Username taken', 'Password too short'] };
13+
expect(extractErrorMessages(body)).toEqual([
14+
'Username taken',
15+
'Password too short',
16+
]);
17+
});
18+
19+
it('falls back to a generic message for unexpected shapes', () => {
20+
expect(extractErrorMessages(null)).toEqual(['Something went wrong']);
21+
expect(extractErrorMessages({})).toEqual(['Something went wrong']);
22+
expect(extractErrorMessages('a plain string')).toEqual([
23+
'Something went wrong',
24+
]);
25+
});
26+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { resolveImageUrl } from './resolve-image-url';
3+
4+
describe('resolveImageUrl', () => {
5+
it('returns null when given null', () => {
6+
expect(resolveImageUrl(null)).toBeNull();
7+
});
8+
9+
it('passes through an absolute https URL unchanged', () => {
10+
const url = 'https://s3.us-west-004.backblazeb2.com/bucket/key?sig=abc';
11+
expect(resolveImageUrl(url)).toBe(url);
12+
});
13+
14+
it('passes through an absolute http URL unchanged', () => {
15+
const url = 'http://localhost:9000/bucket/key';
16+
expect(resolveImageUrl(url)).toBe(url);
17+
});
18+
19+
it('prefixes a relative path with the API base URL', () => {
20+
process.env.NEXT_PUBLIC_API_URL = 'http://localhost:3000';
21+
expect(resolveImageUrl('/local-storage/users/1/file.jpg')).toBe(
22+
'http://localhost:3000/local-storage/users/1/file.jpg',
23+
);
24+
});
25+
});

apps/web/vitest.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { defineConfig } from 'vitest/config';
2+
import react from '@vitejs/plugin-react';
3+
import path from 'path';
4+
5+
export default defineConfig({
6+
plugins: [react()],
7+
test: {
8+
environment: 'jsdom',
9+
setupFiles: ['./vitest.setup.ts'],
10+
globals: true,
11+
},
12+
resolve: {
13+
alias: {
14+
'@': path.resolve(__dirname, './src'),
15+
},
16+
},
17+
});

apps/web/vitest.setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@testing-library/jest-dom';

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"scripts": {
66
"lint": "pnpm --filter api run lint && pnpm --filter web run lint",
77
"build": "pnpm --filter api run build && pnpm --filter web run build",
8-
"test": "pnpm --filter api run test"
8+
"test": "pnpm --filter api run test && pnpm --filter web run test"
99
}
1010
}

0 commit comments

Comments
 (0)