From 97b99d4eabd24e945643b072c4ee4751212e14d7 Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 15:03:01 -0600 Subject: [PATCH 1/9] Refactor UML Generator Tests and Mock Flow Implementation - Consolidate mock flow generation logic into a dedicated function for better readability and maintainability. - Move setup logic out of test step --- src/test/mock_flow.ts | 212 +++++++++++ src/test/uml_generator_test.ts | 629 ++++++++++----------------------- 2 files changed, 389 insertions(+), 452 deletions(-) create mode 100644 src/test/mock_flow.ts diff --git a/src/test/mock_flow.ts b/src/test/mock_flow.ts new file mode 100644 index 0000000..1231312 --- /dev/null +++ b/src/test/mock_flow.ts @@ -0,0 +1,212 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as flowTypes from "../main/flow_types.ts"; +import { ParsedFlow } from "../main/flow_parser.ts"; + +export const NODE_NAMES = { + label: "test", + start: "start", + apexPluginCall: "myApexPluginCall", + assignment: "myAssignment", + collectionProcessor: "myCollectionProcessor", + decision: "myDecision", + loop: "myLoop", + orchestratedStage: "myOrchestratedStage", + recordCreate: "myRecordCreate", + recordDelete: "myRecordDelete", + recordLookup: "myRecordLookup", + recordRollback: "myRecordRollback", + recordUpdate: "myRecordUpdate", + screen: "myScreen", + step: "myStep", + subflow: "mySubflow", + transform: "myTransform", + wait: "myWait", + actionCall: "myActionCall", +}; + +export function generateMockFlow(): ParsedFlow { + return { + label: NODE_NAMES.label, + start: { + name: NODE_NAMES.start, + } as flowTypes.FlowStart, + apexPluginCalls: getFlowNodes( + NODE_NAMES.apexPluginCall + ) as flowTypes.FlowApexPluginCall[], + assignments: getFlowNodes( + NODE_NAMES.assignment + ) as flowTypes.FlowAssignment[], + collectionProcessors: getFlowNodes( + NODE_NAMES.collectionProcessor + ) as flowTypes.FlowCollectionProcessor[], + decisions: getFlowNodes(NODE_NAMES.decision) as flowTypes.FlowDecision[], + loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], + orchestratedStages: getFlowNodes( + NODE_NAMES.orchestratedStage + ) as flowTypes.FlowOrchestratedStage[], + recordCreates: getFlowNodes( + NODE_NAMES.recordCreate + ) as flowTypes.FlowRecordCreate[], + recordDeletes: getFlowNodes( + NODE_NAMES.recordDelete + ) as flowTypes.FlowRecordDelete[], + recordLookups: getFlowNodes( + NODE_NAMES.recordLookup + ) as flowTypes.FlowRecordLookup[], + recordRollbacks: getFlowNodes( + NODE_NAMES.recordRollback + ) as flowTypes.FlowRecordRollback[], + recordUpdates: getFlowNodes( + NODE_NAMES.recordUpdate + ) as flowTypes.FlowRecordUpdate[], + screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], + steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], + subflows: getFlowNodes(NODE_NAMES.subflow) as flowTypes.FlowSubflow[], + transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], + waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], + actionCalls: getFlowNodes( + NODE_NAMES.actionCall + ) as flowTypes.FlowActionCall[], + transitions: [ + { + from: NODE_NAMES.start, + to: NODE_NAMES.apexPluginCall, + fault: false, + }, + { + from: NODE_NAMES.apexPluginCall, + to: NODE_NAMES.assignment, + fault: false, + }, + { + from: NODE_NAMES.assignment, + to: NODE_NAMES.collectionProcessor, + fault: false, + }, + ], + }; +} + +type NodeFactory = (name: string) => flowTypes.FlowNode[]; + +const NODE_FACTORIES: Record = { + [NODE_NAMES.recordUpdate]: createRecordUpdateNode, + [NODE_NAMES.recordLookup]: createRecordLookupNode, + [NODE_NAMES.recordCreate]: createRecordCreateNode, + [NODE_NAMES.recordDelete]: createRecordDeleteNode, + [NODE_NAMES.assignment]: createAssignmentNode, +}; + +function getFlowNodes(name: string): flowTypes.FlowNode[] { + const factory = NODE_FACTORIES[name]; + return factory ? factory(name) : createBasicNode(name); +} + +function createBaseNode(name: string) { + return { + name, + label: name, + locationX: 0, + locationY: 0, + description: "", + }; +} + +function createRecordUpdateNode(name: string): flowTypes.FlowRecordUpdate[] { + return [ + { + ...createBaseNode(name), + object: "Account", + inputAssignments: [], + inputReference: "", + elementSubtype: "RecordUpdate", + filters: [], + }, + ]; +} + +function createRecordLookupNode(name: string): flowTypes.FlowRecordLookup[] { + return [ + { + ...createBaseNode(name), + object: "Account", + elementSubtype: "RecordLookup", + filters: [], + queriedFields: [], + }, + ]; +} + +function createRecordCreateNode(name: string): flowTypes.FlowRecordCreate[] { + return [ + { + ...createBaseNode(name), + object: "Account", + elementSubtype: "RecordCreate", + inputAssignments: [], + inputReference: "", + }, + ]; +} + +function createRecordDeleteNode(name: string): flowTypes.FlowRecordDelete[] { + return [ + { + ...createBaseNode(name), + object: "Account", + elementSubtype: "RecordDelete", + inputReference: "", + }, + ]; +} + +function createAssignmentNode(name: string): flowTypes.FlowAssignment[] { + return [ + { + ...createBaseNode(name), + elementSubtype: "Assignment", + assignmentItems: [ + { + assignToReference: "var1", + operator: flowTypes.FlowAssignmentOperator.ASSIGN, + value: { + stringValue: "Hello World", + }, + processMetadataValues: [], + }, + { + assignToReference: "var2", + operator: flowTypes.FlowAssignmentOperator.ADD_ITEM, + value: { + stringValue: "Test Value", + }, + processMetadataValues: [], + }, + ], + }, + ]; +} + +function createBasicNode(name: string): flowTypes.FlowNode[] { + return [ + { + ...createBaseNode(name), + elementSubtype: "Unknown", + }, + ]; +} diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index 926d85f..dc3086e 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -18,32 +18,11 @@ import { assertEquals } from "@std/assert"; import { ParsedFlow, Transition } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { DiagramNode, UmlGenerator } from "../main/uml_generator.ts"; +import { generateMockFlow, NODE_NAMES } from "./mock_flow.ts"; const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; const TRANSITION_ARROW = "-->"; -const NODE_NAMES = { - label: "test", - start: "start", - apexPluginCall: "myApexPluginCall", - assignment: "myAssignment", - collectionProcessor: "myCollectionProcessor", - decision: "myDecision", - loop: "myLoop", - orchestratedStage: "myOrchestratedStage", - recordCreate: "myRecordCreate", - recordDelete: "myRecordDelete", - recordLookup: "myRecordLookup", - recordRollback: "myRecordRollback", - recordUpdate: "myRecordUpdate", - screen: "myScreen", - step: "myStep", - subflow: "mySubflow", - transform: "myTransform", - wait: "myWait", - actionCall: "myActionCall", -}; - const UML_REPRESENTATIONS = { flowStart: () => `state Flow Start FLOW_START @@ -80,194 +59,45 @@ const UML_REPRESENTATIONS = { transition: (from: string, to: string) => `${from} ${TRANSITION_ARROW} ${to}`, }; -function generateMockFlow() { - return { - label: NODE_NAMES.label, - start: { - name: NODE_NAMES.start, - } as flowTypes.FlowStart, - apexPluginCalls: getFlowNodes( - NODE_NAMES.apexPluginCall, - ) as flowTypes.FlowApexPluginCall[], - assignments: getFlowNodes( - NODE_NAMES.assignment, - ) as flowTypes.FlowAssignment[], - collectionProcessors: getFlowNodes( - NODE_NAMES.collectionProcessor, - ) as flowTypes.FlowCollectionProcessor[], - decisions: getFlowNodes(NODE_NAMES.decision) as flowTypes.FlowDecision[], - loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], - orchestratedStages: getFlowNodes( - NODE_NAMES.orchestratedStage, - ) as flowTypes.FlowOrchestratedStage[], - recordCreates: getFlowNodes( - NODE_NAMES.recordCreate, - ) as flowTypes.FlowRecordCreate[], - recordDeletes: getFlowNodes( - NODE_NAMES.recordDelete, - ) as flowTypes.FlowRecordDelete[], - recordLookups: getFlowNodes( - NODE_NAMES.recordLookup, - ) as flowTypes.FlowRecordLookup[], - recordRollbacks: getFlowNodes( - NODE_NAMES.recordRollback, - ) as flowTypes.FlowRecordRollback[], - recordUpdates: getFlowNodes( - NODE_NAMES.recordUpdate, - ) as flowTypes.FlowRecordUpdate[], - screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], - steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], - subflows: getFlowNodes(NODE_NAMES.subflow) as flowTypes.FlowSubflow[], - transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], - waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], - actionCalls: getFlowNodes( - NODE_NAMES.actionCall, - ) as flowTypes.FlowActionCall[], - transitions: [ - { - from: NODE_NAMES.start, - to: NODE_NAMES.apexPluginCall, - fault: false, - }, - { - from: NODE_NAMES.apexPluginCall, - to: NODE_NAMES.assignment, - fault: false, - }, - { - from: NODE_NAMES.assignment, - to: NODE_NAMES.collectionProcessor, - fault: false, - }, - ], - }; -} - -function getFlowNodes(name: string): flowTypes.FlowNode[] { - const baseNode = { - name: name, - label: name, - locationX: 0, - locationY: 0, - description: "", - }; - - // Add specific properties based on node name - if (name === NODE_NAMES.recordUpdate) { - return [ - { - ...baseNode, - object: "Account", - inputAssignments: [], - inputReference: "", - elementSubtype: "RecordUpdate", - filters: [], - }, - ] as flowTypes.FlowRecordUpdate[]; +class ConcreteUmlGenerator extends UmlGenerator { + getHeader(label: string): string { + return label; } + toUmlString(node: DiagramNode): string { + let result = `state ${node.type} ${node.id}`; + if (node.innerNodes) { + const innerContent = node.innerNodes + .map((innerNode) => { + const header = [innerNode.type, innerNode.label] + .filter(Boolean) + .join(": "); + + const content = innerNode.content + .map((line) => ` ${line}`) + .join(EOL); - if (name === NODE_NAMES.recordLookup) { - return [ - { - ...baseNode, - object: "Account", - elementSubtype: "RecordLookup", - }, - ] as flowTypes.FlowRecordLookup[]; + return header ? ` ${header}${EOL}${content}` : content; + }) + .join(EOL); + result += EOL + innerContent; + } + return result; } - - if (name === NODE_NAMES.recordCreate) { - return [ - { - ...baseNode, - object: "Account", - elementSubtype: "RecordCreate", - }, - ] as flowTypes.FlowRecordCreate[]; + getTransition(transition: Transition): string { + return UML_REPRESENTATIONS.transition(transition.from, transition.to); } - - if (name === NODE_NAMES.recordDelete) { - return [ - { - ...baseNode, - object: "Account", - elementSubtype: "RecordDelete", - }, - ] as flowTypes.FlowRecordDelete[]; + getFooter(): string { + return ""; } - - if (name === NODE_NAMES.assignment) { - return [ - { - ...baseNode, - elementSubtype: "Assignment", - assignmentItems: [ - { - assignToReference: "var1", - operator: flowTypes.FlowAssignmentOperator.ASSIGN, - value: { - stringValue: "Hello World", - }, - processMetadataValues: [], - }, - { - assignToReference: "var2", - operator: flowTypes.FlowAssignmentOperator.ADD_ITEM, - value: { - stringValue: "Test Value", - }, - processMetadataValues: [], - }, - ], - }, - ] as flowTypes.FlowAssignment[]; - } - - // Return basic node for other types - return [baseNode] as flowTypes.FlowNode[]; } Deno.test("UmlGenerator", async (t) => { let systemUnderTest: UmlGenerator; let mockParsedFlow: ParsedFlow; - await t.step("setup", () => { - mockParsedFlow = generateMockFlow(); - - class ConcreteUmlGenerator extends UmlGenerator { - getHeader(label: string): string { - return label; - } - toUmlString(node: DiagramNode): string { - let result = `state ${node.type} ${node.id}`; - if (node.innerNodes) { - const innerContent = node.innerNodes - .map((innerNode) => { - const header = [innerNode.type, innerNode.label] - .filter(Boolean) - .join(": "); - - const content = innerNode.content - .map((line) => ` ${line}`) - .join(EOL); - - return header ? ` ${header}${EOL}${content}` : content; - }) - .join(EOL); - result += EOL + innerContent; - } - return result; - } - getTransition(transition: Transition): string { - return UML_REPRESENTATIONS.transition(transition.from, transition.to); - } - getFooter(): string { - return ""; - } - } - - systemUnderTest = new ConcreteUmlGenerator(mockParsedFlow); - }); + // Setup: initialize test data and system under test + mockParsedFlow = generateMockFlow(); + systemUnderTest = new ConcreteUmlGenerator(mockParsedFlow); await t.step("should generate UML with all flow elements", () => { const uml = systemUnderTest.generateUml(); @@ -294,15 +124,15 @@ Deno.test("UmlGenerator", async (t) => { UML_REPRESENTATIONS.actionCall(NODE_NAMES.actionCall), UML_REPRESENTATIONS.transition( NODE_NAMES.start, - NODE_NAMES.apexPluginCall, + NODE_NAMES.apexPluginCall ), UML_REPRESENTATIONS.transition( NODE_NAMES.apexPluginCall, - NODE_NAMES.assignment, + NODE_NAMES.assignment ), UML_REPRESENTATIONS.transition( NODE_NAMES.assignment, - NODE_NAMES.collectionProcessor, + NODE_NAMES.collectionProcessor ), ].join(EOL); @@ -316,7 +146,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(UML_REPRESENTATIONS.screen(NODE_NAMES.screen)), - false, + false ); }); @@ -327,7 +157,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(UML_REPRESENTATIONS.screen(NODE_NAMES.screen)), - false, + false ); }); @@ -390,10 +220,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -426,10 +256,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -488,10 +318,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -519,10 +349,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -571,10 +401,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -631,10 +461,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -664,10 +494,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -722,10 +552,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -782,10 +612,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -821,10 +651,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); await t.step( @@ -890,247 +720,142 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}`, + `Expected UML: ${uml} to contain: ${content}` ); }); - }, + } ); - await t.step( - "should handle minimal start node configuration", - () => { - // Create a minimal mock flow with just the start node - const minimalMockFlow: ParsedFlow = { - label: "Minimal Test", - processType: undefined, - start: { - name: "FLOW_START", - label: "Flow Start", - locationX: 0, - locationY: 0, - elementSubtype: "Start", - description: "Minimal flow start", - connector: { targetReference: "nextNode", isGoTo: false }, - }, - transitions: [], - }; + await t.step("should handle minimal start node configuration", () => { + // Create a minimal mock flow with just the start node + const minimalMockFlow: ParsedFlow = { + label: "Minimal Test", + processType: undefined, + start: { + name: "FLOW_START", + label: "Flow Start", + locationX: 0, + locationY: 0, + elementSubtype: "Start", + description: "Minimal flow start", + connector: { targetReference: "nextNode", isGoTo: false }, + }, + transitions: [], + }; - // Create a fresh generator with the minimal mock flow - class ConcreteUmlGenerator extends UmlGenerator { - getHeader(label: string): string { - return label; - } - toUmlString(node: DiagramNode): string { - let result = `state ${node.type} ${node.id}`; - if (node.innerNodes) { - const innerContent = node.innerNodes - .map((innerNode) => { - const header = [innerNode.type, innerNode.label] - .filter(Boolean) - .join(": "); - - const content = innerNode.content - .map((line) => ` ${line}`) - .join(EOL); - - return header ? ` ${header}${EOL}${content}` : content; - }) - .join(EOL); - result += EOL + innerContent; - } - return result; - } - getTransition(transition: Transition): string { - return UML_REPRESENTATIONS.transition(transition.from, transition.to); - } - getFooter(): string { - return ""; - } - } - - const minimalGenerator = new ConcreteUmlGenerator(minimalMockFlow); - const uml = minimalGenerator.generateUml(); + // Create a fresh generator with the minimal mock flow + const minimalGenerator = new ConcreteUmlGenerator(minimalMockFlow); + const uml = minimalGenerator.generateUml(); - const expectedContent = [ - "Flow Start FLOW_START", - "Flow Details", - "No specific entry criteria defined", - ]; + const expectedContent = [ + "Flow Start FLOW_START", + "Flow Details", + "No specific entry criteria defined", + ]; - expectedContent.forEach((content) => { - assertEquals( - uml.includes(content), - true, - `Expected UML: ${uml} to contain: ${content}`, - ); - }); + expectedContent.forEach((content) => { + assertEquals( + uml.includes(content), + true, + `Expected UML: ${uml} to contain: ${content}` + ); + }); - // Should not contain any specific trigger information - const unexpectedContent = [ - "Process Type:", - "Trigger Type:", - "Filter Logic:", - ]; + // Should not contain any specific trigger information + const unexpectedContent = [ + "Process Type:", + "Trigger Type:", + "Filter Logic:", + ]; - unexpectedContent.forEach((content) => { - assertEquals( - uml.includes(content), - false, - `Expected UML: ${uml} to NOT contain: ${content}`, - ); - }); - }, - ); + unexpectedContent.forEach((content) => { + assertEquals( + uml.includes(content), + false, + `Expected UML: ${uml} to NOT contain: ${content}` + ); + }); + }); - await t.step( - "should handle start node with record change criteria", - () => { - // Create a record change mock flow with just the start node - const recordChangeMockFlow: ParsedFlow = { - label: "Record Change Test", - processType: flowTypes.FlowProcessType.FLOW, - start: { - name: "FLOW_START", - label: "Flow Start", - locationX: 0, - locationY: 0, - elementSubtype: "Start", - description: "Record change flow start", - connector: { targetReference: "nextNode", isGoTo: false }, - triggerType: flowTypes.FlowTriggerType.RECORD_BEFORE_SAVE, - object: "Opportunity", - recordTriggerType: flowTypes.RecordTriggerType.UPDATE, - entryType: flowTypes.FlowEntryType.ALWAYS, - doesRequireRecordChangedToMeetCriteria: true, - filterLogic: "1 OR 2", - filters: [ - { - field: "StageName", - operator: flowTypes.FlowRecordFilterOperator.EQUAL_TO, - value: { stringValue: "Closed Won" }, - }, - { - field: "Amount", - operator: flowTypes.FlowRecordFilterOperator.GREATER_THAN, - value: { numberValue: "100000" }, - }, - ], - }, - transitions: [], - }; + await t.step("should handle start node with record change criteria", () => { + // Create a record change mock flow with just the start node + const recordChangeMockFlow: ParsedFlow = { + label: "Record Change Test", + processType: flowTypes.FlowProcessType.FLOW, + start: { + name: "FLOW_START", + label: "Flow Start", + locationX: 0, + locationY: 0, + elementSubtype: "Start", + description: "Record change flow start", + connector: { targetReference: "nextNode", isGoTo: false }, + triggerType: flowTypes.FlowTriggerType.RECORD_BEFORE_SAVE, + object: "Opportunity", + recordTriggerType: flowTypes.RecordTriggerType.UPDATE, + entryType: flowTypes.FlowEntryType.ALWAYS, + doesRequireRecordChangedToMeetCriteria: true, + filterLogic: "1 OR 2", + filters: [ + { + field: "StageName", + operator: flowTypes.FlowRecordFilterOperator.EQUAL_TO, + value: { stringValue: "Closed Won" }, + }, + { + field: "Amount", + operator: flowTypes.FlowRecordFilterOperator.GREATER_THAN, + value: { numberValue: "100000" }, + }, + ], + }, + transitions: [], + }; - // Create a fresh generator with the record change mock flow - class ConcreteUmlGenerator extends UmlGenerator { - getHeader(label: string): string { - return label; - } - toUmlString(node: DiagramNode): string { - let result = `state ${node.type} ${node.id}`; - if (node.innerNodes) { - const innerContent = node.innerNodes - .map((innerNode) => { - const header = [innerNode.type, innerNode.label] - .filter(Boolean) - .join(": "); - - const content = innerNode.content - .map((line) => ` ${line}`) - .join(EOL); - - return header ? ` ${header}${EOL}${content}` : content; - }) - .join(EOL); - result += EOL + innerContent; - } - return result; - } - getTransition(transition: Transition): string { - return UML_REPRESENTATIONS.transition(transition.from, transition.to); - } - getFooter(): string { - return ""; - } - } - - const recordChangeGenerator = new ConcreteUmlGenerator( - recordChangeMockFlow, + // Create a fresh generator with the record change mock flow + const recordChangeGenerator = new ConcreteUmlGenerator( + recordChangeMockFlow + ); + const uml = recordChangeGenerator.generateUml(); + + const expectedContent = [ + "Process Type: Flow", + "Trigger Type: RecordBeforeSave", + "Object: Opportunity", + "Record Trigger: Update", + "Entry Type: Always", + "Filter Logic: 1 OR 2", + "1. StageName EqualTo Closed Won", + "2. Amount GreaterThan 100000", + ]; + + expectedContent.forEach((content) => { + assertEquals( + uml.includes(content), + true, + `Expected UML: ${uml} to contain: ${content}` ); - const uml = recordChangeGenerator.generateUml(); - - const expectedContent = [ - "Process Type: Flow", - "Trigger Type: RecordBeforeSave", - "Object: Opportunity", - "Record Trigger: Update", - "Entry Type: Always", - "Filter Logic: 1 OR 2", - "1. StageName EqualTo Closed Won", - "2. Amount GreaterThan 100000", - ]; + }); + }); - expectedContent.forEach((content) => { - assertEquals( - uml.includes(content), - true, - `Expected UML: ${uml} to contain: ${content}`, - ); - }); - }, - ); + await t.step("should handle empty start node gracefully", () => { + // Create an empty mock flow with no start node + const emptyMockFlow: ParsedFlow = { + label: "Empty Test", + processType: undefined, + start: undefined, + transitions: [], + }; - await t.step( - "should handle empty start node gracefully", - () => { - // Create an empty mock flow with no start node - const emptyMockFlow: ParsedFlow = { - label: "Empty Test", - processType: undefined, - start: undefined, - transitions: [], - }; + // Create a fresh generator with the empty mock flow + const emptyGenerator = new ConcreteUmlGenerator(emptyMockFlow); + const uml = emptyGenerator.generateUml(); - // Create a fresh generator with the empty mock flow - class ConcreteUmlGenerator extends UmlGenerator { - getHeader(label: string): string { - return label; - } - toUmlString(node: DiagramNode): string { - let result = `state ${node.type} ${node.id}`; - if (node.innerNodes) { - const innerContent = node.innerNodes - .map((innerNode) => { - const header = [innerNode.type, innerNode.label] - .filter(Boolean) - .join(": "); - - const content = innerNode.content - .map((line) => ` ${line}`) - .join(EOL); - - return header ? ` ${header}${EOL}${content}` : content; - }) - .join(EOL); - result += EOL + innerContent; - } - return result; - } - getTransition(transition: Transition): string { - return UML_REPRESENTATIONS.transition(transition.from, transition.to); - } - getFooter(): string { - return ""; - } - } - - const emptyGenerator = new ConcreteUmlGenerator(emptyMockFlow); - const uml = emptyGenerator.generateUml(); - - // Should not contain flow start node when undefined - assertEquals( - uml.includes("Flow Start FLOW_START"), - false, - "Should not contain flow start node when undefined", - ); - }, - ); + // Should not contain flow start node when undefined + assertEquals( + uml.includes("Flow Start FLOW_START"), + false, + "Should not contain flow start node when undefined" + ); + }); }); From 7afe113f9ab7b293a19f05e73ddb26cb826c94ba Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 15:21:49 -0600 Subject: [PATCH 2/9] Refactor GraphViz Generator Tests and Mock Flow Structure - Remove redundant flow node generation logic from tests. - Update UML representation in tests to reflect changes in mock flow structure. --- src/test/graphviz_generator_test.ts | 174 +++------------------------- src/test/mock_flow.ts | 55 ++++++++- src/test/uml_generator_test.ts | 12 +- 3 files changed, 78 insertions(+), 163 deletions(-) diff --git a/src/test/graphviz_generator_test.ts b/src/test/graphviz_generator_test.ts index 764aa14..6148491 100644 --- a/src/test/graphviz_generator_test.ts +++ b/src/test/graphviz_generator_test.ts @@ -28,150 +28,9 @@ import { Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; +import { generateMockFlow } from "./mock_flow.ts"; const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; -const NODE_NAMES = { - start: "FLOW_START", - apexPluginCall: "myApexPluginCall", - assignment: "myAssignment", - collectionProcessor: "myCollectionProcessor", - decision: "myDecision", - loop: "myLoop", - orchestratedStage: "myOrchestratedStage", - recordCreate: "myRecordCreate", - recordDelete: "myRecordDelete", - recordLookup: "myRecordLookup", - recordRollback: "myRecordRollback", - recordUpdate: "myRecordUpdate", - screen: "myScreen", - stageSteps: ["step1", "step2", "step3"], - step: "myStep", - subflow: "mySubflow", - transform: "myTransform", - wait: "myWait", - actionCall: "myActionCall", -}; - -function generateMockFlow(): ParsedFlow { - return { - start: { - name: NODE_NAMES.start, - } as flowTypes.FlowStart, - apexPluginCalls: getFlowNodes( - NODE_NAMES.apexPluginCall, - ) as flowTypes.FlowApexPluginCall[], - assignments: getFlowNodes( - NODE_NAMES.assignment, - ) as flowTypes.FlowAssignment[], - collectionProcessors: getFlowNodes( - NODE_NAMES.collectionProcessor, - ) as flowTypes.FlowCollectionProcessor[], - decisions: [ - generateDecision(NODE_NAMES.decision), - ] as flowTypes.FlowDecision[], - loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], - orchestratedStages: [ - generateStage(NODE_NAMES.orchestratedStage, NODE_NAMES.stageSteps), - ], - recordCreates: getFlowNodes( - NODE_NAMES.recordCreate, - ) as flowTypes.FlowRecordCreate[], - recordDeletes: getFlowNodes( - NODE_NAMES.recordDelete, - ) as flowTypes.FlowRecordDelete[], - recordLookups: getFlowNodes( - NODE_NAMES.recordLookup, - ) as flowTypes.FlowRecordLookup[], - recordRollbacks: getFlowNodes( - NODE_NAMES.recordRollback, - ) as flowTypes.FlowRecordRollback[], - recordUpdates: getFlowNodes( - NODE_NAMES.recordUpdate, - ) as flowTypes.FlowRecordUpdate[], - screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], - steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], - subflows: getFlowNodes(NODE_NAMES.subflow) as flowTypes.FlowSubflow[], - transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], - waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], - actionCalls: getFlowNodes( - NODE_NAMES.actionCall, - ) as flowTypes.FlowActionCall[], - transitions: [ - { - from: NODE_NAMES.start, - to: NODE_NAMES.apexPluginCall, - fault: false, - }, - { - from: NODE_NAMES.apexPluginCall, - to: NODE_NAMES.assignment, - fault: false, - }, - { - from: NODE_NAMES.assignment, - to: NODE_NAMES.collectionProcessor, - fault: false, - }, - ], - }; -} - -function getFlowNodes(name: string): flowTypes.FlowNode[] { - return [{ name: `${name}`, label: `${name}` }] as flowTypes.FlowNode[]; -} - -function generateStage( - name: string, - stepNames: string[], -): flowTypes.FlowOrchestratedStage { - return { - name: `${name}`, - label: `${name}`, - elementSubtype: "OrchestratedStage", - locationX: 0, - locationY: 0, - description: `${name}`, - stageSteps: stepNames.map((stepName) => ({ - name: `${stepName}`, - label: `${stepName}`, - elementSubtype: "Step", - locationX: 0, - locationY: 0, - description: `${stepName}`, - actionName: `${stepName}Action`, - actionType: flowTypes.FlowStageStepActionType.STEP_BACKGROUND, - })), - } as flowTypes.FlowOrchestratedStage; -} - -function generateDecision(name: string): flowTypes.FlowDecision { - return { - name: `${name}`, - label: `${name}`, - elementSubtype: "Decision", - locationX: 0, - locationY: 0, - description: `${name}`, - rules: [ - { - name: `${name}Rule`, - label: `${name}Rule`, - description: `${name}Rule`, - conditionLogic: "and", - conditions: [ - { - leftValueReference: "foo", - operator: flowTypes.FlowComparisonOperator.EQUAL_TO, - rightValue: { - booleanValue: "true", - }, - processMetadataValues: [], - }, - ], - }, - ], - } as flowTypes.FlowDecision; -} function generateTable( nodeName: string, @@ -179,7 +38,7 @@ function generateTable( icon: Icon, skinColor: SkinColor, fontColor: string, - innerNodeBody?: string, + innerNodeBody?: string ) { const formattedInnerNodeBody = innerNodeBody ? `${EOL}${innerNodeBody}${EOL}` @@ -206,7 +65,7 @@ function generateTable( function generateInnerNodeCell( color: FontColor, expectedLabel: string, - content: string[], + content: string[] ) { return ` @@ -221,15 +80,10 @@ function generateInnerNodeCells(cells: string[]) { } Deno.test("GraphVizGenerator", async (t) => { - let systemUnderTest: GraphVizGenerator; - let mockedFlow: ParsedFlow; + const mockedFlow = generateMockFlow(); + const systemUnderTest = new GraphVizGenerator(mockedFlow); let result: string; - await t.step("Setup", () => { - mockedFlow = generateMockFlow(); - systemUnderTest = new GraphVizGenerator(mockedFlow); - }); - await t.step("should generate header", () => { const label = "foo"; result = systemUnderTest.getHeader(label); @@ -257,8 +111,8 @@ Deno.test("GraphVizGenerator", async (t) => { "Apex Plugin Call", Icon.CODE, SkinColor.NONE, - FontColor.BLACK, - ), + FontColor.BLACK + ) ); }); @@ -278,8 +132,8 @@ Deno.test("GraphVizGenerator", async (t) => { "Assignment", Icon.ASSIGNMENT, SkinColor.ORANGE, - FontColor.WHITE, - ), + FontColor.WHITE + ) ); }); @@ -310,8 +164,8 @@ Deno.test("GraphVizGenerator", async (t) => { FontColor.WHITE, generateInnerNodeCell(FontColor.WHITE, "Rule myDecisionRule", [ "1. foo EqualTo true", - ]), - ), + ]) + ) ); }); @@ -349,8 +203,8 @@ Deno.test("GraphVizGenerator", async (t) => { generateInnerNodeCells([ generateInnerNodeCell(FontColor.WHITE, "Stage Step 1. step1", []), generateInnerNodeCell(FontColor.WHITE, "Stage Step 2. step2", []), - ]), - ), + ]) + ) ); }); @@ -397,7 +251,7 @@ Deno.test("GraphVizGenerator", async (t) => { result = systemUnderTest.getTransition(mockedFlow.transitions![0]); assertEquals( result, - 'FLOW_START -> myApexPluginCall [label="" color="black" style=""]', + 'FLOW_START -> myApexPluginCall [label="" color="black" style=""]' ); }); }); diff --git a/src/test/mock_flow.ts b/src/test/mock_flow.ts index 1231312..945e18c 100644 --- a/src/test/mock_flow.ts +++ b/src/test/mock_flow.ts @@ -19,13 +19,14 @@ import { ParsedFlow } from "../main/flow_parser.ts"; export const NODE_NAMES = { label: "test", - start: "start", + start: "FLOW_START", apexPluginCall: "myApexPluginCall", assignment: "myAssignment", collectionProcessor: "myCollectionProcessor", decision: "myDecision", loop: "myLoop", orchestratedStage: "myOrchestratedStage", + stageSteps: ["step1", "step2", "step3"], recordCreate: "myRecordCreate", recordDelete: "myRecordDelete", recordLookup: "myRecordLookup", @@ -110,6 +111,9 @@ const NODE_FACTORIES: Record = { [NODE_NAMES.recordCreate]: createRecordCreateNode, [NODE_NAMES.recordDelete]: createRecordDeleteNode, [NODE_NAMES.assignment]: createAssignmentNode, + [NODE_NAMES.decision]: generateDecision, + [NODE_NAMES.orchestratedStage]: (name: string) => + generateStage(name, NODE_NAMES.stageSteps), }; function getFlowNodes(name: string): flowTypes.FlowNode[] { @@ -210,3 +214,52 @@ function createBasicNode(name: string): flowTypes.FlowNode[] { }, ]; } + +function generateStage( + name: string, + stepNames: string[] = [] +): flowTypes.FlowOrchestratedStage[] { + return [ + { + ...createBaseNode(name), + elementSubtype: "OrchestratedStage", + stageSteps: stepNames.map((stepName) => ({ + name: stepName, + label: stepName, + elementSubtype: "Step", + locationX: 0, + locationY: 0, + description: stepName, + actionName: `${stepName}Action`, + actionType: flowTypes.FlowStageStepActionType.STEP_BACKGROUND, + })), + }, + ]; +} + +function generateDecision(name: string): flowTypes.FlowDecision[] { + return [ + { + ...createBaseNode(name), + elementSubtype: "Decision", + rules: [ + { + name: `${name}Rule`, + label: `${name}Rule`, + description: `${name}Rule`, + conditionLogic: "and", + conditions: [ + { + leftValueReference: "foo", + operator: flowTypes.FlowComparisonOperator.EQUAL_TO, + rightValue: { + booleanValue: "true", + }, + processMetadataValues: [], + }, + ], + }, + ], + }, + ]; +} diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index dc3086e..31a9ee5 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -34,9 +34,17 @@ const UML_REPRESENTATIONS = { var1 = Hello World var2 AddItem Test Value`, collectionProcessor: (name: string) => `state Collection Processor ${name}`, - decision: (name: string) => `state Decision ${name}${EOL}`, + decision: (name: string) => `state Decision ${name} + Rule: myDecisionRule + 1. foo EqualTo true`, loop: (name: string) => `state Loop ${name}`, - orchestratedStage: (name: string) => `state Orchestrated Stage ${name}${EOL}`, + orchestratedStage: (name: string) => `state Orchestrated Stage ${name} + Step: 1. step1 + + Step: 2. step2 + + Step: 3. step3 +`, recordCreate: (name: string) => `state Record Create ${name}`, recordDelete: (name: string) => `state Record Delete ${name}`, recordLookup: (name: string) => From f335a66466c3657bc9ac2ac58766d94fb0ee5203 Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 15:28:56 -0600 Subject: [PATCH 3/9] Refactor Mermaid Generator Tests and Mock Flow Integration - Simplify test setup by directly using the generateMockFlow function. - Remove redundant flow node generation logic from tests. --- src/test/mermaid_generator_test.ts | 185 +---------------------------- src/test/mock_flow.ts | 7 ++ src/test/uml_generator_test.ts | 4 + 3 files changed, 17 insertions(+), 179 deletions(-) diff --git a/src/test/mermaid_generator_test.ts b/src/test/mermaid_generator_test.ts index 2727949..8f4bc2f 100644 --- a/src/test/mermaid_generator_test.ts +++ b/src/test/mermaid_generator_test.ts @@ -23,187 +23,14 @@ import { Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; - -const NODE_NAMES = { - start: "FLOW_START", - apexPluginCall: "myApexPluginCall", - assignment: "myAssignment", - collectionProcessor: "myCollectionProcessor", - decision: "myDecision", - loop: "myLoop", - orchestratedStage: "myOrchestratedStage", - recordCreate: "myRecordCreate", - recordDelete: "myRecordDelete", - recordLookup: "myRecordLookup", - recordRollback: "myRecordRollback", - recordUpdate: "myRecordUpdate", - screen: "myScreen", - stageSteps: ["step1", "step2", "step3"], - step: "myStep", - subflow: "mySubflow", - transform: "myTransform", - wait: "myWait", - actionCall: "myActionCall", -}; - -function generateMockFlow(): ParsedFlow { - return { - label: "Test Flow", - start: { - name: NODE_NAMES.start, - } as flowTypes.FlowStart, - apexPluginCalls: getFlowNodes( - NODE_NAMES.apexPluginCall, - ) as flowTypes.FlowApexPluginCall[], - assignments: getFlowNodes( - NODE_NAMES.assignment, - ) as flowTypes.FlowAssignment[], - collectionProcessors: getFlowNodes( - NODE_NAMES.collectionProcessor, - ) as flowTypes.FlowCollectionProcessor[], - decisions: [ - generateDecision(NODE_NAMES.decision), - ] as flowTypes.FlowDecision[], - loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], - orchestratedStages: [ - generateStage(NODE_NAMES.orchestratedStage, NODE_NAMES.stageSteps), - ], - recordCreates: getFlowNodes( - NODE_NAMES.recordCreate, - ) as flowTypes.FlowRecordCreate[], - recordDeletes: getFlowNodes( - NODE_NAMES.recordDelete, - ) as flowTypes.FlowRecordDelete[], - recordLookups: getFlowNodes( - NODE_NAMES.recordLookup, - ) as flowTypes.FlowRecordLookup[], - recordRollbacks: getFlowNodes( - NODE_NAMES.recordRollback, - ) as flowTypes.FlowRecordRollback[], - recordUpdates: getFlowNodes( - NODE_NAMES.recordUpdate, - ) as flowTypes.FlowRecordUpdate[], - screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], - steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], - subflows: getFlowNodes(NODE_NAMES.subflow) as flowTypes.FlowSubflow[], - transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], - waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], - actionCalls: getFlowNodes( - NODE_NAMES.actionCall, - ) as flowTypes.FlowActionCall[], - transitions: [ - { - from: NODE_NAMES.start, - to: NODE_NAMES.apexPluginCall, - fault: false, - }, - { - from: NODE_NAMES.apexPluginCall, - to: NODE_NAMES.assignment, - fault: false, - label: "Normal Transition", - }, - { - from: NODE_NAMES.assignment, - to: NODE_NAMES.decision, - fault: true, - label: "Error Path", - }, - ], - }; -} - -function getFlowNodes(name: string): flowTypes.FlowNode[] { - return [{ name: `${name}`, label: `${name}` }] as flowTypes.FlowNode[]; -} - -function generateStage( - name: string, - stepNames: string[], -): flowTypes.FlowOrchestratedStage { - return { - name: `${name}`, - label: `${name}`, - elementSubtype: "OrchestratedStage", - locationX: 0, - locationY: 0, - description: `${name}`, - stageSteps: stepNames.map((stepName) => ({ - name: `${stepName}`, - label: `${stepName}`, - elementSubtype: "Step", - locationX: 0, - locationY: 0, - description: `${stepName}`, - actionName: `${stepName}Action`, - actionType: flowTypes.FlowStageStepActionType.STEP_BACKGROUND, - })), - } as flowTypes.FlowOrchestratedStage; -} - -function generateDecision(name: string): flowTypes.FlowDecision { - return { - name: name, - label: name, - elementSubtype: "Decision", - locationX: 0, - locationY: 0, - description: name, - rules: [ - { - name: `${name}_rule1`, - label: "Rule 1", - description: "Rule 1 description", - conditions: [ - { - leftValueReference: "leftValue", - operator: "EqualTo", - rightValue: { - stringValue: "rightValue", - }, - processMetadataValues: [], - }, - ], - }, - { - name: `${name}_rule2`, - label: "Rule 2", - description: "Rule 2 description", - conditions: [ - { - leftValueReference: "anotherLeftValue", - operator: "NotEqualTo", - rightValue: { - stringValue: "anotherRightValue", - }, - processMetadataValues: [], - }, - { - leftValueReference: "thirdLeftValue", - operator: "GreaterThan", - rightValue: { - numberValue: "10", - }, - processMetadataValues: [], - }, - ], - conditionLogic: "1 AND 2", - }, - ], - } as flowTypes.FlowDecision; -} +import { generateMockFlow } from "./mock_flow.ts"; // @ts-ignore: Deno types Deno.test("MermaidGenerator", async (t) => { - let systemUnderTest: MermaidGenerator; - let mockedFlow: ParsedFlow; + const mockedFlow = generateMockFlow(); + const systemUnderTest = new MermaidGenerator(mockedFlow); let result: string; - await t.step("Setup", () => { - mockedFlow = generateMockFlow(); - systemUnderTest = new MermaidGenerator(mockedFlow); - }); - await t.step("should generate header", () => { const label = "Test Flow"; result = systemUnderTest.getHeader(label); @@ -288,7 +115,7 @@ Deno.test("MermaidGenerator", async (t) => { result = systemUnderTest.toUmlString(node); assertStringIncludes( result, - "+", + "+" ); assertStringIncludes(result, "class myNode pink"); }); @@ -350,11 +177,11 @@ Deno.test("MermaidGenerator", async (t) => { assertStringIncludes(result, "FLOW_START --> myApexPluginCall"); assertStringIncludes( result, - "myApexPluginCall --> myAssignment : Normal Transition ", + "myApexPluginCall --> myAssignment : Normal Transition " ); assertStringIncludes( result, - "myAssignment --> myDecision : ❌ Error Path ❌", + "myAssignment --> myDecision : ❌ Error Path ❌" ); assertStringIncludes(result, "class"); diff --git a/src/test/mock_flow.ts b/src/test/mock_flow.ts index 945e18c..daf4a09 100644 --- a/src/test/mock_flow.ts +++ b/src/test/mock_flow.ts @@ -93,6 +93,13 @@ export function generateMockFlow(): ParsedFlow { from: NODE_NAMES.apexPluginCall, to: NODE_NAMES.assignment, fault: false, + label: "Normal Transition", + }, + { + from: NODE_NAMES.assignment, + to: NODE_NAMES.decision, + fault: true, + label: "Error Path", }, { from: NODE_NAMES.assignment, diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index 31a9ee5..ba6e5bb 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -138,6 +138,10 @@ Deno.test("UmlGenerator", async (t) => { NODE_NAMES.apexPluginCall, NODE_NAMES.assignment ), + UML_REPRESENTATIONS.transition( + NODE_NAMES.assignment, + NODE_NAMES.decision + ), UML_REPRESENTATIONS.transition( NODE_NAMES.assignment, NODE_NAMES.collectionProcessor From 6a8b419e8bf48fc454191b3f45b528a5340832ef Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 15:31:31 -0600 Subject: [PATCH 4/9] Refactor PlantUmlGenerator Tests and Introduce Mock Flow Utilities - Replace inline mock flow generation logic with a dedicated utility function call. - Reformat with `deno fmt` --- src/test/graphviz_generator_test.ts | 24 ++--- src/test/mermaid_generator_test.ts | 8 +- src/test/plantuml_generator_test.ts | 123 +------------------------- src/test/uml_generator_test.ts | 74 ++++++++-------- src/test/{ => utilities}/mock_flow.ts | 26 +++--- 5 files changed, 70 insertions(+), 185 deletions(-) rename src/test/{ => utilities}/mock_flow.ts (93%) diff --git a/src/test/graphviz_generator_test.ts b/src/test/graphviz_generator_test.ts index 6148491..082b83d 100644 --- a/src/test/graphviz_generator_test.ts +++ b/src/test/graphviz_generator_test.ts @@ -28,7 +28,7 @@ import { Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; -import { generateMockFlow } from "./mock_flow.ts"; +import { generateMockFlow } from "./utilities/mock_flow.ts"; const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; @@ -38,7 +38,7 @@ function generateTable( icon: Icon, skinColor: SkinColor, fontColor: string, - innerNodeBody?: string + innerNodeBody?: string, ) { const formattedInnerNodeBody = innerNodeBody ? `${EOL}${innerNodeBody}${EOL}` @@ -65,7 +65,7 @@ function generateTable( function generateInnerNodeCell( color: FontColor, expectedLabel: string, - content: string[] + content: string[], ) { return ` @@ -111,8 +111,8 @@ Deno.test("GraphVizGenerator", async (t) => { "Apex Plugin Call", Icon.CODE, SkinColor.NONE, - FontColor.BLACK - ) + FontColor.BLACK, + ), ); }); @@ -132,8 +132,8 @@ Deno.test("GraphVizGenerator", async (t) => { "Assignment", Icon.ASSIGNMENT, SkinColor.ORANGE, - FontColor.WHITE - ) + FontColor.WHITE, + ), ); }); @@ -164,8 +164,8 @@ Deno.test("GraphVizGenerator", async (t) => { FontColor.WHITE, generateInnerNodeCell(FontColor.WHITE, "Rule myDecisionRule", [ "1. foo EqualTo true", - ]) - ) + ]), + ), ); }); @@ -203,8 +203,8 @@ Deno.test("GraphVizGenerator", async (t) => { generateInnerNodeCells([ generateInnerNodeCell(FontColor.WHITE, "Stage Step 1. step1", []), generateInnerNodeCell(FontColor.WHITE, "Stage Step 2. step2", []), - ]) - ) + ]), + ), ); }); @@ -251,7 +251,7 @@ Deno.test("GraphVizGenerator", async (t) => { result = systemUnderTest.getTransition(mockedFlow.transitions![0]); assertEquals( result, - 'FLOW_START -> myApexPluginCall [label="" color="black" style=""]' + 'FLOW_START -> myApexPluginCall [label="" color="black" style=""]', ); }); }); diff --git a/src/test/mermaid_generator_test.ts b/src/test/mermaid_generator_test.ts index 8f4bc2f..cdec89f 100644 --- a/src/test/mermaid_generator_test.ts +++ b/src/test/mermaid_generator_test.ts @@ -23,7 +23,7 @@ import { Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; -import { generateMockFlow } from "./mock_flow.ts"; +import { generateMockFlow } from "./utilities/mock_flow.ts"; // @ts-ignore: Deno types Deno.test("MermaidGenerator", async (t) => { @@ -115,7 +115,7 @@ Deno.test("MermaidGenerator", async (t) => { result = systemUnderTest.toUmlString(node); assertStringIncludes( result, - "+" + "+", ); assertStringIncludes(result, "class myNode pink"); }); @@ -177,11 +177,11 @@ Deno.test("MermaidGenerator", async (t) => { assertStringIncludes(result, "FLOW_START --> myApexPluginCall"); assertStringIncludes( result, - "myApexPluginCall --> myAssignment : Normal Transition " + "myApexPluginCall --> myAssignment : Normal Transition ", ); assertStringIncludes( result, - "myAssignment --> myDecision : ❌ Error Path ❌" + "myAssignment --> myDecision : ❌ Error Path ❌", ); assertStringIncludes(result, "class"); diff --git a/src/test/plantuml_generator_test.ts b/src/test/plantuml_generator_test.ts index 068399c..1e86628 100644 --- a/src/test/plantuml_generator_test.ts +++ b/src/test/plantuml_generator_test.ts @@ -15,7 +15,6 @@ */ import { assertEquals, assertStringIncludes } from "@std/assert"; -import type { ParsedFlow } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { PlantUmlGenerator } from "../main/plantuml_generator.ts"; import { @@ -23,129 +22,13 @@ import { Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; - -const NODE_NAMES = { - start: "FLOW_START", - apexPluginCall: "myApexPluginCall", - assignment: "myAssignment", - collectionProcessor: "myCollectionProcessor", - decision: "myDecision", - loop: "myLoop", - orchestratedStage: "myOrchestratedStage", - recordCreate: "myRecordCreate", - recordDelete: "myRecordDelete", - recordLookup: "myRecordLookup", - recordRollback: "myRecordRollback", - recordUpdate: "myRecordUpdate", - screen: "myScreen", - stageSteps: ["step1", "step2", "step3"], - step: "myStep", - subflow: "mySubflow", - transform: "myTransform", - wait: "myWait", - actionCall: "myActionCall", -}; - -function generateMockFlow(): ParsedFlow { - return { - start: { - name: NODE_NAMES.start, - } as flowTypes.FlowStart, - apexPluginCalls: getFlowNodes( - NODE_NAMES.apexPluginCall, - ) as flowTypes.FlowApexPluginCall[], - assignments: getFlowNodes( - NODE_NAMES.assignment, - ) as flowTypes.FlowAssignment[], - collectionProcessors: getFlowNodes( - NODE_NAMES.collectionProcessor, - ) as flowTypes.FlowCollectionProcessor[], - decisions: getFlowNodes(NODE_NAMES.decision) as flowTypes.FlowDecision[], - loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], - orchestratedStages: [ - generateStage(NODE_NAMES.orchestratedStage, NODE_NAMES.stageSteps), - ], - recordCreates: getFlowNodes( - NODE_NAMES.recordCreate, - ) as flowTypes.FlowRecordCreate[], - recordDeletes: getFlowNodes( - NODE_NAMES.recordDelete, - ) as flowTypes.FlowRecordDelete[], - recordLookups: getFlowNodes( - NODE_NAMES.recordLookup, - ) as flowTypes.FlowRecordLookup[], - recordRollbacks: getFlowNodes( - NODE_NAMES.recordRollback, - ) as flowTypes.FlowRecordRollback[], - recordUpdates: getFlowNodes( - NODE_NAMES.recordUpdate, - ) as flowTypes.FlowRecordUpdate[], - screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], - steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], - subflows: getFlowNodes(NODE_NAMES.subflow) as flowTypes.FlowSubflow[], - transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], - waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], - actionCalls: getFlowNodes( - NODE_NAMES.actionCall, - ) as flowTypes.FlowActionCall[], - transitions: [ - { - from: NODE_NAMES.start, - to: NODE_NAMES.apexPluginCall, - fault: false, - }, - { - from: NODE_NAMES.apexPluginCall, - to: NODE_NAMES.assignment, - fault: false, - }, - { - from: NODE_NAMES.assignment, - to: NODE_NAMES.collectionProcessor, - fault: false, - }, - ], - }; -} - -function getFlowNodes(name: string): flowTypes.FlowNode[] { - return [{ name: `${name}`, label: `${name}` }] as flowTypes.FlowNode[]; -} - -function generateStage( - name: string, - stepNames: string[], -): flowTypes.FlowOrchestratedStage { - return { - name: `${name}`, - label: `${name}`, - elementSubtype: "OrchestratedStage", - locationX: 0, - locationY: 0, - description: `${name}`, - stageSteps: stepNames.map((stepName) => ({ - name: `${stepName}`, - label: `${stepName}`, - elementSubtype: "Step", - locationX: 0, - locationY: 0, - description: `${stepName}`, - actionName: `${stepName}Action`, - actionType: flowTypes.FlowStageStepActionType.STEP_BACKGROUND, - })), - } as flowTypes.FlowOrchestratedStage; -} +import { generateMockFlow } from "./utilities/mock_flow.ts"; Deno.test("PlantUmlGenerator", async (t) => { - let systemUnderTest: PlantUmlGenerator; - let mockedFlow: ParsedFlow; + const mockedFlow = generateMockFlow(); + const systemUnderTest = new PlantUmlGenerator(mockedFlow); let result: string; - await t.step("Setup", () => { - mockedFlow = generateMockFlow(); - systemUnderTest = new PlantUmlGenerator(mockedFlow); - }); - await t.step("should generate header", () => { const label = "foo"; result = systemUnderTest.getHeader(label); diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index ba6e5bb..69b28e5 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -18,7 +18,7 @@ import { assertEquals } from "@std/assert"; import { ParsedFlow, Transition } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { DiagramNode, UmlGenerator } from "../main/uml_generator.ts"; -import { generateMockFlow, NODE_NAMES } from "./mock_flow.ts"; +import { generateMockFlow, NODE_NAMES } from "./utilities/mock_flow.ts"; const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; const TRANSITION_ARROW = "-->"; @@ -34,11 +34,13 @@ const UML_REPRESENTATIONS = { var1 = Hello World var2 AddItem Test Value`, collectionProcessor: (name: string) => `state Collection Processor ${name}`, - decision: (name: string) => `state Decision ${name} + decision: (name: string) => + `state Decision ${name} Rule: myDecisionRule 1. foo EqualTo true`, loop: (name: string) => `state Loop ${name}`, - orchestratedStage: (name: string) => `state Orchestrated Stage ${name} + orchestratedStage: (name: string) => + `state Orchestrated Stage ${name} Step: 1. step1 Step: 2. step2 @@ -132,19 +134,19 @@ Deno.test("UmlGenerator", async (t) => { UML_REPRESENTATIONS.actionCall(NODE_NAMES.actionCall), UML_REPRESENTATIONS.transition( NODE_NAMES.start, - NODE_NAMES.apexPluginCall + NODE_NAMES.apexPluginCall, ), UML_REPRESENTATIONS.transition( NODE_NAMES.apexPluginCall, - NODE_NAMES.assignment + NODE_NAMES.assignment, ), UML_REPRESENTATIONS.transition( NODE_NAMES.assignment, - NODE_NAMES.decision + NODE_NAMES.decision, ), UML_REPRESENTATIONS.transition( NODE_NAMES.assignment, - NODE_NAMES.collectionProcessor + NODE_NAMES.collectionProcessor, ), ].join(EOL); @@ -158,7 +160,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(UML_REPRESENTATIONS.screen(NODE_NAMES.screen)), - false + false, ); }); @@ -169,7 +171,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(UML_REPRESENTATIONS.screen(NODE_NAMES.screen)), - false + false, ); }); @@ -232,10 +234,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -268,10 +270,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -330,10 +332,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -361,10 +363,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -413,10 +415,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -473,10 +475,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -506,10 +508,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -564,10 +566,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -624,10 +626,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -663,10 +665,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step( @@ -732,10 +734,10 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); - } + }, ); await t.step("should handle minimal start node configuration", () => { @@ -769,7 +771,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); @@ -784,7 +786,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), false, - `Expected UML: ${uml} to NOT contain: ${content}` + `Expected UML: ${uml} to NOT contain: ${content}`, ); }); }); @@ -826,7 +828,7 @@ Deno.test("UmlGenerator", async (t) => { // Create a fresh generator with the record change mock flow const recordChangeGenerator = new ConcreteUmlGenerator( - recordChangeMockFlow + recordChangeMockFlow, ); const uml = recordChangeGenerator.generateUml(); @@ -845,7 +847,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes(content), true, - `Expected UML: ${uml} to contain: ${content}` + `Expected UML: ${uml} to contain: ${content}`, ); }); }); @@ -867,7 +869,7 @@ Deno.test("UmlGenerator", async (t) => { assertEquals( uml.includes("Flow Start FLOW_START"), false, - "Should not contain flow start node when undefined" + "Should not contain flow start node when undefined", ); }); }); diff --git a/src/test/mock_flow.ts b/src/test/utilities/mock_flow.ts similarity index 93% rename from src/test/mock_flow.ts rename to src/test/utilities/mock_flow.ts index daf4a09..118d4e3 100644 --- a/src/test/mock_flow.ts +++ b/src/test/utilities/mock_flow.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import * as flowTypes from "../main/flow_types.ts"; -import { ParsedFlow } from "../main/flow_parser.ts"; +import * as flowTypes from "../../main/flow_types.ts"; +import { ParsedFlow } from "../../main/flow_parser.ts"; export const NODE_NAMES = { label: "test", @@ -47,33 +47,33 @@ export function generateMockFlow(): ParsedFlow { name: NODE_NAMES.start, } as flowTypes.FlowStart, apexPluginCalls: getFlowNodes( - NODE_NAMES.apexPluginCall + NODE_NAMES.apexPluginCall, ) as flowTypes.FlowApexPluginCall[], assignments: getFlowNodes( - NODE_NAMES.assignment + NODE_NAMES.assignment, ) as flowTypes.FlowAssignment[], collectionProcessors: getFlowNodes( - NODE_NAMES.collectionProcessor + NODE_NAMES.collectionProcessor, ) as flowTypes.FlowCollectionProcessor[], decisions: getFlowNodes(NODE_NAMES.decision) as flowTypes.FlowDecision[], loops: getFlowNodes(NODE_NAMES.loop) as flowTypes.FlowLoop[], orchestratedStages: getFlowNodes( - NODE_NAMES.orchestratedStage + NODE_NAMES.orchestratedStage, ) as flowTypes.FlowOrchestratedStage[], recordCreates: getFlowNodes( - NODE_NAMES.recordCreate + NODE_NAMES.recordCreate, ) as flowTypes.FlowRecordCreate[], recordDeletes: getFlowNodes( - NODE_NAMES.recordDelete + NODE_NAMES.recordDelete, ) as flowTypes.FlowRecordDelete[], recordLookups: getFlowNodes( - NODE_NAMES.recordLookup + NODE_NAMES.recordLookup, ) as flowTypes.FlowRecordLookup[], recordRollbacks: getFlowNodes( - NODE_NAMES.recordRollback + NODE_NAMES.recordRollback, ) as flowTypes.FlowRecordRollback[], recordUpdates: getFlowNodes( - NODE_NAMES.recordUpdate + NODE_NAMES.recordUpdate, ) as flowTypes.FlowRecordUpdate[], screens: getFlowNodes(NODE_NAMES.screen) as flowTypes.FlowScreen[], steps: getFlowNodes(NODE_NAMES.step) as flowTypes.FlowStep[], @@ -81,7 +81,7 @@ export function generateMockFlow(): ParsedFlow { transforms: getFlowNodes(NODE_NAMES.transform) as flowTypes.FlowTransform[], waits: getFlowNodes(NODE_NAMES.wait) as flowTypes.FlowWait[], actionCalls: getFlowNodes( - NODE_NAMES.actionCall + NODE_NAMES.actionCall, ) as flowTypes.FlowActionCall[], transitions: [ { @@ -224,7 +224,7 @@ function createBasicNode(name: string): flowTypes.FlowNode[] { function generateStage( name: string, - stepNames: string[] = [] + stepNames: string[] = [], ): flowTypes.FlowOrchestratedStage[] { return [ { From 60f49633a3b1b22f6abc0a015412e1e54125ec8b Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 16:05:27 -0600 Subject: [PATCH 5/9] Update dependencies and refactor FlowFileChangeDetector tests - Upgrade dependencies in deno.json and deno.lock. - Refactor FlowFileChangeDetector tests by extracting setup logic into dedicated functions. - Ensure consistent mock setup across test cases. - Remove use of `as any` --- deno.json | 16 +- deno.lock | 209 +++++++++++---------- src/test/flow_file_change_detector_test.ts | 81 ++++---- src/test/github_client_test.ts | 2 + 4 files changed, 170 insertions(+), 138 deletions(-) diff --git a/deno.json b/deno.json index 54feb3b..220b2b0 100644 --- a/deno.json +++ b/deno.json @@ -4,14 +4,14 @@ "license": "Apache", "exports": "./src/main/main.ts", "imports": { - "@actions/github": "npm:@actions/github@^6.0.0", - "@octokit/core": "npm:@octokit/core@^6.1.4", - "@std/assert": "jsr:@std/assert@^1.0.13", - "@std/cli": "jsr:@std/cli@^1.0.17", - "@std/fs": "jsr:@std/fs@^1.0.17", - "@std/path": "jsr:@std/path@^1.0.9", - "@std/testing": "jsr:@std/testing@^1.0.11", - "@types/node": "npm:@types/node@^22.10.2", + "@actions/github": "npm:@actions/github@^6.0.1", + "@octokit/core": "npm:@octokit/core@^6.1.6", + "@std/assert": "jsr:@std/assert@^1.0.16", + "@std/cli": "jsr:@std/cli@^1.0.25", + "@std/fs": "jsr:@std/fs@^1.0.21", + "@std/path": "jsr:@std/path@^1.1.4", + "@std/testing": "jsr:@std/testing@^1.0.16", + "@types/node": "npm:@types/node@^22.19.3", "xml2js": "npm:xml2js@^0.6.2" }, "tasks": { diff --git a/deno.lock b/deno.lock index 61fccfd..198b37a 100644 --- a/deno.lock +++ b/deno.lock @@ -1,68 +1,76 @@ { - "version": "4", + "version": "5", "specifiers": { - "jsr:@std/assert@^1.0.12": "1.0.13", - "jsr:@std/assert@^1.0.13": "1.0.13", - "jsr:@std/cli@^1.0.17": "1.0.17", - "jsr:@std/data-structures@^1.0.6": "1.0.7", - "jsr:@std/fs@^1.0.16": "1.0.17", - "jsr:@std/fs@^1.0.17": "1.0.17", - "jsr:@std/internal@^1.0.6": "1.0.6", - "jsr:@std/path@^1.0.8": "1.0.9", - "jsr:@std/path@^1.0.9": "1.0.9", - "jsr:@std/testing@^1.0.11": "1.0.11", - "npm:@actions/github@*": "6.0.0_@octokit+core@5.2.0", - "npm:@actions/github@6": "6.0.0_@octokit+core@5.2.0", - "npm:@octokit/core@*": "6.1.4", - "npm:@octokit/core@^6.1.4": "6.1.4", + "jsr:@std/assert@^1.0.15": "1.0.16", + "jsr:@std/assert@^1.0.16": "1.0.16", + "jsr:@std/cli@^1.0.25": "1.0.25", + "jsr:@std/data-structures@^1.0.9": "1.0.9", + "jsr:@std/fs@^1.0.19": "1.0.21", + "jsr:@std/fs@^1.0.21": "1.0.21", + "jsr:@std/internal@^1.0.12": "1.0.12", + "jsr:@std/path@^1.1.2": "1.1.4", + "jsr:@std/path@^1.1.4": "1.1.4", + "jsr:@std/testing@^1.0.16": "1.0.16", + "npm:@actions/github@^6.0.1": "6.0.1_@octokit+core@5.2.2", + "npm:@octokit/core@^6.1.6": "6.1.6", "npm:@types/node@*": "22.5.4", - "npm:@types/node@^22.10.2": "22.10.2", + "npm:@types/node@^22.19.3": "22.19.3", "npm:xml2js@~0.6.2": "0.6.2" }, "jsr": { - "@std/assert@1.0.13": { - "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", + "@std/assert@1.0.16": { + "integrity": "6a7272ed1eaa77defe76e5ff63ca705d9c495077e2d5fd0126d2b53fc5bd6532", "dependencies": [ "jsr:@std/internal" ] }, - "@std/cli@1.0.17": { - "integrity": "e15b9abe629e17be90cc6216327f03a29eae613365f1353837fa749aad29ce7b" + "@std/cli@1.0.25": { + "integrity": "1f85051b370c97a7a9dfc6ba626e7ed57a91bea8c081597276d1e78d929d8c91", + "dependencies": [ + "jsr:@std/internal" + ] }, - "@std/data-structures@1.0.7": { - "integrity": "16932d2c8d281f65eaaa2209af2473209881e33b1ced54cd1b015e7b4cdbb0d2" + "@std/data-structures@1.0.9": { + "integrity": "033d6e17e64bf1f84a614e647c1b015fa2576ae3312305821e1a4cb20674bb4d" }, - "@std/fs@1.0.17": { - "integrity": "1c00c632677c1158988ef7a004cb16137f870aafdb8163b9dce86ec652f3952b", + "@std/fs@1.0.21": { + "integrity": "d720fe1056d78d43065a4d6e0eeb2b19f34adb8a0bc7caf3a4dbf1d4178252cd", "dependencies": [ - "jsr:@std/path@^1.0.9" + "jsr:@std/internal", + "jsr:@std/path@^1.1.4" ] }, - "@std/internal@1.0.6": { - "integrity": "9533b128f230f73bd209408bb07a4b12f8d4255ab2a4d22a1fd6d87304aca9a4" + "@std/internal@1.0.12": { + "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" }, - "@std/path@1.0.9": { - "integrity": "260a49f11edd3db93dd38350bf9cd1b4d1366afa98e81b86167b4e3dd750129e" + "@std/path@1.1.4": { + "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5", + "dependencies": [ + "jsr:@std/internal" + ] }, - "@std/testing@1.0.11": { - "integrity": "12b3db12d34f0f385a26248933bde766c0f8c5ad8b6ab34d4d38f528ab852f48", + "@std/testing@1.0.16": { + "integrity": "a917ffdeb5924c9be436dc78bc32e511760e14d3a96e49c607fc5ecca86d0092", "dependencies": [ - "jsr:@std/assert@^1.0.12", + "jsr:@std/assert@^1.0.15", "jsr:@std/data-structures", - "jsr:@std/fs@^1.0.16", + "jsr:@std/fs@^1.0.19", "jsr:@std/internal", - "jsr:@std/path@^1.0.8" + "jsr:@std/path@^1.1.2" ] } }, "npm": { - "@actions/github@6.0.0_@octokit+core@5.2.0": { - "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", + "@actions/github@6.0.1_@octokit+core@5.2.2": { + "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", "dependencies": [ "@actions/http-client", - "@octokit/core@5.2.0", + "@octokit/core@5.2.2", "@octokit/plugin-paginate-rest", - "@octokit/plugin-rest-endpoint-methods" + "@octokit/plugin-rest-endpoint-methods", + "@octokit/request@8.4.1", + "@octokit/request-error@5.1.1", + "undici" ] }, "@actions/http-client@2.2.3": { @@ -81,41 +89,41 @@ "@octokit/auth-token@5.1.2": { "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==" }, - "@octokit/core@5.2.0": { - "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", + "@octokit/core@5.2.2": { + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dependencies": [ "@octokit/auth-token@4.0.0", "@octokit/graphql@7.1.1", "@octokit/request@8.4.1", "@octokit/request-error@5.1.1", - "@octokit/types@13.8.0", + "@octokit/types@13.10.0", "before-after-hook@2.2.3", "universal-user-agent@6.0.1" ] }, - "@octokit/core@6.1.4": { - "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==", + "@octokit/core@6.1.6": { + "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", "dependencies": [ "@octokit/auth-token@5.1.2", - "@octokit/graphql@8.2.1", - "@octokit/request@9.2.2", - "@octokit/request-error@6.1.7", - "@octokit/types@13.8.0", + "@octokit/graphql@8.2.2", + "@octokit/request@9.2.4", + "@octokit/request-error@6.1.8", + "@octokit/types@14.1.0", "before-after-hook@3.0.2", - "universal-user-agent@7.0.2" + "universal-user-agent@7.0.3" ] }, - "@octokit/endpoint@10.1.3": { - "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", + "@octokit/endpoint@10.1.4": { + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", "dependencies": [ - "@octokit/types@13.8.0", - "universal-user-agent@7.0.2" + "@octokit/types@14.1.0", + "universal-user-agent@7.0.3" ] }, "@octokit/endpoint@9.0.6": { "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "dependencies": [ - "@octokit/types@13.8.0", + "@octokit/types@13.10.0", "universal-user-agent@6.0.1" ] }, @@ -123,50 +131,53 @@ "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", "dependencies": [ "@octokit/request@8.4.1", - "@octokit/types@13.8.0", + "@octokit/types@13.10.0", "universal-user-agent@6.0.1" ] }, - "@octokit/graphql@8.2.1": { - "integrity": "sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==", + "@octokit/graphql@8.2.2": { + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", "dependencies": [ - "@octokit/request@9.2.2", - "@octokit/types@13.8.0", - "universal-user-agent@7.0.2" + "@octokit/request@9.2.4", + "@octokit/types@14.1.0", + "universal-user-agent@7.0.3" ] }, "@octokit/openapi-types@20.0.0": { "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" }, - "@octokit/openapi-types@23.0.1": { - "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==" + "@octokit/openapi-types@24.2.0": { + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==" + }, + "@octokit/openapi-types@25.1.0": { + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==" }, - "@octokit/plugin-paginate-rest@9.2.2_@octokit+core@5.2.0": { + "@octokit/plugin-paginate-rest@9.2.2_@octokit+core@5.2.2": { "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", "dependencies": [ - "@octokit/core@5.2.0", + "@octokit/core@5.2.2", "@octokit/types@12.6.0" ] }, - "@octokit/plugin-rest-endpoint-methods@10.4.1_@octokit+core@5.2.0": { + "@octokit/plugin-rest-endpoint-methods@10.4.1_@octokit+core@5.2.2": { "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", "dependencies": [ - "@octokit/core@5.2.0", + "@octokit/core@5.2.2", "@octokit/types@12.6.0" ] }, "@octokit/request-error@5.1.1": { "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "dependencies": [ - "@octokit/types@13.8.0", + "@octokit/types@13.10.0", "deprecation", "once" ] }, - "@octokit/request-error@6.1.7": { - "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", + "@octokit/request-error@6.1.8": { + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", "dependencies": [ - "@octokit/types@13.8.0" + "@octokit/types@14.1.0" ] }, "@octokit/request@8.4.1": { @@ -174,18 +185,18 @@ "dependencies": [ "@octokit/endpoint@9.0.6", "@octokit/request-error@5.1.1", - "@octokit/types@13.8.0", + "@octokit/types@13.10.0", "universal-user-agent@6.0.1" ] }, - "@octokit/request@9.2.2": { - "integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==", + "@octokit/request@9.2.4": { + "integrity": "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==", "dependencies": [ - "@octokit/endpoint@10.1.3", - "@octokit/request-error@6.1.7", - "@octokit/types@13.8.0", + "@octokit/endpoint@10.1.4", + "@octokit/request-error@6.1.8", + "@octokit/types@14.1.0", "fast-content-type-parse", - "universal-user-agent@7.0.2" + "universal-user-agent@7.0.3" ] }, "@octokit/types@12.6.0": { @@ -194,16 +205,22 @@ "@octokit/openapi-types@20.0.0" ] }, - "@octokit/types@13.8.0": { - "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "@octokit/types@13.10.0": { + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dependencies": [ + "@octokit/openapi-types@24.2.0" + ] + }, + "@octokit/types@14.1.0": { + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dependencies": [ - "@octokit/openapi-types@23.0.1" + "@octokit/openapi-types@25.1.0" ] }, - "@types/node@22.10.2": { - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "@types/node@22.19.3": { + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "dependencies": [ - "undici-types@6.20.0" + "undici-types@6.21.0" ] }, "@types/node@22.5.4": { @@ -239,11 +256,11 @@ "undici-types@6.19.8": { "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, - "undici-types@6.20.0": { - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + "undici-types@6.21.0": { + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, - "undici@5.28.4": { - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "undici@5.29.0": { + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "dependencies": [ "@fastify/busboy" ] @@ -251,8 +268,8 @@ "universal-user-agent@6.0.1": { "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" }, - "universal-user-agent@7.0.2": { - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + "universal-user-agent@7.0.3": { + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==" }, "wrappy@1.0.2": { "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" @@ -556,14 +573,14 @@ }, "workspace": { "dependencies": [ - "jsr:@std/assert@^1.0.13", - "jsr:@std/cli@^1.0.17", - "jsr:@std/fs@^1.0.17", - "jsr:@std/path@^1.0.9", - "jsr:@std/testing@^1.0.11", - "npm:@actions/github@6", - "npm:@octokit/core@^6.1.4", - "npm:@types/node@^22.10.2", + "jsr:@std/assert@^1.0.16", + "jsr:@std/cli@^1.0.25", + "jsr:@std/fs@^1.0.21", + "jsr:@std/path@^1.1.4", + "jsr:@std/testing@^1.0.16", + "npm:@actions/github@^6.0.1", + "npm:@octokit/core@^6.1.6", + "npm:@types/node@^22.19.3", "npm:xml2js@~0.6.2" ] } diff --git a/src/test/flow_file_change_detector_test.ts b/src/test/flow_file_change_detector_test.ts index 9fe1767..0051c3d 100644 --- a/src/test/flow_file_change_detector_test.ts +++ b/src/test/flow_file_change_detector_test.ts @@ -26,32 +26,49 @@ import { const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; const FLOW_FILE_PATH = "file2" + FLOW_FILE_EXTENSION; -Deno.test("FlowFileChangeDetector", async (t) => { - let detector: FlowFileChangeDetector; - - // Function to set up the mock implementations - const setupMocks = () => { - // tslint:disable:no-any - (detector as any).executeVersionCommand = () => undefined; - (detector as any).executeRevParseCommand = () => undefined; - (detector as any).executeDiffCommand = () => - new TextEncoder().encode( - ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL), - ); - (detector as any).executeGetFileContentCommand = () => - new TextEncoder().encode("file content"); - // tslint:enable:no-any +/** + * Test helper type that exposes private methods for mocking purposes. + * Uses Omit to remove private methods and re-adds them as public. + */ +type TestableFlowFileChangeDetector = + & Omit< + FlowFileChangeDetector, + | "executeVersionCommand" + | "executeRevParseCommand" + | "executeDiffCommand" + | "executeGetFileContentCommand" + > + & { + executeVersionCommand: () => void; + executeRevParseCommand: () => void; + executeDiffCommand: () => Uint8Array; + executeGetFileContentCommand: ( + filePath: string, + commitHash: string, + ) => Uint8Array; }; - await t.step("beforeEach setup", async () => { - Configuration.getInstance = () => getTestConfig(); - detector = new FlowFileChangeDetector(); - setupMocks(); // Initial setup - }); +function createDetector(): TestableFlowFileChangeDetector { + const detector = + new FlowFileChangeDetector() as unknown as TestableFlowFileChangeDetector; + detector.executeVersionCommand = () => undefined; + detector.executeRevParseCommand = () => undefined; + detector.executeDiffCommand = () => + new TextEncoder().encode( + ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL), + ); + detector.executeGetFileContentCommand = () => + new TextEncoder().encode("file content"); + return detector; +} + +Configuration.getInstance = () => getTestConfig(); +Deno.test("FlowFileChangeDetector", async (t) => { await t.step( "should get flow files when git is installed and in a repo", () => { + const detector = createDetector(); const flowFiles = detector.getFlowFiles(); assertEquals(flowFiles, [FLOW_FILE_PATH]); @@ -59,9 +76,8 @@ Deno.test("FlowFileChangeDetector", async (t) => { ); await t.step("should throw error if git is not installed", () => { - setupMocks(); // Reset mocks before this test - // tslint:disable-next-line:no-any - (detector as any).executeVersionCommand = () => { + const detector = createDetector(); + detector.executeVersionCommand = () => { throw new Error(ERROR_MESSAGES.gitIsNotInstalledError); }; @@ -73,9 +89,8 @@ Deno.test("FlowFileChangeDetector", async (t) => { }); await t.step("should throw error if not in a git repo", () => { - setupMocks(); // Reset mocks before this test - // tslint:disable-next-line:no-any - (detector as any).executeRevParseCommand = () => { + const detector = createDetector(); + detector.executeRevParseCommand = () => { throw new Error(ERROR_MESSAGES.notInGitRepoError); }; @@ -87,9 +102,8 @@ Deno.test("FlowFileChangeDetector", async (t) => { }); await t.step("should throw error if git diff fails", () => { - setupMocks(); // Reset mocks before this test - // tslint:disable-next-line:no-any - (detector as any).executeDiffCommand = () => { + const detector = createDetector(); + detector.executeDiffCommand = () => { throw new Error("Diff error"); }; @@ -101,23 +115,22 @@ Deno.test("FlowFileChangeDetector", async (t) => { }); await t.step("should get file content from old version", () => { - setupMocks(); // Reset mocks before this test + const detector = createDetector(); const fileContent = detector.getFileContent(FLOW_FILE_PATH, "old"); assertEquals(fileContent, "file content"); }); await t.step("should get file content from new version", () => { - setupMocks(); // Reset mocks before this test + const detector = createDetector(); const fileContent = detector.getFileContent(FLOW_FILE_PATH, "new"); assertEquals(fileContent, "file content"); }); await t.step("should throw error if unable to get file content", () => { - setupMocks(); // Reset mocks before this test - // tslint:disable-next-line:no-any - (detector as any).executeGetFileContentCommand = () => { + const detector = createDetector(); + detector.executeGetFileContentCommand = () => { throw new Error("Get file content error"); }; diff --git a/src/test/github_client_test.ts b/src/test/github_client_test.ts index e566d3c..6f3ea9c 100644 --- a/src/test/github_client_test.ts +++ b/src/test/github_client_test.ts @@ -101,6 +101,7 @@ const mockContext = { job: "mock-job", runNumber: 1, runId: 1, + runAttempt: 1, action: "mock-action", apiUrl: "https://api.github.com", serverUrl: "https://github.com", @@ -126,6 +127,7 @@ const invalidContext = { job: "mock-job", runNumber: 1, runId: 1, + runAttempt: 1, action: "mock-action", apiUrl: "https://api.github.com", serverUrl: "https://github.com", From e755f77c6784e558c2464d6ba9c3e1bf7bec9039 Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 16:09:05 -0600 Subject: [PATCH 6/9] Move mock configuration generation into `test/utilities` - Introduce `mock_config.ts` to centralize test configuration setup. --- src/test/argument_processor_test.ts | 130 +++++++++--------- src/test/flow_file_change_detector_test.ts | 48 ++++--- src/test/flow_to_uml_transformer_test.ts | 18 +-- .../mock_config.ts} | 2 +- 4 files changed, 96 insertions(+), 102 deletions(-) rename src/test/{test_utils.ts => utilities/mock_config.ts} (95%) diff --git a/src/test/argument_processor_test.ts b/src/test/argument_processor_test.ts index 2cf5db0..300bf0f 100644 --- a/src/test/argument_processor_test.ts +++ b/src/test/argument_processor_test.ts @@ -22,7 +22,7 @@ import { RuntimeConfig, } from "../main/argument_processor.ts"; import { assertEquals, assertThrows } from "@std/assert"; -import { getTestConfig } from "./test_utils.ts"; +import { getTestConfig } from "./utilities/mock_config.ts"; const INVALID_DIAGRAM_TOOL = "unsupported"; const INVALID_FILE_PATH = "invalid/file/path/which/does/not/exist"; @@ -31,7 +31,7 @@ const INVALID_OUTPUT_DIRECTORY = "invalid/directory/path"; const INVALID_MODE = "unsupported"; function setupTest( - configModifications: (config: RuntimeConfig) => void = () => {}, + configModifications: (config: RuntimeConfig) => void = () => {} ) { let testConfiguration = getTestConfig(); configModifications(testConfiguration); @@ -54,16 +54,15 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - ( - config, - ) => (config.diagramTool = INVALID_DIAGRAM_TOOL as DiagramTool), + (config) => + (config.diagramTool = INVALID_DIAGRAM_TOOL as DiagramTool) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.unsupportedDiagramTool(INVALID_DIAGRAM_TOOL), + ERROR_MESSAGES.unsupportedDiagramTool(INVALID_DIAGRAM_TOOL) ); - }, + } ); await t.step( @@ -72,14 +71,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.mode = INVALID_MODE as Mode), + (config) => (config.mode = INVALID_MODE as Mode) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.unsupportedMode(INVALID_MODE), + ERROR_MESSAGES.unsupportedMode(INVALID_MODE) ); - }, + } ); await t.step( @@ -95,7 +94,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); const result = argumentProcessor.getConfig(); assertEquals(result, config); - }, + } ); await t.step( @@ -109,7 +108,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); const result = argumentProcessor.getConfig(); assertEquals(result, config); - }, + } ); await t.step( @@ -125,28 +124,25 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.markdownRequiresMermaid, + ERROR_MESSAGES.markdownRequiresMermaid ); - }, + } ); - await t.step( - "should reject markdown mode without outputDirectory", - () => { - assertThrows( - () => { - const { argumentProcessor } = setupTest((config) => { - config.mode = Mode.MARKDOWN; - config.diagramTool = DiagramTool.MERMAID; - config.outputDirectory = undefined; - }); - argumentProcessor.getConfig(); - }, - Error, - ERROR_MESSAGES.outputDirectoryRequired, - ); - }, - ); + await t.step("should reject markdown mode without outputDirectory", () => { + assertThrows( + () => { + const { argumentProcessor } = setupTest((config) => { + config.mode = Mode.MARKDOWN; + config.diagramTool = DiagramTool.MERMAID; + config.outputDirectory = undefined; + }); + argumentProcessor.getConfig(); + }, + Error, + ERROR_MESSAGES.outputDirectoryRequired + ); + }); await t.step( "should reject markdown mode with non-existent outputDirectory", @@ -161,9 +157,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputDirectory("non/existent/directory"), + ERROR_MESSAGES.invalidOutputDirectory("non/existent/directory") ); - }, + } ); await t.step( @@ -178,9 +174,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputDirectoryRequired, + ERROR_MESSAGES.outputDirectoryRequired ); - }, + } ); await t.step( @@ -195,9 +191,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputFileNameRequired, + ERROR_MESSAGES.outputFileNameRequired ); - }, + } ); await t.step( @@ -213,9 +209,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathDoesNotExist(INVALID_FILE_PATH), + ERROR_MESSAGES.filePathDoesNotExist(INVALID_FILE_PATH) ); - }, + } ); await t.step( @@ -224,14 +220,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputFileName = ""), + (config) => (config.outputFileName = "") ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputFileNameRequired, + ERROR_MESSAGES.outputFileNameRequired ); - }, + } ); await t.step( @@ -240,14 +236,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputFileName = INVALID_OUTPUT_FILE_NAME), + (config) => (config.outputFileName = INVALID_OUTPUT_FILE_NAME) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputFileName(INVALID_OUTPUT_FILE_NAME), + ERROR_MESSAGES.invalidOutputFileName(INVALID_OUTPUT_FILE_NAME) ); - }, + } ); await t.step( @@ -256,14 +252,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputDirectory = INVALID_OUTPUT_DIRECTORY), + (config) => (config.outputDirectory = INVALID_OUTPUT_DIRECTORY) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputDirectory(INVALID_OUTPUT_DIRECTORY), + ERROR_MESSAGES.invalidOutputDirectory(INVALID_OUTPUT_DIRECTORY) ); - }, + } ); await t.step( @@ -272,14 +268,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputDirectory = ""), + (config) => (config.outputDirectory = "") ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputDirectoryRequired, + ERROR_MESSAGES.outputDirectoryRequired ); - }, + } ); await t.step( @@ -294,9 +290,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathOrGitDiffFromAndToHashRequired, + ERROR_MESSAGES.filePathOrGitDiffFromAndToHashRequired ); - }, + } ); await t.step( @@ -305,14 +301,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.filePath = [INVALID_FILE_PATH]), + (config) => (config.filePath = [INVALID_FILE_PATH]) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathAndGitDiffFromAndToHashMutuallyExclusive, + ERROR_MESSAGES.filePathAndGitDiffFromAndToHashMutuallyExclusive ); - }, + } ); await t.step( @@ -321,14 +317,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.gitDiffToHash = undefined), + (config) => (config.gitDiffToHash = undefined) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether, + ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether ); - }, + } ); await t.step( @@ -337,14 +333,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.gitDiffFromHash = undefined), + (config) => (config.gitDiffFromHash = undefined) ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether, + ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether ); - }, + } ); await t.step( @@ -358,7 +354,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); validConfig.argumentProcessor.getConfig(); assertEquals(validConfig.argumentProcessor.getErrors(), []); - }, + } ); await t.step( @@ -376,7 +372,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidDiagramTool.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresMermaid, ]); - }, + } ); await t.step( @@ -394,7 +390,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidToHash.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresHeadHash, ]); - }, + } ); await t.step( @@ -412,7 +408,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidFromHash.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresHeadMinusOne, ]); - }, + } ); await t.step( @@ -432,6 +428,6 @@ Deno.test("ArgumentProcessor", async (t) => { ERROR_MESSAGES.githubActionRequiresHeadHash, ERROR_MESSAGES.githubActionRequiresHeadMinusOne, ]); - }, + } ); }); diff --git a/src/test/flow_file_change_detector_test.ts b/src/test/flow_file_change_detector_test.ts index 0051c3d..8d695c9 100644 --- a/src/test/flow_file_change_detector_test.ts +++ b/src/test/flow_file_change_detector_test.ts @@ -16,7 +16,7 @@ import { assertEquals, assertThrows } from "@std/assert"; import { Configuration } from "../main/argument_processor.ts"; -import { getTestConfig } from "./test_utils.ts"; +import { getTestConfig } from "./utilities/mock_config.ts"; import { ERROR_MESSAGES, FLOW_FILE_EXTENSION, @@ -30,23 +30,21 @@ const FLOW_FILE_PATH = "file2" + FLOW_FILE_EXTENSION; * Test helper type that exposes private methods for mocking purposes. * Uses Omit to remove private methods and re-adds them as public. */ -type TestableFlowFileChangeDetector = - & Omit< - FlowFileChangeDetector, - | "executeVersionCommand" - | "executeRevParseCommand" - | "executeDiffCommand" - | "executeGetFileContentCommand" - > - & { - executeVersionCommand: () => void; - executeRevParseCommand: () => void; - executeDiffCommand: () => Uint8Array; - executeGetFileContentCommand: ( - filePath: string, - commitHash: string, - ) => Uint8Array; - }; +type TestableFlowFileChangeDetector = Omit< + FlowFileChangeDetector, + | "executeVersionCommand" + | "executeRevParseCommand" + | "executeDiffCommand" + | "executeGetFileContentCommand" +> & { + executeVersionCommand: () => void; + executeRevParseCommand: () => void; + executeDiffCommand: () => Uint8Array; + executeGetFileContentCommand: ( + filePath: string, + commitHash: string + ) => Uint8Array; +}; function createDetector(): TestableFlowFileChangeDetector { const detector = @@ -55,7 +53,7 @@ function createDetector(): TestableFlowFileChangeDetector { detector.executeRevParseCommand = () => undefined; detector.executeDiffCommand = () => new TextEncoder().encode( - ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL), + ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL) ); detector.executeGetFileContentCommand = () => new TextEncoder().encode("file content"); @@ -72,7 +70,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { const flowFiles = detector.getFlowFiles(); assertEquals(flowFiles, [FLOW_FILE_PATH]); - }, + } ); await t.step("should throw error if git is not installed", () => { @@ -84,7 +82,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.gitIsNotInstalledError, + ERROR_MESSAGES.gitIsNotInstalledError ); }); @@ -97,7 +95,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.notInGitRepoError, + ERROR_MESSAGES.notInGitRepoError ); }); @@ -110,7 +108,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.diffError(new Error("Diff error")), + ERROR_MESSAGES.diffError(new Error("Diff error")) ); }); @@ -139,8 +137,8 @@ Deno.test("FlowFileChangeDetector", async (t) => { Error, ERROR_MESSAGES.unableToGetFileContent( FLOW_FILE_PATH, - new Error("Get file content error"), - ), + new Error("Get file content error") + ) ); }); }); diff --git a/src/test/flow_to_uml_transformer_test.ts b/src/test/flow_to_uml_transformer_test.ts index bbabf5b..fcd7c55 100644 --- a/src/test/flow_to_uml_transformer_test.ts +++ b/src/test/flow_to_uml_transformer_test.ts @@ -21,7 +21,7 @@ import { assertStringIncludes, } from "@std/assert"; import { Configuration, DiagramTool } from "../main/argument_processor.ts"; -import { getTestConfig } from "./test_utils.ts"; +import { getTestConfig } from "./utilities/mock_config.ts"; import { FlowFileChangeDetector } from "../main/flow_file_change_detector.ts"; import { ERROR_MESSAGES, @@ -44,7 +44,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { // Mock the private method which executes git commands using spyOn const executeGitCommand = () => { return new TextEncoder().encode( - Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH), + Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH) ); }; // Replace the private method with our mock implementation, using type assertion to access it. @@ -58,14 +58,14 @@ Deno.test("FlowToUmlTransformer", async (t) => { transformer = new FlowToUmlTransformer( [SAMPLE_FLOW_FILE_PATH], GENERATOR_CONTEXT, - CHANGE_DETECTOR, + CHANGE_DETECTOR ); result = await transformer.transformToUmlDiagrams(); assert( result.has(SAMPLE_FLOW_FILE_PATH), - "result should have the sample file path as a key", + "result should have the sample file path as a key" ); const flowDifference = result.get(SAMPLE_FLOW_FILE_PATH); @@ -75,7 +75,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { assertStringIncludes( newUml, PLANT_UML_SIGNATURE, - "newUml should contain PLANT_UML_SIGNATURE", + "newUml should contain PLANT_UML_SIGNATURE" ); Configuration.getInstance = originalGetInstance; // Restore the original getInstance function @@ -100,7 +100,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { transformer = new FlowToUmlTransformer( [fakeFilePath], GENERATOR_CONTEXT, - CHANGE_DETECTOR, + CHANGE_DETECTOR ); result = await transformer.transformToUmlDiagrams(); @@ -110,16 +110,16 @@ Deno.test("FlowToUmlTransformer", async (t) => { const expectedErrorMessage = ERROR_MESSAGES.unableToProcessFile( fakeFilePath, - new Error(XML_READER_ERROR_MESSAGES.invalidFilePath(fakeFilePath)), + new Error(XML_READER_ERROR_MESSAGES.invalidFilePath(fakeFilePath)) ); assertEquals( consoleErrorCall, expectedErrorMessage, - "console.error call should have the expected message", + "console.error call should have the expected message" ); Configuration.getInstance = originalGetInstance; // Restore the original getInstance function console.error = originalConsoleError; // Restore the original console.error - }, + } ); }); diff --git a/src/test/test_utils.ts b/src/test/utilities/mock_config.ts similarity index 95% rename from src/test/test_utils.ts rename to src/test/utilities/mock_config.ts index 46df948..37ac57e 100644 --- a/src/test/test_utils.ts +++ b/src/test/utilities/mock_config.ts @@ -18,7 +18,7 @@ import { DiagramTool, Mode, RuntimeConfig, -} from "../main/argument_processor.ts"; +} from "../../main/argument_processor.ts"; /** * The test configuration object that is used by the tests. From cc062b90ca89cbcab41ad2774bd07f841959ac8f Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 16:15:35 -0600 Subject: [PATCH 7/9] Refactor EOL handling - Replace inline EOL definitions with imports from `constants.ts`. - Clean up formatting . --- src/main/constants.ts | 17 ++++ src/main/flow_file_change_detector.ts | 2 +- src/main/graphviz_generator.ts | 2 +- src/main/plantuml_generator.ts | 2 +- src/main/uml_generator.ts | 7 +- src/main/uml_writer.ts | 8 +- src/test/argument_processor_test.ts | 99 +++++++++++----------- src/test/flow_file_change_detector_test.ts | 48 ++++++----- src/test/flow_to_uml_transformer_test.ts | 16 ++-- src/test/graphviz_generator_test.ts | 3 +- src/test/uml_generator_test.ts | 2 +- src/test/uml_writer_test.ts | 11 +-- 12 files changed, 114 insertions(+), 103 deletions(-) create mode 100644 src/main/constants.ts diff --git a/src/main/constants.ts b/src/main/constants.ts new file mode 100644 index 0000000..4595a0a --- /dev/null +++ b/src/main/constants.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; diff --git a/src/main/flow_file_change_detector.ts b/src/main/flow_file_change_detector.ts index b139eb4..66df6d4 100644 --- a/src/main/flow_file_change_detector.ts +++ b/src/main/flow_file_change_detector.ts @@ -20,13 +20,13 @@ */ import { Configuration } from "./argument_processor.ts"; +import { EOL } from "./constants.ts"; const ADDED = "A"; const MODIFIED = "M"; const RENAMED = "R"; const COPIED = "C"; const SUPPORTED_DIFF_TYPES = [ADDED, MODIFIED, RENAMED, COPIED].join(""); -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; /** The extension of flow files. */ export const FLOW_FILE_EXTENSION = ".flow-meta.xml"; diff --git a/src/main/graphviz_generator.ts b/src/main/graphviz_generator.ts index 3601441..55405dc 100644 --- a/src/main/graphviz_generator.ts +++ b/src/main/graphviz_generator.ts @@ -20,6 +20,7 @@ import type { Transition } from "./flow_parser.ts"; import * as flowTypes from "./flow_types.ts"; +import { EOL } from "./constants.ts"; import { type DiagramNode, Icon as UmlIcon, @@ -28,7 +29,6 @@ import { UmlGenerator, } from "./uml_generator.ts"; -const EOL = "\n"; const TABLE_BEGIN = `< `; const TABLE_END = `
diff --git a/src/main/plantuml_generator.ts b/src/main/plantuml_generator.ts index 5f323f5..fa8dfae 100644 --- a/src/main/plantuml_generator.ts +++ b/src/main/plantuml_generator.ts @@ -25,7 +25,7 @@ import { Icon as UmlIcon, UmlGenerator, } from "./uml_generator.ts"; -const EOL = "\n"; +import { EOL } from "./constants.ts"; enum SkinColor { NONE = "", diff --git a/src/main/uml_generator.ts b/src/main/uml_generator.ts index d3ea1df..cdaa1f2 100644 --- a/src/main/uml_generator.ts +++ b/src/main/uml_generator.ts @@ -21,8 +21,7 @@ import { ParsedFlow, Transition } from "./flow_parser.ts"; import * as flowTypes from "./flow_types.ts"; - -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; +import { EOL } from "./constants.ts"; /** * The skin color of the node. @@ -215,7 +214,9 @@ export abstract class UmlGenerator { start.filters.forEach((filter, index) => { entryCriteria.push( `${index + 1}. ${filter.field} ${filter.operator} ${ - toString(filter.value) + toString( + filter.value, + ) }`, ); }); diff --git a/src/main/uml_writer.ts b/src/main/uml_writer.ts index 1553ae4..1dd2f31 100644 --- a/src/main/uml_writer.ts +++ b/src/main/uml_writer.ts @@ -22,8 +22,7 @@ import { join } from "@std/path"; import { Configuration, Mode, RuntimeConfig } from "./argument_processor.ts"; import { FlowDifference } from "./flow_to_uml_transformer.ts"; import { GithubClient } from "./github_client.ts"; - -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; +import { EOL } from "./constants.ts"; const FILE_EXTENSION = ".json"; const HIDDEN_COMMENT_PREFIX = ""; @@ -104,10 +103,7 @@ export class UmlWriter { for (const [filePath, flowDifference] of this.filePathToFlowDifference) { const flowApiName = this.extractFlowApiName(filePath); - const outputPath = join( - config.outputDirectory!, - `${flowApiName}.md`, - ); + const outputPath = join(config.outputDirectory!, `${flowApiName}.md`); let markdownContent = ""; const tripleBackticks = "```"; diff --git a/src/test/argument_processor_test.ts b/src/test/argument_processor_test.ts index 300bf0f..9e6aee9 100644 --- a/src/test/argument_processor_test.ts +++ b/src/test/argument_processor_test.ts @@ -31,7 +31,7 @@ const INVALID_OUTPUT_DIRECTORY = "invalid/directory/path"; const INVALID_MODE = "unsupported"; function setupTest( - configModifications: (config: RuntimeConfig) => void = () => {} + configModifications: (config: RuntimeConfig) => void = () => {}, ) { let testConfiguration = getTestConfig(); configModifications(testConfiguration); @@ -54,15 +54,16 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => - (config.diagramTool = INVALID_DIAGRAM_TOOL as DiagramTool) + ( + config, + ) => (config.diagramTool = INVALID_DIAGRAM_TOOL as DiagramTool), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.unsupportedDiagramTool(INVALID_DIAGRAM_TOOL) + ERROR_MESSAGES.unsupportedDiagramTool(INVALID_DIAGRAM_TOOL), ); - } + }, ); await t.step( @@ -71,14 +72,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.mode = INVALID_MODE as Mode) + (config) => (config.mode = INVALID_MODE as Mode), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.unsupportedMode(INVALID_MODE) + ERROR_MESSAGES.unsupportedMode(INVALID_MODE), ); - } + }, ); await t.step( @@ -94,7 +95,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); const result = argumentProcessor.getConfig(); assertEquals(result, config); - } + }, ); await t.step( @@ -108,7 +109,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); const result = argumentProcessor.getConfig(); assertEquals(result, config); - } + }, ); await t.step( @@ -124,9 +125,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.markdownRequiresMermaid + ERROR_MESSAGES.markdownRequiresMermaid, ); - } + }, ); await t.step("should reject markdown mode without outputDirectory", () => { @@ -140,7 +141,7 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputDirectoryRequired + ERROR_MESSAGES.outputDirectoryRequired, ); }); @@ -157,9 +158,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputDirectory("non/existent/directory") + ERROR_MESSAGES.invalidOutputDirectory("non/existent/directory"), ); - } + }, ); await t.step( @@ -174,9 +175,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputDirectoryRequired + ERROR_MESSAGES.outputDirectoryRequired, ); - } + }, ); await t.step( @@ -191,9 +192,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputFileNameRequired + ERROR_MESSAGES.outputFileNameRequired, ); - } + }, ); await t.step( @@ -209,9 +210,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathDoesNotExist(INVALID_FILE_PATH) + ERROR_MESSAGES.filePathDoesNotExist(INVALID_FILE_PATH), ); - } + }, ); await t.step( @@ -220,14 +221,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputFileName = "") + (config) => (config.outputFileName = ""), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputFileNameRequired + ERROR_MESSAGES.outputFileNameRequired, ); - } + }, ); await t.step( @@ -236,14 +237,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputFileName = INVALID_OUTPUT_FILE_NAME) + (config) => (config.outputFileName = INVALID_OUTPUT_FILE_NAME), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputFileName(INVALID_OUTPUT_FILE_NAME) + ERROR_MESSAGES.invalidOutputFileName(INVALID_OUTPUT_FILE_NAME), ); - } + }, ); await t.step( @@ -252,14 +253,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputDirectory = INVALID_OUTPUT_DIRECTORY) + (config) => (config.outputDirectory = INVALID_OUTPUT_DIRECTORY), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.invalidOutputDirectory(INVALID_OUTPUT_DIRECTORY) + ERROR_MESSAGES.invalidOutputDirectory(INVALID_OUTPUT_DIRECTORY), ); - } + }, ); await t.step( @@ -268,14 +269,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.outputDirectory = "") + (config) => (config.outputDirectory = ""), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.outputDirectoryRequired + ERROR_MESSAGES.outputDirectoryRequired, ); - } + }, ); await t.step( @@ -290,9 +291,9 @@ Deno.test("ArgumentProcessor", async (t) => { argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathOrGitDiffFromAndToHashRequired + ERROR_MESSAGES.filePathOrGitDiffFromAndToHashRequired, ); - } + }, ); await t.step( @@ -301,14 +302,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.filePath = [INVALID_FILE_PATH]) + (config) => (config.filePath = [INVALID_FILE_PATH]), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.filePathAndGitDiffFromAndToHashMutuallyExclusive + ERROR_MESSAGES.filePathAndGitDiffFromAndToHashMutuallyExclusive, ); - } + }, ); await t.step( @@ -317,14 +318,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.gitDiffToHash = undefined) + (config) => (config.gitDiffToHash = undefined), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether + ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether, ); - } + }, ); await t.step( @@ -333,14 +334,14 @@ Deno.test("ArgumentProcessor", async (t) => { assertThrows( () => { const { argumentProcessor } = setupTest( - (config) => (config.gitDiffFromHash = undefined) + (config) => (config.gitDiffFromHash = undefined), ); argumentProcessor.getConfig(); }, Error, - ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether + ERROR_MESSAGES.gitDiffFromAndToHashMustBeSpecifiedTogether, ); - } + }, ); await t.step( @@ -354,7 +355,7 @@ Deno.test("ArgumentProcessor", async (t) => { }); validConfig.argumentProcessor.getConfig(); assertEquals(validConfig.argumentProcessor.getErrors(), []); - } + }, ); await t.step( @@ -372,7 +373,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidDiagramTool.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresMermaid, ]); - } + }, ); await t.step( @@ -390,7 +391,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidToHash.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresHeadHash, ]); - } + }, ); await t.step( @@ -408,7 +409,7 @@ Deno.test("ArgumentProcessor", async (t) => { assertEquals(invalidFromHash.argumentProcessor.getErrors(), [ ERROR_MESSAGES.githubActionRequiresHeadMinusOne, ]); - } + }, ); await t.step( @@ -428,6 +429,6 @@ Deno.test("ArgumentProcessor", async (t) => { ERROR_MESSAGES.githubActionRequiresHeadHash, ERROR_MESSAGES.githubActionRequiresHeadMinusOne, ]); - } + }, ); }); diff --git a/src/test/flow_file_change_detector_test.ts b/src/test/flow_file_change_detector_test.ts index 8d695c9..1c6957d 100644 --- a/src/test/flow_file_change_detector_test.ts +++ b/src/test/flow_file_change_detector_test.ts @@ -22,29 +22,31 @@ import { FLOW_FILE_EXTENSION, FlowFileChangeDetector, } from "../main/flow_file_change_detector.ts"; +import { EOL } from "../main/constants.ts"; -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; const FLOW_FILE_PATH = "file2" + FLOW_FILE_EXTENSION; /** * Test helper type that exposes private methods for mocking purposes. * Uses Omit to remove private methods and re-adds them as public. */ -type TestableFlowFileChangeDetector = Omit< - FlowFileChangeDetector, - | "executeVersionCommand" - | "executeRevParseCommand" - | "executeDiffCommand" - | "executeGetFileContentCommand" -> & { - executeVersionCommand: () => void; - executeRevParseCommand: () => void; - executeDiffCommand: () => Uint8Array; - executeGetFileContentCommand: ( - filePath: string, - commitHash: string - ) => Uint8Array; -}; +type TestableFlowFileChangeDetector = + & Omit< + FlowFileChangeDetector, + | "executeVersionCommand" + | "executeRevParseCommand" + | "executeDiffCommand" + | "executeGetFileContentCommand" + > + & { + executeVersionCommand: () => void; + executeRevParseCommand: () => void; + executeDiffCommand: () => Uint8Array; + executeGetFileContentCommand: ( + filePath: string, + commitHash: string, + ) => Uint8Array; + }; function createDetector(): TestableFlowFileChangeDetector { const detector = @@ -53,7 +55,7 @@ function createDetector(): TestableFlowFileChangeDetector { detector.executeRevParseCommand = () => undefined; detector.executeDiffCommand = () => new TextEncoder().encode( - ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL) + ["file1.txt", FLOW_FILE_PATH, "file3.js"].join(EOL), ); detector.executeGetFileContentCommand = () => new TextEncoder().encode("file content"); @@ -70,7 +72,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { const flowFiles = detector.getFlowFiles(); assertEquals(flowFiles, [FLOW_FILE_PATH]); - } + }, ); await t.step("should throw error if git is not installed", () => { @@ -82,7 +84,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.gitIsNotInstalledError + ERROR_MESSAGES.gitIsNotInstalledError, ); }); @@ -95,7 +97,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.notInGitRepoError + ERROR_MESSAGES.notInGitRepoError, ); }); @@ -108,7 +110,7 @@ Deno.test("FlowFileChangeDetector", async (t) => { assertThrows( () => detector.getFlowFiles(), Error, - ERROR_MESSAGES.diffError(new Error("Diff error")) + ERROR_MESSAGES.diffError(new Error("Diff error")), ); }); @@ -137,8 +139,8 @@ Deno.test("FlowFileChangeDetector", async (t) => { Error, ERROR_MESSAGES.unableToGetFileContent( FLOW_FILE_PATH, - new Error("Get file content error") - ) + new Error("Get file content error"), + ), ); }); }); diff --git a/src/test/flow_to_uml_transformer_test.ts b/src/test/flow_to_uml_transformer_test.ts index fcd7c55..4ca1c02 100644 --- a/src/test/flow_to_uml_transformer_test.ts +++ b/src/test/flow_to_uml_transformer_test.ts @@ -44,7 +44,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { // Mock the private method which executes git commands using spyOn const executeGitCommand = () => { return new TextEncoder().encode( - Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH) + Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH), ); }; // Replace the private method with our mock implementation, using type assertion to access it. @@ -58,14 +58,14 @@ Deno.test("FlowToUmlTransformer", async (t) => { transformer = new FlowToUmlTransformer( [SAMPLE_FLOW_FILE_PATH], GENERATOR_CONTEXT, - CHANGE_DETECTOR + CHANGE_DETECTOR, ); result = await transformer.transformToUmlDiagrams(); assert( result.has(SAMPLE_FLOW_FILE_PATH), - "result should have the sample file path as a key" + "result should have the sample file path as a key", ); const flowDifference = result.get(SAMPLE_FLOW_FILE_PATH); @@ -75,7 +75,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { assertStringIncludes( newUml, PLANT_UML_SIGNATURE, - "newUml should contain PLANT_UML_SIGNATURE" + "newUml should contain PLANT_UML_SIGNATURE", ); Configuration.getInstance = originalGetInstance; // Restore the original getInstance function @@ -100,7 +100,7 @@ Deno.test("FlowToUmlTransformer", async (t) => { transformer = new FlowToUmlTransformer( [fakeFilePath], GENERATOR_CONTEXT, - CHANGE_DETECTOR + CHANGE_DETECTOR, ); result = await transformer.transformToUmlDiagrams(); @@ -110,16 +110,16 @@ Deno.test("FlowToUmlTransformer", async (t) => { const expectedErrorMessage = ERROR_MESSAGES.unableToProcessFile( fakeFilePath, - new Error(XML_READER_ERROR_MESSAGES.invalidFilePath(fakeFilePath)) + new Error(XML_READER_ERROR_MESSAGES.invalidFilePath(fakeFilePath)), ); assertEquals( consoleErrorCall, expectedErrorMessage, - "console.error call should have the expected message" + "console.error call should have the expected message", ); Configuration.getInstance = originalGetInstance; // Restore the original getInstance function console.error = originalConsoleError; // Restore the original console.error - } + }, ); }); diff --git a/src/test/graphviz_generator_test.ts b/src/test/graphviz_generator_test.ts index 082b83d..1a559c8 100644 --- a/src/test/graphviz_generator_test.ts +++ b/src/test/graphviz_generator_test.ts @@ -29,8 +29,7 @@ import { SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; import { generateMockFlow } from "./utilities/mock_flow.ts"; - -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; +import { EOL } from "../main/constants.ts"; function generateTable( nodeName: string, diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index 69b28e5..3a11c44 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -19,8 +19,8 @@ import { ParsedFlow, Transition } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { DiagramNode, UmlGenerator } from "../main/uml_generator.ts"; import { generateMockFlow, NODE_NAMES } from "./utilities/mock_flow.ts"; +import { EOL } from "../main/constants.ts"; -const EOL = Deno.build.os === "windows" ? "\r\n" : "\n"; const TRANSITION_ARROW = "-->"; const UML_REPRESENTATIONS = { diff --git a/src/test/uml_writer_test.ts b/src/test/uml_writer_test.ts index 5efaf78..2d127ba 100644 --- a/src/test/uml_writer_test.ts +++ b/src/test/uml_writer_test.ts @@ -27,6 +27,7 @@ import { import { FlowDifference } from "../main/flow_to_uml_transformer.ts"; import { UmlWriter } from "../main/uml_writer.ts"; import { GithubClient, GithubComment } from "../main/github_client.ts"; +import { EOL } from "../main/constants.ts"; const TEST_UNDECLARED_OUTPUTS_DIR = "./"; @@ -195,21 +196,15 @@ Deno.test("UmlWriter", async (t) => { assertExists(existsSync(expectedFile1Path)); assertExists(existsSync(expectedFile2Path)); - // Use OS-appropriate newlines for test expectations - const eol = Deno.build.os === "windows" ? "\r\n" : "\n"; - // Check the content of the first file (no old version) fileContent = Deno.readTextFileSync(expectedFile1Path); - assertEquals( - fileContent, - `\`\`\`mermaid${eol}uml1${eol}\`\`\`${eol}`, - ); + assertEquals(fileContent, `\`\`\`mermaid${EOL}uml1${EOL}\`\`\`${EOL}`); // Check the content of the second file (with old version) fileContent = Deno.readTextFileSync(expectedFile2Path); assertEquals( fileContent, - `## Old Version${eol}${eol}\`\`\`mermaid${eol}uml1${eol}\`\`\`${eol}${eol}## New Version${eol}${eol}\`\`\`mermaid${eol}uml2${eol}\`\`\`${eol}`, + `## Old Version${EOL}${EOL}\`\`\`mermaid${EOL}uml1${EOL}\`\`\`${EOL}${EOL}## New Version${EOL}${EOL}\`\`\`mermaid${EOL}uml2${EOL}\`\`\`${EOL}`, ); // Clean up From 81887c71c63e09c8a891ba176df5f635625970a1 Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 19:38:47 -0600 Subject: [PATCH 8/9] Refactor type imports across multiple files - Change regular imports to type imports for better clarity --- .gitignore | 43 ++++++++++++++++++++++++ src/main/flow_comparator.ts | 2 +- src/main/flow_parser.ts | 2 +- src/main/flow_to_uml_transformer.ts | 6 ++-- src/main/main.ts | 2 +- src/main/uml_generator.ts | 2 +- src/main/uml_generator_context.ts | 4 +-- src/main/uml_writer.ts | 4 +-- src/test/argument_processor_test.ts | 2 +- src/test/flow_comparator_test.ts | 2 +- src/test/flow_parser_test.ts | 4 +-- src/test/flow_to_uml_transformer_test.ts | 2 +- src/test/github_client_test.ts | 2 +- src/test/graphviz_generator_test.ts | 4 +-- src/test/main_test.ts | 2 +- src/test/mermaid_generator_test.ts | 4 +-- src/test/uml_generator_context_test.ts | 2 +- src/test/uml_generator_test.ts | 4 +-- src/test/uml_writer_test.ts | 6 ++-- src/test/utilities/mock_config.ts | 2 +- src/test/utilities/mock_flow.ts | 2 +- 21 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b889b87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# IDE / Editor +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment variables +.env +.env.local +.env.*.local + +# Build outputs +dist/ +build/ +*.tsbuildinfo + +# Test coverage +coverage/ +.nyc_output/ + +# Temporary files +*.tmp +*.temp +.cache/ + diff --git a/src/main/flow_comparator.ts b/src/main/flow_comparator.ts index f025251..455f3d8 100644 --- a/src/main/flow_comparator.ts +++ b/src/main/flow_comparator.ts @@ -18,7 +18,7 @@ * @fileoverview Utility functions to compare two flows. */ -import { ParsedFlow } from "./flow_parser.ts"; +import type { ParsedFlow } from "./flow_parser.ts"; import * as flowTypes from "./flow_types.ts"; const OBJECT = "object"; diff --git a/src/main/flow_parser.ts b/src/main/flow_parser.ts index c173b94..d84d3b2 100644 --- a/src/main/flow_parser.ts +++ b/src/main/flow_parser.ts @@ -20,7 +20,7 @@ */ import { Parser } from "xml2js"; -import * as flowTypes from "./flow_types.ts"; +import type * as flowTypes from "./flow_types.ts"; const FAULT = "Fault"; const END = "End"; diff --git a/src/main/flow_to_uml_transformer.ts b/src/main/flow_to_uml_transformer.ts index 0b6b41f..6a85b18 100644 --- a/src/main/flow_to_uml_transformer.ts +++ b/src/main/flow_to_uml_transformer.ts @@ -22,9 +22,9 @@ import { join } from "@std/path"; import { Configuration } from "./argument_processor.ts"; import { compareFlows } from "./flow_comparator.ts"; -import { FlowFileChangeDetector } from "./flow_file_change_detector.ts"; -import { FlowParser, ParsedFlow } from "./flow_parser.ts"; -import { UmlGeneratorContext } from "./uml_generator_context.ts"; +import type { FlowFileChangeDetector } from "./flow_file_change_detector.ts"; +import { FlowParser, type ParsedFlow } from "./flow_parser.ts"; +import type { UmlGeneratorContext } from "./uml_generator_context.ts"; import { XmlReader } from "./xml_reader.ts"; /** diff --git a/src/main/main.ts b/src/main/main.ts index ed57599..c255cbe 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -21,7 +21,7 @@ import { Configuration } from "./argument_processor.ts"; import { FlowFileChangeDetector } from "./flow_file_change_detector.ts"; import { - FlowDifference, + type FlowDifference, FlowToUmlTransformer, } from "./flow_to_uml_transformer.ts"; import { UmlGeneratorContext } from "./uml_generator_context.ts"; diff --git a/src/main/uml_generator.ts b/src/main/uml_generator.ts index cdaa1f2..2f153d0 100644 --- a/src/main/uml_generator.ts +++ b/src/main/uml_generator.ts @@ -19,7 +19,7 @@ * generate a UML string representation of a Salesforce flow. */ -import { ParsedFlow, Transition } from "./flow_parser.ts"; +import type { ParsedFlow, Transition } from "./flow_parser.ts"; import * as flowTypes from "./flow_types.ts"; import { EOL } from "./constants.ts"; diff --git a/src/main/uml_generator_context.ts b/src/main/uml_generator_context.ts index a78cd61..cbd05bf 100644 --- a/src/main/uml_generator_context.ts +++ b/src/main/uml_generator_context.ts @@ -22,11 +22,11 @@ */ import { DiagramTool } from "./argument_processor.ts"; -import { ParsedFlow } from "./flow_parser.ts"; +import type { ParsedFlow } from "./flow_parser.ts"; import { GraphVizGenerator } from "./graphviz_generator.ts"; import { MermaidGenerator } from "./mermaid_generator.ts"; import { PlantUmlGenerator } from "./plantuml_generator.ts"; -import { UmlGenerator } from "./uml_generator.ts"; +import type { UmlGenerator } from "./uml_generator.ts"; /** * This class is responsible for generating a UML diagram representation of a diff --git a/src/main/uml_writer.ts b/src/main/uml_writer.ts index 1dd2f31..654d2a4 100644 --- a/src/main/uml_writer.ts +++ b/src/main/uml_writer.ts @@ -19,8 +19,8 @@ * the generated UML diagrams to a file. */ import { join } from "@std/path"; -import { Configuration, Mode, RuntimeConfig } from "./argument_processor.ts"; -import { FlowDifference } from "./flow_to_uml_transformer.ts"; +import { Configuration, Mode, type RuntimeConfig } from "./argument_processor.ts"; +import type { FlowDifference } from "./flow_to_uml_transformer.ts"; import { GithubClient } from "./github_client.ts"; import { EOL } from "./constants.ts"; diff --git a/src/test/argument_processor_test.ts b/src/test/argument_processor_test.ts index 9e6aee9..262c9dc 100644 --- a/src/test/argument_processor_test.ts +++ b/src/test/argument_processor_test.ts @@ -19,7 +19,7 @@ import { DiagramTool, ERROR_MESSAGES, Mode, - RuntimeConfig, + type RuntimeConfig, } from "../main/argument_processor.ts"; import { assertEquals, assertThrows } from "@std/assert"; import { getTestConfig } from "./utilities/mock_config.ts"; diff --git a/src/test/flow_comparator_test.ts b/src/test/flow_comparator_test.ts index bd2e356..00e355d 100644 --- a/src/test/flow_comparator_test.ts +++ b/src/test/flow_comparator_test.ts @@ -17,7 +17,7 @@ import { assertEquals } from "@std/assert"; import { compareFlows } from "../main/flow_comparator.ts"; -import { ParsedFlow } from "../main/flow_parser.ts"; +import type { ParsedFlow } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; const NODE = { diff --git a/src/test/flow_parser_test.ts b/src/test/flow_parser_test.ts index 75e006d..2091dfb 100644 --- a/src/test/flow_parser_test.ts +++ b/src/test/flow_parser_test.ts @@ -15,8 +15,8 @@ */ import { join } from "@std/path"; -import { ERROR_MESSAGES, FlowParser, ParsedFlow } from "../main/flow_parser.ts"; -import * as flowTypes from "../main/flow_types.ts"; +import { ERROR_MESSAGES, FlowParser, type ParsedFlow } from "../main/flow_parser.ts"; +import type * as flowTypes from "../main/flow_types.ts"; import { assert, assertEquals, assertRejects } from "@std/assert"; const GOLDENS_PATH = "./src/test/goldens"; diff --git a/src/test/flow_to_uml_transformer_test.ts b/src/test/flow_to_uml_transformer_test.ts index 4ca1c02..fc9f799 100644 --- a/src/test/flow_to_uml_transformer_test.ts +++ b/src/test/flow_to_uml_transformer_test.ts @@ -25,7 +25,7 @@ import { getTestConfig } from "./utilities/mock_config.ts"; import { FlowFileChangeDetector } from "../main/flow_file_change_detector.ts"; import { ERROR_MESSAGES, - FlowDifference, + type FlowDifference, FlowToUmlTransformer, } from "../main/flow_to_uml_transformer.ts"; import { UmlGeneratorContext } from "../main/uml_generator_context.ts"; diff --git a/src/test/github_client_test.ts b/src/test/github_client_test.ts index 6f3ea9c..60dfbc9 100644 --- a/src/test/github_client_test.ts +++ b/src/test/github_client_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { GithubClient, GithubComment } from "../main/github_client.ts"; +import { GithubClient, type GithubComment } from "../main/github_client.ts"; import { assertEquals, assertRejects } from "@std/assert"; import { ERROR_MESSAGES } from "../main/github_client.ts"; diff --git a/src/test/graphviz_generator_test.ts b/src/test/graphviz_generator_test.ts index 1a559c8..7e42b9e 100644 --- a/src/test/graphviz_generator_test.ts +++ b/src/test/graphviz_generator_test.ts @@ -15,7 +15,7 @@ */ import { assertEquals, assertStringIncludes } from "@std/assert"; -import { ParsedFlow } from "../main/flow_parser.ts"; +import type { ParsedFlow } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { FontColor, @@ -24,7 +24,7 @@ import { SkinColor, } from "../main/graphviz_generator.ts"; import { - DiagramNode, + type DiagramNode, Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; diff --git a/src/test/main_test.ts b/src/test/main_test.ts index f3def6a..c9d5416 100644 --- a/src/test/main_test.ts +++ b/src/test/main_test.ts @@ -20,7 +20,7 @@ import { Configuration, DiagramTool, Mode, - RuntimeConfig, + type RuntimeConfig, } from "../main/argument_processor.ts"; import { Runner } from "../main/main.ts"; diff --git a/src/test/mermaid_generator_test.ts b/src/test/mermaid_generator_test.ts index cdec89f..bde191e 100644 --- a/src/test/mermaid_generator_test.ts +++ b/src/test/mermaid_generator_test.ts @@ -15,11 +15,11 @@ */ import { assertEquals, assertStringIncludes } from "@std/assert"; -import { ParsedFlow, Transition } from "../main/flow_parser.ts"; +import type { ParsedFlow, Transition } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; import { MermaidGenerator } from "../main/mermaid_generator.ts"; import { - DiagramNode, + type DiagramNode, Icon as UmlIcon, SkinColor as UmlSkinColor, } from "../main/uml_generator.ts"; diff --git a/src/test/uml_generator_context_test.ts b/src/test/uml_generator_context_test.ts index b225ea5..a311340 100644 --- a/src/test/uml_generator_context_test.ts +++ b/src/test/uml_generator_context_test.ts @@ -16,7 +16,7 @@ import { assertStringIncludes } from "@std/assert"; import { DiagramTool } from "../main/argument_processor.ts"; -import { ParsedFlow } from "../main/flow_parser.ts"; +import type { ParsedFlow } from "../main/flow_parser.ts"; import { UmlGeneratorContext } from "../main/uml_generator_context.ts"; const PLANT_UML_SIGNATURE = "skinparam State"; diff --git a/src/test/uml_generator_test.ts b/src/test/uml_generator_test.ts index 3a11c44..f6b5dd1 100644 --- a/src/test/uml_generator_test.ts +++ b/src/test/uml_generator_test.ts @@ -15,9 +15,9 @@ */ import { assertEquals } from "@std/assert"; -import { ParsedFlow, Transition } from "../main/flow_parser.ts"; +import type { ParsedFlow, Transition } from "../main/flow_parser.ts"; import * as flowTypes from "../main/flow_types.ts"; -import { DiagramNode, UmlGenerator } from "../main/uml_generator.ts"; +import { type DiagramNode, UmlGenerator } from "../main/uml_generator.ts"; import { generateMockFlow, NODE_NAMES } from "./utilities/mock_flow.ts"; import { EOL } from "../main/constants.ts"; diff --git a/src/test/uml_writer_test.ts b/src/test/uml_writer_test.ts index 2d127ba..15f476e 100644 --- a/src/test/uml_writer_test.ts +++ b/src/test/uml_writer_test.ts @@ -22,11 +22,11 @@ import { Configuration, DiagramTool, Mode, - RuntimeConfig, + type RuntimeConfig, } from "../main/argument_processor.ts"; -import { FlowDifference } from "../main/flow_to_uml_transformer.ts"; +import type { FlowDifference } from "../main/flow_to_uml_transformer.ts"; import { UmlWriter } from "../main/uml_writer.ts"; -import { GithubClient, GithubComment } from "../main/github_client.ts"; +import type { GithubClient, GithubComment } from "../main/github_client.ts"; import { EOL } from "../main/constants.ts"; const TEST_UNDECLARED_OUTPUTS_DIR = "./"; diff --git a/src/test/utilities/mock_config.ts b/src/test/utilities/mock_config.ts index 37ac57e..4220ce7 100644 --- a/src/test/utilities/mock_config.ts +++ b/src/test/utilities/mock_config.ts @@ -17,7 +17,7 @@ import { DiagramTool, Mode, - RuntimeConfig, + type RuntimeConfig, } from "../../main/argument_processor.ts"; /** diff --git a/src/test/utilities/mock_flow.ts b/src/test/utilities/mock_flow.ts index 118d4e3..6e2e62b 100644 --- a/src/test/utilities/mock_flow.ts +++ b/src/test/utilities/mock_flow.ts @@ -15,7 +15,7 @@ */ import * as flowTypes from "../../main/flow_types.ts"; -import { ParsedFlow } from "../../main/flow_parser.ts"; +import type { ParsedFlow } from "../../main/flow_parser.ts"; export const NODE_NAMES = { label: "test", From dfa4ecf3ecad818ab12e9cb734a43212f1641a9e Mon Sep 17 00:00:00 2001 From: Mitch Spano Date: Sun, 4 Jan 2026 19:42:59 -0600 Subject: [PATCH 9/9] Fix formatting issues --- src/main/uml_writer.ts | 6 +++++- src/test/flow_parser_test.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/uml_writer.ts b/src/main/uml_writer.ts index 654d2a4..6d79c20 100644 --- a/src/main/uml_writer.ts +++ b/src/main/uml_writer.ts @@ -19,7 +19,11 @@ * the generated UML diagrams to a file. */ import { join } from "@std/path"; -import { Configuration, Mode, type RuntimeConfig } from "./argument_processor.ts"; +import { + Configuration, + Mode, + type RuntimeConfig, +} from "./argument_processor.ts"; import type { FlowDifference } from "./flow_to_uml_transformer.ts"; import { GithubClient } from "./github_client.ts"; import { EOL } from "./constants.ts"; diff --git a/src/test/flow_parser_test.ts b/src/test/flow_parser_test.ts index 2091dfb..e91e1fc 100644 --- a/src/test/flow_parser_test.ts +++ b/src/test/flow_parser_test.ts @@ -15,7 +15,11 @@ */ import { join } from "@std/path"; -import { ERROR_MESSAGES, FlowParser, type ParsedFlow } from "../main/flow_parser.ts"; +import { + ERROR_MESSAGES, + FlowParser, + type ParsedFlow, +} from "../main/flow_parser.ts"; import type * as flowTypes from "../main/flow_types.ts"; import { assert, assertEquals, assertRejects } from "@std/assert";