feat: add quiet reading with rare confirmations

Part of the quiet-user-feedback change; docs and specs land in their own repositories.
This commit is contained in:
2026-10-06 07:01:58 +03:00
parent c096fc70ef
commit 5b30bb802a
77 changed files with 1352 additions and 1259 deletions
@@ -1,6 +1,6 @@
import { MantineProvider } from "@mantine/core";
import { type QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { MemoryRouter } from "react-router";
import { describe, expect, it, vi } from "vitest";
@@ -58,10 +58,11 @@ describe("AccountsPage", () => {
}),
);
renderPage(createTestQueryClient());
fireEvent.click(await screen.findByRole("button", { name: "Повторить" }));
expect(await screen.findByText(account.name)).toBeVisible();
expect(await screen.findByText(account.name, undefined, {
timeout: 4000,
})).toBeVisible();
expect(modes).toEqual([null, "observe"]);
expect(modes).toEqual([null, null]);
});
it.each([
["loading", AccountsPageLoading, "status"],
@@ -81,17 +82,16 @@ describe("AccountsPage", () => {
).toBeInTheDocument();
});
it("routes connection errors to settings and keeps retry available", () => {
const retry = vi.fn();
it("routes connection errors to settings without technical retry", () => {
render(
<MantineProvider>
<MemoryRouter>
<AccountsPageError code="TBANK_UNAVAILABLE" retry={retry} />
<AccountsPageError code="TBANK_NOT_CONNECTED" />
</MemoryRouter>
</MantineProvider>,
);
fireEvent.click(screen.getByRole("button", { name: "Повторить" }));
expect(retry).toHaveBeenCalledOnce();
expect(screen.queryByRole("button", { name: "Повторить" }))
.not.toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Открыть настройки T-Bank" }),
).toHaveAttribute("href", "/settings");
@@ -123,14 +123,13 @@ describe("AccountsPage", () => {
await waitFor(() => expect(requestCount).toBe(2));
expect(screen.getByText(account.name)).toBeInTheDocument();
expect(
screen.getByRole("status", { name: "Обновляем данные" }),
).toBeInTheDocument();
expect(screen.queryByRole("status", { name: "Обновляем данные" }))
.not.toBeInTheDocument();
resolveRefresh?.(HttpResponse.json(resource([account])));
});
it("keeps the cached account list and offers retry after a refresh error", async () => {
it("keeps the cached account list quietly after a refresh error", async () => {
const requests: unknown[] = [];
server.use(
http.post("*/api/v1/tbank-sync", async ({ request }) => {
@@ -161,17 +160,15 @@ describe("AccountsPage", () => {
queryKey: brokerageAccountsQueryKey,
});
const warning = await screen.findByText(
"Не удалось обновить данные. Показаны последние полученные.",
);
expect(warning).toBeInTheDocument();
await waitFor(() => expect(requestCount).toBe(2));
expect(
screen.queryByText(
"Не удалось обновить данные. Показаны последние полученные.",
),
).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Повторить" }))
.not.toBeInTheDocument();
expect(screen.getByText(account.name)).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Повторить" }));
await waitFor(() => expect(requestCount).toBe(3));
await waitFor(() => expect(warning).not.toBeInTheDocument());
expect(screen.getByText(account.name)).toBeVisible();
expect(requests).toEqual([]);
});
@@ -194,11 +191,12 @@ describe("AccountsPage", () => {
queryKey: brokerageAccountsQueryKey,
});
await waitFor(() => expect(requestCount).toBe(2));
expect(
await screen.findByText(
screen.queryByText(
"Не удалось обновить данные. Показаны последние полученные.",
),
).toBeInTheDocument();
).not.toBeInTheDocument();
expect(screen.getByText("Нет инвестиционных счетов")).toBeInTheDocument();
});
});
@@ -226,7 +224,7 @@ it.each(["idle", "failed"] as const)(
.not.toBeInTheDocument();
if (state === "failed")
// eslint-disable-next-line vitest/no-conditional-expect -- Each fixed state has a different observable recovery contract.
expect(await screen.findByRole("button", { name: "Повторить" }))
expect(await screen.findByRole("heading", { name: "Пока недоступно" }))
.toBeVisible();
else
// eslint-disable-next-line vitest/no-conditional-expect -- Each fixed state has a different observable recovery contract.