Files
ProxyWarden/src/app/components/ComponentPackageStatus.test.tsx
T
dokril efda8eb98f
CI / Windows baseline (push) Canceled after 0s
Release v2.0.0
2026-09-10 20:59:52 +03:00

297 lines
11 KiB
TypeScript

import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import type { ComponentPackageStatus as ComponentPackageStatusDto } from "../../domain/types";
import {
ComponentPackageStatus,
ComponentPackageStatusLoading,
ComponentPackageStatusUnavailable,
isNumericVersionNewer,
offlineUpdateDisabledReason,
} from "./ComponentPackageStatus";
function packageStatus(
overrides: Partial<ComponentPackageStatusDto> = {},
): ComponentPackageStatusDto {
return {
componentId: "proxifyre",
installedVersion: "2.2.1",
bundledVersion: "2.2.1",
availableOfflineVersion: "2.2.1",
latestKnownVersion: null,
lastCheckedAt: null,
freshness: "never_checked",
updateState: "unknown_offline",
installSource: "bundled",
offlinePackageSource: "bundled",
canInstallOffline: true,
offlineUnavailableReason: null,
canDownload: false,
...overrides,
};
}
function renderStatus(
status: ComponentPackageStatusDto,
uacCancelled = false,
updateBlockedReason: string | null = null,
externalBusyReason: string | null = null,
) {
return renderToStaticMarkup(
<ComponentPackageStatus
status={status}
busyAction={null}
updateBlockedReason={updateBlockedReason}
externalBusyReason={externalBusyReason}
uacCancelled={uacCancelled}
onCheck={() => undefined}
onDownload={() => undefined}
onUpdate={() => undefined}
/>,
);
}
function openingButtonTag(markup: string, label: string) {
const labelIndex = markup.indexOf(label);
const start = markup.lastIndexOf("<button", labelIndex);
return markup.slice(start, markup.indexOf(">", start) + 1);
}
describe("ComponentPackageStatus", () => {
it("compares only numeric versions for offline update", () => {
expect(isNumericVersionNewer("2.10.0", "2.9.9")).toBe(true);
expect(isNumericVersionNewer("2.2.1", "2.2.1.0")).toBe(false);
expect(isNumericVersionNewer("2.2.0", "2.2.1")).toBe(false);
expect(isNumericVersionNewer("v2.3.0", "2.2.1")).toBe(false);
});
it("enables offline update only for a newer verified local version", () => {
const enabledStatus = packageStatus({
availableOfflineVersion: "2.3.0",
offlinePackageSource: "cache",
});
const enabledMarkup = renderStatus(enabledStatus);
const disabledMarkup = renderStatus(packageStatus());
expect(offlineUpdateDisabledReason(enabledStatus)).toBeNull();
expect(openingButtonTag(enabledMarkup, "Обновить компонент")).not.toContain(
"disabled",
);
expect(enabledMarkup).toContain("повторно проверяется в сети");
expect(openingButtonTag(disabledMarkup, "Обновить компонент")).toContain(
"disabled",
);
});
it("does not offer the cache-only update action for a bundled package", () => {
const markup = renderStatus(
packageStatus({
updateState: "update_available",
installedVersion: "2.2.1",
availableOfflineVersion: "2.4.0",
offlinePackageSource: "bundled",
}),
);
expect(markup).toContain("отдельно скачанного и проверенного пакета");
expect(markup).toContain("Встроенная версия новее");
expect(openingButtonTag(markup, "Обновить компонент")).toContain(
"disabled",
);
});
it("shows local truth without claiming an offline update", () => {
const markup = renderStatus(packageStatus());
expect(markup).toContain("Только локальные данные");
expect(markup).toContain("это не означает, что компонент устарел");
expect(markup).toContain("Установлена");
expect(markup).toContain("Встроена");
expect(markup).toContain("Последняя известная");
expect(markup.match(/<button/g)).toHaveLength(3);
expect(markup).not.toContain(">Установить<");
expect(markup).not.toContain(">Запустить<");
expect(markup).not.toContain(">Остановить<");
});
it.each([
["current", "Актуально", "новая версия не требуется"],
["update_available", "Есть обновление", "доступна для скачивания"],
["check_stale", "Проверка устарела", "стоит повторить"],
] as const)("renders the %s package state", (updateState, label, summary) => {
const markup = renderStatus(
packageStatus({
updateState,
canDownload: updateState === "update_available",
}),
);
expect(markup).toContain(label);
expect(markup).toContain(summary);
});
it("describes an already-downloaded update as local", () => {
const markup = renderStatus(
packageStatus({
updateState: "update_available",
installedVersion: "2.2.1",
latestKnownVersion: "2.4.0",
availableOfflineVersion: "2.4.0",
offlinePackageSource: "cache",
canDownload: false,
}),
);
expect(markup).toContain("сохранено локально");
expect(markup).toContain("уже сохранено локально");
expect(markup).not.toContain("Сначала выполни явную проверку");
});
it("does not call an unverified remote version downloadable", () => {
const markup = renderStatus(
packageStatus({
updateState: "update_available",
latestKnownVersion: "2.4.0",
availableOfflineVersion: "2.2.1",
canDownload: false,
}),
);
expect(markup).toContain("независимая проверка пакета недоступна");
expect(markup).toContain("Скачивание заблокировано");
expect(markup).not.toContain("доступна для скачивания");
});
it("keeps a verified local update usable when observed latest differs", () => {
const markup = renderStatus(
packageStatus({
updateState: "update_available",
installedVersion: "2.2.1",
availableOfflineVersion: "2.4.0",
latestKnownVersion: "2.5.0",
offlinePackageSource: "cache",
canInstallOffline: true,
canDownload: false,
}),
);
expect(markup).toContain("Проверенное локальное обновление готово");
expect(markup).toContain("обнаруженная в сети версия требует");
expect(openingButtonTag(markup, "Обновить компонент")).not.toContain(
"disabled",
);
});
it("describes both verified local and downloadable newer updates truthfully", () => {
const markup = renderStatus(
packageStatus({
updateState: "update_available",
installedVersion: "2.2.1",
availableOfflineVersion: "2.4.0",
latestKnownVersion: "2.5.0",
offlinePackageSource: "cache",
canInstallOffline: true,
canDownload: true,
}),
);
expect(markup).toContain("Проверенное локальное обновление готово");
expect(markup).toContain("более новая проверенная версия доступна");
expect(markup).not.toContain("требует отдельной проверки");
expect(openingButtonTag(markup, "Скачать обновление")).not.toContain(
"disabled",
);
expect(openingButtonTag(markup, "Обновить компонент")).not.toContain(
"disabled",
);
});
it("does not claim the network was skipped after a fresh incomparable check", () => {
const markup = renderStatus(
packageStatus({
installedVersion: null,
latestKnownVersion: "2.4.0",
lastCheckedAt: 1_788_000_000,
freshness: "fresh",
updateState: "unknown_offline",
}),
);
expect(markup).toContain("Проверка выполнена");
expect(markup).toContain("нельзя подтвердить");
expect(markup).not.toContain("Сеть не проверялась");
});
it("does not render path-like or fingerprint-like version values", () => {
const fingerprint = "a".repeat(64);
const markup = renderStatus(
packageStatus({
installedVersion: "C:\\private\\ProxiFyre.exe",
bundledVersion: fingerprint,
latestKnownVersion: "https://secret.example/version",
}),
);
expect(markup).not.toContain("private");
expect(markup).not.toContain(fingerprint);
expect(markup).not.toContain("secret.example");
});
it("presents UAC cancellation as neutral preserved state", () => {
const markup = renderStatus(packageStatus(), true);
expect(markup).toContain("Запрос прав администратора отменён");
expect(markup).toContain("Данные не изменены");
expect(markup).not.toContain('class="error"');
});
it("blocks component mutation while cutover is nonterminal", () => {
const markup = renderStatus(
packageStatus({
installedVersion: "2.2.1",
availableOfflineVersion: "2.4.0",
canInstallOffline: true,
}),
false,
"Сначала заверши перенос компонента.",
);
expect(markup).toContain("Сначала заверши перенос компонента");
expect(openingButtonTag(markup, "Обновить компонент")).toContain(
"disabled",
);
});
it("blocks every package action while another lifecycle action runs", () => {
const markup = renderStatus(
packageStatus({ availableOfflineVersion: "2.4.0" }),
false,
null,
"Дождись завершения операции со службой.",
);
expect(markup.match(/<button[^>]*disabled=""/g)).toHaveLength(3);
expect(markup).toContain("Дождись завершения операции со службой");
});
it("keeps local loading and failure states visible without actions", () => {
const loading = renderToStaticMarkup(
<ComponentPackageStatusLoading componentId="proxifyre" />,
);
const failed = renderToStaticMarkup(
<ComponentPackageStatusUnavailable
componentId="proxifyre"
error={{
code: "component_package_status_failed",
message: "Локальный каталог временно недоступен.",
details: [],
}}
/>,
);
expect(loading).toContain("Читаю только локальный каталог");
expect(failed).toContain("Установка и обновление оставлены");
expect(failed).toContain("Локальный каталог временно недоступен");
expect(`${loading}${failed}`).not.toContain("<button");
});
});