From b259cd476fbb169cb7ec276e660627f1b160b1aa Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 20 Aug 2025 03:01:05 +0000
Subject: [PATCH 1/3] Initial plan
From e0220c1237877314b374c7c1bdc00d9387dd32d0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 20 Aug 2025 03:08:42 +0000
Subject: [PATCH 2/3] Add Copilot instructions with .cursorrules and AI prompts
Co-authored-by: ericelliott <364727+ericelliott@users.noreply.github.com>
---
.cursorrules | 120 +++++++++
ai/README.md | 15 ++
ai/api-development.md | 348 +++++++++++++++++++++++++
ai/component-testing.md | 327 ++++++++++++++++++++++++
ai/debugging.md | 392 ++++++++++++++++++++++++++++
ai/refactoring.md | 553 ++++++++++++++++++++++++++++++++++++++++
ai/test-writing.md | 165 ++++++++++++
7 files changed, 1920 insertions(+)
create mode 100644 .cursorrules
create mode 100644 ai/README.md
create mode 100644 ai/api-development.md
create mode 100644 ai/component-testing.md
create mode 100644 ai/debugging.md
create mode 100644 ai/refactoring.md
create mode 100644 ai/test-writing.md
diff --git a/.cursorrules b/.cursorrules
new file mode 100644
index 00000000..b71e1e3c
--- /dev/null
+++ b/.cursorrules
@@ -0,0 +1,120 @@
+# Riteway - Unit Testing Library
+
+## Project Overview
+Riteway is a JavaScript unit testing library that forces developers to write **R**eadable, **I**solated, **T**horough, and **E**xplicit tests. Built on top of tape, it provides a simple API that ensures every test answers the 5 essential questions:
+
+1. What is the unit under test?
+2. What should it do?
+3. What was the actual output?
+4. What was the expected output?
+5. How do you reproduce the failure?
+
+## Architecture & Key Components
+
+### Core API
+- `describe(unit, testFunction)` - Main test function
+- `assert({given, should, actual, expected})` - Assertion function with required prose descriptions
+- `Try(fn, ...args)` - Safe function execution with error handling
+- `match(text)` - Text/pattern matching utility for component testing
+- `render(component)` - React component rendering utility
+- `countKeys(obj)` - Object key counting utility
+
+### Module Structure
+- `/source/` - Main source code (CommonJS)
+- `/esm/` - ES module versions (copied from source)
+- `/bin/riteway` - CLI test runner
+- Type definitions in root (`.d.ts` files)
+
+## Coding Standards & Patterns
+
+### Test Writing Patterns
+```javascript
+describe('functionName()', async assert => {
+ assert({
+ given: 'a clear description of input/context',
+ should: 'describe expected behavior',
+ actual: functionCall(input),
+ expected: expectedOutput
+ });
+});
+```
+
+### Error Handling
+- Use `Try()` for testing functions that may throw
+- Always handle async operations properly
+- Prefer async/await over raw promises
+
+### Code Style
+- Use ES6+ features (arrow functions, destructuring, etc.)
+- Prefer const over let when possible
+- Use descriptive variable names
+- Follow existing formatting patterns (2 spaces, semicolons)
+
+### React Component Testing
+```javascript
+const $ = render();
+const contains = match($('.selector').html());
+assert({
+ given: 'component with props',
+ should: 'render expected content',
+ actual: contains('expected text'),
+ expected: 'expected text'
+});
+```
+
+## Development Workflow
+
+### Common Commands
+- `npm test` - Run all tests
+- `npm run lint` - Run ESLint
+- `npm run typecheck` - TypeScript checking
+- `npm run esm` - Update ESM modules
+- `npm run watch` - Watch mode for development
+
+### Build Process
+1. Source files are in `/source/`
+2. ESM versions are generated via copy to `/esm/`
+3. TypeScript definitions are manually maintained
+4. Babel is used for transpilation in tests
+
+### Testing Guidelines
+- Every test must have `given`, `should`, `actual`, `expected`
+- Use prose descriptions that read like sentences
+- Test one thing per assertion
+- Prefer isolated unit tests
+- Use `describe.only()` for focused testing
+- Use `describe.skip()` to temporarily disable tests
+
+## Dependencies & Tools
+- **tape** - Underlying test runner
+- **cheerio** - DOM manipulation for component testing
+- **react/react-dom** - For component rendering
+- **babel** - Transpilation
+- **eslint** - Linting
+- **typescript** - Type checking (dev only)
+
+## File Naming Conventions
+- `*-test.js` - Test files
+- `*.d.ts` - TypeScript definitions
+- Main modules: `riteway.js`, `match.js`, `render-component.js`, etc.
+
+## Key Design Principles
+1. **Explicitness over brevity** - Every test should be completely clear
+2. **Readability first** - Tests should read like documentation
+3. **Fail fast and clear** - Test failures should immediately show the problem
+4. **Minimal API surface** - Keep the API small and focused
+5. **TAP compatibility** - Output should work with TAP ecosystem tools
+
+## Common Issues & Solutions
+- Missing babel config causes syntax errors - ensure `.babelrc` is properly configured
+- ESM modules need manual updating via `npm run esm`
+- TypeScript checking is separate from runtime - run `npm run typecheck`
+- For React testing, ensure proper imports and babel presets
+
+## When Contributing
+- Follow existing test patterns exactly
+- Ensure all tests pass before submitting
+- Run linting and type checking
+- Update ESM modules if source changes
+- Maintain TypeScript definitions for API changes
+- Keep test output clear and readable
\ No newline at end of file
diff --git a/ai/README.md b/ai/README.md
new file mode 100644
index 00000000..97732084
--- /dev/null
+++ b/ai/README.md
@@ -0,0 +1,15 @@
+# AI Development Prompts for Riteway
+
+This directory contains prompts and templates to help AI assistants understand and work with the Riteway testing library.
+
+## Available Prompts
+
+- `test-writing.md` - Templates and patterns for writing Riteway tests
+- `component-testing.md` - React component testing patterns
+- `api-development.md` - Guidelines for extending the Riteway API
+- `debugging.md` - Common debugging scenarios and solutions
+- `refactoring.md` - Patterns for safely refactoring code and tests
+
+## Usage
+
+These prompts are designed to provide context and examples for AI assistants when working on Riteway development tasks. Reference them when asking for help with specific development scenarios.
\ No newline at end of file
diff --git a/ai/api-development.md b/ai/api-development.md
new file mode 100644
index 00000000..ab4506f3
--- /dev/null
+++ b/ai/api-development.md
@@ -0,0 +1,348 @@
+# API Development Guidelines for Riteway
+
+## Core Design Principles
+
+When extending or modifying the Riteway API, follow these principles:
+
+1. **Simplicity**: Keep the API surface minimal and focused
+2. **Explicitness**: Force users to be explicit about what they're testing
+3. **Readability**: API calls should read like natural language
+4. **TAP Compatibility**: Maintain compatibility with the TAP ecosystem
+5. **Error Clarity**: Failures should provide clear, actionable feedback
+
+## Current API Structure
+
+### Core Functions
+
+```javascript
+// Main test runner
+describe(unit: String, cb: TestFunction) => Void
+describe.only(unit: String, cb: TestFunction) => Void
+describe.skip(unit: String, cb: TestFunction) => Void
+
+// Assertion function
+assert({
+ given: String,
+ should: String,
+ actual: Any,
+ expected: Any
+}) => Void
+
+// Utility functions
+Try(fn: Function, ...args) => Any
+createStream() => Stream
+countKeys(obj: Object) => Number
+```
+
+### Testing Utilities
+
+```javascript
+// Text/pattern matching
+match(text: String) => (pattern: String|RegExp) => String
+
+// React component rendering
+render(component: ReactElement) => CheerioStatic
+```
+
+## Adding New API Functions
+
+### Function Design Pattern
+
+All API functions should follow these patterns:
+
+```javascript
+// Pure functions - no side effects
+const newUtility = (input) => {
+ // Validate inputs
+ if (!input) {
+ throw new Error('Input is required');
+ }
+
+ // Transform and return
+ return transformedOutput;
+};
+
+// Test runner extensions
+const newTestType = (description, testFn) => {
+ // Wrap with existing patterns
+ return describe(description, testFn);
+};
+```
+
+### Input Validation
+
+Always validate inputs and provide clear error messages:
+
+```javascript
+const validateAssertArgs = (args = {}) => {
+ const requiredKeys = ['given', 'should', 'actual', 'expected'];
+ const missing = requiredKeys.filter(
+ k => !Object.keys(args).includes(k)
+ );
+
+ if (missing.length) {
+ throw new Error(
+ `The following parameters are required: ${missing.join(', ')}`
+ );
+ }
+};
+```
+
+### TypeScript Definitions
+
+Always provide TypeScript definitions for new API functions:
+
+```typescript
+// In appropriate .d.ts file
+export declare function newFunction(
+ param1: string,
+ param2?: number
+): ReturnType;
+
+export interface NewOptions {
+ option1: string;
+ option2?: boolean;
+}
+```
+
+## Extending the Assert Function
+
+The `assert` function is the core of Riteway. Any extensions should maintain its design:
+
+### Current Implementation Pattern
+```javascript
+const assert = (args = {}) => {
+ // Validate required fields
+ validateAssertArgs(args);
+
+ const { given, should, actual, expected } = args;
+
+ // Use tape's assertion
+ test.same(
+ actual,
+ expected,
+ `Given ${given}: should ${should}`
+ );
+};
+```
+
+### Adding New Assertion Types
+
+If adding specialized assertions, follow this pattern:
+
+```javascript
+const assertContains = (args = {}) => {
+ const { given, should, actual, expected } = args;
+
+ // Custom validation logic
+ const contains = actual.includes(expected);
+
+ test.ok(
+ contains,
+ `Given ${given}: should ${should}`
+ );
+};
+```
+
+## Testing New API Functions
+
+All new API functions must be thoroughly tested:
+
+```javascript
+describe('newFunction()', async assert => {
+ assert({
+ given: 'valid input',
+ should: 'return expected output',
+ actual: newFunction('input'),
+ expected: 'expectedOutput'
+ });
+
+ assert({
+ given: 'invalid input',
+ should: 'throw descriptive error',
+ actual: Try(newFunction, null),
+ expected: new Error('Input is required')
+ });
+});
+```
+
+## Utility Function Guidelines
+
+### Pure Functions Only
+```javascript
+// Good - pure function
+const formatMessage = (message) => `[TEST] ${message}`;
+
+// Bad - side effects
+const logMessage = (message) => {
+ console.log(message); // Side effect
+ return message;
+};
+```
+
+### Composable Design
+```javascript
+// Design functions to work together
+const createMatcher = (text) => (pattern) => match(text)(pattern);
+const createRenderer = (component) => render(component);
+```
+
+### Error Handling
+```javascript
+const safeOperation = (input) => {
+ try {
+ return riskyOperation(input);
+ } catch (error) {
+ // Return error object, don't throw
+ return error;
+ }
+};
+```
+
+## Module Organization
+
+### Source Structure
+```
+source/
+├── riteway.js # Main API
+├── match.js # Text matching utilities
+├── render-component.js # React testing utilities
+├── vitest.js # Vitest integration
+└── test.js # API tests
+```
+
+### Export Patterns
+```javascript
+// Named exports for utilities
+export { match, render };
+
+// Default export for main API
+export default describe;
+
+// Combined exports
+export { describe, Try, createStream, countKeys };
+```
+
+## Integration with Test Runners
+
+### Tape Integration
+Riteway is built on tape and should maintain compatibility:
+
+```javascript
+const withTape = tapeFn => (unit = '', TestFunction = noop) =>
+ tapeFn(unit, withRiteway(TestFunction));
+
+const describe = Object.assign(withTape(tape), {
+ only: withTape(tape.only),
+ skip: tape.skip
+});
+```
+
+### Other Test Runner Support
+When adding support for other test runners:
+
+```javascript
+// Maintain the same API
+const withNewRunner = runnerFn => (unit, TestFunction) => {
+ // Adapt to new runner's API
+ return runnerFn(unit, adaptedTestFunction);
+};
+```
+
+## Documentation Requirements
+
+### JSDoc Comments
+```javascript
+/**
+ * Creates a text matcher function for testing content.
+ *
+ * @param {string} text - The text to search within
+ * @returns {function} A function that matches patterns in the text
+ * @example
+ * const contains = match('
Hello World
');
+ * contains('Hello'); // Returns 'Hello'
+ */
+const match = (text) => (pattern) => {
+ // Implementation
+};
+```
+
+### README Updates
+When adding new API functions, update:
+1. API section with new function signatures
+2. Usage examples
+3. TypeScript definitions reference
+
+## Backwards Compatibility
+
+### Deprecation Process
+1. Mark old API as deprecated in JSDoc
+2. Add console warning in development
+3. Maintain functionality for at least one major version
+4. Provide migration guide
+
+### Version Strategy
+- Patch: Bug fixes, no API changes
+- Minor: New features, backwards compatible
+- Major: Breaking changes, API modifications
+
+## Common Patterns to Follow
+
+### Function Factories
+```javascript
+// Create specialized versions of generic functions
+const createAsyncTester = (timeout = 5000) => async (fn) => {
+ // Implementation with timeout
+};
+```
+
+### Currying for Composability
+```javascript
+// Allow partial application
+const matcher = text => pattern => match(text)(pattern);
+const htmlMatcher = matcher(htmlString);
+```
+
+### Error Object Structure
+```javascript
+// Consistent error format
+const createError = (message, code, details = {}) => {
+ const error = new Error(message);
+ error.code = code;
+ error.details = details;
+ return error;
+};
+```
+
+## Testing API Changes
+
+### Integration Tests
+```javascript
+// Test how new features work with existing API
+describe('API integration', async assert => {
+ const result = newFunction(existingFunction(input));
+
+ assert({
+ given: 'new function with existing API',
+ should: 'work seamlessly together',
+ actual: result,
+ expected: expectedResult
+ });
+});
+```
+
+### Performance Considerations
+- Keep API functions lightweight
+- Avoid expensive operations in hot paths
+- Consider memoization for expensive computations
+- Test performance with large inputs
+
+## Release Process
+
+1. Update version in package.json
+2. Run full test suite
+3. Update TypeScript definitions
+4. Update ESM modules (`npm run esm`)
+5. Update documentation
+6. Tag release in git
+7. Publish to npm
\ No newline at end of file
diff --git a/ai/component-testing.md b/ai/component-testing.md
new file mode 100644
index 00000000..7cf44430
--- /dev/null
+++ b/ai/component-testing.md
@@ -0,0 +1,327 @@
+# React Component Testing with Riteway
+
+## Setup and Imports
+
+```javascript
+import React from 'react';
+import { describe } from 'riteway';
+import render from 'riteway/render-component';
+import match from 'riteway/match';
+```
+
+## Basic Component Testing Pattern
+
+```javascript
+// Component to test
+const Greeting = ({ name }) =>
Hello, {name}!
;
+
+describe('Greeting component', async assert => {
+ const $ = render();
+
+ assert({
+ given: 'a name prop',
+ should: 'render greeting with the name',
+ actual: $('h1').text(),
+ expected: 'Hello, World!'
+ });
+});
+```
+
+## Using match() for Content Verification
+
+The `match()` function is particularly useful for testing rendered content:
+
+```javascript
+const BlogPost = ({ title, content }) => (
+
+
{title}
+
{content}
+
+);
+
+describe('BlogPost component', async assert => {
+ const title = 'My Blog Post';
+ const content = 'This is the blog content.';
+ const $ = render();
+
+ // Using match to find specific text
+ const contains = match($('.content').html());
+
+ assert({
+ given: 'title and content props',
+ should: 'render the content',
+ actual: contains(content),
+ expected: content
+ });
+});
+```
+
+## Testing Component State and Props
+
+```javascript
+const Counter = ({ initialCount = 0 }) => {
+ const [count, setCount] = React.useState(initialCount);
+
+ return (
+
+
+);
+
+assert({
+ given: 'children content',
+ should: 'render children in main section',
+ actual: $('main p').text(),
+ expected: 'Content'
+});
+```
+
+## Best Practices
+
+1. **Test the rendered output, not implementation details**
+2. **Use `match()` for text content verification**
+3. **Test component behavior at different prop values**
+4. **Focus on what users see and interact with**
+5. **Keep component tests isolated and independent**
+6. **Use descriptive selectors based on CSS classes or structure**
+7. **Test both positive and negative cases (what should and shouldn't render)**
\ No newline at end of file
diff --git a/ai/debugging.md b/ai/debugging.md
new file mode 100644
index 00000000..64ad34d5
--- /dev/null
+++ b/ai/debugging.md
@@ -0,0 +1,392 @@
+# Debugging Common Issues in Riteway
+
+## Test Failures and Debugging
+
+### Understanding Test Output
+
+Riteway uses TAP format. A typical failure looks like:
+
+```
+not ok 5 Given invalid input: should throw an error
+ ---
+ operator: deepEqual
+ expected: Error: Invalid input
+ actual: undefined
+ ...
+```
+
+Key debugging information:
+- **Test number**: `5` - helps locate the failing test
+- **Description**: `Given invalid input: should throw an error` - from your `given` and `should` fields
+- **Expected vs Actual**: Shows what you expected vs what you got
+
+### Common Test Failure Patterns
+
+#### 1. Missing Error Handling
+```javascript
+// Problem: Function doesn't throw when it should
+describe('validateEmail()', async assert => {
+ assert({
+ given: 'invalid email',
+ should: 'throw validation error',
+ actual: Try(validateEmail, 'invalid'), // Returns undefined instead of Error
+ expected: new Error('Invalid email')
+ });
+});
+
+// Solution: Check your function implementation
+const validateEmail = (email) => {
+ if (!email.includes('@')) {
+ throw new Error('Invalid email'); // Make sure this actually throws
+ }
+ return email;
+};
+```
+
+#### 2. Async/Await Issues
+```javascript
+// Problem: Not awaiting async operations
+describe('fetchUser()', async assert => {
+ assert({
+ given: 'user ID',
+ should: 'return user data',
+ actual: fetchUser(123), // Missing await - returns Promise object
+ expected: { name: 'John' }
+ });
+});
+
+// Solution: Await the async operation
+describe('fetchUser()', async assert => {
+ const user = await fetchUser(123);
+
+ assert({
+ given: 'user ID',
+ should: 'return user data',
+ actual: user,
+ expected: { name: 'John' }
+ });
+});
+```
+
+#### 3. Object Comparison Issues
+```javascript
+// Problem: Comparing objects that aren't deeply equal
+assert({
+ given: 'user data',
+ should: 'create user object',
+ actual: createUser('John'), // Returns { id: 'abc123', name: 'John' }
+ expected: { name: 'John' } // Missing id field
+});
+
+// Solution: Match the exact structure or test specific properties
+assert({
+ given: 'user data',
+ should: 'create user with correct name',
+ actual: createUser('John').name,
+ expected: 'John'
+});
+```
+
+## Component Testing Debug Patterns
+
+### React Component Not Rendering
+```javascript
+// Problem: Component returns null or undefined
+const $ = render();
+
+assert({
+ given: 'show prop is false',
+ should: 'render nothing',
+ actual: $('.component').length, // Might be looking for wrong selector
+ expected: 0
+});
+
+// Debug: Check what's actually rendered
+console.log($.html()); // See the full HTML output
+```
+
+### CSS Selector Issues
+```javascript
+// Problem: Wrong CSS selector
+const $ = render(
John
);
+
+assert({
+ given: 'user component',
+ should: 'render user name',
+ actual: $('.user-name').text(), // Wrong class name
+ expected: 'John'
+});
+
+// Debug: Check available elements
+console.log($.html()); // See actual HTML structure
+console.log($('h1').text()); // Try different selectors
+```
+
+### Text Content Matching Issues
+```javascript
+// Problem: Extra whitespace or different content
+const $ = render(
Hello World
);
+
+assert({
+ given: 'text content',
+ should: 'match exactly',
+ actual: $('p').text(), // " Hello World " (with spaces)
+ expected: 'Hello World' // Without spaces
+});
+
+// Solution: Trim whitespace or use match()
+import match from 'riteway/match';
+
+const contains = match($('p').html());
+assert({
+ given: 'text content with spacing',
+ should: 'contain the expected text',
+ actual: contains('Hello World'),
+ expected: 'Hello World'
+});
+```
+
+## Build and Environment Issues
+
+### Babel Configuration Problems
+```javascript
+// Error: SyntaxError: Unexpected token '<'
+// Problem: JSX not being transpiled
+
+// Solution: Check .babelrc
+{
+ "presets": [
+ "@babel/preset-env",
+ "@babel/preset-react" // Make sure this is included
+ ]
+}
+```
+
+### Module Import Issues
+```javascript
+// Error: Cannot find module 'riteway/match'
+// Problem: Wrong import path
+
+// Wrong
+import match from 'riteway/match';
+
+// Correct
+import match from 'riteway/source/match';
+// OR if using ESM
+import match from 'riteway/esm/match';
+```
+
+### TypeScript Definition Issues
+```javascript
+// Error: Property 'given' does not exist on type...
+// Problem: TypeScript definitions out of sync
+
+// Check if using correct import
+import { describe } from 'riteway'; // Should have proper types
+
+// If types are missing, check index.d.ts file exists and is correct
+```
+
+## Debugging Strategies
+
+### 1. Isolate the Problem
+```javascript
+// Start with simplest possible test
+describe('debug test', async assert => {
+ assert({
+ given: 'simple input',
+ should: 'return simple output',
+ actual: 1 + 1,
+ expected: 2
+ });
+});
+```
+
+### 2. Log Intermediate Values
+```javascript
+describe('complex function', async assert => {
+ const input = { name: 'John', age: 30 };
+ const result = processUser(input);
+
+ console.log('Input:', input);
+ console.log('Result:', result);
+ console.log('Result type:', typeof result);
+
+ assert({
+ given: 'user input',
+ should: 'process correctly',
+ actual: result,
+ expected: expectedOutput
+ });
+});
+```
+
+### 3. Test Individual Properties
+```javascript
+// Instead of comparing entire objects
+assert({
+ given: 'complex object',
+ should: 'have correct name property',
+ actual: result.name,
+ expected: 'John'
+});
+
+assert({
+ given: 'complex object',
+ should: 'have correct age property',
+ actual: result.age,
+ expected: 30
+});
+```
+
+### 4. Use describe.only for Focus Testing
+```javascript
+// Only run this specific test
+describe.only('focused debug test', async assert => {
+ // Your debugging test here
+});
+```
+
+## Common Error Messages and Solutions
+
+### "Test exited without ending"
+```javascript
+// Problem: Async test not properly handled
+describe('async test', (assert, end) => {
+ setTimeout(() => {
+ assert({...});
+ // Missing end() call
+ }, 100);
+});
+
+// Solution: Use async/await OR call end()
+describe('async test', async assert => {
+ await delay(100);
+ assert({...});
+});
+```
+
+### "The following parameters are required by assert"
+```javascript
+// Problem: Missing required assert fields
+assert({
+ given: 'input',
+ // Missing 'should', 'actual', 'expected'
+});
+
+// Solution: Include all required fields
+assert({
+ given: 'input',
+ should: 'produce output',
+ actual: myFunction(input),
+ expected: expectedOutput
+});
+```
+
+### "Cannot read property of undefined"
+```javascript
+// Problem: Function returns undefined
+const result = myFunction();
+
+assert({
+ given: 'some input',
+ should: 'return object with property',
+ actual: result.property, // Error if result is undefined
+ expected: 'value'
+});
+
+// Solution: Check function return value first
+const result = myFunction();
+console.log('Function returned:', result);
+
+assert({
+ given: 'some input',
+ should: 'return defined result',
+ actual: result !== undefined,
+ expected: true
+});
+```
+
+## Performance Debugging
+
+### Slow Tests
+```javascript
+// Add timing to identify slow operations
+describe('performance test', async assert => {
+ const start = Date.now();
+ const result = expensiveOperation();
+ const end = Date.now();
+
+ console.log(`Operation took ${end - start}ms`);
+
+ assert({
+ given: 'expensive operation',
+ should: 'complete in reasonable time',
+ actual: (end - start) < 1000,
+ expected: true
+ });
+});
+```
+
+### Memory Issues
+```javascript
+// Check for memory leaks in component tests
+describe('component memory test', async assert => {
+ // Render many components
+ for (let i = 0; i < 1000; i++) {
+ const $ = render();
+ // Make sure components are properly cleaned up
+ }
+
+ assert({
+ given: 'many component renders',
+ should: 'not leak memory',
+ actual: process.memoryUsage().heapUsed < threshold,
+ expected: true
+ });
+});
+```
+
+## Tools for Debugging
+
+### Using Node.js Debugger
+```bash
+# Run tests with debugger
+node --inspect-brk -r @babel/register source/test.js
+
+# Then open Chrome DevTools
+# Navigate to chrome://inspect
+```
+
+### TAP Formatters for Better Output
+```bash
+# Install tap-nirvana for better test output
+npm install --save-dev tap-nirvana
+
+# Use in package.json
+"test": "riteway test/**/*-test.js | tap-nirvana"
+```
+
+### ESLint for Code Issues
+```bash
+# Run linting to catch common issues
+npm run lint
+
+# Auto-fix some issues
+npm run lint-fix
+```
+
+## Debugging Checklist
+
+When a test fails:
+
+1. ✅ **Read the error message carefully** - it usually tells you exactly what's wrong
+2. ✅ **Check that all assert fields are provided** - given, should, actual, expected
+3. ✅ **Verify async operations are awaited** - use `await` for promises
+4. ✅ **Test the function in isolation** - make sure it works outside the test
+5. ✅ **Log intermediate values** - use console.log to see what's happening
+6. ✅ **Check object structure** - make sure expected and actual have same shape
+7. ✅ **Use describe.only** - focus on just the failing test
+8. ✅ **Simplify the test** - start with the simplest case that works
+9. ✅ **Check environment setup** - babel, imports, dependencies
+10. ✅ **Review similar working tests** - see what's different
\ No newline at end of file
diff --git a/ai/refactoring.md b/ai/refactoring.md
new file mode 100644
index 00000000..e09c10db
--- /dev/null
+++ b/ai/refactoring.md
@@ -0,0 +1,553 @@
+# Refactoring Guidelines for Riteway
+
+## Safe Refactoring Patterns
+
+When refactoring code that uses Riteway, follow these patterns to maintain test reliability and readability.
+
+## Refactoring Test Code
+
+### Extracting Common Test Setup
+
+#### Before: Repeated Setup
+```javascript
+describe('UserService', async assert => {
+ const user = { id: 1, name: 'John', email: 'john@example.com' };
+ const userService = new UserService();
+
+ assert({
+ given: 'valid user data',
+ should: 'create user successfully',
+ actual: userService.create(user).name,
+ expected: 'John'
+ });
+});
+
+describe('UserService validation', async assert => {
+ const user = { id: 1, name: 'John', email: 'john@example.com' };
+ const userService = new UserService();
+
+ assert({
+ given: 'user with valid email',
+ should: 'pass validation',
+ actual: userService.validate(user),
+ expected: true
+ });
+});
+```
+
+#### After: Extracted Setup
+```javascript
+// Helper function for common setup
+const createTestUser = () => ({
+ id: 1,
+ name: 'John',
+ email: 'john@example.com'
+});
+
+const createUserService = () => new UserService();
+
+describe('UserService', async assert => {
+ const user = createTestUser();
+ const userService = createUserService();
+
+ assert({
+ given: 'valid user data',
+ should: 'create user successfully',
+ actual: userService.create(user).name,
+ expected: 'John'
+ });
+});
+
+describe('UserService validation', async assert => {
+ const user = createTestUser();
+ const userService = createUserService();
+
+ assert({
+ given: 'user with valid email',
+ should: 'pass validation',
+ actual: userService.validate(user),
+ expected: true
+ });
+});
+```
+
+### Grouping Related Assertions
+
+#### Before: Scattered Tests
+```javascript
+describe('Calculator add', async assert => {
+ assert({
+ given: 'positive numbers',
+ should: 'return sum',
+ actual: calculator.add(2, 3),
+ expected: 5
+ });
+});
+
+describe('Calculator subtract', async assert => {
+ assert({
+ given: 'positive numbers',
+ should: 'return difference',
+ actual: calculator.subtract(5, 3),
+ expected: 2
+ });
+});
+```
+
+#### After: Grouped Tests
+```javascript
+describe('Calculator operations', async assert => {
+ // Addition tests
+ assert({
+ given: 'positive numbers for addition',
+ should: 'return correct sum',
+ actual: calculator.add(2, 3),
+ expected: 5
+ });
+
+ // Subtraction tests
+ assert({
+ given: 'positive numbers for subtraction',
+ should: 'return correct difference',
+ actual: calculator.subtract(5, 3),
+ expected: 2
+ });
+});
+```
+
+### Improving Test Descriptions
+
+#### Before: Vague Descriptions
+```javascript
+describe('function test', async assert => {
+ assert({
+ given: 'input',
+ should: 'work',
+ actual: myFunction(input),
+ expected: output
+ });
+});
+```
+
+#### After: Clear Descriptions
+```javascript
+describe('formatCurrency()', async assert => {
+ assert({
+ given: 'a number with decimal places',
+ should: 'format as currency with dollar sign and two decimal places',
+ actual: formatCurrency(123.456),
+ expected: '$123.46'
+ });
+});
+```
+
+## Refactoring Production Code with Tests
+
+### Step-by-Step Refactoring Process
+
+1. **Ensure comprehensive test coverage**
+2. **Make one small change at a time**
+3. **Run tests after each change**
+4. **Keep the API unchanged until refactoring is complete**
+
+#### Example: Refactoring a Complex Function
+
+##### Before: Monolithic Function
+```javascript
+const processOrder = (order) => {
+ // Validation
+ if (!order.items || order.items.length === 0) {
+ throw new Error('Order must have items');
+ }
+
+ // Calculate total
+ let total = 0;
+ for (const item of order.items) {
+ total += item.price * item.quantity;
+ }
+
+ // Apply discount
+ if (order.discount) {
+ total = total * (1 - order.discount / 100);
+ }
+
+ // Add tax
+ const tax = total * 0.08;
+ total += tax;
+
+ return {
+ orderId: generateId(),
+ total: Math.round(total * 100) / 100,
+ tax,
+ status: 'processed'
+ };
+};
+```
+
+##### Step 1: Add Comprehensive Tests
+```javascript
+describe('processOrder()', async assert => {
+ const basicOrder = {
+ items: [
+ { price: 10.00, quantity: 2 },
+ { price: 5.00, quantity: 1 }
+ ]
+ };
+
+ assert({
+ given: 'order with basic items',
+ should: 'calculate correct total with tax',
+ actual: processOrder(basicOrder).total,
+ expected: 27.00 // (20 + 5) * 1.08
+ });
+
+ assert({
+ given: 'order with discount',
+ should: 'apply discount before tax',
+ actual: processOrder({
+ ...basicOrder,
+ discount: 10
+ }).total,
+ expected: 24.30 // (25 * 0.9) * 1.08
+ });
+
+ assert({
+ given: 'empty order',
+ should: 'throw validation error',
+ actual: Try(processOrder, { items: [] }),
+ expected: new Error('Order must have items')
+ });
+});
+```
+
+##### Step 2: Extract Validation
+```javascript
+const validateOrder = (order) => {
+ if (!order.items || order.items.length === 0) {
+ throw new Error('Order must have items');
+ }
+};
+
+const processOrder = (order) => {
+ validateOrder(order);
+
+ // Rest of function unchanged...
+};
+
+// Run tests - should still pass
+```
+
+##### Step 3: Extract Calculation Logic
+```javascript
+const calculateSubtotal = (items) => {
+ return items.reduce((total, item) => {
+ return total + (item.price * item.quantity);
+ }, 0);
+};
+
+const applyDiscount = (amount, discountPercent) => {
+ if (!discountPercent) return amount;
+ return amount * (1 - discountPercent / 100);
+};
+
+const calculateTax = (amount, taxRate = 0.08) => {
+ return amount * taxRate;
+};
+
+const processOrder = (order) => {
+ validateOrder(order);
+
+ const subtotal = calculateSubtotal(order.items);
+ const discountedAmount = applyDiscount(subtotal, order.discount);
+ const tax = calculateTax(discountedAmount);
+ const total = discountedAmount + tax;
+
+ return {
+ orderId: generateId(),
+ total: Math.round(total * 100) / 100,
+ tax: Math.round(tax * 100) / 100,
+ status: 'processed'
+ };
+};
+
+// Run tests - should still pass
+```
+
+##### Step 4: Add Tests for New Functions
+```javascript
+describe('calculateSubtotal()', async assert => {
+ assert({
+ given: 'array of items with price and quantity',
+ should: 'return sum of price * quantity',
+ actual: calculateSubtotal([
+ { price: 10, quantity: 2 },
+ { price: 5, quantity: 1 }
+ ]),
+ expected: 25
+ });
+});
+
+describe('applyDiscount()', async assert => {
+ assert({
+ given: 'amount and discount percentage',
+ should: 'return discounted amount',
+ actual: applyDiscount(100, 10),
+ expected: 90
+ });
+
+ assert({
+ given: 'amount with no discount',
+ should: 'return original amount',
+ actual: applyDiscount(100),
+ expected: 100
+ });
+});
+```
+
+## Refactoring Component Tests
+
+### Extracting Component Factories
+
+#### Before: Repeated Component Creation
+```javascript
+describe('UserCard component', async assert => {
+ const $ = render(
+
+ );
+
+ assert({
+ given: 'user with email',
+ should: 'display user name',
+ actual: $('.user-name').text(),
+ expected: 'John'
+ });
+});
+
+describe('UserCard with hidden email', async assert => {
+ const $ = render(
+
+ );
+
+ assert({
+ given: 'showEmail is false',
+ should: 'hide email address',
+ actual: $('.user-email').length,
+ expected: 0
+ });
+});
+```
+
+#### After: Component Factory
+```javascript
+const createUserCard = (props = {}) => {
+ const defaultProps = {
+ user: { name: 'John', email: 'john@example.com' },
+ showEmail: true
+ };
+
+ return render();
+};
+
+describe('UserCard component', async assert => {
+ const $ = createUserCard();
+
+ assert({
+ given: 'user with email',
+ should: 'display user name',
+ actual: $('.user-name').text(),
+ expected: 'John'
+ });
+});
+
+describe('UserCard with hidden email', async assert => {
+ const $ = createUserCard({ showEmail: false });
+
+ assert({
+ given: 'showEmail is false',
+ should: 'hide email address',
+ actual: $('.user-email').length,
+ expected: 0
+ });
+});
+```
+
+### Extracting Common Assertions
+
+#### Before: Repeated Assertions
+```javascript
+describe('Button variants', async assert => {
+ const primaryButton = render();
+
+ assert({
+ given: 'primary variant',
+ should: 'have primary class',
+ actual: primaryButton('.btn').hasClass('btn--primary'),
+ expected: true
+ });
+
+ const secondaryButton = render();
+
+ assert({
+ given: 'secondary variant',
+ should: 'have secondary class',
+ actual: secondaryButton('.btn').hasClass('btn--secondary'),
+ expected: true
+ });
+});
+```
+
+#### After: Assertion Helper
+```javascript
+const assertButtonVariant = (assert, variant) => {
+ const $ = render();
+
+ assert({
+ given: `${variant} variant`,
+ should: `have ${variant} class`,
+ actual: $('.btn').hasClass(`btn--${variant}`),
+ expected: true
+ });
+};
+
+describe('Button variants', async assert => {
+ assertButtonVariant(assert, 'primary');
+ assertButtonVariant(assert, 'secondary');
+});
+```
+
+## Legacy Code Refactoring
+
+### Adding Tests to Untested Code
+
+#### Step 1: Characterization Tests
+```javascript
+// First, test the current behavior (even if it's wrong)
+describe('legacyFunction() characterization', async assert => {
+ assert({
+ given: 'current implementation',
+ should: 'maintain existing behavior',
+ actual: legacyFunction('input'),
+ expected: currentOutput // Whatever it currently returns
+ });
+});
+```
+
+#### Step 2: Incremental Improvement
+```javascript
+// Add tests for desired behavior
+describe('legacyFunction() desired behavior', async assert => {
+ assert({
+ given: 'proper input handling',
+ should: 'return sanitized output',
+ actual: legacyFunction('input'),
+ expected: desiredOutput
+ });
+});
+
+// Then modify the function to pass new tests
+```
+
+### Dealing with Dependencies
+
+#### Before: Hard Dependencies
+```javascript
+const sendEmail = (user) => {
+ const emailService = new EmailService(); // Hard dependency
+ return emailService.send(user.email, 'Welcome!');
+};
+
+// Hard to test
+describe('sendEmail()', async assert => {
+ // This will actually send emails!
+ assert({
+ given: 'user with email',
+ should: 'send welcome email',
+ actual: sendEmail({ email: 'test@example.com' }),
+ expected: true
+ });
+});
+```
+
+#### After: Dependency Injection
+```javascript
+const sendEmail = (user, emailService = new EmailService()) => {
+ return emailService.send(user.email, 'Welcome!');
+};
+
+// Easy to test with mock
+const mockEmailService = {
+ send: (email, message) => `Sent "${message}" to ${email}`
+};
+
+describe('sendEmail()', async assert => {
+ assert({
+ given: 'user with email and mock service',
+ should: 'call email service with correct parameters',
+ actual: sendEmail({ email: 'test@example.com' }, mockEmailService),
+ expected: 'Sent "Welcome!" to test@example.com'
+ });
+});
+```
+
+## Refactoring Anti-Patterns to Avoid
+
+### Don't Change Tests and Code Simultaneously
+```javascript
+// BAD: Changing test and implementation together
+describe('newFunction()', async assert => {
+ assert({
+ given: 'new input format', // Changed test
+ should: 'return new format', // Changed test
+ actual: newFunction(newInput), // Changed implementation
+ expected: newOutput // Changed test
+ });
+});
+
+// GOOD: Change tests first, then implementation
+// 1. First, update tests for new desired behavior
+// 2. Run tests (they should fail)
+// 3. Update implementation to make tests pass
+```
+
+### Don't Remove Tests During Refactoring
+```javascript
+// BAD: Removing tests that became inconvenient
+// describe('oldBehavior()', async assert => {
+// // Commented out because it's hard to maintain
+// });
+
+// GOOD: Update tests to reflect new behavior
+describe('refactoredBehavior()', async assert => {
+ assert({
+ given: 'same input as before',
+ should: 'provide improved output',
+ actual: refactoredFunction(input),
+ expected: improvedOutput
+ });
+});
+```
+
+## Refactoring Checklist
+
+Before refactoring:
+- ✅ **Ensure comprehensive test coverage**
+- ✅ **All tests are passing**
+- ✅ **Understand the current behavior fully**
+
+During refactoring:
+- ✅ **Make small, incremental changes**
+- ✅ **Run tests after each change**
+- ✅ **Keep the public API stable**
+- ✅ **Add tests for new internal functions**
+
+After refactoring:
+- ✅ **All tests still pass**
+- ✅ **Code is more readable and maintainable**
+- ✅ **Performance hasn't degraded**
+- ✅ **Documentation is updated if needed**
\ No newline at end of file
diff --git a/ai/test-writing.md b/ai/test-writing.md
new file mode 100644
index 00000000..d0b7610a
--- /dev/null
+++ b/ai/test-writing.md
@@ -0,0 +1,165 @@
+# Test Writing Patterns for Riteway
+
+## Basic Test Structure
+
+Every Riteway test must follow this exact pattern:
+
+```javascript
+import { describe } from 'riteway';
+
+describe('functionName()', async assert => {
+ assert({
+ given: 'a clear description of the input or context',
+ should: 'describe what the function should do',
+ actual: functionName(input),
+ expected: expectedOutput
+ });
+});
+```
+
+## Key Principles
+
+1. **All four fields are required**: `given`, `should`, `actual`, `expected`
+2. **Use async functions**: Always use `async assert =>` for the test function
+3. **Test one thing per assertion**: Keep assertions focused and isolated
+4. **Use descriptive prose**: The `given` and `should` fields should read like clear English
+
+## Common Patterns
+
+### Testing Pure Functions
+```javascript
+describe('sum()', async assert => {
+ assert({
+ given: 'two positive numbers',
+ should: 'return their sum',
+ actual: sum(2, 3),
+ expected: 5
+ });
+
+ assert({
+ given: 'negative numbers',
+ should: 'handle negative values correctly',
+ actual: sum(-1, -2),
+ expected: -3
+ });
+});
+```
+
+### Testing Functions That Throw
+```javascript
+import { Try } from 'riteway';
+
+describe('validateEmail()', async assert => {
+ assert({
+ given: 'an invalid email',
+ should: 'throw a validation error',
+ actual: Try(validateEmail, 'invalid-email'),
+ expected: new Error('Invalid email format')
+ });
+});
+```
+
+### Testing Async Functions
+```javascript
+describe('fetchUser()', async assert => {
+ const user = await fetchUser(123);
+
+ assert({
+ given: 'a valid user ID',
+ should: 'return user data',
+ actual: user.name,
+ expected: 'John Doe'
+ });
+});
+```
+
+### Testing Object Properties
+```javascript
+describe('createUser()', async assert => {
+ const user = createUser('John', 'john@example.com');
+
+ assert({
+ given: 'name and email',
+ should: 'create user with correct name',
+ actual: user.name,
+ expected: 'John'
+ });
+
+ assert({
+ given: 'name and email',
+ should: 'create user with correct email',
+ actual: user.email,
+ expected: 'john@example.com'
+ });
+});
+```
+
+### Testing Array Operations
+```javascript
+describe('filterAdults()', async assert => {
+ const people = [
+ { name: 'Alice', age: 25 },
+ { name: 'Bob', age: 17 },
+ { name: 'Charlie', age: 30 }
+ ];
+
+ assert({
+ given: 'array of people with mixed ages',
+ should: 'return only adults',
+ actual: filterAdults(people).length,
+ expected: 2
+ });
+});
+```
+
+## Test Organization
+
+### Grouping Related Tests
+```javascript
+describe('Calculator operations', async assert => {
+ // Addition tests
+ assert({
+ given: 'two numbers for addition',
+ should: 'return correct sum',
+ actual: calculator.add(2, 3),
+ expected: 5
+ });
+
+ // Subtraction tests
+ assert({
+ given: 'two numbers for subtraction',
+ should: 'return correct difference',
+ actual: calculator.subtract(5, 3),
+ expected: 2
+ });
+});
+```
+
+### Using describe.only and describe.skip
+```javascript
+// Run only this test
+describe.only('focused test', async assert => {
+ // test implementation
+});
+
+// Skip this test
+describe.skip('broken test', async assert => {
+ // test implementation
+});
+```
+
+## Common Mistakes to Avoid
+
+1. **Missing required fields**: All four fields (given, should, actual, expected) are mandatory
+2. **Not using async**: Always use `async assert =>`
+3. **Testing multiple things in one assertion**: Keep each assertion focused
+4. **Unclear prose descriptions**: Make `given` and `should` read like clear English
+5. **Not handling errors properly**: Use `Try()` for functions that may throw
+
+## Best Practices
+
+1. **Start with the simplest case**: Test the happy path first
+2. **Test edge cases**: Consider null, undefined, empty arrays, etc.
+3. **Use meaningful test data**: Choose inputs that make the test intention clear
+4. **Keep tests independent**: Each test should work in isolation
+5. **Use descriptive names**: The `describe` parameter should clearly identify what's being tested
\ No newline at end of file
From 2eca00a5f11005d704319f21ddb8ceb88ec7dd21 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 20 Aug 2025 03:09:24 +0000
Subject: [PATCH 3/3] Add documentation for AI assistant setup
Co-authored-by: ericelliott <364727+ericelliott@users.noreply.github.com>
---
AI_SETUP.md | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 AI_SETUP.md
diff --git a/AI_SETUP.md b/AI_SETUP.md
new file mode 100644
index 00000000..bd3dc86e
--- /dev/null
+++ b/AI_SETUP.md
@@ -0,0 +1,38 @@
+# AI Development Assistant Setup
+
+This repository includes configuration files to help AI assistants (like GitHub Copilot, Cursor, and others) understand the Riteway testing library and provide better development assistance.
+
+## Files Added
+
+### `.cursorrules`
+Project-wide configuration that provides AI assistants with:
+- Complete project overview and architecture
+- Coding standards and patterns used in Riteway
+- Test writing conventions and best practices
+- Development workflow and common commands
+- Key design principles and guidelines
+
+### `/ai` Directory
+Collection of specialized prompts and documentation for common development scenarios:
+
+- **`README.md`** - Overview of available AI prompts
+- **`test-writing.md`** - Comprehensive patterns for writing Riteway tests
+- **`component-testing.md`** - React component testing with Riteway utilities
+- **`api-development.md`** - Guidelines for extending the Riteway API
+- **`debugging.md`** - Common issues and debugging strategies
+- **`refactoring.md`** - Safe refactoring patterns and techniques
+
+## Benefits
+
+These configurations help AI assistants:
+- Understand Riteway's unique testing philosophy (Readable, Isolated, Thorough, Explicit)
+- Generate proper test code following the required `{given, should, actual, expected}` pattern
+- Suggest appropriate debugging approaches for common test failures
+- Maintain code quality and consistency with project standards
+- Provide relevant examples and patterns for specific development tasks
+
+## Usage
+
+The configuration automatically activates when using compatible AI tools in this repository. No additional setup is required - AI assistants will automatically reference these files to provide contextually appropriate suggestions and help.
+
+For developers using Cursor, VS Code with Copilot, or other AI-enhanced editors, you should notice more accurate and helpful suggestions when working with Riteway code and tests.
\ No newline at end of file