diff --git a/src/Microsoft.PowerApps.TestEngine.Tests/TestInfra/PlaywrightTestInfraFunctionTests.cs b/src/Microsoft.PowerApps.TestEngine.Tests/TestInfra/PlaywrightTestInfraFunctionTests.cs index 77c6203f4..4224da842 100644 --- a/src/Microsoft.PowerApps.TestEngine.Tests/TestInfra/PlaywrightTestInfraFunctionTests.cs +++ b/src/Microsoft.PowerApps.TestEngine.Tests/TestInfra/PlaywrightTestInfraFunctionTests.cs @@ -804,6 +804,30 @@ public async Task RunJavascriptSuccessfulTest() MockPage.Verify(x => x.EvaluateAsync(jsExpression, null), Times.Once()); } + [Fact] + public async Task RunJavascriptWithArgumentSuccessfulTest() + { + var jsExpression = "(value) => value.text"; + var argument = new { text = "hello" }; + var expectedResponse = "hello"; + + LoggingTestHelper.SetupMock(MockLogger); + MockSingleTestInstanceState.Setup(x => x.GetLogger()).Returns(MockLogger.Object); + MockPage.Setup(x => x.EvaluateAsync(jsExpression, argument)).ReturnsAsync(expectedResponse); + + var playwrightTestInfraFunctions = new PlaywrightTestInfraFunctions( + MockTestState.Object, + MockSingleTestInstanceState.Object, + MockFileSystem.Object, + page: MockPage.Object, + testWebProvider: MockTestWebProvider.Object); + + var result = await playwrightTestInfraFunctions.RunJavascriptAsync(jsExpression, argument); + + Assert.Equal(expectedResponse, result); + MockPage.Verify(x => x.EvaluateAsync(jsExpression, argument), Times.Once()); + } + [Fact] public async Task RouteNetworkRequestTest() { diff --git a/src/Microsoft.PowerApps.TestEngine/TestInfra/ITestInfraFunctions.cs b/src/Microsoft.PowerApps.TestEngine/TestInfra/ITestInfraFunctions.cs index 8aa246559..93467ce61 100644 --- a/src/Microsoft.PowerApps.TestEngine/TestInfra/ITestInfraFunctions.cs +++ b/src/Microsoft.PowerApps.TestEngine/TestInfra/ITestInfraFunctions.cs @@ -100,6 +100,15 @@ public interface ITestInfraFunctions /// Return value of javascript public Task RunJavascriptAsync(string jsExpression); + /// + /// Runs javascript on the page with a structured argument. + /// + /// Expected return type + /// Javascript function to run + /// Argument serialized by Playwright + /// Return value of javascript + public Task RunJavascriptAsync(string jsExpression, object argument); + /// /// Triggers a click event on a control /// diff --git a/src/Microsoft.PowerApps.TestEngine/TestInfra/PlaywrightTestInfraFunctions.cs b/src/Microsoft.PowerApps.TestEngine/TestInfra/PlaywrightTestInfraFunctions.cs index 32cedf7ca..6d1e7e846 100644 --- a/src/Microsoft.PowerApps.TestEngine/TestInfra/PlaywrightTestInfraFunctions.cs +++ b/src/Microsoft.PowerApps.TestEngine/TestInfra/PlaywrightTestInfraFunctions.cs @@ -493,6 +493,15 @@ public async Task RunJavascriptAsync(string jsExpression) return await Page.EvaluateAsync(jsExpression); } + public async Task RunJavascriptAsync(string jsExpression, object argument) + { + ValidatePage(); + + _singleTestInstanceState.GetLogger().LogDebug("Run Javascript: " + jsExpression); + + return await Page.EvaluateAsync(jsExpression, argument); + } + public async Task AddScriptContentAsync(string content) { ValidatePage(); diff --git a/src/testengine.provider.mda.tests/ModelDrivenApplicationMock.js b/src/testengine.provider.mda.tests/ModelDrivenApplicationMock.js index 99649cb8c..d2b59e1fe 100644 --- a/src/testengine.provider.mda.tests/ModelDrivenApplicationMock.js +++ b/src/testengine.provider.mda.tests/ModelDrivenApplicationMock.js @@ -128,6 +128,11 @@ var mockCanvasControl = class { static getValue() { return mockValue; } + + static setValue(data) { + mockValue = data; + return true; + } } } @@ -168,6 +173,14 @@ var mockAppMagic = class { return undefined; } } + + static replicatedContexts = class { + static gallery = class { + static bindingContextAt(index) { + return mockAppMagic.Controls.GlobalContextManager.bindingContext; + } + } + } } } } @@ -189,7 +202,11 @@ var OpenAjaxClass = class { static getAuthoringControlContext() { return class { - static _replicatedContext = null; + static _replicatedContext = class { + static manager = class { + static managerId = 'gallery'; + } + }; } } diff --git a/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderCustomPageTest.cs b/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderCustomPageTest.cs index 72b45e626..9911a98ec 100644 --- a/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderCustomPageTest.cs +++ b/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderCustomPageTest.cs @@ -258,6 +258,10 @@ public async Task SetValue(string javaScript, string controlName, string propert .Returns( async (string query) => engine.Evaluate(query).AsBoolean() ); + MockTestInfraFunctions.Setup(m => m.RunJavascriptAsync(It.IsAny(), It.IsAny())) + .Returns( + async (string query, object argument) => engine.Evaluate($"({query})({JsonConvert.SerializeObject(argument)})").AsBoolean() + ); FormulaValue providerValue = null; if (value is string) @@ -276,7 +280,15 @@ public async Task SetValue(string javaScript, string controlName, string propert var result = await provider.SetPropertyAsync(new ItemPath { ControlName = controlName, PropertyName = propertyName }, providerValue); // Assert - + Assert.True(result); + if (value is string) + { + Assert.Equal(value, engine.Evaluate("mockValue").AsString()); + } + else if (value is bool) + { + Assert.Equal(value, engine.Evaluate("mockValue").AsBoolean()); + } } /// @@ -287,13 +299,70 @@ public static IEnumerable GetSetValueData() { // Default Values yield return new object[] { - Common.MockJavaScript("mockPageType = 'custom';mockValue = 'Hello'", "custom"), + Common.MockJavaScript("mockPageType = 'custom';mockValue = 'Initial'", "custom", interfaceResourceNames: new List { "testengine.provider.mda.PowerAppsTestEngineMDA.js", "testengine.provider.mda.PowerAppsTestEngineMDACustom.js" }), "TextInput1", "Text", "Hello" }; } + [Fact] + public async Task SetStructuredValuesThroughCustomPage() + { + const string payload = "a\"});globalThis.__injected=true;({\"b"; + var engine = new Engine(); + engine.Execute(Common.MockJavaScript( + "mockPageType = 'custom';mockValue = null", + "custom", + interfaceResourceNames: new List + { + "testengine.provider.mda.PowerAppsTestEngineMDA.js", + "testengine.provider.mda.PowerAppsTestEngineMDACustom.js" + })); + + MockTestState.Setup(m => m.GetTimeout()).Returns(1000); + MockTestState.Setup(m => m.GetDomain()).Returns(String.Empty); + MockSingleTestInstanceState.Setup(m => m.GetLogger()).Returns(MockLogger.Object); + MockTestInfraFunctions = new Mock(); + MockTestInfraFunctions + .Setup(m => m.RunJavascriptAsync(It.IsAny(), It.IsAny())) + .Returns( + async (string query, object argument) => + engine.Evaluate($"({query})({JsonConvert.SerializeObject(argument)})").AsBoolean()); + + var provider = new ModelDrivenApplicationProvider(MockTestInfraFunctions.Object, MockSingleTestInstanceState.Object, MockTestState.Object); + var galleryPath = new ItemPath + { + ControlName = "TextInput1", + PropertyName = "Text", + ParentControl = new ItemPath + { + ControlName = "Gallery", + PropertyName = "Items", + Index = 0 + } + }; + var record = RecordValue.NewRecordFromFields(new NamedValue("Text", StringValue.New(payload))); + var tableType = RecordType.Empty().Add("Text", FormulaType.String); + var table = TableValue.NewTable(tableType, record); + var guid = Guid.NewGuid(); + var date = new DateTime(2026, 9, 8); + + Assert.True(await provider.SetPropertyAsync(galleryPath, record)); + Assert.Equal($"{{\"Text\":{JsonConvert.SerializeObject(payload)}}}", engine.Evaluate("JSON.stringify(mockValue)").AsString()); + + Assert.True(await provider.SetPropertyAsync(galleryPath, table)); + Assert.Equal($"[{{\"Text\":{JsonConvert.SerializeObject(payload)}}}]", engine.Evaluate("JSON.stringify(mockValue)").AsString()); + + Assert.True(await provider.SetPropertyAsync(galleryPath, GuidValue.New(guid))); + Assert.Equal(guid.ToString(), engine.Evaluate("mockValue").AsString()); + + Assert.True(await provider.SetPropertyAsync(galleryPath, DateValue.NewDateOnly(date))); + Assert.True(engine.Evaluate("typeof mockValue === 'number' && !Number.isNaN(mockValue)").AsBoolean()); + + Assert.False(engine.Evaluate("globalThis.__injected === true").AsBoolean()); + } + [Theory] [MemberData(nameof(GetDataFromParentData))] public async Task GetValueFromParent(ItemPath path, string parentJson, object? expected) diff --git a/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderTest.cs b/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderTest.cs index 9f90e36e1..0375fc6ac 100644 --- a/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderTest.cs +++ b/src/testengine.provider.mda.tests/ModelDrivenApplicationProviderTest.cs @@ -388,6 +388,10 @@ public async Task SetValue(string javaScript, string controlName, string propert .Returns( async (string query) => engine.Evaluate(query).AsBoolean() ); + MockTestInfraFunctions.Setup(m => m.RunJavascriptAsync(It.IsAny(), It.IsAny())) + .Returns( + async (string query, object argument) => engine.Evaluate($"({query})({JsonConvert.SerializeObject(argument)})").AsBoolean() + ); FormulaValue providerValue = null; if (value is string) @@ -422,6 +426,137 @@ public async Task SetValue(string javaScript, string controlName, string propert Assert.Equal(value, controlValue); } + [Fact] + public async Task SetRecordValuePassesUntrustedDataAsStructuredArgument() + { + const string payload = "a\"});globalThis.__injected=true;({\"b"; + const string expression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, args.value)"; + object capturedArgument = null; + + MockSingleTestInstanceState.Setup(m => m.GetLogger()).Returns(MockLogger.Object); + MockTestInfraFunctions + .Setup(m => m.RunJavascriptAsync(expression, It.IsAny())) + .Callback((string _, object argument) => capturedArgument = argument) + .ReturnsAsync(true); + + var provider = new ModelDrivenApplicationProvider(MockTestInfraFunctions.Object, MockSingleTestInstanceState.Object, MockTestState.Object); + var record = RecordValue.NewRecordFromFields(new NamedValue(payload, StringValue.New(payload))); + + var result = await provider.SetPropertyAsync( + new ItemPath { ControlName = "Dropdown1", PropertyName = payload }, + record); + + Assert.True(result); + Assert.DoesNotContain(payload, expression); + var arguments = Assert.IsType>(capturedArgument); + var itemPath = Assert.IsType>(arguments["itemPath"]); + var value = Assert.IsAssignableFrom>(arguments["value"]); + Assert.Equal(payload, itemPath["propertyName"]); + Assert.Equal(payload, value[payload]); + MockTestInfraFunctions.Verify( + m => m.RunJavascriptAsync(expression, It.IsAny()), + Times.Once()); + } + + [Fact] + public async Task SetEmptyRecordSerializesAsJavaScriptObject() + { + const string expression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, args.value)"; + object capturedArgument = null; + + MockSingleTestInstanceState.Setup(m => m.GetLogger()).Returns(MockLogger.Object); + MockTestInfraFunctions + .Setup(m => m.RunJavascriptAsync(expression, It.IsAny())) + .Callback((string _, object argument) => capturedArgument = argument) + .ReturnsAsync(true); + + var provider = new ModelDrivenApplicationProvider(MockTestInfraFunctions.Object, MockSingleTestInstanceState.Object, MockTestState.Object); + var emptyRecord = RecordValue.NewRecordFromFields(Array.Empty()); + + await provider.SetPropertyAsync( + new ItemPath { ControlName = "Dropdown1", PropertyName = "Selected" }, + emptyRecord); + + var arguments = Assert.IsType>(capturedArgument); + Assert.IsType(arguments["value"]); + + var serializedArgument = SerializePlaywrightArgument(capturedArgument); + Assert.Contains("\"k\":\"value\",\"v\":{\"o\":[]", serializedArgument); + Assert.DoesNotContain("\"k\":\"value\",\"v\":{\"a\":[]", serializedArgument); + } + + [Fact] + public async Task SetPropertyPreservesStructuredValueShapes() + { + const string standardExpression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, args.value)"; + const string dateExpression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, Date.parse(args.value))"; + const string payload = "a\"});globalThis.__injected=true;({\"b"; + var guid = Guid.NewGuid(); + var nestedPath = new ItemPath + { + ControlName = "TableRow", + PropertyName = payload, + ParentControl = new ItemPath + { + ControlName = "Gallery", + PropertyName = "Items", + Index = 0 + } + }; + var record = RecordValue.NewRecordFromFields(new NamedValue("Text", StringValue.New(payload))); + var tableType = RecordType.Empty().Add("Text", FormulaType.String); + var table = TableValue.NewTable(tableType, record); + var calls = new List<(string Expression, object Argument)>(); + + MockSingleTestInstanceState.Setup(m => m.GetLogger()).Returns(MockLogger.Object); + MockTestInfraFunctions + .Setup(m => m.RunJavascriptAsync(It.IsAny(), It.IsAny())) + .Callback((string expression, object argument) => calls.Add((expression, argument))) + .ReturnsAsync(true); + + var provider = new ModelDrivenApplicationProvider(MockTestInfraFunctions.Object, MockSingleTestInstanceState.Object, MockTestState.Object); + + await provider.SetPropertyAsync(nestedPath, StringValue.New(payload)); + await provider.SetPropertyAsync(nestedPath, GuidValue.New(guid)); + await provider.SetPropertyAsync(nestedPath, record); + await provider.SetPropertyAsync(nestedPath, table); + await provider.SetPropertyAsync(nestedPath, DateValue.NewDateOnly(new DateTime(2026, 9, 8))); + + Assert.Equal(5, calls.Count); + Assert.Equal(4, calls.Count(call => call.Expression == standardExpression)); + Assert.Single(calls, call => call.Expression == dateExpression); + Assert.All(calls, call => + { + Assert.DoesNotContain(payload, call.Expression); + var serializedArgument = SerializePlaywrightArgument(call.Argument); + Assert.Contains(payload.Replace("\"", "\\u0022"), serializedArgument); + Assert.Contains("\"k\":\"parentControl\"", serializedArgument); + }); + + var standardArguments = calls + .Where(call => call.Expression == standardExpression) + .Select(call => Assert.IsType>(call.Argument)) + .ToList(); + Assert.Equal(payload, standardArguments[0]["value"]); + Assert.Equal(guid, standardArguments[1]["value"]); + Assert.IsType(standardArguments[2]["value"]); + Assert.IsType>(standardArguments[3]["value"]); + } + + private static string SerializePlaywrightArgument(object argument) + { + var assembly = typeof(IPage).Assembly; + var converterType = assembly.GetType("Microsoft.Playwright.Transport.Converters.EvaluateArgumentValueConverter"); + var visitorType = assembly.GetType("Microsoft.Playwright.Transport.Converters.EvaluateArgumentValueConverter+VisitorInfo"); + var handleType = assembly.GetType("Microsoft.Playwright.Core.EvaluateArgumentGuidElement"); + var serializeMethod = converterType.GetMethod("Serialize", BindingFlags.NonPublic | BindingFlags.Static); + var handles = Activator.CreateInstance(typeof(List<>).MakeGenericType(handleType)); + var visitor = Activator.CreateInstance(visitorType, nonPublic: true); + var serialized = serializeMethod.Invoke(null, new[] { argument, handles, visitor }); + + return global::System.Text.Json.JsonSerializer.Serialize(serialized, serialized.GetType()); + } + /// /// Test data for /// @@ -780,7 +915,7 @@ public async Task SetRecordValue(FormulaValue data) // Arrange MockSingleTestInstanceState.Setup(m => m.GetLogger()).Returns(MockLogger.Object); MockTestInfraFunctions = new Mock(); - MockTestInfraFunctions.Setup(m => m.RunJavascriptAsync(It.IsAny())); + MockTestInfraFunctions.Setup(m => m.RunJavascriptAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); var provider = new ModelDrivenApplicationProvider(MockTestInfraFunctions.Object, MockSingleTestInstanceState.Object, MockTestState.Object); diff --git a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs index d4dee61aa..44b90fbc3 100644 --- a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs +++ b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs @@ -451,37 +451,25 @@ public async Task SetPropertyAsync(ItemPath itemPath, FormulaValue value) { try { - Object objectValue = null; - switch (value.Type) { - case (NumberType): - objectValue = ((NumberValue)value).Value; - break; - case (StringType): - objectValue = ((StringValue)value).Value; - break; - case (BooleanType): - objectValue = ((BooleanValue)value).Value; - break; - case (GuidType): - objectValue = ((GuidValue)value).Value; - break; - case (DateType): + case DateType: return await SetPropertyDateAsync(itemPath, (DateValue)value); - case (RecordType): + case RecordType: return await SetPropertyRecordAsync(itemPath, (RecordValue)value); - case (TableType): + case TableType: return await SetPropertyTableAsync(itemPath, (TableValue)value); + case NumberType: + case StringType: + case BooleanType: + case GuidType: + break; default: throw new ArgumentException("SetProperty must be a valid type."); } ValidateItemPath(itemPath, false); - - var expression = $"PowerAppsTestEngine.setPropertyValue({JsonConvert.SerializeObject(itemPath)}, {JsonConvert.SerializeObject(objectValue)})"; - await TestInfraFunctions.RunJavascriptAsync(expression); - return true; + return await SetPropertyValueAsync(itemPath, ConvertFormulaValue(value)); } catch (Exception ex) { @@ -496,16 +484,13 @@ public async Task SetPropertyDateAsync(ItemPath itemPath, DateValue value) { ValidateItemPath(itemPath, false); - var itemPathString = JsonConvert.SerializeObject(itemPath); - var propertyNameString = JsonConvert.SerializeObject(itemPath.PropertyName); var recordValue = value.GetConvertedValue(null); // TODO - Set the Xrm SDK Value and update state for any JS to run // Date.parse() parses the date to unix timestamp - var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},Date.parse(\"{recordValue}\"))"; - - return await TestInfraFunctions.RunJavascriptAsync(expression); + const string expression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, Date.parse(args.value))"; + return await TestInfraFunctions.RunJavascriptAsync(expression, CreateSetPropertyArguments(itemPath, recordValue.ToString("o"))); } catch (Exception ex) { @@ -519,18 +504,7 @@ public async Task SetPropertyRecordAsync(ItemPath itemPath, RecordValue va try { ValidateItemPath(itemPath, false); - - var itemPathString = JsonConvert.SerializeObject(itemPath); - var propertyNameString = JsonConvert.SerializeObject(itemPath.PropertyName); - string checkVal = "null"; - if (value != null) - { - checkVal = FormatValue(value); - } - - var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{checkVal})"; - - return await TestInfraFunctions.RunJavascriptAsync(expression); + return await SetPropertyValueAsync(itemPath, ConvertFormulaValue(value)); } catch (Exception ex) { @@ -539,51 +513,61 @@ public async Task SetPropertyRecordAsync(ItemPath itemPath, RecordValue va } } - /// - /// Convert Power Fx formula value to the string representation - /// - /// The vaue to convert - /// - /// - private string FormatValue(FormulaValue value) + private static object? ConvertFormulaValue(FormulaValue value) { - //TODO: Handle special case of DateTime As unix time to DateTime return value switch { - BlankValue blankValue => "null", - StringValue stringValue => $"\"{stringValue.Value}\"", - NumberValue numberValue => numberValue.Value.ToString(), - DecimalValue decimalValue => decimalValue.Value.ToString(), - BooleanValue booleanValue => booleanValue.Value.ToString().ToLower(), - // Assume all dates should be in UTC - DateValue dateValue => $"\"{dateValue.GetConvertedValue(TimeZoneInfo.Utc).ToString("o")}\"", // ISO 8601 format - DateTimeValue dateTimeValue => $"\"{dateTimeValue.GetConvertedValue(TimeZoneInfo.Utc).ToString("o")}\"", // ISO 8601 format - RecordValue recordValue => FormatRecordValue(recordValue), - TableValue tableValue => FormatTableValue(tableValue), + BlankValue => null, + StringValue stringValue => stringValue.Value, + NumberValue numberValue => numberValue.Value, + DecimalValue decimalValue => decimalValue.Value, + BooleanValue booleanValue => booleanValue.Value, + GuidValue guidValue => guidValue.Value, + DateValue dateValue => dateValue.GetConvertedValue(TimeZoneInfo.Utc).ToString("o"), + DateTimeValue dateTimeValue => dateTimeValue.GetConvertedValue(TimeZoneInfo.Utc).ToString("o"), + RecordValue recordValue => ConvertRecordValue(recordValue), + TableValue tableValue => tableValue.Rows.Select(row => ConvertFormulaValue(row.Value)).ToList(), _ => throw new ArgumentException("Unsupported FormulaValue type") }; } - /// - /// Convert a Power Fx object to String Representation of the Record - /// - /// The record to be converted - /// Power Fx representation - private string FormatRecordValue(RecordValue recordValue) + private static ExpandoObject ConvertRecordValue(RecordValue recordValue) { - var fields = recordValue.Fields.Select(field => $"'{field.Name}': {FormatValue(field.Value)}"); - return $"{{{string.Join(", ", fields)}}}"; + var result = new ExpandoObject(); + var fields = (IDictionary)result; + + foreach (var field in recordValue.Fields) + { + fields[field.Name] = ConvertFormulaValue(field.Value); + } + + return result; } - /// - /// Convert the Power Fx table into string representation - /// - /// The table to be converted - /// The string representation of all rows of the table - private string FormatTableValue(TableValue tableValue) + private static Dictionary CreateSetPropertyArguments(ItemPath itemPath, object? value) { - var rows = tableValue.Rows.Select(row => FormatValue(row.Value)); - return $"[{string.Join(", ", rows)}]"; + return new Dictionary + { + ["itemPath"] = ConvertItemPath(itemPath), + ["value"] = value + }; + } + + private static Dictionary ConvertItemPath(ItemPath itemPath) + { + return new Dictionary + { + ["controlName"] = itemPath.ControlName, + ["index"] = itemPath.Index, + ["parentControl"] = itemPath.ParentControl == null ? null : ConvertItemPath(itemPath.ParentControl), + ["propertyName"] = itemPath.PropertyName + }; + } + + private Task SetPropertyValueAsync(ItemPath itemPath, object? value) + { + const string expression = "(args) => PowerAppsTestEngine.setPropertyValue(args.itemPath, args.value)"; + return TestInfraFunctions.RunJavascriptAsync(expression, CreateSetPropertyArguments(itemPath, value)); } public async Task SetPropertyTableAsync(ItemPath itemPath, TableValue tableValue) @@ -591,14 +575,7 @@ public async Task SetPropertyTableAsync(ItemPath itemPath, TableValue tabl try { ValidateItemPath(itemPath, false); - - var itemPathString = JsonConvert.SerializeObject(itemPath); - - var tabelValue = ConvertTableValueToJson(tableValue); - - var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{tabelValue})"; - - return await TestInfraFunctions.RunJavascriptAsync(expression); + return await SetPropertyValueAsync(itemPath, ConvertFormulaValue(tableValue)); } catch (Exception ex) { @@ -607,23 +584,6 @@ public async Task SetPropertyTableAsync(ItemPath itemPath, TableValue tabl } } - private string ConvertTableValueToJson(TableValue tableValue) - { - var list = new List>(); - - foreach (var record in tableValue.Rows) - { - var dict = new Dictionary(); - foreach (var field in record.Value.Fields) - { - dict[field.Name] = field.Value.ToObject(); - } - list.Add(dict); - } - - return JsonConvert.SerializeObject(list, Formatting.Indented); - } - private void ValidateItemPath(ItemPath itemPath, bool requirePropertyName) { if (string.IsNullOrEmpty(itemPath.ControlName)) diff --git a/src/testengine.provider.mda/PowerAppsTestEngineMDACustom.js b/src/testengine.provider.mda/PowerAppsTestEngineMDACustom.js index aa07faba5..2065032ce 100644 --- a/src/testengine.provider.mda/PowerAppsTestEngineMDACustom.js +++ b/src/testengine.provider.mda/PowerAppsTestEngineMDACustom.js @@ -16,68 +16,12 @@ class PowerAppsModelDrivenCanvas { return AppDependencyHandler.containers[key[0]].AppMagic; } - static executePublishedAppScript(scriptToExecute) { - var promise = new Promise((resolve) => { - var result = eval(scriptToExecute); - resolve(result) - }); - - return promise; - } - static getOngoingActionsInPublishedApp() { - return PowerAppsModelDrivenCanvas.executePublishedAppScript("AppDependencyHandler.containers[Object.keys(AppDependencyHandler.containers).filter(k => k != AppDependencyHandler.prebuiltContainerId)[0]].AppMagic.AuthoringTool.Runtime.existsOngoingAsync()"); + return PowerAppsModelDrivenCanvas.getAppMagic().AuthoringTool.Runtime.existsOngoingAsync(); } static getControlObjectModel() { - return PowerAppsModelDrivenCanvas.executePublishedAppScript("PowerAppsTestEngine.buildControlObjectModel()"); - } - - static getPropertyValueFromPublishedApp(itemPath) { - var script = `PowerAppsModelDrivenCanvas.getPropertyValueFromControl(${JSON.stringify(itemPath)})`; - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); - } - - static getPropertyValueFromPublishedApp(itemPath) { - var script = `PowerAppsModelDrivenCanvas.getPropertyValueFromControl(${JSON.stringify(itemPath)})`; - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); - } - - static selectControl(itemPath) { - var script = `PowerAppsModelDrivenCanvas.selectControl(${JSON.stringify(itemPath)})`; - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); - } - - static interactWithControl(itemPath, value) { - var script = ""; - if (isArray(Object.values(value))) { - var valuesJsonArr = []; - var values = Object.values(value); - for (var index in values) { - valuesJsonArr[`${index}`] = `${JSON.stringify(values[index])}`; - } - var valueJson = `{"${itemPath.propertyName}":${valuesJsonArr}}`; - script = `PowerAppsModelDrivenCanvas.interactWithControl(${JSON.stringify(itemPath)}, ${valueJson})`; - } else { - var valueJson = `{"${itemPath.propertyName}":${value}}`; - script = `PowerAppsModelDrivenCanvas.interactWithControl(${JSON.stringify(itemPath)}, ${valueJson})`; - } - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); - } - - static setPropertyValueForControl(itemPath, value) { - if (typeof value == "object") { - return PowerAppsModelDrivenCanvas.interactWithControl(itemPath, value); - } else if (typeof value == "string") { - value = JSON.stringify(value); - } - var script = `PowerAppsModelDrivenCanvas.setPropertyValueForControl(${JSON.stringify(itemPath)}, ${value})`; - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); - } - - static fetchArrayItemCount(itemPath) { - var script = `PowerAppsModelDrivenCanvas.fetchArrayItemCount(${JSON.stringify(itemPath)})`; - return PowerAppsModelDrivenCanvas.executePublishedAppScript(script); + return PowerAppsModelDrivenCanvas.buildControlObjectModel(); } static isArray(obj) {