11===============================================================================
22PROJECT EXPORT
3- Generated: Mon Jan 26 10:01:00 PM EST 2026
3+ Generated: Tue Jan 27 10:36:14 AM EST 2026
44Project Path: /home/kushal/src/dotnet/MyBlog
55===============================================================================
66
@@ -52,17 +52,18 @@ DIRECTORY STRUCTURE:
5252│ │ │ ├── 0040.txt
5353│ │ │ ├── 0041.txt
5454│ │ │ ├── 0042.txt
55- │ │ │ └── 0043.txt
56- │ │ ├── chatgpt.md
57- │ │ ├── claude.md
55+ │ │ │ ├── 0043.txt
56+ │ │ │ ├── 0044.txt
57+ │ │ │ ├── 0045.txt
58+ │ │ │ ├── 0046.txt
59+ │ │ │ ├── 0047.txt
60+ │ │ │ ├── 0048.txt
61+ │ │ │ └── 0049.txt
5862│ │ ├── command.md
5963│ │ ├── dump.txt
60- │ │ ├── gemini.md
6164│ │ ├── instructions.md
6265│ │ ├── kush.runasp.net-WebDeploy.publishSettings
63- │ │ ├── playwright.md
64- │ │ ├── qwen.md
65- │ │ └── test.html
66+ │ │ └── playwright.md
6667│ └── E2E-TESTING.md
6768├── .github
6869│ └── workflows
@@ -3308,7 +3309,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
33083309================================================================================
33093310FILE: run-e2e.sh
33103311SIZE: 3.12 KB
3311- MODIFIED: 2026-01-26 09:56:37
3312+ MODIFIED: 2026-01-27 09:54:00
33123313================================================================================
33133314
33143315#!/bin/bash
@@ -4662,7 +4663,7 @@ public sealed class PlaywrightCollection : ICollectionFixture<PlaywrightFixture>
46624663================================================================================
46634664FILE: src/MyBlog.E2E/PlaywrightFixture.cs
46644665SIZE: 2.07 KB
4665- MODIFIED: 2026-01-26 09:53:17
4666+ MODIFIED: 2026-01-27 09:44:53
46664667================================================================================
46674668
46684669using Microsoft.Playwright;
@@ -4738,7 +4739,7 @@ public sealed class PlaywrightFixture : IAsyncLifetime
47384739================================================================================
47394740FILE: src/MyBlog.E2E/Tests/AboutPageTests.cs
47404741SIZE: 2.02 KB
4741- MODIFIED: 2026-01-26 09:54:00
4742+ MODIFIED: 2026-01-27 09:54:00
47424743================================================================================
47434744
47444745using Microsoft.Playwright;
@@ -4903,20 +4904,16 @@ public sealed class HomePageTests(PlaywrightFixture fixture)
49034904
49044905================================================================================
49054906FILE: src/MyBlog.E2E/Tests/LoginPageTests.cs
4906- SIZE: 4.78 KB
4907- MODIFIED: 2026-01-26 22:00:04
4907+ SIZE: 1.04 KB
4908+ MODIFIED: 2026-01-27 10:33:54
49084909================================================================================
49094910
4910- // /home/kushal/src/dotnet/MyBlog/src/MyBlog.E2E/Tests/LoginPageTests.cs
4911+ using System.Text.RegularExpressions;
49114912using Microsoft.Playwright;
49124913using Xunit;
49134914
49144915namespace MyBlog.E2E.Tests;
49154916
4916- /// <summary>
4917- /// E2E tests for authentication (Epic 1: Authentication).
4918- /// The login page uses a standard HTML form that posts to /login minimal API endpoint.
4919- /// </summary>
49204917[Collection(PlaywrightCollection.Name)]
49214918public sealed class LoginPageTests(PlaywrightFixture fixture)
49224919{
@@ -4926,7 +4923,6 @@ public sealed class LoginPageTests(PlaywrightFixture fixture)
49264923 public async Task LoginPage_LoadsSuccessfully()
49274924 {
49284925 var page = await _fixture.CreatePageAsync();
4929-
49304926 var response = await page.GotoAsync("/login");
49314927
49324928 Assert.NotNull(response);
@@ -4937,112 +4933,12 @@ public sealed class LoginPageTests(PlaywrightFixture fixture)
49374933 public async Task LoginPage_DisplaysLoginForm()
49384934 {
49394935 var page = await _fixture.CreatePageAsync();
4940-
4941- await page.GotoAsync("/login");
4942-
4943- var usernameInput = page.Locator("input#username, input[name='username']");
4944- var passwordInput = page.Locator("input#password, input[name='password']");
4945- var submitButton = page.Locator("button[type='submit']");
4946-
4947- await Assertions.Expect(usernameInput).ToBeVisibleAsync();
4948- await Assertions.Expect(passwordInput).ToBeVisibleAsync();
4949- await Assertions.Expect(submitButton).ToBeVisibleAsync();
4950- }
4951-
4952- [Fact]
4953- public async Task LoginPage_WithInvalidCredentials_ShowsError()
4954- {
4955- var page = await _fixture.CreatePageAsync();
4956- await page.GotoAsync("/login");
4957-
4958- await page.FillAsync("input[name='username']", "invalid");
4959- await page.FillAsync("input[name='password']", "invalid");
4960-
4961- // Submit and wait for the error message to appear
4962- await page.ClickAsync("button[type='submit']");
4963-
4964- // Target the actual class used in Login.razor
4965- var errorLocator = page.Locator(".error-message");
4966- await Assertions.Expect(errorLocator).ToBeVisibleAsync();
4967- }
4968-
4969- [Obsolete]
4970- [Fact]
4971- public async Task LoginPage_WithValidCredentials_RedirectsToAdmin()
4972- {
4973- var page = await _fixture.CreatePageAsync();
4974- await page.GotoAsync("/login");
4975-
4976- await page.FillAsync("input[name='username']", "admin");
4977- await page.FillAsync("input[name='password']", "ChangeMe123!");
4978-
4979- // Use RunAndWaitForNavigationAsync to capture the redirect to /admin
4980- await page.RunAndWaitForNavigationAsync(async () =>
4981- {
4982- await page.ClickAsync("button[type='submit']");
4983- }, new() { UrlString = "**/admin", WaitUntil = WaitUntilState.DOMContentLoaded });
4984-
4985- Assert.Contains("/admin", page.Url);
4986- }
4987-
4988- [Fact]
4989- public async Task LoginPage_AfterLogin_ShowsLogoutButton()
4990- {
4991- var page = await _fixture.CreatePageAsync();
4992-
49934936 await page.GotoAsync("/login");
49944937
4995- // Login first
4996- await page.FillAsync("input[name='username'], input#username", "admin");
4997- await page.FillAsync("input[name='password'], input#password", "ChangeMe123!");
4998- await page.ClickAsync("button[type='submit'], input[type='submit']");
4999-
5000- await page.WaitForURLAsync("**/admin**", new PageWaitForURLOptions { Timeout = 60000 });
5001-
5002- // Wait for DOM content to be ready (avoiding NetworkIdle due to SignalR)
5003- await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
5004-
5005- // Check for logout element
5006- var logoutButton = page.Locator(
5007- "text=/logout/i, text=/sign out/i, button:has-text('Logout'), " +
5008- "[href='/logout'], form[action*='logout']"
5009- ).First;
5010-
5011- await Assertions.Expect(logoutButton).ToBeVisibleAsync();
5012- }
5013-
5014- [Fact]
5015- public async Task LoginPage_AfterLogin_ShowsLogoutButton_Updated_by_Qwen()
5016- {
5017- var page = await _fixture.CreatePageAsync();
5018- await page.GotoAsync("/login");
5019-
5020- // Login first
5021- await page.FillAsync("input[name='username'], input#username", "admin");
5022- await page.FillAsync("input[name='password'], input#password", "ChangeMe123!");
5023- await page.ClickAsync("button[type='submit'], input[type='submit']");
5024-
5025- try
5026- {
5027- // Wait for navigation to complete
5028- await page.WaitForURLAsync("**/admin**", new PageWaitForURLOptions { Timeout = 90000 });
5029- }
5030- catch (TimeoutException ex)
5031- {
5032- Console.WriteLine("Test timed out waiting for URL: " + ex.Message);
5033- throw;
5034- }
5035-
5036- // Wait for DOM to be ready
5037- await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
5038-
5039- // Check for logout element
5040- var logoutButton = page.Locator(
5041- "text=/logout/i, text=/sign out/i, button:has-text('Logout'), " +
5042- "[href='/logout'], form[action*='logout']"
5043- ).First;
5044-
5045- await Assertions.Expect(logoutButton).ToBeVisibleAsync();
4938+ // Use Web Assertions to wait for visibility
4939+ await Assertions.Expect(page.Locator("input[name='username']")).ToBeVisibleAsync();
4940+ await Assertions.Expect(page.Locator("input[name='password']")).ToBeVisibleAsync();
4941+ await Assertions.Expect(page.Locator("button[type='submit']")).ToBeVisibleAsync();
50464942 }
50474943}
50484944
@@ -5821,7 +5717,7 @@ public static class ServiceCollectionExtensions
58215717================================================================================
58225718FILE: src/MyBlog.Infrastructure/Services/AuthService.cs
58235719SIZE: 3.21 KB
5824- MODIFIED: 2025-12-28 11:33:37
5720+ MODIFIED: 2026-01-27 09:54:00
58255721================================================================================
58265722
58275723using Microsoft.Extensions.Configuration;
@@ -12239,7 +12135,7 @@ MODIFIED: 2026-01-26 05:45:13
1223912135================================================================================
1224012136FILE: src/MyBlog.Web/Dockerfile
1224112137SIZE: 1.37 KB
12242- MODIFIED: 2026-01-26 11:06:02
12138+ MODIFIED: 2026-01-27 09:54:00
1224312139================================================================================
1224412140
1224512141# Build stage
@@ -12570,7 +12466,7 @@ MODIFIED: 2026-01-16 20:29:40
1257012466================================================================================
1257112467FILE: src/MyBlog.Web/Program.cs
1257212468SIZE: 5.35 KB
12573- MODIFIED: 2026-01-26 20:32:10
12469+ MODIFIED: 2026-01-27 09:54:00
1257412470================================================================================
1257512471
1257612472using System.Security.Claims;
@@ -13802,7 +13698,7 @@ window.themeManager = {
1380213698
1380313699
1380413700===============================================================================
13805- EXPORT COMPLETED: Mon Jan 26 10:01:01 PM EST 2026
13701+ EXPORT COMPLETED: Tue Jan 27 10:36:15 AM EST 2026
1380613702Total Files Found: 115
1380713703Files Exported: 115
1380813704Files Skipped: 0 (binary or large files)
0 commit comments