mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
#3501 Phase 0: extract ProviderDetailPageClient + smoke test. Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
cf06adf2e3
commit
bb6d5a288a
@@ -6,6 +6,10 @@
|
||||
|
||||
## [3.8.22] — TBD
|
||||
|
||||
### ♻️ Code Quality
|
||||
|
||||
- **Provider-detail god-component decomposition — Phase 0** ([#3501]): introduced `ProviderDetailPageClient.tsx` and reduced `providers/[id]/page.tsx` to a thin 9-line route wrapper (was 12,882 LOC), following the repo's `*PageClient` convention. Added the first-ever smoke render test for the page (Hard Rule #8) as the safety net every later extraction phase is diffed against. Behavior unchanged; the `check-file-size` ratchet now tracks the extracted client. Foundation for Phases 1–6 (strangler-fig). Thanks @oyi77 for the parallel modularization effort in #3627.
|
||||
|
||||
---
|
||||
|
||||
## [3.8.21] — 2026-06-11
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2570,
|
||||
"src/app/(dashboard)/dashboard/health/page.tsx": 1091,
|
||||
"src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": 847,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/page.tsx": 12883,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 12883,
|
||||
"src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx": 906,
|
||||
"src/app/(dashboard)/dashboard/providers/page.tsx": 1925,
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1127,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,136 @@
|
||||
// @vitest-environment jsdom
|
||||
//
|
||||
// Phase 0 smoke test for Issue #3501 (strangler-fig decomposition of the
|
||||
// 12.8K-LOC providers/[id] god-component). Before any code is moved out of the
|
||||
// component, this proves the freshly-extracted ProviderDetailPageClient still
|
||||
// mounts and renders its loading shell without throwing — the safety net that
|
||||
// every later extraction phase (1..6) is diffed against. Hard Rule #8: the page
|
||||
// had ZERO tests before this; this is the first.
|
||||
import React, { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import ProviderDetailPageClient from "../ProviderDetailPageClient";
|
||||
|
||||
function getRequestPath(input: RequestInfo | URL) {
|
||||
return typeof input === "string" ? input : input instanceof URL ? input.pathname : input.url;
|
||||
}
|
||||
|
||||
function emptyJsonResponse() {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({}),
|
||||
text: async () => "",
|
||||
headers: { get: () => null },
|
||||
} as unknown as Response;
|
||||
}
|
||||
|
||||
const cleanupCallbacks: Array<() => void> = [];
|
||||
|
||||
function installLocalStorageStub() {
|
||||
const store = new Map<string, string>();
|
||||
vi.stubGlobal("localStorage", {
|
||||
getItem: (key: string) => store.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => void store.set(key, value),
|
||||
removeItem: (key: string) => void store.delete(key),
|
||||
clear: () => store.clear(),
|
||||
});
|
||||
}
|
||||
|
||||
// next/navigation — the client reads the route id via useParams() and never
|
||||
// receives props from the thin page.tsx wrapper, so this mock is what supplies it.
|
||||
vi.mock("next/navigation", () => ({
|
||||
useParams: () => ({ id: "openai" }),
|
||||
useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn(), refresh: vi.fn() }),
|
||||
usePathname: () => "/dashboard/providers/openai",
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
}));
|
||||
|
||||
vi.mock("next/link", () => ({
|
||||
default: ({ href, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
|
||||
<a href={String(href)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("next-intl", () => ({
|
||||
// Echo the key back so assertions don't depend on a full message catalog.
|
||||
useTranslations: (namespace?: string) => (key: string) => (namespace ? `${namespace}.${key}` : key),
|
||||
}));
|
||||
|
||||
function renderProviderPage() {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
let mounted = true;
|
||||
|
||||
act(() => {
|
||||
root.render(<ProviderDetailPageClient />);
|
||||
});
|
||||
|
||||
const unmount = () => {
|
||||
if (!mounted) return;
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
mounted = false;
|
||||
};
|
||||
cleanupCallbacks.push(unmount);
|
||||
return { container, unmount };
|
||||
}
|
||||
|
||||
describe("ProviderDetailPageClient (Phase 0 smoke)", () => {
|
||||
const fetchMock = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset();
|
||||
fetchMock.mockImplementation(() => Promise.resolve(emptyJsonResponse()));
|
||||
(
|
||||
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
||||
).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
vi.stubGlobal(
|
||||
"matchMedia",
|
||||
vi.fn(() => ({
|
||||
matches: false,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
}))
|
||||
);
|
||||
installLocalStorageStub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
while (cleanupCallbacks.length > 0) cleanupCallbacks.pop()?.();
|
||||
document.body.innerHTML = "";
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("mounts and renders the loading shell without throwing", () => {
|
||||
const { container } = renderProviderPage();
|
||||
// Mount succeeded (no throw) and produced DOM — the loading skeleton renders
|
||||
// synchronously while the on-mount fetches are still pending.
|
||||
expect(container.childNodes.length).toBeGreaterThan(0);
|
||||
expect(container.querySelector("*")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("kicks off provider data loading on mount", async () => {
|
||||
renderProviderPage();
|
||||
// Let the mount effects schedule their fetches.
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalled();
|
||||
const paths = fetchMock.mock.calls.map(([input]) => getRequestPath(input as RequestInfo | URL));
|
||||
// The provider id from useParams() drives the connection/provider lookups.
|
||||
expect(paths.some((p) => p.includes("openai") || p.includes("/api/"))).toBe(true);
|
||||
});
|
||||
|
||||
it("unmounts cleanly during the initial load", () => {
|
||||
const { unmount } = renderProviderPage();
|
||||
expect(() => unmount()).not.toThrow();
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ export default defineConfig({
|
||||
include: [
|
||||
"src/app/**/dashboard/cache/__tests__/**/*.test.tsx",
|
||||
"src/app/**/dashboard/endpoint/__tests__/**/*.test.tsx",
|
||||
"src/app/**/dashboard/providers/**/__tests__/**/*.test.tsx",
|
||||
"src/app/**/dashboard/webhooks/__tests__/**/*.test.tsx",
|
||||
"src/shared/hooks/__tests__/**/*.test.tsx",
|
||||
"src/lib/memory/__tests__/**/*.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user