diff --git a/Chrono/Tests/FeatureTests.cs b/Chrono/Tests/FeatureTests.cs new file mode 100644 index 0000000..62cc490 --- /dev/null +++ b/Chrono/Tests/FeatureTests.cs @@ -0,0 +1,46 @@ +using Microsoft.Playwright; +using Microsoft.Playwright.NUnit; +using Tests.PageObjects; + +namespace Tests; + +[Parallelizable(ParallelScope.Self)] +[TestFixture] +public class FeatureTests : PageTest +{ + private DecksPage _decksPage; + private DeckDetailPage _deckDetailPage; + private AgentsPage _agentsPage; + + [SetUp] + public void Setup() + { + _decksPage = new DecksPage(Page); + _deckDetailPage = new DeckDetailPage(Page); + _agentsPage = new AgentsPage(Page); + } + + [Test] + public async Task DeckManaCurve_ShouldBeVisibleOnDetail() + { + await _decksPage.GotoAsync(); + + // Assuming there is at least one deck. If not, this might need a more robust approach. + var deckCards = _decksPage.GetDeckCards(); + await Expect(deckCards.First).ToBeVisibleAsync(); + + await deckCards.First.ClickAsync(); + + await Expect(_deckDetailPage.ManaCurve).ToBeVisibleAsync(); + var barCount = await _deckDetailPage.GetManaBarCountAsync(); + Assert.That(barCount, Is.GreaterThan(0)); + } + + [Test] + public async Task AgentsGrid_ShouldLoadSuccessfully() + { + await _agentsPage.GotoAsync(); + await _agentsPage.WaitForGridAsync(); + await Expect(_agentsPage.Grid).ToBeVisibleAsync(); + } +} diff --git a/Chrono/Tests/PageObjects/AgentsPage.cs b/Chrono/Tests/PageObjects/AgentsPage.cs new file mode 100644 index 0000000..5cc227c --- /dev/null +++ b/Chrono/Tests/PageObjects/AgentsPage.cs @@ -0,0 +1,18 @@ +using Microsoft.Playwright; + +namespace Tests.PageObjects; + +public class AgentsPage : BasePage +{ + public AgentsPage(IPage page) : base(page) { } + + public async Task GotoAsync() => await NavigateToAsync("/agents"); + + public ILocator Grid => Page.Locator(".agents-grid"); + + public async Task WaitForGridAsync() + { + await Page.WaitForLoadStateAsync(LoadState.NetworkIdle); + await Grid.WaitForAsync(); + } +} diff --git a/Chrono/Tests/PageObjects/BasePage.cs b/Chrono/Tests/PageObjects/BasePage.cs new file mode 100644 index 0000000..3c0fce1 --- /dev/null +++ b/Chrono/Tests/PageObjects/BasePage.cs @@ -0,0 +1,21 @@ +using Microsoft.Playwright; + +namespace Tests.PageObjects; + +public abstract class BasePage +{ + protected readonly IPage Page; + protected readonly string BaseUrl = "http://localhost:5256"; + + protected BasePage(IPage page) + { + Page = page; + } + + public async Task NavigateToAsync(string path) + { + await Page.GotoAsync($"{BaseUrl}{path}"); + } + + public ILocator GetHeading() => Page.Locator("h1"); +} diff --git a/Chrono/Tests/PageObjects/DeckDetailPage.cs b/Chrono/Tests/PageObjects/DeckDetailPage.cs new file mode 100644 index 0000000..0764abc --- /dev/null +++ b/Chrono/Tests/PageObjects/DeckDetailPage.cs @@ -0,0 +1,15 @@ +using Microsoft.Playwright; + +namespace Tests.PageObjects; + +public class DeckDetailPage : BasePage +{ + public DeckDetailPage(IPage page) : base(page) { } + + public ILocator ManaCurve => Page.Locator(".mana-curve"); + public ILocator ManaBars => Page.Locator(".mana-bar"); + public ILocator ManaBarLabels => Page.Locator(".mana-bar-label"); + public ILocator ManaCosts => Page.Locator(".mana-cost"); + + public async Task GetManaBarCountAsync() => await ManaBars.CountAsync(); +} diff --git a/Chrono/Tests/PageObjects/DecksPage.cs b/Chrono/Tests/PageObjects/DecksPage.cs new file mode 100644 index 0000000..c66d155 --- /dev/null +++ b/Chrono/Tests/PageObjects/DecksPage.cs @@ -0,0 +1,17 @@ +using Microsoft.Playwright; + +namespace Tests.PageObjects; + +public class DecksPage : BasePage +{ + public DecksPage(IPage page) : base(page) { } + + public async Task GotoAsync() => await NavigateToAsync("/decks"); + + public ILocator GetDeckCards() => Page.Locator(".deck-card"); + + public async Task ClickDeckByNameAsync(string name) + { + await Page.Locator(".deck-card-title").GetByText(name, new() { Exact = true }).ClickAsync(); + } +} diff --git a/Chrono/Tests/TelerikLicenseTests.cs b/Chrono/Tests/TelerikLicenseTests.cs index 5da349d..583d4c4 100644 --- a/Chrono/Tests/TelerikLicenseTests.cs +++ b/Chrono/Tests/TelerikLicenseTests.cs @@ -7,24 +7,21 @@ namespace Tests; [TestFixture] public class TelerikLicenseTests : PageTest { + + private const string BaseUrl = "http://localhost:5256"; + [Test] public async Task TelerikLicenseBannerIsNotVisible() { - // 1. Navigate to the agents page which uses Telerik components - await Page.GotoAsync("http://localhost:8080/agents"); - - // 2. Wait for the page to load and the grid to be visible + await Page.GotoAsync(BaseUrl + "/agents"); + await Page.WaitForLoadStateAsync(LoadState.NetworkIdle); var grid = Page.Locator(".agents-grid"); await Expect(grid).ToBeVisibleAsync(); - // 3. Verify that the Telerik license warning banner is NOT present - // According to Telerik docs, the warning text is: - // "We couldn't verify your license key for Telerik UI for Blazor. Please see the build log for details and resolution steps" var licenseWarning = Page.GetByText("We couldn't verify your license key for Telerik UI for Blazor"); await Expect(licenseWarning).Not.ToBeVisibleAsync(); - // 4. Also verify that no "Trial" banner is visible var trialBanner = Page.GetByText("Telerik UI for Blazor Trial", new PageGetByTextOptions { Exact = false }); await Expect(trialBanner).Not.ToBeVisibleAsync(); } diff --git a/Chrono/Tests/TestSetup.cs b/Chrono/Tests/TestSetup.cs new file mode 100644 index 0000000..f9c4842 --- /dev/null +++ b/Chrono/Tests/TestSetup.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using System.Net.Http; + +namespace Tests; + +[SetUpFixture] +public class TestSetup +{ + private Process? _serverProcess; + private const string ServerUrl = "http://localhost:5256"; + + [OneTimeSetUp] + public async Task BeforeAllTests() + { + Console.WriteLine("[DEBUG_LOG] Starting Server..."); + + // Find solution root + var currentDir = AppContext.BaseDirectory; + while (currentDir != null && !File.Exists(Path.Combine(currentDir, "Chrono.sln"))) + { + currentDir = Path.GetDirectoryName(currentDir); + } + + if (currentDir == null) + { + throw new Exception("Could not find Chrono.sln to determine project path."); + } + + var projectPath = Path.Combine(currentDir, "Server", "Server.csproj"); + + var startInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"run --project \"{projectPath}\" --no-build", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = currentDir + }; + + _serverProcess = Process.Start(startInfo); + + _serverProcess.OutputDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine($"[SERVER OUT] {e.Data}"); }; + _serverProcess.ErrorDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine($"[SERVER ERR] {e.Data}"); }; + _serverProcess.BeginOutputReadLine(); + _serverProcess.BeginErrorReadLine(); + + if (_serverProcess == null) + { + throw new Exception("Failed to start server process."); + } + + // Wait for the server to be ready + using var client = new HttpClient(); + var sw = Stopwatch.StartNew(); + var timeout = TimeSpan.FromSeconds(30); + bool isReady = false; + + while (sw.Elapsed < timeout) + { + try + { + var response = await client.GetAsync(ServerUrl); + if (response.IsSuccessStatusCode) + { + isReady = true; + break; + } + } + catch + { + // Wait and retry + await Task.Delay(1000); + } + } + + if (!isReady) + { + _serverProcess.Kill(true); + throw new Exception($"Server did not start within {timeout.TotalSeconds} seconds."); + } + + Console.WriteLine("[DEBUG_LOG] Server is ready."); + } + + [OneTimeTearDown] + public void AfterAllTests() + { + if (_serverProcess != null && !_serverProcess.HasExited) + { + Console.WriteLine("[DEBUG_LOG] Stopping Server..."); + _serverProcess.Kill(true); + _serverProcess.Dispose(); + } + } +}