Skip to content

Testing

WriteTrack can be mocked or used directly in tests depending on your needs.

For unit tests, mock the WriteTrack class to avoid DOM dependencies:

__mocks__/writetrack.ts
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,
}));
your-component.test.tsx
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();
});

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();
});

For integration tests, use WriteTrack with a real DOM:

vitest.config.ts
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();
});

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();
});
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();
});
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');
});
});
test/factories.ts
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,
};
}
// Usage
const event = createKeystrokeEvent({
key: 'Backspace',
code: 'Backspace',
isCorrection: true,
});