@@ -0,0 +1,296 @@
|
||||
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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user