diff --git a/.azurepipelines/azure-pipelines-1ES.yml b/.azurepipelines/azure-pipelines-1ES.yml
new file mode 100644
index 000000000..fff77e038
--- /dev/null
+++ b/.azurepipelines/azure-pipelines-1ES.yml
@@ -0,0 +1,194 @@
+trigger: none
+pr: none
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+extends:
+ template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ sdl:
+ codeSignValidation:
+ enabled: true
+ codeql:
+ ${{ if eq(variables['Build.SourceBranch'], variables['AllowedBranch']) }}:
+ enabledOnNonDefaultBranches: true
+ pool:
+ name: Azure-Pipelines-1ESPT-ExDShared
+ image: windows-2022
+ os: windows
+ stages:
+ - stage: stage
+ jobs:
+ - job: Build_PowerAppsTestEngine
+ displayName: 'Build PowerAppsTestEngine Solution'
+ strategy:
+ matrix:
+ Debug:
+ BuildConfiguration: 'Debug'
+ Release:
+ BuildConfiguration: 'Release'
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ artifactName: 'PowerApps.TestEngine ($(BuildConfiguration))'
+ targetPath: '$(Build.ArtifactStagingDirectory)'
+ - output: nuget
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'), eq(variables['UpdateVer'], 'true'))
+ useDotNetTask: false # The default is false to use the NuGetCommand task. Set to true to use the DotNetCoreCLI task to publish packages.
+ packagesToPush: '$(Build.ArtifactStagingDirectory)/Microsoft.PowerApps.TestEngine.*.nupkg'
+ packageParentPath: '$(Build.ArtifactStagingDirectory)'
+ publishVstsFeed: $(InternalFeed)
+ nuGetFeedType: internal
+ allowPackageConflicts: true # Optional. NuGetCommand task only.
+ steps:
+ - script: |
+ echo "Hello $(myVariable)"
+ - task: UseDotNet@2
+ displayName: 'Use dotnet sdk 8.0'
+ inputs:
+ version: 8.0.x
+ installationPath: '$(Agent.ToolsDirectory)/dotnet'
+ - task: DotNetCoreCLI@2
+ displayName: 'Build and test'
+ inputs:
+ command: 'run'
+ projects: '$(Build.SourcesDirectory)/targets/targets.csproj'
+ arguments: '-- ci -c $(BuildConfiguration)'
+ - task: PublishTestResults@2
+ inputs:
+ testResultsFormat: 'VSTest'
+ testResultsFiles: '**/*-*.trx'
+ searchFolder: '$(Build.SourcesDirectory)/obj/'
+ mergeTestResults: true
+ failTaskOnFailedTests: true
+ - task: EsrpCodeSigning@5
+ displayName: 'ESRP sign'
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ inputs:
+ ConnectedServiceName: $(EsrpConServName)
+ AppRegistrationClientId: $(EsrpAppRegCliId)
+ AppRegistrationTenantId: $(EsrpAppRegTenId)
+ AuthAKVName: $(EsrpKVName)
+ AuthCertName: $(EsrpAuthCertName)
+ AuthSignCertName: $(EsrpAuthSignCertName)
+ FolderPath: '$(Build.SourcesDirectory)/bin/$(BuildConfiguration)/PowerAppsTestEngineWrapper/'
+ Pattern: '*.dll'
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "KeyCode": "CP-233863-SN",
+ "OperationCode": "StrongNameSign",
+ "Parameters": {},
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-233863-SN",
+ "OperationCode": "StrongNameVerify",
+ "ToolName": "sign",
+ "ToolVersion": "1.0",
+ "Parameters": {}
+ },
+ {
+ "KeyCode": "CP-230012",
+ "OperationCode": "SigntoolSign",
+ "Parameters": {
+ "OpusName": "Microsoft",
+ "OpusInfo": "http://www.microsoft.com",
+ "Append": "/as",
+ "FileDigest": "/fd \"SHA256\"",
+ "PageHash": "/NPH",
+ "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ },
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-230012",
+ "OperationCode": "SigntoolVerify",
+ "ToolName": "sign",
+ "ToolVersion": "1.0",
+ "Parameters": {}
+ }
+ ]
+ - task: CopyFiles@2
+ displayName: 'Copy Built Files to Artifact Staging Directory'
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)/bin'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/buildoutput/bin'
+ # Include all files except abc.txt
+ Contents: |
+ **/*
+ !**/ThirdPartyNotices.txt
+ - task: CopyFiles@2
+ displayName: 'Copy Built Files to Artifact Staging Directory'
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)/obj'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/buildoutput/obj'
+ # Include all files except abc.txt
+ Contents: |
+ **/*
+ !**/ThirdPartyNotices.txt
+ - task: CopyFiles@2
+ displayName: 'Copy Built Files to Artifact Staging Directory'
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)/pkg'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/buildoutput/pkg'
+ # Include all files except abc.txt
+ Contents: |
+ **/*
+ !**/ThirdPartyNotices.txt
+ - task: DotNetCoreCLI@2
+ displayName: 'Pack'
+ inputs:
+ command: 'run'
+ projects: '$(Build.SourcesDirectory)/targets/targets.csproj'
+ arguments: '-- pack-AlphaV2 -c $(BuildConfiguration) -o $(Build.ArtifactStagingDirectory)'
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ - task: EsrpCodeSigning@5
+ displayName: 'ESRP sign nuget packages'
+ inputs:
+ ConnectedServiceName: $(EsrpConServName)
+ AppRegistrationClientId: $(EsrpAppRegCliId)
+ AppRegistrationTenantId: $(EsrpAppRegTenId)
+ AuthAKVName: $(EsrpKVName)
+ AuthCertName: $(EsrpAuthCertName)
+ AuthSignCertName: $(EsrpAuthSignCertName)
+ FolderPath: '$(Build.ArtifactStagingDirectory)'
+ Pattern: '*.nupkg'
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "KeyCode": "CP-401405",
+ "OperationCode": "NuGetSign",
+ "Parameters": {},
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-401405",
+ "OperationCode": "NuGetVerify",
+ "Parameters": {},
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ }
+ ]
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
+ - task: PublishSymbols@2
+ displayName: 'Publish symbols'
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'), eq(variables['UpdateVer'], 'true'))
+ continueOnError: true
+ enabled: True
+ inputs:
+ SearchPattern: '$(Build.SourcesDirectory)/bin/$(BuildConfiguration)/**/*.pdb'
+ SymbolServerType: TeamServices
+ SymbolsPath: http://symweb/
+ CompressSymbols: true
+ IndexSources: True
+ SymbolsArtifactName: TestEngine_Symbols_$(Build.BuildNumber)
\ No newline at end of file
diff --git a/src/Microsoft.PowerApps.TestEngine/Microsoft.PowerApps.TestEngine.csproj b/src/Microsoft.PowerApps.TestEngine/Microsoft.PowerApps.TestEngine.csproj
index dc5dea176..b84db77e6 100644
--- a/src/Microsoft.PowerApps.TestEngine/Microsoft.PowerApps.TestEngine.csproj
+++ b/src/Microsoft.PowerApps.TestEngine/Microsoft.PowerApps.TestEngine.csproj
@@ -1,7 +1,7 @@
- netstandard2.1
+ netstandard2.0
enable
disable
True
@@ -42,14 +42,14 @@
-
-
+
+
-
+
diff --git a/src/Microsoft.PowerApps.TestEngine/TestEngine.cs b/src/Microsoft.PowerApps.TestEngine/TestEngine.cs
index d5e4d58aa..d921dfda8 100644
--- a/src/Microsoft.PowerApps.TestEngine/TestEngine.cs
+++ b/src/Microsoft.PowerApps.TestEngine/TestEngine.cs
@@ -60,7 +60,7 @@ public TestEngine(ITestState state,
/// Optional query parameters that would be passed to the Player URL for optional features or parameters.
/// The full path where the test results are saved.
/// Throws ArgumentNullException if any of testConfigFile, environmentId, tenantId or domain are missing or empty.
- public async Task RunTestAsync(FileInfo testConfigFile, string environmentId, Guid tenantId, DirectoryInfo outputDirectory, string domain, string queryParams)
+ public async Task RunTestAsync(FileInfo testConfigFile, string environmentId, Guid? tenantId, DirectoryInfo outputDirectory, string domain, string queryParams)
{
// Set up test reporting
var testRunId = _testReporter.CreateTestRun("Power Fx Test Runner", "User"); // TODO: determine if there are more meaningful values we can put here
diff --git a/src/PowerAppsTestEngine.sln b/src/PowerAppsTestEngine.sln
index bc65b427e..acd28e93e 100644
--- a/src/PowerAppsTestEngine.sln
+++ b/src/PowerAppsTestEngine.sln
@@ -82,6 +82,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.common.user", "t
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.common.user.tests", "testengine.common.user.tests\testengine.common.user.tests.csproj", "{52D935F1-3567-48B7-904F-1183F824A9FB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerAppsTestEngineWrapper", "PowerAppsTestEngineWrapper\PowerAppsTestEngineWrapper.csproj", "{CD9738C3-8FEB-4E4D-A392-5ABE74B0E748}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -204,6 +206,10 @@ Global
{52D935F1-3567-48B7-904F-1183F824A9FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52D935F1-3567-48B7-904F-1183F824A9FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52D935F1-3567-48B7-904F-1183F824A9FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD9738C3-8FEB-4E4D-A392-5ABE74B0E748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD9738C3-8FEB-4E4D-A392-5ABE74B0E748}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD9738C3-8FEB-4E4D-A392-5ABE74B0E748}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD9738C3-8FEB-4E4D-A392-5ABE74B0E748}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/PowerAppsTestEngine/PowerAppsTestEngine.csproj b/src/PowerAppsTestEngine/PowerAppsTestEngine.csproj
index b7a85d91b..8a4c49538 100644
--- a/src/PowerAppsTestEngine/PowerAppsTestEngine.csproj
+++ b/src/PowerAppsTestEngine/PowerAppsTestEngine.csproj
@@ -24,15 +24,29 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+ false
+ False
+ true
+ true
+
diff --git a/src/PowerAppsTestEngine/Program.cs b/src/PowerAppsTestEngine/Program.cs
index 69e99c7a0..9f4098741 100644
--- a/src/PowerAppsTestEngine/Program.cs
+++ b/src/PowerAppsTestEngine/Program.cs
@@ -1,321 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
-using System.ComponentModel.Composition;
-using System.ComponentModel.Composition.Hosting;
-using System.Diagnostics;
-using System.Text.RegularExpressions;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.PowerApps.TestEngine;
-using Microsoft.PowerApps.TestEngine.Config;
-using Microsoft.PowerApps.TestEngine.Modules;
-using Microsoft.PowerApps.TestEngine.PowerFx;
-using Microsoft.PowerApps.TestEngine.Providers;
-using Microsoft.PowerApps.TestEngine.Reporting;
-using Microsoft.PowerApps.TestEngine.System;
-using Microsoft.PowerApps.TestEngine.TestInfra;
-using Microsoft.PowerApps.TestEngine.Users;
-using PowerAppsTestEngine;
-
-var switchMappings = new Dictionary()
-{
- { "-i", "TestPlanFile" },
- { "-e", "EnvironmentId" },
- { "-t", "TenantId" },
- { "-o", "OutputDirectory" },
- { "-l", "LogLevel" },
- { "-q", "QueryParams" },
- { "-d", "Domain" },
- { "-m", "Modules" },
- { "-u", "UserAuth" },
- { "-p", "Provider" },
- { "-a", "UserAuthType"},
- { "-w", "Wait" },
- { "-r", "Record" }
-};
-
-var inputOptions = new ConfigurationBuilder()
- .SetBasePath(Directory.GetCurrentDirectory())
- .AddJsonFile("config.json", true)
- .AddJsonFile("config.dev.json", true)
- .AddCommandLine(args, switchMappings)
- .Build()
- .Get();
-
-if (inputOptions == null)
-{
- Console.WriteLine("[Critical Error]: Input options are null");
- return;
-}
-else
-{
-
- // If an empty field is put in via commandline, it won't register as empty
- // It will cannabalize the next flag, and then ruin the next flag's operation
- // Therefore, we have to abort the program in this instance
-
- if (!string.IsNullOrEmpty(inputOptions.TestPlanFile))
- {
- if (inputOptions.TestPlanFile.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: TestPlanFile field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.EnvironmentId))
- {
- if (inputOptions.EnvironmentId.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: EnvironmentId field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.TenantId))
- {
- if (inputOptions.TenantId.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: TenantId field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.OutputDirectory))
- {
- if (inputOptions.OutputDirectory.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: OutputDirectory field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.LogLevel))
- {
- if (inputOptions.LogLevel.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: LogLevel field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.Domain))
- {
- if (inputOptions.Domain.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: Domain field is blank.");
- return;
- }
- }
-
- if (!string.IsNullOrEmpty(inputOptions.QueryParams))
- {
- if (inputOptions.QueryParams.Substring(0, 1) == "-")
- {
- Console.WriteLine("[Critical Error]: QueryParams field is blank.");
- return;
- }
- }
- if (!string.IsNullOrEmpty(inputOptions.Wait) && inputOptions.Wait.ToLower() == "true")
- {
- Console.WriteLine("Waiting, press enter to continue. You can now optionally attach debugger to dotnet PowerAppsTestEngine.dll process now");
- Console.ReadLine();
- if (Debugger.IsAttached)
- {
- // Welcome to the debugger experience for Power Apps Test Engine
- //
- // Key classes you may want to investigate and add breakpoint inside to understand key components or :
- // - SingleTestRunner.RunTestAsync that will run a single test case
- // - PlaywrightTestInfraFunctions.SetupAsync for setup of Playwright state
- // - PowerFxEngine.ExecuteWithRetryAsync that execute Power Fx test steps
- // - Implementations or ITestWebProvider for Test Engine providers that get the state of the resource to be tested
- // - Implementations of ITestEngineModule for Power Fx extensions
- Debugger.Break();
- }
- }
-
-
- var logLevel = LogLevel.Information; // Default log level
- if (string.IsNullOrEmpty(inputOptions.LogLevel) || !Enum.TryParse(inputOptions.LogLevel, true, out logLevel))
- {
- Console.WriteLine($"Unable to parse log level: {inputOptions.LogLevel}, using default");
- logLevel = LogLevel.Information;
- }
-
- var userAuth = "storagestate"; // Default to storage state
- if (!string.IsNullOrEmpty(inputOptions.UserAuth))
- {
- userAuth = inputOptions.UserAuth;
- }
-
- var provider = "canvas";
- if (!string.IsNullOrEmpty(inputOptions.Provider))
- {
- provider = inputOptions.Provider;
- }
-
- var auth = "default";
- if (!string.IsNullOrEmpty(inputOptions.UserAuthType))
- {
- auth = inputOptions.UserAuthType;
- }
-
- try
- {
- using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
- .ClearProviders()
- .AddFilter(l => l >= logLevel)
- .AddProvider(new TestLoggerProvider(new FileSystem())));
-
- var logger = loggerFactory.CreateLogger();
-
- var serviceProvider = new ServiceCollection()
- .AddSingleton(loggerFactory)
- .AddSingleton()
- .AddSingleton()
- .AddScoped()
- .AddScoped(sp =>
- {
- var testState = sp.GetRequiredService();
- var userManagers = testState.GetTestEngineUserManager();
- if (userManagers.Count == 0)
- {
- testState.LoadExtensionModules(logger);
- userManagers = testState.GetTestEngineUserManager();
- }
-
- var match = userManagers.Where(x => x.Name.Equals(userAuth)).FirstOrDefault();
-
- if (match == null)
- {
- throw new InvalidDataException($"Unable to find user auth {userAuth}");
- }
-
- return match;
- })
- .AddTransient(sp =>
- {
- var testState = sp.GetRequiredService();
- var testWebProviders = testState.GetTestEngineWebProviders();
- if (testWebProviders.Count == 0)
- {
- testState.LoadExtensionModules(logger);
- testWebProviders = testState.GetTestEngineWebProviders();
- }
-
- var match = testWebProviders.Where(x => x.Name.Equals(provider)).FirstOrDefault();
-
- if (match == null)
- {
- throw new InvalidDataException($"Unable to find provider {provider}");
- }
-
-
- return match;
- })
- .AddSingleton(sp =>
- {
- var testState = sp.GetRequiredService();
- var testAuthProviders = testState.GetTestEngineAuthProviders();
- if (testAuthProviders.Count == 0)
- {
- testState.LoadExtensionModules(logger);
- testAuthProviders = testState.GetTestEngineAuthProviders();
- }
-
- var match = testAuthProviders.Where(x => x.Name.Equals(auth)).FirstOrDefault();
-
- if (match == null)
- {
- match = new DefaultUserCertificateProvider();
- }
-
- return match;
- })
- .AddSingleton()
- .AddSingleton()
- .AddScoped()
- .AddScoped()
- .AddScoped((sp) => sp.GetRequiredService().GetLogger())
- .AddSingleton()
- .AddScoped()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .BuildServiceProvider();
-
- TestEngine testEngine = serviceProvider.GetRequiredService();
-
- // Default value for optional arguments is set before the class library is invoked.
- // The class library expects actual types in its input arguments, so optional arguments
- // to the Test Engine entry point function RunTestAsync must be checked for null values and their
- // corresponding default values set beforehand.
- var testPlanFile = new FileInfo(inputOptions.TestPlanFile);
- var tenantId = Guid.Parse(inputOptions.TenantId);
- var environmentId = inputOptions.EnvironmentId;
- var domain = string.Empty;
- var queryParams = "";
-
- DirectoryInfo outputDirectory;
- const string DefaultOutputDirectory = "TestOutput";
- var _fileSystem = serviceProvider.GetRequiredService();
- if (!string.IsNullOrEmpty(inputOptions.OutputDirectory))
- {
- if (Path.IsPathRooted(inputOptions.OutputDirectory.Trim()))
- {
- Console.WriteLine("[Critical Error]: Please provide a relative path for the output.");
- return;
- }
- else
- {
- outputDirectory = new DirectoryInfo(Path.Combine(_fileSystem.GetDefaultRootTestEngine(), inputOptions.OutputDirectory.Trim()));
- }
- }
- else
- {
- outputDirectory = new DirectoryInfo(Path.Combine(_fileSystem.GetDefaultRootTestEngine(), DefaultOutputDirectory.Trim()));
- }
-
- if (!string.IsNullOrEmpty(inputOptions.QueryParams))
- {
- queryParams = inputOptions.QueryParams;
- }
-
- if (!string.IsNullOrEmpty(inputOptions.Domain))
- {
- domain = inputOptions.Domain;
- }
-
- string modulePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
- List modules = new List();
- if (!string.IsNullOrEmpty(inputOptions.Modules) && Directory.Exists(inputOptions.Modules))
- {
- modulePath = inputOptions.Modules;
- }
-
- ITestState state = serviceProvider.GetService();
- state.SetModulePath(modulePath);
-
- if (!string.IsNullOrEmpty(inputOptions.Record))
- {
- state.SetRecordMode();
- }
-
- //setting defaults for optional parameters outside RunTestAsync
- var testResult = await testEngine.RunTestAsync(testPlanFile, environmentId, tenantId, outputDirectory, domain, queryParams);
- if (testResult != "InvalidOutputDirectory")
- {
- Console.WriteLine($"Test results can be found here: {testResult}");
- }
-
- }
- catch (Exception ex)
- {
- Console.WriteLine("[Critical Error]: " + ex.Message);
- Console.WriteLine(ex);
- }
-}
-
-
+await PowerAppsTestEngineWrapper.Program.Main(args);
diff --git a/src/PowerAppsTestEngine/InputOptions.cs b/src/PowerAppsTestEngineWrapper/InputOptions.cs
similarity index 95%
rename from src/PowerAppsTestEngine/InputOptions.cs
rename to src/PowerAppsTestEngineWrapper/InputOptions.cs
index b23f5d07b..5297bbcbe 100644
--- a/src/PowerAppsTestEngine/InputOptions.cs
+++ b/src/PowerAppsTestEngineWrapper/InputOptions.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
-namespace PowerAppsTestEngine
+namespace PowerAppsTestEngineWrapper
{
public class InputOptions
{
diff --git a/src/PowerAppsTestEngineWrapper/PowerAppsTestEngineWrapper.csproj b/src/PowerAppsTestEngineWrapper/PowerAppsTestEngineWrapper.csproj
new file mode 100644
index 000000000..214cd01fd
--- /dev/null
+++ b/src/PowerAppsTestEngineWrapper/PowerAppsTestEngineWrapper.csproj
@@ -0,0 +1,110 @@
+
+
+
+ netstandard2.0
+ enable
+ enable
+ True
+
+
+
+ portable
+ true
+
+
+
+ true
+ true
+ ../../35MSSharedLib1024.snk
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Microsoft
+ crmsdk,Microsoft
+ Microsoft.PowerApps.TestEngine
+ Alpha Release: Providing makers with a single automated testing platform for all Power Apps apps
+
+ Notice:
+ This package is an ALPHA release. - Use at your own risk.
+
+ Intial Alpha release of Microsoft.PowerAppsTestEngine
+
+ © Microsoft Corporation. All rights reserved.
+ true
+ 1.0
+
+
+
+ false
+ False
+ true
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+ true
+ lib\$(TargetFramework)\
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
\ No newline at end of file
diff --git a/src/PowerAppsTestEngineWrapper/Program.cs b/src/PowerAppsTestEngineWrapper/Program.cs
new file mode 100644
index 000000000..6aaf6b4d0
--- /dev/null
+++ b/src/PowerAppsTestEngineWrapper/Program.cs
@@ -0,0 +1,325 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+using System.Diagnostics;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.PowerApps.TestEngine;
+using Microsoft.PowerApps.TestEngine.Config;
+using Microsoft.PowerApps.TestEngine.Modules;
+using Microsoft.PowerApps.TestEngine.PowerFx;
+using Microsoft.PowerApps.TestEngine.Providers;
+using Microsoft.PowerApps.TestEngine.Reporting;
+using Microsoft.PowerApps.TestEngine.System;
+using Microsoft.PowerApps.TestEngine.TestInfra;
+using Microsoft.PowerApps.TestEngine.Users;
+
+namespace PowerAppsTestEngineWrapper
+{
+ public class Program
+ {
+ public static async Task Main(string[] args)
+ {
+
+ var switchMappings = new Dictionary()
+ {
+ { "-i", "TestPlanFile" },
+ { "-e", "EnvironmentId" },
+ { "-t", "TenantId" },
+ { "-o", "OutputDirectory" },
+ { "-l", "LogLevel" },
+ { "-q", "QueryParams" },
+ { "-d", "Domain" },
+ { "-m", "Modules" },
+ { "-u", "UserAuth" },
+ { "-p", "Provider" },
+ { "-a", "UserAuthType"},
+ { "-w", "Wait" },
+ { "-r", "Record" }
+ };
+
+ var inputOptions = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("config.json", true)
+ .AddJsonFile("config.dev.json", true)
+ .AddCommandLine(args, switchMappings)
+ .Build()
+ .Get();
+
+ if (inputOptions == null)
+ {
+ Console.WriteLine("[Critical Error]: Input options are null");
+ return;
+ }
+ else
+ {
+
+ // If an empty field is put in via commandline, it won't register as empty
+ // It will cannabalize the next flag, and then ruin the next flag's operation
+ // Therefore, we have to abort the program in this instance
+
+ if (!string.IsNullOrEmpty(inputOptions.TestPlanFile))
+ {
+ if (inputOptions.TestPlanFile.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: TestPlanFile field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.EnvironmentId))
+ {
+ if (inputOptions.EnvironmentId.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: EnvironmentId field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.TenantId))
+ {
+ if (inputOptions.TenantId.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: TenantId field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.OutputDirectory))
+ {
+ if (inputOptions.OutputDirectory.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: OutputDirectory field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.LogLevel))
+ {
+ if (inputOptions.LogLevel.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: LogLevel field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.Domain))
+ {
+ if (inputOptions.Domain.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: Domain field is blank.");
+ return;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.QueryParams))
+ {
+ if (inputOptions.QueryParams.Substring(0, 1) == "-")
+ {
+ Console.WriteLine("[Critical Error]: QueryParams field is blank.");
+ return;
+ }
+ }
+ if (!string.IsNullOrEmpty(inputOptions.Wait) && inputOptions.Wait.ToLower() == "true")
+ {
+ Console.WriteLine("Waiting, press enter to continue. You can now optionally attach debugger to dotnet PowerAppsTestEngine.dll process now");
+ Console.ReadLine();
+ if (Debugger.IsAttached)
+ {
+ // Welcome to the debugger experience for Power Apps Test Engine
+ //
+ // Key classes you may want to investigate and add breakpoint inside to understand key components or :
+ // - SingleTestRunner.RunTestAsync that will run a single test case
+ // - PlaywrightTestInfraFunctions.SetupAsync for setup of Playwright state
+ // - PowerFxEngine.ExecuteWithRetryAsync that execute Power Fx test steps
+ // - Implementations or ITestWebProvider for Test Engine providers that get the state of the resource to be tested
+ // - Implementations of ITestEngineModule for Power Fx extensions
+ Debugger.Break();
+ }
+ }
+
+
+ var logLevel = LogLevel.Information; // Default log level
+ if (string.IsNullOrEmpty(inputOptions.LogLevel) || !Enum.TryParse(inputOptions.LogLevel, true, out logLevel))
+ {
+ Console.WriteLine($"Unable to parse log level: {inputOptions.LogLevel}, using default");
+ logLevel = LogLevel.Information;
+ }
+
+ var userAuth = "storagestate"; // Default to storage state
+ if (!string.IsNullOrEmpty(inputOptions.UserAuth))
+ {
+ userAuth = inputOptions.UserAuth;
+ }
+
+ var provider = "canvas";
+ if (!string.IsNullOrEmpty(inputOptions.Provider))
+ {
+ provider = inputOptions.Provider;
+ }
+
+ var auth = "default";
+ if (!string.IsNullOrEmpty(inputOptions.UserAuthType))
+ {
+ auth = inputOptions.UserAuthType;
+ }
+
+ try
+ {
+ using var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder
+ .ClearProviders()
+ .AddFilter(l => l >= logLevel)
+ .AddProvider(new TestLoggerProvider(new FileSystem())));
+
+ var logger = loggerFactory.CreateLogger();
+
+ var serviceProvider = new ServiceCollection()
+ .AddSingleton(loggerFactory)
+ .AddSingleton()
+ .AddSingleton()
+ .AddScoped()
+ .AddScoped(sp =>
+ {
+ var testState = sp.GetRequiredService();
+ var userManagers = testState.GetTestEngineUserManager();
+ if (userManagers.Count == 0)
+ {
+ testState.LoadExtensionModules(logger);
+ userManagers = testState.GetTestEngineUserManager();
+ }
+
+ var match = userManagers.Where(x => x.Name.Equals(userAuth)).FirstOrDefault();
+
+ if (match == null)
+ {
+ throw new InvalidDataException($"Unable to find user auth {userAuth}");
+ }
+
+ return match;
+ })
+ .AddTransient(sp =>
+ {
+ var testState = sp.GetRequiredService();
+ var testWebProviders = testState.GetTestEngineWebProviders();
+ if (testWebProviders.Count == 0)
+ {
+ testState.LoadExtensionModules(logger);
+ testWebProviders = testState.GetTestEngineWebProviders();
+ }
+
+ var match = testWebProviders.Where(x => x.Name.Equals(provider)).FirstOrDefault();
+
+ if (match == null)
+ {
+ throw new InvalidDataException($"Unable to find provider {provider}");
+ }
+
+
+ return match;
+ })
+ .AddSingleton(sp =>
+ {
+ var testState = sp.GetRequiredService();
+ var testAuthProviders = testState.GetTestEngineAuthProviders();
+ if (testAuthProviders.Count == 0)
+ {
+ testState.LoadExtensionModules(logger);
+ testAuthProviders = testState.GetTestEngineAuthProviders();
+ }
+
+ var match = testAuthProviders.Where(x => x.Name.Equals(auth)).FirstOrDefault();
+
+ if (match == null)
+ {
+ match = new DefaultUserCertificateProvider();
+ }
+
+ return match;
+ })
+ .AddSingleton()
+ .AddSingleton()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped((sp) => sp.GetRequiredService().GetLogger())
+ .AddSingleton()
+ .AddScoped()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .BuildServiceProvider();
+
+ TestEngine testEngine = serviceProvider.GetRequiredService();
+
+ // Default value for optional arguments is set before the class library is invoked.
+ // The class library expects actual types in its input arguments, so optional arguments
+ // to the Test Engine entry point function RunTestAsync must be checked for null values and their
+ // corresponding default values set beforehand.
+ var testPlanFile = new FileInfo(inputOptions.TestPlanFile);
+ var tenantId = Guid.Parse(inputOptions.TenantId);
+ var environmentId = inputOptions.EnvironmentId;
+ var domain = string.Empty;
+ var queryParams = "";
+
+ DirectoryInfo outputDirectory;
+ const string DefaultOutputDirectory = "TestOutput";
+ var _fileSystem = serviceProvider.GetRequiredService();
+ if (!string.IsNullOrEmpty(inputOptions.OutputDirectory))
+ {
+ if (Path.IsPathRooted(inputOptions.OutputDirectory.Trim()))
+ {
+ Console.WriteLine("[Critical Error]: Please provide a relative path for the output.");
+ return;
+ }
+ else
+ {
+ outputDirectory = new DirectoryInfo(Path.Combine(_fileSystem.GetDefaultRootTestEngine(), inputOptions.OutputDirectory.Trim()));
+ }
+ }
+ else
+ {
+ outputDirectory = new DirectoryInfo(Path.Combine(_fileSystem.GetDefaultRootTestEngine(), DefaultOutputDirectory.Trim()));
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.QueryParams))
+ {
+ queryParams = inputOptions.QueryParams;
+ }
+
+ if (!string.IsNullOrEmpty(inputOptions.Domain))
+ {
+ domain = inputOptions.Domain;
+ }
+
+ string modulePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
+ List modules = new List();
+ if (!string.IsNullOrEmpty(inputOptions.Modules) && Directory.Exists(inputOptions.Modules))
+ {
+ modulePath = inputOptions.Modules;
+ }
+
+ ITestState state = serviceProvider.GetService();
+ state.SetModulePath(modulePath);
+
+ if (!string.IsNullOrEmpty(inputOptions.Record))
+ {
+ state.SetRecordMode();
+ }
+
+ //setting defaults for optional parameters outside RunTestAsync
+ var testResult = await testEngine.RunTestAsync(testPlanFile, environmentId, tenantId, outputDirectory, domain, queryParams);
+ if (testResult != "InvalidOutputDirectory")
+ {
+ Console.WriteLine($"Test results can be found here: {testResult}");
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("[Critical Error]: " + ex.Message);
+ Console.WriteLine(ex);
+ }
+ }
+ }
+ }
+}
diff --git a/src/PowerAppsTestEngineWrapper/app.config b/src/PowerAppsTestEngineWrapper/app.config
new file mode 100644
index 000000000..4823ae093
--- /dev/null
+++ b/src/PowerAppsTestEngineWrapper/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/PowerAppsTestEngine/config.json b/src/PowerAppsTestEngineWrapper/config.json
similarity index 100%
rename from src/PowerAppsTestEngine/config.json
rename to src/PowerAppsTestEngineWrapper/config.json
diff --git a/src/testengine.auth.certificatestore.tests/testengine.auth.certificatestore.tests.csproj b/src/testengine.auth.certificatestore.tests/testengine.auth.certificatestore.tests.csproj
index d7330e097..c1bc72231 100644
--- a/src/testengine.auth.certificatestore.tests/testengine.auth.certificatestore.tests.csproj
+++ b/src/testengine.auth.certificatestore.tests/testengine.auth.certificatestore.tests.csproj
@@ -32,6 +32,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.auth.certificatestore/testengine.auth.certificatestore.csproj b/src/testengine.auth.certificatestore/testengine.auth.certificatestore.csproj
index 615a482b0..609116737 100644
--- a/src/testengine.auth.certificatestore/testengine.auth.certificatestore.csproj
+++ b/src/testengine.auth.certificatestore/testengine.auth.certificatestore.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
© Microsoft Corporation. All rights reserved.
@@ -32,9 +32,5 @@
-
-
-
-
diff --git a/src/testengine.common.user/LoginState.cs b/src/testengine.common.user/LoginState.cs
index 04d5717eb..2bb6bf52e 100644
--- a/src/testengine.common.user/LoginState.cs
+++ b/src/testengine.common.user/LoginState.cs
@@ -23,7 +23,7 @@ public class LoginState
public string? MatchHost { get; set; }
- public Func? CallbackDesiredUrlFound { get; set; }
- public Func? CallbackErrorFound { get; set; }
+ public Func CallbackDesiredUrlFound { get; set; } = null;
+ public Func CallbackErrorFound { get; set; } = null;
}
}
diff --git a/src/testengine.common.user/PowerPlatformLogin.cs b/src/testengine.common.user/PowerPlatformLogin.cs
index 4040ac944..f20a510ed 100644
--- a/src/testengine.common.user/PowerPlatformLogin.cs
+++ b/src/testengine.common.user/PowerPlatformLogin.cs
@@ -32,7 +32,7 @@ public virtual async Task HandleCommonLoginState(LoginState state)
{
if (!state.Module.Settings.ContainsKey(ERROR_DIALOG_KEY))
{
- state.Module.Settings.TryAdd(ERROR_DIALOG_KEY, title);
+ state.Module.Settings.Add(ERROR_DIALOG_KEY, title);
}
else
{
diff --git a/src/testengine.common.user/testengine.common.user.csproj b/src/testengine.common.user/testengine.common.user.csproj
index 1045532fe..511a5fb14 100644
--- a/src/testengine.common.user/testengine.common.user.csproj
+++ b/src/testengine.common.user/testengine.common.user.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
@@ -37,8 +37,4 @@
-
-
-
-
diff --git a/src/testengine.module.mda.tests/testengine.module.mda.tests.csproj b/src/testengine.module.mda.tests/testengine.module.mda.tests.csproj
index c3d52a01a..7aeca2830 100644
--- a/src/testengine.module.mda.tests/testengine.module.mda.tests.csproj
+++ b/src/testengine.module.mda.tests/testengine.module.mda.tests.csproj
@@ -27,6 +27,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.module.mda/testengine.module.mda.csproj b/src/testengine.module.mda/testengine.module.mda.csproj
index d694f415f..6cfef8e99 100644
--- a/src/testengine.module.mda/testengine.module.mda.csproj
+++ b/src/testengine.module.mda/testengine.module.mda.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
Microsoft
@@ -40,10 +40,10 @@
-
+
-
+
@@ -54,7 +54,4 @@
-
-
-
diff --git a/src/testengine.module.pause.tests/testengine.module.pause.tests.csproj b/src/testengine.module.pause.tests/testengine.module.pause.tests.csproj
index ff092b827..59fb32f4b 100644
--- a/src/testengine.module.pause.tests/testengine.module.pause.tests.csproj
+++ b/src/testengine.module.pause.tests/testengine.module.pause.tests.csproj
@@ -27,6 +27,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.module.pause/testengine.module.pause.csproj b/src/testengine.module.pause/testengine.module.pause.csproj
index 1e3a3f7f9..bccca538e 100644
--- a/src/testengine.module.pause/testengine.module.pause.csproj
+++ b/src/testengine.module.pause/testengine.module.pause.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
Microsoft
@@ -40,10 +40,10 @@
-
+
-
+
@@ -54,7 +54,4 @@
-
-
-
diff --git a/src/testengine.module.playwrightaction.tests/testengine.module.playwrightaction.tests.csproj b/src/testengine.module.playwrightaction.tests/testengine.module.playwrightaction.tests.csproj
index 2ac168054..724d11033 100644
--- a/src/testengine.module.playwrightaction.tests/testengine.module.playwrightaction.tests.csproj
+++ b/src/testengine.module.playwrightaction.tests/testengine.module.playwrightaction.tests.csproj
@@ -22,6 +22,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.module.playwrightaction/testengine.module.playwrightaction.csproj b/src/testengine.module.playwrightaction/testengine.module.playwrightaction.csproj
index cf15edc06..004b87e25 100644
--- a/src/testengine.module.playwrightaction/testengine.module.playwrightaction.csproj
+++ b/src/testengine.module.playwrightaction/testengine.module.playwrightaction.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
@@ -29,11 +29,4 @@
-
-
-
-
diff --git a/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj b/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj
index 1e3b41ffc..f6ca161c4 100644
--- a/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj
+++ b/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj b/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj
index 7a33d31e6..59fef6028 100644
--- a/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj
+++ b/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
true
@@ -40,8 +40,4 @@
-
-
-
-
diff --git a/src/testengine.module.powerapps.portal.tests/testengine.module.powerapps.portal.tests.csproj b/src/testengine.module.powerapps.portal.tests/testengine.module.powerapps.portal.tests.csproj
index c625fdd9e..a7b94c563 100644
--- a/src/testengine.module.powerapps.portal.tests/testengine.module.powerapps.portal.tests.csproj
+++ b/src/testengine.module.powerapps.portal.tests/testengine.module.powerapps.portal.tests.csproj
@@ -28,6 +28,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.module.powerapps.portal/testengine.module.powerapps.portal.csproj b/src/testengine.module.powerapps.portal/testengine.module.powerapps.portal.csproj
index ef2c015bf..94631d8e6 100644
--- a/src/testengine.module.powerapps.portal/testengine.module.powerapps.portal.csproj
+++ b/src/testengine.module.powerapps.portal/testengine.module.powerapps.portal.csproj
@@ -1,6 +1,6 @@
- net8.0
+ netstandard2.0
enable
enable
Microsoft
@@ -47,10 +47,11 @@
-
+
-
+
+
@@ -61,7 +62,4 @@
-
-
-
diff --git a/src/testengine.module.sample/testengine.module.sample.csproj b/src/testengine.module.sample/testengine.module.sample.csproj
index a27eb222b..2b6f427e1 100644
--- a/src/testengine.module.sample/testengine.module.sample.csproj
+++ b/src/testengine.module.sample/testengine.module.sample.csproj
@@ -1,6 +1,6 @@
- net8.0
+ netstandard2.0
enable
enable
Microsoft
@@ -33,8 +33,8 @@
-
-
+
+
@@ -45,7 +45,4 @@
-
-
-
diff --git a/src/testengine.module.simulation/SimulateConnectorFunction.cs b/src/testengine.module.simulation/SimulateConnectorFunction.cs
index d7df9cbf1..92930e2d1 100644
--- a/src/testengine.module.simulation/SimulateConnectorFunction.cs
+++ b/src/testengine.module.simulation/SimulateConnectorFunction.cs
@@ -6,7 +6,6 @@
using System.Web;
using Microsoft.Extensions.Logging;
using Microsoft.Playwright;
-using Microsoft.Playwright.Core;
using Microsoft.PowerApps.TestEngine.Config;
using Microsoft.PowerApps.TestEngine.TestInfra;
using Microsoft.PowerFx;
@@ -15,7 +14,6 @@
using Microsoft.PowerFx.Types;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using static System.Runtime.InteropServices.JavaScript.JSType;
namespace testengine.module
{
diff --git a/src/testengine.module.simulation/testengine.module.simulation.csproj b/src/testengine.module.simulation/testengine.module.simulation.csproj
index e0b4ac722..d8b090a18 100644
--- a/src/testengine.module.simulation/testengine.module.simulation.csproj
+++ b/src/testengine.module.simulation/testengine.module.simulation.csproj
@@ -1,6 +1,6 @@
- net8.0
+ netstandard2.0
enable
enable
@@ -21,10 +21,10 @@
-
+
-
+
@@ -35,7 +35,4 @@
-
-
-
diff --git a/src/testengine.module.tests.common/testengine.module.tests.common.csproj b/src/testengine.module.tests.common/testengine.module.tests.common.csproj
index 8b0ae1058..97b890aa1 100644
--- a/src/testengine.module.tests.common/testengine.module.tests.common.csproj
+++ b/src/testengine.module.tests.common/testengine.module.tests.common.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/testengine.modules.simulation.tests/testengine.modules.simulation.tests.csproj b/src/testengine.modules.simulation.tests/testengine.modules.simulation.tests.csproj
index 2d74832f7..b2b9693fb 100644
--- a/src/testengine.modules.simulation.tests/testengine.modules.simulation.tests.csproj
+++ b/src/testengine.modules.simulation.tests/testengine.modules.simulation.tests.csproj
@@ -27,6 +27,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.provider.canvas/testengine.provider.canvas.csproj b/src/testengine.provider.canvas/testengine.provider.canvas.csproj
index 87fe7045d..b86a90334 100644
--- a/src/testengine.provider.canvas/testengine.provider.canvas.csproj
+++ b/src/testengine.provider.canvas/testengine.provider.canvas.csproj
@@ -1,6 +1,6 @@
- net8.0
+ netstandard2.0
enable
enable
© Microsoft Corporation. All rights reserved.
@@ -52,7 +52,6 @@
-
diff --git a/src/testengine.provider.mda.tests/testengine.provider.mda.tests.csproj b/src/testengine.provider.mda.tests/testengine.provider.mda.tests.csproj
index d059778dd..85397ea79 100644
--- a/src/testengine.provider.mda.tests/testengine.provider.mda.tests.csproj
+++ b/src/testengine.provider.mda.tests/testengine.provider.mda.tests.csproj
@@ -42,6 +42,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/testengine.provider.mda/ModelDrivenApplicationCanvasState.cs b/src/testengine.provider.mda/ModelDrivenApplicationCanvasState.cs
index d58d59473..d93671bc3 100644
--- a/src/testengine.provider.mda/ModelDrivenApplicationCanvasState.cs
+++ b/src/testengine.provider.mda/ModelDrivenApplicationCanvasState.cs
@@ -1,4 +1,5 @@
using System.Dynamic;
+using System.Threading.Tasks;
using ICSharpCode.Decompiler.DebugInfo;
using Microsoft.PowerApps.TestEngine.Config;
using Microsoft.PowerApps.TestEngine.TestInfra;
@@ -12,7 +13,7 @@ namespace testengine.provider.mda
public class ModelDrivenApplicationCanvasState
{
public Dictionary VariableState { get; set; } = new Dictionary();
- public Dictionary CollectionState { get; set; } = new Dictionary();
+ public Dictionary CollectionState { get; set; } = new Dictionary();
///
/// Query the state of the browser and update the Power Fx state
@@ -90,7 +91,7 @@ public async Task ApplyChanges(ITestInfraFunctions testInfraFunctions, ModelDriv
}
else
{
- originalState.VariableState.TryAdd(variable, await originalState.ConvertToVariableState(newPowerFxVariableValue));
+ originalState.VariableState.Add(variable, await originalState.ConvertToVariableState(newPowerFxVariableValue));
}
}
}
@@ -121,7 +122,14 @@ public async Task ApplyChanges(ITestInfraFunctions testInfraFunctions, ModelDriv
{
// Add the new collction and cache a copy of the collection state
originalEngine.UpdateVariable(collection, newPowerFxCollectionValue);
- originalState.CollectionState.TryAdd(collection, await originalState.ConvertToVariableState(newPowerFxCollectionValue));
+ if (originalState.CollectionState.ContainsKey(collection))
+ {
+ originalState.CollectionState[collection] = await originalState.ConvertToVariableState(newPowerFxCollectionValue);
+ }
+ else
+ {
+ originalState.CollectionState.Add(collection, await originalState.ConvertToVariableState(newPowerFxCollectionValue));
+ }
}
}
}
@@ -261,7 +269,10 @@ private void HandleCollectionValueChange(string collection, ModelDrivenApplicati
if (value is ObjectRecordValue)
{
var objectValue = (ObjectRecordValue)value;
- return JsonConvert.SerializeObject(objectValue.ToObject());
+ if (objectValue.ToObject() is ExpandoObject expando)
+ {
+ return JsonConvert.SerializeObject(expando);
+ }
}
}
return null;
@@ -618,7 +629,7 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out
}
// Override ToObject method
- public override ExpandoObject ToObject()
+ public override Object ToObject()
{
// Convert the Dictionary to an ExpandoObject
ExpandoObject expandoObject = new ExpandoObject();
diff --git a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs
index 4a53da302..bb5934431 100644
--- a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs
+++ b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs
@@ -372,7 +372,7 @@ public async Task EmbedMDAJSScripts(string resourceName, string embeddedScriptNa
using (var memoryStream = new MemoryStream())
{
await stream.CopyToAsync(memoryStream);
- scriptHash = "sha256-" + Convert.ToBase64String(SHA256.HashData(memoryStream.ToArray()));
+ scriptHash = "sha256-" + Convert.ToBase64String(SHA256.Create().ComputeHash(memoryStream.ToArray()));
}
string scriptUrl = $"/{embeddedScriptName}?hash={scriptHash}";
var opt = new PageAddScriptTagOptions()
diff --git a/src/testengine.provider.mda/testengine.provider.mda.csproj b/src/testengine.provider.mda/testengine.provider.mda.csproj
index a0e9593bf..332c00e91 100644
--- a/src/testengine.provider.mda/testengine.provider.mda.csproj
+++ b/src/testengine.provider.mda/testengine.provider.mda.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
© Microsoft Corporation. All rights reserved.
@@ -32,6 +32,10 @@
+
+
+
+
@@ -48,7 +52,4 @@
-
-
-
diff --git a/src/testengine.provider.powerapps.portal/testengine.provider.powerapps.portal.csproj b/src/testengine.provider.powerapps.portal/testengine.provider.powerapps.portal.csproj
index fdfa2431f..068beb88c 100644
--- a/src/testengine.provider.powerapps.portal/testengine.provider.powerapps.portal.csproj
+++ b/src/testengine.provider.powerapps.portal/testengine.provider.powerapps.portal.csproj
@@ -1,7 +1,7 @@
- net8.0
+ netstandard2.0
enable
enable
© Microsoft Corporation. All rights reserved.
@@ -40,8 +40,4 @@
-
-
-
-
diff --git a/src/testengine.user.storagestate/StorageStateUserManagerModule.cs b/src/testengine.user.storagestate/StorageStateUserManagerModule.cs
index 2d977ef20..fc6e4354a 100644
--- a/src/testengine.user.storagestate/StorageStateUserManagerModule.cs
+++ b/src/testengine.user.storagestate/StorageStateUserManagerModule.cs
@@ -262,6 +262,10 @@ public bool IsValidEmail(string emailAddress)
{
try
{
+ if (string.IsNullOrEmpty(emailAddress))
+ {
+ return false;
+ }
var email = new MailAddress(emailAddress);
return email.Address == emailAddress.Trim();
}
@@ -271,7 +275,7 @@ public bool IsValidEmail(string emailAddress)
}
}
- public string GetUserNameFromEmail(string? emailAddress)
+ public string GetUserNameFromEmail(string emailAddress)
{
if (string.IsNullOrEmpty(emailAddress))
{
diff --git a/src/testengine.user.storagestate/testengine.user.storagestate.csproj b/src/testengine.user.storagestate/testengine.user.storagestate.csproj
index 8027b06e0..9cb666a33 100644
--- a/src/testengine.user.storagestate/testengine.user.storagestate.csproj
+++ b/src/testengine.user.storagestate/testengine.user.storagestate.csproj
@@ -1,8 +1,8 @@
- net8.0
+ netstandard2.0
enable
- enable
+ disable
@@ -35,8 +35,4 @@
-
-
-
-
diff --git a/targets/Program.cs b/targets/Program.cs
index be4f72188..491f1c152 100644
--- a/targets/Program.cs
+++ b/targets/Program.cs
@@ -54,6 +54,8 @@ static void Main(string[] args)
var solution = Path.Combine(SrcDir, "PowerAppsTestEngine.sln");
var project = Path.Combine(PATestEngineDir, "Microsoft.PowerApps.TestEngine.csproj");
+ var projectTestEngine2 = Path.Combine(SrcDir, "PowerAppsTestEngineWrapper", "PowerAppsTestEngineWrapper.csproj");
+ var customPackageId = "Microsoft.PowerApps.TestEngine";
Target("squeaky-clean",
() =>
@@ -82,6 +84,9 @@ static void Main(string[] args)
Target("pack",
() => RunDotnet("pack", $"{EscapePath(project)} --configuration {options.Configuration} --no-build -o {options.OutputDirectory} -p:Packing=true", gitExists, LogDir));
+ Target("pack-AlphaV2",
+ () => RunDotnet("pack", $"{EscapePath(projectTestEngine2)} --configuration {options.Configuration} --no-build -o {options.OutputDirectory} -p:Packing=true /p:PackageID={customPackageId}", gitExists, LogDir));
+
Target("ci",
DependsOn("squeaky-clean", "rebuild", "test"));