Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 85 additions & 36 deletions copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
---
applies_to: **/*.cs
applyTo: '**/*.cs'
author: 'Huaxing YUAN'
---
# Context for Copilot
# Context for Copilot - Test Automation
We are working on Test Automation using Selenium WebDriver customized with WebEngine Framework.
Speak with me in french.
Speak with me in French.

Framework repository: https://github.com/AxaFrance/webengine-dotnet

## We use two approaches to write tests:
## We use following approaches to write test cases:
- Linear Scripting : for unit or very simple scenarios. We translate test scenario to test scripts directly.
- Behavior-Driven Development : where the test scenarios are written in gherkin format with `Reqnroll` (specflow is no more maintained).
- Keyword-Driven Testing : where the test scenario are written using keywords that represent actions or verifications in the application under test.
- Behavior-Driven Development : where the test scenarios are written in gherkin format with SpecFlow.
by default, use keyword-driven approche.

## When writing test case, follow these guidelines:
- Apply PageObject Model design pattern to separate UI element locators to test script.
- Add Web elements into PageObject Models and refer it in test actions.
- In actions, perform actions using PageObject Models and their elements.
You will analyze the structure of the projet to determine the approach, and tell me which approach is used.

## When writing test case, follow these guidelines for every approaches :
- Do not use hardcoded locators such as xpath or css selectors in test script when possible. Use ElementDescription instead.
- Add Web elements into PageObject Models and refer it in test actions.
- Apply PageObject Model design pattern to separate UI element locators to test script.
- when suggest test script, use native functions from WebEngine Framework if possible.
- **DO NOT** create ElementDescription directly in SharedActions, add them to approprate PageModels

For linear scripting and Reqroll, DO NOT use SharedActions or TestCaseWeb/TestCaseApp classes, write the script direcly by using PageObjectModel.
For Keyword-Driven, you should use SharedActions to implemented reusable action groups.
For Gherkin, you can also use shared action inside step definition to maximum reuse.
- Use parameters in actions, and use parameter list to store all parameters used in the test case.
- In actions, perform actions using PageObject Models and their elements.

For Gherkin approach, if user not give the class of step definitions, ask user before create new one.

Project should be structured as follow based on current active project, using current projet's namespace.
- folder PageModels: store all page object models
For a Keyword-Driven test project, it should be structured as follow based on current active project, using current projet's namespace.
- folder PageModels: store all page object models, under namespace
- folder Actions: store all SharedActions (which should be reused in different test cases)
- folder TestCases: store all test scenarios
- folder TestData: store all test data files, such as XML or Excel files
Expand All @@ -31,9 +40,22 @@ folders should be inside project folder, not the solution folder.
## Nuget Packages required:
- AxaFrance.WebEngine.Web: for web based tests (based on selenium)
- AxaFrance.WebEngine.MobileApp: for mobile apps (based on appium)
- AxeFrance.WebEngine.Runner: only used for Keyword-Driven approach
Install required packages or ask user to install them if they are not referenced.

# WebDriver or AppiumDriver
Use BrowserFactory to get WebDriver or AppiumDriver instance.
var driver = BrowserFactory.GetDriver(Platform.Windows, BrowserType.Edge);
var driver = AppFactory.GetDriver(Platform.Android)

Platform is defined in namespace: AxaFrance.WebEngine

# Use ElementDescription to find web elements

Prioritize stable identifiers such as TagName, Id, Name, or other html attributes (test-id, accessibility for example)
to make sure the locators are stable and not likely to change. you can combine multiple locators to make it unique.
Use CssSelector or XPath only when there is no other stable attributes available.

## WebElementDescription for elements on Web Page:
WebElementDescription class has following properties:
- ClassName: equivalent to html attribute class. class name can contain spaces.
Expand All @@ -48,7 +70,22 @@ WebElementDescription class has following properties:
- Attributes.Name: the name of the attribute.
- Attributes.Value: the value of the attribute.

### Example of a WebElementDescription in C#:
### Actions
WebElementDescription expose following methods to interact with web element:
* Click()
* SendKeys(string)
* SetValue(string)
* SetSecure(string) - Set the value of a password textbox using encrypted string.
* Exists()
* Clear()
* MouseHover()
* ScrollIntoView()
* SelectByIndex(int) - Select an option from html tag by its displayed text.
* SelectByText(string) - Select an option from html tag by its displayed text.
* SelectByValue(string) - Select an option from html tag by its value.
* CheckByValue(string) - Checks an RadioButton from a given RadioGroup (used for radio button group, elements)

