GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
GPAL.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 DocumentFormat.OpenXml.Office.CustomUI;
21using OpenQA.Selenium;
22using OpenQA.Selenium.Remote;
23using System;
24using System.Collections;
25using System.Collections.Generic;
26using System.Data.SqlClient;
27using System.Diagnostics;
28using System.Drawing;
29using System.Drawing.Imaging;
30using System.IO;
31using System.Linq;
32using System.Net.Http;
33using System.Runtime.CompilerServices;
34using System.Text;
35using System.Threading.Tasks;
36using System.Windows.Automation;
37using System.Windows.Forms;
39using static GenerallyPositive.Enums;
41
42namespace GenerallyPositive
43{
48 public class GPAL
49 {
50 private static Stopwatch _appTimer;
51 private static DateTime _startTime;
52
53 #region <Virtual Keys>
58 public static byte VK_ALT { [Keys()] get { return 0x12; } }
62 public static byte VK_DELETE { [Keys()] get { return 0x2e; } }
66 public static byte VK_F10 { [Keys()] get { return 0x79; } }
70 public static byte VK_PRIOR { [Keys()] get { return 0x21; } } // page up - to scroll up a page
74 public static byte VK_NEXT { [Keys()] get { return 0x22; } } // page down - to scroll down a page
78 public static byte VK_END { [Keys()] get { return 0x23; } } // end key
82 public static byte VK_HOME { [Keys()] get { return 0x24; } } // home key
86 public static byte VK_DOWN { [Keys()] get { return 0x28; } } // down arrow
90 public static byte VK_UP { [Keys()] get { return 0x26; } } // up arrow
94 public static byte VK_TAB { [Keys()] get { return 0x09; } } // tab
98 public static byte VK_RETURN { [Keys()] get { return 0x0D; } } // enter
102 public static byte VK_SHIFT_RIGHT { [Keys()] get { return 0x27; } }
106 public static byte VK_SHIFT_LEFT { [Keys()] get { return 0xA0; } }
110 public static byte VK_CONTROL_LEFT { [Keys()] get { return 0xA2; } }
114 public static byte VK_CONTROL_RIGHT { [Keys()] get { return 0xA3; } }
118 public static byte VK_LWIN { [Keys()] get { return 0x5B; } }
122 public static byte VK_RWIN { [Keys()] get { return 0x5C; } }
126 public static byte VK_SCROLL { get { return 0x91; } }
130 public static byte VK_LMENU { get { return 0xA4; } }
134 public static byte VK_RMENU { get { return 0xA5; } }
138 public static byte VK_APPS { get { return 0x5D; } }
142 public static byte VK_SPACE { [Keys()] get { return 0x20; } }
146 public static byte VK_BACK { [Keys()] get { return 0x08; } }
160 [Keys()]
161 public static uint KEYEVENTF_EXTENDEDKEY { [Keys()] get { return 0x0001; } }
171 public static uint KEYEVENTF_KEYUP { [Keys()] get { return 0x0002; } }
172 #endregion <Virtual Keys>
173 #region <Working Variables>
174 private static List<Application.Application> ApplicationList { get; } = new List<Application.Application>();
175 private static List<Browser.Browser> BrowserList { get; } = new List<Browser.Browser>();
176 private static List<Selector> SelectorList { get; } = new List<Selector>();
177 private static List<GPALForm.GPALForm> FormList { get; } = new List<GPALForm.GPALForm>();
178 private static List<IGPALAI> AIList { get; } = new List<IGPALAI>();
179 private static List<GPALFile> FileList { get; } = new List<GPALFile>();
180 private static List<IGPALMail> MailList { get; } = new List<IGPALMail>();
181 public static List<IGPALLogger> LoggerList { get; } = new List<IGPALLogger>();
182 private static List<GPALControl> ControlList { get; } = new List<GPALControl>();
183 private static List<IGPALGrid<string>> GridList { get; } = new List<IGPALGrid<string>>();
184
185 private static bool SettingsLoaded
186 {
187 get
188 {
189 return gpalSettings.SettingsLoaded;
190 }
191
192 set
193 {
194 gpalSettings.SettingsLoaded = value;
195 }
196 }
197 // deep copy cannot copy to properties
198 private static GPALSettings gpalSettings = new GPALSettings(true); // this will be initialized if we can't load from GPAL.yaml
199 private static string _settingsFilePath = "GPAL.yaml";
200 public static GPALSettings GPALSettings
201 {
202 get
203 {
204 return gpalSettings;
205 }
206 set
207 {
208 gpalSettings = value;
209 }
210 }
211 private static Browser.ElementAssistant elementAssistant { get; } = new Browser.ElementAssistant();
212 private static Application.ElementAssistant applicationElementAssistant { get; } = new Application.ElementAssistant();
213
214 private static Browser.Browser tmpBrowser { get; set; } = null;
215 private static Application.Application tmpApplication { get; set; } = null;
216 private static bool UncaughtExceptionHandlerSet { get; set; } = false;
217
218 private static int applicationCount = 1;
219 private static int browserCount = 1;
220 private static int selectorCount = 1;
221 private static int formCount = 1;
222 private static int controlCount = 1; // will not count each type of control (but should we?)
223 private static int executableCount = 1;
224 private static int restClientCount = 1;
225
226 private static readonly string applicationName = "Application";
227 private static readonly string browserName = "Browser";
228 private static readonly string chartName = "Chart";
229 private static readonly string selectorName = "Selector";
230 private static readonly string formName = "Form";
231 private static readonly string buttonName = "Button";
232 private static readonly string checkboxName = "Checkbox";
233 private static readonly string inputName = "Input";
234 private static readonly string labelName = "Label";
235 private static readonly string radioButtonName = "Radiobutton";
236 private static readonly string textAreaName = "TextArea";
237 private static readonly string richTextBoxName = "RichTextBox";
238 private static readonly string executableName = "Executable";
239 private static readonly string restClientName = "RESTClient";
240
241 private static bool inPublishEvent = false;
242 // which loggers are part way through a write on this thread, so one can be kept from receiving what it
243 // raised while writing without keeping it from the others
244 [ThreadStatic] private static HashSet<IGPALLogger> loggersWriting;
245 #endregion <Working Variables>
246 #region Constructor
247 static bool inStaticInit = false;
248 // Private constructor to prevent instantiation
249 private GPAL() { }
255 static GPAL()
256 {
257 if (false == inStaticInit)
258 {
259 inStaticInit = true;
260
261 // Initialize timer
262 _startTime = DateTime.Now;
263 _appTimer = Stopwatch.StartNew();
264
265 GPAL.LoadSettings();
266
267 if (ConsoleEvents.HasFlag(GPALEventType.INFO))
268 {
269 PublishSimpleEvent(GPALEventType.INFO, $">>> GPAL v[{GPAL.Version}]");
270 PublishSimpleEvent(GPALEventType.INFO, $">>> Application Start Time: [{_startTime.ToString("yyyy-MM-dd HH:mm:ss")}] <<<");
271 }
272 else if (ConsoleEvents.HasFlag(GPALEventType.NOTICE))
273 {
274 PublishSimpleEvent(GPALEventType.NOTICE, $">>> GPAL v[{GPAL.Version}]");
275 PublishSimpleEvent(GPALEventType.NOTICE, $">>> Application Start Time: [{_startTime.ToString("yyyy-MM-dd HH:mm:ss")}] <<<");
276 }
277
278 SetCatchUncaughtExceptions();
279
280 inStaticInit = false;
281 }
282 }
283 #endregion Constructor
284 #region <Exception & Information EventHandler>
292 public class GPALEventArgs : System.EventArgs
293 {
294 public Enums.GPALEventType GPALEventType { get; set; }
298 public dynamic GPALObject { get; set; }
302 public Enums.GPALObjectType GPALObjectType { get; set; }
307 public UnitOfWork CurrentUOW { get; set; }
311 public string Message { get; set; }
315 public Exception ExceptionRaised { get; set; }
319 public Image ScreenShot { get; set; }
323 public IWebDriver BrowserDriver { get; set; }
327 public Selector Selector { get; set; }
331 public GPALElement GPALElement { get; set; }
335 public List<GPALElement> WebElements { get; set; }
343 public List<GPALAutomationElement> AutomationElements { get; set; }
352 public IGPALGrid<string> Tokens { get; set; }
356 public List<IGPALGrid<string>> TokenList { get; set; }
360 public SqlCommand SqlCommand { get; set; }
364 public dynamic Data { get; set; }
368 public DateTime DateTimeStamp { get; set; }
369 }
370
371 internal static EventHandler<GPALEventArgs> ExceptionEventHandler { get; set; }
372 internal static EventHandler<GPALEventArgs> InformationEventHandler { get; set; }
373 #endregion <Exception & Information EventHandler>
374 #region <Globals>
394 internal static EventHandler<GPALEventArgs> ExceptionHandler
395 {
396 get
397 {
398 return ExceptionEventHandler;
399 }
400 }
421 internal static EventHandler<GPALEventArgs> InformationHandler
422 {
423 get
424 {
425 return InformationEventHandler;
426 }
427 }
428 #endregion <Globals>
429 #region <GPAL Object Getters>
430 public static Cryptography Cryptography
431 {
432 get => new Cryptography();
433 }
434 public static Base64Helper Base64Helper
435 {
436 get => new Base64Helper();
437 }
458 public static IAllowAIProvider AI
459 {
460 get
461 {
462 GPALAI ai = new GPALAI();
463 AIList.Add(ai);
464 return ai;
465 }
466 }
467
468 public static IGPALYouTube YouTube => new Browser.GPALYouTube();
469
470 private static Func<OCRInterfaces.IGPALOCR> _ocrFactory;
471 public static void RegisterOCR(Func<OCRInterfaces.IGPALOCR> factory) => _ocrFactory = factory;
472 public static OCRInterfaces.IGPALOCR OCR => _ocrFactory != null
473 ? _ocrFactory()
474 : throw new InvalidOperationException("OCR provider not registered. Add the GenerallyPositive.OCR NuGet and call GPAL.RegisterOCR(() => new GPALOCR()).");
475
476 internal static ImageHelper imageHelper;
477 public static IImageHelper ImageHelper
478 {
479 get
480 {
481 if (null == imageHelper)
482 imageHelper = new ImageHelper();
483 return imageHelper;
484 }
485 }
486
507 public static IAllowFileName File
508 {
509 [Component()]
510 get
511 {
512 GPALFile file = new GPALFile();
513 FileList.Add(file);
514 return (IAllowFileName)file;
515 }
516 }
517
520 public static IAllowGridActions<string> Grid
521 {
522 [Component()]
523 get
524 {
525 IGPALGrid<string> grid = new GPALGrid<string>();
526 GridList.Add(grid);
527 return grid;
528 }
529 }
530
534 {
535 [Component()]
536 get
537 {
538 IGPALLogger logger = (IGPALLogger)new GPALLogger();
539 LoggerList.Add(logger);
540 return (IAllowLoggingSettings)logger;
541 }
542 }
543
547 {
548 [Component()]
549 get
550 {
551 GPALMail mail = new GPALMail();
552 MailList.Add(mail);
553 return (IAllowEmailServerSettings)mail;
554 }
555 }
556
560 {
561 [Fluent(Description = "")]
562 get
563 {
564 GPALConverter converter = new GPALConverter();
565 return (IAllowConverterInput)converter;
566 }
567 }
568
586 {
587 [Fluent()]
588 get
589 {
590 tmpApplication = new Application.Application();
591 tmpApplication.Name = applicationName + applicationCount++.ToString();
592 ApplicationList.Add(tmpApplication);
593 return tmpApplication;
594 }
595 }
596
617 {
618 [Fluent()]
619 get
620 {
621 tmpBrowser = new Browser.Browser();
622 tmpBrowser.Name = browserName + browserCount++.ToString();
623
624 CheckVersions();
625
626 // if we already have a browser and instantiate a new one, it MUST use the same automation engine as the first browser instantiated.
627 if (0 < BrowserList.Count)
628 tmpBrowser.WithAutomationEngine(BrowserList[0].AutomationEngine);
629
630 BrowserList.Add(tmpBrowser);
631 tmpBrowser.BrowserSettings.MagicHelper = new GenerallyPositive.Browser.MagicHelper(tmpBrowser); // one helper per browser, talking to that browser's port
632 return tmpBrowser;
633 }
634 }
635
660 {
661 [Component()]
662 get
663 {
664 GPALDatabase database = new GPALDatabase();
665 return (IAllowDatabaseSettings)database;
666 }
667 }
668
693 {
694 [Component()]
695 get
696 {
697 Selector tmpSelector = null;
698 if (null != tmpBrowser)
699 tmpSelector = new Selector(tmpBrowser);
700 else if (null != tmpApplication)
701 tmpSelector = new Selector(tmpApplication);
702 else
703 tmpSelector = new Selector();
704 tmpSelector.Name = selectorName + selectorCount++.ToString();
705 SelectorList.Add(tmpSelector);
706 return tmpSelector;
707 }
708 }
709
724 {
725 [Component()]
726 get
727 {
728 return new GPALRequest();
729 }
730 }
731 public static IAllowExecutableSetup Launcher
732 {
733 [Component()]
734 get
735 {
736 Launcher launcher = new Launcher
737 {
738 Name = executableName + executableCount++.ToString()
739 };
740 return launcher;
741 }
742
743 }
914 {
915 [Component()]
916 get
917 {
918 RESTClient restClient = new RESTClient
919 {
920 Name = restClientName + restClientCount++.ToString()
921 };
922 return restClient;
923 }
924 }
925
929 {
930 [Component()]
931 get => new Credentials((Browser.Browser)Browser); // TODO: CAVEAT: BUG: implement correct, this browser is a placeholder
932 }
933 public static IAllowGiven Gherkin
934 {
935 [Component()]
936 get => new Gherkin();
937 }
942 {
943 [Component()]
944 get => new GoogleSheets();
945 }
946
950 {
951 [Component()]
952 get => new GoogleDrive();
953 }
954 public static IGPALExcel Excel
955 {
956 [Component()]
957 get => new GPALExcel();
958 }
959 public static IAllowGPALUrlForUrl Url
960 {
961 [Component()]
962 get => new GPALUrl(null);
963 }
964
965 #region Internal GPAL factory for ActivatorHelper
966 internal static GPALElement Element
967 {
968 get => new GPALElement();
969 }
970 internal static GPALFileSettings FileSettings
971 {
972 get => new GPALFileSettings();
973 }
974 internal static GPALMailSettings MailSettings
975 {
976 get => new GPALMailSettings();
977 }
978 internal static GPALSettings Settings
979 {
980 get => new GPALSettings();
981 }
982 #endregion Internal GPAL factory for ActivatorHelper
983 #endregion <GPAL Objects>
984 #region Shorthand Helpers
985 [Component()]
986 public static IGPALExcel ExcelFor(GPALFile excelFile)
987 {
988 return new GPALExcel().WithFile(excelFile).ToGPALObject();
989 }
990
991 [Component()]
992 public static ICredentials CredentialsFor(CredentialServiceType credentialServiceType)
993 {
994 return new Credentials((Browser.Browser)Browser).WithService(credentialServiceType).ToGPALObject();
995 }
996 [Component()]
997 public static IGoogleSheets GoogleSheetsFor(string spreadsheetName)
998 {
999 return new GoogleSheets().WithSpreadsheet(spreadsheetName).ToGPALObject();
1000 }
1001 [Component()]
1002 public static IGoogleDrive GoogleDriveFor(ICredentials credentials)
1003 {
1004 return new GoogleDrive().WithCredentials(credentials).ToGPALObject();
1005 }
1006 [Component()]
1007 public static IGPALGrid<T> GridForType<T>()
1008 {
1009 return new GPALGrid<T>();
1010 }
1025 [Component()]
1026 public static Selector CssSelector(string css, string selectorName = null)
1027 {
1028 if (null != selectorName)
1029 return Selector.WithCSS(css).WithSelectorName(selectorName).ToGPALObject();
1030 else
1031 return Selector.WithCSS(css).ToGPALObject();
1032 }
1033
1034 [Component()]
1035 public static Selector XPathSelector(string xpath, string selectorName = null)
1036 {
1037 if (null != selectorName)
1038 return Selector.WithXPath(xpath).WithSelectorName(selectorName).ToGPALObject();
1039 else
1040 return Selector.WithXPath(xpath).ToGPALObject();
1041 }
1042 [Component()]
1043 public static GPALFile FileFor(string path)
1044 {
1045 return File.WithFileName(path).ToGPALObject();
1046 }
1047 // New browser shorthand methods
1048 [Component()]
1049 public static IBrowser BrowserGet(GPALUrl url)
1050 {
1051 return Browser
1052 .Get(url) // Headless navigation
1053 .ToGPALObject();
1054
1055 }
1056 [Component()]
1057 public static IBrowser BrowserGoto(GPALUrl url)
1058 {
1059 return Browser
1060 .GoTo(url) // Windowed navigation
1061 .ToGPALObject();
1062 }
1063 [Component()]
1064 public static GPALButton ButtonFor(string text)
1065 {
1066 var c = new GPALButton { Name = buttonName + controlCount++.ToString() };
1067 c.IsDefault = false;
1068 c.Text = text;
1069 ControlList.Add(c);
1070 return c;
1071 }
1072 [Component()]
1073 public static GPALLabel LabelFor(string text)
1074 {
1075 var c = new GPALLabel { Name = labelName + controlCount++.ToString() };
1076 c.Text = text;
1077 ControlList.Add(c);
1078 return c;
1079 }
1080 [Component()]
1081 public static GPALInput InputFor(string text = "")
1082 {
1083 var c = new GPALInput { Name = inputName + controlCount++.ToString() };
1084 c.Text = text;
1085 ControlList.Add(c);
1086 return c;
1087 }
1088 [Component()]
1089 public static GPALTextArea TextAreaFor(string text = "")
1090 {
1091 var c = new GPALTextArea { Name = textAreaName + controlCount++.ToString() };
1092 c.Text = text;
1093 ControlList.Add(c);
1094 return c;
1095 }
1096 [Component()]
1097 public static GPALRichTextBox RichTextBoxFor(string text = "")
1098 {
1099 var c = new GPALRichTextBox { Name = richTextBoxName + controlCount++.ToString() };
1100 c.Text = text;
1101 ControlList.Add(c);
1102 return c;
1103 }
1104 [Component()]
1105 public static GPALCheckbox CheckboxFor(string text, bool isChecked = false)
1106 {
1107 var c = new GPALCheckbox { Name = checkboxName + controlCount++.ToString() };
1108 c.Text = text;
1109 c.IsChecked = isChecked;
1110 ControlList.Add(c);
1111 return c;
1112 }
1113 [Component()]
1114 public static GPALRadioButton RadioButtonFor(string text, bool isChecked = false)
1115 {
1116 var c = new GPALRadioButton { Name = radioButtonName + controlCount++.ToString() };
1117 c.Text = text;
1118 c.IsChecked = isChecked;
1119 ControlList.Add(c);
1120 return c;
1121 }
1122 [Component()]
1123 public static GPALTab TabFor(string text)
1124 {
1125 var c = new GPALTab { Name = radioButtonName + controlCount++.ToString() };
1126 c.Text = text;
1127 ControlList.Add(c);
1128 return c;
1129 }
1130 [Component()]
1131 public static GPALChart ChartFor(string title = "")
1132 {
1133 var c = new GPALChart { Name = chartName + controlCount++.ToString() };
1134 if (!string.IsNullOrEmpty(title)) c.WithTitle(title);
1135 ControlList.Add(c);
1136 return c;
1137 }
1138 [Component()]
1139 public static GPALComboBox ComboBoxFor(params string[] items)
1140 {
1141 var c = new GPALComboBox { Name = "ComboBox" + controlCount++.ToString() };
1142 if (items != null && items.Length > 0) c.WithItems(items);
1143 return c;
1144 }
1145 [Component()]
1146 public static GPALDataGridView DataGridViewFor(params string[] columns)
1147 {
1148 var c = new GPALDataGridView { Name = "DataGridView" + controlCount++.ToString() };
1149 if (columns != null && columns.Length > 0) c.WithColumns(columns);
1150 return c;
1151 }
1152 [Component()]
1153 public static GPALListView ListViewFor(params string[] columns)
1154 {
1155 var c = new GPALListView { Name = "ListView" + controlCount++.ToString() };
1156 if (columns != null && columns.Length > 0) c.WithColumns(columns);
1157 return c;
1158 }
1159 [Component()]
1160 public static GPALProgressBar ProgressBarFor(int maximum = 100)
1161 {
1162 var c = new GPALProgressBar { Name = "ProgressBar" + controlCount++.ToString() };
1163 c.WithMaximum(maximum);
1164 return c;
1165 }
1166 [Component()]
1167 public static GPALStatusStrip StatusStripFor(string text = "Ready")
1168 {
1169 var c = new GPALStatusStrip { Name = "StatusStrip" + controlCount++.ToString() };
1170 c.Text = text;
1171 return c;
1172 }
1173 [Component()]
1174 public static GPALNumericUpDown NumericUpDownFor(decimal minimum = 0, decimal maximum = 100)
1175 {
1176 var c = new GPALNumericUpDown { Name = "NumericUpDown" + controlCount++.ToString() };
1177 c.WithMinimum(minimum);
1178 c.WithMaximum(maximum);
1179 return c;
1180 }
1181 [Component()]
1182 public static GPALDateTimePicker DateTimePickerFor(DateTime? value = null)
1183 {
1184 var c = new GPALDateTimePicker { Name = "DateTimePicker" + controlCount++.ToString() };
1185 if (value.HasValue) c.WithValue(value.Value);
1186 return c;
1187 }
1188 [Component()]
1189 public static GPALTreeView TreeViewFor()
1190 {
1191 return new GPALTreeView { Name = "TreeView" + controlCount++.ToString() };
1192 }
1193 [Component()]
1194 public static GPALFileSelector FileSelectorFor(string title = "Select File/Folder")
1195 {
1196 var c = new GPALFileSelector { Name = "FileSeleector" + controlCount++.ToString() };
1197 c.WithTitle(title);
1198 return c;
1199 }
1200 [Component()]
1201 public static GPALTableLayoutPanel TableLayoutPanelFor(int columns = 1, int rows = 1)
1202 {
1203 var c = new GPALTableLayoutPanel { Name = "TableLayoutPanel" + controlCount++.ToString() };
1204 c.WithColumnCount(columns);
1205 c.WithRowCount(rows);
1206 return c;
1207 }
1208 #endregion Shorthand Helpers
1209 #region <ElementAssitant>
1232 [ElementAssitant()]
1234 {
1235 elementAssistant.Browser = selector.Browser;
1236 elementAssistant.ForSelector(selector);
1237 return elementAssistant;
1238 }
1239
1263 [ElementAssitant()]
1264 public static Application.IAllowForSelector ElementAssistant(Application.Application application)
1265 {
1266 applicationElementAssistant.Application = application;
1267 return applicationElementAssistant;
1268 }
1269 #endregion <ElementAssitant>
1270 #region <Form Controls>
1302 {
1303 [Fluent()]
1304 get
1305 {
1306 GPALForm.GPALForm tmpForms = new GPALForm.GPALForm()
1307 {
1308 FormName = formName + formCount++.ToString(),
1309 };
1310 FormList.Add(tmpForms);
1311 return tmpForms;
1312 }
1313 }
1314
1347 {
1348 [Forms()]
1349 get
1350 {
1351 GPALButton tmpControl = new GPALButton
1352 {
1353 Name = buttonName + controlCount++.ToString(),
1354 IsDefault = false
1355 };
1356 //CAVEAT: very strange bug, isdefault keeps getting set to true
1357 tmpControl.IsDefault = false;
1358 ControlList.Add(tmpControl);
1359 return tmpControl;
1360 }
1361 }
1362
1404 {
1405 [Forms()]
1406 get
1407 {
1408 GPALChart tmpControl = new GPALChart
1409 {
1410 Name = chartName + controlCount++.ToString()
1411 };
1412 ControlList.Add(tmpControl);
1413 return tmpControl;
1414 }
1415 }
1416
1447 {
1448 [Forms()]
1449 get
1450 {
1451 GPALCheckbox tmpControl = new GPALCheckbox
1452 {
1453 Name = checkboxName + controlCount++.ToString()
1454 };
1455 ControlList.Add(tmpControl);
1456 return tmpControl;
1457 }
1458 }
1459
1486 {
1487 get
1488 {
1489 GPALComboBox tmpControl = new GPALComboBox
1490 {
1491 Name = "ComboBox" + controlCount++.ToString() // or use your naming scheme
1492 };
1493 // ControlList.Add(tmpControl); ← usually done in WithFormControl / AddComboBox
1494 return tmpControl;
1495 }
1496 }
1497
1514 {
1515 get
1516 {
1518 {
1519 Name = "DataGridView" + controlCount++.ToString()
1520 };
1521 return tmp;
1522 }
1523 }
1524
1543 {
1544 get
1545 {
1547 {
1548 Name = "DateTimePicker" + controlCount++.ToString()
1549 };
1550 return tmp;
1551 }
1552 }
1553
1595 {
1596 get
1597 {
1599 {
1600 Name = "FileSeleector" + controlCount++.ToString()
1601 };
1602 return gfs;
1603 }
1604 }
1605
1626 {
1627 [Forms()]
1628 get
1629 {
1630 GPALInput tmpControl = new GPALInput
1631 {
1632 Name = inputName + controlCount++.ToString()
1633 };
1634 ControlList.Add(tmpControl);
1635 return tmpControl;
1636 }
1637 }
1638
1658 {
1659 [Forms()]
1660 get
1661 {
1662 GPALLabel tmpControl = new GPALLabel
1663 {
1664 Name = labelName + controlCount++.ToString()
1665 };
1666 ControlList.Add(tmpControl);
1667 return tmpControl;
1668 }
1669 }
1670
1698 {
1699 get
1700 {
1701 GPALListView tmp = new GPALListView
1702 {
1703 Name = "ListView" + controlCount++.ToString()
1704 };
1705 return tmp;
1706 }
1707 }
1708
1729 {
1730 get
1731 {
1733 {
1734 Name = "NumericUpDown" + controlCount++.ToString()
1735 };
1736 return tmp;
1737 }
1738 }
1739
1760 {
1761 get
1762 {
1764 {
1765 Name = "ProgressBar" + controlCount++.ToString()
1766 };
1767 return tmp;
1768 }
1769 }
1770
1809 {
1810 [Forms()]
1811 get
1812 {
1813 GPALRadioButton tmpControl = new GPALRadioButton
1814 {
1815 Name = radioButtonName + controlCount++.ToString()
1816 };
1817 ControlList.Add(tmpControl);
1818 return tmpControl;
1819 }
1820 }
1821
1838 {
1839 get
1840 {
1842 {
1843 Name = "StatusStrip" + controlCount++.ToString()
1844 };
1845 return tmp;
1846 }
1847 }
1848
1868 {
1869 get
1870 {
1871 GPALMenuBar tmp = new GPALMenuBar
1872 {
1873 Name = "MenuBar" + controlCount++.ToString()
1874 };
1875 return tmp;
1876 }
1877 }
1878
1895 {
1896 get
1897 {
1898 GPALBarItem tmp = new GPALBarItem
1899 {
1900 Name = "BarItem" + controlCount++.ToString()
1901 };
1902 return tmp;
1903 }
1904 }
1905
1921 {
1922 get
1923 {
1924 GPALToolbar tmp = new GPALToolbar
1925 {
1926 Name = "Toolbar" + controlCount++.ToString()
1927 };
1928 return tmp;
1929 }
1930 }
1931
1956 {
1957 [Forms()]
1958 get
1959 {
1960 GPALTab tmpControl = new GPALTab
1961 {
1962 Name = radioButtonName + controlCount++.ToString()
1963 };
1964 ControlList.Add(tmpControl);
1965 return tmpControl;
1966 }
1967 }
1968
1985
1991
1993 public static GPALSpliter SplitRight => new GPALSpliter();
1994
1996 public static GPALSpliter SplitBottom => new GPALSpliter();
1997
2020 {
2021 [Forms()]
2022 get
2023 {
2024 GPALTextArea tmpControl = new GPALTextArea
2025 {
2026 Name = textAreaName + controlCount++.ToString()
2027 };
2028 ControlList.Add(tmpControl);
2029 return tmpControl;
2030 }
2031 }
2032
2054 {
2055 [Forms()]
2056 get
2057 {
2058 GPALRichTextBox tmpControl = new GPALRichTextBox
2059 {
2060 Name = richTextBoxName + controlCount++.ToString()
2061 };
2062 ControlList.Add(tmpControl);
2063 return tmpControl;
2064 }
2065 }
2066
2086 {
2087 get
2088 {
2089 GPALTreeView tmp = new GPALTreeView
2090 {
2091 Name = "TreeView" + controlCount++.ToString()
2092 };
2093 return tmp;
2094 }
2095 }
2096 #endregion <Form Controls>
2097 #region <Getters/Setters>
2098 public static bool UseHardware
2099 {
2100 get => gpalSettings.UseHardware;
2101 }
2102 public static bool UseJavaSCript
2103 {
2104 get => gpalSettings.UseJavaScript;
2105 }
2106
2122 {
2123 [Component()]
2124 get
2125 {
2126 return new Browser.WorkflowScheduler();
2127 }
2128 }
2129
2146 {
2147 [Component()]
2148 get
2149 {
2150 return new Browser.GPALHiddenDesktops();
2151 }
2152 }
2153 public static string Version
2154 {
2155 get => GPALSettings.Version;
2156 }
2162 public static string OAuthTokenUrl
2163 {
2164 get
2165 {
2166 return gpalSettings.OAuthTokenUrl;
2167 }
2168 set
2169 {
2170 gpalSettings.OAuthTokenUrl = value;
2171 }
2172 }
2173 internal static List<IGPALLogger> PublishToLoggers
2174 {
2175 get
2176 {
2177 return GPALSettings.PublishToLoggers;
2178 }
2179 }
2180 internal static bool PublishToConsole
2181 {
2182 get
2183 {
2184 return GPALSettings.PublishToConsole;
2185 }
2186 set
2187 {
2188 GPALSettings.PublishToConsole = value;
2189 }
2190 }
2191 internal static bool PublishToDebug
2192 {
2193 get
2194 {
2195 return GPALSettings.PublishToDebug;
2196 }
2197 }
2198 internal static GPALEventType ConsoleEvents
2199 {
2200 get => GPALSettings.ConsoleEvents;
2201 set => GPALSettings.ConsoleEvents = value;
2202 }
2203 internal static GPALEventType DebugEvents
2204 {
2205 get => GPALSettings.DebugEvents;
2206 }
2207 internal static bool PublishStackTrace
2208 {
2209 get
2210 {
2211 return GPALSettings.PublishStackTrace;
2212 }
2213 }
2214 internal static string ErrorPlaceholder
2215 {
2216 get
2217 {
2218 return GPALSettings.ErrorPlaceholder;
2219 }
2220 }
2221 internal static string DriverLocation
2222 {
2223 get
2224 {
2225 return GPALSettings.DriverLocation;
2226 }
2227 }
2228 internal static bool SimulateMouse
2229 {
2230 get
2231 {
2232 return GPALSettings.SimulateMouse;
2233 }
2234 }
2235 internal static string TempProfileDirectory
2236 {
2237 get
2238 {
2239 return GPALSettings.TempProfileDirectory;
2240 }
2241 }
2242 internal static List<Browser.Browser> Browsers
2243 {
2244 get
2245 {
2246 return BrowserList;
2247 }
2248 }
2249 internal static int MatchingPercentage
2250 {
2251 get
2252 {
2253 return GPALSettings.MatchingPercentage;
2254 }
2255 }
2256 internal static int TypingDelay
2257 {
2258 get
2259 {
2260 return GPALSettings.TypingDelay;
2261 }
2262 }
2263 internal static bool StopOnNotFound
2264 {
2265 get
2266 {
2267 return GPALSettings.StopOnNotFound;
2268 }
2269 }
2270 internal static bool NoFallbackRecoveryActions
2271 {
2272 get
2273 {
2274 return gpalSettings.NoFallbackRecoveryActions;
2275 }
2276 }
2277 #endregion <Getters/Setters>
2278 #region <Helpers>
2279 [Helper()]
2280 public static void LoadSettings(GPALFile settingsFile = null)
2281 {
2282 bool hold = GPAL.PublishToConsole;
2283 GPALEventType events = GPAL.GPALSettings.ConsoleEvents;
2284
2285 try
2286 {
2287 if (settingsFile != null)
2288 {
2289 _settingsFilePath = settingsFile.Filenames[0];
2290 SettingsLoaded = false;
2291 }
2292
2293 if (false == SettingsLoaded)
2294 {
2295 SettingsLoaded = true;
2296 string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
2297 GPALFile resolvedFile = _settingsFilePath;
2298
2299 if (true == System.IO.File.Exists(resolvedFile.Filenames[0]))
2300 {
2301 // load setting which contains default events to publish
2302 GPAL.Converter.WithInput(resolvedFile).SaveTo(ref gpalSettings);
2303
2304 hold = GPAL.PublishToConsole;
2305 events = GPAL.GPALSettings.ConsoleEvents;
2306
2307 // NOTE: CAVEAT: WE CANNOT EVER send anything on stdio when we are running as a browser Native App - that will disconnect it as garbage received
2308 if ("1" == Environment.GetEnvironmentVariable("GPAL_IS_SILENT"))
2309 GPAL.PublishToConsole = false;
2310
2311 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"[{resolvedFile.Filenames[0]}] loaded, using settings.", gpalSettings, GPALObjectType.Other);
2312
2313 if (false == gpalSettings.Version.Equals(currentVersion))
2314 {
2315 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"[{resolvedFile.Filenames[0]}] updated to current GPAL version [{currentVersion}].", gpalSettings, GPALObjectType.Other);
2316 gpalSettings.Version = currentVersion;
2317 SaveSettings();
2318 }
2319 }
2320 else // if file doesn't exist, initialize default settings and save a settings file, the user can then tweak those settings for next run/load
2321 {
2322 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"[{resolvedFile.Filenames[0]}] does not exist. Using default settings and creating GPAL.yaml.", gpalSettings, GPALObjectType.Other);
2323 SaveSettings();
2324 }
2325 }
2326
2327 AIProvidersConfig.TryAutoLoad();
2328 }
2329 catch (Exception ex)
2330 {
2331 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"[{_settingsFilePath}] load error. Try deleting it and restarting.", gpalSettings, GPALObjectType.Other, ex);
2332 }
2333 finally
2334 {
2335 GPAL.PublishToConsole = hold;
2336 GPAL.GPALSettings.ConsoleEvents = events;
2337 }
2338 }
2339 [Helper()]
2340 public static void SaveSettings(GPALFile settingsFile = null)
2341 {
2342 if (settingsFile != null) _settingsFilePath = settingsFile.Filenames[0];
2343 GPAL.Converter.WithInput(GPALSettings).SaveTo((GPALFile)_settingsFilePath);
2344 }
2352 [Helper()]
2353 public static IAllowGraphicSettings CaptureScreen(Browser.IBrowser browser)
2354 {
2355 ((ImageHelper)GPAL.ImageHelper).CaptureScreen(browser).ToBitmap(out ((Browser.Browser)browser).BrowserSettings.ScreenShot);
2356
2357 return ImageHelper;
2358 }
2359
2371 // https://stackoverflow.com/questions/44153/can-you-use-reflection-to-find-the-name-of-the-currently-executing-method/67372375#67372375
2372 [MethodImpl(MethodImplOptions.NoInlining)]
2373 [Helper()]
2374 public static string MyMethodName()
2375 {
2376 var st = new StackTrace();
2377 var sf = st.GetFrame(1);
2378 string name = sf.GetMethod().Name;
2379
2380 if (name.Equals("MoveNext"))
2381 {
2382 // We're inside an async method
2383 name = sf.GetMethod().ReflectedType.Name
2384 .Split(new char[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries)[0];
2385 }
2386
2387 return name;
2388 }
2389
2404 [MethodImpl(MethodImplOptions.NoInlining)]
2405 [Helper()]
2406 public static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject = null, Enums.GPALObjectType gPALObjectType = GPALObjectType.None, Exception ex = null)
2407 {
2408 // Early-out BEFORE the costly new StackTrace()/reflection below: skip entirely when no sink will
2409 // consume this event level. Console/Debug are level-masked here (ConsoleEvents/DebugEvents), so a
2410 // high-volume low level like DEEPDEBUG no longer pays for a full stack capture when it is not in the
2411 // masks. Any attached handler still receives every event (handlers filter on their own side), so this
2412 // does not change the handler contract.
2413 bool handlerWants = (null != ExceptionHandler || null != InformationHandler);
2414 bool consoleWants = true == GPAL.PublishToConsole && gpalSettings.ConsoleEvents.HasFlag(gPALEventType);
2415 bool debugWants = true == GPAL.PublishToDebug && gpalSettings.DebugEvents.HasFlag(gPALEventType);
2416 bool loggerWants = 0 < GPAL.PublishToLoggers.Count;
2417
2418 if (false == handlerWants && false == consoleWants && false == debugWants && false == loggerWants)
2419 return;
2420
2421 if (null != ExceptionHandler || null != InformationHandler || true == GPAL.PublishToConsole || true == GPAL.PublishToDebug || true == loggerWants)
2422 {
2423 var st = new StackTrace();
2424 var sf = st.GetFrame(1);
2425 string name = sf.GetMethod().Name;
2426 DateTime dateTime = DateTime.Now;
2427 string stackTrace = null != ex && true == GPAL.PublishStackTrace ? $"[{ex.StackTrace}]" : string.Empty;
2428
2429 if (name.Equals("MoveNext"))
2430 {
2431 // We're inside an async method
2432 name = sf.GetMethod().ReflectedType.Name
2433 .Split(new char[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries)[0];
2434 }
2435
2436 string outMessage = $"[{dateTime.ToString("yyyy-MM-dd HH:mm:ss.ffff")}][{gPALEventType}][{name}]: {msg}";
2437 Bitmap ss = null;
2438
2439 // NOTE: we now only capture a screen image on error or exception
2440 if (GPALEventType.ERROR == gPALEventType || GPALEventType.EXCEPTION == gPALEventType)
2441 if (false == inPublishEvent && false == handlingTermination)
2442 {
2443 try
2444 {
2445 inPublishEvent = true; // avoid reentrant calls from events published in different paths call;ing capture screen
2446 if (true == gpalSettings.TakeEventScreenshot)
2447 if (typeof(Browser.Browser) == gPALObject?.GetType())
2448 ((ImageHelper)GPAL.ImageHelper).CaptureScreen(gPALObject).ToBitmap(out ss);
2449 else
2450 ((ImageHelper)GPAL.ImageHelper).CaptureScreen().ToBitmap(out ss);
2451 }
2452 catch // (Exception ex2)
2453 {
2454 // Console.WriteLine($"Unable to get screenshot in PublishSimpleEvent. [{ex2.Message}]");
2455 // Debug.WriteLine($"Unable to get screenshot in PublishSimpleEvent. [{ex2.Message}]");
2456 // if we are using ottomagic, nothing will be started up to call capture-visible-tab, so ignore
2457 }
2458 finally
2459 {
2460 inPublishEvent = false;
2461 }
2462 }
2463
2464 GPALEventArgs args = new GPALEventArgs()
2465 {
2466 GPALEventType = gPALEventType,
2467 GPALObject = gPALObject,
2468 GPALObjectType = gPALObjectType,
2469 CurrentUOW = (Enums.GPALObjectType.UnitOfWork == gPALObjectType ? (UnitOfWork)gPALObject : null),
2470 Message = $"{name}: {msg}",
2471 ScreenShot = ss,
2472 ExceptionRaised = ex,
2473 Data = (Enums.GPALObjectType.Converter == gPALObjectType ? gPALObject : string.Empty),
2474 DateTimeStamp = dateTime
2475 };
2476
2477 // the same mask the early-out above uses, so a handler is offered what it asked for and no more
2478 if (true == handlerWants)
2479 {
2480 if (null != ex)
2481 ExceptionHandler?.Invoke(gPALObject, args);
2482 else
2483 InformationHandler?.Invoke(gPALObject, args);
2484 }
2485
2486 if (true == GPAL.PublishToConsole)
2487 {
2488 // NOTE: silent init is used by gpal rest api, we must never output anything on stdio
2489 if (gpalSettings.ConsoleEvents.HasFlag(gPALEventType) && Environment.GetEnvironmentVariable("GPAL_IS_SILENT") != "1")
2490 {
2491 Console.WriteLine(outMessage);
2492
2493 // the exception text and its stack trace are diagnostics, not the event. Our message already
2494 // says what failed, so turn DEBUG on when you also need to know how it failed underneath
2495 if (null != ex && gpalSettings.ConsoleEvents.HasFlag(GPALEventType.DEBUG))
2496 Console.WriteLine($"[{ex.Message}]{stackTrace}");
2497 }
2498 }
2499
2500 if (true == GPAL.PublishToDebug)
2501 {
2502 if (gpalSettings.DebugEvents.HasFlag(gPALEventType) && Environment.GetEnvironmentVariable("GPAL_IS_SILENT") != "1")
2503 {
2504 Debug.WriteLine(outMessage);
2505
2506 // see the console block above: exception text and stack trace ride on DEBUG
2507 if (null != ex && gpalSettings.DebugEvents.HasFlag(GPALEventType.DEBUG))
2508 Debug.WriteLine($"[{ex.Message}]{stackTrace}");
2509 }
2510 }
2511
2512 // the logger decides where this lands, a file or a database, and whether it wants this type at
2513 // all. the event's own type is handed over so the entry is labelled with what actually happened.
2514 // guarded because the logger's write publishes events of its own, and those would arrive straight
2515 // back here
2516 if (0 < GPAL.PublishToLoggers.Count && true == Enum.TryParse(gPALEventType.ToString(), out Enums.LogType logType)) // Enums. because selenium has a LogType too
2517 {
2518 if (null == loggersWriting)
2519 loggersWriting = new HashSet<IGPALLogger>();
2520
2521 // a copy, because a logger raising an event while it writes could attach another one
2522 foreach (IGPALLogger logger in GPAL.PublishToLoggers.ToList())
2523 {
2524 // this one is already writing, so this event is one it raised itself. handing it back is
2525 // how a logger feeds itself its own failures. every other logger still gets it: a
2526 // database that will not take a row says nothing about a file that will
2527 if (true == loggersWriting.Contains(logger))
2528 continue;
2529
2530 try
2531 {
2532 loggersWriting.Add(logger);
2533
2534 // through the class rather than the interface: Log is not something a workflow calls
2535 // any more, it is how an event becomes an entry
2536 GPALLogger writer = (GPALLogger)logger;
2537
2538 // the type goes in with the entry rather than being set on the logger first, so two
2539 // threads publishing at once cannot swap each other's severity between the calls
2540 writer.Log(msg, name, logType);
2541
2542 // the exception text rides on DEBUG here the same way it does in the console and
2543 // debug blocks above, so a logger that only wants exceptions is not handed them.
2544 // without it an EXCEPTION entry says what failed and never why, and a log file is
2545 // the only account of a run nobody watched
2546 if (null != ex)
2547 writer.Log($"[{ex.Message}]{stackTrace}", name, Enums.LogType.DEBUG);
2548 }
2549 catch
2550 {
2551 // one logger that cannot write is not the others' problem, and not worth the run
2552 }
2553 finally
2554 {
2555 loggersWriting.Remove(logger);
2556 }
2557 }
2558 }
2559
2560 // flush any pending output, following operations may block or delay output
2561 // also flush for user who might be outputting and not realizing they need to flush output. :)
2562 if (Environment.GetEnvironmentVariable("GPAL_IS_SILENT") != "1")
2563 {
2564 Console.Out.Flush();
2565 Debug.Flush();
2566 }
2567 }
2568 }
2569
2581 string title,
2582 IDictionary<(TRow row, TCol col), TValue> data,
2583 string cornerLabel = "",
2584 Func<TValue, string> formatCell = null,
2585 string emptyCell = "-")
2586 {
2587 if (null == data || 0 == data.Count)
2588 return;
2589
2590 formatCell = formatCell ?? (v => v?.ToString() ?? string.Empty);
2591
2592 var rows = data.Keys.Select(k => k.row).Distinct().OrderBy(r => r, Comparer<TRow>.Default).ToList();
2593 var cols = data.Keys.Select(k => k.col).Distinct().OrderBy(c => c, Comparer<TCol>.Default).ToList();
2594
2595 string Cell(TRow r, TCol c) => data.TryGetValue((r, c), out var v) ? formatCell(v) : emptyCell;
2596
2597 int rowHeaderWidth = cornerLabel.Length;
2598 foreach (var r in rows)
2599 rowHeaderWidth = Math.Max(rowHeaderWidth, (r?.ToString() ?? string.Empty).Length);
2600
2601 var colWidth = new Dictionary<TCol, int>();
2602 foreach (var c in cols)
2603 {
2604 int w = (c?.ToString() ?? string.Empty).Length;
2605 foreach (var r in rows)
2606 w = Math.Max(w, Cell(r, c).Length);
2607 colWidth[c] = w;
2608 }
2609
2610 var sb = new StringBuilder();
2611 sb.AppendLine();
2612 sb.AppendLine($"=== {title} ===");
2613
2614 sb.Append(cornerLabel.PadRight(rowHeaderWidth));
2615 foreach (var c in cols)
2616 sb.Append(" | " + (c?.ToString() ?? string.Empty).PadRight(colWidth[c]));
2617 sb.AppendLine();
2618
2619 sb.Append(new string('-', rowHeaderWidth));
2620 foreach (var c in cols)
2621 sb.Append("-+-" + new string('-', colWidth[c]));
2622 sb.AppendLine();
2623
2624 foreach (var r in rows)
2625 {
2626 sb.Append((r?.ToString() ?? string.Empty).PadRight(rowHeaderWidth));
2627 foreach (var c in cols)
2628 sb.Append(" | " + Cell(r, c).PadRight(colWidth[c]));
2629 sb.AppendLine();
2630 }
2631
2632 // NOTICE rather than INFO: a table is something the workflow asked to be shown, not GPAL narrating
2633 // itself, so it belongs with the workflow's own output and survives a subscription that excludes ours
2634 PublishSimpleEvent(GPALEventType.NOTICE, sb.ToString());
2635 }
2636
2641 public static void EmitRuntimeTable(string title, IDictionary<(BrowserType bt, AutomationEngine ae), TimeSpan> runtimes)
2642 => EmitTable(title + " runtime (min:sec.ms)", runtimes, "Browser",
2643 ts => $"{(int)ts.TotalMinutes}:{ts.Seconds:D2}.{ts.Milliseconds:D3}");
2644 #endregion <Helpers>
2645 #region <GPAL Settings>
2646 // due to the fluent nature, we have to return an object with the interface IAllowGPALSettings
2647 // these are implemented in GPAL but also in GPALSettings, the object we chain to
2648 // but we want our language syntax to start with GPAL.
2649 // and then chain quietly, from GPAL to GPALSettings.
2650 // Problem is static classes cannot inherit interfaces, so we have to return the setting object.
2651
2657 public static IAllowGPALSettings AppCallIfFound(Application.Application.CallIfDelegate callIfFound)
2658 {
2659 gpalSettings.AppCallIfFoundList.Add(callIfFound);
2660 return (IAllowGPALSettings)GPALSettings;
2661 }
2662
2667 public static IAllowGPALSettings AppCallIfNotFound(Application.Application.CallIfDelegate callIfNotFound)
2668 {
2669 gpalSettings.AppCallIfNotFoundList.Add(callIfNotFound);
2670 return (IAllowGPALSettings)GPALSettings;
2671 }
2672
2677 public static IAllowGPALSettings CallIfFound(Browser.Browser.CallIfDelegate callIfFound)
2678 {
2679 gpalSettings.CallIfFoundList.Add(callIfFound);
2680 return (IAllowGPALSettings)GPALSettings;
2681 }
2682
2687 public static IAllowGPALSettings CallIfNotFound(Browser.Browser.CallIfDelegate callIfNotFound)
2688 {
2689 gpalSettings.CallIfNotFoundList.Add(callIfNotFound);
2690 return (IAllowGPALSettings)GPALSettings;
2691 }
2692
2697 [GPALSettings(Description = "Print specified GPAL events to the console. [none (default)]")]
2698 public static IAllowGPALSettings WithPublishToConsole(GPALEventType eventType = GPALEventType.INFO | GPALEventType.WARNING | GPALEventType.ERROR | GPALEventType.EXCEPTION | GPALEventType.NOTICE | GPALEventType.CAUTION | GPALEventType.FAILURE)
2699 {
2700 GPALSettings.ConsoleEvents = eventType;
2701 if (GPALEventType.NONE == eventType)
2702 GPALSettings.PublishToConsole = false;
2703 else
2704 GPALSettings.PublishToConsole = true;
2705 return (IAllowGPALSettings)GPALSettings;
2706 }
2707
2712 [GPALSettings(Description = "Print specified GPAL events to the output window in VisualStudio. [none (default)]")]
2713 public static IAllowGPALSettings WithPublishToDebug(GPALEventType eventType = GPALEventType.INFO | GPALEventType.WARNING | GPALEventType.ERROR | GPALEventType.EXCEPTION | GPALEventType.NOTICE | GPALEventType.CAUTION | GPALEventType.FAILURE)
2714 {
2715 GPALSettings.DebugEvents = eventType;
2716 if (GPALEventType.NONE == eventType)
2717 GPALSettings.PublishToDebug = false;
2718 else
2719 GPALSettings.PublishToDebug = true;
2720 return (IAllowGPALSettings)GPALSettings;
2721 }
2722
2729 [GPALSettings(Description = "Publish GPAL events to a logger, which writes them to whatever it was given: a file or a database.")]
2731 {
2732 GPALSettings.WithPublishToLogger(logger);
2733 return (IAllowGPALSettings)GPALSettings;
2734 }
2735
2740 [GPALSettings(Description = "Stop publishing GPAL events to a logger that was previously given one.")]
2742 {
2743 GPALSettings.RemoveLogger(logger);
2744 return (IAllowGPALSettings)GPALSettings;
2745 }
2746
2761 [GPALSettings(Description = "Which GPAL events reach an attached Information or Exception handler. [All (default)]")]
2762 public static IAllowGPALSettings WithHandlerEvents(GPALEventType eventTypes)
2763 {
2764 GPALSettings.HandlerEvents = eventTypes;
2765 return (IAllowGPALSettings)GPALSettings;
2766 }
2767
2772 [GPALSettings(Description = "Print stack trace when PublishToConsole or PublishToDebug is true. [true (default)]")]
2773 public static IAllowGPALSettings WithPublishStackTrace(bool trueFalse = true)
2774 {
2775 GPALSettings.PublishStackTrace = trueFalse;
2776 return (IAllowGPALSettings)GPALSettings;
2777 }
2778
2787 [GPALSettings(Description = "Callback for failures a workflow can decide about, used by any browser without its own CallOnFail")]
2788 public static IAllowGPALSettings CallOnFail(Browser.Browser.CallOnFailDelegate callOnFail)
2789 {
2790 GPALSettings.CallOnFailHandlers.Add(callOnFail);
2791 return (IAllowGPALSettings)GPALSettings;
2792 }
2793
2799 [GPALSettings(Description = "Substitue string when data cannot be retrieved for an element [##UNABLE TO GET## (default)]")]
2800 public static IAllowGPALSettings WithDefaultErrorPlaceholder(string message = "##UNABLE TO GET##")
2801 {
2802 GPALSettings.ErrorPlaceholder = message;
2803 return (IAllowGPALSettings)GPALSettings;
2804 }
2805
2811 [GPALSettings(Description = "Set the default timeout for waiting for a download to start.")]
2813 {
2814 GPALSettings.DownloadTimeoutInSec = seconds;
2815 return (IAllowGPALSettings)GPALSettings;
2816 }
2817
2825 [GPALSettings("DO NOT use GPAL fallback actions on failure, just fail. Usually involves javascript recovery attempt. [false (default)")]
2826 public static IAllowGPALSettings WithNoFallbackActions(bool trueFalse = true)
2827 {
2828 GPALSettings.NoFallbackRecoveryActions = trueFalse;
2829 return (IAllowGPALSettings)GPALSettings;
2830 }
2831
2836 public static IAllowGPALSettings WithAllThatMatch(int matchCount = int.MaxValue)
2837 {
2838 GPALSettings.WithAllThatMatch = matchCount;
2839 return (IAllowGPALSettings)GPALSettings;
2840 }
2841
2848 public static IAllowGPALSettings WithAutoUpdateWebDriver(bool trueFalse = true)
2849 {
2850 GPALSettings.AutoUpdateWebDriver = trueFalse;
2851 return (IAllowGPALSettings)GPALSettings;
2852 }
2853
2859 public static IAllowGPALSettings WithAutomationEngine(AutomationEngine automationEngine)
2860 {
2861 GPALSettings.AutomationEngine = automationEngine;
2862 return (IAllowGPALSettings)GPALSettings;
2863 }
2864
2871 [GPALSettings(Description = "Global location for browser drivers. [./ (default)(same dir as exe)]")]
2872 public static IAllowGPALSettings WithDriverLocation(string driverPath)
2873 {
2874 GPALSettings.DriverLocation = FileHelper.EnsureDirectoryEndsWithBackslash(driverPath);
2875 return (IAllowGPALSettings)GPALSettings;
2876 }
2877
2885 [GPALSettings(Description = "Where GPAL writes files it created itself and nobody asked to keep. [GPALfilesSafeToDelete beside the exe (default)]")]
2886 public static IAllowGPALSettings WithTempDirectory(string tempPath)
2887 {
2888 GPALSettings.TempDirectory = FileHelper.EnsureDirectoryEndsWithBackslash(tempPath);
2889 return (IAllowGPALSettings)GPALSettings;
2890 }
2891
2896 internal static string TempDirectory()
2897 {
2898 string tempPath = true == string.IsNullOrWhiteSpace(GPALSettings.TempDirectory)
2899 ? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "GPALfilesSafeToDelete") + @"\"
2900 : GPALSettings.TempDirectory;
2901
2902 if (false == Directory.Exists(tempPath))
2903 Directory.CreateDirectory(tempPath);
2904
2905 return tempPath;
2906 }
2942 [GPALSettings(Description = "Intercharacter typing delay ceiling [0ms (default), no pacing]")]
2943 public static IAllowGPALSettings WithTypingDelay(int delayInTicks)
2944 {
2945 GPALSettings.WithTypingDelay(delayInTicks);
2946 return (IAllowGPALSettings)GPALSettings;
2947 }
2948
2960 [GPALSettings(Description = "Image matching similarity percentage. [80 (default)]")]
2961 public static IAllowGPALSettings WithImageMatchingPercentage(int matchPercent)
2962 {
2963 GPALSettings.WithImageMatchingPercentage(matchPercent);
2964 return (IAllowGPALSettings)GPALSettings;
2965 }
2966
2979 [GPALSettings(Description = "Simulate moving the mouse for every action (implies .WithHardware). [false (default)]")]
2980 public static IAllowGPALSettings WithSimulateMouseMovement(bool trueFalse = true)
2981 {
2982 GPALSettings.SimulateMouse = trueFalse;
2983 return (IAllowGPALSettings)GPALSettings;
2984 }
2985
3002 [GPALSettings(Description = "Stop workflow if any selector is not found")]
3003 public static IAllowGPALSettings WithStopOnNotFound(bool trueFalse = true)
3004 {
3005 GPALSettings.StopOnNotFound = trueFalse;
3006 return (IAllowGPALSettings)GPALSettings;
3007 }
3008
3009 [GPALSettings(Description = "Set the default location to store temporary profiles if browser.WithProfileDataDirectory is not used. default [C:\\profiles]")]
3010 public static IAllowGPALSettings WithTempProfileDirectory(string tempProfileDirectory)
3011 {
3012 GPALSettings.TempProfileDirectory = tempProfileDirectory;
3014 }
3015
3029 [GPALSettings(Description = "Handler to receive GPAL Exception events")]
3030 public static IAllowGPALSettings WithExceptionHandler(EventHandler<GPALEventArgs> eventHandler)
3031 {
3032 ExceptionEventHandler = eventHandler;
3033 Exception ex = new Exception("Welcome Exception :)");
3034 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION, $"Welcome to GPAL [{Version}] Exception Channel", null, Enums.GPALObjectType.None, ex);
3035 return (IAllowGPALSettings)GPALSettings;
3036 }
3037
3043 [GPALSettings(Description = "Print GPAL published information or exception messages to the output window in VisualStudio. [true (default)]")]
3044 public static IAllowGPALSettings WithPromptBeforeUnexpectedExit(bool trueFalse = true)
3045 {
3046 GPALSettings.PromptBeforeUnexpectedExit = trueFalse;
3047 return (IAllowGPALSettings)GPALSettings;
3048 }
3049
3061 [GPALSettings(Description = "Handler to receive GPAL Information events")]
3062 public static IAllowGPALSettings WithInformationHandler(EventHandler<GPALEventArgs> eventHandler)
3063 {
3064 InformationEventHandler = eventHandler;
3065 GPAL.PublishSimpleEvent(Enums.GPALEventType.INFO, $"Welcome to GPAL [{Version}] Information Channel");
3066 return (IAllowGPALSettings)GPALSettings;
3067 }
3068
3080 public static IAllowGPALSettings WithWaitFor(int waitForInTicks)
3081 {
3082 GPALSettings.WaitForInMs = waitForInTicks;
3083 return (IAllowGPALSettings)GPALSettings;
3084 }
3085
3086 public static IAllowGPALSettings WithTakeEventScreenshot(bool trueOrFalse)
3087 {
3088 GPALSettings.TakeEventScreenshot = trueOrFalse;
3090 }
3091
3092
3093 #endregion <GPAL Settings>
3094 #region <Private>
3095 // nmot sure why i coded this, may have been when i started to code the driver update logic but determined a better way to get the version...
3096 private static void CheckVersions()
3097 {
3098 if (string.Empty.Equals(tmpBrowser?.BrowserVersion))
3099 {
3100 BrowserType browserType = tmpBrowser.BrowserType;
3101
3102 if (BrowserType.Chrome == browserType)
3103 {
3104 DriverHelper.GetChromeVersion(out tmpBrowser.BrowserSettings.Version);
3105 }
3106 else if (BrowserType.Edge == browserType)
3107 {
3108 DriverHelper.GetEdgeVersion(out tmpBrowser.BrowserSettings.Version);
3109 }
3110 else if (BrowserType.FireFox == browserType)
3111 {
3112 DriverHelper.GetFirefoxVersion(out tmpBrowser.BrowserSettings.Version);
3113 }
3114
3115 //ICapabilities capabilities = ((RemoteWebDriver)tmpBrowser.BrowserDriver).Capabilities;
3116 //tmpBrowser.BrowserVersion = capabilities.GetCapability("browserVersion")?.ToString();
3117
3118 // Common WebDriver Capabilities:
3119 // "browserName" - Name of the browser (e.g., "chrome", "firefox", "edge")
3120 // "browserVersion" - Version of the browser (e.g., "120.0.6099.110")
3121 // "platformName" - OS platform ("windows", "linux", "macOS")
3122 // "acceptInsecureCerts" - Whether SSL cert warnings are ignored (true/false)
3123 // "setWindowRect" - Whether the WebDriver can resize the window (true/false)
3124 //
3125 // Chrome-Specific:
3126 // "goog:chromeOptions" - ChromeDriver options (e.g., args, binary path)
3127 // "goog:loggingPrefs" - Logging preferences (e.g., performance, console logs)
3128 //
3129 // Edge-Specific:
3130 // "ms:edgeOptions" - EdgeDriver options (e.g., args, headless mode)
3131 // "ms:edgeChromium" - Whether Edge is running on Chromium (true/false)
3132 //
3133 // Firefox-Specific:
3134 // "moz:firefoxOptions" - FirefoxDriver options
3135 // "moz:headless" - Whether running in headless mode (true/false)
3136 //
3137 // Remote/Grid Execution:
3138 // "webdriver.remote.sessionid" - Session ID for remote WebDriver
3139 // "timeouts" - Dictionary of timeouts (implicit, pageLoad, script)
3140 }
3141 }
3142 private static void SetCatchUncaughtExceptions()
3143 {
3144 if (false == UncaughtExceptionHandlerSet)
3145 {
3146 CheckVersions();
3147
3148 try
3149 {
3150 // Set the unhandled exception mode to force errors to go through our handler.
3151 System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
3152
3153 // Add the event handler for handling thread exceptions to the event.
3154 AppDomain.CurrentDomain.UnhandledException +=
3155 new UnhandledExceptionEventHandler(UncaughtExceptionHandler);
3156
3157 // Hook cleanup handlers
3158 Console.CancelKeyPress += OnCancelKeyPress;
3159 AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
3160
3161 }
3162 catch (Exception ex)
3163 {
3164 PublishSimpleEvent(GPALEventType.EXCEPTION, $"Unable to set uncaught exception handler", null, Enums.GPALObjectType.None, ex);
3165 }
3166
3167 UncaughtExceptionHandlerSet = true;
3168 }
3169 }
3170
3171 private static bool handlingTermination = false;
3172 private static void UncaughtExceptionHandler(object sender, UnhandledExceptionEventArgs args)
3173 {
3174 Exception e = (Exception)args.ExceptionObject;
3175
3176 if (false == handlingTermination)
3177 {
3178 handlingTermination = true;
3179
3180 // the message is what the reader needs. the exception is not passed on, because PublishSimpleEvent
3181 // emits ex.StackTrace from it and that is noise on top of a message we already have.
3182 bool ours = e is GPALException;
3183 string reason = true == ours ? "GPAL exception" : "uncaught exception";
3184
3185 if (ConsoleEvents.HasFlag(GPALEventType.WARNING))
3186 {
3187 PublishSimpleEvent(GPALEventType.WARNING, $">>> Caught {reason}: [{e.Message}][{e.StackTrace}]", null, Enums.GPALObjectType.None);
3188 PublishSimpleEvent(GPALEventType.WARNING, $">>> [{reason}] Cleaning up before terminating.", null, GPALObjectType.None);
3189 }
3190 else if (ConsoleEvents.HasFlag(GPALEventType.CAUTION))
3191 {
3192 PublishSimpleEvent(GPALEventType.CAUTION, $">>> Caught {reason}: [{e.Message}][{e.StackTrace}]", null, Enums.GPALObjectType.None);
3193 PublishSimpleEvent(GPALEventType.CAUTION, $">>> [{reason}] Cleaning up before terminating.", null, GPALObjectType.None);
3194 }
3195
3196 if (false == PublishToConsole)
3197 GPAL.PublishSimpleEvent(GPALEventType.NOTICE, $"Terminating due to [{reason}]: [{e.Message}]");
3198
3199 if (false == PublishToDebug)
3200 Debug.WriteLine($"Terminating due to {reason}: [{e.Message}]");
3201
3202 CleanupAndExit(true == ours ? "GPAL Exception" : "Unhandled Exception");
3203 }
3204 }
3205
3206 private static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
3207 {
3208 //PublishSimpleEvent(GPALEventType.INFO, $">>> Detected Ctrl+C or Ctrl+Break.");
3209
3210 if (false == handlingTermination)
3211 {
3212 handlingTermination = true;
3213
3214 e.Cancel = true; // Prevents immediate termination
3215
3216 if (false == PublishToConsole)
3217 GPAL.PublishSimpleEvent(GPALEventType.NOTICE, $"Terminating due to Ctrl+C or Ctrl+Break.");
3218
3219 if (false == PublishToDebug)
3220 Debug.WriteLine($"Terminating due to Ctrl+C or Ctrl+Break.");
3221
3222 CleanupAndExit("Ctrl+C or Ctrl+Break");
3223 }
3224 }
3225
3226 private static void OnProcessExit(object sender, EventArgs e)
3227 {
3228 // PublishSimpleEvent(GPALEventType.INFO, $">>> Detected process exit or console X.");
3229
3230 if (false == handlingTermination)
3231 {
3232 handlingTermination = true;
3233
3234 if (false == PublishToConsole)
3235 GPAL.PublishSimpleEvent(GPALEventType.NOTICE, $"Terminating due to Process Exit or console X.");
3236
3237 if (false == PublishToDebug)
3238 Debug.WriteLine($"Terminating due to Process Exit or console X.");
3239
3240 CleanupAndExit("Process Exit or console X");
3241 }
3242 }
3243
3244 private static bool _cleanupDone = false;
3245
3246 internal static void CleanupAndExit(string reason)
3247 {
3248 if (false == _cleanupDone) // in case we get multiple kill signals somehow, only run this once..
3249 {
3250 _cleanupDone = true;
3251 try
3252 {
3253 if (ConsoleEvents.HasFlag(GPALEventType.INFO))
3254 PublishSimpleEvent(GPALEventType.INFO, $">>> [{reason}] Cleaning up before terminating.", null, GPALObjectType.None);
3255 else if (ConsoleEvents.HasFlag(GPALEventType.NOTICE))
3256 PublishSimpleEvent(GPALEventType.NOTICE, $">>> [{reason}] Cleaning up before terminating.", null, GPALObjectType.None);
3257
3258 if (false == PublishToConsole)
3259 GPAL.PublishSimpleEvent(GPALEventType.NOTICE, $"[{reason}] Cleaning up before terminating.");
3260
3261 if (false == PublishToDebug)
3262 Debug.WriteLine($"[{reason}] Cleaning up before terminating.");
3263
3264 BrowserHelper.KillAllRunningProcesses(true, null);
3265
3266 // the desktop itself lives until the last window on it is gone, so this only gives up our
3267 // handle rather than closing anything still running there
3268 GenerallyPositive.Browser.HiddenDesktop.CloseAll();
3269 }
3270 catch (Exception ex)
3271 {
3272 PublishSimpleEvent(GPALEventType.EXCEPTION, $"Cleanup error", null, GPALObjectType.None, ex);
3273 }
3274
3275 // Stop timer and publish end time + total runtime
3276 _appTimer.Stop();
3277 string endTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
3278 TimeSpan totalRuntime = _appTimer.Elapsed;
3279 string formattedRuntime = $"{totalRuntime.Hours:D2}:{totalRuntime.Minutes:D2}:{totalRuntime.Seconds:D2}.{totalRuntime.Milliseconds:D3}";
3280
3281 if (ConsoleEvents.HasFlag(GPALEventType.INFO))
3282 {
3283 PublishSimpleEvent(GPALEventType.INFO, $">>> Application End Time: [{endTime}] <<<", null, GPALObjectType.None);
3284 PublishSimpleEvent(GPALEventType.INFO, $">>> Total Runtime: [{formattedRuntime}] <<<", null, GPALObjectType.None);
3285 }
3286 else if (ConsoleEvents.HasFlag(GPALEventType.NOTICE))
3287 {
3288 PublishSimpleEvent(GPALEventType.NOTICE, $">>> Application End Time: [{endTime}] <<<", null, GPALObjectType.None);
3289 PublishSimpleEvent(GPALEventType.NOTICE, $">>> Total Runtime: [{formattedRuntime}] <<<", null, GPALObjectType.None);
3290 }
3291
3292 // published and nothing else, the same as the start time and the version are at startup. writing
3293 // these directly when console publishing was off said them anyway, which is the one thing turning
3294 // it off asked us not to do
3295
3296 if (true == GPALSettings.PromptBeforeUnexpectedExit)
3297 {
3298 GPAL.PublishSimpleEvent(GPALEventType.NOTICE, "Press any key to exit...");
3299 Debug.WriteLine("Press any key to exit...");
3300 Console.ReadKey();
3301 }
3302 }
3303
3304 // normal exit, return 0
3305 if (true == reason.Equals("Process Exit or console X"))
3306 Environment.Exit(0);
3307
3308 Environment.Exit(0xdead);
3309 }
3310 #endregion <Private>
3311 }
3312}
Application object that contains the fluent methods to create your Application workflow....
Browser object that contains the fluent methods to create your Browser workflow. Instatiated using G...
Definition Browser.cs:68
Represents a URL with optional pre-navigation storage cleanup / inspection actions....
Definition GPALUrl.cs:53
Fluent YouTube video uploader. Entry point: GPAL.YouTube. Upload phase: WithCredentials -> WithTitle ...
Definition YouTube.cs:124
File-side plumbing behind the fluent chain: writing a unit of work's data out in a delimited format,...
Definition FileHelper.cs:55
static string EnsureDirectoryEndsWithBackslash(string directoryPath)
Adds a trailing backslash to a directory path when it does not already have one, so the path can be c...
Used to pass objects of interest when publishing an Information or Exception Channel message NOTE: P...
Definition GPAL.cs:293
List< GPALElement > WebElements
List of WebElements returned for the given Browser Selector.
Definition GPAL.cs:335
IWebDriver BrowserDriver
The browser driver instantiated and used to connect to the browser.
Definition GPAL.cs:323
IGPALGrid< string > Tokens
GPALGrid [rows/columns] of tokens being used to fill input.
Definition GPAL.cs:352
string Message
The Message being published to the Information/Exception channel.
Definition GPAL.cs:311
GPALElement GPALElement
The current web element.
Definition GPAL.cs:331
SelectorSettings.SelectorPathEntry SelectorPathEntry
The SelectorPathEntry is the current path (css, xpath, text, image, etc) being evaluated/used to find...
Definition GPAL.cs:348
SqlCommand SqlCommand
SqlCommand which threw an exception.
Definition GPAL.cs:360
Exception ExceptionRaised
The Exception being published on the Exception channel.
Definition GPAL.cs:315
Image ScreenShot
Every message is accompanied by an automatic screenshot in case this additional information provides ...
Definition GPAL.cs:319
List< IGPALGrid< string > > TokenList
List of Grids associated with GPALFile.
Definition GPAL.cs:356
UnitOfWork CurrentUOW
The Current Unit of Work for the GPALObject supplied. GPALObject (depending upon context) might also...
Definition GPAL.cs:307
Selector Selector
The current Selector being used.
Definition GPAL.cs:327
DateTime DateTimeStamp
The date/time this event is published.
Definition GPAL.cs:368
AutomationElement AutomationElement
The current AutomationElement.
Definition GPAL.cs:339
List< GPALAutomationElement > AutomationElements
List of AutomationElements returned for the given Application Selector.
Definition GPAL.cs:343
Enums.GPALObjectType GPALObjectType
GPALObjectType enumeration: None, Application, Browser, GPALForm, UnitOfWork.
Definition GPAL.cs:302
dynamic Data
Any type of data that does not fite in the above, converter data, for example.
Definition GPAL.cs:364
dynamic GPALObject
Current context's Application, Browser, GPALForm, UnitOfWork object.
Definition GPAL.cs:298
Class to define database usage. Currently only used for input from a table, sql or stored procedure....
Provides fluent, file-based automation of Excel workbooks: opening one or more workbooks (including w...
Definition GPALExcel.cs:46
GPAL File object instantied with GPAL.File Used to load tokens into a GPALGrid [rows/columns].
Definition GPALFile.cs:36
List< string > Filenames
Get the list of filenames.
Definition GPALFile.cs:536
A single item shared by GPAL menu bars and toolbars. Because it derives from GPALControl it speaks th...
A GPAL button instantiated with GPAL.Button for use on GPAL forms. Callback EventHandler is invoked ...
A GPAL chart instantiated with GPAL.Chart for use on GPAL forms. Callback EventHandler is invoked on...
A GPAL checkbox instantiateds with GPAL,Checkbox for use on GPAL forms. Callback EventHandler is inv...
A dropdown list (ComboBox) for selecting one option from a predefined set of choices....
A grid control instantiated with GPAL.DataGridView for use on GPAL forms, displaying the contents of ...
A date/time picker control instantiated with GPAL.DateTimePicker for use on GPAL forms.
A file/folder selector control instantiated with GPAL.FileSelector for use on GPAL forms....
Form object that contains the fluent methods to create interactive forms. Instatiated using GPAL....
Definition GPALForm.cs:64
A titled box around a set of controls, instantiated with GPAL.GroupBox and laid out like any other co...
A GPAL single line input instantiated with GPAL.Input for use on GPAL forms. Callback EventHandler i...
A GPAL lable instantiated with GPAL.Label for use on GPAL forms. Callback EventHandler is invoked on...
A list view control capable of displaying data in Details, List, LargeIcon, SmallIcon,...
A menu bar docked to the top of a GPAL form. Open a top-level menu with WithMenu(string),...
A numeric up/down spinner control instantiated with GPAL.NumericUpDown for use on GPAL forms.
A progress indicator control that shows completion percentage or indeterminate activity....
A GPAL radiobutton instantiated with GPAL.RadioButton for use GPAL forms. Callback EventHandler is i...
A read-only display control for rendering simple Markdown - headings (#, ##, ###),...
Marks where one pane of a GPALSplitter ends and the other begins, added to the splitter with GPAL....
Two panes with a bar between them, instantiated with GPAL.Splitter and laid out like any other contro...
A status bar (usually docked at the bottom of the form) that displays real-time messages,...
A GPAL tab instantiated with GPAL.Tab for use on GPAL forms. Callback EventHandler is invoked on the...
A GPAL multiline textare instantiated with GPAL.TextArea for use on GPAL forms. Callback EventHandle...
A toolbar docked below the menu bar, containing GPALBarItem buttons and optional separators.
A tree view control instantiated with GPAL.TreeView for use on GPAL forms.
static byte VK_ALT
Alt key virtual keycode (VK) for use with keybd_event. https://docs.microsoft.com/en-us/windows/win32...
Definition GPAL.cs:58
static IAllowProgressBarControlSettings ProgressBar
A progress bar control for GPAL forms. Displays task completion percentage or indeterminate (marquee...
Definition GPAL.cs:1760
static IAllowMenuBarControlSettings MenuBar
A menu bar docked to the top of a GPAL form. Open a top-level menu with WithMenu("TopMenu"),...
Definition GPAL.cs:1868
static IAllowGPALSettings WithNoFallbackActions(bool trueFalse=true)
GPAL has fallback alternatives for actions should they fail, generally applies to selenium....
Definition GPAL.cs:2826
static IAllowGPALSettings WithHandlerEvents(GPALEventType eventTypes)
Which event types reach the handlers given to WithInformationHandler and WithExceptionHandler....
Definition GPAL.cs:2762
static byte VK_END
End key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:78
static byte VK_F10
F10 key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:66
static IAllowGPALSettings RemoveLogger(IGPALLogger logger)
Stops publishing events to a logger.
Definition GPAL.cs:2741
static Browser.IHiddenDesktops HiddenDesktop
Every hidden desktop this process has made, and the screen itself. Peek puts one on the screen and pu...
Definition GPAL.cs:2146
static IAllowRichTextBoxControlSettings RichTextBox
Instantiates a new GPALRichTextBox for use on a GPAL form. Renders Markdown (headings,...
Definition GPAL.cs:2054
static IAllowApplicationOpenOrParameters Application
Instantiates a new fluent Application object.
Definition GPAL.cs:586
static GPALSpliter SplitRight
Marks the end of a splitter's left pane. Everything added after it is the right pane.
Definition GPAL.cs:1993
static IAllowControlSettingsAndChartSettings Chart
Instantiates a new GPALChart for use on GPAL forms. Callback EventHandler is invoked on the GPALChar...
Definition GPAL.cs:1404
static IAllowLoggingSettings Logger
New GPAL Logger.
Definition GPAL.cs:534
static IAllowSelectorSettings Selector
Instantiates a new fluent GPAL Selector object used to locate an element. Selectors are for browsers...
Definition GPAL.cs:693
static IAllowGraphicSettings CaptureScreen(Browser.IBrowser browser)
Take a screenshot.
Definition GPAL.cs:2353
static IAllowGPALSettings WithPublishToDebug(GPALEventType eventType=GPALEventType.INFO|GPALEventType.WARNING|GPALEventType.ERROR|GPALEventType.EXCEPTION|GPALEventType.NOTICE|GPALEventType.CAUTION|GPALEventType.FAILURE)
Indicate which event types to print to the console. Default is none.
Definition GPAL.cs:2713
static IAllowControlSettingsAndEnabled TextArea
Instantiates a new GPALTextArea for use on a GPAL form. Callback EventHandler is invoked on the GPAL...
Definition GPAL.cs:2020
static IAllowConverterInput Converter
New GPAL Convertor.
Definition GPAL.cs:560
static byte VK_LMENU
Left Alt key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:130
static IAllowGPALSettings WithInformationHandler(EventHandler< GPALEventArgs > eventHandler)
Set the handler to receive Information messages from GPAL which you might like to know about....
Definition GPAL.cs:3062
static void EmitTable< TRow, TCol, TValue >(string title, IDictionary<(TRow row, TCol col), TValue > data, string cornerLabel="", Func< TValue, string > formatCell=null, string emptyCell="-")
Emits an ASCII table for any 2-axis keyed dictionary as a single INFO event: row keys down the side,...
Definition GPAL.cs:2580
static GPALSpliter SplitBottom
Marks the end of a splitter's top pane. Everything added after it is the bottom pane.
Definition GPAL.cs:1996
static IAllowControlSettingsPlaceholderEnabledAndPassword Input
Instantiates a new one-line GPALInput control for use on a GPAL form. Callback EventHandler is invok...
Definition GPAL.cs:1626
static Browser.IAllowElementSetting ElementAssistant(Selector selector)
Instantiates a new Browser ElementAssistant object with fluent methods to help you manipulate WebElem...
Definition GPAL.cs:1233
static byte VK_BACK
Backspace key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:146
static IAllowGPALSettings CallIfFound(Browser.Browser.CallIfDelegate callIfFound)
Global Browser CallIfFound handler called for every selector in every Unit of Work.
Definition GPAL.cs:2677
static IAllowGPALSettings WithPublishStackTrace(bool trueFalse=true)
Print the stack trace along with the published GPAL message.
Definition GPAL.cs:2773
static IAllowToolbarControlSettings Toolbar
A toolbar docked below the menu bar, holding GPAL.BarItem buttons and optional separators.
Definition GPAL.cs:1921
static IAllowDateTimePickerControlSettings DateTimePicker
Instantiates a new GPALDateTimePicker for use on a GPAL form. Callback EventHandler is invoked on th...
Definition GPAL.cs:1543
static uint KEYEVENTF_EXTENDEDKEY
The extended-key flag indicates whether the keystroke message originated from one of the additional k...
Definition GPAL.cs:161
static IAllowDatabaseSettings Database
Instantiates a new fluent Database SETTINGS object. This data object defines settings for use by ....
Definition GPAL.cs:660
static byte VK_RWIN
Right Windows key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:122
static Application.IAllowForSelector ElementAssistant(Application.Application application)
Instantiates a new Application ElementAssistant with fluent methods to help you manipulate Automation...
Definition GPAL.cs:1264
static IAllowSplitterControlSettings Splitter
Instantiates a new GPALSplitter for use on a GPAL form: two panes with a bar between them,...
Definition GPAL.cs:1984
static IAllowFormSettings Form
Instantiates a new GPALForm for creating simple interactive user interfaces.
Definition GPAL.cs:1302
static IAllowGridActions< string > Grid
New GPALGrid<string></string> (rows/columns).
Definition GPAL.cs:521
static IAllowControlSettingsEnabledAndChecked RadioButton
Instantiates a new GPALRadioButton for use on a GPAL form. Callback EventHandler is invoked on the G...
Definition GPAL.cs:1809
static IAllowComboBoxControlSettings ComboBox
Instantiates a new GPALComboBox for use on a GPAL form. Callback(s) set via WhenSelectedDo are invok...
Definition GPAL.cs:1486
static IAllowBarItemSettings BarItem
A single item shared by menu bars and toolbars: text, optional image, optional shortcut key,...
Definition GPAL.cs:1895
static string OAuthTokenUrl
Where an OAuth redirect lands and the token is read back from, e.g. http://localhost:3117/....
Definition GPAL.cs:2163
static byte VK_RETURN
Enter/Return key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:98
static byte VK_SCROLL
Scroll Lock key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:126
static byte VK_DOWN
Down Arrow key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:86
static IAllowDataGridViewControlSettings DataGridView
Instantiates a new GPALDataGridView for use on a GPAL form. Callback EventHandler is invoked on the ...
Definition GPAL.cs:1514
static IAllowGPALSettings AppCallIfFound(Application.Application.CallIfDelegate callIfFound)
Global Browser CallIfFound handler called for every selector in every Unit of Work.
Definition GPAL.cs:2657
static IAllowRequestSettings Request
Start describing an API request for .Fetch to issue from inside the page, so it carries the session t...
Definition GPAL.cs:724
static IAllowRESTEndpoint RESTClient
Instantiate a new fluent RESTClient.
Definition GPAL.cs:914
static IAllowGPALSettings WithPublishToLogger(IGPALLogger logger)
Print the published information or exception messages to the supplied database. Uses Database Create ...
Definition GPAL.cs:2730
static IAllowGPALSettings CallOnFail(Browser.Browser.CallOnFailDelegate callOnFail)
Add a handler to be called when something fails that the workflow is in a position to decide about,...
Definition GPAL.cs:2788
static byte VK_CONTROL_LEFT
Left Control key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:110
static byte VK_SHIFT_RIGHT
Right Shift key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:102
static string MyMethodName()
Returns the calling methods name (used in Information/Exception messages). Instead of harcoding meth...
Definition GPAL.cs:2374
static byte VK_SHIFT_LEFT
Left Shift key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:106
static IAllowControlSettingsAndFontSettings Label
Instantiates a new GPALLabel for use on a GPAL form. Callback EventHandler is invoked on the GPALLab...
Definition GPAL.cs:1658
static Selector CssSelector(string css, string selectorName=null)
Helper for shorthand notation.
Definition GPAL.cs:1026
static IAllowControlSettingsEnabledAndChecked Checkbox
Instantiates a new GPALCheckbox for use on a GPAL form. Callback EventHandler is invoked on the GPAL...
Definition GPAL.cs:1447
static IAllowGPALSettings WithExceptionHandler(EventHandler< GPALEventArgs > eventHandler)
Set the handler to receive handled Exception messages from GPAL. GPAL also sets an unhandled excepti...
Definition GPAL.cs:3030
static IAllowGPALSettings WithWaitFor(int waitForInTicks)
Global setting to wait for time in ticks for an element to show up. Applied to every element in every...
Definition GPAL.cs:3080
static IAllowGPALSettings AppCallIfNotFound(Application.Application.CallIfDelegate callIfNotFound)
Global Browser CallIfNotFound handler called for every selector in every Unit of Work.
Definition GPAL.cs:2667
static IAllowGPALSettings CallIfNotFound(Browser.Browser.CallIfDelegate callIfNotFound)
Global Browser CallIfNotFound handler called for every selector in every Unit of Work.
Definition GPAL.cs:2687
static IAllowTreeViewControlSettings TreeView
Instantiates a new GPALTreeView for use on a GPAL form. Callback EventHandler is invoked on the GPAL...
Definition GPAL.cs:2086
static byte VK_APPS
Application (Menu) key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:138
static byte VK_NEXT
Page Down key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:74
static void EmitRuntimeTable(string title, IDictionary<(BrowserType bt, AutomationEngine ae), TimeSpan > runtimes)
Convenience wrapper over EmitTable<TRow,TCol,TValue> for test-program bt/ae[/headless] loops: browser...
Definition GPAL.cs:2641
static IAllowGPALSettings WithAutoUpdateWebDriver(bool trueFalse=true)
Automatically download and unzip the web driver that matches the version of the browser being run....
Definition GPAL.cs:2848
static IAllowListVIewControlSettings ListView
Instantiates a new GPALListView for use on a GPAL form. Callback EventHandler is invoked on the GPAL...
Definition GPAL.cs:1698
static IAllowNumericUpDownControlSettings NumericUpDown
Instantiates a new GPALNumericUpDown for use on a GPAL form. Callback EventHandler is invoked on the...
Definition GPAL.cs:1729
static IAllowCredentialServiceType Credentials
Return a new credential manager.
Definition GPAL.cs:929
static IAllowGPALSettings WithAutomationEngine(AutomationEngine automationEngine)
Set the automation type to use for all browsers. NOTE: can be overridden at the selector level (to ch...
Definition GPAL.cs:2859
static IAllowEmailServerSettings Mail
New GPAL Mail handler.
Definition GPAL.cs:547
static IAllowGPALSettings WithStopOnNotFound(bool trueFalse=true)
Global setting instructing GPAL to terminate if any Selector cannot find any elements....
Definition GPAL.cs:3003
static IAllowGPALSettings WithPublishToConsole(GPALEventType eventType=GPALEventType.INFO|GPALEventType.WARNING|GPALEventType.ERROR|GPALEventType.EXCEPTION|GPALEventType.NOTICE|GPALEventType.CAUTION|GPALEventType.FAILURE)
Print the published information or exception messages to the console.
Definition GPAL.cs:2698
static IAllowGPALSettings WithImageMatchingPercentage(int matchPercent)
Sets the image matching percentage for image Selectors. .
Definition GPAL.cs:2961
static IAllowControlSettings Tab
Instantiates a new GPALTab for use on a GPAL form. Callback EventHandler is invoked on the GPALTab....
Definition GPAL.cs:1956
static byte VK_LWIN
Left Windows key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:118
static IAllowGPALSettings WithDefaultErrorPlaceholder(string message="##UNABLE TO GET##")
When GPAL is unable to retrieve data from an element, this message will be placed in the grid....
Definition GPAL.cs:2800
static IAllowGPALSettings WithDownloadTimeoutInSec(int seconds)
Set the default timeout time in seconds to wait for a download to start The default is 60 seconds.
Definition GPAL.cs:2812
static byte VK_UP
Up Arrow key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:90
static IAllowAIProvider AI
Entry point for the GPAL AI subsystem, providing a fluent interface for AI tasks.
Definition GPAL.cs:459
static byte VK_SPACE
Space bar key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:142
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2406
static byte VK_DELETE
Delete key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:62
static IAllowGPALSettings WithTempDirectory(string tempPath)
Where GPAL puts files it had to create but nobody asked to keep, such as a GPALFile given a url rathe...
Definition GPAL.cs:2886
static IAllowGPALSettings WithSimulateMouseMovement(bool trueFalse=true)
Global setting to simulate a human moving the mouse to the next point for all commands....
Definition GPAL.cs:2980
static IAllowStatusStripControlSettings StatusStrip
A status strip control (docked at the bottom) for GPAL forms. Displays real-time status messages,...
Definition GPAL.cs:1838
static IAllowControlSettingsEnabledAndDefault Button
Instantiates a new GPALButton for use on GPAL forms. Callback EventHandler is invoked on the GPALBut...
Definition GPAL.cs:1347
static IAllowDriveCredentials GoogleDrive
New GoogleDrive workflow.
Definition GPAL.cs:950
static IAllowGroupBoxControlSettings GroupBox
Instantiates a new GPALGroupBox for use on a GPAL form: a titled box around a set of controls,...
Definition GPAL.cs:1990
static byte VK_PRIOR
Page Up key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:70
static Browser.IAllowScheduledWorkflow Workflow
Runs whole workflows at once, each building and owning whatever it drives, so one can run Chrome on O...
Definition GPAL.cs:2122
static IAllowGPALSettings WithAllThatMatch(int matchCount=int.MaxValue)
Global value for selector matches so it's not set UOW at a time.
Definition GPAL.cs:2836
static byte VK_TAB
Tab key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:94
static uint KEYEVENTF_KEYUP
Indicates a keyup keystroke is being sent. For use with keybd_event.
Definition GPAL.cs:171
static IAllowGPALSettings WithDriverLocation(string driverPath)
The location of the browser drivers to be used for each GPAL.Browser instance. This can be overridde...
Definition GPAL.cs:2872
static IAllowBrowserTypeOrGoto Browser
Instantiates a new fluent Browser object.
Definition GPAL.cs:617
static IAllowGPALSettings WithTypingDelay(int delayInTicks)
Sets the ceiling for the inter-character typing delay used by SendString across every automation engi...
Definition GPAL.cs:2943
static IAllowGPALSettings WithPromptBeforeUnexpectedExit(bool trueFalse=true)
Prompt 'Press any key...' before terminating due to an uncaught exception. GPAL tries to handle all e...
Definition GPAL.cs:3044
static byte VK_HOME
Home key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:82
static IAllowFileSelectorControlSettings FileSelector
Instantiates a new GPALFileSelector for use on a GPAL form. Opens a standard file or folder selectio...
Definition GPAL.cs:1595
static byte VK_RMENU
Right Alt key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:134
static byte VK_CONTROL_RIGHT
Right Control key virtual keycode (VK) for use with keybd_event.
Definition GPAL.cs:114
static IAllowSpreadsheetSelection GoogleSheets
New Googlesheets workflow.
Definition GPAL.cs:942
static IAllowFileName File
Instantiates a new fluent File SETTINGS object. This data object defines settings for use by the ....
Definition GPAL.cs:508
Provides configurable logging to a file (in any format supported by GPALConverter) or to a GPALDataba...
Definition GPALLogger.cs:48
Provides a fluent API for configuring, sending, and receiving email via SMTP, IMAP,...
Definition GPALMail.cs:55
Describes an API request for .Fetch to issue from inside the page, so it carries the session the brow...
Provides comprehensive image capture, conversion, and matching capabilities for automation scenarios....
GPAL Selector used to locate Application and Browser elements. Instantiated with GPAL....
Definition Selector.cs:56
Browser.Browser Browser
Browser associated with this selector.
Definition Selector.cs:746
IAllowSelectorSettings WithXPath(string xPath)
Define XPath used to find elements Both.
Definition Selector.cs:180
An entry defining a selector locator, a selector path.
Everything revolves around the Unit of Work. A Unit of Work is defined as one or more selectors betw...
Definition UnitOfWork.cs:39
Starting interface - only allows setting the target URL.
Every hidden desktop this process has made, and the screen itself. Peek and Return move the screen,...