Untitled

 avatar
unknown
plain_text
21 days ago
47 kB
5
Indexable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace QADocumentationGenerator
{
    class Program
    {
        private static string? _itosProjectPath;
        private static Dictionary<string, UIForm> _formsMap = new();
        private static List<QATestCase> _testCases = new();
        private static Dictionary<string, string> _menuItemTexts = new();
        private static Dictionary<string, List<string>> _formChildMap = new(StringComparer.OrdinalIgnoreCase);

        private static readonly List<string> _screenFlowOrder = new()
        {
            // Vessel Operations
            "frmVessel", "frmVesselJourney", "frmVesselStructure", "frmVesselStructureHeader",
            "frmVesselStructureBay", "frmVesselStructureSlot", "frmBLInfo", "frmBLHeader",
            "frmBLInfoAudit", "frmVesselTally", "frmCargoTallyList", "frmCraneSequence",
            "frmGangHeader", "frmGangDetail", "frmGangInfo", "frmHatch",
            "frmDischargeCargoOperations", "FrmDischargeCargo", "FrmDischargeCargoList",
            "frmLoadingPlan", "frmLoadingSummary", "frmBayPlanner",
            "frmVesselJourneyDynamicForms", "frmVesselJourneyDynamicFormsPermits",
            "frmVesselDischargePlanSummary", "frmExcelUpload", "frmF89Manifest",
            "frmEDIFileUpload", "frmBerthPlanner", "frmVesselAISUnlinkedData",
            "frmRailCallAndMovementInfo", "frmCFAModal", "frmWasteCargoDeclaration",
            "frmISPSSecurityLevel", "frmLiquidFlowInfo", "frmLiqBulkUpdate",
            "frmPumpSequence", "frmEventAndDelays", "frmVesselPermitManager", "frmSendBAPLIE",
            // Land Side Operations
            "frmOTS", "frmOTSHeader", "frmOTSDetail", "frmOTSQuickAdd",
            "frmMVInspection", "frmMVInspectionDetails", "frmInspectionHistory",
            "frmTruckTally", "frmTruckDischargedCargo", "frmTruckOperationSummary",
            "frmBargeTally", "frmStuffingTally",
            // Gate Operations
            "frmGateIn", "frmGateOut", "frmGateCall", "frmMVOD", "frmMVODDetails",
            "frmBargeIn", "frmBargeCall", "frmBargeOut", "frmActivateGateCallAndCargo",
            "frmRailCallInfo", "frmRailCallOperation", "frmRailGatein", "frmRailMovement",
            "frmRailCall", "frmTruckGateCallList", "frmQuickGate", "frmSealAndVGM",
            // Invoicing
            "frmVesselInvoicing", "frmLandSideInvoice", "frmInvoiceManagement",
            "frmInvoiceHeader", "frmInvoiceDetail", "frmInvoiceAndPayment",
            "frmBuildInvoice", "frmCreditNote", "frmPaymentReceipt", "frmRoyaltyInvoicing",
            "frmMapPayments", "frmExtraStorage", "frmVATInfo",
            // Rail Operation
            "FrmEngineMaster", "FrmTrackMaster", "FrmWagonType", "frmWagonMaster",
            "frmWagonCall", "frmWagonCallInfo", "frmRailRouteMaster", "frmRailRouteDetails",
            "frmRailRouteInfo",
            // Yard Operations
            "frmNewPosition", "frmUnStuffingOperations", "frmAreaPlanner", "frmAddAreaPlan",
            "frmAreaPlanDetails", "frmAreaTransferRequest", "frmBerthPlannerRestriction",
            "frmBerthPlannerDuration", "frmBerthRestrictaionDetail", "frmAddBerthRestriction",
            "frmEquipmentInstructions", "frmYardPositions", "frmActiveTrucks",
            "frmVesselCallYardOccupation", "frmVesselCallYardMoveTo", "frmStackRegistration",
            "frmContainerScan", "frmPendingPositions", "frmHouseKeeping",
            "frmHouseKeepingMove", "frmHouseKeepingQueue",
            "frmAssignTruckForStuffingUnStuffing", "frmAssignTruckForLoadDischarge",
            "frmAssignNoOfTrucks", "frmAssignVesselcall", "frmCargo", "frmAddContainer",
            "frmIMDGCargo", "frmOPSDetail", "frmPlanDetail", "frmPlanningLog", "frmWorkQueue",
            // Cargo History
            "frmCargoHistory",
            // Reports
            "frmReports", "frmCorporateKPI", "frmMonthwiseReport", "frmRevenueCollection",
            "frmLBPatchReport", "frmPatchReport",
            // iTOS Config
            "frmEquipmentInfo", "frmEquipment", "frmEquipmentType",
            "frmVesselMovementServiceSetup", "frmVesselMovement", "frmVesselMovementArea",
            "frmVesselMovementInfo", "frmVesselMovementRemark", "frmVesselMovementResources",
            "frmVesselMovementServices", "frmArea", "frmRateConfigInfo", "frmRateConfig",
            "frmRateConfigHeader", "frmRateConfigDetail", "frmRateConfigHeaderLookUp",
            "frmContainerISOConfig", "frmTariffLinks", "frmGangTemplate",
            "FrmTemplateGangHeader", "frmTemplateGangDetail", "frmIncidentTypeManagement",
            "frmIncidentType", "frmIncidentInfo", "frmIncident", "frmIncidentEvent",
            "frmIncidentProcedureTask", "frmIncidentVesselCall", "frmIPPath",
            "frmIPPathDetails", "frmVesselPath",
            // Admin
            "FrmOrganization", "FrmEmployee", "FrmEmployeeDetails", "frmEmployeeLookUp",
            "FrmGroups", "FrmGroupRights", "FrmRights", "FrmUserRights", "frmConfig",
            "FrmLookupMaster", "frmExcelUploadStatus", "frmLoginAudit", "FrmAuditLog",
            "frmTerminal", "frmTeSWSMessages", "frnReleaseUpload", "frmITOSQueue",
            "frmRebindLODtoREC", "frmMissingPortServices", "frmAddRoyaltyInvoice",
        };

        private static readonly Dictionary<string, string> _formToMainMenu =
            new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            // Vessel Operations
            { "frmVessel", "Vessel Operations" }, { "frmVesselJourney", "Vessel Operations" },
            { "frmVesselTracking2D", "Vessel Operations" }, { "frmVesselStructure", "Vessel Operations" },
            { "frmVesselStructureHeader", "Vessel Operations" }, { "frmVesselStructureBay", "Vessel Operations" },
            { "frmVesselStructureSlot", "Vessel Operations" }, { "frmBLInfo", "Vessel Operations" },
            { "frmBLHeader", "Vessel Operations" }, { "frmBLInfoAudit", "Vessel Operations" },
            { "frmVesselTally", "Vessel Operations" }, { "frmCargoTallyList", "Vessel Operations" },
            { "frmExcelUpload", "Vessel Operations" }, { "frmF89Manifest", "Vessel Operations" },
            { "frmEDIFileUpload", "Vessel Operations" }, { "frmBerthPlanner", "Vessel Operations" },
            { "frmVesselAISUnlinkedData", "Vessel Operations" }, { "frmRailCallAndMovementInfo", "Vessel Operations" },
            { "frmCraneSequence", "Vessel Operations" }, { "frmGangHeader", "Vessel Operations" },
            { "frmGangDetail", "Vessel Operations" }, { "frmGangInfo", "Vessel Operations" },
            { "frmHatch", "Vessel Operations" }, { "frmDischargeCargoOperations", "Vessel Operations" },
            { "FrmDischargeCargo", "Vessel Operations" }, { "FrmDischargeCargoList", "Vessel Operations" },
            { "frmLoadingPlan", "Vessel Operations" }, { "frmLoadingSummary", "Vessel Operations" },
            { "frmBayPlanner", "Vessel Operations" }, { "frmVesselJourneyDynamicForms", "Vessel Operations" },
            { "frmVesselJourneyDynamicFormsPermits", "Vessel Operations" },
            { "frmVesselDischargePlanSummary", "Vessel Operations" }, { "frmSendBAPLIE", "Vessel Operations" },
            { "frmCFAModal", "Vessel Operations" }, { "frmWasteCargoDeclaration", "Vessel Operations" },
            { "frmISPSSecurityLevel", "Vessel Operations" }, { "frmLiquidFlowInfo", "Vessel Operations" },
            { "frmLiqBulkUpdate", "Vessel Operations" }, { "frmPumpSequence", "Vessel Operations" },
            { "frmEventAndDelays", "Vessel Operations" }, { "frmVesselPermitManager", "Vessel Operations" },
            { "frmVesselCall", "Vessel Operations" }, { "frmVesselCallLookUp", "Vessel Operations" },
            { "frmActiveVesselCallLookUp", "Vessel Operations" }, { "frmAttachments", "Vessel Operations" },
            { "frmLinkBayPlan", "Vessel Operations" }, { "frmViewBayStructure", "Vessel Operations" },
            // Land Side Operations
            { "frmOTS", "Land Side Operations" }, { "frmOTSDetail", "Land Side Operations" },
            { "frmOTSHeader", "Land Side Operations" }, { "frmOTSQuickAdd", "Land Side Operations" },
            { "frmMVInspection", "Land Side Operations" }, { "frmMVInspectionDetails", "Land Side Operations" },
            { "frmInspectionHistory", "Land Side Operations" }, { "frmTruckTally", "Land Side Operations" },
            { "frmBargeTally", "Land Side Operations" }, { "frmTruckDischargedCargo", "Land Side Operations" },
            { "frmTruckOperationSummary", "Land Side Operations" }, { "frmStuffingTally", "Land Side Operations" },
            // Gate Operations
            { "frmGateIn", "Gate Operations" }, { "frmGateOut", "Gate Operations" },
            { "frmGateCall", "Gate Operations" }, { "frmMVOD", "Gate Operations" },
            { "frmMVODDetails", "Gate Operations" }, { "frmBargeIn", "Gate Operations" },
            { "frmBargeOut", "Gate Operations" }, { "frmBargeCall", "Gate Operations" },
            { "frmActivateGateCallAndCargo", "Gate Operations" }, { "frmRailCallInfo", "Gate Operations" },
            { "frmRailCallOperation", "Gate Operations" }, { "frmRailGatein", "Gate Operations" },
            { "frmRailMovement", "Gate Operations" }, { "frmRailCall", "Gate Operations" },
            { "frmTruckGateCallList", "Gate Operations" }, { "frmQuickGate", "Gate Operations" },
            { "frmSealAndVGM", "Gate Operations" },
            // Invoicing
            { "frmVesselInvoicing", "Invoicing" }, { "frmLandSideInvoice", "Invoicing" },
            { "frmInvoiceManagement", "Invoicing" }, { "frmRoyaltyInvoicing", "Invoicing" },
            { "frmInvoiceHeader", "Invoicing" }, { "frmInvoiceDetail", "Invoicing" },
            { "frmInvoiceAndPayment", "Invoicing" }, { "frmBuildInvoice", "Invoicing" },
            { "frmCreditNote", "Invoicing" }, { "frmPaymentReceipt", "Invoicing" },
            { "frmMapPayments", "Invoicing" }, { "frmExtraStorage", "Invoicing" },
            { "frmVATInfo", "Invoicing" }, { "frmAddRoyaltyInvoice", "Invoicing" },
            // Rail Operation
            { "FrmEngineMaster", "Rail Operation" }, { "FrmTrackMaster", "Rail Operation" },
            { "FrmWagonType", "Rail Operation" }, { "frmWagonMaster", "Rail Operation" },
            { "frmWagonCall", "Rail Operation" }, { "frmWagonCallInfo", "Rail Operation" },
            { "frmRailRouteMaster", "Rail Operation" }, { "frmRailRouteDetails", "Rail Operation" },
            // Yard Operations
            { "frmNewPosition", "Yard Operations" }, { "frmUnStuffingOperations", "Yard Operations" },
            { "frmAreaPlanner", "Yard Operations" }, { "frmAddAreaPlan", "Yard Operations" },
            { "frmAreaPlanDetails", "Yard Operations" }, { "frmBerthPlannerRestriction", "Yard Operations" },
            { "frmBerthPlannerDuration", "Yard Operations" }, { "frmBerthRestrictaionDetail", "Yard Operations" },
            { "frmAddBerthRestriction", "Yard Operations" }, { "frmEquipmentInstructions", "Yard Operations" },
            { "frmYardPositions", "Yard Operations" }, { "frmActiveTrucks", "Yard Operations" },
            { "frmVesselCallYardOccupation", "Yard Operations" }, { "frmVesselCallYardMoveTo", "Yard Operations" },
            { "frmStackRegistration", "Yard Operations" }, { "frmContainerScan", "Yard Operations" },
            { "frmPendingPositions", "Yard Operations" }, { "frmHouseKeeping", "Yard Operations" },
            { "frmHouseKeepingMove", "Yard Operations" }, { "frmHouseKeepingQueue", "Yard Operations" },
            { "frmAssignTruckForStuffingUnStuffing", "Yard Operations" },
            { "frmAssignTruckForLoadDischarge", "Yard Operations" },
            { "frmAssignNoOfTrucks", "Yard Operations" }, { "frmAssignVesselcall", "Yard Operations" },
            { "frmCargo", "Yard Operations" }, { "frmAddContainer", "Yard Operations" },
            { "frmIMDGCargo", "Yard Operations" }, { "frmAreaTransferRequest", "Yard Operations" },
            { "frmOPSDetail", "Yard Operations" }, { "frmPlanDetail", "Yard Operations" },
            { "frmPlanningLog", "Yard Operations" }, { "frmWorkQueue", "Yard Operations" },
            // Cargo History
            { "frmCargoHistory", "Cargo History" },
            // Reports
            { "frmReports", "Reports" }, { "frmCorporateKPI", "Reports" },
            { "frmMonthwiseReport", "Reports" }, { "frmRevenueCollection", "Reports" },
            { "frmLBPatchReport", "Reports" }, { "frmPatchReport", "Reports" },
            // iTOS Config
            { "frmEquipmentInfo", "iTOS Config" }, { "frmEquipment", "iTOS Config" },
            { "frmEquipmentType", "iTOS Config" }, { "frmVesselMovementServiceSetup", "iTOS Config" },
            { "frmVesselMovement", "iTOS Config" }, { "frmVesselMovementArea", "iTOS Config" },
            { "frmVesselMovementInfo", "iTOS Config" }, { "frmVesselMovementRemark", "iTOS Config" },
            { "frmVesselMovementResources", "iTOS Config" }, { "frmVesselMovementServices", "iTOS Config" },
            { "frmArea", "iTOS Config" }, { "frmRateConfigInfo", "iTOS Config" },
            { "frmRateConfig", "iTOS Config" }, { "frmRateConfigHeader", "iTOS Config" },
            { "frmRateConfigDetail", "iTOS Config" }, { "frmRateConfigHeaderLookUp", "iTOS Config" },
            { "frmContainerISOConfig", "iTOS Config" }, { "frmTariffLinks", "iTOS Config" },
            { "frmGangTemplate", "iTOS Config" }, { "FrmTemplateGangHeader", "iTOS Config" },
            { "frmTemplateGangDetail", "iTOS Config" }, { "frmIncidentTypeManagement", "iTOS Config" },
            { "frmIncidentType", "iTOS Config" }, { "frmIncidentInfo", "iTOS Config" },
            { "frmIncident", "iTOS Config" }, { "frmIncidentEvent", "iTOS Config" },
            { "frmIncidentProcedureTask", "iTOS Config" }, { "frmIncidentVesselCall", "iTOS Config" },
            { "frmRailRouteInfo", "iTOS Config" }, { "frmIPPath", "iTOS Config" },
            { "frmIPPathDetails", "iTOS Config" }, { "frmVesselPath", "iTOS Config" },
            // Admin
            { "FrmOrganization", "Admin" }, { "FrmEmployee", "Admin" },
            { "FrmEmployeeDetails", "Admin" }, { "frmEmployeeLookUp", "Admin" },
            { "FrmGroups", "Admin" }, { "FrmGroupRights", "Admin" },
            { "FrmRights", "Admin" }, { "FrmUserRights", "Admin" },
            { "frmConfig", "Admin" }, { "FrmLookupMaster", "Admin" },
            { "frmExcelUploadStatus", "Admin" }, { "frmLoginAudit", "Admin" },
            { "FrmAuditLog", "Admin" }, { "frmTerminal", "Admin" },
            { "frmTeSWSMessages", "Admin" }, { "frnReleaseUpload", "Admin" },
            { "frmITOSQueue", "Admin" }, { "frmRebindLODtoREC", "Admin" },
            { "frmMissingPortServices", "Admin" },
        };

        static void Main(string[] args)
        {
            ExcelPackage.LicenseContext = LicenseContext.NonCommercial;

            _itosProjectPath = FindITOSProjectPath();
            if (_itosProjectPath == null)
            {
                Console.WriteLine("Error: Could not find ITOS project folder.");
                return;
            }
            Console.WriteLine($"✓ Found ITOS project at: {_itosProjectPath}");

            Console.WriteLine("\n[Phase 1] Extracting menu item labels...");
            ExtractMenuItemLabels();
            Console.WriteLine($"✓ Extracted {_menuItemTexts.Count} menu item labels");

            Console.WriteLine("\n[Phase 2] Scanning Designer.cs files for controls...");
            ScanAllDesignerFiles();
            Console.WriteLine($"✓ Discovered {_formsMap.Count} forms with controls");

            Console.WriteLine("\n[Phase 2.5] Building parent-child form hierarchy...");
            BuildFormChildMap();
            Console.WriteLine($"✓ Mapped {_formChildMap.Count} parent forms with sub-screens/popups");

            Console.WriteLine("\n[Phase 3] Generating QA test cases (depth-first)...");
            GenerateTestCasesFromForms();
            Console.WriteLine($"✓ Generated {_testCases.Count} test cases");

            Console.WriteLine("\n[Phase 4] Exporting to TestCaseControls.xlsx...");
            ExportToExcel();
            Console.WriteLine($"✓ Excel workbook created successfully!");
        }

        static string? FindITOSProjectPath()
        {
            var workspaceRoot = Path.GetDirectoryName(Directory.GetCurrentDirectory());
            while (workspaceRoot != null && !Directory.Exists(Path.Combine(workspaceRoot, "ITOS")))
                workspaceRoot = Path.GetDirectoryName(workspaceRoot);

            if (workspaceRoot != null && Directory.Exists(Path.Combine(workspaceRoot, "ITOS")))
                return Path.Combine(workspaceRoot, "ITOS");

            if (Directory.Exists("../ITOS")) return Path.GetFullPath("../ITOS");
            if (Directory.Exists("../../ITOS")) return Path.GetFullPath("../../ITOS");
            return null;
        }

        static void ExtractMenuItemLabels()
        {
            var designerFile = Path.Combine(_itosProjectPath!, "frmHome.Designer.cs");
            if (!File.Exists(designerFile)) return;
            var content = File.ReadAllText(designerFile);
            foreach (Match m in Regex.Matches(content, @"(\w+ToolStripMenuItem)\.Text\s*=\s*[""']([^""']+)[""']"))
                _menuItemTexts[m.Groups[1].Value] = m.Groups[2].Value;
        }

        // Button display texts that are not meaningful actions — skip these
        private static readonly HashSet<string> _skipButtonTexts = new(StringComparer.OrdinalIgnoreCase)
        {
            "...", "…", ">>", "<<", ">", "<", "›", "‹", "↑", "↓", "→", "←"
        };

        static void ScanAllDesignerFiles()
        {
            var designerFiles = Directory.GetFiles(_itosProjectPath!, "*.Designer.cs", SearchOption.AllDirectories);

            foreach (var designerFile in designerFiles)
            {
                try
                {
                    var content = File.ReadAllText(designerFile);
                    var formName = Path.GetFileNameWithoutExtension(designerFile).Replace(".Designer", "");
                    var form = new UIForm { Name = formName, FilePath = designerFile };

                    var titleMatch = Regex.Match(content, @"this\.Text\s*=\s*""([^""]+)""");
                    if (titleMatch.Success) form.Title = titleMatch.Groups[1].Value;

                    // Build control-name → .Text map for the whole file
                    var allTextMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                    foreach (Match m in Regex.Matches(content, @"(\w+)\.Text\s*=\s*""([^""]+)"""))
                        allTextMap[m.Groups[1].Value] = m.Groups[2].Value;

                    // DataGridViews
                    foreach (Match m in Regex.Matches(content,
                        @"(\w*[Dd]gv\w+)\s*=\s*new\s+(?:Zuby\.ADGV\.)?(?:AdvancedDataGridView|DataGridView)\(\)"))
                        form.Grids.Add(new UIControl { ControlName = m.Groups[1].Value, ControlType = "DataGridView" });

                    // Buttons — skip navigation/browse symbols
                    foreach (Match m in Regex.Matches(content, @"(btn\w+)\s*=\s*new\s+Button\(\)"))
                    {
                        var name = m.Groups[1].Value;
                        allTextMap.TryGetValue(name, out var displayText);
                        if (displayText != null && _skipButtonTexts.Contains(displayText)) continue;
                        form.Buttons.Add(new UIControl { ControlName = name, ControlType = "Button", DisplayText = displayText });
                    }

                    // Context menu items
                    ExtractContextMenuItems(content, form, allTextMap);

                    if (form.Grids.Count > 0 || form.Buttons.Count > 0 || form.ContextMenuItems.Count > 0)
                        _formsMap[formName] = form;
                }
                catch { }
            }
        }

        static void ExtractContextMenuItems(string content, UIForm form, Dictionary<string, string> allTextMap)
        {
            var ctxStripNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (Match m in Regex.Matches(content, @"(\w+)\s*=\s*new\s+ContextMenuStrip"))
                ctxStripNames.Add(m.Groups[1].Value);

            if (ctxStripNames.Count == 0) return;

            var ctxItemNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var stripName in ctxStripNames)
            {
                var escapedName = Regex.Escape(stripName);
                foreach (Match m in Regex.Matches(content,
                    escapedName + @"\.Items\.AddRange\(new\s+ToolStripItem\[\]\s*\{([^}]+)\}"))
                    foreach (Match item in Regex.Matches(m.Groups[1].Value, @"\b([a-zA-Z]\w+)\b"))
                        ctxItemNames.Add(item.Groups[1].Value);

                foreach (Match m in Regex.Matches(content, escapedName + @"\.Items\.Add\((\w+)\)"))
                    ctxItemNames.Add(m.Groups[1].Value);
            }

            var dropDownChildren = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (Match m in Regex.Matches(content,
                @"(\w+)\.DropDownItems\.AddRange\(new\s+ToolStripItem\[\]\s*\{([^}]+)\}"))
            {
                if (ctxItemNames.Contains(m.Groups[1].Value))
                    foreach (Match item in Regex.Matches(m.Groups[2].Value, @"\b([a-zA-Z]\w+)\b"))
                        dropDownChildren.Add(item.Groups[1].Value);
            }
            ctxItemNames.UnionWith(dropDownChildren);

            foreach (var itemName in ctxItemNames)
            {
                if (allTextMap.TryGetValue(itemName, out var text) &&
                    !string.IsNullOrWhiteSpace(text) && text.Length > 1 && text.Length < 80 &&
                    !form.ContextMenuItems.Contains(text))
                {
                    form.ContextMenuItems.Add(text);
                }
            }
        }

        static void BuildFormChildMap()
        {
            var csFiles = Directory.GetFiles(_itosProjectPath!, "*.cs", SearchOption.AllDirectories)
                .Where(f => !f.EndsWith(".Designer.cs", StringComparison.OrdinalIgnoreCase));

            foreach (var csFile in csFiles)
            {
                var parentName = Path.GetFileNameWithoutExtension(csFile);
                if (!_formsMap.ContainsKey(parentName)) continue;

                try
                {
                    var content = File.ReadAllText(csFile);
                    var children = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

                    foreach (Match m in Regex.Matches(content, @"new\s+([Ff]rm\w+)\s*[\(\{]"))
                    {
                        var childName = m.Groups[1].Value;
                        if (!childName.Equals(parentName, StringComparison.OrdinalIgnoreCase) &&
                            _formsMap.ContainsKey(childName))
                            children.Add(childName);
                    }

                    if (children.Count > 0)
                        _formChildMap[parentName] = children.ToList();
                }
                catch { }
            }
        }

        static void GenerateTestCasesFromForms()
        {
            var visitedForms = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            // First pass: follow application menu flow order
            foreach (var flowFormName in _screenFlowOrder)
            {
                if (_formsMap.TryGetValue(flowFormName, out var flowForm) && !visitedForms.Contains(flowFormName))
                {
                    var mainMenu = DetermineMainMenu(flowFormName);
                    ProcessFormDepthFirst(flowFormName, flowForm, mainMenu, visitedForms, 0);
                }
            }

            // Second pass: remaining forms not yet visited (sub-screens, popups not in flow list)
            foreach (var kvp in _formsMap)
            {
                if (!visitedForms.Contains(kvp.Key))
                {
                    var mainMenu = DetermineMainMenu(kvp.Key);
                    ProcessFormDepthFirst(kvp.Key, kvp.Value, mainMenu, visitedForms, 0);
                }
            }
        }

        static void ProcessFormDepthFirst(
            string formName, UIForm formObj, string mainMenu,
            HashSet<string> visitedForms, int depth)
        {
            if (visitedForms.Contains(formName)) return;
            visitedForms.Add(formName);

            var screenName = formObj.Title ?? CleanFormName(formName);
            var entityName = DeriveEntityName(screenName);

            // --- Screen Load ---
            _testCases.Add(new QATestCase
            {
                ParentActionItem = screenName,
                MainMenu = mainMenu,
                ScreenSubMenu = screenName,
                SectionGrid = "Form",
                ActionContextMenu = "Screen Load",
                ActionType = "Screen Load",
                ScenarioDescription = depth == 0
                    ? $"User navigates to the '{screenName}' screen from the {mainMenu} application menu"
                    : $"The '{screenName}' sub-screen opens as part of the current operation",
                ExpectedResult = $"'{screenName}' loads successfully with all controls visible, enabled, and relevant data populated from the database"
            });

            // --- Search Panel ---
            var searchBtn = formObj.Buttons.FirstOrDefault(b =>
                (b.ControlName?.IndexOf("Search", StringComparison.OrdinalIgnoreCase) >= 0 ||
                 b.DisplayText?.Equals("Search", StringComparison.OrdinalIgnoreCase) == true) &&
                !_skipButtonTexts.Contains(b.DisplayText ?? ""));
            var clearBtn = formObj.Buttons.FirstOrDefault(b =>
                (b.ControlName?.IndexOf("Clear", StringComparison.OrdinalIgnoreCase) >= 0 ||
                 b.DisplayText?.Equals("Clear", StringComparison.OrdinalIgnoreCase) == true) &&
                !_skipButtonTexts.Contains(b.DisplayText ?? ""));

            if (searchBtn != null)
                _testCases.Add(new QATestCase
                {
                    ParentActionItem = $"{screenName} > Search Panel > {searchBtn.DisplayText ?? "Search"}",
                    MainMenu = mainMenu,
                    ScreenSubMenu = screenName,
                    SectionGrid = "Search Panel",
                    ActionContextMenu = searchBtn.DisplayText ?? "Search",
                    ActionType = "Action (Button)",
                    ScenarioDescription = $"User enters search criteria in the '{screenName}' search panel and clicks the '{searchBtn.DisplayText ?? "Search"}' button to filter records",
                    ExpectedResult = $"The grid refreshes and displays only the {entityName} records that match the entered search criteria"
                });

            if (clearBtn != null)
                _testCases.Add(new QATestCase
                {
                    ParentActionItem = $"{screenName} > Search Panel > {clearBtn.DisplayText ?? "Clear"}",
                    MainMenu = mainMenu,
                    ScreenSubMenu = screenName,
                    SectionGrid = "Search Panel",
                    ActionContextMenu = clearBtn.DisplayText ?? "Clear",
                    ActionType = "Action (Button)",
                    ScenarioDescription = $"User clicks the '{clearBtn.DisplayText ?? "Clear"}' button in the '{screenName}' search panel to reset all filter fields",
                    ExpectedResult = "All search fields are cleared and the grid reloads displaying all available records without any filter applied"
                });

            // Get child forms for this parent
            _formChildMap.TryGetValue(formName, out var childNames);
            var availableChildren = childNames ?? new List<string>();
            var handledChildren = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            // --- Grids with depth-first context menu traversal ---
            int gridIndex = 1;
            bool ctxMenusEmitted = false;

            foreach (var grid in formObj.Grids)
            {
                var gridLabel = InferGridLabel(grid.ControlName ?? "");
                var sectionName = $"Grid {gridIndex} – {gridLabel}";

                _testCases.Add(new QATestCase
                {
                    ParentActionItem = $"{screenName} > {sectionName}",
                    MainMenu = mainMenu,
                    ScreenSubMenu = screenName,
                    SectionGrid = sectionName,
                    ActionContextMenu = "Load Data",
                    ActionType = "Grid Display",
                    ScenarioDescription = $"The '{sectionName}' loads and displays {entityName} records when the '{screenName}' screen opens",
                    ExpectedResult = "Grid is populated with records retrieved from the database, displayed in the configured sort order"
                });

                // Emit context menus only under the primary (first) grid
                if (gridIndex == 1 && formObj.ContextMenuItems.Count > 0)
                {
                    ctxMenusEmitted = true;
                    EmitContextMenusDepthFirst(formObj.ContextMenuItems, sectionName, screenName,
                        entityName, mainMenu, availableChildren, handledChildren, visitedForms, depth);
                }

                gridIndex++;
            }

            // If context menus exist but no grids, emit under an "Actions" section
            if (!ctxMenusEmitted && formObj.ContextMenuItems.Count > 0)
            {
                EmitContextMenusDepthFirst(formObj.ContextMenuItems, "Actions", screenName,
                    entityName, mainMenu, availableChildren, handledChildren, visitedForms, depth);
            }

            // --- Action Buttons (non-search, non-clear) ---
            var actionButtons = formObj.Buttons.Where(b =>
                !(b.ControlName?.IndexOf("Search", StringComparison.OrdinalIgnoreCase) >= 0) &&
                !(b.ControlName?.IndexOf("Clear", StringComparison.OrdinalIgnoreCase) >= 0) &&
                !(b.DisplayText?.Equals("Search", StringComparison.OrdinalIgnoreCase) == true) &&
                !(b.DisplayText?.Equals("Clear", StringComparison.OrdinalIgnoreCase) == true))
                .ToList();

            foreach (var btn in actionButtons)
            {
                var action = btn.DisplayText ?? InferLabelFromControlName(btn.ControlName ?? "");
                _testCases.Add(new QATestCase
                {
                    ParentActionItem = $"{screenName} > Action Buttons > {action}",
                    MainMenu = mainMenu,
                    ScreenSubMenu = screenName,
                    SectionGrid = "Action Buttons",
                    ActionContextMenu = action,
                    ActionType = "Action (Button)",
                    ScenarioDescription = BuildButtonDescription(action, entityName, screenName),
                    ExpectedResult = BuildButtonExpectedResult(action, entityName)
                });
            }

            // --- Remaining child forms not matched via context menus ---
            if (depth < 2)
            {
                foreach (var childName in availableChildren)
                {
                    if (!handledChildren.Contains(childName) && !visitedForms.Contains(childName) &&
                        _formsMap.TryGetValue(childName, out var childForm))
                    {
                        var childScreen = childForm.Title ?? CleanFormName(childName);
                        _testCases.Add(new QATestCase
                        {
                            ParentActionItem = $"{screenName} > {childScreen}",
                            MainMenu = mainMenu,
                            ScreenSubMenu = screenName,
                            SectionGrid = "Sub-Screen / Popup",
                            ActionContextMenu = $"Open {childScreen}",
                            ActionType = "Popup / Sub-Screen",
                            ScenarioDescription = $"User triggers an action on '{screenName}' that opens the '{childScreen}' sub-screen",
                            ExpectedResult = $"'{childScreen}' opens correctly with context data pre-loaded from '{screenName}'"
                        });
                        ProcessFormDepthFirst(childName, childForm, mainMenu, visitedForms, depth + 1);
                    }
                }
            }
        }

        static void EmitContextMenusDepthFirst(
            List<string> contextMenuItems, string sectionName, string screenName,
            string entityName, string mainMenu,
            List<string> availableChildren, HashSet<string> handledChildren,
            HashSet<string> visitedForms, int depth)
        {
            foreach (var ctxItem in contextMenuItems)
            {
                _testCases.Add(new QATestCase
                {
                    ParentActionItem = $"{screenName} > {sectionName} > {ctxItem}",
                    MainMenu = mainMenu,
                    ScreenSubMenu = screenName,
                    SectionGrid = sectionName,
                    ActionContextMenu = ctxItem,
                    ActionType = "Context Menu",
                    ScenarioDescription = $"User right-clicks on a {entityName} record in '{sectionName}' and selects '{ctxItem}' from the context menu",
                    ExpectedResult = $"'{ctxItem}' action executes successfully and the {entityName} record is updated or the corresponding operation completes"
                });

                // Depth-first: immediately inline matching child form before next context menu item
                if (depth < 2)
                {
                    var matchedChild = FindMatchingChildForm(ctxItem, availableChildren);
                    if (matchedChild != null && !visitedForms.Contains(matchedChild) &&
                        _formsMap.TryGetValue(matchedChild, out var childForm))
                    {
                        handledChildren.Add(matchedChild);
                        var childScreen = childForm.Title ?? CleanFormName(matchedChild);

                        _testCases.Add(new QATestCase
                        {
                            ParentActionItem = $"{screenName} > {sectionName} > {ctxItem} > {childScreen}",
                            MainMenu = mainMenu,
                            ScreenSubMenu = screenName,
                            SectionGrid = sectionName,
                            ActionContextMenu = ctxItem,
                            ActionType = "Popup / Sub-Screen",
                            ScenarioDescription = $"Selecting '{ctxItem}' opens the '{childScreen}' sub-screen for the selected {entityName} record",
                            ExpectedResult = $"'{childScreen}' sub-screen opens with the selected {entityName} data pre-populated and ready for the '{ctxItem}' operation"
                        });

                        // Recurse into child form immediately (depth-first)
                        ProcessFormDepthFirst(matchedChild, childForm, mainMenu, visitedForms, depth + 1);
                    }
                }
            }
        }

        static string? FindMatchingChildForm(string ctxItemText, List<string> childFormNames)
        {
            var stopWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
            {
                "create", "modify", "delete", "view", "open", "edit", "add", "new", "update",
                "remove", "show", "display", "and", "the", "a", "an", "to", "for", "of", "by",
                "set", "get", "list", "all", "with", "from"
            };

            var ctxWords = Regex.Replace(ctxItemText, @"[^a-zA-Z0-9\s]", " ")
                .Split(' ', StringSplitOptions.RemoveEmptyEntries)
                .Select(w => w.ToLowerInvariant())
                .Where(w => !stopWords.Contains(w) && w.Length > 2)
                .ToHashSet();

            if (ctxWords.Count == 0) return null;

            string? bestMatch = null;
            int bestScore = 0;

            foreach (var childFormName in childFormNames)
            {
                var stripped = Regex.Replace(childFormName, @"^[Ff]rm", "");
                var formWords = Regex.Replace(stripped, @"([A-Z])", " $1").Trim()
                    .ToLowerInvariant()
                    .Split(' ', StringSplitOptions.RemoveEmptyEntries)
                    .Where(w => !stopWords.Contains(w) && w.Length > 2)
                    .ToHashSet();

                int score = 0;
                foreach (var cw in ctxWords)
                {
                    // Exact word match OR one word starts with the other (handles plural/abbreviation)
                    if (formWords.Contains(cw) ||
                        formWords.Any(fw => fw.StartsWith(cw) || cw.StartsWith(fw)))
                        score++;
                }

                if (score > bestScore)
                {
                    bestScore = score;
                    bestMatch = childFormName;
                }
            }

            return bestScore >= 1 ? bestMatch : null;
        }

        static string CleanFormName(string formName)
        {
            var name = Regex.Replace(formName, @"^[Ff]rm", "");
            return Regex.Replace(name, @"([A-Z])", " $1").Trim();
        }

        static string DeriveEntityName(string screenName)
        {
            // Lowercase the screen name for use as "a vessel journey record" etc.
            return screenName.ToLowerInvariant();
        }

        static string InferGridLabel(string controlName)
        {
            var name = Regex.Replace(controlName, @"^(dgv|Dgv)", "", RegexOptions.IgnoreCase);
            if (string.IsNullOrWhiteSpace(name)) return controlName;
            return Regex.Replace(name, @"([A-Z])", " $1").Trim();
        }

        static string BuildButtonDescription(string action, string entityName, string screenName)
        {
            var a = action.ToLowerInvariant();
            if (a is "new" or "create")
                return $"User clicks '{action}' to initiate the creation of a new {entityName} record on the '{screenName}' screen";
            if (a == "save")
                return $"User clicks '{action}' to validate and save the {entityName} data entered or modified on the form";
            if (a == "delete")
                return $"User selects a {entityName} record and clicks '{action}' to permanently remove it from the system";
            if (a == "close")
                return $"User clicks '{action}' to close the '{screenName}' screen and return to the previous view";
            if (a == "approve")
                return $"User reviews the {entityName} record and clicks '{action}' to mark it as approved";
            if (a == "cancel")
                return $"User clicks '{action}' to discard all unsaved changes and cancel the current {entityName} operation";
            if (a == "print")
                return $"User clicks '{action}' to generate a printable document of the {entityName} details";
            if (a == "export")
                return $"User clicks '{action}' to export the {entityName} data from the grid to an external file";
            if (a == "refresh")
                return $"User clicks '{action}' to reload the latest {entityName} data from the database";
            if (a == "submit")
                return $"User clicks '{action}' to submit the {entityName} record for further processing or approval";
            if (a == "copy")
                return $"User selects a {entityName} record and clicks '{action}' to create a duplicate entry for editing";
            if (a == "activate")
                return $"User selects a {entityName} record and clicks '{action}' to change its status to Active";
            if (a == "deactivate")
                return $"User selects a {entityName} record and clicks '{action}' to change its status to Inactive";
            return $"User clicks the '{action}' button on the '{screenName}' screen to perform the corresponding operation";
        }

        static string BuildButtonExpectedResult(string action, string entityName)
        {
            var a = action.ToLowerInvariant();
            if (a is "new" or "create")
                return $"Form fields are cleared or a new entry dialog opens, ready for entering a new {entityName} record";
            if (a == "save")
                return $"{entityName} data is validated and persisted to the database; a success confirmation message is displayed";
            if (a == "delete")
                return $"The selected {entityName} record is permanently removed and the grid refreshes to reflect the deletion";
            if (a == "close")
                return "Screen closes cleanly without errors and the user is returned to the previous screen";
            if (a == "approve")
                return $"The {entityName} record status is updated to 'Approved' and any associated workflow notifications are triggered";
            if (a == "cancel")
                return "All unsaved changes are discarded and the form reverts to its last saved state without any data loss";
            if (a == "print")
                return "A print dialog appears and the formatted document is sent to the selected printer successfully";
            if (a == "export")
                return "The data is exported and the output file is saved or downloaded to the specified location";
            if (a == "refresh")
                return "The grid and form fields reload with the most up-to-date data retrieved from the database";
            if (a == "submit")
                return $"The {entityName} is submitted successfully and its status is updated accordingly in the system";
            if (a == "copy")
                return $"A duplicate {entityName} record is created and loaded into the form ready for editing";
            if (a == "activate")
                return $"The selected {entityName} record status changes to Active and is reflected immediately in the grid";
            if (a == "deactivate")
                return $"The selected {entityName} record status changes to Inactive and is reflected immediately in the grid";
            return "The operation completes successfully without errors and the screen updates accordingly";
        }

        static string DetermineMainMenu(string formName)
        {
            if (_formToMainMenu.TryGetValue(formName, out var mainMenu))
                return mainMenu;
            return "General";
        }

        static string InferLabelFromControlName(string controlName)
        {
            var name = Regex.Replace(controlName, @"^(txt|cb|chk|dtp|dt|btn|dgv|lbl)", "", RegexOptions.IgnoreCase);
            if (string.IsNullOrWhiteSpace(name)) return controlName;
            return Regex.Replace(name, @"([A-Z])", " $1").Trim();
        }

        static void ExportToExcel()
        {
            using var package = new ExcelPackage();
            var worksheet = package.Workbook.Worksheets.Add("QA Test Cases");

            var headers = new[]
            {
                "Parent Action Item", "Main Menu", "Screen / Sub-Menu",
                "Section / Grid", "Action / Context Menu", "Action Type",
                "Scenario Description", "Expected Result"
            };

            for (int i = 0; i < headers.Length; i++)
            {
                worksheet.Cells[1, i + 1].Value = headers[i];
                worksheet.Cells[1, i + 1].Style.Font.Bold = true;
                worksheet.Cells[1, i + 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
                worksheet.Cells[1, i + 1].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightBlue);
            }

            int row = 2;
            foreach (var tc in _testCases)
            {
                worksheet.Cells[row, 1].Value = tc.ParentActionItem;
                worksheet.Cells[row, 2].Value = tc.MainMenu;
                worksheet.Cells[row, 3].Value = tc.ScreenSubMenu;
                worksheet.Cells[row, 4].Value = tc.SectionGrid;
                worksheet.Cells[row, 5].Value = tc.ActionContextMenu;
                worksheet.Cells[row, 6].Value = tc.ActionType;
                worksheet.Cells[row, 7].Value = tc.ScenarioDescription;
                worksheet.Cells[row, 8].Value = tc.ExpectedResult;
                row++;
            }

            worksheet.Cells.AutoFitColumns(10, 120);

            var outputPath = Path.Combine(Directory.GetCurrentDirectory(), "TestCaseControls.xlsx");
            package.SaveAs(new FileInfo(outputPath));
            Console.WriteLine($"\n✓ Saved to: {outputPath}");
        }
    }

    class UIForm
    {
        public string? Name { get; set; }
        public string? Title { get; set; }
        public string? FilePath { get; set; }
        public List<UIControl> Grids { get; set; } = new();
        public List<UIControl> Buttons { get; set; } = new();
        public List<string> ContextMenuItems { get; set; } = new();
    }

    class UIControl
    {
        public string? ControlName { get; set; }
        public string? ControlType { get; set; }
        public string? DisplayText { get; set; }
    }

    class QATestCase
    {
        public string? ParentActionItem { get; set; }
        public string? MainMenu { get; set; }
        public string? ScreenSubMenu { get; set; }
        public string? SectionGrid { get; set; }
        public string? ActionContextMenu { get; set; }
        public string? ActionType { get; set; }
        public string? ScenarioDescription { get; set; }
        public string? ExpectedResult { get; set; }
    }
}
Editor is loading...
Leave a Comment