diff --git a/NEW/Directory.Packages.props b/NEW/Directory.Packages.props
new file mode 100644
index 0000000..17b13b3
--- /dev/null
+++ b/NEW/Directory.Packages.props
@@ -0,0 +1,78 @@
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NEW/JdeScoping.slnx b/NEW/JdeScoping.slnx
index 6c0e274..3a8b38c 100644
--- a/NEW/JdeScoping.slnx
+++ b/NEW/JdeScoping.slnx
@@ -28,6 +28,7 @@
+
diff --git a/NEW/src/JdeScoping.Api/JdeScoping.Api.csproj b/NEW/src/JdeScoping.Api/JdeScoping.Api.csproj
index 5b9b0c9..186ebad 100644
--- a/NEW/src/JdeScoping.Api/JdeScoping.Api.csproj
+++ b/NEW/src/JdeScoping.Api/JdeScoping.Api.csproj
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/NEW/src/JdeScoping.Client/JdeScoping.Client.csproj b/NEW/src/JdeScoping.Client/JdeScoping.Client.csproj
index 2f3d35d..654ed9b 100644
--- a/NEW/src/JdeScoping.Client/JdeScoping.Client.csproj
+++ b/NEW/src/JdeScoping.Client/JdeScoping.Client.csproj
@@ -8,12 +8,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.Core/JdeScoping.Core.csproj b/NEW/src/JdeScoping.Core/JdeScoping.Core.csproj
index f5d8d9a..0b77c77 100644
--- a/NEW/src/JdeScoping.Core/JdeScoping.Core.csproj
+++ b/NEW/src/JdeScoping.Core/JdeScoping.Core.csproj
@@ -7,15 +7,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.DataAccess/JdeScoping.DataAccess.csproj b/NEW/src/JdeScoping.DataAccess/JdeScoping.DataAccess.csproj
index b2bfa0e..f81bc7d 100644
--- a/NEW/src/JdeScoping.DataAccess/JdeScoping.DataAccess.csproj
+++ b/NEW/src/JdeScoping.DataAccess/JdeScoping.DataAccess.csproj
@@ -7,17 +7,17 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.DataSync.Dev/JdeScoping.DataSync.Dev.csproj b/NEW/src/JdeScoping.DataSync.Dev/JdeScoping.DataSync.Dev.csproj
index dce71c4..0ce74db 100644
--- a/NEW/src/JdeScoping.DataSync.Dev/JdeScoping.DataSync.Dev.csproj
+++ b/NEW/src/JdeScoping.DataSync.Dev/JdeScoping.DataSync.Dev.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj b/NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj
index a3f71f8..8adac7c 100644
--- a/NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj
+++ b/NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj
@@ -17,14 +17,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.Database/JdeScoping.Database.csproj b/NEW/src/JdeScoping.Database/JdeScoping.Database.csproj
index 46bd5b5..122336b 100644
--- a/NEW/src/JdeScoping.Database/JdeScoping.Database.csproj
+++ b/NEW/src/JdeScoping.Database/JdeScoping.Database.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/NEW/src/JdeScoping.ExcelIO/JdeScoping.ExcelIO.csproj b/NEW/src/JdeScoping.ExcelIO/JdeScoping.ExcelIO.csproj
index 3655446..717802a 100644
--- a/NEW/src/JdeScoping.ExcelIO/JdeScoping.ExcelIO.csproj
+++ b/NEW/src/JdeScoping.ExcelIO/JdeScoping.ExcelIO.csproj
@@ -7,12 +7,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.Host/JdeScoping.Host.csproj b/NEW/src/JdeScoping.Host/JdeScoping.Host.csproj
index 23b0dcb..d657d6d 100644
--- a/NEW/src/JdeScoping.Host/JdeScoping.Host.csproj
+++ b/NEW/src/JdeScoping.Host/JdeScoping.Host.csproj
@@ -12,12 +12,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/src/JdeScoping.Host/Program.cs b/NEW/src/JdeScoping.Host/Program.cs
index 9ae4d02..a0a5ade 100644
--- a/NEW/src/JdeScoping.Host/Program.cs
+++ b/NEW/src/JdeScoping.Host/Program.cs
@@ -62,7 +62,7 @@ try
.AddDataAccess(builder.Configuration) // 1. Database access + search processing
.AddInfrastructure(builder.Configuration) // 2. Infrastructure (JDE/CMS/Auth)
.AddDataSyncServices(builder.Configuration) // 3. Data sync background service
- .AddExcelIO(builder.Configuration) // 4. Result export
+ .AddExcelIO(builder.Configuration) // 4. Result export
.AddWebApi(builder.Configuration); // 5. Web API (controllers, auth, SignalR)
var app = builder.Build();
diff --git a/NEW/src/JdeScoping.Infrastructure/JdeScoping.Infrastructure.csproj b/NEW/src/JdeScoping.Infrastructure/JdeScoping.Infrastructure.csproj
index f59f613..9a2d10f 100644
--- a/NEW/src/JdeScoping.Infrastructure/JdeScoping.Infrastructure.csproj
+++ b/NEW/src/JdeScoping.Infrastructure/JdeScoping.Infrastructure.csproj
@@ -7,14 +7,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/NEW/src/Utils/JdeScoping.ConfigManager.Cli/JdeScoping.ConfigManager.Cli.csproj b/NEW/src/Utils/JdeScoping.ConfigManager.Cli/JdeScoping.ConfigManager.Cli.csproj
index a7ab85b..25cad4b 100644
--- a/NEW/src/Utils/JdeScoping.ConfigManager.Cli/JdeScoping.ConfigManager.Cli.csproj
+++ b/NEW/src/Utils/JdeScoping.ConfigManager.Cli/JdeScoping.ConfigManager.Cli.csproj
@@ -8,12 +8,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/src/Utils/JdeScoping.ConfigManager.Core/JdeScoping.ConfigManager.Core.csproj b/NEW/src/Utils/JdeScoping.ConfigManager.Core/JdeScoping.ConfigManager.Core.csproj
index c953f94..2442538 100644
--- a/NEW/src/Utils/JdeScoping.ConfigManager.Core/JdeScoping.ConfigManager.Core.csproj
+++ b/NEW/src/Utils/JdeScoping.ConfigManager.Core/JdeScoping.ConfigManager.Core.csproj
@@ -6,12 +6,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/src/Utils/JdeScoping.ConfigManager.Ui/JdeScoping.ConfigManager.Ui.csproj b/NEW/src/Utils/JdeScoping.ConfigManager.Ui/JdeScoping.ConfigManager.Ui.csproj
index c051560..4060c33 100644
--- a/NEW/src/Utils/JdeScoping.ConfigManager.Ui/JdeScoping.ConfigManager.Ui.csproj
+++ b/NEW/src/Utils/JdeScoping.ConfigManager.Ui/JdeScoping.ConfigManager.Ui.csproj
@@ -12,18 +12,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Api.IntegrationTests/JdeScoping.Api.IntegrationTests.csproj b/NEW/tests/JdeScoping.Api.IntegrationTests/JdeScoping.Api.IntegrationTests.csproj
index 2d81f4d..e8ae092 100644
--- a/NEW/tests/JdeScoping.Api.IntegrationTests/JdeScoping.Api.IntegrationTests.csproj
+++ b/NEW/tests/JdeScoping.Api.IntegrationTests/JdeScoping.Api.IntegrationTests.csproj
@@ -16,14 +16,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/NEW/tests/JdeScoping.Api.Tests/JdeScoping.Api.Tests.csproj b/NEW/tests/JdeScoping.Api.Tests/JdeScoping.Api.Tests.csproj
index b964046..39d707f 100644
--- a/NEW/tests/JdeScoping.Api.Tests/JdeScoping.Api.Tests.csproj
+++ b/NEW/tests/JdeScoping.Api.Tests/JdeScoping.Api.Tests.csproj
@@ -13,12 +13,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/NEW/tests/JdeScoping.Client.Tests/JdeScoping.Client.Tests.csproj b/NEW/tests/JdeScoping.Client.Tests/JdeScoping.Client.Tests.csproj
index ae0028e..f6617c7 100644
--- a/NEW/tests/JdeScoping.Client.Tests/JdeScoping.Client.Tests.csproj
+++ b/NEW/tests/JdeScoping.Client.Tests/JdeScoping.Client.Tests.csproj
@@ -12,13 +12,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Core.Tests/JdeScoping.Core.Tests.csproj b/NEW/tests/JdeScoping.Core.Tests/JdeScoping.Core.Tests.csproj
index 48dfa4b..4555652 100644
--- a/NEW/tests/JdeScoping.Core.Tests/JdeScoping.Core.Tests.csproj
+++ b/NEW/tests/JdeScoping.Core.Tests/JdeScoping.Core.Tests.csproj
@@ -8,12 +8,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.DataAccess.Tests/JdeScoping.DataAccess.Tests.csproj b/NEW/tests/JdeScoping.DataAccess.Tests/JdeScoping.DataAccess.Tests.csproj
index 348de2b..825b82d 100644
--- a/NEW/tests/JdeScoping.DataAccess.Tests/JdeScoping.DataAccess.Tests.csproj
+++ b/NEW/tests/JdeScoping.DataAccess.Tests/JdeScoping.DataAccess.Tests.csproj
@@ -9,17 +9,17 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
-
-
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/NEW/tests/JdeScoping.DataSync.Dev.Tests/JdeScoping.DataSync.Dev.Tests.csproj b/NEW/tests/JdeScoping.DataSync.Dev.Tests/JdeScoping.DataSync.Dev.Tests.csproj
index eba4d70..fb656a7 100644
--- a/NEW/tests/JdeScoping.DataSync.Dev.Tests/JdeScoping.DataSync.Dev.Tests.csproj
+++ b/NEW/tests/JdeScoping.DataSync.Dev.Tests/JdeScoping.DataSync.Dev.Tests.csproj
@@ -9,23 +9,23 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
-
-
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj b/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj
index 7dfd001..4fcf909 100644
--- a/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj
+++ b/NEW/tests/JdeScoping.DataSync.Tests/JdeScoping.DataSync.Tests.csproj
@@ -9,26 +9,26 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj b/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj
index eda758c..635e88b 100644
--- a/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj
+++ b/NEW/tests/JdeScoping.Database.Tests/JdeScoping.Database.Tests.csproj
@@ -9,14 +9,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.ExcelIO.Tests/JdeScoping.ExcelIO.Tests.csproj b/NEW/tests/JdeScoping.ExcelIO.Tests/JdeScoping.ExcelIO.Tests.csproj
index 3f2df8b..447d9bc 100644
--- a/NEW/tests/JdeScoping.ExcelIO.Tests/JdeScoping.ExcelIO.Tests.csproj
+++ b/NEW/tests/JdeScoping.ExcelIO.Tests/JdeScoping.ExcelIO.Tests.csproj
@@ -9,16 +9,16 @@
-
-
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/NEW/tests/JdeScoping.Host.Tests/JdeScoping.Host.Tests.csproj b/NEW/tests/JdeScoping.Host.Tests/JdeScoping.Host.Tests.csproj
index 3ea3ade..6602de0 100644
--- a/NEW/tests/JdeScoping.Host.Tests/JdeScoping.Host.Tests.csproj
+++ b/NEW/tests/JdeScoping.Host.Tests/JdeScoping.Host.Tests.csproj
@@ -8,12 +8,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Infrastructure.Tests/JdeScoping.Infrastructure.Tests.csproj b/NEW/tests/JdeScoping.Infrastructure.Tests/JdeScoping.Infrastructure.Tests.csproj
index b7ac024..c9b34fd 100644
--- a/NEW/tests/JdeScoping.Infrastructure.Tests/JdeScoping.Infrastructure.Tests.csproj
+++ b/NEW/tests/JdeScoping.Infrastructure.Tests/JdeScoping.Infrastructure.Tests.csproj
@@ -8,12 +8,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Ui.Tests/AuthApiSmokeTests.cs b/NEW/tests/JdeScoping.Ui.Tests/AuthApiSmokeTests.cs
new file mode 100644
index 0000000..9c4fca0
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/AuthApiSmokeTests.cs
@@ -0,0 +1,56 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.Json;
+using JdeScoping.Core.Models.Auth;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// API-level smoke tests for the authentication endpoint against the Docker host.
+/// Validates the RSA public-key exchange, encrypted login, and session cookie flow.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public class AuthApiSmokeTests
+{
+ ///
+ /// Verifies the full login flow: fetch public key, encrypt credentials, POST login, and confirm session via /me.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Create an HttpClient with a CookieContainer for session tracking.
+ /// - GET /api/auth/public-key and verify the PEM response.
+ /// - RSA-encrypt a test login payload using the returned public key.
+ /// - POST /api/auth/login with the encrypted payload and assert HTTP 200.
+ /// - GET /api/auth/me and assert HTTP 200 (session is authenticated).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task AuthApi_Login_WorksAgainstDockerHost()
+ {
+ var cookies = new CookieContainer();
+ using var handler = new HttpClientHandler { CookieContainer = cookies };
+ using var client = new HttpClient(handler) { BaseAddress = new Uri(UiTestSettings.BaseUrl) };
+
+ var key = await client.GetFromJsonAsync("api/auth/public-key");
+ Assert.NotNull(key);
+ Assert.Contains("BEGIN PUBLIC KEY", key!.PublicKeyPem);
+
+ string payload = JsonSerializer.Serialize(new LoginModel { Username = "testuser", Password = "testpass" });
+
+ using var rsa = RSA.Create();
+ rsa.ImportFromPem(key.PublicKeyPem);
+ byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(payload), RSAEncryptionPadding.OaepSHA256);
+
+ var login = await client.PostAsJsonAsync("api/auth/login",
+ new EncryptedLoginRequest(Convert.ToBase64String(encrypted)));
+ Assert.Equal(HttpStatusCode.OK, login.StatusCode);
+
+ var me = await client.GetAsync("api/auth/me");
+ Assert.Equal(HttpStatusCode.OK, me.StatusCode);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/ComponentLotSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/ComponentLotSearchTests.cs
new file mode 100644
index 0000000..5f6b8d0
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/ComponentLotSearchTests.cs
@@ -0,0 +1,39 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Component Lot" search type (TC-020).
+/// Validates search form interaction in smoke mode and full submission with workbook upload in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class ComponentLotSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Component Lot search form submits with an uploaded workbook filter (TC-020).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-020".
+ /// - Select the "Component Lot" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Upload "single_lot.xlsx" to the "Filter By Component Lot" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task ComponentLot_SubmitsWithUploadedWorkbook()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.ComponentLot,
+ "MIGRATED-TC-020",
+ uploads:
+ [
+ ("Filter By Component Lot", "single_lot.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/DataSyncPageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/DataSyncPageTests.cs
new file mode 100644
index 0000000..b0b8786
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/DataSyncPageTests.cs
@@ -0,0 +1,56 @@
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI smoke tests for the Data Sync Requests page.
+/// Validates that the page loads and shows action buttons or redirects to search.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class DataSyncPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the Data Sync page loads at /data-sync/requests and displays action buttons.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the Data Sync Requests page.
+ /// - Assert the URL ends with /data-sync/requests or /search (redirect).
+ /// - If on the data sync page, assert "Data Sync Requests" heading is visible.
+ /// - Assert "New Request" or "Reload Pipelines" button is visible.
+ /// - If redirected, assert "Search Details" is visible.
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task DataSync_Loads()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToDataSyncAsync(page);
+ string url = page.Url;
+ bool onDataSync = url.EndsWith("/data-sync/requests", StringComparison.OrdinalIgnoreCase);
+ bool redirectedToSearch = url.EndsWith("/search", StringComparison.OrdinalIgnoreCase);
+ Assert.True(onDataSync || redirectedToSearch, $"Unexpected URL: {url}");
+
+ if (onDataSync)
+ {
+ await Assertions.Expect(page.GetByText("Data Sync Requests"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ var newRequestButton =
+ page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "New Request" });
+ var reloadButton =
+ page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "Reload Pipelines" });
+ bool hasAnyControl = await newRequestButton.IsVisibleAsync() || await reloadButton.IsVisibleAsync();
+ Assert.True(hasAnyControl, "Expected Data Sync action buttons to be visible.");
+ }
+ else
+ {
+ await Assertions.Expect(page.GetByText("Search Details"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/GlobalUsings.cs b/NEW/tests/JdeScoping.Ui.Tests/GlobalUsings.cs
new file mode 100644
index 0000000..e64e483
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/GlobalUsings.cs
@@ -0,0 +1,2 @@
+global using Xunit;
+global using Microsoft.Playwright;
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiAuthHelper.cs b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiAuthHelper.cs
new file mode 100644
index 0000000..49292b4
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiAuthHelper.cs
@@ -0,0 +1,45 @@
+namespace JdeScoping.Ui.Tests.Helpers;
+
+internal static class UiAuthHelper
+{
+ public static async Task LoginAsync(IPage page, string username = "testuser", string password = "testpass")
+ {
+ var loginForm = page.GetByText("Authentication Required");
+ bool formVisible = await loginForm.IsVisibleAsync();
+ if (!formVisible) return;
+
+ await page.Locator("input[name='Username']").FillAsync(username);
+ await page.Locator("input[name='Password']").FillAsync(password);
+ await page.Locator("button[type='submit']:has-text('LOGIN')").ClickAsync();
+
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await page.WaitForTimeoutAsync(3_000);
+ await page.GotoAsync("/search");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+
+ if (await loginForm.IsVisibleAsync())
+ {
+ var notifications = page.Locator(".rz-notification");
+ int count = await notifications.CountAsync();
+ var details = new List();
+ for (var i = 0; i < count; i++)
+ {
+ string text = (await notifications.Nth(i).InnerTextAsync()).Trim();
+ if (!string.IsNullOrWhiteSpace(text)) details.Add(text);
+ }
+
+ throw new InvalidOperationException(
+ $"Login did not complete. URL={page.Url}. Notifications={string.Join(" | ", details)}");
+ }
+ }
+
+ public static async Task LogoutAsync(IPage page)
+ {
+ var logoutButton = page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "Logout" });
+ if (await logoutButton.IsVisibleAsync())
+ {
+ await logoutButton.ClickAsync();
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiNavigationHelper.cs b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiNavigationHelper.cs
new file mode 100644
index 0000000..c0a4095
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiNavigationHelper.cs
@@ -0,0 +1,102 @@
+namespace JdeScoping.Ui.Tests.Helpers;
+
+internal static class UiNavigationHelper
+{
+ public static async Task NavigateToSearchPageAsync(IPage page)
+ {
+ await page.GotoAsync("/search");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await UiAuthHelper.LoginAsync(page);
+ await EnsureAuthenticatedOrThrowAsync(page);
+ await WaitForBlazorReadyAsync(page);
+ }
+
+ public static async Task NavigateToSearchesDashboardAsync(IPage page)
+ {
+ await page.GotoAsync("/searches");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await UiAuthHelper.LoginAsync(page);
+ await EnsureAuthenticatedOrThrowAsync(page);
+ await WaitForBlazorReadyAsync(page);
+ }
+
+ public static async Task NavigateToQueueAsync(IPage page)
+ {
+ await page.GotoAsync("/search/queue");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await UiAuthHelper.LoginAsync(page);
+ await EnsureAuthenticatedOrThrowAsync(page);
+ await WaitForBlazorReadyAsync(page);
+ }
+
+ public static async Task NavigateToRefreshStatusAsync(IPage page)
+ {
+ await page.GotoAsync("/refresh-status");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await UiAuthHelper.LoginAsync(page);
+ await EnsureAuthenticatedOrThrowAsync(page);
+ await WaitForBlazorReadyAsync(page);
+ }
+
+ public static async Task NavigateToDataSyncAsync(IPage page)
+ {
+ await page.GotoAsync("/data-sync/requests");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await UiAuthHelper.LoginAsync(page);
+ await EnsureAuthenticatedOrThrowAsync(page);
+ await WaitForBlazorReadyAsync(page);
+ }
+
+ public static async Task NavigateToLoginAsync(IPage page)
+ {
+ await page.GotoAsync("/login");
+ await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ }
+
+ private static async Task WaitForBlazorReadyAsync(IPage page)
+ {
+ var timeoutMs = 15_000;
+ try
+ {
+ await page.Locator(".rz-dropdown").First.WaitForAsync(new LocatorWaitForOptions
+ {
+ State = WaitForSelectorState.Visible,
+ Timeout = timeoutMs
+ });
+ return;
+ }
+ catch (PlaywrightException)
+ {
+ // Try additional readiness markers.
+ }
+
+ try
+ {
+ await page.Locator(".rz-data-grid").First.WaitForAsync(new LocatorWaitForOptions
+ {
+ State = WaitForSelectorState.Visible,
+ Timeout = timeoutMs
+ });
+ return;
+ }
+ catch (PlaywrightException)
+ {
+ // Try text marker as final fallback.
+ }
+
+ await page.GetByText("Search Details").First.WaitForAsync(new LocatorWaitForOptions
+ {
+ State = WaitForSelectorState.Visible,
+ Timeout = timeoutMs
+ });
+ await page.WaitForTimeoutAsync(1_000);
+ }
+
+ private static async Task EnsureAuthenticatedOrThrowAsync(IPage page)
+ {
+ var meResponse = await page.Context.APIRequest.GetAsync("/api/auth/me");
+ if (meResponse.Status != 200)
+ throw new InvalidOperationException(
+ $"UI test host did not establish authenticated session after login. /api/auth/me status={meResponse.Status}.");
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiSearchFormHelper.cs b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiSearchFormHelper.cs
new file mode 100644
index 0000000..3255fc1
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Helpers/UiSearchFormHelper.cs
@@ -0,0 +1,63 @@
+namespace JdeScoping.Ui.Tests.Helpers;
+
+internal static class UiSearchFormHelper
+{
+ public static async Task SelectSearchTypeAsync(IPage page, string searchType)
+ {
+ await page.Locator(".rz-dropdown").First.ClickAsync();
+ await page.GetByRole(AriaRole.Option, new PageGetByRoleOptions { Name = searchType, Exact = true })
+ .ClickAsync();
+ await page.WaitForTimeoutAsync(500);
+ }
+
+ public static Task EnterSearchNameAsync(IPage page, string name)
+ {
+ return page.Locator("input[placeholder=' ']").First.FillAsync(name);
+ }
+
+ public static async Task SetDateRangeAsync(IPage page, string minimumMmDdYyyy, string maximumMmDdYyyy)
+ {
+ await page.Locator("input[name='MinimumDt']").FillAsync(minimumMmDdYyyy);
+ await page.Locator("input[name='MaximumDt']").FillAsync(maximumMmDdYyyy);
+ }
+
+ public static async Task AddAutocompleteItemAsync(IPage page, string panelHeader, string value)
+ {
+ var panel = page.Locator($".rz-card:has-text('{panelHeader}')");
+ await panel.Locator(".rz-autocomplete input").First.FillAsync(value);
+ await page.WaitForTimeoutAsync(500);
+
+ var listItem = page.Locator(".rz-autocomplete-list .rz-autocomplete-list-item").First;
+ if (await listItem.IsVisibleAsync()) await listItem.ClickAsync();
+
+ await panel.GetByRole(AriaRole.Button, new LocatorGetByRoleOptions { Name = "Add" }).ClickAsync();
+ await page.WaitForTimeoutAsync(250);
+ }
+
+ public static async Task UploadFileAsync(IPage page, string panelHeader, string filePath)
+ {
+ var panel = page.Locator($".rz-card:has-text('{panelHeader}')");
+ await panel.Locator("input[type='file']").First.SetInputFilesAsync(filePath);
+ await page.WaitForTimeoutAsync(1_500);
+ }
+
+ public static async Task SubmitSearchAsync(IPage page)
+ {
+ await page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "Submit" }).First.ClickAsync();
+ await page.GetByText("Confirm Submit").WaitForAsync(new LocatorWaitForOptions
+ {
+ State = WaitForSelectorState.Visible,
+ Timeout = 10_000
+ });
+ await page.Locator(".rz-dialog-wrapper button")
+ .GetByText("Submit", new LocatorGetByTextOptions { Exact = true }).ClickAsync();
+ await page.WaitForTimeoutAsync(1_000);
+ }
+
+ public static async Task AssertNoErrorNotificationAsync(IPage page)
+ {
+ var error = page.Locator(".rz-notification-error");
+ await Assertions.Expect(error).Not
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 5_000 });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj b/NEW/tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj
new file mode 100644
index 0000000..21ddbca
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NEW/tests/JdeScoping.Ui.Tests/LoginPageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/LoginPageTests.cs
new file mode 100644
index 0000000..59ff8a8
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/LoginPageTests.cs
@@ -0,0 +1,49 @@
+using System.Text.RegularExpressions;
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the Login page.
+/// Validates that the login form renders, credentials are accepted, and logout revokes the session.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class LoginPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the login page renders, credentials authenticate the user, and logout revokes the session.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the login page.
+ /// - Assert the page title contains "Login - JDE Scoping Tool".
+ /// - Submit test credentials via UiAuthHelper.LoginAsync.
+ /// - Assert the user sees the Logout button or remains on the login view.
+ /// - Invoke UiAuthHelper.LogoutAsync.
+ /// - GET /api/auth/me and assert HTTP 401 (session revoked).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task LoginPage_AllowsLoginAndLogout()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToLoginAsync(page);
+ await Assertions.Expect(page).ToHaveTitleAsync(new Regex("Login - JDE Scoping Tool"));
+
+ await UiAuthHelper.LoginAsync(page);
+ var loggedOutView = page.GetByText("Authentication Required");
+ var logoutButton = page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "Logout" });
+ bool authenticated = await logoutButton.IsVisibleAsync();
+ bool stillOnLogin = await loggedOutView.IsVisibleAsync();
+ Assert.True(authenticated || stillOnLogin);
+
+ await UiAuthHelper.LogoutAsync(page);
+ var meAfterLogout = await page.Context.APIRequest.GetAsync("/api/auth/me");
+ Assert.Equal(401, meAfterLogout.Status);
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/README.md b/NEW/tests/JdeScoping.Ui.Tests/README.md
new file mode 100644
index 0000000..d7681a2
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/README.md
@@ -0,0 +1,29 @@
+# JdeScoping.Ui.Tests
+
+Playwright-for-.NET UI tests migrated from the legacy TypeScript Playwright suite.
+
+## Preconditions
+
+- Docker host container is already running via `/Users/dohertj2/Desktop/JdeScopingTool/NEW/deploy/docker/deploy-host.sh`
+- App reachable at `http://localhost:5294` (or set `JDESCOPING_UI_BASE_URL`)
+
+## First-time setup
+
+```bash
+cd /Users/dohertj2/Desktop/JdeScopingTool/NEW/tests/JdeScoping.Ui.Tests
+dotnet build
+pwsh bin/Debug/net10.0/playwright.ps1 install chromium
+```
+
+## Run tests
+
+```bash
+cd /Users/dohertj2/Desktop/JdeScopingTool/NEW
+dotnet test tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj --filter "Category=RequiresDockerHost"
+```
+
+Run headed mode:
+
+```bash
+JDESCOPING_UI_HEADED=true dotnet test tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj
+```
diff --git a/NEW/tests/JdeScoping.Ui.Tests/RefreshStatusPageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/RefreshStatusPageTests.cs
new file mode 100644
index 0000000..db4f0d9
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/RefreshStatusPageTests.cs
@@ -0,0 +1,45 @@
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI smoke tests for the Cache Refresh Status page.
+/// Validates that the page loads and shows the "Cache Refresh Status" heading or redirects to search.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class RefreshStatusPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the Refresh Status page loads at /refresh-status and displays the expected heading.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the Refresh Status page.
+ /// - Assert the URL ends with /refresh-status or /search (redirect).
+ /// - If on the refresh page, assert "Cache Refresh Status" heading is visible.
+ /// - If redirected, assert "Search Details" is visible.
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task RefreshStatus_Loads()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToRefreshStatusAsync(page);
+ string url = page.Url;
+ bool onRefreshStatus = url.EndsWith("/refresh-status", StringComparison.OrdinalIgnoreCase);
+ bool redirectedToSearch = url.EndsWith("/search", StringComparison.OrdinalIgnoreCase);
+ Assert.True(onRefreshStatus || redirectedToSearch, $"Unexpected URL: {url}");
+
+ if (onRefreshStatus)
+ await Assertions.Expect(page.GetByText("Cache Refresh Status"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ else
+ await Assertions.Expect(page.GetByText("Search Details"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/SearchPageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/SearchPageTests.cs
new file mode 100644
index 0000000..5666fa6
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/SearchPageTests.cs
@@ -0,0 +1,38 @@
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI smoke tests for the Search page.
+/// Validates that the search form loads with primary controls visible and no error notifications.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class SearchPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the search page loads and displays the "Search Details" heading, Submit button, and no errors.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the search page.
+ /// - Assert the "Search Details" text is visible.
+ /// - Assert the Submit button is visible.
+ /// - Assert no error notification is present.
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task SearchPage_LoadsAndShowsPrimaryControls()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToSearchPageAsync(page);
+ await Assertions.Expect(page.GetByText("Search Details")).ToBeVisibleAsync();
+ await Assertions.Expect(page.GetByRole(AriaRole.Button, new PageGetByRoleOptions { Name = "Submit" }).First)
+ .ToBeVisibleAsync();
+ await UiSearchFormHelper.AssertNoErrorNotificationAsync(page);
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/SearchQueuePageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/SearchQueuePageTests.cs
new file mode 100644
index 0000000..09044bc
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/SearchQueuePageTests.cs
@@ -0,0 +1,53 @@
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI smoke tests for the Search Queue page.
+/// Validates that the queue page loads and shows a data grid, alert, or loading indicator.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class SearchQueuePageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the Search Queue page loads at /search/queue and displays queue content or a redirect.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the Search Queue page.
+ /// - Assert the URL ends with /search/queue or /search (redirect).
+ /// - If on the queue page, assert "Search Queue" heading and grid/alert/loading indicator are visible.
+ /// - If redirected, assert "Search Details" is visible.
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task SearchQueue_Loads()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToQueueAsync(page);
+ string url = page.Url;
+ bool onQueue = url.EndsWith("/search/queue", StringComparison.OrdinalIgnoreCase);
+ bool redirectedToSearch = url.EndsWith("/search", StringComparison.OrdinalIgnoreCase);
+ Assert.True(onQueue || redirectedToSearch, $"Unexpected URL: {url}");
+
+ if (onQueue)
+ {
+ await Assertions.Expect(page.GetByText("Search Queue"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ bool hasGrid = await page.Locator(".rz-data-grid").First.IsVisibleAsync();
+ bool hasAlert = await page.Locator(".rz-alert").First.IsVisibleAsync();
+ bool hasLoading = await page.GetByText("Loading queue").IsVisibleAsync();
+ Assert.True(hasGrid || hasAlert || hasLoading, "Expected queue grid, alert, or loading indicator.");
+ }
+ else
+ {
+ await Assertions.Expect(page.GetByText("Search Details"))
+ .ToBeVisibleAsync(new LocatorAssertionsToBeVisibleOptions { Timeout = 15_000 });
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/SearchesDashboardPageTests.cs b/NEW/tests/JdeScoping.Ui.Tests/SearchesDashboardPageTests.cs
new file mode 100644
index 0000000..a60d5c1
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/SearchesDashboardPageTests.cs
@@ -0,0 +1,44 @@
+using JdeScoping.Ui.Tests.Helpers;
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI smoke tests for the Searches Dashboard page.
+/// Validates that the dashboard loads at the expected URL and shows a heading or data grid.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class SearchesDashboardPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ ///
+ /// Verifies the Searches Dashboard page loads at /searches and displays a heading or data grid.
+ ///
+ ///
+ /// Steps:
+ ///
+ /// - Navigate to the Searches Dashboard page.
+ /// - Assert the URL ends with /searches, /search, or /.
+ /// - Assert that "Searches Dashboard", "Search Details", or the Radzen data grid is visible.
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public async Task SearchesDashboard_Loads()
+ {
+ await RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToSearchesDashboardAsync(page);
+ string url = page.Url;
+ Assert.True(
+ url.EndsWith("/searches", StringComparison.OrdinalIgnoreCase) ||
+ url.EndsWith("/search", StringComparison.OrdinalIgnoreCase) ||
+ url.EndsWith("/", StringComparison.OrdinalIgnoreCase),
+ $"Unexpected URL: {url}");
+
+ bool hasSearchesHeading = await page.GetByText("Searches Dashboard").IsVisibleAsync();
+ bool hasSearchDetails = await page.GetByText("Search Details").IsVisibleAsync();
+ bool hasGrid = await page.Locator(".rz-data-grid").First.IsVisibleAsync();
+ Assert.True(hasSearchesHeading || hasSearchDetails || hasGrid);
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/PlaywrightFixture.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/PlaywrightFixture.cs
new file mode 100644
index 0000000..16f5948
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/PlaywrightFixture.cs
@@ -0,0 +1,26 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+public sealed class PlaywrightFixture : IAsyncLifetime
+{
+ private IBrowser? _browser;
+ private IPlaywright? _playwright;
+
+ public IBrowser Browser => _browser ?? throw new InvalidOperationException("Browser is not initialized.");
+
+ public async Task InitializeAsync()
+ {
+ _playwright = await Playwright.CreateAsync();
+ _browser = await _playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
+ {
+ Headless = UiTestSettings.Headless,
+ Args = ["--no-sandbox", "--disable-dev-shm-usage"]
+ });
+ }
+
+ public async Task DisposeAsync()
+ {
+ if (_browser is not null) await _browser.CloseAsync();
+
+ _playwright?.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/SearchFlowTestBase.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/SearchFlowTestBase.cs
new file mode 100644
index 0000000..238d4ad
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/SearchFlowTestBase.cs
@@ -0,0 +1,46 @@
+using JdeScoping.Ui.Tests.Helpers;
+
+namespace JdeScoping.Ui.Tests.Support;
+
+public abstract class SearchFlowTestBase(PlaywrightFixture fixture) : UiTestBase(fixture)
+{
+ private static bool StrictMode =>
+ string.Equals(Environment.GetEnvironmentVariable("JDESCOPING_UI_STRICT"), "true",
+ StringComparison.OrdinalIgnoreCase);
+
+ protected Task RunSearchSubmissionAsync(
+ string searchType,
+ string testName,
+ string? minDate = null,
+ string? maxDate = null,
+ (string PanelHeader, string Value)[]? autocompleteItems = null,
+ (string PanelHeader, string FileName)[]? uploads = null)
+ {
+ return RunAsync(async page =>
+ {
+ await UiNavigationHelper.NavigateToSearchPageAsync(page);
+ await UiSearchFormHelper.EnterSearchNameAsync(page, testName);
+ await UiSearchFormHelper.SelectSearchTypeAsync(page, searchType);
+ await Assertions.Expect(page.Locator(".rz-dropdown-label").First).ToContainTextAsync(searchType);
+
+ if (!StrictMode)
+ // Default mode is smoke-only against local docker where source systems can be offline.
+ return;
+
+ if (!string.IsNullOrWhiteSpace(minDate) && !string.IsNullOrWhiteSpace(maxDate))
+ await UiSearchFormHelper.SetDateRangeAsync(page, minDate, maxDate);
+
+ if (autocompleteItems is not null)
+ foreach (var item in autocompleteItems)
+ await UiSearchFormHelper.AddAutocompleteItemAsync(page, item.PanelHeader, item.Value);
+
+ if (uploads is not null)
+ foreach (var upload in uploads)
+ await UiSearchFormHelper.UploadFileAsync(page, upload.PanelHeader,
+ TestDataPaths.Get(upload.FileName));
+
+ await UiSearchFormHelper.SubmitSearchAsync(page);
+ await UiSearchFormHelper.AssertNoErrorNotificationAsync(page);
+ });
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/TestDataPaths.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/TestDataPaths.cs
new file mode 100644
index 0000000..2792581
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/TestDataPaths.cs
@@ -0,0 +1,9 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+internal static class TestDataPaths
+{
+ public static string Get(string fileName)
+ {
+ return Path.Combine(AppContext.BaseDirectory, "TestData", fileName);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/UiSearchTypes.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/UiSearchTypes.cs
new file mode 100644
index 0000000..5f6c4fe
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/UiSearchTypes.cs
@@ -0,0 +1,21 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+internal static class UiSearchTypes
+{
+ public const string WorkOrder = "Work Order";
+ public const string ComponentLot = "Component Lot";
+ public const string TimeSpanProfitCenter = "Time Span + Profit Center";
+ public const string TimeSpanWorkCenter = "Time Span + Work Center";
+ public const string TimeSpanOperator = "Time Span + Operator";
+ public const string TimeSpanPcItem = "Time Span + Profit Center + Item Number";
+ public const string TimeSpanPcPartOp = "Time Span + Profit Center + Item/Operation/MIS";
+ public const string TimeSpanPcWoPartOp = "Time Span + Profit Center + Work Order + Item/Operation/MIS";
+ public const string TimeSpanPcExtractMis = "Time Span + Profit Center + Extract MIS";
+ public const string TimeSpanWcItem = "Time Span + Work Center + Item Number";
+ public const string TimeSpanWcExtractMis = "Time Span + Work Center + Extract MIS";
+ public const string TimeSpanWcPartOp = "Time Span + Work Center + Item/Operation/MIS";
+ public const string TimeSpanWcWoPartOp = "Time Span + Work Center + Work Order + Item/Operation/MIS";
+ public const string TimeSpanItem = "Time Span + Item Number";
+ public const string TimeSpanWcOperator = "Time Span + Work Center + Operator";
+ public const string TimeSpanPcOperator = "Time Span + Profit Center + Operator";
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestBase.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestBase.cs
new file mode 100644
index 0000000..57455a1
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestBase.cs
@@ -0,0 +1,27 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+[Collection(UiTestCollection.Name)]
+public abstract class UiTestBase
+{
+ private readonly PlaywrightFixture _fixture;
+
+ protected UiTestBase(PlaywrightFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ protected async Task RunAsync(Func action)
+ {
+ await using var context = await _fixture.Browser.NewContextAsync(new BrowserNewContextOptions
+ {
+ BaseURL = UiTestSettings.BaseUrl,
+ IgnoreHTTPSErrors = true
+ });
+
+ var page = await context.NewPageAsync();
+ page.SetDefaultTimeout(30_000);
+ page.SetDefaultNavigationTimeout(120_000);
+
+ await action(page);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestCollection.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestCollection.cs
new file mode 100644
index 0000000..3b2a49c
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestCollection.cs
@@ -0,0 +1,7 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+[CollectionDefinition(Name)]
+public sealed class UiTestCollection : ICollectionFixture
+{
+ public const string Name = "UiTests";
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestSettings.cs b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestSettings.cs
new file mode 100644
index 0000000..3adfb0b
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/Support/UiTestSettings.cs
@@ -0,0 +1,12 @@
+namespace JdeScoping.Ui.Tests.Support;
+
+internal static class UiTestSettings
+{
+ public static string BaseUrl =>
+ Environment.GetEnvironmentVariable("JDESCOPING_UI_BASE_URL")
+ ?? "http://localhost:5294";
+
+ public static bool Headless =>
+ !string.Equals(Environment.GetEnvironmentVariable("JDESCOPING_UI_HEADED"), "true",
+ StringComparison.OrdinalIgnoreCase);
+}
\ No newline at end of file
diff --git a/TestScripts/playwright/test-data/empty_file.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/empty_file.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/empty_file.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/empty_file.xlsx
diff --git a/TestScripts/playwright/test-data/invalid_format.txt b/NEW/tests/JdeScoping.Ui.Tests/TestData/invalid_format.txt
similarity index 100%
rename from TestScripts/playwright/test-data/invalid_format.txt
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/invalid_format.txt
diff --git a/TestScripts/playwright/test-data/invalid_workorders.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/invalid_workorders.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/invalid_workorders.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/invalid_workorders.xlsx
diff --git a/TestScripts/playwright/test-data/max_workorders.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/max_workorders.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/max_workorders.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/max_workorders.xlsx
diff --git a/TestScripts/playwright/test-data/multiple_items.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_items.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/multiple_items.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_items.xlsx
diff --git a/TestScripts/playwright/test-data/multiple_lots.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_lots.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/multiple_lots.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_lots.xlsx
diff --git a/TestScripts/playwright/test-data/multiple_operations.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_operations.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/multiple_operations.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_operations.xlsx
diff --git a/TestScripts/playwright/test-data/multiple_workorders.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_workorders.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/multiple_workorders.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/multiple_workorders.xlsx
diff --git a/TestScripts/playwright/test-data/single_item.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/single_item.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/single_item.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/single_item.xlsx
diff --git a/TestScripts/playwright/test-data/single_lot.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/single_lot.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/single_lot.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/single_lot.xlsx
diff --git a/TestScripts/playwright/test-data/single_operation.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/single_operation.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/single_operation.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/single_operation.xlsx
diff --git a/TestScripts/playwright/test-data/single_workorder.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/single_workorder.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/single_workorder.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/single_workorder.xlsx
diff --git a/TestScripts/playwright/test-data/special_chars_workorders.xlsx b/NEW/tests/JdeScoping.Ui.Tests/TestData/special_chars_workorders.xlsx
similarity index 100%
rename from TestScripts/playwright/test-data/special_chars_workorders.xlsx
rename to NEW/tests/JdeScoping.Ui.Tests/TestData/special_chars_workorders.xlsx
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TestData/test-data.json b/NEW/tests/JdeScoping.Ui.Tests/TestData/test-data.json
new file mode 100644
index 0000000..132ce5e
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TestData/test-data.json
@@ -0,0 +1,281 @@
+{
+ "profitCenters": [
+ {
+ "code": "1AM",
+ "description": "Profit Center 1AM"
+ },
+ {
+ "code": "1BM",
+ "description": "Profit Center 1BM"
+ },
+ {
+ "code": "1CM",
+ "description": "Profit Center 1CM"
+ },
+ {
+ "code": "1PM",
+ "description": "Profit Center 1PM"
+ },
+ {
+ "code": "2DM",
+ "description": "Profit Center 2DM"
+ },
+ {
+ "code": "2SM",
+ "description": "Profit Center 2SM"
+ },
+ {
+ "code": "3TM",
+ "description": "Profit Center 3TM"
+ },
+ {
+ "code": "4IM",
+ "description": "Profit Center 4IM"
+ },
+ {
+ "code": "5SM",
+ "description": "Profit Center 5SM"
+ }
+ ],
+ "workCenters": [
+ {
+ "code": "WC001",
+ "description": "Work Center 001"
+ },
+ {
+ "code": "WC002",
+ "description": "Work Center 002"
+ },
+ {
+ "code": "WC003",
+ "description": "Work Center 003"
+ }
+ ],
+ "operators": [
+ {
+ "userId": "ADAMSSN",
+ "fullName": "Adams, S N"
+ },
+ {
+ "userId": "AGNEWA",
+ "fullName": "Agnew, A"
+ },
+ {
+ "userId": "AGNEWL",
+ "fullName": "Agnew, L"
+ },
+ {
+ "userId": "ALASMARB",
+ "fullName": "Alasmar, B"
+ },
+ {
+ "userId": "ALEXIUCG",
+ "fullName": "Alexiuc, G"
+ },
+ {
+ "userId": "ALLENHY",
+ "fullName": "Allen, H Y"
+ },
+ {
+ "userId": "ALLENNI",
+ "fullName": "Allen, N I"
+ },
+ {
+ "userId": "ALURUM",
+ "fullName": "Aluru, M"
+ },
+ {
+ "userId": "ALVESM1",
+ "fullName": "Alves, M"
+ },
+ {
+ "userId": "APONTEVE",
+ "fullName": "Aponte, V E"
+ }
+ ],
+ "workOrders": [
+ {
+ "workOrderNumber": "99059700",
+ "itemNumber": "00598004702"
+ },
+ {
+ "workOrderNumber": "99002260",
+ "itemNumber": "82070000028"
+ },
+ {
+ "workOrderNumber": "99002259",
+ "itemNumber": "82070000027"
+ },
+ {
+ "workOrderNumber": "99002258",
+ "itemNumber": "82070000019"
+ },
+ {
+ "workOrderNumber": "99002257",
+ "itemNumber": "82070000018"
+ },
+ {
+ "workOrderNumber": "99002256",
+ "itemNumber": "82070000017"
+ },
+ {
+ "workOrderNumber": "99002255",
+ "itemNumber": "00855140333"
+ },
+ {
+ "workOrderNumber": "99002254",
+ "itemNumber": "00855480834"
+ },
+ {
+ "workOrderNumber": "99002252",
+ "itemNumber": "82070000016"
+ },
+ {
+ "workOrderNumber": "99002251",
+ "itemNumber": "00855910448"
+ },
+ {
+ "workOrderNumber": "99002250",
+ "itemNumber": "82070000015"
+ },
+ {
+ "workOrderNumber": "99002249",
+ "itemNumber": "00855480834"
+ },
+ {
+ "workOrderNumber": "99002248",
+ "itemNumber": "00855910446"
+ },
+ {
+ "workOrderNumber": "99002247",
+ "itemNumber": "00855910447"
+ },
+ {
+ "workOrderNumber": "99002246",
+ "itemNumber": "82900171601"
+ }
+ ],
+ "itemNumbers": [
+ {
+ "itemNumber": "00598004702",
+ "description": "Item 598004702"
+ },
+ {
+ "itemNumber": "82070000028",
+ "description": "Item 82070000028"
+ },
+ {
+ "itemNumber": "82070000027",
+ "description": "Item 82070000027"
+ },
+ {
+ "itemNumber": "00855140333",
+ "description": "Item 855140333"
+ },
+ {
+ "itemNumber": "00855480834",
+ "description": "Item 855480834"
+ }
+ ],
+ "componentLots": [
+ {
+ "lotNumber": "LOT001",
+ "itemNumber": "00598004702"
+ },
+ {
+ "lotNumber": "LOT002",
+ "itemNumber": "82070000028"
+ },
+ {
+ "lotNumber": "LOT003",
+ "itemNumber": "82070000027"
+ }
+ ],
+ "partOperations": [
+ {
+ "itemNumber": "00598004702",
+ "operationNumber": "100",
+ "misNumber": "MIS001",
+ "misRevision": "A"
+ },
+ {
+ "itemNumber": "00598004702",
+ "operationNumber": "200",
+ "misNumber": "MIS002",
+ "misRevision": "B"
+ },
+ {
+ "itemNumber": "82070000028",
+ "operationNumber": "100",
+ "misNumber": "MIS003",
+ "misRevision": "A"
+ }
+ ],
+ "dateRanges": {
+ "recent": {
+ "min": "2020-01-01",
+ "max": "2020-09-01",
+ "description": "Recent data range"
+ },
+ "midRange": {
+ "min": "2018-01-01",
+ "max": "2019-12-31",
+ "description": "Mid-range data"
+ },
+ "historical": {
+ "min": "2016-01-01",
+ "max": "2017-12-31",
+ "description": "Historical data"
+ },
+ "sameDay": {
+ "min": "2020-06-15",
+ "max": "2020-06-15",
+ "description": "Single day range"
+ },
+ "startBoundary": {
+ "min": "1905-01-20",
+ "max": "1905-12-31",
+ "description": "Start of data range"
+ },
+ "endBoundary": {
+ "min": "2020-08-01",
+ "max": "2020-09-01",
+ "description": "End of data range"
+ }
+ },
+ "invalidData": {
+ "workOrders": {
+ "invalidFormat": "ABC123XYZ",
+ "specialChars": "99059700!@#",
+ "empty": "",
+ "whitespace": " "
+ },
+ "profitCenters": {
+ "invalid": "INVALID",
+ "specialChars": "1AM!@#",
+ "empty": "",
+ "tooLong": "1AMEXTRALONG"
+ },
+ "dates": {
+ "invalidFormat": "31-12-2020",
+ "future": {
+ "min": "2025-01-01",
+ "max": "2025-12-31"
+ },
+ "reversed": {
+ "min": "2020-09-01",
+ "max": "2020-01-01"
+ }
+ }
+ },
+ "testCredentials": {
+ "validUser": {
+ "username": "testuser",
+ "password": "testpass"
+ },
+ "invalidUser": {
+ "username": "invaliduser",
+ "password": "wrongpassword"
+ }
+ }
+}
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanItemNumberSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanItemNumberSearchTests.cs
new file mode 100644
index 0000000..7b2cd26
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanItemNumberSearchTests.cs
@@ -0,0 +1,42 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Item Number" search type (TC-140).
+/// Validates search form interaction in smoke mode and full submission with workbook upload in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanItemNumberSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Item Number search form submits with an uploaded workbook filter (TC-140).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-140".
+ /// - Select the "Time Span + Item Number" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Upload "single_item.xlsx" to the "Filter by Item Number" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanItemNumber_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanItem,
+ "MIGRATED-TC-140",
+ "01/01/2019",
+ "12/31/2019",
+ uploads:
+ [
+ ("Filter by Item Number", "single_item.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanOperatorSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanOperatorSearchTests.cs
new file mode 100644
index 0000000..f236957
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanOperatorSearchTests.cs
@@ -0,0 +1,41 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Operator" search type (TC-050).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Operator search form submits with autocomplete filter (TC-050).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-050".
+ /// - Select the "Time Span + Operator" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "ADAMSSN" to the "Filter by Operator" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanOperator_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanOperator,
+ "MIGRATED-TC-050",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Operator", "ADAMSSN")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcExtractMisSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcExtractMisSearchTests.cs
new file mode 100644
index 0000000..8eb476a
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcExtractMisSearchTests.cs
@@ -0,0 +1,41 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center + Extract MIS" search type (TC-090).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanPcExtractMisSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Profit Center + Extract MIS search form submits with autocomplete filter (TC-090).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-090".
+ /// - Select the "Time Span + Profit Center + Extract MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanPcExtractMis_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanPcExtractMis,
+ "MIGRATED-TC-090",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcItemSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcItemSearchTests.cs
new file mode 100644
index 0000000..291237b
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcItemSearchTests.cs
@@ -0,0 +1,45 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center + Item Number" search type (TC-060).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanPcItemSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Profit Center + Item Number search form submits with autocomplete and upload (TC-060).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-060".
+ /// - Select the "Time Span + Profit Center + Item Number" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Upload "single_item.xlsx" to the "Filter by Item Number" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanPcItem_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanPcItem,
+ "MIGRATED-TC-060",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM")
+ ],
+ [
+ ("Filter by Item Number", "single_item.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcOperatorSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcOperatorSearchTests.cs
new file mode 100644
index 0000000..80bd56b
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcOperatorSearchTests.cs
@@ -0,0 +1,43 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center + Operator" search type (TC-160).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanPcOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Profit Center + Operator search form submits with autocomplete filters (TC-160).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-160".
+ /// - Select the "Time Span + Profit Center + Operator" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Add autocomplete value "ADAMSSN" to the "Filter by Operator" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanPcOperator_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanPcOperator,
+ "MIGRATED-TC-160",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM"),
+ ("Filter by Operator", "ADAMSSN")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcPartOpSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcPartOpSearchTests.cs
new file mode 100644
index 0000000..6fb40f5
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcPartOpSearchTests.cs
@@ -0,0 +1,45 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center + Item/Operation/MIS" search type (TC-070).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanPcPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Profit Center + Item/Operation/MIS search form submits with autocomplete and upload (TC-070).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-070".
+ /// - Select the "Time Span + Profit Center + Item/Operation/MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Upload "single_operation.xlsx" to the "Filter By Item/Operation/MIS" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanPcPartOp_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanPcPartOp,
+ "MIGRATED-TC-070",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM")
+ ],
+ [
+ ("Filter By Item/Operation/MIS", "single_operation.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcWoPartOpSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcWoPartOpSearchTests.cs
new file mode 100644
index 0000000..0d3dcd9
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanPcWoPartOpSearchTests.cs
@@ -0,0 +1,47 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center + Work Order + Item/Operation/MIS" search type (TC-080).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanPcWoPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + PC + Work Order + Item/Op/MIS search form submits with autocomplete and uploads (TC-080).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-080".
+ /// - Select the "Time Span + Profit Center + Work Order + Item/Operation/MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Upload "single_workorder.xlsx" to the "Filter by Work Order" panel (strict only).
+ /// - Upload "single_operation.xlsx" to the "Filter By Item/Operation/MIS" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanPcWoPartOp_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanPcWoPartOp,
+ "MIGRATED-TC-080",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM")
+ ],
+ [
+ ("Filter by Work Order", "single_workorder.xlsx"),
+ ("Filter By Item/Operation/MIS", "single_operation.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanProfitCenterSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanProfitCenterSearchTests.cs
new file mode 100644
index 0000000..36dce2f
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanProfitCenterSearchTests.cs
@@ -0,0 +1,41 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Profit Center" search type (TC-030).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanProfitCenterSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Profit Center search form submits with autocomplete filter (TC-030).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-030".
+ /// - Select the "Time Span + Profit Center" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "1AM" to the "Filter by Profit Center" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanProfitCenter_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanProfitCenter,
+ "MIGRATED-TC-030",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Profit Center", "1AM")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcExtractMisSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcExtractMisSearchTests.cs
new file mode 100644
index 0000000..efe7a5d
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcExtractMisSearchTests.cs
@@ -0,0 +1,41 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center + Extract MIS" search type (TC-110).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWcExtractMisSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Work Center + Extract MIS search form submits with autocomplete filter (TC-110).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-110".
+ /// - Select the "Time Span + Work Center + Extract MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWcExtractMis_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWcExtractMis,
+ "MIGRATED-TC-110",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcItemSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcItemSearchTests.cs
new file mode 100644
index 0000000..81366b4
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcItemSearchTests.cs
@@ -0,0 +1,45 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center + Item Number" search type (TC-100).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWcItemSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Work Center + Item Number search form submits with autocomplete and upload (TC-100).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-100".
+ /// - Select the "Time Span + Work Center + Item Number" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Upload "single_item.xlsx" to the "Filter by Item Number" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWcItem_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWcItem,
+ "MIGRATED-TC-100",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS")
+ ],
+ [
+ ("Filter by Item Number", "single_item.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcOperatorSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcOperatorSearchTests.cs
new file mode 100644
index 0000000..c2135cd
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcOperatorSearchTests.cs
@@ -0,0 +1,43 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center + Operator" search type (TC-150).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWcOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Work Center + Operator search form submits with autocomplete filters (TC-150).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-150".
+ /// - Select the "Time Span + Work Center + Operator" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Add autocomplete value "ADAMSSN" to the "Filter by Operator" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWcOperator_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWcOperator,
+ "MIGRATED-TC-150",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS"),
+ ("Filter by Operator", "ADAMSSN")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcPartOpSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcPartOpSearchTests.cs
new file mode 100644
index 0000000..693270c
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcPartOpSearchTests.cs
@@ -0,0 +1,45 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center + Item/Operation/MIS" search type (TC-120).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWcPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Work Center + Item/Operation/MIS search form submits with autocomplete and upload (TC-120).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-120".
+ /// - Select the "Time Span + Work Center + Item/Operation/MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Upload "single_operation.xlsx" to the "Filter By Item/Operation/MIS" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWcPartOp_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWcPartOp,
+ "MIGRATED-TC-120",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS")
+ ],
+ [
+ ("Filter By Item/Operation/MIS", "single_operation.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcWoPartOpSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcWoPartOpSearchTests.cs
new file mode 100644
index 0000000..7b616c5
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWcWoPartOpSearchTests.cs
@@ -0,0 +1,47 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center + Work Order + Item/Operation/MIS" search type (TC-130).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWcWoPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + WC + Work Order + Item/Op/MIS search form submits with autocomplete and uploads (TC-130).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-130".
+ /// - Select the "Time Span + Work Center + Work Order + Item/Operation/MIS" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Upload "single_workorder.xlsx" to the "Filter by Work Order" panel (strict only).
+ /// - Upload "single_operation.xlsx" to the "Filter By Item/Operation/MIS" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWcWoPartOp_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWcWoPartOp,
+ "MIGRATED-TC-130",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS")
+ ],
+ [
+ ("Filter by Work Order", "single_workorder.xlsx"),
+ ("Filter By Item/Operation/MIS", "single_operation.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWorkCenterSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWorkCenterSearchTests.cs
new file mode 100644
index 0000000..0999a11
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/TimeSpanWorkCenterSearchTests.cs
@@ -0,0 +1,41 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Time Span + Work Center" search type (TC-040).
+/// Validates search form interaction in smoke mode and full submission in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class TimeSpanWorkCenterSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Time Span + Work Center search form submits with autocomplete filter (TC-040).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-040".
+ /// - Select the "Time Span + Work Center" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Set the date range to 01/01/2019 – 12/31/2019 (strict only).
+ /// - Add autocomplete value "0083AS" to the "Filter by Work Center" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task TimeSpanWorkCenter_Submits()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.TimeSpanWorkCenter,
+ "MIGRATED-TC-040",
+ "01/01/2019",
+ "12/31/2019",
+ [
+ ("Filter by Work Center", "0083AS")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/JdeScoping.Ui.Tests/WorkOrderSearchTests.cs b/NEW/tests/JdeScoping.Ui.Tests/WorkOrderSearchTests.cs
new file mode 100644
index 0000000..b051378
--- /dev/null
+++ b/NEW/tests/JdeScoping.Ui.Tests/WorkOrderSearchTests.cs
@@ -0,0 +1,39 @@
+using JdeScoping.Ui.Tests.Support;
+
+namespace JdeScoping.Ui.Tests;
+
+///
+/// Playwright UI tests for the "Work Order" search type (TC-010).
+/// Validates search form interaction in smoke mode and full submission with workbook upload in strict mode.
+/// Requires a running Docker host (Category: RequiresDockerHost).
+///
+public sealed class WorkOrderSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
+{
+ ///
+ /// Verifies the Work Order search form submits with an uploaded workbook filter (TC-010).
+ ///
+ ///
+ /// Steps (smoke mode stops after step 4; strict mode runs all steps):
+ ///
+ /// - Navigate to the search page.
+ /// - Enter the search name "MIGRATED-TC-010".
+ /// - Select the "Work Order" search type from the dropdown.
+ /// - Verify the dropdown displays the selected type.
+ /// - Upload "single_workorder.xlsx" to the "Filter by Work Order" panel (strict only).
+ /// - Click Submit (strict only).
+ /// - Assert no error notification is present (strict only).
+ ///
+ ///
+ [Fact]
+ [Trait("Category", "RequiresDockerHost")]
+ public Task WorkOrder_SubmitsWithUploadedWorkbook()
+ {
+ return RunSearchSubmissionAsync(
+ UiSearchTypes.WorkOrder,
+ "MIGRATED-TC-010",
+ uploads:
+ [
+ ("Filter by Work Order", "single_workorder.xlsx")
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/NEW/tests/Utils/JdeScoping.ConfigManager.Cli.Tests/JdeScoping.ConfigManager.Cli.Tests.csproj b/NEW/tests/Utils/JdeScoping.ConfigManager.Cli.Tests/JdeScoping.ConfigManager.Cli.Tests.csproj
index 5d9f0c2..bdcfe1e 100644
--- a/NEW/tests/Utils/JdeScoping.ConfigManager.Cli.Tests/JdeScoping.ConfigManager.Cli.Tests.csproj
+++ b/NEW/tests/Utils/JdeScoping.ConfigManager.Cli.Tests/JdeScoping.ConfigManager.Cli.Tests.csproj
@@ -13,10 +13,10 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/NEW/tests/Utils/JdeScoping.ConfigManager.Core.Tests/JdeScoping.ConfigManager.Core.Tests.csproj b/NEW/tests/Utils/JdeScoping.ConfigManager.Core.Tests/JdeScoping.ConfigManager.Core.Tests.csproj
index 52b1841..86b4ffe 100644
--- a/NEW/tests/Utils/JdeScoping.ConfigManager.Core.Tests/JdeScoping.ConfigManager.Core.Tests.csproj
+++ b/NEW/tests/Utils/JdeScoping.ConfigManager.Core.Tests/JdeScoping.ConfigManager.Core.Tests.csproj
@@ -12,10 +12,10 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/NEW/tests/Utils/JdeScoping.ConfigManager.Ui.Tests/JdeScoping.ConfigManager.Ui.Tests.csproj b/NEW/tests/Utils/JdeScoping.ConfigManager.Ui.Tests/JdeScoping.ConfigManager.Ui.Tests.csproj
index 7a14e49..3f9c43c 100644
--- a/NEW/tests/Utils/JdeScoping.ConfigManager.Ui.Tests/JdeScoping.ConfigManager.Ui.Tests.csproj
+++ b/NEW/tests/Utils/JdeScoping.ConfigManager.Ui.Tests/JdeScoping.ConfigManager.Ui.Tests.csproj
@@ -13,12 +13,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/TestScripts/playwright/README.md b/TestScripts/deprecated/playwright/README.md
similarity index 100%
rename from TestScripts/playwright/README.md
rename to TestScripts/deprecated/playwright/README.md
diff --git a/TestScripts/playwright/debug-screenshot.png b/TestScripts/deprecated/playwright/debug-screenshot.png
similarity index 100%
rename from TestScripts/playwright/debug-screenshot.png
rename to TestScripts/deprecated/playwright/debug-screenshot.png
diff --git a/TestScripts/playwright/fixtures/test.fixture.ts b/TestScripts/deprecated/playwright/fixtures/test.fixture.ts
similarity index 100%
rename from TestScripts/playwright/fixtures/test.fixture.ts
rename to TestScripts/deprecated/playwright/fixtures/test.fixture.ts
diff --git a/TestScripts/playwright/helpers/auth.helper.ts b/TestScripts/deprecated/playwright/helpers/auth.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/auth.helper.ts
rename to TestScripts/deprecated/playwright/helpers/auth.helper.ts
diff --git a/TestScripts/playwright/helpers/autocomplete.helper.ts b/TestScripts/deprecated/playwright/helpers/autocomplete.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/autocomplete.helper.ts
rename to TestScripts/deprecated/playwright/helpers/autocomplete.helper.ts
diff --git a/TestScripts/playwright/helpers/date-picker.helper.ts b/TestScripts/deprecated/playwright/helpers/date-picker.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/date-picker.helper.ts
rename to TestScripts/deprecated/playwright/helpers/date-picker.helper.ts
diff --git a/TestScripts/playwright/helpers/file-upload.helper.ts b/TestScripts/deprecated/playwright/helpers/file-upload.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/file-upload.helper.ts
rename to TestScripts/deprecated/playwright/helpers/file-upload.helper.ts
diff --git a/TestScripts/playwright/helpers/index.ts b/TestScripts/deprecated/playwright/helpers/index.ts
similarity index 100%
rename from TestScripts/playwright/helpers/index.ts
rename to TestScripts/deprecated/playwright/helpers/index.ts
diff --git a/TestScripts/playwright/helpers/navigation.helper.ts b/TestScripts/deprecated/playwright/helpers/navigation.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/navigation.helper.ts
rename to TestScripts/deprecated/playwright/helpers/navigation.helper.ts
diff --git a/TestScripts/playwright/helpers/radzen.helper.ts b/TestScripts/deprecated/playwright/helpers/radzen.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/radzen.helper.ts
rename to TestScripts/deprecated/playwright/helpers/radzen.helper.ts
diff --git a/TestScripts/playwright/helpers/search-type.helper.ts b/TestScripts/deprecated/playwright/helpers/search-type.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/search-type.helper.ts
rename to TestScripts/deprecated/playwright/helpers/search-type.helper.ts
diff --git a/TestScripts/playwright/helpers/validation.helper.ts b/TestScripts/deprecated/playwright/helpers/validation.helper.ts
similarity index 100%
rename from TestScripts/playwright/helpers/validation.helper.ts
rename to TestScripts/deprecated/playwright/helpers/validation.helper.ts
diff --git a/TestScripts/playwright/package-lock.json b/TestScripts/deprecated/playwright/package-lock.json
similarity index 100%
rename from TestScripts/playwright/package-lock.json
rename to TestScripts/deprecated/playwright/package-lock.json
diff --git a/TestScripts/playwright/package.json b/TestScripts/deprecated/playwright/package.json
similarity index 100%
rename from TestScripts/playwright/package.json
rename to TestScripts/deprecated/playwright/package.json
diff --git a/TestScripts/playwright/playwright-report/index.html b/TestScripts/deprecated/playwright/playwright-report/index.html
similarity index 100%
rename from TestScripts/playwright/playwright-report/index.html
rename to TestScripts/deprecated/playwright/playwright-report/index.html
diff --git a/TestScripts/playwright/playwright.config.ts b/TestScripts/deprecated/playwright/playwright.config.ts
similarity index 100%
rename from TestScripts/playwright/playwright.config.ts
rename to TestScripts/deprecated/playwright/playwright.config.ts
diff --git a/TestScripts/playwright/scripts/create-test-excel.js b/TestScripts/deprecated/playwright/scripts/create-test-excel.js
similarity index 100%
rename from TestScripts/playwright/scripts/create-test-excel.js
rename to TestScripts/deprecated/playwright/scripts/create-test-excel.js
diff --git a/TestScripts/deprecated/playwright/test-data/empty_file.xlsx b/TestScripts/deprecated/playwright/test-data/empty_file.xlsx
new file mode 100644
index 0000000..097cb81
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/empty_file.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/invalid_format.txt b/TestScripts/deprecated/playwright/test-data/invalid_format.txt
new file mode 100644
index 0000000..3df86f3
--- /dev/null
+++ b/TestScripts/deprecated/playwright/test-data/invalid_format.txt
@@ -0,0 +1,2 @@
+This is not a valid Excel file
+Just plain text
\ No newline at end of file
diff --git a/TestScripts/deprecated/playwright/test-data/invalid_workorders.xlsx b/TestScripts/deprecated/playwright/test-data/invalid_workorders.xlsx
new file mode 100644
index 0000000..c0e5a0d
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/invalid_workorders.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/max_workorders.xlsx b/TestScripts/deprecated/playwright/test-data/max_workorders.xlsx
new file mode 100644
index 0000000..9d6a4c4
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/max_workorders.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/multiple_items.xlsx b/TestScripts/deprecated/playwright/test-data/multiple_items.xlsx
new file mode 100644
index 0000000..0326409
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/multiple_items.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/multiple_lots.xlsx b/TestScripts/deprecated/playwright/test-data/multiple_lots.xlsx
new file mode 100644
index 0000000..5ecc266
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/multiple_lots.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/multiple_operations.xlsx b/TestScripts/deprecated/playwright/test-data/multiple_operations.xlsx
new file mode 100644
index 0000000..395ef6d
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/multiple_operations.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/multiple_workorders.xlsx b/TestScripts/deprecated/playwright/test-data/multiple_workorders.xlsx
new file mode 100644
index 0000000..5f1b2af
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/multiple_workorders.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/single_item.xlsx b/TestScripts/deprecated/playwright/test-data/single_item.xlsx
new file mode 100644
index 0000000..6ba8707
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/single_item.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/single_lot.xlsx b/TestScripts/deprecated/playwright/test-data/single_lot.xlsx
new file mode 100644
index 0000000..909b0e2
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/single_lot.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/single_operation.xlsx b/TestScripts/deprecated/playwright/test-data/single_operation.xlsx
new file mode 100644
index 0000000..999ecbe
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/single_operation.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/single_workorder.xlsx b/TestScripts/deprecated/playwright/test-data/single_workorder.xlsx
new file mode 100644
index 0000000..a71dc24
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/single_workorder.xlsx differ
diff --git a/TestScripts/deprecated/playwright/test-data/special_chars_workorders.xlsx b/TestScripts/deprecated/playwright/test-data/special_chars_workorders.xlsx
new file mode 100644
index 0000000..9363abb
Binary files /dev/null and b/TestScripts/deprecated/playwright/test-data/special_chars_workorders.xlsx differ
diff --git a/TestScripts/playwright/test-data/test-data.json b/TestScripts/deprecated/playwright/test-data/test-data.json
similarity index 100%
rename from TestScripts/playwright/test-data/test-data.json
rename to TestScripts/deprecated/playwright/test-data/test-data.json
diff --git a/TestScripts/playwright/test-results/.last-run.json b/TestScripts/deprecated/playwright/test-results/.last-run.json
similarity index 100%
rename from TestScripts/playwright/test-results/.last-run.json
rename to TestScripts/deprecated/playwright/test-results/.last-run.json
diff --git a/TestScripts/playwright/tests/component-lot.spec.ts b/TestScripts/deprecated/playwright/tests/component-lot.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/component-lot.spec.ts
rename to TestScripts/deprecated/playwright/tests/component-lot.spec.ts
diff --git a/TestScripts/playwright/tests/data-sync.spec.ts b/TestScripts/deprecated/playwright/tests/data-sync.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/data-sync.spec.ts
rename to TestScripts/deprecated/playwright/tests/data-sync.spec.ts
diff --git a/TestScripts/playwright/tests/login.spec.ts b/TestScripts/deprecated/playwright/tests/login.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/login.spec.ts
rename to TestScripts/deprecated/playwright/tests/login.spec.ts
diff --git a/TestScripts/playwright/tests/refresh-status.spec.ts b/TestScripts/deprecated/playwright/tests/refresh-status.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/refresh-status.spec.ts
rename to TestScripts/deprecated/playwright/tests/refresh-status.spec.ts
diff --git a/TestScripts/playwright/tests/search-page.spec.ts b/TestScripts/deprecated/playwright/tests/search-page.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/search-page.spec.ts
rename to TestScripts/deprecated/playwright/tests/search-page.spec.ts
diff --git a/TestScripts/playwright/tests/search-queue.spec.ts b/TestScripts/deprecated/playwright/tests/search-queue.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/search-queue.spec.ts
rename to TestScripts/deprecated/playwright/tests/search-queue.spec.ts
diff --git a/TestScripts/playwright/tests/searches-dashboard.spec.ts b/TestScripts/deprecated/playwright/tests/searches-dashboard.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/searches-dashboard.spec.ts
rename to TestScripts/deprecated/playwright/tests/searches-dashboard.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-item-number.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-item-number.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-item-number.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-item-number.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-operator.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-operator.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-operator.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-operator.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-pc-extractmis.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-pc-extractmis.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-pc-extractmis.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-pc-extractmis.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-pc-item.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-pc-item.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-pc-item.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-pc-item.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-pc-operator.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-pc-operator.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-pc-operator.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-pc-operator.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-pc-partop.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-pc-partop.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-pc-partop.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-pc-partop.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-pc-wo-partop.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-pc-wo-partop.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-pc-wo-partop.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-pc-wo-partop.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-profit-center.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-profit-center.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-profit-center.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-profit-center.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-wc-extractmis.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-wc-extractmis.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-wc-extractmis.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-wc-extractmis.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-wc-item.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-wc-item.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-wc-item.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-wc-item.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-wc-operator.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-wc-operator.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-wc-operator.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-wc-operator.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-wc-partop.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-wc-partop.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-wc-partop.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-wc-partop.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-wc-wo-partop.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-wc-wo-partop.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-wc-wo-partop.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-wc-wo-partop.spec.ts
diff --git a/TestScripts/playwright/tests/timespan-work-center.spec.ts b/TestScripts/deprecated/playwright/tests/timespan-work-center.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/timespan-work-center.spec.ts
rename to TestScripts/deprecated/playwright/tests/timespan-work-center.spec.ts
diff --git a/TestScripts/playwright/tests/work-order.spec.ts b/TestScripts/deprecated/playwright/tests/work-order.spec.ts
similarity index 100%
rename from TestScripts/playwright/tests/work-order.spec.ts
rename to TestScripts/deprecated/playwright/tests/work-order.spec.ts