Testing
FitFileViewer uses root-owned Vitest and Playwright tooling for test coverage.
Test Structureβ
tests/
βββ unit/ # Root-owned unit tests for tooling, runtime, rendering, maps, charts, and shared UI behavior
β βββ strictTests/ # Browser-oriented strict regression tests
βββ integration/ # Root-owned Vitest integration tests
βββ fixtures/ # Reusable Vitest fixtures
βββ playwright/ # Electron Playwright smoke tests
βββ vitest/ # Shared Vitest setup, shims, and stubs
Running Testsβ
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Run in watch mode
npm run test:watch
# Run specific file
npm test -- tests/unit/rendering/helpers/renderSummaryHelpers.test.ts
# Run with pattern
npm test -- --grep "formatDistance"
Writing Testsβ
Basic Test Structureβ
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { formatDistance } from "../../../../electron-app/utils/formatting/formatDistance.js";
describe("formatDistance", () => {
describe("basic formatting", () => {
it("should format meters to kilometers", () => {
expect(formatDistance(5000)).toBe("5.00 km");
});
it("should handle decimal values", () => {
expect(formatDistance(5500)).toBe("5.50 km");
});
});
describe("edge cases", () => {
it("should handle zero", () => {
expect(formatDistance(0)).toBe("0.00 km");
});
it("should handle negative values", () => {
expect(formatDistance(-1000)).toBe("-1.00 km");
});
});
});
Testing with Setup/Teardownβ
describe("StateManager", () => {
let stateManager;
beforeEach(() => {
stateManager = new StateManager();
});
afterEach(() => {
stateManager.reset();
});
it("should store values", () => {
stateManager.set("key", "value");
expect(stateManager.get("key")).toBe("value");
});
});
Testing Async Functionsβ
describe("loadFile", () => {
it("should load file successfully", async () => {
const result = await loadFile("test.fit");
expect(result).toBeDefined();
expect(result.records).toBeInstanceOf(Array);
});
it("should reject invalid files", async () => {
await expect(loadFile("invalid.txt")).rejects.toThrow("Invalid file type");
});
});
Mockingβ
import { vi } from "vitest";
describe("with mocks", () => {
it("should call callback", () => {
const callback = vi.fn();
processData(data, callback);
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledWith(expectedData);
});
});
Test Categoriesβ
Unit Testsβ
Test individual functions in isolation:
// Test pure functions
describe("calculateAverage", () => {
it("should calculate average", () => {
expect(calculateAverage([1, 2, 3])).toBe(2);
});
});
Integration Testsβ
Test module interactions:
// Test components working together
describe("FileProcessor", () => {
it("should parse and format file data", async () => {
const fileData = await loadFile("test.fit");
const formatted = formatFileData(fileData);
expect(formatted.summary).toBeDefined();
expect(formatted.records.length).toBeGreaterThan(0);
});
});
E2E Testsβ
Test full user workflows:
// Test complete user scenarios
describe("User opens FIT file", () => {
it("should display data in all tabs", async () => {
await openFile("activity.fit");
await clickTab("Map");
expect(await isMapVisible()).toBe(true);
await clickTab("Charts");
expect(await areChartsVisible()).toBe(true);
});
});
Coverageβ
View Coverage Reportβ
npm run test:coverage
Coverage Targetsβ
| Type | Target |
|---|---|
| Statements | 80%+ |
| Branches | 75%+ |
| Functions | 80%+ |
| Lines | 80%+ |
Coverage Configurationβ
// vitest.config.ts (root-owned)
export default {
test: {
coverage: {
provider: "v8",
reporter: ["text", "html", "lcov"],
exclude: ["tests/", "node_modules/"],
},
},
};
Best Practicesβ
Doβ
- β Test one thing per test
- β Use descriptive test names
- β Test edge cases
- β Test error conditions
- β Keep tests independent
Don'tβ
- β Test implementation details
- β Use magic numbers
- β Leave console.log in tests
- β Depend on test order
Test Namingβ
// Good: Describes behavior
it("should return formatted string when given valid input", () => {});
it("should throw error when input is null", () => {});
// Bad: Vague
it("works", () => {});
it("test1", () => {});
Next: Build & Release β