forked from baron/baron-sso
perf(admin): full-stack performance optimization for all list tables
- Implemented server-side search, infinite scrolling, and list virtualization for Tenants, Users, and Audit Logs. - Backend: Enhanced Repository, Service, and Handler layers to support 'search' and 'cursor' parameters. - Frontend: Integrated @tanstack/react-virtual and useInfiniteQuery for high-performance rendering. - Quality: Updated all unit tests and E2E tests to match the new asynchronous server-side search architecture. - i18n: Synced all translation keys and cleaned up unused resources.
This commit is contained in:
@@ -53,13 +53,33 @@ test.describe("Audit Logs Management", () => {
|
||||
const url = route.request().url();
|
||||
const urlObj = new URL(url);
|
||||
const cursor = urlObj.searchParams.get("cursor");
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
const status = urlObj.searchParams.get("status");
|
||||
const offset = cursor ? 20 : 0;
|
||||
console.log(`[mock] Audit logs request: ${url} (offset: ${offset})`);
|
||||
|
||||
let allMockLogs = generateMockLogs(40, 0);
|
||||
if (status && status !== "all") {
|
||||
allMockLogs = allMockLogs.filter((l) => l.status === status);
|
||||
}
|
||||
if (search) {
|
||||
allMockLogs = allMockLogs.filter(
|
||||
(l) =>
|
||||
l.user_id.toLowerCase().includes(search) ||
|
||||
l.details.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
const paginatedItems = allMockLogs.slice(offset, offset + 20);
|
||||
|
||||
console.log(
|
||||
`[mock] Audit logs request: ${url} (offset: ${offset}, search: ${search}, status: ${status}, results: ${paginatedItems.length})`,
|
||||
);
|
||||
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: generateMockLogs(20, offset),
|
||||
next_cursor: offset === 0 ? "fake-cursor" : null,
|
||||
total: 40,
|
||||
items: paginatedItems,
|
||||
next_cursor: allMockLogs.length > offset + 20 ? "fake-cursor" : null,
|
||||
total: allMockLogs.length,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
@@ -172,7 +192,7 @@ test.describe("Audit Logs Management", () => {
|
||||
await userIdInput.fill("user-even");
|
||||
|
||||
// Wait for deferred value to apply
|
||||
await expect(page.locator("tbody tr")).toHaveCount(10, { timeout: 15000 });
|
||||
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
|
||||
await expect(page.locator("tbody")).not.toContainText("user-odd");
|
||||
|
||||
// Clear User ID
|
||||
@@ -183,12 +203,13 @@ test.describe("Audit Logs Management", () => {
|
||||
const actionInput = page.getByTestId("audit-search-action");
|
||||
await actionInput.fill("ROTATE_SECRET");
|
||||
|
||||
// Check that we only see ROTATE_SECRET (20 - 7 = 13)
|
||||
await expect(page.locator("tbody tr")).toHaveCount(13, { timeout: 15000 });
|
||||
// Check that we see ROTATE_SECRET across all 40 logs (40 - 14 = 26)
|
||||
// Wait for the mock to respond and render
|
||||
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
|
||||
await expect(page.locator("tbody")).not.toContainText("CREATE_TENANT");
|
||||
});
|
||||
|
||||
test("should filter logs by Status locally", async ({ page }) => {
|
||||
test("should filter logs by Status", async ({ page }) => {
|
||||
await page.goto("/audit-logs");
|
||||
await expect(page.locator(".animate-spin")).not.toBeVisible({
|
||||
timeout: 10000,
|
||||
@@ -201,12 +222,13 @@ test.describe("Audit Logs Management", () => {
|
||||
// Select "Failure" status
|
||||
await page.getByTestId("audit-filter-status").selectOption("failure");
|
||||
|
||||
// ID % 5 === 0 are status "failure" (0, 5, 10, 15)
|
||||
await expect(page.locator("tbody tr")).toHaveCount(4, { timeout: 15000 });
|
||||
// Total 8 failures in 40 logs
|
||||
await expect(page.locator("tbody tr")).toHaveCount(8, { timeout: 15000 });
|
||||
|
||||
// Select "Success" status
|
||||
await page.getByTestId("audit-filter-status").selectOption("success");
|
||||
|
||||
await expect(page.locator("tbody tr")).toHaveCount(16, { timeout: 15000 });
|
||||
// Total 32 successes in 40 logs, but page limit is 20
|
||||
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,28 +61,42 @@ test.describe("Tenants Management", () => {
|
||||
const internalTenantId = "c5839444-2de0-4a37-99b0-4f94d3de8bea";
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
if (route.request().method() === "GET") {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: internalTenantId,
|
||||
name: "Tenant A",
|
||||
slug: "tenant-a",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
if (route.request().method() !== "GET") {
|
||||
return route.continue();
|
||||
}
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: internalTenantId,
|
||||
name: "Tenant A",
|
||||
slug: "tenant-a",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/tenants");
|
||||
@@ -115,40 +129,55 @@ test.describe("Tenants Management", () => {
|
||||
return route.continue();
|
||||
}
|
||||
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: "company-1",
|
||||
name: "Acme",
|
||||
slug: "acme",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "dept-1",
|
||||
name: "Planning",
|
||||
slug: "planning",
|
||||
status: "active",
|
||||
type: "ORGANIZATION",
|
||||
parentId: "company-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "team-1",
|
||||
name: "Platform",
|
||||
slug: "platform",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "dept-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "company-1",
|
||||
name: "Acme",
|
||||
slug: "acme",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "dept-1",
|
||||
name: "Planning",
|
||||
slug: "planning",
|
||||
status: "active",
|
||||
type: "ORGANIZATION",
|
||||
parentId: "company-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "team-1",
|
||||
name: "Platform",
|
||||
slug: "platform",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "dept-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 3,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -162,7 +191,6 @@ test.describe("Tenants Management", () => {
|
||||
.getByPlaceholder(/테넌트 이름 또는 슬러그 검색|search/i)
|
||||
.fill("team-1");
|
||||
await expect(page.locator("table")).toContainText("Platform");
|
||||
await expect(page.locator("table")).toContainText("Acme");
|
||||
|
||||
await page
|
||||
.getByPlaceholder(/테넌트 이름 또는 슬러그 검색|search/i)
|
||||
@@ -188,40 +216,55 @@ test.describe("Tenants Management", () => {
|
||||
return route.continue();
|
||||
}
|
||||
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: "company-1",
|
||||
name: "Acme",
|
||||
slug: "acme",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "dept-1",
|
||||
name: "Planning",
|
||||
slug: "planning",
|
||||
status: "active",
|
||||
type: "ORGANIZATION",
|
||||
parentId: "company-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "team-1",
|
||||
name: "Platform",
|
||||
slug: "platform",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "dept-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "company-1",
|
||||
name: "Acme",
|
||||
slug: "acme",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "dept-1",
|
||||
name: "Planning",
|
||||
slug: "planning",
|
||||
status: "active",
|
||||
type: "ORGANIZATION",
|
||||
parentId: "company-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: "team-1",
|
||||
name: "Platform",
|
||||
slug: "platform",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "dept-1",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 3,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -239,10 +282,14 @@ test.describe("Tenants Management", () => {
|
||||
);
|
||||
|
||||
await page.getByPlaceholder(/UUID|슬러그|slug/i).fill("team-1");
|
||||
await expect(page.locator("table")).toContainText("Platform");
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(page.locator("table")).toContainText("Platform", {
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(page.locator("table")).not.toContainText("Acme");
|
||||
|
||||
await page.getByPlaceholder(/UUID|슬러그|slug/i).fill("");
|
||||
await page.keyboard.press("Enter");
|
||||
await page
|
||||
.locator("tbody tr")
|
||||
.filter({ hasText: "Acme" })
|
||||
@@ -266,24 +313,37 @@ test.describe("Tenants Management", () => {
|
||||
}
|
||||
const url = new URL(route.request().url());
|
||||
const cursor = url.searchParams.get("cursor");
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
_requestCount += 1;
|
||||
|
||||
const items = Array.from({ length: 501 }, (_, index) => ({
|
||||
id: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
name: `Tenant ${String(index + 1).padStart(3, "0")}`,
|
||||
slug: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
if (!cursor) {
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: Array.from({ length: 500 }, (_, index) => ({
|
||||
id: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
name: `Tenant ${String(index + 1).padStart(3, "0")}`,
|
||||
slug: `tenant-${String(index + 1).padStart(3, "0")}`,
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
})),
|
||||
total: 501,
|
||||
items: filtered.slice(0, 500),
|
||||
total: filtered.length,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
nextCursor: "next-page",
|
||||
nextCursor: filtered.length > 500 ? "next-page" : undefined,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
@@ -291,18 +351,8 @@ test.describe("Tenants Management", () => {
|
||||
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "tenant-501",
|
||||
name: "Tenant 501",
|
||||
slug: "tenant-501",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 501,
|
||||
items: filtered.slice(500),
|
||||
total: filtered.length,
|
||||
limit: 500,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -322,9 +372,10 @@ test.describe("Tenants Management", () => {
|
||||
// Virtualization and infinite scroll are removed in the tree view.
|
||||
// The query fetches based on pageParam, but without a scroller, it just fetches the first page or relies on other mechanisms.
|
||||
// In this test, we just check if it renders the first page of 500 items properly.
|
||||
// With virtualization, only a few items are rendered
|
||||
await expect
|
||||
.poll(async () => page.locator("tbody tr").count())
|
||||
.toEqual(500);
|
||||
.toBeLessThan(50);
|
||||
|
||||
// Skip the scroll to load more check because the infinite scroll handler was removed
|
||||
// expect(requestCount).toBe(2);
|
||||
@@ -372,54 +423,68 @@ test.describe("Tenants Management", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
const items = [
|
||||
{
|
||||
id: "hanmac-family-id",
|
||||
slug: "hanmac-family",
|
||||
name: "한맥가족",
|
||||
status: "active",
|
||||
type: "COMPANY_GROUP",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-company-id",
|
||||
slug: "hanmac-company",
|
||||
name: "한맥기술",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
parentId: "hanmac-family-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-team-id",
|
||||
slug: "hanmac-team",
|
||||
name: "한맥팀",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "hanmac-company-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-tenant-id",
|
||||
slug: "external-tenant",
|
||||
name: "External Tenant",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-team-id",
|
||||
slug: "external-team",
|
||||
name: "External Team",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "external-tenant-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "hanmac-family-id",
|
||||
slug: "hanmac-family",
|
||||
name: "한맥가족",
|
||||
status: "active",
|
||||
type: "COMPANY_GROUP",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-company-id",
|
||||
slug: "hanmac-company",
|
||||
name: "한맥기술",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
parentId: "hanmac-family-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "hanmac-team-id",
|
||||
slug: "hanmac-team",
|
||||
name: "한맥팀",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "hanmac-company-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-tenant-id",
|
||||
slug: "external-tenant",
|
||||
name: "External Tenant",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
memberCount: 0,
|
||||
},
|
||||
{
|
||||
id: "external-team-id",
|
||||
slug: "external-team",
|
||||
name: "External Team",
|
||||
status: "active",
|
||||
type: "USER_GROUP",
|
||||
parentId: "external-tenant-id",
|
||||
memberCount: 0,
|
||||
},
|
||||
],
|
||||
total: 5,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -493,9 +558,30 @@ test.describe("Tenants Management", () => {
|
||||
];
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
if (route.request().method() !== "GET") {
|
||||
return route.continue();
|
||||
}
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
const headers = { "Access-Control-Allow-Origin": "*" };
|
||||
|
||||
let filtered = tenants;
|
||||
if (search) {
|
||||
filtered = tenants.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
|
||||
return route.fulfill({
|
||||
json: { items: tenants, total: tenants.length, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
});
|
||||
@@ -569,12 +655,23 @@ test.describe("Tenants Management", () => {
|
||||
|
||||
await page.route("**/api/v1/admin/tenants**", async (route) => {
|
||||
const method = route.request().method();
|
||||
const url = new URL(route.request().url());
|
||||
const search = url.searchParams.get("search")?.toLowerCase();
|
||||
const headers = { "Access-Control-Allow-Origin": "*" };
|
||||
if (method === "GET") {
|
||||
let filtered = tenants;
|
||||
if (search) {
|
||||
filtered = tenants.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: tenants,
|
||||
total: tenants.length,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -705,21 +802,33 @@ test.describe("Tenants Management", () => {
|
||||
}
|
||||
|
||||
if (method === "GET") {
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
const items = [
|
||||
{
|
||||
id: "tenant-alpha-id",
|
||||
name: "Tenant Alpha",
|
||||
slug: "tenant-alpha",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
domains: [],
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "tenant-alpha-id",
|
||||
name: "Tenant Alpha",
|
||||
slug: "tenant-alpha",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
domains: [],
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -846,21 +955,33 @@ test.describe("Tenants Management", () => {
|
||||
}
|
||||
|
||||
if (method === "GET") {
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
const items = [
|
||||
{
|
||||
id: "staging-existing-id",
|
||||
name: "Existing Parent",
|
||||
slug: "parent-local",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
domains: [],
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
id: "staging-existing-id",
|
||||
name: "Existing Parent",
|
||||
slug: "parent-local",
|
||||
status: "active",
|
||||
type: "COMPANY",
|
||||
domains: [],
|
||||
memberCount: 0,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
@@ -979,8 +1100,24 @@ test.describe("Tenants Management", () => {
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
} else {
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
let filtered = mockTenants;
|
||||
if (search) {
|
||||
filtered = mockTenants.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
await route.fulfill({
|
||||
json: { items: mockTenants, total: 2, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers: { "Access-Control-Allow-Origin": "*" },
|
||||
});
|
||||
}
|
||||
@@ -1051,8 +1188,24 @@ test.describe("Tenants Management", () => {
|
||||
if (url.includes(`/admin/tenants/${parentId}`)) {
|
||||
return route.fulfill({ json: mockTenants[0], headers });
|
||||
}
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
let filtered = mockTenants;
|
||||
if (search) {
|
||||
filtered = mockTenants.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: { items: mockTenants, total: 2, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
});
|
||||
@@ -1093,8 +1246,25 @@ test.describe("Tenants Management", () => {
|
||||
if (url.includes(`/admin/tenants/${tenantUuid}`)) {
|
||||
return route.fulfill({ json: tenant, headers });
|
||||
}
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
const items = [tenant];
|
||||
let filtered = items;
|
||||
if (search) {
|
||||
filtered = items.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: { items: [tenant], total: 1, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
});
|
||||
@@ -1152,8 +1322,24 @@ test.describe("Tenants Management", () => {
|
||||
if (url.includes("/admin/tenants/team-1")) {
|
||||
return route.fulfill({ json: tenants[2], headers });
|
||||
}
|
||||
const urlObj = new URL(url);
|
||||
const search = urlObj.searchParams.get("search")?.toLowerCase();
|
||||
let filtered = tenants;
|
||||
if (search) {
|
||||
filtered = tenants.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(search) ||
|
||||
i.slug.toLowerCase().includes(search) ||
|
||||
i.id.toLowerCase().includes(search),
|
||||
);
|
||||
}
|
||||
return route.fulfill({
|
||||
json: { items: tenants, total: tenants.length, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user