### Example of a WebElementDescription in C# used individualy:
```csharp
using AxaFrance.WebEngine.Web;
using AxaFrance.WebEngine;
Expand Down Expand Up @@ -94,34 +131,42 @@ The above two selectors can only be used individually, when they are provided al

## Organize UI Elements With PageModel
AppElementDescription and WebElementDescription can be used to create Page Model classes.
To test Web applications, use WebElementDescription instead of AppElementDescription.

Example: CalculatorPage is the model used to test android application, which derives from PageModel class.
To test Web applications, add WebElementDescription instead of AppElementDescription.
Attention:
- Do not passe Driver in the constructor of WebElementDescription or AppElementDescription if it is used inside a PageModel.
Page model will take care of it.
- WebElementDescription and AppElementDescription are properties, with (get and set)

```csharp
using OpenQA.Selenium;
using AxaFrance.WebEngine.Web;
using AxaFrance.WebEngine.MobileApp;

public class CalculatorPage : PageModel
{
public AppElementDescription Digit0 = new AppElementDescription
{
Id = "com.Android.calculator2:id/digit_0"
};

public AppElementDescription Equals = new AppElementDescription
{
Id = "com.Android.calculator2:id/eq"
};

public AppElementDescription Multiply = new AppElementDescription
{
ClassName = "Android.widget.Button",
AccessbilityId = "multiply"
};

public CalculatorPage(WebDriver driver) : base(driver)
{
}
namespace YouTestProjectNS.PageModels{
public class CalculatorPage : PageModel
{
public AppElementDescription Digit0 {get;set;} = new AppElementDescription
{
Id = "com.Android.calculator2:id/digit_0"
};

public AppElementDescription Equals {get;set;} = new AppElementDescription
{
Id = "com.Android.calculator2:id/eq"
};

public AppElementDescription Multiply {get;set;} = new AppElementDescription
{
ClassName = "Android.widget.Button",
AccessbilityId = "multiply"
};

public CalculatorPage(WebDriver driver) : base(driver)
{
}
}
}
```
In Page mode, we use WebDriver and not IWebDriver
Expand All @@ -132,6 +177,7 @@ if use Keyword-Driven approaches, the test case will be defined like this:
2. Each test step is assigned with an action which is a class that derives from SharedActionWeb or SharedActionApp.
```scharp
using AxaFrance.WebEngine;
using AxaFrance.WebEngine.Web;

namespace Samples.KeywordDriven.TestCases
{
Expand Down Expand Up @@ -199,6 +245,7 @@ public abstract bool DoCheckpoint(AppiumDriver driver);
## Test Data
WebEngine supports XML format test data files. test data is in following structure:
Each structure TestData contains a TestName and Data with Variables used for a test case.
ParameterList is used only for Keyword Driven approach.
```xml
<?xml version="1.0" encoding="utf-8"?>
<TestSuiteData
Expand All @@ -223,7 +270,9 @@ Each structure TestData contains a TestName and Data with Variables used for a t

## ParameterList
To void using hardcoded variable name in the actions, we can use a class named ParameterList
to store all parameters used in the test case, for example:
to store all parameters used in the test case.
ParameterList is used only for Keyword Driven approach.
for example:
```csharp
public static class ParameterList {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Deque.AxeCore.Commons" Version="4.10.1" />
<PackageReference Include="Deque.AxeCore.Commons" Version="4.11.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Deque.AxeCore.Selenium" Version="4.10.1" />
<PackageReference Include="Deque.AxeCore.Selenium" Version="4.11.0" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@
-->
<ItemGroup>
<Reference Include="Accessibility" />
<Reference Include="Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Office.Interop.Excel.15.0.4795.1001\lib\net20\Microsoft.Office.Interop.Excel.dll</HintPath>
<Reference Include="Microsoft.Office.Interop.Excel, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Office.Interop.Excel.16.0.18925.20022\lib\net40\Microsoft.Office.Interop.Excel.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
Expand Down
2 changes: 1 addition & 1 deletion src/AxaFrance.WebEngine.ExcelUI/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Office.Interop.Excel" version="15.0.4795.1001" targetFramework="net472" />
<package id="Microsoft.Office.Interop.Excel" version="16.0.18925.20022" targetFramework="net48" />
<package id="MSBuild.Microsoft.VisualStudio.Tools.Office.targets" version="15.0.1" targetFramework="net45" />
</packages>
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Appium.WebDriver" Version="8.0.0" />
<PackageReference Include="Appium.WebDriver" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Selenium.WebDriver" Version="4.35.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.39.0" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<OutputType>WinExe</OutputType>
<TargetFrameworks>net48;net8.0-windows;net9.0-windows</TargetFrameworks>
<Nullable>disable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<UseWPF>true</UseWPF>
<AssemblyName>ReportViewer</AssemblyName>
<IsPublishable>False</IsPublishable>
Expand Down Expand Up @@ -43,8 +44,8 @@
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.3.1.120" />
<PackageReference Include="Hummingbird.UI" Version="1.2.2772.2" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3485.44" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc5.1" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="System.Text.Json" Version="9.0.9" />
</ItemGroup>

</Project>
7 changes: 3 additions & 4 deletions src/AxaFrance.WebEngine.Web/AxaFrance.WebEngine.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@


<ItemGroup>
<PackageReference Include="Appium.WebDriver" Version="8.0.0" />
<PackageReference Include="Appium.WebDriver" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Selenium.Support" Version="4.35.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.35.0" />
<PackageReference Include="Selenium.Support" Version="4.39.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.39.0" />
<PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="System.Text.Json" Version="9.0.9" />
<PackageReference Include="WebDriverManager" Version="2.17.6" />
</ItemGroup>

Expand Down
38 changes: 36 additions & 2 deletions src/AxaFrance.WebEngine.Web/BrowserFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,21 +439,55 @@ private static WebDriver GetEdgeDriver(IEnumerable<string> browserOptions)

private static WebDriver GetChromeDriver(IEnumerable<string> browserOptions)
{
new WebDriverManager.DriverManager().SetUpDriver(new ChromeConfig(), "MatchingBrowser");
OpenQA.Selenium.Chrome.ChromeOptions options = new OpenQA.Selenium.Chrome.ChromeOptions()
string version = "MatchingBrowser";
var options = new OpenQA.Selenium.Chrome.ChromeOptions()
{
AcceptInsecureCertificates = true,
};

// Handle binary location
const string binaryLocationPrefix = "binarylocation=";
if (browserOptions.Any(x => x.ToLower().StartsWith(binaryLocationPrefix)))
{
var location = browserOptions.First(x => x.ToLower().StartsWith(binaryLocationPrefix));
options.BinaryLocation = location.Substring(binaryLocationPrefix.Length);
}

// Handle user preferences
const string userPreferencePrefix = "userpreference=";
var userPrefOption = browserOptions.FirstOrDefault(x => x.ToLower().StartsWith("userPreferencePrefix"));
if (!string.IsNullOrEmpty(userPrefOption))
{
var userPrefJson = userPrefOption.Substring("userPreferencePrefix".Length);
try
{
var prefs = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(userPrefJson);
if (prefs != null)
{
foreach (var kvp in prefs)
{
options.AddUserProfilePreference(kvp.Key, kvp.Value);
}
}
}
catch (Exception ex)
{
AxaFrance.WebEngine.DebugLogger.WriteLine($"Failed to parse userPreference JSON: {ex.Message}");
}
}

string datadir = $"{GetEnvironmentVariable("LOCALAPPDATA")}\\Google\\Chrome\\TestData";
Directory.CreateDirectory(datadir);
options.AddArgument($"user-data-dir={datadir}");
options.AddArgument("dns-prefetch-disable");
options.AddArgument("homepage=about:blank");
options.AddArgument("disable-popup-blocking");
options.AddArgument("disable-dev-shm-usage");
options.AddArgument("no-default-browser-check");
options.AddArgument("no-sandbox");
if (browserOptions != null) options.AddArguments(browserOptions);

new WebDriverManager.DriverManager().SetUpDriver(new ChromeConfig(), version);
OpenQA.Selenium.Chrome.ChromeDriver cd = new OpenQA.Selenium.Chrome.ChromeDriver(options);
return cd;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Samples.DataDriven/Samples.DataDriven.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

<ItemGroup>
<PackageReference Include="SkiaSharp" Version="3.119.1" />
<PackageReference Include="System.Text.Json" Version="9.0.9" />
<PackageReference Include="System.Text.Json" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading