Skip to content

Commit 5fa9d91

Browse files
committed
fix: allow user to select columns, link to subject_id page from assets, add craniotomy/headframe details
1 parent a184e1c commit 5fa9d91

5 files changed

Lines changed: 442 additions & 132 deletions

File tree

web/src/__tests__/assets-view.test.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,42 +159,55 @@ describe('renderAssetRow', () => {
159159
process_date: null,
160160
};
161161

162+
const visibleColumns = [
163+
'subject_id',
164+
'acquisition_start_time',
165+
'project_name',
166+
'modalities',
167+
'data_level',
168+
'genotype',
169+
'links',
170+
];
171+
162172
it('returns a <tr> string', () => {
163-
const html = renderAssetRow(row);
173+
const html = renderAssetRow(row, visibleColumns);
164174
expect(html).toMatch(/^<tr>/);
165175
expect(html).toMatch(/<\/tr>$/);
166176
});
167177

168-
it('includes the subject_id', () => {
169-
expect(renderAssetRow(row)).toContain('12345');
178+
it('includes the subject_id as a link', () => {
179+
const html = renderAssetRow(row, visibleColumns);
180+
expect(html).toContain('/subject?subject_id=12345');
181+
expect(html).toContain('12345');
170182
});
171183

172184
it('includes formatted acquisition time', () => {
173-
expect(renderAssetRow(row)).toContain('2024-01-01 10:00');
185+
expect(renderAssetRow(row, visibleColumns)).toContain('2024-01-01 10:00');
174186
});
175187

176188
it('includes S3 console link', () => {
177-
const html = renderAssetRow(row);
189+
const html = renderAssetRow(row, visibleColumns);
178190
expect(html).toContain('s3.console.aws.amazon.com');
179191
});
180192

181193
it('includes QC, metadata, and CO links', () => {
182-
const html = renderAssetRow(row);
194+
const html = renderAssetRow(row, visibleColumns);
183195
expect(html).toContain('qc.allenneuraldynamics.org');
184196
expect(html).toContain('metadata-portal.allenneuraldynamics.org');
185197
expect(html).toContain('codeocean.allenneuraldynamics.org');
186198
});
187199

188200
it('renders fallback "—" when code_ocean is missing', () => {
189-
const html = renderAssetRow({ ...row, code_ocean: null });
201+
const html = renderAssetRow({ ...row, code_ocean: null }, visibleColumns);
190202
expect(html).toContain('no-link');
191203
});
192204

193205
it('handles missing optional fields without throwing', () => {
194-
expect(() => renderAssetRow({})).not.toThrow();
206+
expect(() => renderAssetRow({}, visibleColumns)).not.toThrow();
195207
});
196208
});
197209

210+
198211
// ---------------------------------------------------------------------------
199212
// sortRows
200213
// ---------------------------------------------------------------------------

web/src/__tests__/ephys-view.test.js

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
import { describe, it, expect, vi } from 'vitest';
88

9-
// Mock Three.js and OBJLoader so the ephys-viz-3d import doesn't fail in Node
109
vi.mock('three', () => ({ default: {} }));
1110
vi.mock('three/addons/loaders/OBJLoader.js', () => ({ OBJLoader: class {} }));
1211

13-
// Mock brain-viz-3d exports needed by ephys-viz-3d
1412
vi.mock('../subject/brain-viz-3d.js', () => ({
1513
STRUCTURE_COLORS: {},
1614
TARGET_X: 0, TARGET_Y: -3.668, TARGET_Z: -1.2,
@@ -21,25 +19,24 @@ vi.mock('../subject/brain-viz-3d.js', () => ({
2119
createBrainViz3D: () => document.createElement('div'),
2220
}));
2321

24-
// Mock brain-viz for ITEM_COLORS
2522
vi.mock('../subject/brain-viz.js', () => ({
2623
ITEM_COLORS: ['#FF6B6B', '#4ECDC4', '#45B7D1'],
2724
createBrainVizCanvas: () => ({ canvas: document.createElement('canvas') }),
2825
}));
2926

30-
// Mock assets/view.js
3127
vi.mock('../assets/view.js', () => ({
3228
buildQcLink: () => null,
3329
buildMetadataLink: () => null,
3430
buildCoLink: () => null,
3531
buildS3ConsoleUrl: () => null,
3632
}));
3733

38-
import { hasEphysAssemblies, buildEphysProbeCard } from '../subject/details.js';
39-
40-
// ---------------------------------------------------------------------------
41-
// Fixtures
42-
// ---------------------------------------------------------------------------
34+
import {
35+
hasEphysAssemblies,
36+
buildEphysProbeCard,
37+
buildCraniotomySubProcHtml,
38+
buildHeadframeSubProcHtml,
39+
} from '../subject/details.js';
4340

4441
const EPHYS_CONFIG = {
4542
object_type: 'Ephys assembly config',
@@ -93,10 +90,6 @@ const ACQUISITION_WITHOUT_EPHYS = {
9390
],
9491
};
9592

96-
// ---------------------------------------------------------------------------
97-
// hasEphysAssemblies
98-
// ---------------------------------------------------------------------------
99-
10093
describe('hasEphysAssemblies', () => {
10194
it('returns true when acquisition has at least one Ephys assembly config', () => {
10295
expect(hasEphysAssemblies(ACQUISITION_WITH_EPHYS)).toBe(true);
@@ -133,10 +126,6 @@ describe('hasEphysAssemblies', () => {
133126
});
134127
});
135128

136-
// ---------------------------------------------------------------------------
137-
// buildEphysProbeCard
138-
// ---------------------------------------------------------------------------
139-
140129
describe('buildEphysProbeCard', () => {
141130
const probe = {
142131
name: '46121',
@@ -229,3 +218,49 @@ describe('buildEphysProbeCard', () => {
229218
expect(html).toContain('Probe 3:');
230219
});
231220
});
221+
222+
describe('buildCraniotomySubProcHtml', () => {
223+
it('renders craniotomy-specific fields', () => {
224+
const html = buildCraniotomySubProcHtml({
225+
object_type: 'Craniotomy',
226+
protocol_id: null,
227+
craniotomy_type: 'Circle',
228+
coordinate_system_name: 'BREGMA_ARID',
229+
position: ['Origin'],
230+
size: 5,
231+
size_unit: 'millimeter',
232+
protective_material: null,
233+
implant_part_number: '5mm stacked coverslip',
234+
dura_removed: null,
235+
});
236+
237+
expect(html).toContain('Craniotomy');
238+
expect(html).toContain('Circle');
239+
expect(html).toContain('BREGMA_ARID');
240+
expect(html).toContain('Origin');
241+
expect(html).toContain('5 millimeter');
242+
expect(html).toContain('5mm stacked coverslip');
243+
expect(html).toContain('Unknown');
244+
});
245+
});
246+
247+
describe('buildHeadframeSubProcHtml', () => {
248+
it('renders headframe-specific fields', () => {
249+
const html = buildHeadframeSubProcHtml({
250+
object_type: 'Headframe',
251+
protocol_id: null,
252+
headframe_type: 'Visual Ctx',
253+
headframe_part_number: '0160-100-10',
254+
headframe_material: null,
255+
well_part_number: '0160-200-20',
256+
well_type: 'Mesoscope',
257+
});
258+
259+
expect(html).toContain('Headframe');
260+
expect(html).toContain('Visual Ctx');
261+
expect(html).toContain('0160-100-10');
262+
expect(html).toContain('0160-200-20');
263+
expect(html).toContain('Mesoscope');
264+
expect(html).toContain('Unknown');
265+
});
266+
});

0 commit comments

Comments
 (0)