Testing
WriteTrack can be mocked or used directly in tests depending on your needs.
Mocking WriteTrack
Section titled “Mocking WriteTrack”For unit tests, mock the WriteTrack class to avoid DOM dependencies:
Jest / Vitest
Section titled “Jest / Vitest”export class WriteTrack { start = vi.fn(); stop = vi.fn(); getRawEvents = vi.fn(() => [ { key: 'h', code: 'KeyH', type: 'keydown', timestamp: 100, windowFocused: true, timeSinceLastMouse: 0, }, { key: 'i', code: 'KeyI', type: 'keydown', timestamp: 220, flightTime: 120, windowFocused: true, timeSinceLastMouse: 0, }, ]); getData = vi.fn(() => ({ version: '2.1.0', metadata: { tool: { name: 'writetrack', version: '0.5.0' } }, session: { events: [] }, quality: { qualityLevel: 'GOOD', overallScore: 0.8 }, })); getKeystrokeCount = vi.fn(() => 100);}If your component uses the React or Vue hook, mock the sub-path export separately:
// __mocks__/writetrack/react.ts (or writetrack/vue.ts)export const useWriteTrack = vi.fn(() => ({ start: vi.fn(), stop: vi.fn(), reset: vi.fn(), isTracking: true, tracker: null,}));vi.mock('writetrack');vi.mock('writetrack/react'); // Mock the hook's sub-path export
import { render, screen, fireEvent } from '@testing-library/react';import { ResponseForm } from './ResponseForm';
test('submits form with typing data', async () => { render(<ResponseForm />);
fireEvent.click(screen.getByText('Submit'));
expect(screen.getByText('Submitted successfully')).toBeInTheDocument();});Testing Different Scenarios
Section titled “Testing Different Scenarios”Control mock return values per test:
import { WriteTrack } from 'writetrack';
vi.mock('writetrack');
const mockWriteTrack = vi.mocked(WriteTrack);
test('shows message when keystroke count is low', () => { mockWriteTrack.prototype.getKeystrokeCount.mockReturnValue(5);
render(<ResponseForm />); fireEvent.click(screen.getByText('Submit'));
expect(screen.getByText('Please type more before submitting')).toBeInTheDocument();});Integration Testing
Section titled “Integration Testing”For integration tests, use WriteTrack with a real DOM:
With jsdom
Section titled “With jsdom”export default { test: { environment: 'jsdom', },};import { WriteTrack } from 'writetrack';
test('captures keystrokes', () => { document.body.innerHTML = '<textarea id="input"></textarea>'; const textarea = document.getElementById('input') as HTMLTextAreaElement;
const tracker = new WriteTrack({ target: textarea }); tracker.start();
// Simulate keystrokes textarea.focus(); fireEvent.keyDown(textarea, { key: 'a', code: 'KeyA' }); fireEvent.keyUp(textarea, { key: 'a', code: 'KeyA' }); fireEvent.keyDown(textarea, { key: 'b', code: 'KeyB' }); fireEvent.keyUp(textarea, { key: 'b', code: 'KeyB' });
const events = tracker.getRawEvents(); expect(events.length).toBeGreaterThan(0);
tracker.stop();});Simulating Typing Patterns
Section titled “Simulating Typing Patterns”Create helpers to simulate realistic typing:
async function simulateTyping( element: HTMLElement, text: string, options: { avgDelay?: number; variance?: number } = {}) { const { avgDelay = 100, variance = 30 } = options;
element.focus();
for (const char of text) { const delay = avgDelay + (Math.random() - 0.5) * variance * 2; await new Promise((r) => setTimeout(r, delay));
const code = `Key${char.toUpperCase()}`; fireEvent.keyDown(element, { key: char, code });
await new Promise((r) => setTimeout(r, 50 + Math.random() * 30)); fireEvent.keyUp(element, { key: char, code }); }}
test('captures keystroke events from typing', async () => { const textarea = document.createElement('textarea'); document.body.appendChild(textarea);
const tracker = new WriteTrack({ target: textarea }); tracker.start();
await simulateTyping(textarea, 'hello world', { avgDelay: 120 });
const events = tracker.getRawEvents(); expect(events.length).toBeGreaterThan(0);
tracker.stop();});E2E Testing
Section titled “E2E Testing”Playwright
Section titled “Playwright”import { test, expect } from '@playwright/test';
test('captures typing data on form submit', async ({ page }) => { await page.goto('/form');
const textarea = page.locator('#response-field');
// Type with realistic delays await textarea.focus(); await textarea.pressSequentially('This is my response content.', { delay: 100, });
await page.click('button[type="submit"]');
// Check submission succeeded await expect(page.locator('.success-message')).toBeVisible();});Cypress
Section titled “Cypress”describe('Response Form', () => { it('captures typing data', () => { cy.visit('/form');
cy.get('#response-field') .focus() .type('This is typed content.', { delay: 100 });
cy.get('button[type="submit"]').click();
cy.contains('Submitted successfully').should('be.visible'); });});Testing Utilities
Section titled “Testing Utilities”Factory Function
Section titled “Factory Function”import type { KeystrokeEvent } from 'writetrack';
export function createKeystrokeEvent( overrides: Partial<KeystrokeEvent> = {}): KeystrokeEvent { return { key: 'a', code: 'KeyA', type: 'keydown', timestamp: Date.now(), windowFocused: true, timeSinceLastMouse: 0, ...overrides, };}
// Usageconst event = createKeystrokeEvent({ key: 'Backspace', code: 'Backspace', isCorrection: true,});