From 4f3fe64cad09606f3aeaccdcceb14f220d2785f5 Mon Sep 17 00:00:00 2001 From: Experiments DB Dev Date: Sun, 19 Jul 2026 15:18:56 -0400 Subject: [PATCH] test(export): stale-group + same-date ordinal coverage; comment/guard polish --- frontend/src/components/ExportDataModal.jsx | 2 +- frontend/src/lib/dataExport.js | 4 +++- frontend/tests/ExportDataModal.test.jsx | 11 +++++++++++ frontend/tests/dataExport.test.js | 10 ++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ExportDataModal.jsx b/frontend/src/components/ExportDataModal.jsx index c97eedc..56c0eb2 100644 --- a/frontend/src/components/ExportDataModal.jsx +++ b/frontend/src/components/ExportDataModal.jsx @@ -44,7 +44,7 @@ export default function ExportDataModal({ return () => { alive = false; }; }, [experimentId]); - const params = useMemo(() => listExportableParams(dailyTemplate, statuses), [dailyTemplate, statuses]); + const params = useMemo(() => listExportableParams(dailyTemplate ?? [], statuses), [dailyTemplate, statuses]); const effDataId = dataId ?? params[0]?.id ?? null; const dataParam = params.find((p) => p.id === effDataId) ?? params[0] ?? null; diff --git a/frontend/src/lib/dataExport.js b/frontend/src/lib/dataExport.js index 27e5ed6..6ca80d3 100644 --- a/frontend/src/lib/dataExport.js +++ b/frontend/src/lib/dataExport.js @@ -207,7 +207,7 @@ function escapeCsvCell(value) { } // Serialize a matrix to an RFC-4180-ish CSV string (LF line endings): -// , +// Data, // (blank line) // [Group,] -- only when groupAxis === 'col' // [,Group?], @@ -222,6 +222,8 @@ export function toCSV(matrix) { lines.push(groupRow.map(escapeCsvCell).join(',')); } + // toCSV is a general serializer; the subject-series builder never emits groupAxis + // === 'row' (only 'col' or null), but the branch is retained for that generality. const header = matrix.groupAxis === 'row' ? ['Group', matrix.corner, ...matrix.columns.map((c) => c.label)] : [matrix.corner, ...matrix.columns.map((c) => c.label)]; diff --git a/frontend/tests/ExportDataModal.test.jsx b/frontend/tests/ExportDataModal.test.jsx index 2cdd755..de741fc 100644 --- a/frontend/tests/ExportDataModal.test.jsx +++ b/frontend/tests/ExportDataModal.test.jsx @@ -110,4 +110,15 @@ describe('ExportDataModal', () => { expect(text).toMatch(/^Group,Control,Drug$/m); await dl.restore(); }); + + it('a stale/unset group field coerces to None (no Group row in the export)', async () => { + const dl = mockDownload(); + renderModal({ groupField: 'ghost-field' }); + await screen.findByLabelText('X axis (rows)'); + await waitFor(() => expect(screen.getByText(/2 rows/i)).toBeInTheDocument()); + fireEvent.click(screen.getByText('Export CSV')); + const text = await readBlobText(dl.getBlob()); + expect(text).not.toMatch(/^Group,/m); + await dl.restore(); + }); }); diff --git a/frontend/tests/dataExport.test.js b/frontend/tests/dataExport.test.js index 4ecd091..0ea0add 100644 --- a/frontend/tests/dataExport.test.js +++ b/frontend/tests/dataExport.test.js @@ -237,6 +237,16 @@ describe('buildSampleIndex', () => { expect(idx.get('a1').get(250).date).toContain('2026-07-01'); expect(idx.get('a1').get(260).date).toContain('2026-07-03'); }); + it('# days reach counts per-status, not per-distinct-date (same-day statuses get separate ordinals)', () => { + const sameDay = [ + { animal_id: 'z1', date: '2026-07-01T08:00:00Z', analysis_summary: { total: 1 } }, + { animal_id: 'z1', date: '2026-07-01T20:00:00Z', analysis_summary: { total: 2 } }, + ]; + const idx = buildSampleIndex(sameDay, '__days__', []); + expect(idx.get('z1').has(1)).toBe(true); + expect(idx.get('z1').has(2)).toBe(true); + expect(idx.get('z1').has(3)).toBe(false); + }); }); import { buildSubjectSeriesMatrix } from '../src/lib/dataExport';