-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #139 from MADE-Apps/feature/pageobject-generator
Ported Legerity page object generator into main project repo
- Loading branch information
Showing
24 changed files
with
956 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,17 @@ | ||
## Fixes # | ||
## Resolves # | ||
<!-- Add the issue ID after the '#' to automatically close the issue once the PR is merged --> | ||
|
||
<!-- Please provide a description below of the changes made and how it has been tested --> | ||
|
||
## PR checklist | ||
|
||
- [ ] Sample tests have been added/updated and pass | ||
- [ ] [Documentation](/docs) has been added/updated for changes | ||
- [ ] Code styling has been met on new source file changes | ||
- [ ] Contains **NO** breaking changes | ||
- [ ] Have Legerity sample tests been added or updated, run locally, and all pass | ||
- [ ] Have added or updated support for platform specific element wrappers been reflected in the Page Object Generator | ||
- [ ] Have code styling rules been run on all new source file changes | ||
- [ ] Have relevant articles in the docs been added or updated for all new source file changes | ||
- [ ] Have major breaking changes been made and are documented | ||
|
||
<!-- If a breaking change has been made, please provide a detailed description below of the impact and the migration path --> | ||
|
||
## Other information | ||
<!-- Please provide any additional information, links, or screenshots below if applicable --> | ||
<!-- Provide any additional information below that may be relevant to the changes made (e.g. app screenshots, documentation links, or existing PR reference) --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ on: | |
- samples/** | ||
- tests/** | ||
- build/** | ||
- tools/** | ||
- .github/workflows/ci.yml | ||
pull_request: | ||
branches: | ||
|
@@ -20,6 +21,7 @@ on: | |
- samples/** | ||
- tests/** | ||
- build/** | ||
- tools/** | ||
- .github/workflows/ci.yml | ||
workflow_dispatch: | ||
|
||
|
@@ -52,7 +54,7 @@ jobs: | |
uses: NuGet/[email protected] | ||
|
||
- name: Restore dependencies | ||
run: nuget restore $SOLUTION | ||
run: dotnet restore $SOLUTION | ||
|
||
- name: Build | ||
run: dotnet build $SOLUTION --configuration $BUILD_CONFIG -p:Version=$BUILD_VERSION --no-restore | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project> | ||
|
||
<PropertyGroup> | ||
<Version>1.0.0.0</Version> | ||
<Authors>MADE Apps</Authors> | ||
<Company>MADE Apps</Company> | ||
<Copyright>Copyright (C) MADE Apps. All rights reserved.</Copyright> | ||
<NeutralLanguage>en</NeutralLanguage> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<LangVersion>latest</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/> | ||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive"/> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<Project> | ||
|
||
<PropertyGroup> | ||
<PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
<EmbedUntrackedSources>true</EmbedUntrackedSources> | ||
<IncludeSymbols>true</IncludeSymbols> | ||
<SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||
<Version>1.0.0.0</Version> | ||
<Authors>MADE Apps</Authors> | ||
<Company>MADE Apps</Company> | ||
<Copyright>Copyright (C) MADE Apps. All rights reserved.</Copyright> | ||
<PackageProjectUrl>https://github.com/MADE-Apps/legerity</PackageProjectUrl> | ||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | ||
<PackageIcon>ProjectLogo.png</PackageIcon> | ||
<PackageReleaseNotes>https://github.com/MADE-Apps/legerity/releases</PackageReleaseNotes> | ||
<NeutralLanguage>en</NeutralLanguage> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<LangVersion>latest</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<None Include="..\..\assets\ProjectLogo.png" Pack="true" PackagePath=""/> | ||
<None Include="..\..\LICENSE" Pack="true" PackagePath=""/> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/> | ||
</ItemGroup> | ||
|
||
</Project> |
166 changes: 166 additions & 0 deletions
166
tools/Legerity.PageObjectGenerator/Features/Generators/Android/AxmlPageObjectGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
namespace Legerity.Features.Generators.Android; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Xml.Linq; | ||
using Infrastructure.IO; | ||
using Legerity.Features.Generators; | ||
using Legerity.Features.Generators.Models; | ||
using Legerity.Infrastructure.Extensions; | ||
using MADE.Collections.Compare; | ||
using MADE.Data.Validation.Extensions; | ||
using Scriban; | ||
using Serilog; | ||
|
||
internal class AxmlPageObjectGenerator : IPageObjectGenerator | ||
{ | ||
private const string AndroidNamespace = "http://schemas.android.com/apk/res/android"; | ||
|
||
private const string BaseElementType = "AndroidElement"; | ||
|
||
private static readonly GenericEqualityComparer<string> SimpleStringComparer = new(s => s.ToLower()); | ||
|
||
public static IEnumerable<string> SupportedCoreAndroidElements => new List<string> | ||
{ | ||
"Button", | ||
"CheckBox", | ||
"DatePicker", | ||
"EditText", | ||
"RadioButton", | ||
"Spinner", | ||
"Switch", | ||
"TextView", | ||
"ToggleButton", | ||
"View" | ||
}; | ||
|
||
public async Task GenerateAsync(string ns, string inputPath, string outputPath) | ||
{ | ||
IEnumerable<string>? filePaths = GetAxmlFilePaths(inputPath)?.ToList(); | ||
|
||
if (filePaths == null || !filePaths.Any()) | ||
{ | ||
Log.Warning("No AXML files found in {InputPath}", inputPath); | ||
return; | ||
} | ||
|
||
foreach (string filePath in filePaths) | ||
{ | ||
Log.Information($"Processing {filePath}..."); | ||
|
||
await using FileStream fileStream = File.Open(filePath, FileMode.Open); | ||
var axml = XDocument.Load(fileStream); | ||
|
||
if (axml.Root != null) | ||
{ | ||
var templateData = | ||
new GeneratorTemplateData(ns, Path.GetFileNameWithoutExtension(filePath), BaseElementType); | ||
|
||
Log.Information($"Generating template for {templateData}..."); | ||
|
||
IEnumerable<XElement> elements = this.FlattenElements(axml.Root.Elements()); | ||
foreach (XElement element in elements) | ||
{ | ||
string? id = RemoveAndroidIdReference(element.Attribute(XName.Get("id", AndroidNamespace))?.Value); | ||
string? contentDesc = element.Attribute(XName.Get("contentDescription", AndroidNamespace))?.Value; | ||
|
||
string? byLocatorType = GetByLocatorType(id, contentDesc); | ||
if (byLocatorType == null || byLocatorType.IsNullOrWhiteSpace()) | ||
{ | ||
continue; | ||
} | ||
|
||
string? byQueryValue = id ?? contentDesc; | ||
if (byQueryValue == null || byQueryValue.IsNullOrWhiteSpace()) | ||
{ | ||
continue; | ||
} | ||
|
||
var uiElement = new UiElement( | ||
GetElementWrapperType(element.Name.LocalName), | ||
byQueryValue.Capitalize(), | ||
byLocatorType, | ||
byQueryValue); | ||
|
||
Log.Information($"Element found on page - {uiElement}"); | ||
|
||
templateData.Trait = uiElement; | ||
templateData.Elements.Add(uiElement); | ||
} | ||
|
||
await GeneratePageObjectClassFileAsync(templateData, outputPath); | ||
} | ||
else | ||
{ | ||
Log.Warning($"Skipping {filePath} as a page was not detected"); | ||
} | ||
} | ||
} | ||
|
||
private static string? RemoveAndroidIdReference(string? value) | ||
{ | ||
return value == null || string.IsNullOrWhiteSpace(value) | ||
? null | ||
: value.Replace("+", string.Empty).Replace("@id/", string.Empty); | ||
} | ||
|
||
private static async Task GeneratePageObjectClassFileAsync( | ||
GeneratorTemplateData templateData, | ||
string outputFolder) | ||
{ | ||
var pageObjectTemplate = Template.Parse(await EmbeddedResourceLoader.ReadAsync("Legerity.Templates.AndroidPageObject.template")); | ||
|
||
string outputFile = $"{templateData.Page}.cs"; | ||
|
||
Log.Information($"Generating {outputFile} page object file..."); | ||
string result = await pageObjectTemplate.RenderAsync(templateData); | ||
|
||
FileStream output = File.Create(Path.Combine(outputFolder, outputFile)); | ||
var outputWriter = new StreamWriter(output, Encoding.UTF8); | ||
|
||
await using (outputWriter) | ||
{ | ||
await outputWriter.WriteAsync(result); | ||
} | ||
} | ||
|
||
private static string? GetByLocatorType(string? id, string? contentDesc) | ||
{ | ||
if (id != null && !id.IsNullOrWhiteSpace()) | ||
{ | ||
return "Id"; | ||
} | ||
|
||
return contentDesc != null && !contentDesc.IsNullOrWhiteSpace() ? "AndroidContentDesc" : null; | ||
} | ||
|
||
private static IEnumerable<string>? GetAxmlFilePaths(string searchFolder) | ||
{ | ||
string[]? filePaths = default; | ||
|
||
try | ||
{ | ||
filePaths = Directory.GetFiles(searchFolder, "*.axml", SearchOption.AllDirectories); | ||
} | ||
catch (UnauthorizedAccessException) | ||
{ | ||
Log.Error("An error occurred while retrieving AXML files for processing"); | ||
} | ||
|
||
return filePaths; | ||
} | ||
|
||
private static string GetElementWrapperType(string elementName) | ||
{ | ||
return SupportedCoreAndroidElements.Contains(elementName, SimpleStringComparer) ? elementName : BaseElementType; | ||
} | ||
|
||
private IEnumerable<XElement> FlattenElements(IEnumerable<XElement> elements) | ||
{ | ||
return elements.SelectMany(c => this.FlattenElements(c.Elements())).Concat(elements); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
tools/Legerity.PageObjectGenerator/Features/Generators/IPageObjectGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Legerity.Features.Generators; | ||
|
||
using System.Threading.Tasks; | ||
|
||
internal interface IPageObjectGenerator | ||
{ | ||
Task GenerateAsync(string ns, string inputPath, string outputPath); | ||
} |
28 changes: 28 additions & 0 deletions
28
tools/Legerity.PageObjectGenerator/Features/Generators/Models/GeneratorTemplateData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
namespace Legerity.Features.Generators.Models; | ||
|
||
using System.Collections.Generic; | ||
|
||
internal class GeneratorTemplateData | ||
{ | ||
public GeneratorTemplateData(string ns, string page, string baseElementType) | ||
{ | ||
this.Namespace = ns; | ||
this.Page = page; | ||
this.Type = baseElementType; | ||
} | ||
|
||
public string Page { get; set; } | ||
|
||
public string Type { get; set; } | ||
|
||
public string Namespace { get; set; } | ||
|
||
public UiElement Trait { get; set; } | ||
|
||
public List<UiElement> Elements { get; set; } = new(); | ||
|
||
public override string ToString() | ||
{ | ||
return $"[Page] {this.Page};"; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
tools/Legerity.PageObjectGenerator/Features/Generators/Models/UiElement.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
namespace Legerity.Features.Generators.Models; | ||
|
||
internal class UiElement | ||
{ | ||
public UiElement(string type, string name, string by, string value) | ||
{ | ||
this.Type = type; | ||
this.Name = name; | ||
this.By = by; | ||
this.Value = value; | ||
} | ||
|
||
public string Type { get; set; } | ||
|
||
public string Name { get; set; } | ||
|
||
public string By { get; set; } | ||
|
||
public string Value { get; set; } | ||
|
||
public override string ToString() | ||
{ | ||
return $"[Type] {this.Type}; [Name] {this.Name}; [By] {this.By}; [Value] {this.Value};"; | ||
} | ||
} |
Oops, something went wrong.