From 881718e3f06b07ee9e25c1c9767aa8f2d6aa3e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Thu, 9 Apr 2026 09:47:18 +0200 Subject: [PATCH] refactor(06-02): migrate test imports to new module locations --- assets/test/heatmap.test.ts | 51 +++++++++++++++++++++++---------- assets/test/interaction.test.ts | 30 +++++++++++++++---- assets/test/stats.test.ts | 2 +- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/assets/test/heatmap.test.ts b/assets/test/heatmap.test.ts index d32ef10..a679b86 100644 --- a/assets/test/heatmap.test.ts +++ b/assets/test/heatmap.test.ts @@ -1,6 +1,20 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { renderHeatmap } from '../src/heatmap'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { YearModeRenderer } from '../src/renderers/year'; +import type { RenderContext } from '../src/renderers/types'; import type { HeatmapData } from '../src/types'; +import { createInitialState } from '../src/state'; + +const DEFAULT_CONFIG = { cellSize: 13, cellGap: 2, marginTop: 20, marginLeft: 30, marginBottom: 4 }; + +function makeCtx(container: HTMLElement, data: HeatmapData, overrides?: Partial): RenderContext { + return { + container, + data, + state: createInitialState('monday'), + config: DEFAULT_CONFIG, + ...overrides, + }; +} function makeMockData(overrides: Partial = {}): HeatmapData { return { @@ -21,34 +35,41 @@ function makeMockData(overrides: Partial = {}): HeatmapData { describe('renderHeatmap', () => { let container: HTMLDivElement; + let renderer: YearModeRenderer; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); + renderer = new YearModeRenderer(); + }); + + afterEach(() => { + renderer.destroy(); + document.querySelectorAll('.heatmap-tooltip').forEach(el => el.remove()); }); it('renders an SVG element', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const svg = container.querySelector('svg'); expect(svg).not.toBeNull(); }); it('renders correct number of rect elements for date range', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const rects = container.querySelectorAll('rect.heatmap-cell'); // Jan 1 to Jan 31 = 31 days expect(rects.length).toBe(31); }); it('applies heatmap-empty class to cells with no data', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const emptyRects = container.querySelectorAll('rect.heatmap-empty'); // 31 days total, 5 with data = 26 empty expect(emptyRects.length).toBe(26); }); it('applies fill attribute to cells with data', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const allRects = container.querySelectorAll('rect.heatmap-cell:not(.heatmap-empty)'); expect(allRects.length).toBe(5); allRects.forEach((rect) => { @@ -57,7 +78,7 @@ describe('renderHeatmap', () => { }); it('renders day labels (Mon, Wed, Fri)', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const dayLabels = container.querySelectorAll('text.day-label'); expect(dayLabels.length).toBe(7); // all 7 slots rendered const texts = Array.from(dayLabels).map((el) => el.textContent); @@ -75,7 +96,7 @@ describe('renderHeatmap', () => { { date: '2025-02-10', hours: 4.0, count: 1 }, ], }); - renderHeatmap(container, data); + renderer.render(makeCtx(container, data)); const monthLabels = container.querySelectorAll('text.month-label'); expect(monthLabels.length).toBeGreaterThan(0); const texts = Array.from(monthLabels).map((el) => el.textContent); @@ -83,7 +104,7 @@ describe('renderHeatmap', () => { }); it('creates tooltip on mouseenter', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const rect = container.querySelector( 'rect.heatmap-cell:not(.heatmap-empty)', ); @@ -102,7 +123,7 @@ describe('renderHeatmap', () => { }); it('hides tooltip on mouseleave', () => { - renderHeatmap(container, makeMockData()); + renderer.render(makeCtx(container, makeMockData())); const rect = container.querySelector( 'rect.heatmap-cell:not(.heatmap-empty)', ); @@ -115,7 +136,7 @@ describe('renderHeatmap', () => { }); it('handles empty days array gracefully', () => { - renderHeatmap(container, makeMockData({ days: [] })); + renderer.render(makeCtx(container, makeMockData({ days: [] }))); const svg = container.querySelector('svg'); expect(svg).toBeNull(); expect(container.textContent).toContain('No tracking data available'); @@ -129,7 +150,7 @@ describe('renderHeatmap', () => { ], range: { begin: '2025-03-01', end: '2025-03-07' }, }; - renderHeatmap(container, data); + renderer.render(makeCtx(container, data)); const rects = container.querySelectorAll('rect.heatmap-cell'); expect(rects.length).toBe(7); }); @@ -142,7 +163,7 @@ describe('renderHeatmap', () => { ], range: { begin: '2025-01-01', end: '2025-01-07' }, }; - renderHeatmap(container, data); + renderer.render(makeCtx(container, data)); const weekendRects = container.querySelectorAll('rect.heatmap-weekend'); // Jan 1 (Wed) through Jan 7 (Tue): Sat Jan 4 + Sun Jan 5 = 2 weekend cells expect(weekendRects.length).toBe(2); @@ -155,14 +176,14 @@ describe('renderHeatmap', () => { ], range: { begin: '2025-01-06', end: '2025-01-10' }, }; - renderHeatmap(container, data); + renderer.render(makeCtx(container, data)); const weekendRects = container.querySelectorAll('rect.heatmap-weekend'); // Mon-Fri, no weekends expect(weekendRects.length).toBe(0); }); it('renders day labels for Sunday week start', () => { - renderHeatmap(container, makeMockData(), undefined, undefined, undefined, 'sunday'); + renderer.render(makeCtx(container, makeMockData(), { state: createInitialState('sunday') })); const dayLabels = container.querySelectorAll('text.day-label'); const texts = Array.from(dayLabels).map((el) => el.textContent); expect(texts).toContain('Sun'); diff --git a/assets/test/interaction.test.ts b/assets/test/interaction.test.ts index be37a54..36cb57e 100644 --- a/assets/test/interaction.test.ts +++ b/assets/test/interaction.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { renderHeatmap, init } from '../src/heatmap'; +import { init } from '../src/heatmap'; +import { YearModeRenderer } from '../src/renderers/year'; +import { createInitialState } from '../src/state'; import type { HeatmapData } from '../src/types'; const MOCK_DATA: HeatmapData = { @@ -10,22 +12,31 @@ const MOCK_DATA: HeatmapData = { range: { begin: '2025-01-01', end: '2025-01-14' }, }; +const DEFAULT_CONFIG = { cellSize: 13, cellGap: 2, marginTop: 20, marginLeft: 30, marginBottom: 4 }; + describe('click navigation', () => { let container: HTMLDivElement; + let renderer: YearModeRenderer; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); + renderer = new YearModeRenderer(); }); afterEach(() => { + renderer.destroy(); + document.querySelectorAll('.heatmap-tooltip').forEach(el => el.remove()); document.body.removeChild(container); vi.restoreAllMocks(); }); it('calls onCellClick with dateStr when a data cell is clicked', () => { const onClick = vi.fn(); - renderHeatmap(container, MOCK_DATA, undefined, onClick); + renderer.render({ + container, data: MOCK_DATA, state: createInitialState('monday'), + config: DEFAULT_CONFIG, onCellClick: onClick, + }); const cell = container.querySelector('rect.heatmap-cell:not(.heatmap-empty)') as SVGRectElement; cell.dispatchEvent(new MouseEvent('click', { bubbles: true })); expect(onClick).toHaveBeenCalledOnce(); @@ -34,20 +45,29 @@ describe('click navigation', () => { it('calls onCellClick when an empty cell is clicked', () => { const onClick = vi.fn(); - renderHeatmap(container, MOCK_DATA, undefined, onClick); + renderer.render({ + container, data: MOCK_DATA, state: createInitialState('monday'), + config: DEFAULT_CONFIG, onCellClick: onClick, + }); const emptyCell = container.querySelector('rect.heatmap-empty') as SVGRectElement; emptyCell.dispatchEvent(new MouseEvent('click', { bubbles: true })); expect(onClick).toHaveBeenCalledOnce(); }); it('does not throw when no onCellClick provided', () => { - renderHeatmap(container, MOCK_DATA); + renderer.render({ + container, data: MOCK_DATA, state: createInitialState('monday'), + config: DEFAULT_CONFIG, + }); const cell = container.querySelector('rect.heatmap-cell') as SVGRectElement; expect(() => cell.dispatchEvent(new MouseEvent('click', { bubbles: true }))).not.toThrow(); }); it('all cells have heatmap-cell class for cursor styling', () => { - renderHeatmap(container, MOCK_DATA); + renderer.render({ + container, data: MOCK_DATA, state: createInitialState('monday'), + config: DEFAULT_CONFIG, + }); const allRects = container.querySelectorAll('rect.heatmap-cell'); expect(allRects.length).toBeGreaterThan(0); allRects.forEach((rect) => { diff --git a/assets/test/stats.test.ts b/assets/test/stats.test.ts index 16d53bc..890af39 100644 --- a/assets/test/stats.test.ts +++ b/assets/test/stats.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { timeFormat } from 'd3-time-format'; import { timeDay } from 'd3-time'; -import { calculateStreak, calculateStats } from '../src/heatmap'; +import { calculateStreak, calculateStats } from '../src/shared/stats'; import type { DayEntry } from '../src/types'; const DATE_FORMAT = timeFormat('%Y-%m-%d');