Converting Tests back to C# but still with Playwright
This commit is contained in:
@@ -25,7 +25,7 @@ jobs:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_CALM_MUD_04916B210 }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: "upload"
|
||||
app_location: "IGP"
|
||||
app_location: "Web"
|
||||
api_location: ""
|
||||
output_location: "./wwwroot"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_NICE_COAST_0F8B08010 }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: "upload"
|
||||
app_location: "IGP"
|
||||
app_location: "Web"
|
||||
api_location: ""
|
||||
output_location: "./wwwroot"
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
3.0
|
||||
Vendored
+3
-3
@@ -7,7 +7,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/IGP/IGP.csproj",
|
||||
"${workspaceFolder}/Web/Web.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -19,7 +19,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/IGP/IGP.csproj",
|
||||
"${workspaceFolder}/Web/Web.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -33,7 +33,7 @@
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/IGP/IGP.csproj"
|
||||
"${workspaceFolder}/Web/Web.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>IGP.Calculator.Cli</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Services\Services.csproj" />
|
||||
<ProjectReference Include="..\Model\Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +1,8 @@
|
||||
using IGP.Calculator.Cli.Services;
|
||||
using Model.Entity;
|
||||
using Model.Entity.Data;
|
||||
using Services;
|
||||
using Services.Immortal;
|
||||
using Services.Website;
|
||||
using IGP.Calculator.Cli.Services;
|
||||
|
||||
var faction = DataType.FACTION_QRath;
|
||||
var immortal = DataType.IMMORTAL_Orzum;
|
||||
@@ -11,7 +10,6 @@ var attackTime = 1500;
|
||||
var entityNames = new List<string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i].ToLower())
|
||||
{
|
||||
case "--faction" when i + 1 < args.Length:
|
||||
@@ -48,7 +46,6 @@ for (var i = 0; i < args.Length; i++)
|
||||
entityNames.Add(args[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var toastService = new ToastService();
|
||||
var storageService = new NullStorageService();
|
||||
@@ -124,12 +121,12 @@ var finalEconomy = economyService.GetEconomy(timingService.GetAttackTime());
|
||||
var lastEconomy = economyService.GetEconomy(lastInterval);
|
||||
|
||||
Console.WriteLine($"Army Attacking At: {timingService.GetAttackTime()}s");
|
||||
Console.WriteLine($"");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine($"At attack time ({timingService.GetAttackTime()}s):");
|
||||
Console.WriteLine($" Alloy: {finalEconomy.Alloy,10:F1}");
|
||||
Console.WriteLine($" Ether: {finalEconomy.Ether,10:F1}");
|
||||
Console.WriteLine($" Pyre: {finalEconomy.Pyre,10:F1}");
|
||||
Console.WriteLine($"");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine($"At last build action ({lastInterval}s):");
|
||||
Console.WriteLine($" Alloy: {lastEconomy.Alloy,10:F1}");
|
||||
Console.WriteLine($" Ether: {lastEconomy.Ether,10:F1}");
|
||||
@@ -155,4 +152,4 @@ static EntityModel? FindEntity(string name, string faction, string immortal)
|
||||
if (vanguardMatch != null) return vanguardMatch;
|
||||
|
||||
return candidates.FirstOrDefault(e => e.VanguardAdded() == null);
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -6,8 +6,13 @@ public class NullStorageService : IStorageService
|
||||
{
|
||||
private readonly Dictionary<string, object?> _store = new();
|
||||
|
||||
public void Subscribe(Action action) { }
|
||||
public void Unsubscribe(Action action) { }
|
||||
public void Subscribe(Action action)
|
||||
{
|
||||
}
|
||||
|
||||
public void Unsubscribe(Action action)
|
||||
{
|
||||
}
|
||||
|
||||
public T GetValue<T>(string forKey)
|
||||
{
|
||||
@@ -21,5 +26,8 @@ public class NullStorageService : IStorageService
|
||||
_store[key] = value;
|
||||
}
|
||||
|
||||
public Task Load() => Task.CompletedTask;
|
||||
}
|
||||
public Task Load()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Components</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@@ -29,8 +30,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||
<ProjectReference Include="..\Services\Services.csproj"/>
|
||||
<ProjectReference Include="..\Model\Model.csproj" />
|
||||
<ProjectReference Include="..\Services\Services.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Inputs\"/>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
<div>
|
||||
<input readonly="@MemoryQuestion.IsRevealed"
|
||||
class="formTextInput @(MemoryQuestion.IsRevealed ? "revealed" : IsSubmitted == false ? "guess" : int.Parse(guess ?? string.Empty) == MemoryQuestion.Answer ? "correct" : "wrong")"
|
||||
class="formTextInput @(MemoryQuestion.IsRevealed ? "revealed" : !IsSubmitted ? "guess" : int.Parse(guess ?? string.Empty) == MemoryQuestion.Answer ? "correct" : "wrong")"
|
||||
placeholder="guess..."
|
||||
type="number"
|
||||
value="@guess"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN dotnet publish IGP/IGP.csproj -c Release -o /app/publish
|
||||
RUN dotnet publish Web/Web.csproj -c Release -o /app/publish
|
||||
|
||||
FROM nginx:alpine AS final
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>IGP.Calculator.Cli</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Services\Services.csproj" />
|
||||
<ProjectReference Include="..\Model\Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "Web\Web.csproj", "{4A54E237-4FF6-4459-91D9-9249AE3E72E0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "CLI\CLI.csproj", "{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components", "Components\Components.csproj", "{C0ACFFEB-8098-4119-9C28-3A4D671C2514}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {19100811-B909-4D20-9AE0-2EB579A55B3A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IGP", "IGP.csproj", "{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "..\Model\Model.csproj", "{77395F7A-BE93-470C-9F10-F48FFA445B63}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components", "..\Components\Components.csproj", "{0419E7CD-0971-4A56-A61F-C090DF60FAF6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Services", "..\Services\Services.csproj", "{621178C8-4E8B-478E-80E5-7478F0E7B67E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IGP.Calculator.Cli", "..\IGP.Calculator.Cli\IGP.Calculator.Cli.csproj", "{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|x64.Build.0 = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9AA71488-E2F5-43C0-8E40-43E72DB2E3CC}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {19100811-B909-4D20-9AE0-2EB579A55B3A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -4,6 +4,5 @@ namespace Model.Entity.Parts;
|
||||
|
||||
public class IEntityPartInterface
|
||||
{
|
||||
[JsonIgnore]
|
||||
public EntityModel Parent { get; set; }
|
||||
[JsonIgnore] public EntityModel Parent { get; set; }
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>Model</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('https://calm-mud-04916b210.1.azurestaticapps.net/build-calculator', { timeout: 15000 });
|
||||
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
await page.locator('select').nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(300);
|
||||
await page.locator('select').nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const keys = await page.locator('.keyContainer > div > div').all();
|
||||
for (const key of keys) {
|
||||
const text = await key.textContent();
|
||||
if (text && text.trim().startsWith('TAB')) {
|
||||
console.log('TAB key textContent:', text.substring(0, 500));
|
||||
const innerHtml = await key.evaluate(el => el.innerHTML);
|
||||
console.log('TAB key innerHTML (first 2000):', innerHtml.substring(0, 2000));
|
||||
const childDivs = await key.locator('> div').all();
|
||||
console.log('Entity div count:', childDivs.length);
|
||||
for (const div of childDivs) {
|
||||
const t = await div.textContent();
|
||||
const s = await div.getAttribute('style');
|
||||
console.log(' Entity:', (t || '').trim(), 'Style:', (s || 'none').substring(0, 100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await page.locator('.keyContainer > div > div').filter({ hasText: 'TAB' }).first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
const entityViewName = await page.locator('.entityClickView #entityName').textContent();
|
||||
console.log('Entity view shows:', entityViewName);
|
||||
const entityViewHtml = await page.locator('.entityClickView').evaluate(el => el.innerHTML.substring(0, 1000));
|
||||
console.log('Entity view HTML:', entityViewHtml);
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error(e.message); process.exit(1); });
|
||||
@@ -1,51 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error' || msg.type() === 'warning')
|
||||
console.log(msg.type().toUpperCase() + ':', msg.text().substring(0, 300));
|
||||
});
|
||||
|
||||
page.on('pageerror', err => console.log('PAGE_ERR:', err.message));
|
||||
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'load' });
|
||||
console.log('Page load event fired');
|
||||
|
||||
// Read button text immediately
|
||||
let buttons = page.locator('.keyContainer > div > div');
|
||||
let count = await buttons.count();
|
||||
console.log('Button count:', count);
|
||||
let qBtn = buttons.filter({ hasText: /^Q/ }).first();
|
||||
console.log('Immediate Q button text:', JSON.stringify(await qBtn.textContent()));
|
||||
|
||||
// Wait for Blazor to finish initializing - look for .blazor-error-boundary or just wait
|
||||
// Actually, let's poll for the button text changing from what it is now
|
||||
for (let i = 0; i < 15; i++) {
|
||||
await page.waitForTimeout(1000);
|
||||
const text = (await buttons.nth(0).textContent() || '').trim();
|
||||
// Check all 19 buttons for Q and F keys
|
||||
const allTexts = [];
|
||||
for (let j = 0; j < count; j++) {
|
||||
const t = (await buttons.nth(j).textContent() || '').trim();
|
||||
if (t.toUpperCase().startsWith('Q') || t.toUpperCase().startsWith('F') || t.toUpperCase().startsWith('W') || t.toUpperCase().startsWith('E')) {
|
||||
allTexts.push(` B${j}: "${t.substring(0,30)}"`);
|
||||
}
|
||||
}
|
||||
console.log(`After ${i+1}s:`);
|
||||
allTexts.forEach(t => console.log(t));
|
||||
}
|
||||
|
||||
// Now check what the filter is currently showing
|
||||
const selects = page.locator('select');
|
||||
console.log('\nSelect values:');
|
||||
for (let s = 0; s < await selects.count(); s++) {
|
||||
const val = await selects.nth(s).inputValue();
|
||||
const opts = await selects.nth(s).locator('option').allTextContents();
|
||||
const selectedIdx = await selects.nth(s).evaluate(el => el.selectedIndex);
|
||||
console.log(` Select ${s}: value=${val}, selectedIndex=${selectedIdx}, options=${JSON.stringify(opts.map(o=>o.trim()))}`);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,74 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
page.on('pageerror', err => console.log('PAGE_ERR:', err.message));
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') console.log('CONSOLE_ERR:', msg.text().substring(0,200));
|
||||
});
|
||||
|
||||
try {
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'load' });
|
||||
console.log('Page loaded');
|
||||
} catch (e) {
|
||||
console.log('Page load error:', e.message);
|
||||
}
|
||||
|
||||
await page.waitForTimeout(10000);
|
||||
console.log('Waited 10s');
|
||||
|
||||
const selCount = await page.locator('select').count();
|
||||
console.log('Select elements count:', selCount);
|
||||
|
||||
if (selCount > 0) {
|
||||
for (let s = 0; s < selCount; s++) {
|
||||
const opts = await page.locator('select').nth(s).locator('option').allTextContents();
|
||||
console.log('Select', s, 'options:', JSON.stringify(opts.map(o => o.trim())));
|
||||
}
|
||||
|
||||
await page.locator('select').nth(0).selectOption("Q'Rath");
|
||||
console.log('Selected Q\' Rath');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.locator('select').nth(1).selectOption('Orzum');
|
||||
console.log('Selected Orzum');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Log all key buttons
|
||||
const buttons = page.locator('.keyContainer > div > div');
|
||||
const count = await buttons.count();
|
||||
console.log('=== All key buttons (' + count + ') ===');
|
||||
for (let i = 0; i < count; i++) {
|
||||
const txt = (await buttons.nth(i).textContent() || '').trim();
|
||||
console.log('Button', i, ':', JSON.stringify(txt.substring(0,60)));
|
||||
}
|
||||
|
||||
// Find Q button
|
||||
for (let i = 0; i < count; i++) {
|
||||
const txt = (await buttons.nth(i).textContent() || '');
|
||||
if (txt.trim().toUpperCase().startsWith('Q')) {
|
||||
console.log('\nFound Q button at index', i, ':', JSON.stringify(txt.trim().substring(0,60)));
|
||||
console.log('Clicking it...');
|
||||
await buttons.nth(i).click({ force: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Check entity view
|
||||
const evCount = await page.locator('.entityClickView #entityName').count();
|
||||
console.log('entityName count:', evCount);
|
||||
if (evCount > 0) {
|
||||
const name = (await page.locator('.entityClickView #entityName').textContent() || '').trim();
|
||||
console.log('entityName text:', JSON.stringify(name));
|
||||
}
|
||||
} else {
|
||||
console.log('No select elements found!');
|
||||
console.log('URL:', page.url());
|
||||
const body = await page.evaluate(() => document.body.innerText.substring(0, 500));
|
||||
console.log('Body text:', JSON.stringify(body));
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,59 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
|
||||
const page = await context.newPage();
|
||||
console.log('Navigating...');
|
||||
await page.goto('https://calm-mud-04916b210.1.azurestaticapps.net/build-calculator', { timeout: 30000, waitUntil: 'domcontentloaded' });
|
||||
console.log('Page loaded, waiting for Blazor...');
|
||||
await page.waitForTimeout(8000);
|
||||
|
||||
console.log('Selecting Q\'Rath...');
|
||||
await page.locator('select').nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(500);
|
||||
await page.locator('select').nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
console.log('Looking for TAB key...');
|
||||
const allKeys = await page.locator('.keyContainer > div > div').all();
|
||||
console.log('Total key divs found:', allKeys.length);
|
||||
for (let i = 0; i < allKeys.length; i++) {
|
||||
const text = await allKeys[i].textContent();
|
||||
const preview = (text || '').trim().substring(0, 100);
|
||||
console.log('Key ' + i + ' starts with:', preview.replace(/\n/g, ' '));
|
||||
if (text && text.trim().startsWith('TAB')) {
|
||||
console.log('FOUND TAB KEY at index ' + i);
|
||||
const entityDivs = await allKeys[i].locator('> div').all();
|
||||
console.log('Entity divs:', entityDivs.length);
|
||||
for (const d of entityDivs) {
|
||||
const t = await d.textContent();
|
||||
console.log(' Entity:', (t || '').trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Clicking TAB key...');
|
||||
try {
|
||||
const tabKey = page.locator('.keyContainer > div > div').filter({ hasText: 'TAB' }).first();
|
||||
await tabKey.click({ timeout: 5000 });
|
||||
console.log('Click succeeded');
|
||||
} catch (e) {
|
||||
console.log('Click failed:', e.message.substring(0, 200));
|
||||
}
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
const entityViewCount = await page.locator('.entityClickView').count();
|
||||
console.log('entityClickView count:', entityViewCount);
|
||||
if (entityViewCount > 0) {
|
||||
const ev = await page.locator('.entityClickView #entityName').textContent();
|
||||
console.log('Entity view name:', ev);
|
||||
const id = await page.locator('.entityClickView .entitiesContainer').getAttribute('id');
|
||||
console.log('Entity container id:', id);
|
||||
} else {
|
||||
console.log('Entity click view NOT found - checking for errors...');
|
||||
const errorEls = await page.locator('[class*=\"error\"], [class*=\"Error\"]').count();
|
||||
console.log('Error elements:', errorEls);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
})().catch(e => { console.error(e.message); process.exit(1); });
|
||||
@@ -1,69 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'networkidle' });
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
const selects = page.locator('select');
|
||||
await selects.nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(500);
|
||||
await selects.nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check entity view before adding
|
||||
let evCount = await page.locator('.entityClickView #entityName').count();
|
||||
console.log('entityName count before add:', evCount);
|
||||
if (evCount > 0) console.log('entityName text:', (await page.locator('.entityClickView #entityName').textContent() || '').trim());
|
||||
|
||||
// Check timeline intervals before
|
||||
let intervals = page.locator('[class*="interval"], .timelineInterval');
|
||||
console.log('interval count before add:', await intervals.count());
|
||||
if ((await intervals.count()) > 0) {
|
||||
console.log('first interval text:', ((await intervals.first().textContent()) || '').trim().substring(0,100));
|
||||
}
|
||||
|
||||
// Click Q
|
||||
const buttons = page.locator('.keyContainer > div > div');
|
||||
const count = await buttons.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const txt = (await buttons.nth(i).textContent()) || '';
|
||||
if (txt.trim().toUpperCase().startsWith('Q')) {
|
||||
await buttons.nth(i).click({ force: true });
|
||||
console.log('Clicked Q button at index', i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check entity view after adding
|
||||
evCount = await page.locator('.entityClickView #entityName').count();
|
||||
console.log('entityName count after add:', evCount);
|
||||
if (evCount > 0) console.log('entityName text:', ((await page.locator('.entityClickView #entityName').textContent()) || '').trim());
|
||||
|
||||
// Check timeline intervals after
|
||||
intervals = page.locator('[class*="interval"], .timelineInterval');
|
||||
console.log('interval count after add:', await intervals.count());
|
||||
if ((await intervals.count()) > 0) {
|
||||
const ic = await intervals.count();
|
||||
for (let i = 0; i < Math.min(ic, 3); i++) {
|
||||
console.log('interval', i, 'text:', ((await intervals.nth(i).textContent()) || '').trim().substring(0,120));
|
||||
}
|
||||
}
|
||||
|
||||
// Click Clear Build Order
|
||||
await page.locator('button').filter({ hasText: 'Clear Build Order' }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check entity view after clear
|
||||
evCount = await page.locator('.entityClickView #entityName').count();
|
||||
console.log('entityName count after clear:', evCount);
|
||||
if (evCount > 0) console.log('entityName text:', ((await page.locator('.entityClickView #entityName').textContent()) || '').trim());
|
||||
|
||||
// Check timeline intervals after clear
|
||||
intervals = page.locator('[class*="interval"], .timelineInterval');
|
||||
console.log('interval count after clear:', await intervals.count());
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,39 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'networkidle' });
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
const selects = page.locator('select');
|
||||
await selects.nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(500);
|
||||
await selects.nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const grid = page.locator('.calculatorGrid > div');
|
||||
const gCount = await grid.count();
|
||||
console.log('calculatorGrid child divs:', gCount);
|
||||
for (let i = 0; i < gCount; i++) {
|
||||
const cls = await grid.nth(i).getAttribute('class');
|
||||
const text = (await grid.nth(i).textContent() || '').trim().substring(0,80);
|
||||
console.log(' child', i, 'class:', JSON.stringify(cls), 'text:', JSON.stringify(text));
|
||||
}
|
||||
|
||||
// Check for interval-related elements
|
||||
for (const sel of ['[class*="interval"]', '[class*="Interval"]', '[class*="timeline"]', '[class*="Timeline"]']) {
|
||||
console.log(sel, 'count:', await page.locator(sel).count());
|
||||
}
|
||||
|
||||
// Also check displayContainer children
|
||||
const dc = page.locator('.displayContainer');
|
||||
const dcCount = await dc.count();
|
||||
console.log('displayContainer count:', dcCount);
|
||||
for (let i = 0; i < dcCount; i++) {
|
||||
const cls = await dc.nth(i).getAttribute('class');
|
||||
const text = (await dc.nth(i).textContent() || '').trim().substring(0,150);
|
||||
console.log(' dc', i, 'class:', JSON.stringify(cls), 'text:', JSON.stringify(text));
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,90 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'networkidle' });
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
const selects = page.locator('select');
|
||||
await selects.nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(500);
|
||||
await selects.nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const gridItems = page.locator('.calculatorGrid > div');
|
||||
const gCount = await gridItems.count();
|
||||
|
||||
// Find the timeline section (has "Shows economy" text)
|
||||
let timelineIdx = -1;
|
||||
for (let i = 0; i < gCount; i++) {
|
||||
const txt = (await gridItems.nth(i).textContent() || '');
|
||||
if (txt.includes('economy')) {
|
||||
timelineIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log('Timeline grid item index:', timelineIdx);
|
||||
|
||||
// Get full timeline text before adding
|
||||
if (timelineIdx >= 0) {
|
||||
const text = (await gridItems.nth(timelineIdx).textContent() || '').trim();
|
||||
console.log('Timeline text before add (first 500 chars):');
|
||||
console.log(text.substring(0, 500));
|
||||
console.log('...');
|
||||
console.log('Contains Acropolis:', text.includes('Acropolis'));
|
||||
console.log('Contains Requested:', text.includes('Requested'));
|
||||
}
|
||||
|
||||
// Click Q to add Acropolis
|
||||
const buttons = page.locator('.keyContainer > div > div');
|
||||
const count = await buttons.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const txt = (await buttons.nth(i).textContent()) || '';
|
||||
if (txt.trim().toUpperCase().startsWith('Q')) {
|
||||
await buttons.nth(i).click({ force: true });
|
||||
console.log('Clicked Q at index', i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check entity view
|
||||
const evName = (await page.locator('.entityClickView #entityName').textContent() || '').trim();
|
||||
console.log('EntityView name after add:', evName);
|
||||
|
||||
// Get timeline text after adding
|
||||
if (timelineIdx >= 0) {
|
||||
const text = (await gridItems.nth(timelineIdx).textContent() || '').trim();
|
||||
console.log('Timeline text after add (first 500 chars):');
|
||||
console.log(text.substring(0, 500));
|
||||
console.log('Contains Acropolis:', text.includes('Acropolis'));
|
||||
console.log('Contains Requested:', text.includes('Requested'));
|
||||
console.log('Contains New:', text.includes('New'));
|
||||
}
|
||||
|
||||
// Count Virtualize items in timeline
|
||||
const virtualItems = page.locator('[style*="grid-template-columns: 1fr 1fr"]');
|
||||
console.log('Virtualize items (grid items):', await virtualItems.count());
|
||||
|
||||
// Click Clear Build Order
|
||||
await page.locator('button').filter({ hasText: 'Clear Build Order' }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check entity view after clear
|
||||
const evAfterClear = await page.locator('.entityClickView #entityName').count();
|
||||
console.log('EntityView #entityName count after clear:', evAfterClear);
|
||||
if (evAfterClear > 0) {
|
||||
console.log('EntityView name after clear:', (await page.locator('.entityClickView #entityName').textContent() || '').trim());
|
||||
}
|
||||
|
||||
// Check timeline after clear
|
||||
if (timelineIdx >= 0) {
|
||||
const text = (await gridItems.nth(timelineIdx).textContent() || '').trim();
|
||||
console.log('Timeline text after clear (first 300 chars):');
|
||||
console.log(text.substring(0, 300));
|
||||
console.log('Contains Acropolis:', text.includes('Acropolis'));
|
||||
console.log('Virtualize items:', await virtualItems.count());
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,64 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:5111/build-calculator', { timeout: 30000, waitUntil: 'networkidle' });
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
const selects = page.locator('select');
|
||||
await selects.nth(0).selectOption("Q'Rath");
|
||||
await page.waitForTimeout(500);
|
||||
await selects.nth(1).selectOption('Orzum');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const gridItems = page.locator('.calculatorGrid > div');
|
||||
const gCount = await gridItems.count();
|
||||
|
||||
for (let i = 0; i < gCount; i++) {
|
||||
const txt = (await gridItems.nth(i).textContent() || '').trim();
|
||||
const cls = await gridItems.nth(i).getAttribute('class');
|
||||
console.log('=== Grid Item', i, 'class:', cls, '===');
|
||||
console.log(txt.substring(0, 200));
|
||||
console.log('---');
|
||||
}
|
||||
|
||||
console.log('\n=== Clicking Q ===');
|
||||
const buttons = page.locator('.keyContainer > div > div');
|
||||
const count = await buttons.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const txt = (await buttons.nth(i).textContent()) || '';
|
||||
if (txt.trim().toUpperCase().startsWith('Q')) {
|
||||
await buttons.nth(i).click({ force: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
console.log('\n=== After Q click ===');
|
||||
for (let i = 0; i < gCount; i++) {
|
||||
const txt = (await gridItems.nth(i).textContent() || '').trim();
|
||||
console.log('Grid', i, '- contains Acropolis:', txt.includes('Acropolis'));
|
||||
if (txt.includes('Acropolis')) {
|
||||
console.log(' Text around Acropolis:');
|
||||
const idx = txt.indexOf('Acropolis');
|
||||
console.log(' ', txt.substring(Math.max(0, idx - 40), idx + 40));
|
||||
}
|
||||
}
|
||||
|
||||
// Entity view
|
||||
console.log('\nEntityView name:', (await page.locator('.entityClickView #entityName').textContent() || '').trim());
|
||||
|
||||
// Click Clear
|
||||
await page.locator('button').filter({ hasText: 'Clear Build Order' }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
console.log('\n=== After Clear ===');
|
||||
for (let i = 0; i < gCount; i++) {
|
||||
const txt = (await gridItems.nth(i).textContent() || '').trim();
|
||||
console.log('Grid', i, '- contains Acropolis:', txt.includes('Acropolis'));
|
||||
}
|
||||
|
||||
console.log('\nEntityView after clear count:', await page.locator('.entityClickView #entityName').count());
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -114,7 +114,6 @@ public interface IEntityDialogService
|
||||
public bool HasHistory();
|
||||
}
|
||||
|
||||
|
||||
public interface INoteService
|
||||
{
|
||||
public List<NoteContentModel> NoteContentModels { get; set; }
|
||||
|
||||
@@ -294,11 +294,11 @@ public class BuildOrderService : IBuildOrderService
|
||||
where ordersAtTime.Key + (orders.Production() == null
|
||||
? 0
|
||||
: orders.Production().BuildTime) <= interval
|
||||
&& !orders.Harvest().IsDepleted(
|
||||
interval,
|
||||
ordersAtTime.Key + (orders.Production() == null
|
||||
? 0
|
||||
: orders.Production().BuildTime))
|
||||
&& !orders.Harvest().IsDepleted(
|
||||
interval,
|
||||
ordersAtTime.Key + (orders.Production() == null
|
||||
? 0
|
||||
: orders.Production().BuildTime))
|
||||
select orders).ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ public class EconomyComparisionService : IEconomyComparisonService
|
||||
if (usedWorkers < harvester.Slots) workersNeeded += 1;
|
||||
}
|
||||
|
||||
if (harvester.RequiresWorker == false)
|
||||
if (!harvester.RequiresWorker)
|
||||
{
|
||||
if (harvester.Resource == ResourceType.Ether)
|
||||
economyAtSecond.Ether += harvester.HarvestedPerInterval * harvester.Slots;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Services</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@@ -22,7 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||
<ProjectReference Include="..\Model\Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using Tests.Helpers;
|
||||
|
||||
namespace Tests;
|
||||
|
||||
[SetUpFixture]
|
||||
public class GlobalSetup
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public async Task GlobalStart()
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("RUN_AGAINST_PRODUCTION") != "true")
|
||||
{
|
||||
await LocalServer.StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void GlobalStop()
|
||||
{
|
||||
LocalServer.Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Tests.Helpers;
|
||||
|
||||
public static class LocalServer
|
||||
{
|
||||
private static Process? _process;
|
||||
public static string? BaseUrl { get; private set; }
|
||||
|
||||
public static async Task StartAsync()
|
||||
{
|
||||
if (_process != null) return;
|
||||
|
||||
var root = FindProjectRoot();
|
||||
var webProject = Path.Combine(root, "Web");
|
||||
|
||||
Console.WriteLine($"[DEBUG_LOG] Starting local server for project: {webProject}");
|
||||
|
||||
_process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"run --project \"{webProject}\" --urls http://127.0.0.1:0",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = root
|
||||
}
|
||||
};
|
||||
|
||||
var tcs = new TaskCompletionSource<string>();
|
||||
|
||||
_process.OutputDataReceived += (s, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Data)) return;
|
||||
Console.WriteLine($"[SERVER] {e.Data}");
|
||||
var match = Regex.Match(e.Data, @"Now listening on:\s+(http://127.0.0.1:\d+)");
|
||||
if (match.Success)
|
||||
{
|
||||
tcs.TrySetResult(match.Groups[1].Value);
|
||||
}
|
||||
};
|
||||
|
||||
_process.ErrorDataReceived += (s, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
Console.Error.WriteLine($"[SERVER ERROR] {e.Data}");
|
||||
};
|
||||
|
||||
_process.Start();
|
||||
_process.BeginOutputReadLine();
|
||||
_process.BeginErrorReadLine();
|
||||
|
||||
// Wait for the URL to be parsed from output
|
||||
BaseUrl = await Task.WhenAny(tcs.Task, Task.Delay(30000)) == tcs.Task
|
||||
? tcs.Task.Result
|
||||
: null;
|
||||
|
||||
if (BaseUrl == null)
|
||||
{
|
||||
Stop();
|
||||
throw new Exception("Timeout waiting for local server to start and provide a URL.");
|
||||
}
|
||||
|
||||
Console.WriteLine($"[DEBUG_LOG] Local server started at: {BaseUrl}");
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
Console.WriteLine("[DEBUG_LOG] Stopping local server...");
|
||||
_process.Kill(true);
|
||||
_process.Dispose();
|
||||
_process = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindProjectRoot()
|
||||
{
|
||||
var current = AppDomain.CurrentDomain.BaseDirectory;
|
||||
while (!string.IsNullOrEmpty(current) && !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
var parent = Path.GetDirectoryName(current);
|
||||
if (parent == current) break;
|
||||
current = parent;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(current) || !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
// Fallback to searching up from current directory if BaseDirectory fails
|
||||
current = Directory.GetCurrentDirectory();
|
||||
while (!string.IsNullOrEmpty(current) && !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
var parent = Path.GetDirectoryName(current);
|
||||
if (parent == current) break;
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return current ?? throw new Exception("Could not find project root containing IGP.sln");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.Playwright;
|
||||
using Tests.Pages;
|
||||
using Tests.Shared;
|
||||
|
||||
namespace Tests.Helpers;
|
||||
|
||||
public class Website
|
||||
{
|
||||
public IPage Page { get; }
|
||||
public bool RunAgainstProduction { get; }
|
||||
public string BaseUrl { get; }
|
||||
|
||||
public Website(IPage page)
|
||||
{
|
||||
Page = page;
|
||||
RunAgainstProduction = Environment.GetEnvironmentVariable("RUN_AGAINST_PRODUCTION") == "true";
|
||||
|
||||
BaseUrl = RunAgainstProduction ? "https://igpfanreference.ca" : (LocalServer.BaseUrl ?? "http://localhost:5234");
|
||||
|
||||
NavigationBar = new NavigationBar(this);
|
||||
SearchDialog = new SearchDialog(this);
|
||||
BuildCalculatorPage = new BuildCalculatorPage(this);
|
||||
HarassCalculatorPage = new HarassCalculatorPage(this);
|
||||
DatabasePage = new DatabasePage(this);
|
||||
DatabaseSinglePage = new DatabaseSinglePage(this);
|
||||
}
|
||||
|
||||
public ILocator Locator(string selector) => Page.Locator(selector);
|
||||
public ILocator FindById(string id) => Page.Locator($"#{id}");
|
||||
public NavigationBar NavigationBar { get; }
|
||||
public SearchDialog SearchDialog { get; }
|
||||
public BuildCalculatorPage BuildCalculatorPage { get; }
|
||||
public HarassCalculatorPage HarassCalculatorPage { get; }
|
||||
public DatabasePage DatabasePage { get; }
|
||||
public DatabaseSinglePage DatabaseSinglePage { get; }
|
||||
|
||||
public async Task GotoAsync(string? path = null)
|
||||
{
|
||||
var url = path is null ? BaseUrl : $"{BaseUrl}/{path}";
|
||||
await Page.GotoAsync(url);
|
||||
}
|
||||
|
||||
public async Task ClickElementAsync(ILocator locator) => await locator.ClickAsync();
|
||||
|
||||
public async Task EnterInputAsync(ILocator locator, string value)
|
||||
{
|
||||
await locator.FillAsync(value);
|
||||
await locator.PressAsync("Enter");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Tests.Helpers;
|
||||
|
||||
namespace Tests.Pages;
|
||||
|
||||
public abstract class BasePage
|
||||
{
|
||||
protected Website Website { get; }
|
||||
|
||||
protected BasePage(Website website)
|
||||
{
|
||||
Website = website;
|
||||
}
|
||||
|
||||
public abstract string Url { get; }
|
||||
|
||||
public virtual async Task GotoAsync()
|
||||
{
|
||||
await Website.GotoAsync(Url);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetLinksAsync()
|
||||
{
|
||||
var content = Website.FindById("content");
|
||||
var links = content.Locator("a");
|
||||
var hrefs = await links.EvaluateAllAsync<string[]>("els => els.map(el => el.getAttribute('href')).filter(Boolean)");
|
||||
return hrefs.ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class ArmyComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public ArmyComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator ArmyView => _website.Locator(".armyView");
|
||||
|
||||
public ILocator DisplayValue(string label) =>
|
||||
_website.Locator(".displayContainer").Filter(new() { HasText = label }).Locator(".displayContent");
|
||||
|
||||
public ILocator ArmyCards => ArmyView.Locator(".armyCard");
|
||||
|
||||
public async Task<string> GetArmyCompletedAtAsync() =>
|
||||
(await DisplayValue("Army Completed At").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetArmyAttackingAtAsync() =>
|
||||
(await DisplayValue("Army Attacking At").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetArmyUnitNamesAsync()
|
||||
{
|
||||
var cards = await ArmyCards.AllAsync();
|
||||
var names = new List<string>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var text = (await card.InnerTextAsync()).Trim();
|
||||
var match = System.Text.RegularExpressions.Regex.Match(text, @"\d+x\s*(.+)");
|
||||
names.Add(match.Success ? match.Groups[1].Value.Trim() : text);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<(string Name, int Count)>> GetArmyUnitCountsAsync()
|
||||
{
|
||||
var cards = await ArmyCards.AllAsync();
|
||||
var counts = new List<(string, int)>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var countEl = card.Locator(".armyCount");
|
||||
var nameEl = card.Locator("div").Last;
|
||||
var count = (await countEl.TextContentAsync())?.Replace("x", "").Trim() ?? "0";
|
||||
var name = (await nameEl.TextContentAsync())?.Trim() ?? "";
|
||||
counts.Add((name, int.Parse(count)));
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class BankComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public BankComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator BankContainer => _website.Locator(".bankContainer");
|
||||
|
||||
public ILocator DisplayValue(string label) =>
|
||||
BankContainer.Locator(".displayContainer").Filter(new() { HasText = label }).Locator(".displayContent");
|
||||
|
||||
public async Task<string> GetTimeAsync() => (await DisplayValue("Time").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetAlloyAsync() => (await DisplayValue("Alloy").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetEtherAsync() => (await DisplayValue("Ether").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetPyreAsync() => (await DisplayValue("Pyre").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetSupplyAsync() => (await DisplayValue("Supply").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(0).TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetBusyWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(1).TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetCreatingWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(2).TextContentAsync())?.Trim() ?? "";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class BuildChartComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public BuildChartComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator ChartsContainer => _website.Locator(".chartsContainer");
|
||||
|
||||
public ILocator DisplayValue(string label) =>
|
||||
_website.Locator(".displayContainer").Filter(new() { HasText = label }).Locator(".displayContent");
|
||||
|
||||
public async Task<string> GetHighestAlloyAsync() => (await DisplayValue("Highest Alloy").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetHighestEtherAsync() => (await DisplayValue("Highest Ether").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetHighestPyreAsync() => (await DisplayValue("Highest Pyre").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetHighestArmyAsync() => (await DisplayValue("Highest Army").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<int> GetChartCountAsync() =>
|
||||
await ChartsContainer.Locator("> div").CountAsync();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class BuildOrderComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public BuildOrderComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator JsonTextarea => _website.Locator("textarea");
|
||||
|
||||
public async Task<string> GetJsonDataAsync() =>
|
||||
await JsonTextarea.InputValueAsync();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class EntityClickViewComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public EntityClickViewComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator EntityClickView => _website.Locator(".entityClickView");
|
||||
|
||||
public async Task<string?> GetEntityNameAsync()
|
||||
{
|
||||
var el = EntityClickView.Locator("#entityName");
|
||||
if (await el.CountAsync() == 0) return null;
|
||||
return (await el.TextContentAsync())?.Trim();
|
||||
}
|
||||
|
||||
public async Task<string?> GetEntityHealthAsync()
|
||||
{
|
||||
var healthText = EntityClickView.Locator("div").Filter(new() { HasTextRegex = new System.Text.RegularExpressions.Regex("Health", System.Text.RegularExpressions.RegexOptions.IgnoreCase) }).First;
|
||||
if (await healthText.CountAsync() == 0) return null;
|
||||
var text = (await healthText.TextContentAsync()) ?? "";
|
||||
var match = System.Text.RegularExpressions.Regex.Match(text, @"(\d+)");
|
||||
return match.Success ? match.Groups[1].Value : null;
|
||||
}
|
||||
|
||||
public async Task ClickDetailedViewAsync() =>
|
||||
await EntityClickView.Locator("button").Filter(new() { HasText = "Detailed" }).ClickAsync();
|
||||
|
||||
public async Task ClickPlainViewAsync() =>
|
||||
await EntityClickView.Locator("button").Filter(new() { HasText = "Plain" }).ClickAsync();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class FilterComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public FilterComponent(Website website) => _website = website;
|
||||
|
||||
private ILocator FactionSelect =>
|
||||
_website.Locator("select").Filter(new() { Has = _website.Locator("option:has-text('Aru'), option:has-text(\"Q'Rath\")") });
|
||||
|
||||
private ILocator ImmortalSelect =>
|
||||
_website.Locator("select").Filter(new() { Has = _website.Locator("option:has-text('Orzum'), option:has-text('Ajari'), option:has-text('Atzlan'), option:has-text('Mala'), option:has-text('Xol')") });
|
||||
|
||||
public async Task SelectFactionAsync(string faction) =>
|
||||
await FactionSelect.SelectOptionAsync(faction);
|
||||
|
||||
public async Task SelectImmortalAsync(string immortal) =>
|
||||
await ImmortalSelect.SelectOptionAsync(immortal);
|
||||
|
||||
public async Task<string> GetSelectedFactionAsync() =>
|
||||
await FactionSelect.InputValueAsync();
|
||||
|
||||
public async Task<string> GetSelectedImmortalAsync() =>
|
||||
await ImmortalSelect.InputValueAsync();
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetAvailableImmortalsAsync() =>
|
||||
await ImmortalSelect.Locator("option").AllTextContentsAsync();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class HighlightsComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public HighlightsComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator HighlightsContainer => _website.Locator(".highlightsContainer");
|
||||
public ILocator RequestedColumn => HighlightsContainer.Locator("div").Filter(new() { HasText = "Requested" }).Locator("+ div");
|
||||
public ILocator FinishedColumn => HighlightsContainer.Locator("div").Filter(new() { HasText = "Finished" }).Locator("+ div");
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetRequestedItemsAsync() =>
|
||||
await GetHighlightItemsAsync();
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetFinishedItemsAsync() =>
|
||||
await GetHighlightItemsAsync();
|
||||
|
||||
private async Task<IReadOnlyList<string>> GetHighlightItemsAsync()
|
||||
{
|
||||
var items = await _website.Locator(".highlightsContainer").Locator("div").Filter(new() { HasTextRegex = new System.Text.RegularExpressions.Regex(@"^\d+\s*\|") }).AllAsync();
|
||||
var result = new List<string>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var text = (await item.TextContentAsync())?.Trim();
|
||||
if (text is not null) result.Add(text);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class HotkeyViewerComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public HotkeyViewerComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator KeyContainer => _website.Locator(".keyContainer");
|
||||
|
||||
public async Task<ILocator?> FindKeyButtonAsync(string keyLabel)
|
||||
{
|
||||
var upper = keyLabel.ToUpperInvariant();
|
||||
var buttons = KeyContainer.Locator("> div > div");
|
||||
var count = await buttons.CountAsync();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var btn = buttons.Nth(i);
|
||||
var text = (await btn.TextContentAsync())?.Trim().ToUpperInvariant() ?? "";
|
||||
if (text.StartsWith(upper)) return btn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task ClickKeyAsync(string keyText)
|
||||
{
|
||||
var btn = await FindKeyButtonAsync(keyText);
|
||||
if (btn is null) throw new InvalidOperationException($"Key \"{keyText}\" not found");
|
||||
await btn.ClickAsync(new() { Force = true });
|
||||
}
|
||||
|
||||
public async Task<string?> GetFirstEntityNameAsync(string keyText)
|
||||
{
|
||||
var btn = await FindKeyButtonAsync(keyText);
|
||||
if (btn is null) return null;
|
||||
var entities = btn.Locator("> div");
|
||||
if (await entities.CountAsync() == 0) return null;
|
||||
return (await entities.First.TextContentAsync())?.Trim();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetEntityNamesOnKeyAsync(string keyText)
|
||||
{
|
||||
var btn = await FindKeyButtonAsync(keyText);
|
||||
if (btn is null) return Array.Empty<string>();
|
||||
var entities = btn.Locator("> div");
|
||||
var count = await entities.CountAsync();
|
||||
var names = new List<string>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var text = (await entities.Nth(i).TextContentAsync())?.Trim();
|
||||
if (!string.IsNullOrEmpty(text)) names.Add(text);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class OptionsComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public OptionsComponent(Website website) => _website = website;
|
||||
|
||||
private ILocator FormNumberInput(string label) =>
|
||||
_website.Locator(".formNumberContainer").Filter(new() { HasText = label }).Locator("input[type='number']");
|
||||
|
||||
private ILocator ButtonWithLabel(string label) =>
|
||||
_website.Locator("button").Filter(new() { HasText = label });
|
||||
|
||||
public ILocator BuildingInputDelayInput => FormNumberInput("Building Input Delay");
|
||||
public ILocator WaitTimeInput => FormNumberInput("Wait Time");
|
||||
public ILocator WaitToInput => FormNumberInput("Wait To");
|
||||
public ILocator AddWaitButton => ButtonWithLabel("Add Wait").First;
|
||||
public ILocator AddWaitToButton => ButtonWithLabel("Add Wait").Last;
|
||||
|
||||
public async Task SetBuildingInputDelayAsync(int value)
|
||||
{
|
||||
await BuildingInputDelayInput.FillAsync(value.ToString());
|
||||
await BuildingInputDelayInput.PressAsync("Enter");
|
||||
}
|
||||
|
||||
public async Task SetWaitTimeAsync(int value) =>
|
||||
await WaitTimeInput.FillAsync(value.ToString());
|
||||
|
||||
public async Task SetWaitToAsync(int value) =>
|
||||
await WaitToInput.FillAsync(value.ToString());
|
||||
|
||||
public async Task ClickAddWaitAsync() => await AddWaitButton.ClickAsync();
|
||||
public async Task ClickAddWaitToAsync() => await AddWaitToButton.ClickAsync();
|
||||
|
||||
public async Task<string> GetBuildingInputDelayAsync() => await BuildingInputDelayInput.InputValueAsync();
|
||||
public async Task<string> GetWaitTimeAsync() => await WaitTimeInput.InputValueAsync();
|
||||
public async Task<string> GetWaitToAsync() => await WaitToInput.InputValueAsync();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class TimelineComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public TimelineComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator Container =>
|
||||
_website.Locator(".calculatorGrid > div").Filter(new() { HasText = "Timeline highlights" });
|
||||
|
||||
public async Task<bool> ContainsEntityAsync(string name)
|
||||
{
|
||||
var text = (await Container.TextContentAsync()) ?? "";
|
||||
return text.Contains(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class TimingComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public TimingComponent(Website website) => _website = website;
|
||||
|
||||
private ILocator FormNumberInput(string label) =>
|
||||
_website.Locator(".formNumberContainer").Filter(new() { HasText = label }).Locator("input[type='number']");
|
||||
|
||||
public ILocator AttackTimeInput => FormNumberInput("Attack Time");
|
||||
public ILocator TravelTimeInput => FormNumberInput("Travel Time");
|
||||
|
||||
public async Task SetAttackTimeAsync(int value)
|
||||
{
|
||||
await AttackTimeInput.FillAsync(value.ToString());
|
||||
await AttackTimeInput.PressAsync("Enter");
|
||||
}
|
||||
|
||||
public async Task SetTravelTimeAsync(int value)
|
||||
{
|
||||
await TravelTimeInput.FillAsync(value.ToString());
|
||||
await TravelTimeInput.PressAsync("Enter");
|
||||
}
|
||||
|
||||
public async Task<string> GetAttackTimeAsync() => await AttackTimeInput.InputValueAsync();
|
||||
public async Task<string> GetTravelTimeAsync() => await TravelTimeInput.InputValueAsync();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Tests.Pages.BuildCalculator;
|
||||
using Tests.Shared;
|
||||
|
||||
namespace Tests.Pages;
|
||||
|
||||
public class BuildCalculatorPage : BasePage
|
||||
{
|
||||
public BuildCalculatorPage(Website website) : base(website)
|
||||
{
|
||||
Timing = new TimingComponent(website);
|
||||
Filter = new FilterComponent(website);
|
||||
Options = new OptionsComponent(website);
|
||||
Bank = new BankComponent(website);
|
||||
Army = new ArmyComponent(website);
|
||||
Highlights = new HighlightsComponent(website);
|
||||
BuildOrder = new BuildOrderComponent(website);
|
||||
Timeline = new TimelineComponent(website);
|
||||
Hotkeys = new HotkeyViewerComponent(website);
|
||||
EntityView = new EntityClickViewComponent(website);
|
||||
Chart = new BuildChartComponent(website);
|
||||
Toast = new ToastComponent(website);
|
||||
}
|
||||
|
||||
public override string Url => "build-calculator";
|
||||
|
||||
public TimingComponent Timing { get; }
|
||||
public FilterComponent Filter { get; }
|
||||
public OptionsComponent Options { get; }
|
||||
public BankComponent Bank { get; }
|
||||
public ArmyComponent Army { get; }
|
||||
public HighlightsComponent Highlights { get; }
|
||||
public BuildOrderComponent BuildOrder { get; }
|
||||
public TimelineComponent Timeline { get; }
|
||||
public HotkeyViewerComponent Hotkeys { get; }
|
||||
public EntityClickViewComponent EntityView { get; }
|
||||
public BuildChartComponent Chart { get; }
|
||||
public ToastComponent Toast { get; }
|
||||
|
||||
public ILocator CalculatorGrid => Website.Locator(".calculatorGrid");
|
||||
public ILocator ClearBuildOrderButton => Website.Locator("button").Filter(new() { HasText = "Clear Build Order" });
|
||||
|
||||
public async Task ClickClearBuildOrderAsync() => await ClearBuildOrderButton.ClickAsync();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Tests.Pages;
|
||||
|
||||
public class DatabasePage : BasePage
|
||||
{
|
||||
public DatabasePage(Website website) : base(website) { }
|
||||
|
||||
public override string Url => "database";
|
||||
|
||||
public async Task FilterNameAsync(string name)
|
||||
{
|
||||
var input = Website.FindById("filterName").First;
|
||||
await Website.EnterInputAsync(input, name);
|
||||
}
|
||||
|
||||
public async Task<string> GetEntityNameAsync(string entityType, string entityName)
|
||||
{
|
||||
var el = Website.Locator($"#{entityType.ToLower()}-{entityName.ToLower()}").Locator("#entityName");
|
||||
return (await el.InnerTextAsync()).Trim();
|
||||
}
|
||||
|
||||
public async Task<string> GetEntityNameByIndexAsync(int index)
|
||||
{
|
||||
var el = Website.FindById("entityName").Nth(index);
|
||||
return (await el.InnerTextAsync()).Trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Tests.Pages;
|
||||
|
||||
public class DatabaseSinglePage : BasePage
|
||||
{
|
||||
public DatabaseSinglePage(Website website) : base(website) { }
|
||||
|
||||
public override string Url => "database";
|
||||
|
||||
public async Task GotoWithSearchAsync(string searchText)
|
||||
{
|
||||
await Website.GotoAsync($"{Url}/{searchText}");
|
||||
}
|
||||
|
||||
public async Task<string> GetEntityNameAsync() =>
|
||||
(await Website.FindById("entityName").InnerTextAsync()).Trim();
|
||||
|
||||
public async Task<string> GetEntityHealthAsync() =>
|
||||
(await Website.FindById("entityHealth").InnerTextAsync()).Trim();
|
||||
|
||||
public async Task<string> GetInvalidSearchAsync() =>
|
||||
(await Website.FindById("invalidSearch").InnerTextAsync()).Trim();
|
||||
|
||||
public async Task<string> GetValidSearchAsync() =>
|
||||
(await Website.FindById("validSearch").InnerTextAsync()).Trim();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Tests.Pages;
|
||||
|
||||
public class HarassCalculatorPage : BasePage
|
||||
{
|
||||
public HarassCalculatorPage(Website website) : base(website) { }
|
||||
|
||||
public override string Url => "harass-calculator";
|
||||
|
||||
public async Task SetWorkersLostToHarassAsync(int number) =>
|
||||
await EnterAndPressAsync("numberOfWorkersLostToHarass", number);
|
||||
|
||||
public async Task SetNumberOfTownHallsExistingAsync(int number) =>
|
||||
await EnterAndPressAsync("numberOfTownHallsExisting", number);
|
||||
|
||||
public async Task SetTownHallTravelTimeAsync(int index, int seconds) =>
|
||||
await EnterInputAtIndexAsync("numberOfTownHallTravelTimes", index, seconds);
|
||||
|
||||
public async Task<int> GetTotalAlloyHarassmentAsync() => await ReadIntAsync("totalAlloyHarassment");
|
||||
public async Task<int> GetWorkerReplacementCostAsync() => await ReadIntAsync("workerReplacementCost");
|
||||
public async Task<int> GetDelayedMiningCostAsync() => await ReadIntAsync("delayedMiningCost");
|
||||
public async Task<int> GetAverageTravelTimeAsync() => await ReadIntAsync("getAverageTravelTime");
|
||||
public async Task<int> GetExampleTotalAlloyLossAsync() => await ReadIntAsync("exampleTotalAlloyLoss");
|
||||
public async Task<int> GetExampleWorkerCostAsync() => await ReadIntAsync("exampleWorkerCost");
|
||||
public async Task<int> GetExampleMiningTimeCostAsync() => await ReadIntAsync("exampleMiningTimeCost");
|
||||
public async Task<int> GetExampleTotalAlloyLossAccurateAsync() => await ReadIntAsync("exampleTotalAlloyLossAccurate");
|
||||
public async Task<int> GetExampleTotalAlloyLossDifferenceAsync() => await ReadIntAsync("exampleTotalAlloyLossDifference");
|
||||
public async Task<int> GetExampleTotalAlloyLossAccurateDifferenceAsync() => await ReadIntAsync("exampleTotalAlloyLossAccurateDifference");
|
||||
|
||||
private async Task EnterAndPressAsync(string id, int value)
|
||||
{
|
||||
var locator = Website.FindById(id);
|
||||
await Website.EnterInputAsync(locator, value.ToString());
|
||||
}
|
||||
|
||||
private async Task EnterInputAtIndexAsync(string parentId, int index, int value)
|
||||
{
|
||||
var inputs = Website.FindById(parentId).Locator("input");
|
||||
var input = inputs.Nth(index);
|
||||
await input.FillAsync(value.ToString());
|
||||
await input.PressAsync("Enter");
|
||||
}
|
||||
|
||||
private async Task<int> ReadIntAsync(string id)
|
||||
{
|
||||
var text = await Website.FindById(id).TextContentAsync() ?? "";
|
||||
return int.Parse(text.Trim());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Tests.Shared;
|
||||
|
||||
public class NavigationBar
|
||||
{
|
||||
private readonly Website _website;
|
||||
public NavigationBar(Website website) => _website = website;
|
||||
|
||||
public ILocator SearchButton => _website.Locator("#desktop-searchButton");
|
||||
|
||||
public async Task ClickHomeLinkAsync()
|
||||
{
|
||||
await _website.Locator("a:has-text(\"IGP Fan Reference\")").ClickAsync();
|
||||
}
|
||||
|
||||
public async Task<SearchDialog> ClickSearchButtonAsync()
|
||||
{
|
||||
await SearchButton.ClickAsync();
|
||||
return _website.SearchDialog;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Tests.Shared;
|
||||
|
||||
public class SearchDialog
|
||||
{
|
||||
private readonly Website _website;
|
||||
public SearchDialog(Website website) => _website = website;
|
||||
|
||||
public ILocator SearchBackground => _website.FindById("searchBackground");
|
||||
public ILocator SearchInput => _website.FindById("searchInput");
|
||||
|
||||
public async Task CloseDialogAsync()
|
||||
{
|
||||
await _website.ClickElementAsync(SearchBackground);
|
||||
}
|
||||
|
||||
public async Task<SearchDialog> SearchAsync(string text)
|
||||
{
|
||||
await _website.EnterInputAsync(SearchInput, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task SelectSearchEntityAsync(string label)
|
||||
{
|
||||
await _website.ClickElementAsync(_website.Locator($"button[label=\"{label}\"]"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Tests.Shared;
|
||||
|
||||
public class ToastComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public ToastComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator Container => _website.Locator(".toastsContainer");
|
||||
public ILocator Toasts => _website.Locator(".toastsContainer .toastContainer");
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetToastTitlesAsync()
|
||||
{
|
||||
var titles = await _website.Locator(".toastsContainer .toastTitle").AllTextContentsAsync();
|
||||
return titles.Select(t => t.Trim()).Where(t => !string.IsNullOrEmpty(t)).ToList();
|
||||
}
|
||||
|
||||
public async Task<bool> HasToastContainingAsync(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _website.Page.WaitForFunctionAsync(
|
||||
@"(expected) => {
|
||||
const titles = document.querySelectorAll('.toastsContainer .toastTitle');
|
||||
return Array.from(titles).some(t => t.textContent.trim().includes(expected));
|
||||
}",
|
||||
text,
|
||||
new() { Timeout = 3000 }
|
||||
);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Microsoft.Playwright.NUnit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Specs;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
||||
public class BuildCalculatorTests : PageTest
|
||||
{
|
||||
private Helpers.Website _website = null!;
|
||||
|
||||
[SetUp]
|
||||
public void CreateWebsite() => _website = new Helpers.Website(Page);
|
||||
|
||||
[Test]
|
||||
public async Task AddEntitiesViaKeyboardQWE()
|
||||
{
|
||||
var calc = _website.BuildCalculatorPage;
|
||||
await calc.GotoAsync();
|
||||
|
||||
await calc.Filter.SelectFactionAsync("Q'Rath");
|
||||
await calc.Filter.SelectImmortalAsync("Orzum");
|
||||
|
||||
await calc.Hotkeys.ClickKeyAsync("TAB");
|
||||
|
||||
var keyMap = new Dictionary<string, string> { ["Q"] = "q", ["W"] = "w", ["E"] = "e", ["TAB"] = "Tab" };
|
||||
|
||||
foreach (var key in new[] { "Q", "W", "E", "TAB" })
|
||||
{
|
||||
var entityNames = await calc.Hotkeys.GetEntityNamesOnKeyAsync(key);
|
||||
if (entityNames.Count == 0) continue;
|
||||
|
||||
await Page.Keyboard.PressAsync(keyMap[key]);
|
||||
|
||||
var viewName = await calc.EntityView.GetEntityNameAsync();
|
||||
Assert.That(viewName, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(entityNames, Does.Contain(viewName));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AddEntitiesViaHotkeysClickTABQWE()
|
||||
{
|
||||
var calc = _website.BuildCalculatorPage;
|
||||
await calc.GotoAsync();
|
||||
|
||||
await calc.Filter.SelectFactionAsync("Q'Rath");
|
||||
await calc.Filter.SelectImmortalAsync("Orzum");
|
||||
|
||||
foreach (var key in new[] { "TAB", "Q", "W", "E" })
|
||||
{
|
||||
var entityNames = await calc.Hotkeys.GetEntityNamesOnKeyAsync(key);
|
||||
if (entityNames.Count == 0) continue;
|
||||
|
||||
await calc.Hotkeys.ClickKeyAsync(key);
|
||||
|
||||
var viewName = await calc.EntityView.GetEntityNameAsync();
|
||||
Assert.That(viewName, Is.Not.Null.And.Not.Empty);
|
||||
Assert.That(entityNames, Does.Contain(viewName));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AddAcropolisViaQVerifyEntityViewAndTimelineThenClear()
|
||||
{
|
||||
var calc = _website.BuildCalculatorPage;
|
||||
await calc.GotoAsync();
|
||||
|
||||
await calc.Filter.SelectFactionAsync("Q'Rath");
|
||||
await calc.Filter.SelectImmortalAsync("Orzum");
|
||||
|
||||
Assert.That(await calc.Timeline.ContainsEntityAsync("Acropolis"), Is.False);
|
||||
|
||||
await calc.Hotkeys.ClickKeyAsync("Q");
|
||||
|
||||
Assert.That(await calc.EntityView.GetEntityNameAsync(), Is.EqualTo("Acropolis"));
|
||||
Assert.That(await calc.Timeline.ContainsEntityAsync("Acropolis"), Is.True);
|
||||
|
||||
await calc.ClickClearBuildOrderAsync();
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.That(await calc.Timeline.ContainsEntityAsync("Acropolis"), Is.False);
|
||||
Assert.That(await calc.EntityView.GetEntityNameAsync(), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MissingRequirementsToastWhenBuildingSoulFoundryWithoutLegionHall()
|
||||
{
|
||||
var calc = _website.BuildCalculatorPage;
|
||||
await calc.GotoAsync();
|
||||
|
||||
await calc.Filter.SelectFactionAsync("Q'Rath");
|
||||
await calc.Filter.SelectImmortalAsync("Orzum");
|
||||
|
||||
await calc.Hotkeys.ClickKeyAsync("E");
|
||||
var hasToast = await calc.Toast.HasToastContainingAsync("Missing Requirements");
|
||||
Assert.That(hasToast, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NotEnoughEtherToastWhenBuildingSoulFoundryAfterLegionHall()
|
||||
{
|
||||
var calc = _website.BuildCalculatorPage;
|
||||
await calc.GotoAsync();
|
||||
|
||||
await calc.Filter.SelectFactionAsync("Q'Rath");
|
||||
await calc.Filter.SelectImmortalAsync("Orzum");
|
||||
|
||||
await calc.Hotkeys.ClickKeyAsync("W");
|
||||
await calc.Hotkeys.ClickKeyAsync("E");
|
||||
|
||||
var hasToast = await calc.Toast.HasToastContainingAsync("Not Enough Ether");
|
||||
Assert.That(hasToast, Is.True);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.Playwright.NUnit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Specs;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
||||
public class HarassCalculatorTests : PageTest
|
||||
{
|
||||
private Helpers.Website _website = null!;
|
||||
|
||||
[SetUp]
|
||||
public void CreateWebsite() => _website = new Helpers.Website(Page);
|
||||
|
||||
[Test]
|
||||
public async Task CalculatorInput()
|
||||
{
|
||||
var page = _website.HarassCalculatorPage;
|
||||
await page.GotoAsync();
|
||||
await page.SetWorkersLostToHarassAsync(3);
|
||||
await page.SetNumberOfTownHallsExistingAsync(2);
|
||||
await page.SetTownHallTravelTimeAsync(0, 30);
|
||||
var result = await page.GetTotalAlloyHarassmentAsync();
|
||||
Assert.That(result, Is.EqualTo(240));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CalculatedExampleInformation()
|
||||
{
|
||||
var page = _website.HarassCalculatorPage;
|
||||
await page.GotoAsync();
|
||||
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
Assert.That(await page.GetExampleTotalAlloyLossAsync(), Is.EqualTo(720));
|
||||
Assert.That(await page.GetExampleWorkerCostAsync(), Is.EqualTo(300));
|
||||
Assert.That(await page.GetExampleMiningTimeCostAsync(), Is.EqualTo(420));
|
||||
Assert.That(await page.GetExampleTotalAlloyLossAccurateAsync(), Is.EqualTo(450));
|
||||
Assert.That(await page.GetExampleTotalAlloyLossDifferenceAsync(), Is.EqualTo(300));
|
||||
Assert.That(await page.GetExampleTotalAlloyLossAccurateDifferenceAsync(), Is.EqualTo(270));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.Playwright.NUnit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Specs;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
||||
public class LinksTests : PageTest
|
||||
{
|
||||
private Helpers.Website _website = null!;
|
||||
|
||||
[SetUp]
|
||||
public void CreateWebsite() => _website = new Helpers.Website(Page);
|
||||
|
||||
[Test]
|
||||
public async Task VerifyPageLinks()
|
||||
{
|
||||
_website = new Helpers.Website(Page);
|
||||
|
||||
await _website.HarassCalculatorPage.GotoAsync();
|
||||
var harassLinks = await _website.HarassCalculatorPage.GetLinksAsync();
|
||||
foreach (var link in harassLinks) await VerifyLinkAsync(link);
|
||||
|
||||
await _website.DatabasePage.GotoAsync();
|
||||
var dbLinks = await _website.DatabasePage.GetLinksAsync();
|
||||
foreach (var link in dbLinks) await VerifyLinkAsync(link);
|
||||
|
||||
await _website.DatabaseSinglePage.GotoWithSearchAsync("throne");
|
||||
var singleLinks = await _website.DatabaseSinglePage.GetLinksAsync();
|
||||
foreach (var link in singleLinks) await VerifyLinkAsync(link);
|
||||
}
|
||||
|
||||
private static async Task VerifyLinkAsync(string link)
|
||||
{
|
||||
if (link.StartsWith("mailto")) return;
|
||||
|
||||
using var client = new System.Net.Http.HttpClient();
|
||||
var response = await client.GetAsync(link);
|
||||
Assert.That(response.IsSuccessStatusCode, Is.True, $"Link '{link}' returned {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Playwright.NUnit;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Specs;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
||||
public class SearchFeaturesTests : PageTest
|
||||
{
|
||||
private Helpers.Website _website = null!;
|
||||
|
||||
[SetUp]
|
||||
public void CreateWebsite() => _website = new Helpers.Website(Page);
|
||||
|
||||
[Test]
|
||||
public async Task DesktopOpenCloseSearchDialog()
|
||||
{
|
||||
await _website.GotoAsync();
|
||||
await _website.NavigationBar.ClickSearchButtonAsync();
|
||||
await _website.SearchDialog.CloseDialogAsync();
|
||||
await _website.NavigationBar.ClickHomeLinkAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DesktopSearchForThrone()
|
||||
{
|
||||
await _website.GotoAsync();
|
||||
await _website.NavigationBar.ClickSearchButtonAsync();
|
||||
await _website.SearchDialog.SearchAsync("Throne");
|
||||
await _website.SearchDialog.SelectSearchEntityAsync("Throne");
|
||||
|
||||
var name = await _website.DatabaseSinglePage.GetEntityNameAsync();
|
||||
var health = await _website.DatabaseSinglePage.GetEntityHealthAsync();
|
||||
|
||||
Assert.That(name, Is.EqualTo("Throne"));
|
||||
Assert.That(health.Trim(), Is.Not.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DesktopFilterForThrone()
|
||||
{
|
||||
var page = _website.DatabasePage;
|
||||
await page.GotoAsync();
|
||||
await page.FilterNameAsync("Throne");
|
||||
var name = await page.GetEntityNameByIndexAsync(0);
|
||||
Assert.That(name, Is.EqualTo("Throne"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SeeThroneByDefault()
|
||||
{
|
||||
var page = _website.DatabasePage;
|
||||
await page.GotoAsync();
|
||||
var name = await page.GetEntityNameAsync("army", "throne");
|
||||
Assert.That(name, Is.EqualTo("Throne"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DirectLinkNotThroneFailure()
|
||||
{
|
||||
var page = _website.DatabaseSinglePage;
|
||||
await page.GotoWithSearchAsync("not throne");
|
||||
var invalidSearch = await page.GetInvalidSearchAsync();
|
||||
var validSearch = await page.GetValidSearchAsync();
|
||||
Assert.That(invalidSearch, Is.EqualTo("not throne"));
|
||||
Assert.That(validSearch, Is.EqualTo("Throne"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="NUnit" Version="4.3.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.52.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.Playwright" />
|
||||
<Using Include="Tests.Helpers" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -48,11 +48,11 @@
|
||||
--immortal-mala: #dc7a29;
|
||||
--immortal-xol: #87aa87;
|
||||
--immortal-atzlan: #8B7355;
|
||||
|
||||
|
||||
--faction-qrath: #8EACCD;
|
||||
--immortal-orzum: #4A6B8A;
|
||||
--immortal-ajari: #b4e2e3;
|
||||
|
||||
|
||||
--severity-warning-color: #2a2000;
|
||||
--severity-warning-border-color: #755c13;
|
||||
--severity-error-color: #290102;
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
</FactionBorderComponent>
|
||||
</InfoTooltipComponent>
|
||||
</PanelComponent>
|
||||
|
||||
|
||||
<ButtonComponent MyButtonType="MyButtonType.Secondary" OnClick="OnResetClicked">Clear Build Order
|
||||
</ButtonComponent>
|
||||
<PanelComponent>
|
||||
+2
-2
@@ -231,13 +231,13 @@ else
|
||||
|
||||
var alloyAutomaticHarvesters = from harvester in economyAtSecond.HarvestPoints
|
||||
where harvester.Harvest() != null
|
||||
where harvester.Harvest().RequiresWorker == false
|
||||
where !harvester.Harvest().RequiresWorker
|
||||
where harvester.Harvest().Resource == ResourceType.Alloy
|
||||
select harvester;
|
||||
|
||||
var etherAutomaticHarvesters = from harvester in economyAtSecond.HarvestPoints
|
||||
where harvester.Harvest() != null
|
||||
where harvester.Harvest().RequiresWorker == false
|
||||
where !harvester.Harvest().RequiresWorker
|
||||
where harvester.Harvest().Resource == ResourceType.Ether
|
||||
select harvester;
|
||||
|
||||
+7
-7
@@ -9,14 +9,14 @@
|
||||
</FormLayoutComponent>
|
||||
|
||||
@code {
|
||||
/**
|
||||
// TODO: Make this more elegant, and useful. Also, it currently doesn't clear properly
|
||||
<FormTextAreaComponent Label="JSON Data"
|
||||
Rows="14"
|
||||
Value="@buildOrderService.AsJson()">
|
||||
</FormTextAreaComponent>
|
||||
*/
|
||||
|
||||
/**
|
||||
* // TODO: Make this more elegant, and useful. Also, it currently doesn't clear properly
|
||||
* <FormTextAreaComponent Label="JSON Data"
|
||||
* Rows="14"
|
||||
* Value="@buildOrderService.AsJson()">
|
||||
* </FormTextAreaComponent>
|
||||
*/
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
+1
@@ -28,4 +28,5 @@
|
||||
var color = faction == DataType.FACTION_Aru ? "var(--faction-aru)" : "var(--faction-qrath)";
|
||||
return $"border-top: 4px solid {color}; padding-top: 14px; margin-top: -12px;";
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -33,4 +33,5 @@
|
||||
else if (immortal == DataType.IMMORTAL_Xol) color = "var(--immortal-xol)";
|
||||
return $"border-top: 4px solid {color}; padding-top: 14px; margin-top: -12px;";
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -55,7 +55,7 @@
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RefreshDefaults()
|
||||
{
|
||||
_viewType = StorageService.GetValue<bool>(StorageKeys.IsPlainView) ? EntityViewType.Plain : EntityViewType.Detailed;
|
||||
@@ -77,4 +77,5 @@
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -77,6 +77,6 @@
|
||||
{
|
||||
FilterService.Unsubscribe(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -114,7 +114,7 @@
|
||||
[Inject] public IEntityFilterService EntityFilterService { get; set; } = default!;
|
||||
|
||||
readonly List<EntityModel> defaults = (from entity in EntityModel.GetList()
|
||||
where entity.IsSpeculative == false
|
||||
where !entity.IsSpeculative
|
||||
select entity).ToList();
|
||||
|
||||
List<EntityModel> factions = default!;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user