feat(org-chart): finalize chart features and AI generation rules
This commit is contained in:
1 parent
7614507583
commit
d0828a7ff4
80 files changed
+8152
-221
No files matched your search
@@ -0,0 +1,111 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fixture from '../../../../examples/org-chart/data-expanded-large.json';
|
||||
import { companyColorPresets, type CompanyId } from '../company-color-presets.js';
|
||||
import {
|
||||
calculateSummary, createOrgChartExampleDataset, JsonOrgChartDataSource,
|
||||
normalizeOrgChartDataset, snapshotOrgChartDataset, validateOrgChartDataset,
|
||||
type OrgChartDataset, type OrgChartExampleEmployeeCount,
|
||||
} from './index.js';
|
||||
|
||||
const base = fixture as OrgChartDataset;
|
||||
const companies = Object.keys(companyColorPresets) as CompanyId[];
|
||||
const sizes: OrgChartExampleEmployeeCount[] = [20, 100, 300];
|
||||
|
||||
function assertContract(dataset: OrgChartDataset) {
|
||||
const result = validateOrgChartDataset(dataset);
|
||||
assert.equal(result.valid, true, result.valid ? undefined : JSON.stringify(result.issues));
|
||||
assert.equal(new Set(dataset.employees.map(employee => employee.id)).size, dataset.employees.length);
|
||||
assert.equal(new Set(dataset.organizations.map(org => org.id)).size, dataset.organizations.length);
|
||||
assert.equal(new Set(dataset.affiliations.map(affiliation => affiliation.id)).size, dataset.affiliations.length);
|
||||
const summary = calculateSummary(dataset).raw;
|
||||
assert.equal(summary.total, dataset.employees.length, 'leaders count once as employees');
|
||||
const pending = normalizeOrgChartDataset(dataset).roots.map(node => ({ node, depth: 1 }));
|
||||
let maximumDepth = 0;
|
||||
while (pending.length) {
|
||||
const { node, depth } = pending.pop()!;
|
||||
maximumDepth = Math.max(maximumDepth, depth);
|
||||
pending.push(...node.children.map(child => ({ node: child, depth: depth + 1 })));
|
||||
}
|
||||
assert.ok(maximumDepth >= 3);
|
||||
assert.ok(new Set(Object.values(summary.byOrganization).map(value => value.direct)).size > 1, 'unequal organization membership');
|
||||
}
|
||||
|
||||
for (const employeeCount of sizes) {
|
||||
test(`total ${employeeCount} contains exactly that many unique employees across all seven preset companies`, () => {
|
||||
const dataset = createOrgChartExampleDataset(base, { scope: 'total', employeeCount });
|
||||
assertContract(dataset);
|
||||
assert.equal(dataset.employees.length, employeeCount);
|
||||
assert.deepEqual(dataset.affiliations.map(item => item.id), companies);
|
||||
for (const companyId of companies) assert.ok(dataset.employees.some(employee => employee.affiliationId === companyId));
|
||||
});
|
||||
test(`per-company ${employeeCount} supports every company, pairs, and the full seven-company selection`, () => {
|
||||
for (const companyId of companies) {
|
||||
const dataset = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount, companyIds: [companyId] });
|
||||
assertContract(dataset);
|
||||
assert.equal(dataset.employees.length, employeeCount);
|
||||
assert.ok(dataset.organizations.every(org => org.companyId === companyId));
|
||||
assert.ok(dataset.employees.every(employee => employee.affiliationId === companyId));
|
||||
}
|
||||
for (const companyIds of [['hanmac', 'saman'], companies] as CompanyId[][]) {
|
||||
const dataset = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount, companyIds });
|
||||
assertContract(dataset);
|
||||
assert.equal(dataset.employees.length, employeeCount * companyIds.length);
|
||||
for (const id of companyIds) assert.equal(dataset.employees.filter(employee => employee.affiliationId === id).length, employeeCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test('total company filtering preserves company allocation without redistribution or lost ancestors', () => {
|
||||
for (const employeeCount of sizes) {
|
||||
const complete = createOrgChartExampleDataset(base, { scope: 'total', employeeCount });
|
||||
const selected = createOrgChartExampleDataset(base, { scope: 'total', employeeCount, companyIds: ['saman', 'hanmac'] });
|
||||
assertContract(selected);
|
||||
assert.deepEqual(selected.employees, complete.employees.filter(employee => ['hanmac', 'saman'].includes(employee.affiliationId)));
|
||||
assert.deepEqual(selected.organizations.map(org => org.id), complete.organizations.filter(org => ['hanmac', 'saman'].includes(org.companyId!)).map(org => org.id));
|
||||
}
|
||||
});
|
||||
|
||||
test('total 300 reuses every P5-3 large employee record and the original organization IDs', () => {
|
||||
const dataset = createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 300 });
|
||||
const originalById = new Map(base.employees.map(employee => [employee.id, employee]));
|
||||
assert.equal(dataset.employees.length, base.employees.length);
|
||||
for (const employee of dataset.employees) assert.deepEqual(employee, originalById.get(employee.id));
|
||||
assert.deepEqual(dataset.organizations, base.organizations);
|
||||
});
|
||||
|
||||
test('selections are deterministic sets and snapshots never mutate the source or another result', async () => {
|
||||
const before = structuredClone(base);
|
||||
const frozen = snapshotOrgChartDataset(base);
|
||||
const first = createOrgChartExampleDataset(frozen, { scope: 'per-company', employeeCount: 100, companyIds: ['saman', 'hanmac', 'saman'] });
|
||||
const equivalent = createOrgChartExampleDataset(frozen, { scope: 'per-company', employeeCount: 100, companyIds: ['hanmac', 'saman'] });
|
||||
assert.deepEqual(first, equivalent);
|
||||
assert.notEqual(first, equivalent);
|
||||
assert.notEqual(first.employees[0], equivalent.employees[0]);
|
||||
assert.ok(Object.isFrozen(first) && Object.isFrozen(first.organizations) && Object.isFrozen(first.employees[0]));
|
||||
assert.throws(() => { first.employees[0]!.name = 'changed'; }, TypeError);
|
||||
assert.deepEqual(base, before);
|
||||
assert.deepEqual(await new JsonOrgChartDataSource(first).load(), first);
|
||||
const single = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount: 100, companyIds: ['saman'] });
|
||||
assert.deepEqual(single.employees, first.employees.filter(employee => employee.affiliationId === 'saman'));
|
||||
});
|
||||
|
||||
test('expansion resolves source IDs that already use the generated employee namespace', () => {
|
||||
const template = structuredClone(base);
|
||||
const employee = template.employees.find(employee => employee.affiliationId === 'hanmac' && !template.organizations.some(org => org.leaderId === employee.id))!;
|
||||
employee.id = 'p7-virtual-hanmac-050';
|
||||
const dataset = createOrgChartExampleDataset(template, { scope: 'per-company', employeeCount: 100 });
|
||||
assertContract(dataset);
|
||||
assert.ok(dataset.employees.some(employee => employee.id === 'p7-virtual-hanmac-050-example'));
|
||||
});
|
||||
|
||||
test('invalid scale, company IDs, empty view selection, and incomplete templates fail explicitly', () => {
|
||||
assert.throws(() => createOrgChartExampleDataset(base, { scope: 'invalid' as 'total', employeeCount: 20 }), /scope/);
|
||||
assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 99 as 20 }), /employeeCount/);
|
||||
assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 20, companyIds: ['unknown' as CompanyId] }), /presets/);
|
||||
assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 20, companyIds: [] }), /empty view selection/);
|
||||
const incomplete = structuredClone(base);
|
||||
incomplete.employees = incomplete.employees.filter(employee => employee.affiliationId !== 'baron');
|
||||
incomplete.organizations.filter(org => org.companyId === 'baron').forEach(org => { org.leaderId = null; });
|
||||
assert.throws(() => createOrgChartExampleDataset(incomplete, { scope: 'total', employeeCount: 20 }), /all seven/);
|
||||
});
|
||||
Reference in new issue
Block a user