From 672cd30bf6f48b587d45ce910c8d865606a10418 Mon Sep 17 00:00:00 2001 From: v-raghulraja Date: Tue, 10 Dec 2024 21:13:17 +0530 Subject: [PATCH 1/4] Intial Commit --- .../ClassicInputControls_testPlan.fx.yaml | 98 +++++++++++++++++++ .../PowerFxModel/ControlRecordValue.cs | 22 +++-- .../PowerAppFunctions.cs | 3 + .../ModelDrivenApplicationProvider.cs | 29 +++++- 4 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml diff --git a/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml b/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml new file mode 100644 index 00000000..95418b44 --- /dev/null +++ b/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml @@ -0,0 +1,98 @@ +testSuite: + testSuiteName: InputControlRuntimeValidation + testSuiteDescription: Validate runtime behavior for classic input controls, ensuring positive and negative test cases are handled appropriately. + persona: User1 + appLogicalName: input_control_runtime_testing + + testCases: + - testCaseName: Runtime_Label_Text_Case + testCaseDescription: Verify the Label control updates its Text property correctly at runtime. + testSteps: | + SetProperty(Label1.Text, "Runtime Update"); + Assert(Label1.Text = "Runtime Update", "Validate Label1 displays the correct runtime text."); + + - testCaseName: Select Button Once + testCaseDescription: Verify that the button performs the correct action when selected once. + testSteps: | + Select(Button1); + Assert(Label1.Text = "Button Clicked!", "Verify button performs the correct action when selected once"); + + - testCaseName: Select Button Twice + testCaseDescription: Verify that the button performs the correct action when selected twice. + testSteps: | + Select(Button1); + Select(Button1); + Assert(Label1.Text = "Button Clicked!", "Verify button performs the correct action when selected twice"); + + - testCaseName: Enter Text in TextBox + testCaseDescription: Verify that the text box accepts and displays input correctly. + testSteps: | + SetProperty(TextInput1.Value, "Sample Text"); + Assert(TextInput1.Value = "Sample Text", "Verify text box displays the input text correctly"); + + - testCaseName: Test Visible Property + testCaseDescription: Verify that the visibility can be toggled correctly. + testSteps: | + SetProperty(Checkbox1.Visible, true); + Assert(Checkbox1.Visible = true, "Expected Checkbox1.Visible to be true"); + + - testCaseName: Test Checked Property + testCaseDescription: Verify that the checked state can be set and retrieved correctly. + testSteps: | + SetProperty(Checkbox1.Checked, true); + Assert(Checkbox1.Checked = true, "Expected Checkbox1.Checked to be true"); + + - testCaseName: Test SelectedItems Property + testCaseDescription: Verify that the SelectedItems property can be set and retrieved correctly. + testSteps: | + SetProperty('Combobox1'.SelectedItems, Table({'Value1':"Item 7",'Value2':7,'Value3':70}, + {'Value1':"Item 10",'Value2':10,'Value3':100},{'Value1':"Item 12",'Value2':12,'Value3':120})); + Assert(CountRows('Combobox1'.SelectedItems) = 3, "Validated Succesfully"); + + - testCaseName: Test SelectedDate Property + testCaseDescription: Verify that the SelectedDate property can be set and retrieved correctly. + testSteps: | + SetProperty(DatePicker1.SelectedDate, Date(2024,10,01)); + Assert(DatePicker1.SelectedDate = Date(2024,10,01), "Checking the SelectedDate property"); + + - testCaseName: Test RadioButton DefaultSelectedItems Property + testCaseDescription: Verify that the RadioButton DefaultSelectedItems property can be set and retrieved correctly. + testSteps: | + SetProperty(RadioGroup1.DefaultSelectedItems, Table({'Value1':"Item 7"})); + Assert(CountRows('RadioGroup1'.SelectedItems) = 1, "Validated Succesfully"); + + - testCaseName: Test Value Property + testCaseDescription: Verify that the Value property can be set and retrieved correctly. + testSteps: | + SetProperty(Slider1.Value, 50); + Assert(Slider1.Value = 50, "Checking the Value property"); + SetProperty(Slider1.Value, 25); + Assert(Slider1.Value = 25, "Checking the Value property"); + SetProperty(Slider1.Value, 100); + Assert(Slider1.Value = 100, "Checking the Value property"); + SetProperty(Slider1.Value, 75); + Assert(Slider1.Value = 75, "Checking the Value property"); + + - testCaseName: Test_Toggle_User_Action + testCaseDescription: Verify that user interaction with the Toggle control is correctly reflected in its state. + testSteps: | + SetProperty(Toggle1.Checked, true); + Assert(Toggle1.Checked = true, "User action correctly toggled Toggle1 to on."); + SetProperty(Toggle1.Checked, false); + Assert(Toggle1.Checked = false, "User action correctly toggled Toggle1 to off."); + +testSettings: + headless: false + locale: "en-US" + recordVideo: true + extensionModules: + enable: true + browserConfigurations: + - browser: Chromium + channel: msedge + +environmentVariables: + users: + - personaName: User1 + emailKey: user1Email + passwordKey: NotNeeded diff --git a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs index 90dfd2e8..eb853e03 100644 --- a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs +++ b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Globalization; using System.Linq; using System.Text; @@ -126,16 +127,23 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out result = BooleanValue.New(bool.Parse(jsPropertyValueModel.PropertyValue)); return true; } + else if (fieldType is ColorType) + { + result = ColorValue.New(Color.FromArgb(jsPropertyValueModel.PropertyValue[0], jsPropertyValueModel.PropertyValue[1], jsPropertyValueModel.PropertyValue[2])); + return true; + } else if (fieldType is DateTimeType) { - double milliseconds; + long milliseconds; // When converted from DateTime to a string, a value from Wait() gets roundtripped into a UTC Timestamp format // The compiler does not register this format as a valid DateTime format // Because of this, we have to manually convert it into a DateTime - if (double.TryParse(jsPropertyValueModel.PropertyValue, out milliseconds)) + if (long.TryParse(jsPropertyValueModel.PropertyValue, out milliseconds)) { - var trueDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(milliseconds); + var dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); + DateTime trueDateTime = dateTimeOffset.LocalDateTime; + //var trueDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(milliseconds); result = DateTimeValue.New(trueDateTime.Date); } // When converted from DateTime to a string, a value from SetProperty() retains it's MMDDYYYY hh::mm::ss format @@ -149,14 +157,16 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out } else if (fieldType is DateType) { - double milliseconds; + long milliseconds; // When converted from Date to a string, a value from Wait() gets roudntripped into a UTC Timestamp format // The compiler does not register this format as a valid DateTime format // Because of this, we have to manually convert it into a DateTime - if (double.TryParse(jsPropertyValueModel.PropertyValue, out milliseconds)) + if (long.TryParse(jsPropertyValueModel.PropertyValue, out milliseconds)) { - var trueDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(milliseconds); + var dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); + DateTime trueDateTime = dateTimeOffset.LocalDateTime; + //var trueDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(milliseconds); result = DateValue.NewDateOnly(trueDateTime.Date); } // When converted from DateTime to a string, a value from SetProperty() retains it's MMDDYYYY hh::mm::ss format diff --git a/src/testengine.provider.canvas/PowerAppFunctions.cs b/src/testengine.provider.canvas/PowerAppFunctions.cs index e81cb0ed..e46051dd 100644 --- a/src/testengine.provider.canvas/PowerAppFunctions.cs +++ b/src/testengine.provider.canvas/PowerAppFunctions.cs @@ -276,6 +276,9 @@ public async Task SetPropertyAsync(ItemPath itemPath, FormulaValue value) return await SetPropertyRecordAsync(itemPath, (RecordValue)value); case (TableType): return await SetPropertyTableAsync(itemPath, (TableValue)value); + case (ColorType): + objectValue = ((ColorValue)value).Value; + break; default: throw new ArgumentException("SetProperty must be a valid type."); } diff --git a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs index c5b2192e..4ad63e94 100644 --- a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs +++ b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs @@ -18,6 +18,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using testengine.provider.mda; +using System.Drawing; [assembly: InternalsVisibleTo("testengine.provider.mda.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] namespace Microsoft.PowerApps.TestEngine.Providers @@ -157,6 +158,7 @@ private async Task GetPropertyValueFromControlAsync(ItemPath itemPath) { case "disabled": case "visible": + case "checked": return (T)(object)("{PropertyValue: " + value.ToString().ToLower() + "}"); default: switch (value.GetType().ToString()) @@ -391,6 +393,9 @@ public async Task SetPropertyAsync(ItemPath itemPath, FormulaValue value) case (BooleanType): objectValue = ((BooleanValue)value).Value; break; + case (ColorType): + objectValue = ((ColorValue)value).Value; + break; case (DateType): return await SetPropertyDateAsync(itemPath, (DateValue)value); case (RecordType): @@ -427,7 +432,7 @@ public async Task SetPropertyDateAsync(ItemPath itemPath, DateValue value) // 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},{{{propertyNameString}:Date.parse(\"{recordValue}\")}})"; + var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},Date.parse(\"{recordValue}\"))"; return await TestInfraFunctions.RunJavascriptAsync(expression); } @@ -451,7 +456,7 @@ public async Task SetPropertyRecordAsync(ItemPath itemPath, RecordValue va RecordValueObject json = new RecordValueObject(val); var checkVal = JsonConvert.SerializeObject(json); - var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{{{propertyNameString}:{checkVal}}})"; + var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{checkVal})"; return await TestInfraFunctions.RunJavascriptAsync(expression); } @@ -486,11 +491,10 @@ public async Task SetPropertyTableAsync(ItemPath itemPath, TableValue tabl } } } - var checkVal = JsonConvert.SerializeObject(jsonArr); + var checkVal = ConvertTableValueToJson(tableValue); // TODO - Set the Xrm SDK Value and update state for any JS to run - var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{{{propertyNameString}:{checkVal}}})"; - + var expression = $"PowerAppsTestEngine.setPropertyValue({itemPathString},{checkVal})"; return await TestInfraFunctions.RunJavascriptAsync(expression); } catch (Exception ex) @@ -623,5 +627,20 @@ private static string GetQueryParametersForTestUrl(string tenantId, string addit { return $"?tenantId={tenantId}&source=testengine{additionalQueryParams}"; } + + 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); + } } } From 1ffef27560e9c5eb9d05c42cdb8a1cd7a147a0e1 Mon Sep 17 00:00:00 2001 From: v-raghulraja Date: Thu, 12 Dec 2024 18:08:13 +0530 Subject: [PATCH 2/4] 2nd set of changes for classic controls --- .../ClassicInputControls_testPlan.fx.yaml | 210 ++++++++++++------ samples/mdaclassicinputcontrols/README.md | 34 +++ .../PowerFxModel/ControlRecordValue.cs | 2 +- .../ModelDrivenApplicationProvider.cs | 5 + 4 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 samples/mdaclassicinputcontrols/README.md diff --git a/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml b/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml index 95418b44..18a10336 100644 --- a/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml +++ b/samples/mdaclassicinputcontrols/ClassicInputControls_testPlan.fx.yaml @@ -1,86 +1,156 @@ testSuite: - testSuiteName: InputControlRuntimeValidation - testSuiteDescription: Validate runtime behavior for classic input controls, ensuring positive and negative test cases are handled appropriately. + testSuiteName: Classic Input Controls + testSuiteDescription: Verifies that the classic input controls work correctly. persona: User1 - appLogicalName: input_control_runtime_testing - - testCases: - - testCaseName: Runtime_Label_Text_Case - testCaseDescription: Verify the Label control updates its Text property correctly at runtime. - testSteps: | - SetProperty(Label1.Text, "Runtime Update"); - Assert(Label1.Text = "Runtime Update", "Validate Label1 displays the correct runtime text."); - - - testCaseName: Select Button Once - testCaseDescription: Verify that the button performs the correct action when selected once. - testSteps: | + appLogicalName: classic_input_controls_app + + testCases: + - testCaseName: Test Label Property + testCaseDescription: Verify that the label's Text property can be set and retrieved correctly. + testSteps: | + SetProperty(Label1.Text, "Check it"); + Assert(Label1.Text = "Check it", "Expected Label1.Text to be 'Check it'"); + + - testCaseName: Test Label Empty Text + testCaseDescription: Verify that the label's Text property can be set to an empty string. + testSteps: | + SetProperty(Label1.Text, ""); + Assert(Label1.Text = "", "Expected Label1.Text to be empty"); + + - testCaseName: Test Label Long Text + testCaseDescription: Verify that the label's Text property can handle long text. + testSteps: | + SetProperty(Label1.Text, "This is a very long text to check if the label can handle it without any issues."); + Assert(Label1.Text = "This is a very long text to check if the label can handle it without any issues.", "Expected Label1.Text to be the long text"); + + - testCaseName: Test Label Numeric Text + testCaseDescription: Verify that the label's Text property can handle numeric text. + testSteps: | + SetProperty(Label1.Text, "1234567890"); + Assert(Label1.Text = "1234567890", "Expected Label1.Text to be numeric text"); + + - testCaseName: Test Label Special Characters + testCaseDescription: Verify the Label control updates its Text property correctly with special characters at runtime. + testSteps: | + SetProperty(Label1.Text, "Hello@#$%^&*!"); + Assert(Label1.Text = "Hello@#$%^&*!", "Ensuring the Label displays special characters correctly."); + + - testCaseName: Test TextInput Sample Text + testCaseDescription: Verify that the text box accepts and displays input correctly. + testSteps: | + SetProperty(TextInput1.Value, "Sample Text"); + Assert(TextInput1.Value = "Sample Text", "Verify text box displays the input text correctly"); + + - testCaseName: Test TextInput Empty + testCaseDescription: Verify that the TextInput control can be set to an empty string at runtime. + testSteps: | + SetProperty(TextInput1.Value, ""); + Assert(TextInput1.Value = "", "Expected TextInput1.Value to be empty."); + + - testCaseName: Test TextInput Long Text + testCaseDescription: Verify that the TextInput control can handle long text at runtime. + testSteps: | + SetProperty(TextInput1.Value, "This is a very long text to check if the TextInput can handle it without any issues."); + Assert(TextInput1.Value = "This is a very long text to check if the TextInput can handle it without any issues.", "Expected TextInput1.Value to be the long text."); + + - testCaseName: Test TextInput Special Characters + testCaseDescription: Verify that the TextInput control updates its Value property correctly with special characters at runtime. + testSteps: | + SetProperty(TextInput1.Value, "Special@#$%^&*()!"); + Assert(TextInput1.Value = "Special@#$%^&*()!", "Ensuring the TextInput displays special characters correctly."); + + - testCaseName: Test TextInput Numeric Text + testCaseDescription: Verify that the TextInput control can handle numeric text at runtime. + testSteps: | + SetProperty(TextInput1.Value, "1234567890"); + Assert(TextInput1.Value = "1234567890", "Expected TextInput1.Value to be numeric text."); + + - testCaseName: Select Button Once + testCaseDescription: Verify that the button performs the correct action when selected once. + testSteps: | Select(Button1); Assert(Label1.Text = "Button Clicked!", "Verify button performs the correct action when selected once"); - - testCaseName: Select Button Twice - testCaseDescription: Verify that the button performs the correct action when selected twice. - testSteps: | + - testCaseName: Select Button Twice + testCaseDescription: Verify that the button performs the correct action when selected twice. + testSteps: | Select(Button1); Select(Button1); Assert(Label1.Text = "Button Clicked!", "Verify button performs the correct action when selected twice"); - - testCaseName: Enter Text in TextBox - testCaseDescription: Verify that the text box accepts and displays input correctly. - testSteps: | - SetProperty(TextInput1.Value, "Sample Text"); - Assert(TextInput1.Value = "Sample Text", "Verify text box displays the input text correctly"); - - - testCaseName: Test Visible Property - testCaseDescription: Verify that the visibility can be toggled correctly. - testSteps: | - SetProperty(Checkbox1.Visible, true); - Assert(Checkbox1.Visible = true, "Expected Checkbox1.Visible to be true"); - - - testCaseName: Test Checked Property - testCaseDescription: Verify that the checked state can be set and retrieved correctly. - testSteps: | - SetProperty(Checkbox1.Checked, true); - Assert(Checkbox1.Checked = true, "Expected Checkbox1.Checked to be true"); + - testCaseName: Test Visible Property + testCaseDescription: Verify that the visibility can be toggled correctly. + testSteps: | + SetProperty(Checkbox1.Visible, true); + Assert(Checkbox1.Visible = true, "Expected Checkbox1.Visible to be true"); + + - testCaseName: Test Checked Property + testCaseDescription: Verify that the checked state can be set and retrieved correctly. + testSteps: | + SetProperty(Checkbox1.Checked, true); + Assert(Checkbox1.Checked = true, "Expected Checkbox1.Checked to be true"); - - testCaseName: Test SelectedItems Property - testCaseDescription: Verify that the SelectedItems property can be set and retrieved correctly. - testSteps: | + - testCaseName: Test SelectedItems Property + testCaseDescription: Verify that the SelectedItems property can be set and retrieved correctly. + testSteps: | SetProperty('Combobox1'.SelectedItems, Table({'Value1':"Item 7",'Value2':7,'Value3':70}, {'Value1':"Item 10",'Value2':10,'Value3':100},{'Value1':"Item 12",'Value2':12,'Value3':120})); - Assert(CountRows('Combobox1'.SelectedItems) = 3, "Validated Succesfully"); - - - testCaseName: Test SelectedDate Property - testCaseDescription: Verify that the SelectedDate property can be set and retrieved correctly. - testSteps: | - SetProperty(DatePicker1.SelectedDate, Date(2024,10,01)); - Assert(DatePicker1.SelectedDate = Date(2024,10,01), "Checking the SelectedDate property"); - - - testCaseName: Test RadioButton DefaultSelectedItems Property - testCaseDescription: Verify that the RadioButton DefaultSelectedItems property can be set and retrieved correctly. - testSteps: | - SetProperty(RadioGroup1.DefaultSelectedItems, Table({'Value1':"Item 7"})); - Assert(CountRows('RadioGroup1'.SelectedItems) = 1, "Validated Succesfully"); - - - testCaseName: Test Value Property - testCaseDescription: Verify that the Value property can be set and retrieved correctly. - testSteps: | - SetProperty(Slider1.Value, 50); - Assert(Slider1.Value = 50, "Checking the Value property"); - SetProperty(Slider1.Value, 25); - Assert(Slider1.Value = 25, "Checking the Value property"); - SetProperty(Slider1.Value, 100); - Assert(Slider1.Value = 100, "Checking the Value property"); - SetProperty(Slider1.Value, 75); - Assert(Slider1.Value = 75, "Checking the Value property"); - - - testCaseName: Test_Toggle_User_Action - testCaseDescription: Verify that user interaction with the Toggle control is correctly reflected in its state. - testSteps: | - SetProperty(Toggle1.Checked, true); - Assert(Toggle1.Checked = true, "User action correctly toggled Toggle1 to on."); - SetProperty(Toggle1.Checked, false); - Assert(Toggle1.Checked = false, "User action correctly toggled Toggle1 to off."); + Assert(CountRows('Combobox1'.SelectedItems) = 3, "Validated Successfully"); + + - testCaseName: Test SelectedDate Property + testCaseDescription: Verify that the SelectedDate property can be set and retrieved correctly. + testSteps: | + SetProperty(DatePicker1.SelectedDate, Date(2024,10,01)); + Assert(DatePicker1.SelectedDate = Date(2024,10,01), "Checking the SelectedDate property"); + + - testCaseName: Test RadioGroup DefaultSelectedItems Property + testCaseDescription: Verify that the RadioGroup control's DefaultSelectedItems property can be set and retrieved correctly. + testSteps: | + SetProperty(RadioGroup1.DefaultSelectedItems, Table({Value1:"Item 7"})); + Assert(CountRows(RadioGroup1.SelectedItems) = 1, "Validated Successfully"); + - testCaseName: Test RadioGroup Visible Property + testCaseDescription: Verify that the RadioGroup control's Visible property can be toggled correctly. + testSteps: | + SetProperty(RadioGroup1.Visible, true); + Assert(RadioGroup1.Visible = true, "RadioGroup1 is visible."); + SetProperty(RadioGroup1.Visible, false); + Assert(RadioGroup1.Visible = false, "RadioGroup1 is not visible."); + + - testCaseName: Test RadioGroup Items Property + testCaseDescription: Verify that the RadioGroup control's Items property can be set and retrieved correctly. + testSteps: | + SetProperty(RadioGroup1.Items, Table({Value1:"Item 1"}, {Value1:"Item 2"}, {Value1:"Item 3"})); + Assert(CountRows(RadioGroup1.Items) = 3, "RadioGroup1 items count is 3."); + + - testCaseName: Test Slider User Interactions + testCaseDescription: Verify that the Slider control's Value property can be set and retrieved correctly, and validate its Min and Max properties. + testSteps: | + SetProperty(Slider1.Value, 50); + Assert(Slider1.Value = 50, "Checking the Value property"); + SetProperty(Slider1.Value, 25); + Assert(Slider1.Value = 25, "Checking the Value property"); + SetProperty(Slider1.Value, 100); + Assert(Slider1.Value = 100, "Checking the Value property"); + SetProperty(Slider1.Value, 75); + Assert(Slider1.Value = 75, "Checking the Value property"); + SetProperty(Slider1.Min, 0); + Assert(Slider1.Min = 0, "Slider1 minimum value is set to 0."); + SetProperty(Slider1.Max, 100); + Assert(Slider1.Max = 100, "Slider1 maximum value is set to 100."); + + - testCaseName: Test Toggle User Interactions + testCaseDescription: Verify that user interaction with the Toggle control is correctly reflected in its Checked and Visible properties. + testSteps: | + SetProperty(Toggle1.Checked, true); + Assert(Toggle1.Checked = true, "User action correctly toggled Toggle1 to on."); + SetProperty(Toggle1.Checked, false); + Assert(Toggle1.Checked = false, "User action correctly toggled Toggle1 to off."); + SetProperty(Toggle1.Visible, true); + Assert(Toggle1.Visible = true, "Toggle1 is visible."); + SetProperty(Toggle1.Visible, false); + Assert(Toggle1.Visible = false, "Toggle1 is not visible."); + testSettings: headless: false locale: "en-US" diff --git a/samples/mdaclassicinputcontrols/README.md b/samples/mdaclassicinputcontrols/README.md new file mode 100644 index 00000000..ccbae1bc --- /dev/null +++ b/samples/mdaclassicinputcontrols/README.md @@ -0,0 +1,34 @@ + +# Overview + +This Power Apps Test Engine sample demonstrates how to assert and interact with the values of classic input controls in a model-driven application form. + +## Usage + +1. **Build the Test Engine Solution** + Ensure the Power Apps Test Engine solution is built and ready to be executed. + +2. **Get the URL of the Model-Driven Application Form** + Acquire the URL of the specific Model-Driven Application form that you want to test. + +3. **Modify the classicinputcontrols_testPlan.fx.yaml** + Update the YAML file to assert expected values of the shape controls. + + > [!NOTE] The controls are referenced using the [logical name](https://learn.microsoft.com/power-apps/developer/data-platform/entity-metadata#table-names). + +4. **Update the Domain URL for Your Model-Driven Application** + + | URL Part | Description | + |----------|-------------| + | `appid=a1234567-cccc-44444-9999-a123456789123` | The unique identifier of your model-driven application. | + | `etn=shape` | The name of the entity being validated. | + | `id=26bafa27-ca7d-ee11-8179-0022482a91f4` | The unique identifier of the record being edited. | + | `pagetype=custom` | The type of page to open. | + +5. **Execute the Test for Custom Page** + Change the example below to the URL of your organization: + +```pwsh +cd bin\Debug\PowerAppsEngine +dotnet PowerAppsTestEngine.dll -i ..\..\..\samples\mdaclassicinputcontrols\classicinputcontrols_testPlan.fx.yaml -e 00000000-0000-0000-0000-11112223333 -t 11112222-3333-4444-5555-666677778888 -u browser -p mda -d "https://contoso.crm4.dynamics.com/main.aspx?appid=9e9c25f3-1851-ef11-bfe2-6045bd8f802c&pagetype=custom&name=cr7d6_displaycontrols_7009b" +``` diff --git a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs index eb853e03..39d35b5c 100644 --- a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs +++ b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs @@ -111,7 +111,7 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out if (jsPropertyValueModel != null) { - if (string.IsNullOrEmpty(jsPropertyValueModel.PropertyValue)) + if (string.IsNullOrEmpty(jsPropertyValueModel.PropertyValue) && fieldType is not StringType) { result = null; return false; diff --git a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs index 4ad63e94..15c20353 100644 --- a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs +++ b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs @@ -164,6 +164,11 @@ private async Task GetPropertyValueFromControlAsync(ItemPath itemPath) switch (value.GetType().ToString()) { case "System.String": + var stringValue = value.ToString(); + if (string.IsNullOrEmpty(stringValue)) + { + return (T)(object)("{\"PropertyValue\": \"\"}"); + } return (T)(object)("{PropertyValue: '" + value.ToString() + "'}"); default: return (T)(object)("{PropertyValue: " + value.ToString() + "}"); From 2539ba031b2b0cfa30a3c74a19d413cb844354f9 Mon Sep 17 00:00:00 2001 From: v-raghulraja Date: Thu, 19 Dec 2024 11:04:44 +0530 Subject: [PATCH 3/4] Enable PowerApps MDA Classic Input controls for Custom Pages with Test Engine Support --- .../ComboBox-Simulated_testPlan.fx.yaml | 76 +++++++++++++++++++ .../PowerAppFunctions.cs | 3 - .../ModelDrivenApplicationProvider.cs | 17 +---- 3 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 samples/mdaclassicinputcontrols/ComboBox-Simulated_testPlan.fx.yaml diff --git a/samples/mdaclassicinputcontrols/ComboBox-Simulated_testPlan.fx.yaml b/samples/mdaclassicinputcontrols/ComboBox-Simulated_testPlan.fx.yaml new file mode 100644 index 00000000..2103fb30 --- /dev/null +++ b/samples/mdaclassicinputcontrols/ComboBox-Simulated_testPlan.fx.yaml @@ -0,0 +1,76 @@ +testSuite: + testSuiteName: ComboBox Data Load + testSuiteDescription: Load data into Combobox1 + persona: User1 + appLogicalName: NotNeeded + onTestSuiteStart: | + = Experimental.SimulateDataverse({ + Action: "Query", + Entity: "cr693_combotable", + Then: Table( + { + 'cr693_name': "Item 1", + 'cr693_id': 3, + 'cr693_combotableid': "8cd3faaa-97ac-4e78-8b71-16c82cabb856", + 'createdon': "2024-12-02T17:52:45Z" + }, + { + 'cr693_name': "RR2", + 'cr693_id': 4, + 'cr693_combotableid': "ff58de6c-905d-457d-846b-3e0b2aa4c5fd", + 'createdon': "2024-12-02T17:54:45Z" + }, + { + 'cr693_name': "RR3", + 'cr693_id': 5, + 'cr693_combotableid': "ff58de6c-905d-457d-846b-3e0b2aa4c5fe", + 'createdon': "2024-12-02T17:54:45Z" + } + ) + }); + + testCases: + - testCaseName: Load ComboBox Data + testCaseDescription: Verify data loaded into Combobox1 + testSteps: | + = SetProperty(Combobox1.SelectedItems, Table(First(Combobox1.Items))); + Assert(CountRows(Combobox1.SelectedItems)=1, "True"); + + - testCaseName: Test ComboBox Search Functionality + testCaseDescription: Verify that the ComboBox can filter items based on the search query. + testSteps: | + = SetProperty(Combobox1.SelectedItems, Table()); + SetProperty(Combobox1.SearchText, "Nonexistent"); + Assert(CountRows(Combobox1.SelectedItems) = 0, "Expected no items to match the search query 'Nonexistent'."); + + - testCaseName: Test ComboBox Multi-Search Functionality + testCaseDescription: Verify that the ComboBox can filter items based on multiple search queries. + testSteps: | + = SetProperty(Combobox1.SelectedItems, Table()); + SetProperty(Combobox1.SearchText, "Item"); + SetProperty(Combobox1.SearchText, "RR2"); + Assert(CountRows(Filter(Combobox1.Items, "Item" in cr693_name || "RR2" in cr693_name)) = 2, "Expected two items to match the search queries 'Item' and 'RR2'."); + + - testCaseName: Test ComboBox SelectMultiple + testCaseDescription: Verify that the ComboBox can select multiple items. + testSteps: | + = SetProperty(Combobox1.SelectMultiple, true); + SetProperty(Combobox1.SelectedItems, Table(First(Combobox1.Items), Last(Combobox1.Items))); + Assert(CountRows(Combobox1.SelectedItems) = 2, "Expected two items to be selected."); + +testSettings: + headless: false + locale: "en-US" + recordVideo: true + extensionModules: + enable: true + browserConfigurations: + - browser: Chromium + channel: msedge + timeout: 480000 + +environmentVariables: + users: + - personaName: User1 + emailKey: user1Email + passwordKey: NotNeeded \ No newline at end of file diff --git a/src/testengine.provider.canvas/PowerAppFunctions.cs b/src/testengine.provider.canvas/PowerAppFunctions.cs index 5d4ef4d5..8a7477c9 100644 --- a/src/testengine.provider.canvas/PowerAppFunctions.cs +++ b/src/testengine.provider.canvas/PowerAppFunctions.cs @@ -278,9 +278,6 @@ public async Task SetPropertyAsync(ItemPath itemPath, FormulaValue value) return await SetPropertyRecordAsync(itemPath, (RecordValue)value); case (TableType): return await SetPropertyTableAsync(itemPath, (TableValue)value); - case (ColorType): - objectValue = ((ColorValue)value).Value; - break; default: throw new ArgumentException("SetProperty must be a valid type."); } diff --git a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs index 9ae93a5e..bab96223 100644 --- a/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs +++ b/src/testengine.provider.mda/ModelDrivenApplicationProvider.cs @@ -729,20 +729,5 @@ private static string GetQueryParametersForTestUrl(string tenantId, string addit { return $"?tenantId={tenantId}&source=testengine{additionalQueryParams}"; } - - 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); - } - } + } } From 7cf753c64df924f4fd2f0b478fe40c318567b3ff Mon Sep 17 00:00:00 2001 From: v-raghulraja Date: Thu, 9 Jan 2025 13:12:50 +0530 Subject: [PATCH 4/4] Resolving review comments --- .../Providers/PowerFxModel/ControlRecordValue.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs index 52dc7fa9..156fe7c2 100644 --- a/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs +++ b/src/Microsoft.PowerApps.TestEngine/Providers/PowerFxModel/ControlRecordValue.cs @@ -175,8 +175,7 @@ protected override bool TryGetField(FormulaType fieldType, string fieldName, out if (long.TryParse(jsPropertyValueModel.PropertyValue, out milliseconds)) { var dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); - DateTime trueDateTime = dateTimeOffset.LocalDateTime; - //var trueDateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddMilliseconds(milliseconds); + DateTime trueDateTime = dateTimeOffset.LocalDateTime; result = DateValue.NewDateOnly(trueDateTime.Date); } // When converted from DateTime to a string, a value from SetProperty() retains it's MMDDYYYY hh::mm::ss format