-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathreport.test.ts
More file actions
104 lines (90 loc) · 4.34 KB
/
Copy pathreport.test.ts
File metadata and controls
104 lines (90 loc) · 4.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { describe, it, expect } from 'vitest';
import {
ReportSchema,
ReportChartSchema,
ReportType,
Report,
JoinedReportBlockSchema,
} from './report.zod';
/**
* ADR-0021 single-form: a report binds a `dataset` and selects `rows`
* (dimensions) + `values` (measures). The legacy inline `objectName` +
* `columns` + `groupings` query was removed in the cutover. A `joined` report
* carries its data on `blocks`, each itself dataset-bound.
*/
describe('ReportSchema (dataset-bound)', () => {
it('accepts a summary report (dataset + rows + values)', () => {
const r = ReportSchema.parse({
name: 'sales_by_stage', label: 'Sales by Stage', type: 'summary',
dataset: 'sales', rows: ['stage'], values: ['revenue'],
});
expect(r.dataset).toBe('sales');
expect(r.rows).toEqual(['stage']);
});
it('accepts a matrix report (rows down × columns across) + runtimeFilter', () => {
const r = ReportSchema.parse({
name: 'hours_matrix', label: 'Hours', type: 'matrix',
dataset: 'tasks', rows: ['owner'], columns: ['category'], values: ['est_hours', 'actual_hours'],
runtimeFilter: { is_completed: true },
});
expect(r.rows).toEqual(['owner']);
expect(r.columns).toEqual(['category']);
expect(r.runtimeFilter).toEqual({ is_completed: true });
});
it('drilldown defaults on and can be disabled', () => {
const on = ReportSchema.parse({ name: 'r1', label: 'R', type: 'summary', dataset: 'sales', rows: ['stage'], values: ['revenue'] });
expect(on.drilldown).toBe(true);
const off = ReportSchema.parse({ name: 'r2', label: 'R', type: 'summary', dataset: 'sales', rows: ['stage'], values: ['revenue'], drilldown: false });
expect(off.drilldown).toBe(false);
});
it('accepts an embedded chart', () => {
const r = ReportSchema.parse({
name: 'rep_x', label: 'R', type: 'summary', dataset: 'sales', rows: ['stage'], values: ['revenue'],
chart: { type: 'bar', xAxis: 'stage', yAxis: 'revenue' },
});
expect(r.chart?.xAxis).toBe('stage');
});
it('rejects a (non-joined) report with no dataset', () => {
expect(() => ReportSchema.parse({ name: 'rep_x', label: 'R', type: 'summary', rows: ['stage'], values: ['revenue'] })).toThrow();
});
it('rejects a (non-joined) report with no values', () => {
expect(() => ReportSchema.parse({ name: 'rep_x', label: 'R', type: 'summary', dataset: 'sales', rows: ['stage'] })).toThrow();
});
it('a report supplying only the removed inline fields is invalid', () => {
expect(() => ReportSchema.parse({ name: 'rep_x', label: 'R', type: 'summary', objectName: 'opportunity', columns: [{ field: 'amount' }] } as any)).toThrow();
});
it('Report.create factory parses + returns a typed report', () => {
const r = Report.create({ name: 'rep_x', label: 'R', type: 'summary', dataset: 'sales', rows: ['stage'], values: ['revenue'] });
expect(r.name).toBe('rep_x');
});
it('ReportType enum', () => {
expect(ReportType.parse('matrix')).toBe('matrix');
expect(() => ReportType.parse('nope')).toThrow();
});
});
describe('Joined reports', () => {
it('accepts a joined report whose blocks are dataset-bound', () => {
const r = ReportSchema.parse({
name: 'overview', label: 'Overview', type: 'joined',
blocks: [
{ name: 'open_block', label: 'Open', type: 'summary', dataset: 'tasks', rows: ['status'], values: ['task_count'], runtimeFilter: { done: false } },
{ name: 'done_block', label: 'Done', type: 'summary', dataset: 'tasks', rows: ['status'], values: ['task_count'], runtimeFilter: { done: true } },
],
});
expect(r.blocks).toHaveLength(2);
});
it('rejects a joined report with no blocks', () => {
expect(() => ReportSchema.parse({ name: 'rep_x', label: 'R', type: 'joined' })).toThrow();
});
it('JoinedReportBlockSchema parses a dataset-bound block', () => {
const b = JoinedReportBlockSchema.parse({ name: 'blk_x', type: 'summary', dataset: 'tasks', rows: ['status'], values: ['task_count'] });
expect(b.dataset).toBe('tasks');
});
});
describe('ReportChartSchema', () => {
it('requires xAxis + yAxis', () => {
expect(ReportChartSchema.parse({ type: 'bar', xAxis: 'stage', yAxis: 'revenue' }).type).toBe('bar');
expect(() => ReportChartSchema.parse({ type: 'bar' })).toThrow();
});
});