From b20432cdfc17a4db7630955af88fd7fa112c1dc7 Mon Sep 17 00:00:00 2001 From: Richard Ward Date: Tue, 30 Dec 2025 14:11:25 +0000 Subject: [PATCH] config: tsx,icons,fakemui (15 files) --- fakemui/icons/CenterFocusStrong.tsx | 12 ++ fakemui/icons/CompareArrows.tsx | 11 ++ fakemui/icons/Launch.tsx | 11 ++ fakemui/icons/NavigateBefore.tsx | 8 + fakemui/icons/SwapHoriz.tsx | 9 + fakemui/icons/SwapVert.tsx | 9 + fakemui/icons/UnfoldLess.tsx | 9 + fakemui/icons/UnfoldMore.tsx | 9 + fakemui/icons/ZoomInMap.tsx | 15 ++ fakemui/icons/ZoomOutMap.tsx | 15 ++ packages/workflow_editor/seed/metadata.json | 8 +- .../seed/scripts/tests/editor.cases.json | 121 +++++++++++++ .../seed/scripts/tests/editor.test.lua | 48 +++++ .../seed/scripts/tests/run.cases.json | 167 ++++++++++++++++++ .../seed/scripts/tests/run.test.lua | 53 ++++++ 15 files changed, 503 insertions(+), 2 deletions(-) create mode 100644 fakemui/icons/CenterFocusStrong.tsx create mode 100644 fakemui/icons/CompareArrows.tsx create mode 100644 fakemui/icons/Launch.tsx create mode 100644 fakemui/icons/NavigateBefore.tsx create mode 100644 fakemui/icons/SwapHoriz.tsx create mode 100644 fakemui/icons/SwapVert.tsx create mode 100644 fakemui/icons/UnfoldLess.tsx create mode 100644 fakemui/icons/UnfoldMore.tsx create mode 100644 fakemui/icons/ZoomInMap.tsx create mode 100644 fakemui/icons/ZoomOutMap.tsx create mode 100644 packages/workflow_editor/seed/scripts/tests/editor.cases.json create mode 100644 packages/workflow_editor/seed/scripts/tests/editor.test.lua create mode 100644 packages/workflow_editor/seed/scripts/tests/run.cases.json create mode 100644 packages/workflow_editor/seed/scripts/tests/run.test.lua diff --git a/fakemui/icons/CenterFocusStrong.tsx b/fakemui/icons/CenterFocusStrong.tsx new file mode 100644 index 000000000..bad3738ec --- /dev/null +++ b/fakemui/icons/CenterFocusStrong.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const CenterFocusStrong = (props: IconProps) => ( + + + + + + + +) diff --git a/fakemui/icons/CompareArrows.tsx b/fakemui/icons/CompareArrows.tsx new file mode 100644 index 000000000..4c8401c1a --- /dev/null +++ b/fakemui/icons/CompareArrows.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const CompareArrows = (props: IconProps) => ( + + + + + + +) diff --git a/fakemui/icons/Launch.tsx b/fakemui/icons/Launch.tsx new file mode 100644 index 000000000..17620d34a --- /dev/null +++ b/fakemui/icons/Launch.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const Launch = (props: IconProps) => ( + + + + + + +) diff --git a/fakemui/icons/NavigateBefore.tsx b/fakemui/icons/NavigateBefore.tsx new file mode 100644 index 000000000..e2fa24a9f --- /dev/null +++ b/fakemui/icons/NavigateBefore.tsx @@ -0,0 +1,8 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const NavigateBefore = (props: IconProps) => ( + + + +) diff --git a/fakemui/icons/SwapHoriz.tsx b/fakemui/icons/SwapHoriz.tsx new file mode 100644 index 000000000..b8fbf3ab0 --- /dev/null +++ b/fakemui/icons/SwapHoriz.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const SwapHoriz = (props: IconProps) => ( + + + + +) diff --git a/fakemui/icons/SwapVert.tsx b/fakemui/icons/SwapVert.tsx new file mode 100644 index 000000000..54fb8c761 --- /dev/null +++ b/fakemui/icons/SwapVert.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const SwapVert = (props: IconProps) => ( + + + + +) diff --git a/fakemui/icons/UnfoldLess.tsx b/fakemui/icons/UnfoldLess.tsx new file mode 100644 index 000000000..8bc68b2f9 --- /dev/null +++ b/fakemui/icons/UnfoldLess.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const UnfoldLess = (props: IconProps) => ( + + + + +) diff --git a/fakemui/icons/UnfoldMore.tsx b/fakemui/icons/UnfoldMore.tsx new file mode 100644 index 000000000..f92c3fd95 --- /dev/null +++ b/fakemui/icons/UnfoldMore.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const UnfoldMore = (props: IconProps) => ( + + + + +) diff --git a/fakemui/icons/ZoomInMap.tsx b/fakemui/icons/ZoomInMap.tsx new file mode 100644 index 000000000..d49e4fcb3 --- /dev/null +++ b/fakemui/icons/ZoomInMap.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const ZoomInMap = (props: IconProps) => ( + + + + + + + + + + +) diff --git a/fakemui/icons/ZoomOutMap.tsx b/fakemui/icons/ZoomOutMap.tsx new file mode 100644 index 000000000..316b600ba --- /dev/null +++ b/fakemui/icons/ZoomOutMap.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const ZoomOutMap = (props: IconProps) => ( + + + + + + + + + + +) diff --git a/packages/workflow_editor/seed/metadata.json b/packages/workflow_editor/seed/metadata.json index bbaf5f207..10063697b 100644 --- a/packages/workflow_editor/seed/metadata.json +++ b/packages/workflow_editor/seed/metadata.json @@ -22,10 +22,14 @@ }, "tests": { "scripts": [ - "tests/status.test.lua" + "tests/status.test.lua", + "tests/editor.test.lua", + "tests/run.test.lua" ], "cases": [ - "tests/status.cases.json" + "tests/status.cases.json", + "tests/editor.cases.json", + "tests/run.cases.json" ] }, "minLevel": 5 diff --git a/packages/workflow_editor/seed/scripts/tests/editor.cases.json b/packages/workflow_editor/seed/scripts/tests/editor.cases.json new file mode 100644 index 000000000..567c91267 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/tests/editor.cases.json @@ -0,0 +1,121 @@ +{ + "render": [ + { + "desc": "Renders new workflow with default name", + "input": null, + "expected_name": "New Workflow", + "expected_steps_count": 0 + }, + { + "desc": "Renders existing workflow with name", + "input": { + "id": "wf-123", + "name": "Deploy Pipeline", + "steps": [] + }, + "expected_id": "wf-123", + "expected_name": "Deploy Pipeline", + "expected_steps_count": 0 + }, + { + "desc": "Renders workflow with steps", + "input": { + "id": "wf-456", + "name": "Build & Test", + "steps": [ + { "type": "build" }, + { "type": "test" } + ] + }, + "expected_id": "wf-456", + "expected_name": "Build & Test", + "expected_steps_count": 2 + }, + { + "desc": "Renders workflow without ID", + "input": { + "name": "CI Pipeline" + }, + "expected_name": "CI Pipeline", + "expected_steps_count": 0 + } + ], + "add_step": [ + { + "desc": "Adds build step without config", + "step_type": "build", + "config": null + }, + { + "desc": "Adds test step with config", + "step_type": "test", + "config": { + "framework": "jest", + "coverage": true + } + }, + { + "desc": "Adds deploy step with target", + "step_type": "deploy", + "config": { + "target": "production", + "region": "us-east-1" + } + }, + { + "desc": "Adds notification step", + "step_type": "notify", + "config": { + "channel": "slack", + "webhook": "https://hooks.slack.com/..." + } + }, + { + "desc": "Adds approval step", + "step_type": "approval", + "config": { + "approvers": ["admin", "devops"] + } + }, + { + "desc": "Adds script step with inline code", + "step_type": "script", + "config": { + "language": "bash", + "code": "echo 'Hello World'" + } + } + ], + "connect_steps": [ + { + "desc": "Connects two steps without condition", + "from": "step-1", + "to": "step-2", + "condition": null + }, + { + "desc": "Connects steps with success condition", + "from": "build", + "to": "test", + "condition": "on_success" + }, + { + "desc": "Connects steps with failure condition", + "from": "test", + "to": "notify-failure", + "condition": "on_failure" + }, + { + "desc": "Connects steps with always condition", + "from": "deploy", + "to": "cleanup", + "condition": "always" + }, + { + "desc": "Connects approval to deploy", + "from": "approval-step", + "to": "deploy-prod", + "condition": "approved" + } + ] +} diff --git a/packages/workflow_editor/seed/scripts/tests/editor.test.lua b/packages/workflow_editor/seed/scripts/tests/editor.test.lua new file mode 100644 index 000000000..08cbf4f08 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/tests/editor.test.lua @@ -0,0 +1,48 @@ +-- Workflow editor tests +-- Uses lua_test framework with parameterized test cases + +describe("Workflow Editor", function() + local cases = load_cases("editor.cases.json") + local editor = require("editor") + + describe("render", function() + it_each(cases.render, "$desc", function(tc) + local result = editor.render(tc.input) + expect(result.type).toBe("workflow_editor") + expect(result.props.name).toBe(tc.expected_name) + if tc.expected_id then + expect(result.props.id).toBe(tc.expected_id) + end + if tc.expected_steps_count ~= nil then + expect(#result.props.steps).toBe(tc.expected_steps_count) + end + end) + end) + + describe("add_step", function() + it_each(cases.add_step, "$desc", function(tc) + local result = editor.add_step(tc.step_type, tc.config) + expect(result.type).toBe("workflow_step") + expect(result.step_type).toBe(tc.step_type) + expect(result.position.x).toBe(0) + expect(result.position.y).toBe(0) + if tc.config then + expect(result.config).toBeDefined() + end + end) + end) + + describe("connect_steps", function() + it_each(cases.connect_steps, "$desc", function(tc) + local result = editor.connect_steps(tc.from, tc.to, tc.condition) + expect(result.type).toBe("connection") + expect(result.from).toBe(tc.from) + expect(result.to).toBe(tc.to) + if tc.condition then + expect(result.condition).toBe(tc.condition) + else + expect(result.condition).toBeNil() + end + end) + end) +end) diff --git a/packages/workflow_editor/seed/scripts/tests/run.cases.json b/packages/workflow_editor/seed/scripts/tests/run.cases.json new file mode 100644 index 000000000..b8c79bae6 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/tests/run.cases.json @@ -0,0 +1,167 @@ +{ + "render": [ + { + "desc": "Renders completed run", + "input": { + "workflow_name": "Deploy Production", + "run_number": 42, + "started_at": "2025-01-15 14:30:00", + "duration": "5m 32s", + "status": "success" + }, + "expected_title": "Deploy Production", + "expected_subtitle": "Run #42", + "expected_duration": "5m 32s", + "expected_status": "success" + }, + { + "desc": "Renders failed run", + "input": { + "workflow_name": "CI Pipeline", + "run_number": 128, + "started_at": "2025-01-15 15:45:00", + "duration": "2m 15s", + "status": "failure" + }, + "expected_title": "CI Pipeline", + "expected_subtitle": "Run #128", + "expected_duration": "2m 15s", + "expected_status": "failure" + }, + { + "desc": "Renders running workflow", + "input": { + "workflow_name": "Build & Test", + "run_number": 99, + "started_at": "2025-01-15 16:00:00", + "status": "running" + }, + "expected_title": "Build & Test", + "expected_subtitle": "Run #99", + "expected_duration": "running", + "expected_status": "running" + }, + { + "desc": "Renders cancelled run", + "input": { + "workflow_name": "Nightly Build", + "run_number": 7, + "started_at": "2025-01-15 02:00:00", + "duration": "1m 05s", + "status": "cancelled" + }, + "expected_title": "Nightly Build", + "expected_subtitle": "Run #7", + "expected_duration": "1m 05s", + "expected_status": "cancelled" + }, + { + "desc": "Renders queued run", + "input": { + "workflow_name": "Integration Tests", + "run_number": 200, + "started_at": "2025-01-15 17:00:00", + "status": "queued" + }, + "expected_title": "Integration Tests", + "expected_subtitle": "Run #200", + "expected_duration": "running", + "expected_status": "queued" + }, + { + "desc": "Renders run with very long duration", + "input": { + "workflow_name": "Performance Tests", + "run_number": 1, + "started_at": "2025-01-15 10:00:00", + "duration": "1h 45m 30s", + "status": "success" + }, + "expected_title": "Performance Tests", + "expected_subtitle": "Run #1", + "expected_duration": "1h 45m 30s", + "expected_status": "success" + } + ], + "render_list": [ + { + "desc": "Renders single run", + "input": [ + { + "workflow_name": "Deploy", + "run_number": 1, + "started_at": "2025-01-15 14:00:00", + "duration": "3m", + "status": "success" + } + ], + "expected_count": 1 + }, + { + "desc": "Renders multiple runs", + "input": [ + { + "workflow_name": "Build", + "run_number": 1, + "started_at": "2025-01-15 10:00:00", + "duration": "2m", + "status": "success" + }, + { + "workflow_name": "Test", + "run_number": 2, + "started_at": "2025-01-15 11:00:00", + "duration": "5m", + "status": "success" + }, + { + "workflow_name": "Deploy", + "run_number": 3, + "started_at": "2025-01-15 12:00:00", + "status": "running" + } + ], + "expected_count": 3 + }, + { + "desc": "Renders many runs in grid", + "input": [ + { + "workflow_name": "Run 1", + "run_number": 1, + "started_at": "2025-01-15 08:00:00", + "duration": "1m", + "status": "success" + }, + { + "workflow_name": "Run 2", + "run_number": 2, + "started_at": "2025-01-15 09:00:00", + "duration": "2m", + "status": "success" + }, + { + "workflow_name": "Run 3", + "run_number": 3, + "started_at": "2025-01-15 10:00:00", + "duration": "1m", + "status": "failure" + }, + { + "workflow_name": "Run 4", + "run_number": 4, + "started_at": "2025-01-15 11:00:00", + "status": "running" + }, + { + "workflow_name": "Run 5", + "run_number": 5, + "started_at": "2025-01-15 12:00:00", + "duration": "3m", + "status": "success" + } + ], + "expected_count": 5 + } + ] +} diff --git a/packages/workflow_editor/seed/scripts/tests/run.test.lua b/packages/workflow_editor/seed/scripts/tests/run.test.lua new file mode 100644 index 000000000..3b647c21c --- /dev/null +++ b/packages/workflow_editor/seed/scripts/tests/run.test.lua @@ -0,0 +1,53 @@ +-- Workflow run card tests +-- Uses lua_test framework with parameterized test cases + +describe("Workflow Run Cards", function() + local cases = load_cases("run.cases.json") + local run = require("run") + + describe("render", function() + it_each(cases.render, "$desc", function(tc) + local result = run.render(tc.input) + expect(result.type).toBe("card") + expect(result.props.title).toBe(tc.expected_title) + expect(result.props.subtitle).toBe(tc.expected_subtitle) + expect(#result.children).toBe(3) + + -- Check started timestamp + expect(result.children[1].type).toBe("text") + expect(result.children[1].content).toContain("Started:") + + -- Check duration + expect(result.children[2].type).toBe("text") + expect(result.children[2].content).toContain("Duration:") + if tc.expected_duration then + expect(result.children[2].content).toContain(tc.expected_duration) + end + + -- Check status badge + expect(result.children[3].type).toBe("status_badge") + expect(result.children[3].status).toBe(tc.expected_status) + end) + end) + + describe("render_list", function() + it_each(cases.render_list, "$desc", function(tc) + local result = run.render_list(tc.input) + expect(result.type).toBe("grid") + expect(result.columns).toBe(2) + expect(#result.children).toBe(tc.expected_count) + + -- Verify all children are cards + for i, child in ipairs(result.children) do + expect(child.type).toBe("card") + end + end) + + it("renders empty list", function() + local result = run.render_list({}) + expect(result.type).toBe("grid") + expect(result.columns).toBe(2) + expect(#result.children).toBe(0) + end) + end) +end)