test: remove redundant frontend checks
Frontend verify / commits (pull_request) Successful in 28s
Frontend verify / security (pull_request) Successful in 12s
Frontend verify / verify (pull_request) Successful in 8m31s

This commit is contained in:
2026-09-28 21:04:27 +03:00
parent 64fe902192
commit 320ea5922a
5 changed files with 71 additions and 126 deletions
@@ -44,6 +44,71 @@ function summary(currency: string, coupon: string, dividend: string) {
};
}
describe("income membership validation", () => {
const entry: IncomeAnalytics["entries"][number] = {
accruedInterest: null,
category: "income",
commission: null,
description: "Купон",
figi: "FIGI",
id: "coupon-1",
instrumentKey: "income-group",
instrumentType: "bond",
instrumentUid: null,
logoName: null,
occurredAt: "2026-08-01T12:00:00Z",
payment: money("RUB", "10"),
price: null,
quantity: null,
ticker: "BOND",
type: "OPERATION_TYPE_COUPON",
};
const instrument: IncomeAnalytics["instruments"][number] = {
...summary("RUB", "10", "0"),
displayName: "BOND",
figi: "FIGI",
instrumentType: "bond",
instrumentUid: null,
key: entry.instrumentKey,
receiptCount: 1,
ticker: "BOND",
};
const valid: IncomeAnalytics = {
...analytics,
entries: [entry],
instruments: [instrument],
totalsByCurrency: [summary("RUB", "10", "0")],
};
it.each([
["missing key", undefined],
["empty key", ""],
["whitespace key", " "],
["non-string key", 12],
["unknown key", "not-in-breakdown"],
])("rejects an entry with %s", (_name, instrumentKey) => {
// Deliberately malformed API data exercises the runtime validation boundary.
const response = {
...valid,
entries: [{ ...entry, instrumentKey }],
} as unknown as IncomeAnalytics;
expect(toIncomeDisplayModel(response)).toBeNull();
});
it.each([
["duplicate instrument keys", { instruments: [instrument, instrument] }],
["currency mismatch", { entries: [{ ...entry, payment: money("USD", "10") }] }],
["receipt count mismatch", { instruments: [{ ...instrument, receiptCount: 2 }] }],
["missing entries", { entries: [] }],
["empty instrument key", { instruments: [{ ...instrument, key: "" }] }],
])("rejects %s", (_name, invalid) => {
expect(toIncomeDisplayModel({ ...valid, ...invalid })).toBeNull();
});
});
describe("income comparison display", () => {
it.each([
["25", "100", "+25 %"],
@@ -324,17 +324,11 @@ describe("AccountIncomePage", () => {
expect(within(panel).getAllByRole("listitem")).toHaveLength(2);
});
it.each([
["missing key", undefined],
["empty key", ""],
["whitespace key", " "],
["non-string key", 12],
["unknown key", "not-in-breakdown"],
])("rejects %s and recovers through retry", async (_name, instrumentKey) => {
it("rejects inconsistent analytics and recovers through retry", async () => {
let response = {
...analytics,
entries: [
{ ...analytics.entries[0], instrumentKey },
{ ...analytics.entries[0], instrumentKey: "not-in-breakdown" },
analytics.entries[1],
],
};
@@ -356,51 +350,6 @@ describe("AccountIncomePage", () => {
).toBeInTheDocument();
});
it.each([
[
"duplicate instrument keys",
{
...analytics,
instruments: [...analytics.instruments, analytics.instruments[0]],
},
],
[
"currency mismatch",
{
...analytics,
entries: [
{ ...analytics.entries[0], payment: money("USD", "10") },
analytics.entries[1],
],
},
],
[
"receipt count mismatch",
{
...analytics,
instruments: [{ ...analytics.instruments[0], receiptCount: 3 }],
},
],
["missing entries", { ...analytics, entries: [] }],
[
"empty instrument key",
{ ...analytics, instruments: [{ ...analytics.instruments[0], key: "" }] },
],
])("shows a local error for %s", async (_name, response) => {
server.use(
http.get("*/api/v1/brokerage-accounts/account-1/income-analytics", () =>
HttpResponse.json(resource(response)),
),
);
renderPage();
expect(
await screen.findByRole("button", { name: "Повторить" }),
).toBeInTheDocument();
expect(
screen.queryByRole("region", { name: "Выплаты" }),
).not.toBeInTheDocument();
});
it("accepts an empty analytics response", async () => {
server.use(
http.get("*/api/v1/brokerage-accounts/account-1/income-analytics", () =>
@@ -870,8 +819,6 @@ describe("income period and snapshot boundary", () => {
expect(
screen.getByRole("heading", { name: "Выплаты" }),
).toBeInTheDocument();
expect(screen.queryByRole("heading", { level: 3 })).not.toBeInTheDocument();
expect(screen.queryByRole("heading", { level: 4 })).not.toBeInTheDocument();
expect(screen.queryByText("RUB")).not.toBeInTheDocument();
expect(screen.queryByText("USD")).not.toBeInTheDocument();
const cards = screen.getAllByRole("listitem");
@@ -881,7 +828,6 @@ describe("income period and snapshot boundary", () => {
expect(cards[2]).toHaveTextContent("−3 ₽");
expect(screen.queryByText(/Итого:|Выплат:/)).not.toBeInTheDocument();
expect(screen.queryByRole("button")).not.toBeInTheDocument();
expect(screen.getAllByText("Купон")).toHaveLength(6);
});
});
@@ -302,7 +302,9 @@ describe("AccountPage states", () => {
expect(
screen.queryByRole("button", { name: "Повторить" }),
).not.toBeInTheDocument();
expect(screen.getByRole("link")).toHaveAttribute(
expect(screen.getByRole("link", {
name: code === "NOT_FOUND" ? "К счетам" : "Открыть настройки T-Bank",
})).toHaveAttribute(
"href",
code === "NOT_FOUND" ? "/accounts" : "/settings",
);
@@ -427,34 +429,13 @@ describe("AccountPage states", () => {
},
);
it("renders loading and empty states", () => {
renderPage(<AccountPageLoading />);
expect(screen.getByRole("status")).toBeInTheDocument();
it("renders the empty state", () => {
renderPage(<AccountPageEmpty {...emptyProps()} />);
expect(
screen.getByRole("heading", { name: "Портфель пока пуст" }),
).toBeInTheDocument();
});
it("renders allowed error actions", () => {
const retry = vi.fn();
renderPage(<AccountPageError code="NOT_FOUND" retry={retry} />);
expect(screen.getByRole("link", { name: "К счетам" })).toHaveAttribute(
"href",
"/accounts",
);
renderPage(<AccountPageError code="TBANK_NOT_CONNECTED" retry={retry} />);
expect(
screen.getByRole("link", { name: "Открыть настройки T-Bank" }),
).toHaveAttribute("href", "/settings");
renderPage(<AccountPageError code="TBANK_UNAVAILABLE" retry={retry} />);
fireEvent.click(screen.getByRole("button", { name: "Повторить" }));
expect(retry).toHaveBeenCalledOnce();
});
it("renders a stale portfolio in upstream order with nullable values hidden", () => {
renderPage(<AccountPageLoaded {...loadedProps()} />);