feat(web): /fields two-pane screen (grouped list + create form) + nav (no stubs left)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { http, HttpResponse } from "msw";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
|
||||
import { server } from "../test/server";
|
||||
import { renderApp } from "../test/render";
|
||||
import { FieldsPage } from "./fields-page";
|
||||
|
||||
function tree() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/fields" element={<FieldsPage />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
test("lists field definitions grouped, with an Other heading for ungrouped", async () => {
|
||||
renderApp(tree(), { route: "/fields" });
|
||||
expect(await screen.findByText("Inscription")).toBeInTheDocument();
|
||||
expect(screen.getByText(/^Description$/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/^Other$/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("creates a text field — posts the body and clears the key input", async () => {
|
||||
let body: { key: string; data_type: string } | undefined;
|
||||
|
||||
server.use(
|
||||
http.post("/api/admin/field-definitions", async ({ request }) => {
|
||||
body = (await request.json()) as { key: string; data_type: string };
|
||||
return HttpResponse.json({ key: "notes" }, { status: 201 });
|
||||
}),
|
||||
);
|
||||
renderApp(tree(), { route: "/fields" });
|
||||
|
||||
await userEvent.type(screen.getByLabelText(/^key$/i), "notes");
|
||||
await userEvent.type(screen.getByLabelText(/label \(en\)/i), "Notes");
|
||||
await userEvent.click(screen.getByRole("button", { name: /create field/i }));
|
||||
|
||||
await waitFor(() => expect(body?.key).toBe("notes"));
|
||||
expect(body?.data_type).toBe("text");
|
||||
await waitFor(() => expect(screen.getByLabelText(/^key$/i)).toHaveValue(""));
|
||||
});
|
||||
|
||||
test("selecting Term reveals the vocabulary picker and blocks submit until chosen", async () => {
|
||||
let posted = false;
|
||||
|
||||
server.use(
|
||||
http.post("/api/admin/field-definitions", () => {
|
||||
posted = true;
|
||||
return HttpResponse.json({ key: "x" }, { status: 201 });
|
||||
}),
|
||||
);
|
||||
renderApp(tree(), { route: "/fields" });
|
||||
|
||||
await userEvent.type(screen.getByLabelText(/^key$/i), "material");
|
||||
await userEvent.type(screen.getByLabelText(/label \(en\)/i), "Material");
|
||||
await userEvent.selectOptions(screen.getByLabelText(/^type$/i), "term");
|
||||
|
||||
const vocab = await screen.findByLabelText(/^vocabulary$/i);
|
||||
|
||||
expect(vocab).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: /create field/i }));
|
||||
expect(await screen.findByRole("alert")).toBeInTheDocument();
|
||||
expect(posted).toBe(false);
|
||||
|
||||
await userEvent.selectOptions(vocab, "v-material");
|
||||
await userEvent.click(screen.getByRole("button", { name: /create field/i }));
|
||||
await waitFor(() => expect(posted).toBe(true));
|
||||
});
|
||||
Reference in New Issue
Block a user