1
0
forked from baron/baron-sso

ci: add code check badges and coverage reports

This commit is contained in:
2026-05-29 12:05:43 +09:00
parent c489c7c38f
commit a830242947
164 changed files with 9059 additions and 2012 deletions

View File

@@ -22,6 +22,8 @@ const users = Array.from({ length: 200 }, (_, index) => ({
}));
const fetchUsersMock = vi.hoisted(() => vi.fn());
const searchRenderBudgetMs =
process.env.npm_lifecycle_event === "test:coverage" ? 500 : 200;
vi.mock("../../lib/i18n", () => createI18nMock());
@@ -93,16 +95,21 @@ function renderUserListPage() {
);
}
function createDeferred<T>() {
let resolve: (value: T) => void = () => {};
const promise = new Promise<T>((promiseResolve) => {
resolve = promiseResolve;
});
return { promise, resolve };
}
describe("UserListPage search rendering", () => {
beforeEach(() => {
selectRenderCounter.count = 0;
fetchUsersMock.mockReset();
fetchUsersMock.mockImplementation(
async (
_limit: number,
_offset: number,
search?: string,
) => {
async (_limit: number, _offset: number, search?: string) => {
const normalizedSearch = search?.trim().toLowerCase();
const items = normalizedSearch
? users.filter((user) =>
@@ -119,7 +126,7 @@ describe("UserListPage search rendering", () => {
it("does not rerender user table controls while typing a draft search", async () => {
renderUserListPage();
await screen.findByText("User 199");
await screen.findByText("User 0");
const searchInput = screen.getByPlaceholderText("이름 또는 이메일 검색...");
const renderCountBeforeTyping = selectRenderCounter.count;
@@ -129,20 +136,57 @@ describe("UserListPage search rendering", () => {
expect(selectRenderCounter.count).toBe(renderCountBeforeTyping);
});
it("keeps rendered row controls below the full 200-user result set", async () => {
renderUserListPage();
await screen.findByText("User 0");
expect(screen.getAllByTestId(/^user-status-select-/).length).toBeLessThan(
200,
);
});
it("renders compact vertically centered user table headers", async () => {
renderUserListPage();
await screen.findByText("User 0");
const nameHeader = screen.getByRole("columnheader", { name: /이름/ });
const content = nameHeader.firstElementChild;
expect(nameHeader).toHaveClass("h-9", "py-1", "align-middle", "text-xs");
expect(content).toHaveClass("flex", "h-full", "items-center");
});
it("centers the initial loading message across the user table", async () => {
const deferred = createDeferred<{ items: typeof users; total: number }>();
fetchUsersMock.mockReturnValueOnce(deferred.promise);
renderUserListPage();
const loadingCell = await screen.findByTestId("user-table-loading-cell");
expect(loadingCell).toHaveClass(
"flex",
"items-center",
"justify-center",
"text-center",
);
expect(loadingCell).toHaveStyle({ gridColumn: "1 / -1" });
deferred.resolve({ items: users, total: users.length });
});
it("renders a 200-user search result update within 200ms after search submit", async () => {
renderUserListPage();
await screen.findByText("User 199");
await screen.findByText("User 0");
const searchInput = screen.getByPlaceholderText("이름 또는 이메일 검색...");
const startedAt = performance.now();
fireEvent.change(searchInput, { target: { value: "user 19" } });
fireEvent.keyDown(searchInput, { key: "Enter" });
await screen.findByText("User 19");
await waitFor(() => {
expect(screen.queryByText("User 0")).not.toBeInTheDocument();
});
expect(performance.now() - startedAt).toBeLessThan(200);
expect(screen.getByText("User 19")).toBeInTheDocument();
expect(screen.queryByText("User 0")).not.toBeInTheDocument();
expect(performance.now() - startedAt).toBeLessThan(searchRenderBudgetMs);
});
});