feat: The Big One UI #47

Merged
j.gerla merged 115 commits from temp_screenshot_manual into dev 2026-01-28 08:27:30 +00:00
2 changed files with 186 additions and 23 deletions
Showing only changes of commit 9f359de953 - Show all commits

View File

@@ -0,0 +1,156 @@
import { describe, it, expect} from '@jest/globals';
import {
type EditorWarning, warningSummary
} from "../../../../../src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx";
import useFlowStore from "../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx";
function makeWarning(
overrides?: Partial<EditorWarning>
): EditorWarning {
return {
scope: { id: 'node-1' },
type: 'MISSING_INPUT',
severity: 'ERROR',
description: 'Missing input',
...overrides,
};
}
describe("editorWarnings", () => {
describe('registerWarning', () => {
it('registers a node-level warning', () => {
const warning = makeWarning();
const {registerWarning, getWarnings} = useFlowStore.getState()
registerWarning(warning);
const warnings = getWarnings();
expect(warnings).toHaveLength(1);
expect(warnings[0]).toEqual(warning);
});
it('registers a handle-level warning with scoped key', () => {
const warning = makeWarning({
scope: { id: 'node-1', handleId: 'input-1' },
});
const {registerWarning, editorWarningRegistry} = useFlowStore.getState()
registerWarning(warning);
const nodeWarnings = editorWarningRegistry.get('node-1');
expect(nodeWarnings?.has('MISSING_INPUT:input-1')).toBe(true);
});
it('updates severityIndex correctly', () => {
const {registerWarning, severityIndex} = useFlowStore.getState()
registerWarning(makeWarning());
console.log(severityIndex);
expect(severityIndex.get('ERROR')!.size).toBe(1);
});
});
describe('getWarningsBySeverity', () => {
it('returns only warnings of requested severity', () => {
const {registerWarning, getWarningsBySeverity} = useFlowStore.getState()
registerWarning(
makeWarning({ severity: 'ERROR' })
);
registerWarning(
makeWarning({
severity: 'WARNING',
type: 'MISSING_OUTPUT',
})
);
const errors = getWarningsBySeverity('ERROR');
const warnings = getWarningsBySeverity('WARNING');
expect(errors).toHaveLength(1);
expect(warnings).toHaveLength(1);
});
});
describe('isProgramValid', () => {
it('returns true when no ERROR warnings exist', () => {
expect(useFlowStore.getState().isProgramValid()).toBe(true);
});
it('returns false when ERROR warnings exist', () => {
const {registerWarning, isProgramValid} = useFlowStore.getState()
registerWarning(makeWarning());
expect(isProgramValid()).toBe(false);
});
});
describe('unregisterWarning', () => {
it('removes warning from registry and severityIndex', () => {
const warning = makeWarning();
const {
registerWarning,
getWarnings,
unregisterWarning,
severityIndex
} = useFlowStore.getState()
registerWarning(warning);
unregisterWarning('node-1', 'MISSING_INPUT');
expect(getWarnings()).toHaveLength(0);
expect(severityIndex.get('ERROR')!.size).toBe(0);
});
it('does nothing if warning does not exist', () => {
expect(() =>
useFlowStore.getState().unregisterWarning('node-1', 'DOES_NOT_EXIST')
).not.toThrow();
});
});
describe('unregisterWarningsForId', () => {
it('removes all warnings for a node', () => {
const {registerWarning, unregisterWarningsForId, getWarnings, severityIndex} = useFlowStore.getState()
registerWarning(
makeWarning({
scope: { id: 'node-1', handleId: 'h1' },
})
);
registerWarning(
makeWarning({
scope: { id: 'node-1' },
type: 'MISSING_OUTPUT',
severity: 'WARNING',
})
);
unregisterWarningsForId('node-1');
expect(getWarnings()).toHaveLength(0);
expect(
severityIndex.get('ERROR')!.size
).toBe(0);
expect(
severityIndex.get('WARNING')!.size
).toBe(0);
});
});
describe('warningSummary', () => {
it('returns correct counts and validity', () => {
const {registerWarning} = useFlowStore.getState()
registerWarning(
makeWarning({ severity: 'ERROR' })
);
const summary = warningSummary();
expect(summary.error).toBe(1);
expect(summary.warning).toBe(0);
expect(summary.info).toBe(0);
expect(summary.isValid).toBe(false);
});
});
})

View File

@@ -3,7 +3,6 @@ import { cleanup } from '@testing-library/react';
import { import {
type CompositeWarningKey, type CompositeWarningKey,
type SeverityIndex, type SeverityIndex,
type WarningRegistry
} from "../src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx"; } from "../src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx";
import useFlowStore from '../src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx'; import useFlowStore from '../src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx';
@@ -74,15 +73,8 @@ export const mockReactFlow = () => {
}; };
const emptySeverityIndex : SeverityIndex = new Map([ beforeAll(() => {
['INFO', new Set<CompositeWarningKey>()], useFlowStore.setState({
['WARNING', new Set<CompositeWarningKey>()],
['ERROR', new Set<CompositeWarningKey>()],
]);
const emptyWarningRegistry : WarningRegistry = new Map();
const defaultState = {
nodes: [], nodes: [],
edges: [], edges: [],
past: [], past: [],
@@ -90,17 +82,32 @@ const defaultState = {
isBatchAction: false, isBatchAction: false,
edgeReconnectSuccessful: true, edgeReconnectSuccessful: true,
ruleRegistry: new Map(), ruleRegistry: new Map(),
editorWarningRegistry: emptyWarningRegistry, editorWarningRegistry: new Map(),
severityIndex: emptySeverityIndex, severityIndex: new Map([
} ['INFO', new Set<CompositeWarningKey>()],
['WARNING', new Set<CompositeWarningKey>()],
beforeAll(() => { ['ERROR', new Set<CompositeWarningKey>()],
useFlowStore.setState(defaultState); ]) as SeverityIndex,
});
}); });
afterEach(() => { afterEach(() => {
cleanup(); cleanup();
useFlowStore.setState(defaultState); useFlowStore.setState({
nodes: [],
edges: [],
past: [],
future: [],
isBatchAction: false,
edgeReconnectSuccessful: true,
ruleRegistry: new Map(),
editorWarningRegistry: new Map(),
severityIndex: new Map([
['INFO', new Set<CompositeWarningKey>()],
['WARNING', new Set<CompositeWarningKey>()],
['ERROR', new Set<CompositeWarningKey>()],
]) as SeverityIndex,
});
}); });
if (typeof HTMLDialogElement !== 'undefined') { if (typeof HTMLDialogElement !== 'undefined') {