GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
WorkflowManager.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using OpenQA.Selenium;
18using System;
19using System.Collections.Concurrent;
20using System.Collections.Generic;
21using System.Linq;
22using System.Threading;
23using System.Threading.Tasks;
24using static GenerallyPositive.Enums;
25
27{
31 internal class WorkflowManager
32 {
33 private readonly Browser _browser;
34
35 internal WorkflowManager(Browser browser)
36 {
37 _browser = browser ?? throw new ArgumentNullException(nameof(browser)); // Internal, safe to throw
38 }
39
40 public IAllowWorkflowExecution WithWorkflow(Action<IBrowser> workflow)
41 {
42 if (workflow == null)
43 GPAL.PublishSimpleEvent(GPALEventType.WARNING, "Workflow is null in WithWorkflow. Adding empty workflow.", _browser, GPALObjectType.Browser);
44 _browser.BrowserSettings.Workflows.Add(workflow ?? (b => { }));
45 return _browser;
46 }
47
48 public IAllowBrowserActionOrAnySelector While(Func<Browser, bool> predicate)
49 {
50 if (_browser.BrowserSettings.Workflows.Count == 0)
51 {
52 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No workflows defined for While. Continuing without execution.", _browser, GPALObjectType.Browser);
53 return _browser;
54 }
55 if (predicate == null)
56 {
57 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Predicate is null for While. Continuing without execution.", _browser, GPALObjectType.Browser);
58 return _browser;
59 }
60
61 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Starting While loop with [{_browser.BrowserSettings.Workflows.Count}] workflow(s).", _browser, GPALObjectType.Browser);
62
63 var startTime = DateTime.UtcNow;
64 int iteration = 0;
65
66 while (predicate(_browser) && (DateTime.UtcNow - startTime).TotalMilliseconds < _browser.BrowserSettings.WhileLoopTimeoutMs && iteration < _browser.BrowserSettings.WhileLoopMaxIterations)
67 {
68 _browser.WorkflowSetup(checkWaitFor: true, doMaintenance: true);
69 foreach (var workflow in _browser.BrowserSettings.Workflows)
70 workflow(_browser);
71 iteration++;
72 Thread.Sleep(100);
73 }
74
75 if (iteration >= _browser.BrowserSettings.WhileLoopMaxIterations)
76 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"While loop reached maximum iterations ([{_browser.BrowserSettings.WhileLoopMaxIterations}]).", _browser, GPALObjectType.Browser);
77 else if ((DateTime.UtcNow - startTime).TotalMilliseconds >= _browser.BrowserSettings.WhileLoopTimeoutMs)
78 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"While loop timed out after [{_browser.BrowserSettings.WhileLoopTimeoutMs}]ms.", _browser, GPALObjectType.Browser);
79 else
80 GPAL.PublishSimpleEvent(GPALEventType.INFO, "While loop completed.", _browser, GPALObjectType.Browser);
81
82 _browser.BrowserSettings.Workflows.Clear();
83 return _browser;
84 }
85
86 public IAllowBrowserActionOrAnySelector Until(Selector selector)
87 {
88 if (_browser.BrowserSettings.Workflows.Count == 0)
89 {
90 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No workflows defined for Until(selector). Continuing without execution.", _browser, GPALObjectType.Browser);
91 return _browser;
92 }
93 if (selector == null)
94 {
95 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Selector is null for Until. Continuing without execution.", _browser, GPALObjectType.Browser);
96 return _browser;
97 }
98
99 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Starting Until(selector) loop with [{_browser.BrowserSettings.Workflows.Count}] workflow(s).", _browser, GPALObjectType.Browser);
100
101 var startTime = DateTime.UtcNow;
102 int iteration = 0;
103 bool elementsFound = false;
104
105 while (!elementsFound && (DateTime.UtcNow - startTime).TotalMilliseconds < _browser.BrowserSettings.WhileLoopTimeoutMs && iteration < _browser.BrowserSettings.WhileLoopMaxIterations)
106 {
107 _browser.WorkflowSetup(checkWaitFor: true, doMaintenance: true);
108 foreach (var workflow in _browser.BrowserSettings.Workflows)
109 workflow(_browser);
110
111 var foundElements = ElementHelper.FindWebElements(_browser, _browser.CurrentUOW, selector, out bool matchedAll, out List<GPALElement> matchedElements, _browser.CurrentUOW.ShadowRoot);
112 elementsFound = foundElements != null && foundElements.Count > 0;
113
114 iteration++;
115 Thread.Sleep(100);
116 }
117
118 if (iteration >= _browser.BrowserSettings.WhileLoopMaxIterations)
119 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Until(selector) loop reached maximum iterations ([{_browser.BrowserSettings.WhileLoopMaxIterations}]).", _browser, GPALObjectType.Browser);
120 else if ((DateTime.UtcNow - startTime).TotalMilliseconds >= _browser.BrowserSettings.WhileLoopTimeoutMs)
121 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Until(selector) loop timed out after [{_browser.BrowserSettings.WhileLoopTimeoutMs}]ms.", _browser, GPALObjectType.Browser);
122 else
123 GPAL.PublishSimpleEvent(GPALEventType.INFO, "Until(selector) loop completed.", _browser, GPALObjectType.Browser);
124
125 _browser.BrowserSettings.Workflows.Clear();
126 return _browser;
127 }
128
129 public IAllowBrowserActionOrAnySelector Until(Func<Browser, bool> predicate)
130 {
131 if (_browser.BrowserSettings.Workflows.Count == 0)
132 {
133 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No workflows defined for Until(predicate). Continuing without execution.", _browser, GPALObjectType.Browser);
134 return _browser;
135 }
136 if (predicate == null)
137 {
138 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Predicate is null for Until. Continuing without execution.", _browser, GPALObjectType.Browser);
139 return _browser;
140 }
141
142 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Starting Until(predicate) loop with [{_browser.BrowserSettings.Workflows.Count}] workflow(s).", _browser, GPALObjectType.Browser);
143
144 var startTime = DateTime.UtcNow;
145 int iteration = 0;
146
147 while (!predicate(_browser) && (DateTime.UtcNow - startTime).TotalMilliseconds < _browser.BrowserSettings.WhileLoopTimeoutMs && iteration < _browser.BrowserSettings.WhileLoopMaxIterations)
148 {
149 _browser.WorkflowSetup(checkWaitFor: true, doMaintenance: true);
150 foreach (var workflow in _browser.BrowserSettings.Workflows)
151 workflow(_browser);
152 iteration++;
153 Thread.Sleep(100);
154 }
155
156 if (iteration >= _browser.BrowserSettings.WhileLoopMaxIterations)
157 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Until(predicate) loop reached maximum iterations ([{_browser.BrowserSettings.WhileLoopMaxIterations}]).", _browser, GPALObjectType.Browser);
158 else if ((DateTime.UtcNow - startTime).TotalMilliseconds >= _browser.BrowserSettings.WhileLoopTimeoutMs)
159 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Until(predicate) loop timed out after [{_browser.BrowserSettings.WhileLoopTimeoutMs}]ms.", _browser, GPALObjectType.Browser);
160 else
161 GPAL.PublishSimpleEvent(GPALEventType.INFO, "Until(predicate) loop completed.", _browser, GPALObjectType.Browser);
162
163 _browser.BrowserSettings.Workflows.Clear();
164 return _browser;
165 }
166 public void Run()
167 {
168 if (_browser.BrowserSettings.Workflows.Count == 0)
169 {
170 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No workflows defined for Run. Continuing without execution.", _browser, GPALObjectType.Browser);
171 return;
172 }
173
174 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Starting Run with [{_browser.BrowserSettings.Workflows.Count}] workflow(s).", _browser, GPALObjectType.Browser);
175
176 var workflows = _browser.BrowserSettings.Workflows.ToList();
177 _browser.BrowserSettings.Workflows.Clear();
178
179 foreach (var workflow in workflows)
180 {
181 try
182 {
183 _browser.WorkflowSetup(checkWaitFor: true, doMaintenance: true);
184 workflow(_browser);
185 }
186 catch (Exception ex)
187 {
188 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"Workflow failed", _browser, GPALObjectType.Browser, ex);
189 }
190 }
191
192 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Run completed with [{workflows.Count}] workflow(s).", _browser, GPALObjectType.Browser);
193 }
194 }
195}