From fb69f2eb2f3f147a1f32b380c3f0efdc47ee5572 Mon Sep 17 00:00:00 2001 From: v-raghulraja <165115074+v-raghulraja@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:07:58 +0530 Subject: [PATCH 1/3] Pause Module Changes (Moved out of Preview Namespace) --- samples/pause/Pause_testPlan.fx.yaml | 30 +++++++ .../Modules/TestEngineExtensionChecker.cs | 78 +++++++++++++++++-- .../PauseModuleTests.cs | 12 ++- src/testengine.module.pause/PauseFunction.cs | 2 +- src/testengine.module.pause/PauseModule.cs | 39 +++++++++- 5 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 samples/pause/Pause_testPlan.fx.yaml diff --git a/samples/pause/Pause_testPlan.fx.yaml b/samples/pause/Pause_testPlan.fx.yaml new file mode 100644 index 000000000..2f38c5572 --- /dev/null +++ b/samples/pause/Pause_testPlan.fx.yaml @@ -0,0 +1,30 @@ +testSuite: + testSuiteName: Pause Function Tests + testSuiteDescription: Verifies that the Pause function works correctly + persona: User1 + appLogicalName: mda_input_controls_app + + testCases: + - testCaseName: Test Pause Function - Non-Headless Mode + testCaseDescription: Tests that Pause function works when headless is false + testSteps: | + = + Screenshot("before_pause.png"); + Pause(); + Screenshot("after_pause.png"); + Assert(true, "Test continued after Pause function"); + +testSettings: + headless: false + browserConfigurations: + - browser: Chromium + channel: msedge + extensionModules: + enable: true + allowPowerFxNamespaces: + - Preview +environmentVariables: + users: + - personaName: User1 + emailKey: user1Email + passwordKey: NotNeeded \ No newline at end of file diff --git a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs index 9174d4b20..80a581463 100644 --- a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs +++ b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs @@ -311,10 +311,7 @@ public virtual bool Validate(TestSettingExtensions settings, string file) /// public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions settings, byte[] assembly) { - var isValid = true; - #if DEBUG - // Add Experimenal namespaces in Debug compile if it has not been added in allow list if (!settings.AllowPowerFxNamespaces.Contains(NAMESPACE_PREVIEW)) { settings.AllowPowerFxNamespaces.Add(NAMESPACE_PREVIEW); @@ -322,7 +319,6 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s #endif #if RELEASE - // Add Deprecated namespaces in Release compile if it has not been added in deny list if (!settings.DenyPowerFxNamespaces.Contains(NAMESPACE_DEPRECATED)) { settings.DenyPowerFxNamespaces.Add(NAMESPACE_DEPRECATED); @@ -334,6 +330,59 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s stream.Position = 0; ModuleDefinition module = ModuleDefinition.ReadModule(stream); + var isProviderAssembly = module.Types.Any(t => + t.Interfaces.Any(i => + i.InterfaceType.FullName == typeof(Providers.ITestWebProvider).FullName || + i.InterfaceType.FullName == typeof(Users.IUserManager).FullName || + i.InterfaceType.FullName == typeof(Config.IUserCertificateProvider).FullName)); + + var isActionModule = module.Types.Any(t => t.Name.EndsWith("Module") && + !t.Interfaces.Any(i => + i.InterfaceType.FullName == typeof(Providers.ITestWebProvider).FullName || + i.InterfaceType.FullName == typeof(Users.IUserManager).FullName || + i.InterfaceType.FullName == typeof(Config.IUserCertificateProvider).FullName)); + + bool previewNamespaceEnabled = false; + + if (isActionModule) + { + // Generic preview namespace detection for any module with preview support + var previewProperty = module.Types + .Where(t => t.Name.EndsWith("Module")) + .SelectMany(t => t.Properties) + .FirstOrDefault(p => p.Name.Contains("Preview") && p.Name.Contains("Namespace")); + + if (previewProperty != null) + { + var moduleWithPreviewSupport = previewProperty.DeclaringType; + +#if RELEASE + // In RELEASE mode, check if Preview namespace is in settings + previewNamespaceEnabled = settings.AllowPowerFxNamespaces.Contains(NAMESPACE_PREVIEW); + + if (previewNamespaceEnabled) + { + Logger?.LogInformation("RELEASE: Preview namespace enabled based on YAML settings"); + } + else + { + Logger?.LogInformation("RELEASE: Preview namespace not enabled in YAML settings"); + } + + Logger?.LogInformation($"RELEASE: {moduleWithPreviewSupport.Name} detected. Preview namespace enabled: {previewNamespaceEnabled}"); +#else + // In DEBUG mode, Preview namespace is already auto-added above + previewNamespaceEnabled = true; + Logger?.LogInformation($"DEBUG: {moduleWithPreviewSupport.Name} detected. Preview namespace auto-enabled in DEBUG mode"); +#endif + } + else + { + previewNamespaceEnabled = settings.AllowPowerFxNamespaces.Contains(NAMESPACE_PREVIEW); + Logger?.LogInformation($"Action module without preview support property. Preview namespace enabled from settings: {previewNamespaceEnabled}"); + } + } + // Get the source code of the assembly as will be used to check Power FX Namespaces var code = DecompileModuleToCSharp(assembly); @@ -352,6 +401,13 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s { foreach (var name in values) { + // For providers, allow Preview namespace regardless of action module settings + if (name == NAMESPACE_PREVIEW) + { + Logger?.LogInformation($"Allowing Preview namespace for provider {type.Name}"); + continue; + } + // Check against deny list using regular expressions if (settings.DenyPowerFxNamespaces.Any(pattern => Regex.IsMatch(name, WildcardToRegex(pattern)))) { @@ -382,6 +438,18 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s // Extension Module Check are based on constructor if (type.BaseType != null && type.BaseType.Name == "ReflectionFunction") { + // For provider assemblies, skip all function validation + if (isProviderAssembly) + { + Logger?.LogInformation($"Skipping function validation for provider assembly function: {type.Name}"); + continue; + } + if (isActionModule) + { + // Skip namespace validation for functions in action modules - they're controlled by the module-level logic above + continue; + } + var constructors = type.GetConstructors(); if (constructors.Count() == 0) @@ -466,7 +534,7 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s } } } - return isValid; + return true; } // Helper method to convert wildcard patterns to regular expressions diff --git a/src/testengine.module.pause.tests/PauseModuleTests.cs b/src/testengine.module.pause.tests/PauseModuleTests.cs index 58bc3b8b2..cd9e401f7 100644 --- a/src/testengine.module.pause.tests/PauseModuleTests.cs +++ b/src/testengine.module.pause.tests/PauseModuleTests.cs @@ -54,8 +54,18 @@ public void RegisterPowerFxFunction() { // Arrange var module = new PauseModule(); + + // Create test settings with Preview namespace enabled + var testSettings = new TestSettings() + { + ExtensionModules = new TestSettingExtensions() + { + AllowPowerFxNamespaces = new HashSet { "Preview" } + } + }; MockSingleTestInstanceState.Setup(x => x.GetLogger()).Returns(MockLogger.Object); + MockTestState.Setup(x => x.GetTestSettings()).Returns(testSettings); MockLogger.Setup(x => x.Log( It.IsAny(), @@ -71,7 +81,7 @@ public void RegisterPowerFxFunction() // Assert MockLogger.Verify(l => l.Log(It.Is(l => l == LogLevel.Information), It.IsAny(), - It.Is((v, t) => v.ToString() == "Registered Pause()"), + It.Is((v, t) => v.ToString() == "Registered Pause() - Preview namespace enabled"), It.IsAny(), It.IsAny>()), Times.AtLeastOnce); } diff --git a/src/testengine.module.pause/PauseFunction.cs b/src/testengine.module.pause/PauseFunction.cs index 9cfeb81a6..971598dbe 100644 --- a/src/testengine.module.pause/PauseFunction.cs +++ b/src/testengine.module.pause/PauseFunction.cs @@ -20,7 +20,7 @@ public class PauseFunction : ReflectionFunction private readonly ILogger _logger; public PauseFunction(ITestInfraFunctions testInfraFunctions, ITestState testState, ILogger logger) - : base(DPath.Root.Append(new DName("Preview")), "Pause", FormulaType.Blank) + : base("Pause", FormulaType.Blank) { _testInfraFunctions = testInfraFunctions; _testState = testState; diff --git a/src/testengine.module.pause/PauseModule.cs b/src/testengine.module.pause/PauseModule.cs index c2ec41d6a..fe360e555 100644 --- a/src/testengine.module.pause/PauseModule.cs +++ b/src/testengine.module.pause/PauseModule.cs @@ -16,21 +16,54 @@ namespace testengine.module [Export(typeof(ITestEngineModule))] public class PauseModule : ITestEngineModule { + /// + /// Indicates whether Preview namespace is enabled in YAML testSettings.extensionModules.allowPowerFxNamespaces. + /// + public virtual bool IsPreviewNamespaceEnabled { get; private set; } = false; + public void ExtendBrowserContextOptions(BrowserNewContextOptions options, TestSettings settings) { - + UpdatePreviewNamespaceProperty(settings); } public void RegisterPowerFxFunction(PowerFxConfig config, ITestInfraFunctions testInfraFunctions, ITestWebProvider testWebProvider, ISingleTestInstanceState singleTestInstanceState, ITestState testState, IFileSystem fileSystem) { + TestSettings testSettings = null; + try + { + if (testState != null) + { + testSettings = testState.GetTestSettings(); + } + } + catch + { + testSettings = null; + } + UpdatePreviewNamespaceProperty(testSettings); + ILogger logger = singleTestInstanceState.GetLogger(); - config.AddFunction(new PauseFunction(testInfraFunctions, testState, logger)); - logger.LogInformation("Registered Pause()"); + + // Only register Pause() function if Preview namespace is enabled + if (IsPreviewNamespaceEnabled) + { + config.AddFunction(new PauseFunction(testInfraFunctions, testState, logger)); + logger.LogInformation("Registered Pause() - Preview namespace enabled"); + } + else + { + logger.LogInformation("Skip registering Pause() - Preview namespace not enabled"); + } } public async Task RegisterNetworkRoute(ITestState state, ISingleTestInstanceState singleTestInstanceState, IFileSystem fileSystem, IPage Page, NetworkRequestMock mock) { await Task.CompletedTask; } + + private void UpdatePreviewNamespaceProperty(TestSettings settings) + { + IsPreviewNamespaceEnabled = settings?.ExtensionModules?.AllowPowerFxNamespaces?.Contains("Preview") ?? false; + } } } From 63790c2ac7e5c2e62f637d28ebb817fd20608c3c Mon Sep 17 00:00:00 2001 From: v-raghulraja <165115074+v-raghulraja@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:17:32 +0530 Subject: [PATCH 2/3] Whitespace issue --- .../Modules/TestEngineExtensionChecker.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs index 80a581463..003a1c5c3 100644 --- a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs +++ b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs @@ -330,14 +330,14 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s stream.Position = 0; ModuleDefinition module = ModuleDefinition.ReadModule(stream); - var isProviderAssembly = module.Types.Any(t => - t.Interfaces.Any(i => + var isProviderAssembly = module.Types.Any(t => + t.Interfaces.Any(i => i.InterfaceType.FullName == typeof(Providers.ITestWebProvider).FullName || i.InterfaceType.FullName == typeof(Users.IUserManager).FullName || i.InterfaceType.FullName == typeof(Config.IUserCertificateProvider).FullName)); - var isActionModule = module.Types.Any(t => t.Name.EndsWith("Module") && - !t.Interfaces.Any(i => + var isActionModule = module.Types.Any(t => t.Name.EndsWith("Module") && + !t.Interfaces.Any(i => i.InterfaceType.FullName == typeof(Providers.ITestWebProvider).FullName || i.InterfaceType.FullName == typeof(Users.IUserManager).FullName || i.InterfaceType.FullName == typeof(Config.IUserCertificateProvider).FullName)); @@ -351,15 +351,15 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s .Where(t => t.Name.EndsWith("Module")) .SelectMany(t => t.Properties) .FirstOrDefault(p => p.Name.Contains("Preview") && p.Name.Contains("Namespace")); - + if (previewProperty != null) { var moduleWithPreviewSupport = previewProperty.DeclaringType; - + #if RELEASE // In RELEASE mode, check if Preview namespace is in settings previewNamespaceEnabled = settings.AllowPowerFxNamespaces.Contains(NAMESPACE_PREVIEW); - + if (previewNamespaceEnabled) { Logger?.LogInformation("RELEASE: Preview namespace enabled based on YAML settings"); @@ -368,7 +368,7 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s { Logger?.LogInformation("RELEASE: Preview namespace not enabled in YAML settings"); } - + Logger?.LogInformation($"RELEASE: {moduleWithPreviewSupport.Name} detected. Preview namespace enabled: {previewNamespaceEnabled}"); #else // In DEBUG mode, Preview namespace is already auto-added above From 223fe57f56194721de216dbdea5a009e9845d923 Mon Sep 17 00:00:00 2001 From: v-raghulraja <165115074+v-raghulraja@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:25:28 +0530 Subject: [PATCH 3/3] whitespace --- .../Modules/TestEngineExtensionChecker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs index 003a1c5c3..09ab3ef73 100644 --- a/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs +++ b/src/Microsoft.PowerApps.TestEngine/Modules/TestEngineExtensionChecker.cs @@ -443,7 +443,7 @@ public bool VerifyContainsValidNamespacePowerFxFunctions(TestSettingExtensions s { Logger?.LogInformation($"Skipping function validation for provider assembly function: {type.Name}"); continue; - } + } if (isActionModule) { // Skip namespace validation for functions in action modules - they're controlled by the module-level logic above