Migrate Playwright suite to .NET UI tests and deprecate TS project
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
<Project Path="tests/JdeScoping.ExcelIO.Tests/JdeScoping.ExcelIO.Tests.csproj" />
|
||||
<Project Path="tests/JdeScoping.Host.Tests/JdeScoping.Host.Tests.csproj" />
|
||||
<Project Path="tests/JdeScoping.Infrastructure.Tests/JdeScoping.Infrastructure.Tests.csproj" />
|
||||
<Project Path="tests/JdeScoping.Ui.Tests/JdeScoping.Ui.Tests.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/utils/">
|
||||
<Project Path="tests/Utils/JdeScoping.ConfigManager.Core.Tests/JdeScoping.ConfigManager.Core.Tests.csproj" />
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using JdeScoping.Core.Models;
|
||||
using JdeScoping.Core.Models.Auth;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public class AuthApiSmokeTests
|
||||
{
|
||||
[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<PublicKeyResponse>("api/auth/public-key");
|
||||
Assert.NotNull(key);
|
||||
Assert.Contains("BEGIN PUBLIC KEY", key!.PublicKeyPem);
|
||||
|
||||
var payload = JsonSerializer.Serialize(new LoginModel { Username = "testuser", Password = "testpass" });
|
||||
|
||||
using var rsa = RSA.Create();
|
||||
rsa.ImportFromPem(key.PublicKeyPem);
|
||||
var 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class ComponentLotSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task ComponentLot_SubmitsWithUploadedWorkbook() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.ComponentLot,
|
||||
"MIGRATED-TC-020",
|
||||
uploads:
|
||||
[
|
||||
("Filter By Component Lot", "single_lot.xlsx")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class DataSyncPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public async Task DataSync_Loads()
|
||||
{
|
||||
await RunAsync(async page =>
|
||||
{
|
||||
await UiNavigationHelper.NavigateToDataSyncAsync(page);
|
||||
var url = page.Url;
|
||||
var onDataSync = url.EndsWith("/data-sync/requests", StringComparison.OrdinalIgnoreCase);
|
||||
var 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() { Timeout = 15_000 });
|
||||
var newRequestButton = page.GetByRole(Microsoft.Playwright.AriaRole.Button, new() { Name = "New Request" });
|
||||
var reloadButton = page.GetByRole(Microsoft.Playwright.AriaRole.Button, new() { Name = "Reload Pipelines" });
|
||||
var 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() { Timeout = 15_000 });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
global using Xunit;
|
||||
global using Microsoft.Playwright;
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
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");
|
||||
var 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");
|
||||
var count = await notifications.CountAsync();
|
||||
var details = new List<string>();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
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}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
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) =>
|
||||
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 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.Playwright" Version="1.51.0" />
|
||||
<PackageReference Include="Shouldly" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\JdeScoping.Core\JdeScoping.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="TestData/**/*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class LoginPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[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(Microsoft.Playwright.AriaRole.Button, new() { Name = "Logout" });
|
||||
var authenticated = await logoutButton.IsVisibleAsync();
|
||||
var 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class RefreshStatusPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public async Task RefreshStatus_Loads()
|
||||
{
|
||||
await RunAsync(async page =>
|
||||
{
|
||||
await UiNavigationHelper.NavigateToRefreshStatusAsync(page);
|
||||
var url = page.Url;
|
||||
var onRefreshStatus = url.EndsWith("/refresh-status", StringComparison.OrdinalIgnoreCase);
|
||||
var 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() { Timeout = 15_000 });
|
||||
}
|
||||
else
|
||||
{
|
||||
await Assertions.Expect(page.GetByText("Search Details")).ToBeVisibleAsync(new() { Timeout = 15_000 });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class SearchPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[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(Microsoft.Playwright.AriaRole.Button, new() { Name = "Submit" }).First).ToBeVisibleAsync();
|
||||
await UiSearchFormHelper.AssertNoErrorNotificationAsync(page);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class SearchQueuePageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public async Task SearchQueue_Loads()
|
||||
{
|
||||
await RunAsync(async page =>
|
||||
{
|
||||
await UiNavigationHelper.NavigateToQueueAsync(page);
|
||||
var url = page.Url;
|
||||
var onQueue = url.EndsWith("/search/queue", StringComparison.OrdinalIgnoreCase);
|
||||
var redirectedToSearch = url.EndsWith("/search", StringComparison.OrdinalIgnoreCase);
|
||||
Assert.True(onQueue || redirectedToSearch, $"Unexpected URL: {url}");
|
||||
|
||||
if (onQueue)
|
||||
{
|
||||
await Assertions.Expect(page.GetByText("Search Queue")).ToBeVisibleAsync(new() { Timeout = 15_000 });
|
||||
var hasGrid = await page.Locator(".rz-data-grid").First.IsVisibleAsync();
|
||||
var hasAlert = await page.Locator(".rz-alert").First.IsVisibleAsync();
|
||||
var 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() { Timeout = 15_000 });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using JdeScoping.Ui.Tests.Helpers;
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class SearchesDashboardPageTests(PlaywrightFixture fixture) : UiTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public async Task SearchesDashboard_Loads()
|
||||
{
|
||||
await RunAsync(async page =>
|
||||
{
|
||||
await UiNavigationHelper.NavigateToSearchesDashboardAsync(page);
|
||||
var url = page.Url;
|
||||
Assert.True(
|
||||
url.EndsWith("/searches", StringComparison.OrdinalIgnoreCase) ||
|
||||
url.EndsWith("/search", StringComparison.OrdinalIgnoreCase) ||
|
||||
url.EndsWith("/", StringComparison.OrdinalIgnoreCase),
|
||||
$"Unexpected URL: {url}");
|
||||
|
||||
var hasSearchesHeading = await page.GetByText("Searches Dashboard").IsVisibleAsync();
|
||||
var hasSearchDetails = await page.GetByText("Search Details").IsVisibleAsync();
|
||||
var hasGrid = await page.Locator(".rz-data-grid").First.IsVisibleAsync();
|
||||
Assert.True(hasSearchesHeading || hasSearchDetails || hasGrid);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
namespace JdeScoping.Ui.Tests.Support;
|
||||
|
||||
public sealed class PlaywrightFixture : IAsyncLifetime
|
||||
{
|
||||
private IPlaywright? _playwright;
|
||||
private IBrowser? _browser;
|
||||
|
||||
public IBrowser Browser => _browser ?? throw new InvalidOperationException("Browser is not initialized.");
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_playwright = await Microsoft.Playwright.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace JdeScoping.Ui.Tests.Support;
|
||||
|
||||
internal static class TestDataPaths
|
||||
{
|
||||
public static string Get(string fileName) => Path.Combine(AppContext.BaseDirectory, "TestData", fileName);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Playwright;
|
||||
|
||||
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<IPage, Task> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Xunit;
|
||||
|
||||
namespace JdeScoping.Ui.Tests.Support;
|
||||
|
||||
[CollectionDefinition(Name)]
|
||||
public sealed class UiTestCollection : ICollectionFixture<PlaywrightFixture>
|
||||
{
|
||||
public const string Name = "UiTests";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanItemNumberSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanItemNumber_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanItem,
|
||||
"MIGRATED-TC-140",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
uploads:
|
||||
[
|
||||
("Filter by Item Number", "single_item.xlsx")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanOperator_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanOperator,
|
||||
"MIGRATED-TC-050",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Operator", "ADAMSSN")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanPcExtractMisSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanPcExtractMis_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanPcExtractMis,
|
||||
"MIGRATED-TC-090",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Profit Center", "1AM")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanPcItemSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanPcItem_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanPcItem,
|
||||
"MIGRATED-TC-060",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Profit Center", "1AM")
|
||||
],
|
||||
[
|
||||
("Filter by Item Number", "single_item.xlsx")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanPcOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanPcOperator_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanPcOperator,
|
||||
"MIGRATED-TC-160",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Profit Center", "1AM"),
|
||||
("Filter by Operator", "ADAMSSN")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanPcPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanPcPartOp_Submits() =>
|
||||
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")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanPcWoPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanPcWoPartOp_Submits() =>
|
||||
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")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanProfitCenterSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanProfitCenter_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanProfitCenter,
|
||||
"MIGRATED-TC-030",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Profit Center", "1AM")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWcExtractMisSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWcExtractMis_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanWcExtractMis,
|
||||
"MIGRATED-TC-110",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Work Center", "0083AS")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWcItemSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWcItem_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanWcItem,
|
||||
"MIGRATED-TC-100",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Work Center", "0083AS")
|
||||
],
|
||||
[
|
||||
("Filter by Item Number", "single_item.xlsx")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWcOperatorSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWcOperator_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanWcOperator,
|
||||
"MIGRATED-TC-150",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Work Center", "0083AS"),
|
||||
("Filter by Operator", "ADAMSSN")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWcPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWcPartOp_Submits() =>
|
||||
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")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWcWoPartOpSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWcWoPartOp_Submits() =>
|
||||
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")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class TimeSpanWorkCenterSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task TimeSpanWorkCenter_Submits() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.TimeSpanWorkCenter,
|
||||
"MIGRATED-TC-040",
|
||||
"01/01/2019",
|
||||
"12/31/2019",
|
||||
[
|
||||
("Filter by Work Center", "0083AS")
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using JdeScoping.Ui.Tests.Support;
|
||||
|
||||
namespace JdeScoping.Ui.Tests;
|
||||
|
||||
public sealed class WorkOrderSearchTests(PlaywrightFixture fixture) : SearchFlowTestBase(fixture)
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "RequiresDockerHost")]
|
||||
public Task WorkOrder_SubmitsWithUploadedWorkbook() =>
|
||||
RunSearchSubmissionAsync(
|
||||
UiSearchTypes.WorkOrder,
|
||||
"MIGRATED-TC-010",
|
||||
uploads:
|
||||
[
|
||||
("Filter by Work Order", "single_workorder.xlsx")
|
||||
]);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Generated
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
This is not a valid Excel file
|
||||
Just plain text
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user