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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 52 additions & 42 deletions evals/001-server-component/input/app/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,59 @@
import { expect, test } from 'vitest';
import { readFileSync } from 'fs';
import { join } from 'path';

test('Page is an async server component', () => {
const pageContent = readFileSync(join(process.cwd(), 'app', 'page.tsx'), 'utf-8');

// Should be an async function (server component)
expect(pageContent).toMatch(/export\s+default\s+async\s+function|async\s+function.*Page/);

// Should NOT have 'use client' directive
expect(pageContent).not.toMatch(/['"]use client['"];?/);
import { expect, test, vi, beforeEach } from 'vitest';
import { renderToString } from 'react-dom/server';
import Page from './page';

// Mock fetch to test server component behavior
const mockProducts = [
{ id: 1, name: 'First Product' },
{ id: 2, name: 'Second Product' },
{ id: 3, name: 'Third Product' },
];

beforeEach(() => {
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve(mockProducts),
})
) as any;
});

test('Page fetches from correct API endpoint', () => {
const pageContent = readFileSync(join(process.cwd(), 'app', 'page.tsx'), 'utf-8');

// Should fetch from the correct URL
expect(pageContent).toMatch(/api\.vercel\.app\/products/);

// Should use fetch
expect(pageContent).toMatch(/fetch\s*\(/);

// Should use await for the fetch
expect(pageContent).toMatch(/await.*fetch|fetch.*await/);
test('Page component is async (server component)', async () => {
// Server components must be async functions
expect(Page.constructor.name).toBe('AsyncFunction');
});

test('Page renders first product in h1 tag', () => {
const pageContent = readFileSync(join(process.cwd(), 'app', 'page.tsx'), 'utf-8');

// Should access first product (array[0] or similar)
expect(pageContent).toMatch(/\[0\]|\bfirst\b|\.at\(0\)/i);

// Should render in h1 tag
expect(pageContent).toMatch(/<h1[^>]*>.*<\/h1>/);

// Should access the product name property
expect(pageContent).toMatch(/\.name\b/);
test('Page fetches data and renders first product', async () => {
// Call the async server component
const result = await Page();

// Render the component to HTML
const html = renderToString(result);

// Should render the first product name in an h1
expect(html).toContain('<h1');
expect(html).toContain('First Product');

// Should NOT render other products
expect(html).not.toContain('Second Product');
expect(html).not.toContain('Third Product');
});

test('Page handles JSON response correctly', () => {
const pageContent = readFileSync(join(process.cwd(), 'app', 'page.tsx'), 'utf-8');

// Should parse JSON response
expect(pageContent).toMatch(/\.json\(\)/);

// Should await the JSON parsing
expect(pageContent).toMatch(/await.*\.json\(\)|\.json\(\).*await/);
test('Page calls fetch with correct API endpoint', async () => {
await Page();

// Verify fetch was called with the correct URL
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining('api.vercel.app/products'),
expect.anything()
);
});

test('Page renders h1 element with product name', async () => {
const result = await Page();
const html = renderToString(result);

// Check that h1 contains the product name
const h1Match = html.match(/<h1[^>]*>(.*?)<\/h1>/);
expect(h1Match).toBeTruthy();
expect(h1Match?.[1]).toContain('First Product');
});
72 changes: 39 additions & 33 deletions evals/022-prefer-server-actions/input/app/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,55 @@
import { expect, test } from 'vitest';
import { render, screen } from '@testing-library/react';
import { readFileSync } from 'fs';
import { join } from 'path';
import Page from './page';

test('renders contact form component', () => {
test('renders contact form with heading', () => {
render(<Page />);
expect(screen.getByText('Contact Us')).toBeDefined();
});

test('uses server action instead of client-side submission', () => {
const content = readFileSync(join(process.cwd(), 'app', 'ContactForm.tsx'), 'utf-8');

expect(content).not.toMatch(/['"]use client['"];?/);
expect(content).not.toMatch(/onSubmit|fetch\s*\(|useState|preventDefault/);
expect(content).toMatch(/['"]use server['"];?/);
expect(content).toMatch(/async\s+function\s+\w+.*FormData/);
});
test('form has all required input fields', () => {
render(<Page />);

// Check for name input
const nameInput = screen.getByLabelText(/name/i) || screen.getByPlaceholderText(/name/i);
expect(nameInput).toBeDefined();
expect(nameInput.getAttribute('name')).toBe('name');

test('processes form data using FormData API', () => {
const content = readFileSync(join(process.cwd(), 'app', 'ContactForm.tsx'), 'utf-8');

expect(content).toMatch(/formData\.get\s*\(\s*['"]name['"]\s*\)/);
expect(content).toMatch(/formData\.get\s*\(\s*['"]email['"]\s*\)/);
expect(content).toMatch(/formData\.get\s*\(\s*['"]message['"]\s*\)/);
// Check for email input
const emailInput = screen.getByLabelText(/email/i) || screen.getByPlaceholderText(/email/i);
expect(emailInput).toBeDefined();
expect(emailInput.getAttribute('name')).toBe('email');

// Check for message input (textarea or input)
const messageInput = screen.getByLabelText(/message/i) || screen.getByPlaceholderText(/message/i);
expect(messageInput).toBeDefined();
expect(messageInput.getAttribute('name')).toBe('message');
});

test('has proper form structure with action attribute', () => {
const content = readFileSync(join(process.cwd(), 'app', 'ContactForm.tsx'), 'utf-8');

expect(content).toMatch(/<form[^>]*action\s*=\s*{[^}]+}/);
expect(content).toMatch(/name\s*=\s*['"]name['"]/);
expect(content).toMatch(/name\s*=\s*['"]email['"]/);
expect(content).toMatch(/name\s*=\s*['"]message['"]/);
expect(content).toMatch(/type\s*=\s*['"]submit['"]/);
test('form has submit button', () => {
render(<Page />);

const submitButton = screen.getByRole('button', { name: /submit/i });
expect(submitButton).toBeDefined();
expect(submitButton.getAttribute('type')).toBe('submit');
});

test('includes form validation', () => {
const content = readFileSync(join(process.cwd(), 'app', 'ContactForm.tsx'), 'utf-8');

expect(content).toMatch(/!name\s*\|\|\s*!email\s*\|\|\s*!message|if\s*\([^)]*(!name|!email|!message)/);
test('form uses server action (has action attribute)', () => {
render(<Page />);

// Get the form element
const form = screen.getByRole('form') || document.querySelector('form');
expect(form).toBeDefined();

// Server actions are passed as the action prop - should exist
expect(form?.getAttribute('action')).toBeTruthy();
});

test('does not use API routes pattern', () => {
const content = readFileSync(join(process.cwd(), 'app', 'ContactForm.tsx'), 'utf-8');

expect(content).not.toMatch(/\/api\/\w+|JSON\.stringify|response\.json\(\)/);
test('form does not use client-side event handlers', () => {
render(<Page />);

const form = screen.getByRole('form') || document.querySelector('form');

// Server action forms should NOT have onSubmit handlers
expect(form?.getAttribute('onsubmit')).toBeNull();
});
Loading