fix(web): ссылка в ветке «задачи нет в зеркале» в .issue, тесты класса и подписи фильтра, withCurrent выше комментария (#29/O-10, T5.1, T5.4, #29)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6940da5a20
commit
a002812f12
@ -180,6 +180,13 @@ describe("карточка задачи", () => {
|
||||
await waitFor(() => expect(screen.queryByRole("alert")).toBeNull());
|
||||
});
|
||||
|
||||
it("O-10: ссылка в ветке «задачи нет в зеркале» стоит внутри .issue (цвет темы)", async () => {
|
||||
mockFetch((_m, url) => url.pathname === "/settings" ? json(200, settings) : json(404, { error: "not_found", detail: "issue" }));
|
||||
open("/issues/AURORA-1");
|
||||
const link = await screen.findByRole("link", { name: "к списку задач" });
|
||||
expect(link.closest(".issue")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("O-11: задача без команд — текст вместо пустой таблицы", async () => {
|
||||
mockFetch((_m, url) => url.pathname === "/settings" ? json(200, settings) : json(200, issue("AURORA-9001", { commands: [] })));
|
||||
open("/issues/AURORA-9001");
|
||||
|
||||
@ -17,7 +17,7 @@ export function IssuePage() {
|
||||
|
||||
if (issue.isPending) return <p className="hint" data-testid="page-issue">загрузка {key}…</p>;
|
||||
if (issue.error instanceof ApiError && issue.error.status === 404) {
|
||||
return <p data-testid="page-issue">Задачи {key} нет в зеркале хаба. <Link to="/issues">к списку задач</Link></p>;
|
||||
return <p className="issue" data-testid="page-issue">Задачи {key} нет в зеркале хаба. <Link to="/issues">к списку задач</Link></p>;
|
||||
}
|
||||
if (issue.isError && !issue.data) {
|
||||
return (
|
||||
|
||||
@ -10,6 +10,7 @@ import { renderWithProviders } from "../test/render.tsx";
|
||||
import { IssuesPage } from "./IssuesPage.tsx";
|
||||
|
||||
const SNAP = JSON.parse(readFileSync(join(import.meta.dirname, "..", "board", "__fixtures__", "hub-basic.board.json"), "utf8"));
|
||||
const deferred = <T,>() => { let resolve!: (v: T) => void; const promise = new Promise<T>((r) => { resolve = r; }); return { promise, resolve }; };
|
||||
const row = (n: number) => ({ ...SNAP.issues[1], key: `AURORA-${9000 + n}`, summary: `Задача ${n}`, fetched_at: "" });
|
||||
|
||||
function hub(pages: (params: URLSearchParams) => unknown[]) {
|
||||
@ -117,6 +118,23 @@ describe("список задач", () => {
|
||||
expect(sel).toHaveValue("AWPCA_Спринт 0");
|
||||
});
|
||||
|
||||
it("T5.1: значение есть на доске, снимок грузится — подпись без пометки и после загрузки", async () => {
|
||||
const board = deferred<Response>();
|
||||
mockFetch((_m, url) => {
|
||||
if (url.pathname === "/settings") return json(200, settings);
|
||||
if (url.pathname === "/board") return board.promise;
|
||||
return json(200, { issues: [row(1)] });
|
||||
});
|
||||
renderWithProviders(<IssuesPage />, { route: "/issues?sprint=AWPCA_Спринт 2", path: "/issues" });
|
||||
await screen.findByRole("link", { name: "AURORA-9001" });
|
||||
const sel = await screen.findByLabelText("Спринт");
|
||||
expect(within(sel).getByRole("option", { name: "AWPCA_Спринт 2" })).toBeInTheDocument();
|
||||
board.resolve(json(200, SNAP));
|
||||
await waitFor(() => expect(within(sel).getByRole("option", { name: "AWPCA_Спринт 2" })).toBeInTheDocument());
|
||||
expect(within(sel).queryByRole("option", { name: /нет на доске/ })).toBeNull();
|
||||
expect(sel).toHaveValue("AWPCA_Спринт 2");
|
||||
});
|
||||
|
||||
it("T13.1: пока снимок доски грузится, пометки «(нет на доске)» нет", async () => {
|
||||
mockFetch((_m, url) => {
|
||||
if (url.pathname === "/settings") return json(200, settings);
|
||||
|
||||
@ -9,13 +9,13 @@ export const epicNames = (snap: BoardSnapshot | undefined): Record<string, strin
|
||||
|
||||
export const dueOf = (i: { planned_end: string | null; duedate: string | null }) => (i.planned_end || i.duedate || "").slice(0, 10);
|
||||
|
||||
// варианты выпадающих списков фильтров: из снимка /board и словарей настроек, без новых маршрутов API
|
||||
// T13.1: значение фильтра из адреса, которого нет среди вариантов (закрытый спринт, ушедший исполнитель), видно в списке;
|
||||
// пока снимок доски не загружен, пометку не ставим (R-4)
|
||||
export const withCurrent = (opts: Opt[], value: string | undefined, loaded: boolean): Opt[] =>
|
||||
!value || opts.some((o) => o.value === value) ? opts
|
||||
: [{ value, label: loaded ? `${value} (нет на доске)` : value }, ...opts];
|
||||
|
||||
// варианты выпадающих списков фильтров: из снимка /board и словарей настроек, без новых маршрутов API
|
||||
export function filterOptions(snap: BoardSnapshot | undefined, s: HubSettings | undefined) {
|
||||
const issues = snap?.issues ?? [];
|
||||
const names = epicNames(snap);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user