Skip to content

Commit

Permalink
Merge pull request #139 from MADE-Apps/feature/pageobject-generator
Browse files Browse the repository at this point in the history
Ported Legerity page object generator into main project repo
  • Loading branch information
jamesmcroft authored Jun 6, 2022
2 parents fd5387a + fdf2f99 commit a62b647
Show file tree
Hide file tree
Showing 24 changed files with 956 additions and 8 deletions.
13 changes: 7 additions & 6 deletions .github/pull_request_template.md
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) -->
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- samples/**
- tests/**
- build/**
- tools/**
- .github/workflows/ci.yml
pull_request:
branches:
Expand All @@ -20,6 +21,7 @@ on:
- samples/**
- tests/**
- build/**
- tools/**
- .github/workflows/ci.yml
workflow_dispatch:

Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions samples/Directory.Build.props
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>
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<PackageReleaseNotes>https://github.com/MADE-Apps/legerity/releases</PackageReleaseNotes>
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions src/Legerity.sln
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Legerity.IOS", "Legerity.IO
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Legerity.Web", "Legerity.Web\Legerity.Web.csproj", "{66469000-1C91-4CBA-A27E-043C3E222DA6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9064E354-7A64-4288-93E5-B3628CA12DD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Legerity.PageObjectGenerator", "..\tools\Legerity.PageObjectGenerator\Legerity.PageObjectGenerator.csproj", "{063E6264-F623-4469-BADF-95C8E93E3ACF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -131,6 +135,10 @@ Global
{66469000-1C91-4CBA-A27E-043C3E222DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66469000-1C91-4CBA-A27E-043C3E222DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66469000-1C91-4CBA-A27E-043C3E222DA6}.Release|Any CPU.Build.0 = Release|Any CPU
{063E6264-F623-4469-BADF-95C8E93E3ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{063E6264-F623-4469-BADF-95C8E93E3ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{063E6264-F623-4469-BADF-95C8E93E3ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{063E6264-F623-4469-BADF-95C8E93E3ACF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -154,6 +162,7 @@ Global
{EF10E4CE-62F1-41A9-807A-79A153CD7A04} = {158E9EAA-AEE0-4E0C-81E0-6E9E63341CC1}
{9D100607-5873-419C-B1AD-8DE55E464CFE} = {44456B3E-73D9-43C5-9644-430E293ECD5E}
{89BD06DC-7E77-4062-9149-EF95D3F9D999} = {1BA37704-10A3-4EDC-94AA-BF0D8CD11A9C}
{063E6264-F623-4469-BADF-95C8E93E3ACF} = {9064E354-7A64-4288-93E5-B3628CA12DD3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FAB58D66-B3F1-408A-A96F-572170771367}
Expand Down
31 changes: 31 additions & 0 deletions tools/Directory.Build.props
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>
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);
}
}
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);
}
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};";
}
}
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};";
}
}
Loading

0 comments on commit a62b647

Please sign in to comment.