refactor: simplify errors and focus tests
This commit is contained in:
+31
-15
@@ -292,31 +292,47 @@ describe("TbankConnectionSection", () => {
|
||||
expect(client.getQueryData(operationsQueryKey)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("preserves status and financial cache when a connection update fails", async () => {
|
||||
server.use(
|
||||
http.put("*/api/v1/tbank-connection", () =>
|
||||
HttpResponse.json({ code: "TBANK_UNAVAILABLE" }, { status: 503 }),
|
||||
),
|
||||
);
|
||||
it.each([
|
||||
["INVALID_TBANK_TOKEN", "Проверьте token и его права."],
|
||||
["TBANK_UNAVAILABLE", "T-Bank временно недоступен. Повторите попытку позже."],
|
||||
[undefined, "Не удалось выполнить запрос. Попробуйте ещё раз."],
|
||||
])("preserves the draft and cache after %s, then retries successfully", async (code, message) => {
|
||||
const { promise: response, resolve: finishUpdate }
|
||||
= Promise.withResolvers<Response>();
|
||||
|
||||
const update = vi.fn()
|
||||
.mockImplementationOnce(() =>
|
||||
HttpResponse.json({ code }, { status: 503 }),
|
||||
)
|
||||
.mockImplementationOnce(() => response);
|
||||
|
||||
server.use(http.put("*/api/v1/tbank-connection", update));
|
||||
const client = seededClient();
|
||||
renderSection(client);
|
||||
|
||||
await screen.findByText("Не подключено");
|
||||
fireEvent.change(screen.getByLabelText("Token T-Bank"), {
|
||||
target: { value: "replacement-token" },
|
||||
});
|
||||
const token = screen.getByLabelText("Token T-Bank");
|
||||
fireEvent.change(token, { target: { value: "replacement-token" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Подключить" }));
|
||||
|
||||
await screen.findByText(
|
||||
"T-Bank временно недоступен. Повторите попытку позже.",
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Подключить" }),
|
||||
).toBeInTheDocument();
|
||||
await screen.findByText(message);
|
||||
expect(token).toHaveValue("replacement-token");
|
||||
expect(screen.getByText("Не подключено")).toBeVisible();
|
||||
expect(client.getQueryData(brokerageAccountsQueryKey)).toBeDefined();
|
||||
expect(client.getQueryData(portfolioQueryKey)).toEqual({
|
||||
source: "previous-connection",
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: "Подключить" }));
|
||||
await waitFor(() => expect(update).toHaveBeenCalledTimes(2));
|
||||
expect(screen.queryByText(message)).not.toBeInTheDocument();
|
||||
expect(token).toBeDisabled();
|
||||
expect(token).toHaveValue("replacement-token");
|
||||
finishUpdate(HttpResponse.json({
|
||||
connected: true, connectedAt: "2026-08-14T00:00:00Z",
|
||||
}));
|
||||
expect(await screen.findByText("Подключено с 14.08.2026")).toBeVisible();
|
||||
expect(token).toHaveValue("");
|
||||
expect(screen.queryByText(message)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps DELETE recovery inside the dialog and preserves cache until successful retry", async () => {
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ const TbankConnectionSectionLoaded: FC<TbankConnectionSectionLoadedProps> = (
|
||||
t,
|
||||
unavailableMessage,
|
||||
update,
|
||||
} = useTbankConnectionSectionLoadedVm(props);
|
||||
} = useTbankConnectionSectionLoadedVm(props.status);
|
||||
|
||||
return (
|
||||
<Stack mt="xl">
|
||||
@@ -92,7 +92,7 @@ const TbankConnectionSectionLoaded: FC<TbankConnectionSectionLoadedProps> = (
|
||||
error={errors.token?.message}
|
||||
/>
|
||||
|
||||
{requestError && (
|
||||
{update.isError && (
|
||||
<Alert color="red">
|
||||
{t(
|
||||
isApiErrorCode(requestError, ApiErrorCode.InvalidTbankToken)
|
||||
|
||||
+3
-18
@@ -14,26 +14,12 @@ import {
|
||||
commitTbankConnectionBoundary,
|
||||
type TbankConnectionStatus,
|
||||
} from "../../model/connection-boundary";
|
||||
enum ConnectionErrorCode {
|
||||
RequestFailed = "REQUEST_ERROR",
|
||||
}
|
||||
|
||||
type TbankConnectionSectionLoadedProps = {
|
||||
refreshError: boolean;
|
||||
refreshing: boolean;
|
||||
retry: () => void;
|
||||
status: TbankConnectionStatus;
|
||||
};
|
||||
|
||||
function useTbankConnectionSectionLoadedVm(
|
||||
props: TbankConnectionSectionLoadedProps,
|
||||
) {
|
||||
const { status } = props;
|
||||
function useTbankConnectionSectionLoadedVm(status: TbankConnectionStatus) {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const [isDeleteOpened, setIsDeleteOpened] = useState(false);
|
||||
const removalButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const [requestError, setRequestError] = useState<null | string>(null);
|
||||
|
||||
const {
|
||||
formState: { errors },
|
||||
@@ -43,8 +29,6 @@ function useTbankConnectionSectionLoadedVm(
|
||||
} = useForm<{ token: string }>();
|
||||
|
||||
const update = apiQueryClient.useMutation("put", "/api/v1/tbank-connection", {
|
||||
onError: error =>
|
||||
setRequestError(apiErrorCode(error) ?? ConnectionErrorCode.RequestFailed),
|
||||
onSuccess: (data) => {
|
||||
commitTbankConnectionBoundary(queryClient, data);
|
||||
reset();
|
||||
@@ -66,6 +50,8 @@ function useTbankConnectionSectionLoadedVm(
|
||||
},
|
||||
);
|
||||
|
||||
const requestError = apiErrorCode(update.error);
|
||||
|
||||
const unavailableMessage = isApiErrorCode(
|
||||
requestError,
|
||||
ApiErrorCode.TbankUnavailable,
|
||||
@@ -80,7 +66,6 @@ function useTbankConnectionSectionLoadedVm(
|
||||
}, [isDeleteOpened, remove.isError]);
|
||||
|
||||
const onSubmit = handleSubmit(({ token }) => {
|
||||
setRequestError(null);
|
||||
update.mutate({ body: { token } });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user