using ESuite.UI.E2E.Helpers; using ESuite.UI.E2E.Models; using ESuite.UI.E2E.Pages; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; namespace ESuite.UI.E2E.StepDefinitions { [Binding] public class AuditLogsStepDefinitions { private readonly ScenarioContext _scenarioContext; private readonly AutomationTestManagerHelper automationTestManagerHelper; private readonly ConfigHelper configHelper; public AuditLogsStepDefinitions(ScenarioContext scenarioContext) { _scenarioContext = scenarioContext; automationTestManagerHelper = new(_scenarioContext); configHelper = new(); } /// /// Step definition for checking Audit logs of a specific object type. /// /// The type of object for which Audit logs are being checked. /// Example: "Forms", "Sequence, "Glossary", "Custom Fields", "Domains", "Users" /// A table containing the expected Audit log entries. /// Example: If in column "EntityDisplayName" will be "scenarioDataInstance" /// we will use data from current scenario instance. [Then(@"I check (.*) Audit logs")] public void ThenICheckAuditLogs(string objectType, Table dataTable) { RetryHelper.Retry(() => { string objectName; string? objectTypeCF = null; var logs = dataTable.CreateSet(); foreach (var log in logs) { switch (log.Type) { case "Create": switch (objectType) { case "Forms": objectName = _scenarioContext["FormName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); break; case "Sequence": objectName = _scenarioContext["SequenceName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckAuditLogs(log.Type, objectType, _scenarioContext["SequenceCreated"].ToString()!, true); break; case "Glossaries": objectName = _scenarioContext["GlossaryName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type!, objectType, objectName, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; case "Glossary Item": objectName = _scenarioContext["GlossaryItemName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type!, objectType, objectName, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; case "Glossary Custom Field Value": objectName = _scenarioContext["GlossaryName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type); CheckPartialExistingAuditLogs(log.Type!, objectType, objectName, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; case "Custom Field": objectName = _scenarioContext["CustomFieldName"].ToString()!; objectTypeCF = objectType + " " + _scenarioContext["CustomFieldType"].ToString()!; CheckCustomFieldsAudits(objectTypeCF, objectName, _scenarioContext["CustomFieldType"].ToString()!, log); break; case "Client Domains": objectName = _scenarioContext["DomainName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); I.DebounceDelay(configHelper.DebounceDelayMilliseconds); CheckAuditLogs(log.Type, objectType, _scenarioContext["DomainCreated"].ToString()!); break; case "Users": objectName = _scenarioContext["UserName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); //To Do to change from CheckPartialExistingAuditLogsForCustomFields to CheckAuditLogs CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["UserCreated"].ToString()!, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; case "Sso Manager": objectName = _scenarioContext["SSOProviderName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckAuditLogs(log.Type, objectType, _scenarioContext["SSOProviderCreated"].ToString()!); break; case "Organisation": objectName = _scenarioContext["OrganisationName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["OrganisationCreated"].ToString()!); break; case "Site": objectName = _scenarioContext["SiteName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["SiteCreated"].ToString()!); break; case "Role": objectName = _scenarioContext["DomainRoleName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["RoleCreated"].ToString()!, true); break; case "UserRoles": NavigateToAuditLog(); FillAuditLogSearchFields(log.Type); CheckAuditLogs(log.Type, objectType, _scenarioContext["DomainRoleName"].ToString()!, true); break; case "Role Access": NavigateToAuditLog(); FillAuditLogSearchFields(log.Type); CheckAuditLogs(log.Type, objectType, _scenarioContext["DomainRoleName"].ToString()!, true); break; case "Mail Template": objectName = ""; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["DomainMailTemplateCreated"].ToString()!, () => [.. AuditPage.GetTextFromAuditLog(log.Type)], () => I.GetTextsFromElements(AuditPage.EntityDisplayName).AsEnumerable()); break; case "Specification": objectName = _scenarioContext["SpecificationName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["Specification"].ToString()!, () => [.. AuditPage.GetTextFromAuditLog(log.Type)], () => I.GetTextsFromElements(AuditPage.EntityDisplayName).AsEnumerable()); break; case "Form Field Instance": objectName = _scenarioContext["FormFieldInstance"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type); CheckPartialExistingAuditLogs(log.Type!, objectType, objectName, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; default: throw new NoSuchElementException($"This type of Log: {objectType} doesn't exist!"); } break; case "Update": switch (objectType) { case "Forms": //it's for checking version logs registered as create for each version log.Type = ""; objectName = _scenarioContext["FormName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckEditedForm(objectName); break; case "Sequence": objectName = _scenarioContext["SequenceName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckAuditLogs(log.Type, objectType, _scenarioContext["SequenceEdited"].ToString()!); break; case "Glossary": objectName = _scenarioContext["GlossaryName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); break; case "Custom Field": objectName = _scenarioContext["CustomFieldName"].ToString()!; objectTypeCF = objectType + " " + _scenarioContext["CustomFieldType"].ToString()!; CheckCustomFieldsAudits(objectType, objectName, _scenarioContext["CustomFieldType"].ToString()!, log); break; case "Client Domains": objectName = _scenarioContext["DomainName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckAuditLogs(log.Type, objectType, _scenarioContext["DomainEdited"].ToString()!); break; case "Users": objectName = _scenarioContext["UserName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); //To Do to change from CheckPartialExistingAuditLogsForCustomFields to CheckPartialExistingAuditLogs CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["UserNameEdited"].ToString()!, () => [.. AuditPage.GetTextFromAuditLog(log.Type)], () => I.GetTextsFromElements(AuditPage.EntityDisplayName).AsEnumerable()); break; case "Sso Manager": objectName = _scenarioContext["SSOProviderName"].ToString()!; CheckAuditOnEntity(objectType, objectName, log); CheckAuditLogs(log.Type, objectType, _scenarioContext["SSOProviderEdited"].ToString()!); break; case "Organisation": objectName = _scenarioContext["OrganisationName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["OrganisationEdited"].ToString()!); break; case "Site": objectName = _scenarioContext["SiteName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["SiteEdited"].ToString()!); break; case "Role": objectName = _scenarioContext["DomainRoleName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckAuditLogs(log.Type, objectType, _scenarioContext["DomainRoleName"].ToString()!); break; case "Mail Template": objectName = ""; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["DomainMailTemplateUpdated"].ToString()!, () => [.. AuditPage.GetTextFromAuditLog(log.Type)], () => I.GetTextsFromElements(AuditPage.EntityDisplayName).AsEnumerable()); break; case "Specification": objectName = _scenarioContext!["SpecificationName"].ToString()!; NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); CheckPartialExistingAuditLogs(log.Type, objectType, _scenarioContext["SpecificationEdited"].ToString()!, () => [.. AuditPage.GetTextFromAuditLog(log.Type)], () => I.GetTextsFromElements(AuditPage.EntityDisplayName).AsEnumerable()); break; case "Form Field Instance": objectName = _scenarioContext["FormFieldInstance"].ToString()!; NavigateToAuditLog(); I.FillField(AuditPage.TypeSearchInputField, log.Type); I.DebounceDelay(configHelper.DebounceDelayMilliseconds); CheckPartialExistingAuditLogs(log.Type!, objectType, objectName, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); break; default: throw new NoSuchElementException($"This type of entity: {objectType} doesn't exist!"); } break; case "Delete": CheckDeletedAuditLog(objectType, GetObjectNameFromScenarioContext(objectType), log); break; case "Purge": CheckPurgedAuditLog(objectType, GetObjectNameFromScenarioContext(objectType), log); break; default: throw new NoSuchElementException($"This type of Log: {log.Type} doesn't exist!"); } } }); } private string GetObjectNameFromScenarioContext(string objectType) { return objectType switch { "Forms" => _scenarioContext["FormName"].ToString()!, "Form Template" => _scenarioContext["FormName"].ToString()!, "Sequence" => _scenarioContext["SequenceName"].ToString()!, "Glossary" => _scenarioContext["GlossaryName"].ToString()!, "Custom Field" => _scenarioContext["CustomFieldName"].ToString()!, "Client Domains" => _scenarioContext["DomainName"].ToString()!, "Users" => _scenarioContext["UserName"].ToString()!, "Sso Manager" => _scenarioContext["SSOProviderName"].ToString()!, "Organisation" => _scenarioContext["OrganisationName"].ToString()!, "Site" => _scenarioContext["SiteName"].ToString()!, "Role" => _scenarioContext["DomainRoleName"].ToString()!, "Specification" => _scenarioContext["SpecificationName"].ToString()!, "UserRoles" => _scenarioContext["RoleUserCreated"].ToString()!, "Custom Field Text" => _scenarioContext["CustomFieldName"].ToString()!, _ => throw new NoSuchElementException($"This type of entity: {objectType} doesn't exist!") }; } private void CheckDeletedAuditLog(string objectType, string objectName, AuditData log) { NavigateToAuditLog(); FillAuditLogSearchFields(log.Type, objectName); var actualTiming = I.GetTextFromElement(AuditPage.TimingDisplay); var expectedTiming = _scenarioContext["Timing"].ToString(); var actualLogType = I.GetTextFromElement(AuditPage.AuditLogTypeOfEntity); var actualEntityDisplayName = I.GetTextFromElement(AuditPage.EntityDisplayName); var expectedEntityDisplayName = AutomationTestManagerHelper.AuditEntityDisplayName[objectType]; Assert.AreEqual(log.Type, actualLogType, $"Expected log type: {log.Type}, but found: {actualLogType}"); Assert.AreEqual(expectedEntityDisplayName, actualEntityDisplayName, $"Expected Entity Display Name: {expectedEntityDisplayName}, but found: {actualEntityDisplayName}"); Assert.AreEqual(expectedTiming, actualTiming, $"Expected Timing was: {expectedTiming}, but found: {actualTiming}"); Assert.IsTrue(I.IsVisibleNumberOfElements(AuditPage.LogLines, 1), "Number of visible log lines doesn't match: 1"); } private void CheckPurgedAuditLog(string objectType, string objectName, AuditData log) { NavigateToAuditLog(); FillAuditLogSearchFields(log.Type); CheckAuditLogs(log.Type!, objectType, objectName, true); } private static void NavigateToAuditLog() { BasicPage.ClickOnDropdownMenuItem("Audit Log", "Support"); I.WaitForElementVisibleAndClickable(AuditPage.TypeSearchInputField); } private void FillAuditLogSearchFields(string? logType = null, string? objectName = null) { if (string.IsNullOrEmpty(objectName)) { objectName = ""; } if (string.IsNullOrEmpty(logType)) { logType = ""; } I.FillField(AuditPage.DisplayNameSearchInputField, objectName); Console.WriteLine($"Filling audit log search fields: LogType='{logType}', ObjectName='{objectName ?? ""}'"); I.FillField(AuditPage.TypeSearchInputField, logType); I.DebounceDelay(configHelper.DebounceDelayMilliseconds); } public void CheckEditedForm(string objectName) { RetryHelper.Retry(() => { NavigateToAuditLog(); I.FillField(AuditPage.DisplayNameSearchInputField, objectName); int numberOfParentLogs = 1; int numberOfAllRelatedLogs = (int)_scenarioContext["FormVersion"]!; int expectedNumberOfAllLogLines = (int)_scenarioContext["FormVersion"]! + numberOfParentLogs; Assert.IsTrue( I.IsVisibleNumberOfElements(AuditPage.LogLines, expectedNumberOfAllLogLines), $"Number of all existing Logs: {numberOfParentLogs} doesn't match number of visible Logs"); I.Click(AuditPage.ParentLogButton); I.WaitTillInvisible(BasicPage.LoadingMessage); Assert.IsTrue( I.IsVisibleNumberOfElements(AuditPage.LogLines, numberOfParentLogs), $"Number of Parent Logs: {numberOfParentLogs} doesn't match number of visible Logs"); I.Click(AuditPage.AllRelatedLogsButton); I.WaitTillInvisible(BasicPage.LoadingMessage); Assert.IsTrue( I.IsVisibleNumberOfElements(AuditPage.LogLines, numberOfAllRelatedLogs), $"Number of all related Logs: {numberOfAllRelatedLogs} doesn't match number of visible Logs"); }); } public void CheckCustomFieldsAudits(string objectType, string objectName, string? customFieldType, AuditData log) { BasicPage.ClickOnDropdownMenuItem("Custom Fields"); BasicPage.SearchObjectNameInTableViaSearchInputField(objectName); I.Click(BasicPage.AuditObjectButton(objectName)); I.WaitTillInvisible(BasicPage.AuditObjectButton(objectName)); I.WaitForElementVisibleAndClickable(AuditPage.TypeSearchInputField); I.FillField(AuditPage.TypeSearchInputField, log.Type!); I.WaitTillInvisible(BasicPage.LoadingMessage); I.DebounceDelay(configHelper.DebounceDelayMilliseconds); string auditEntity = customFieldType!.Replace(" ", string.Empty); if (!string.IsNullOrEmpty(auditEntity) && !auditEntity.Equals("FormTemplate") && !auditEntity.Equals("Domain")) { CheckPartialExistingAuditLogs(log.Type!, objectType, _scenarioContext["CustomField"].ToString()!, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); CheckPartialExistingAuditLogs(log.Type!, objectType, auditEntity, () => [.. AuditPage.GetTextFromAuditLogOnCustomFields(log.Type!)], () => I.GetTextsFromElements(AuditPage.CustomFieldsEntityDisplayName).AsEnumerable()); } } public void CheckCustomFieldsAudits(string objectType, string objectName, AuditData log) { CheckCustomFieldsAudits(objectType, objectName, null, log); } public void CheckAuditOnEntity(string objectType, string objectName, AuditData log) { NavigateToEntityMenu(objectType); SearchAuditEntity(objectType, objectName); OpenAuditLogOnEntity(objectName); FillAuditLogSearchFields(log.Type, log.DisplayName == "scenarioDataInstance" ? objectName : log.DisplayName); ValidateAuditLogDetails(log); } private static void NavigateToEntityMenu(string objectType) { BasicPage.ClickOnDropdownMenuItem(objectType); } public void CheckAuditLogs(string logType, string entityDisplayName, string auditEntity, bool allowPartialMatch = false) { string[] createdData = auditEntity.Split(", "); string[] auditData = [.. AuditPage.GetTextFromAuditLog(logType)]; var actualLogType = I.GetTextFromElement(AuditPage.AuditLogTypeOfEntity); var actualEntityDisplayName = I.GetTextFromElement(AuditPage.EntityDisplayName); var expectedEntityDisplayName = AutomationTestManagerHelper.AuditEntityDisplayName[entityDisplayName]; var actualTiming = I.GetTextFromElement(AuditPage.TimingDisplay); var expectedTiming = _scenarioContext["Timing"].ToString()!; Assert.AreEqual(logType, actualLogType, $"Expected log type: {logType}, but found: {actualLogType}"); Assert.AreEqual(expectedEntityDisplayName, actualEntityDisplayName, $"Expected Entity Display Name: {expectedEntityDisplayName}, but found: {actualEntityDisplayName}"); Assert.AreEqual(expectedTiming, actualTiming, $"Expected Timing was: {expectedTiming}, but found: {actualTiming}"); if (allowPartialMatch) { foreach (string data in createdData) { Assert.IsTrue(auditData.Contains(data), $"Expected audit data does not contain: {data}"); } } else { Assert.AreEqual(createdData.Length, auditData.Length, $"Expected {createdData.Length} audit entries, but found {auditData.Length}"); for (int i = 0; i < createdData.Length; i++) { Assert.AreEqual(createdData[i], auditData[i], $"Expected audit data was: '{createdData[i]}', but found '{auditData[i]}'"); } } } public void CheckPartialExistingAuditLogs(string logType, string entityDisplayName, string auditEntity, Func getAuditLogFunc, Func> getEntityDisplayNamesFunc) { string[] createdData = auditEntity.Split(", "); string[] auditData = getAuditLogFunc(); var actualLogType = I.GetTextFromElement(AuditPage.AuditLogTypeOfEntity); var actualDisplayNames = getEntityDisplayNamesFunc(); var expectedDisplayName = AutomationTestManagerHelper.AuditEntityDisplayName[entityDisplayName]; var actualTiming = I.GetTextFromElement(AuditPage.TimingDisplay); var expectedTiming = _scenarioContext["Timing"].ToString()!; Assert.AreEqual(logType, actualLogType, $"Expected log type: {logType}, but found: {actualLogType}"); Assert.IsTrue(actualDisplayNames.Any(name => name.Equals(expectedDisplayName)), $"Expected Entity Display Name: {expectedDisplayName}, but found: {string.Join(", ", actualDisplayNames)}"); Assert.AreEqual(expectedTiming, actualTiming, $"Expected Timing was: {expectedTiming}, but found: {actualTiming}"); foreach (string data in createdData) { Assert.IsTrue(auditData.Any(x => x.Contains(data)), $"Expected audit data does not contain: {data}"); } } private void SearchAuditEntity(string objectType, string objectName) { if (objectType.Equals("Users", StringComparison.OrdinalIgnoreCase)) { automationTestManagerHelper.SaveDataFromCreatedUser(); I.WaitForElementVisibleAndClickable(BasicPage.SearchUsersInput); BasicPage.SearchObjectNameInTableViaSearchInputField(objectName, BasicPage.SearchUsersInput); I.WaitForVisible(BasicPage.ObjectNameInTable(objectName)); } else { I.WaitForElementVisibleAndClickable(BasicPage.SearchInput); BasicPage.SearchObjectNameInTableViaSearchInputField(objectName); } } private static void OpenAuditLogOnEntity(string objectName) { I.Click(BasicPage.AuditObjectButton(objectName)); I.WaitTillInvisible(BasicPage.AuditObjectButton(objectName)); I.WaitForElementVisibleAndClickable(AuditPage.TypeSearchInputField); } private void ValidateAuditLogDetails(AuditData log) { I.Click(AuditPage.ParentLogButton); I.WaitTillInvisible(BasicPage.LoadingMessage); I.DebounceDelay(configHelper.DebounceDelayMilliseconds); var actualTiming = I.GetTextFromElement(AuditPage.TimingDisplay); var expectedTiming = _scenarioContext["Timing"].ToString()!; var actualLogType = I.GetTextFromElement(AuditPage.AuditLogTypeOfEntity); if (log.Type!.Equals("Create", StringComparison.OrdinalIgnoreCase)) { Assert.IsTrue( I.IsVisibleNumberOfElements(AuditPage.LogLines, 1), "Number of the Log Lines wasn't equal to 1" ); Assert.AreEqual(log.Type!, actualLogType, $"Expected log type: {log.Type!}, but found: {actualLogType}"); } Assert.AreEqual(expectedTiming, actualTiming, $"Expected Timing was: {expectedTiming}, but found: {actualTiming}"); } } }