Chat
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in miliseconds.
This document provides comprehensive guidance for testing the Anthropic Streaming Chat MCP application.
The application uses Deno as the test runner with React Testing Library for component testing. Tests are written in TypeScript and follow modern testing practices with proper mocking and isolation.
# Install dependencies (if using npm scripts) npm install # Run all tests npm test # Run tests in watch mode npm run test:watch # Run with coverage npm run test:coverage
- Test Runner: Deno Test
- Component Testing: React Testing Library
- Assertions: Deno Standard Library Assert
- Mocking: Custom utilities + built-in Deno mocks
- Coverage: Deno built-in coverage tools
test/
├── components/ # Component-specific tests
│ └── Message.test.tsx # Message component tests
├── utils/ # Testing utilities
│ └── test-helpers.ts # Mock factories and helpers
├── setup.ts # Global test configuration
└── README.md # Test-specific documentation
/** @jsxImportSource https://esm.sh/react@18.2.0 */
import { assertEquals, assertExists } from "https://deno.land/std@0.208.0/assert/mod.ts";
import { render, cleanup } from "https://esm.sh/@testing-library/react@14.0.0?deps=react@18.2.0,react-dom@18.2.0";
import React from "https://esm.sh/react@18.2.0";
import { createMockMessage } from "../utils/test-helpers.ts";
import ComponentToTest from "../../frontend/components/ComponentToTest.tsx";
Deno.test("ComponentToTest - Feature Category", async (t) => {
await t.step("should render correctly with basic props", () => {
const mockData = createMockMessage();
const { container } = render(
<ComponentToTest data={mockData} />
);
const element = container.querySelector('.expected-class');
assertExists(element, "Component should render expected element");
});
// Always cleanup after component tests
cleanup();
});
The test-helpers.ts
file provides utilities for creating consistent mock data:
import {
createMockMessage,
createMockMCPServer,
createMockCallback,
createMockAnthropicBlocks
} from "../utils/test-helpers.ts";
// Create mock message with defaults
const message = createMockMessage();
// Create mock message with overrides
const assistantMessage = createMockMessage({
role: "assistant",
content: "Custom content"
});
// Create mock callback that tracks calls
const mockCallback = createMockCallback();
// Later: assertEquals(mockCallback.calls.length, 1);
// Create mock Anthropic blocks for streaming tests
const blocks = createMockAnthropicBlocks([
{ type: "text", text: "Hello" },
{ type: "code", text: "console.log('test')", language: "javascript" }
]);
await t.step("should render text message", () => {
const message = createMockMessage({
content: "Simple text message"
});
const { container } = render(<MessageComponent message={message} />);
assertStringIncludes(container.innerHTML, "Simple text message");
});
await t.step("should render streaming blocks", () => {
const message = createMockMessage({
content: [
{ type: "text", text: "Streaming text", _streaming: true },
{ type: "code", text: "const x = 1;", language: "javascript" }
]
});
const { container } = render(<MessageComponent message={message} />);
// Check for streaming cursor
const cursor = container.querySelector('.streaming-cursor');
assertExists(cursor, "Should show streaming cursor");
// Check for code block
const codeBlock = container.querySelector('.anthropic-code-block');
assertExists(codeBlock, "Should render code block");
});
await t.step("should render MCP tool results", () => {
const message = createMockMessage({
content: [
createMockMCPToolUse({ name: "file_reader" }),
createMockMCPToolResult({
content: [{ text: JSON.stringify({ output: "File content" }) }]
})
]
});
const { container } = render(<MessageComponent message={message} />);
const toolDetails = container.querySelector('.mcp-details');
assertExists(toolDetails, "Should render MCP tool details");
});
await t.step("should handle button clicks", () => {
const mockOnDelete = createMockCallback();
const message = createMockMessage();
const { container } = render(
<MessageComponent
message={message}
onDelete={mockOnDelete}
/>
);
const deleteButton = container.querySelector('.delete-btn');
assertExists(deleteButton, "Delete button should exist");
// Simulate click (note: actual click simulation requires more setup)
// This tests that the button is rendered correctly
});
await t.step("should conditionally render retry button", () => {
const message = createMockMessage();
const mockOnRetry = createMockCallback();
// Test with canRetry = true
const { container: containerWithRetry } = render(
<MessageComponent
message={message}
onRetry={mockOnRetry}
canRetry={true}
/>
);
assertExists(
containerWithRetry.querySelector('.retry-btn'),
"Should show retry button when canRetry is true"
);
// Test with canRetry = false
const { container: containerNoRetry } = render(
<MessageComponent
message={message}
onRetry={mockOnRetry}
canRetry={false}
/>
);
assertEquals(
containerNoRetry.querySelector('.retry-btn'),
null,
"Should not show retry button when canRetry is false"
);
});
- Individual component functionality
- Utility function behavior
- Type validation
- Component interaction
- Data flow between components
- MCP client integration
- Component output verification
- Conditional rendering
- Props handling
- Button clicks and callbacks
- Form submissions
- User input handling
// DOM APIs
globalThis.localStorage = mockLocalStorage;
globalThis.fetch = mockFetch;
globalThis.crypto.randomUUID = mockUUID;
// Browser APIs
navigator.clipboard = mockClipboard;
globalThis.alert = mockAlert;
// Mock MCP clients
const mockMCPClients = [];
// Mock callbacks
const mockOnSendMessage = createMockCallback();
const mockOnDelete = createMockCallback();
# Generate coverage data npm run test:coverage # Generate HTML report npm run test:coverage-report # View report open coverage/html/index.html
- Components: >90% line coverage
- Utilities: >95% line coverage
- Critical paths: 100% coverage
- All tests should be deterministic
- No flaky tests
- Fast execution (<30s total)
- Clear, descriptive test names
Deno.test("ComponentName - Feature Category", async (t) => {
await t.step("should handle normal case", () => {
// Test implementation
});
await t.step("should handle edge case", () => {
// Test implementation
});
await t.step("should handle error case", () => {
// Test implementation
});
cleanup(); // Always cleanup after component tests
});
- Test files:
ComponentName.test.tsx
- Test suites:
"ComponentName - Feature Category"
- Test cases:
"should [expected behavior]"
- Mock functions:
mock[FunctionName]
// Good: Specific assertions
assertExists(element, "Button should exist");
assertEquals(calls.length, 1, "Callback should be called once");
assertStringIncludes(html, "expected text");
// Avoid: Generic assertions
assert(element); // Not descriptive
assertEquals(result, true); // Not specific
// Good: Use test helpers
const message = createMockMessage({ role: "assistant" });
// Avoid: Inline mock data
const message = {
id: "123",
role: "assistant",
content: "test",
timestamp: 1234567890
};
- Import Errors: Ensure React version pinning in imports
- Component Not Rendering: Check required props
- Async Issues: Use proper async/await patterns
- Mock Problems: Verify setup.ts configuration
// Log component output
const { container } = render(<Component />);
console.log("HTML:", container.innerHTML);
// Check mock calls
console.log("Mock calls:", mockCallback.calls);
// Verify element selection
const element = container.querySelector('.target');
console.log("Element found:", !!element);
- Each test should be independent
- Use
cleanup()
after component tests - Reset mocks between tests if needed
- Avoid shared state between tests
# Run tests before commit npm test # Run linting deno lint # Run formatting deno fmt --check
# Example GitHub Actions workflow - name: Run Tests run: | deno test --allow-all test/ deno coverage coverage --html
- Create test file in appropriate directory
- Follow naming conventions
- Import required testing utilities
- Use test helpers for mock data
- Include cleanup calls
- Test happy path and edge cases
- Add descriptive assertions
- Update documentation if needed
/** @jsxImportSource https://esm.sh/react@18.2.0 */
import { assertEquals, assertExists } from "https://deno.land/std@0.208.0/assert/mod.ts";
import { render, cleanup } from "https://esm.sh/@testing-library/react@14.0.0?deps=react@18.2.0,react-dom@18.2.0";
import React from "https://esm.sh/react@18.2.0";
import { createMockMessage } from "../utils/test-helpers.ts";
import NewComponent from "../../frontend/components/NewComponent.tsx";
Deno.test("NewComponent - Basic Functionality", async (t) => {
await t.step("should render with default props", () => {
// Test implementation
});
await t.step("should handle prop changes", () => {
// Test implementation
});
cleanup();
});
- Deno Testing Documentation
- React Testing Library Documentation
- Deno Standard Library Assert
- ESM.sh for React Dependencies
When adding tests:
- Follow the established patterns
- Use the provided test helpers
- Maintain good coverage
- Write clear, descriptive tests
- Update documentation as needed
For questions or issues with testing, refer to the test README or create an issue in the project repository.