test(04-02): filter dropdown tests for project selection, fetch, and empty state
This commit is contained in:
parent
4b87dbf087
commit
b50972ac59
1 changed files with 152 additions and 0 deletions
152
assets/test/filter.test.ts
Normal file
152
assets/test/filter.test.ts
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { init } from '../src/heatmap';
|
||||||
|
import type { HeatmapData } from '../src/types';
|
||||||
|
|
||||||
|
const MOCK_DATA: HeatmapData = {
|
||||||
|
days: [
|
||||||
|
{ date: '2025-01-06', hours: 2.5, count: 3 },
|
||||||
|
{ date: '2025-01-07', hours: 5.0, count: 5 },
|
||||||
|
],
|
||||||
|
range: { begin: '2025-01-01', end: '2025-01-14' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_DATA: HeatmapData = {
|
||||||
|
days: [],
|
||||||
|
range: { begin: '2025-01-01', end: '2025-01-14' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const PROJECTS = [
|
||||||
|
{ id: 1, name: 'Alpha' },
|
||||||
|
{ id: 2, name: 'Beta' },
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('filter dropdown', () => {
|
||||||
|
let container: HTMLDivElement;
|
||||||
|
let fetchMock: ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.setAttribute('data-url', '/heatmap/data');
|
||||||
|
container.setAttribute('data-timesheet-url', '/en/timesheet/');
|
||||||
|
container.setAttribute('data-projects', JSON.stringify(PROJECTS));
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
fetchMock = vi.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve(MOCK_DATA),
|
||||||
|
});
|
||||||
|
vi.stubGlobal('fetch', fetchMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.removeChild(container);
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders select with form-select class when projects exist', () => {
|
||||||
|
init(container);
|
||||||
|
const select = container.querySelector('select.form-select');
|
||||||
|
expect(select).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render select when no projects', () => {
|
||||||
|
container.setAttribute('data-projects', '[]');
|
||||||
|
init(container);
|
||||||
|
const select = container.querySelector('select.form-select');
|
||||||
|
expect(select).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has "All Projects" as first option', () => {
|
||||||
|
init(container);
|
||||||
|
const options = container.querySelectorAll('select.form-select option');
|
||||||
|
expect(options[0].textContent).toBe('All Projects');
|
||||||
|
expect((options[0] as HTMLOptionElement).value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has one option per project', () => {
|
||||||
|
init(container);
|
||||||
|
const options = container.querySelectorAll('select.form-select option');
|
||||||
|
expect(options.length).toBe(3); // All Projects + 2 projects
|
||||||
|
expect((options[1] as HTMLOptionElement).value).toBe('1');
|
||||||
|
expect(options[1].textContent).toBe('Alpha');
|
||||||
|
expect((options[2] as HTMLOptionElement).value).toBe('2');
|
||||||
|
expect(options[2].textContent).toBe('Beta');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has aria-label for accessibility', () => {
|
||||||
|
init(container);
|
||||||
|
const select = container.querySelector('select.form-select');
|
||||||
|
expect(select?.getAttribute('aria-label')).toBe('Filter by project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetches with project param on selection', async () => {
|
||||||
|
init(container);
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = container.querySelector('select.form-select') as HTMLSelectElement;
|
||||||
|
select.value = '1';
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
expect(fetchMock.mock.calls[1][0]).toBe('/heatmap/data?project=1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetches without project param for All Projects', async () => {
|
||||||
|
init(container);
|
||||||
|
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
|
||||||
|
|
||||||
|
const select = container.querySelector('select.form-select') as HTMLSelectElement;
|
||||||
|
select.value = '1';
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2));
|
||||||
|
|
||||||
|
select.value = '';
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(3));
|
||||||
|
expect(fetchMock.mock.calls[2][0]).toBe('/heatmap/data');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('re-renders heatmap after filter change', async () => {
|
||||||
|
init(container);
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(container.querySelector('rect.heatmap-cell')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = container.querySelector('select.form-select') as HTMLSelectElement;
|
||||||
|
select.value = '1';
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(container.querySelector('rect.heatmap-cell')).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows filtered empty message when no data for project', async () => {
|
||||||
|
fetchMock
|
||||||
|
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(MOCK_DATA) })
|
||||||
|
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(EMPTY_DATA) });
|
||||||
|
|
||||||
|
init(container);
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(container.querySelector('rect.heatmap-cell')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = container.querySelector('select.form-select') as HTMLSelectElement;
|
||||||
|
select.value = '1';
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(container.textContent).toContain('No tracking data for this project');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Reference in a new issue