feat(accounts): show current account check status
This commit is contained in:
+4
-15
@@ -989,19 +989,7 @@ function formatDebridLinkCountQuota(info: DebridLinkHostLimitInfo | null | undef
|
|||||||
return info.note || "Nicht verfügbar";
|
return info.note || "Nicht verfügbar";
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCheckedAgo(checkedAt: number): string {
|
function rotationEventText(ev: { event: string; cooldownSec?: number; next?: string; reason?: string }): string {
|
||||||
const deltaMs = Date.now() - checkedAt;
|
|
||||||
if (!Number.isFinite(deltaMs) || deltaMs < 0) return "gerade eben";
|
|
||||||
const min = Math.floor(deltaMs / 60000);
|
|
||||||
if (min < 1) return "gerade eben";
|
|
||||||
if (min < 60) return `vor ${min} Min`;
|
|
||||||
const hours = Math.floor(min / 60);
|
|
||||||
if (hours < 24) return `vor ${hours} Std`;
|
|
||||||
const days = Math.floor(hours / 24);
|
|
||||||
return `vor ${days} Tag${days === 1 ? "" : "en"}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rotationEventText(ev: { event: string; cooldownSec?: number; next?: string; reason?: string }): string {
|
|
||||||
const untilRestart = /bis zum Tagesreset gesperrt/i.test(ev.reason || "");
|
const untilRestart = /bis zum Tagesreset gesperrt/i.test(ev.reason || "");
|
||||||
switch (ev.event) {
|
switch (ev.event) {
|
||||||
case "OK": return "erfolgreich";
|
case "OK": return "erfolgreich";
|
||||||
@@ -5058,7 +5046,7 @@ export function App(): ReactElement {
|
|||||||
: null;
|
: null;
|
||||||
const accountSources = useMemo<AccountRowSource[]>(() => accountRows.map((row) => {
|
const accountSources = useMemo<AccountRowSource[]>(() => accountRows.map((row) => {
|
||||||
const checkedStatus = row.accountId ? snapshot.settings.debridAccountStatuses?.[row.accountId] : undefined;
|
const checkedStatus = row.accountId ? snapshot.settings.debridAccountStatuses?.[row.accountId] : undefined;
|
||||||
const state: AccountRowSource["status"]["state"] = resolveAccountStatusState(row.disabled, checkedStatus);
|
const state: AccountRowSource["status"]["state"] = resolveAccountStatusState(row.disabled, checkedStatus, runtimeNow);
|
||||||
return {
|
return {
|
||||||
identityId: row.accountId || row.rowKey,
|
identityId: row.accountId || row.rowKey,
|
||||||
service: row.entry.service,
|
service: row.entry.service,
|
||||||
@@ -5070,6 +5058,7 @@ export function App(): ReactElement {
|
|||||||
state,
|
state,
|
||||||
message: checkedStatus?.message || row.entry.statusLabel,
|
message: checkedStatus?.message || row.entry.statusLabel,
|
||||||
premiumUntilMs: checkedStatus?.premiumUntilMs ?? null,
|
premiumUntilMs: checkedStatus?.premiumUntilMs ?? null,
|
||||||
|
checkedAt: checkedStatus?.checkedAt,
|
||||||
username: checkedStatus?.username,
|
username: checkedStatus?.username,
|
||||||
email: checkedStatus?.email
|
email: checkedStatus?.email
|
||||||
},
|
},
|
||||||
@@ -5079,7 +5068,7 @@ export function App(): ReactElement {
|
|||||||
credentialKind: row.credentialLabel.includes("API") ? "api-key" : row.credentialLabel.includes("•") ? "password" : "protected",
|
credentialKind: row.credentialLabel.includes("API") ? "api-key" : row.credentialLabel.includes("•") ? "password" : "protected",
|
||||||
canCheck: row.checkable
|
canCheck: row.checkable
|
||||||
};
|
};
|
||||||
}), [accountRows, snapshot.settings.debridAccountStatuses]);
|
}), [accountRows, runtimeNow, snapshot.settings.debridAccountStatuses]);
|
||||||
const selectedAccountViewIds = useMemo(() => accountRows
|
const selectedAccountViewIds = useMemo(() => accountRows
|
||||||
.filter((row) => selectedAccountRowKeys.has(row.rowKey))
|
.filter((row) => selectedAccountRowKeys.has(row.rowKey))
|
||||||
.map(accountRowViewId), [accountRows, selectedAccountRowKeys]);
|
.map(accountRowViewId), [accountRows, selectedAccountRowKeys]);
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ export function resolveAccountUsername(storedUsername: string, checkedEmail?: st
|
|||||||
|
|
||||||
export function resolveAccountStatusState(
|
export function resolveAccountStatusState(
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
checkedStatus?: { valid: boolean; isPremium: boolean }
|
checkedStatus?: { valid: boolean; isPremium: boolean; premiumUntilMs?: number | null },
|
||||||
|
nowMs: number = Date.now()
|
||||||
): "disabled" | "unchecked" | "invalid" | "free" | "premium" {
|
): "disabled" | "unchecked" | "invalid" | "free" | "premium" {
|
||||||
if (checkedStatus && !checkedStatus.valid) {
|
if (checkedStatus && !checkedStatus.valid) {
|
||||||
return "invalid";
|
return "invalid";
|
||||||
@@ -128,6 +129,13 @@ export function resolveAccountStatusState(
|
|||||||
if (!checkedStatus) {
|
if (!checkedStatus) {
|
||||||
return "unchecked";
|
return "unchecked";
|
||||||
}
|
}
|
||||||
|
if (checkedStatus.isPremium
|
||||||
|
&& typeof checkedStatus.premiumUntilMs === "number"
|
||||||
|
&& Number.isFinite(checkedStatus.premiumUntilMs)
|
||||||
|
&& checkedStatus.premiumUntilMs > 0
|
||||||
|
&& checkedStatus.premiumUntilMs <= nowMs) {
|
||||||
|
return "free";
|
||||||
|
}
|
||||||
return checkedStatus.isPremium ? "premium" : "free";
|
return checkedStatus.isPremium ? "premium" : "free";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ const pairs = [
|
|||||||
["Liste leeren", "Clear list"], ["Sitzung", "Session"], ["Gesamt", "Total"], ["Verbleibend", "Remaining"], ["Bereit", "Ready"], ["Download läuft", "Download running"], ["Wartet", "Waiting"], ["Offline", "Offline"],
|
["Liste leeren", "Clear list"], ["Sitzung", "Session"], ["Gesamt", "Total"], ["Verbleibend", "Remaining"], ["Bereit", "Ready"], ["Download läuft", "Download running"], ["Wartet", "Waiting"], ["Offline", "Offline"],
|
||||||
["Übersicht", "Overview"], ["Verwendungsregeln", "Usage rules"], ["Laufzeit", "Runtime"], ["Accountverwaltung", "Account management"], ["Accounts hinzufügen, prüfen und verwalten.", "Add, check and manage accounts."],
|
["Übersicht", "Overview"], ["Verwendungsregeln", "Usage rules"], ["Laufzeit", "Runtime"], ["Accountverwaltung", "Account management"], ["Accounts hinzufügen, prüfen und verwalten.", "Add, check and manage accounts."],
|
||||||
["Provider-Laufzeit", "Provider runtime"], ["Account-Laufzeit", "Account runtime"], ["Aktive Downloads", "Active downloads"], ["Erfolgsquote · Diese Sitzung", "Success rate · This session"], ["Zuletzt verwendet", "Last used"], ["Cooldown / Grund", "Cooldown / reason"],
|
["Provider-Laufzeit", "Provider runtime"], ["Account-Laufzeit", "Account runtime"], ["Aktive Downloads", "Active downloads"], ["Erfolgsquote · Diese Sitzung", "Success rate · This session"], ["Zuletzt verwendet", "Last used"], ["Cooldown / Grund", "Cooldown / reason"],
|
||||||
["Noch keine Accounts konfiguriert.", "No accounts configured yet."], ["Noch keine Laufzeitdaten verfügbar.", "No runtime data available yet."], ["Noch nicht in dieser Sitzung", "Not yet in this session"], ["Gerade eben", "Just now"], ["Prüfung", "Checking"], ["Tageslimit", "Daily limit"], ["Cooldown", "Cooldown"],
|
["Noch keine Accounts konfiguriert.", "No accounts configured yet."], ["Noch keine Laufzeitdaten verfügbar.", "No runtime data available yet."], ["Noch nicht in dieser Sitzung", "Not yet in this session"], ["Gerade eben", "Just now"], ["gerade eben", "just now"], ["Prüfung", "Checking"], ["Tageslimit", "Daily limit"], ["Cooldown", "Cooldown"],
|
||||||
["aktiver Download", "active download"], ["aktive Downloads", "active downloads"], ["heute", "today"], ["Account deaktiviert", "Account disabled"], ["Tageslimit erreicht", "Daily limit reached"], ["Anmeldung ungültig", "Invalid login"], ["Rate-Limit aktiv", "Rate limit active"], ["Traffic- oder Kontolimit erreicht", "Traffic or account limit reached"], ["Vorübergehender Cooldown", "Temporary cooldown"], ["Provider oder Link vorübergehend nicht verfügbar", "Provider or link temporarily unavailable"],
|
["aktiver Download", "active download"], ["aktive Downloads", "active downloads"], ["heute", "today"], ["Account deaktiviert", "Account disabled"], ["Tageslimit erreicht", "Daily limit reached"], ["Anmeldung ungültig", "Invalid login"], ["Rate-Limit aktiv", "Rate limit active"], ["Traffic- oder Kontolimit erreicht", "Traffic or account limit reached"], ["Vorübergehender Cooldown", "Temporary cooldown"], ["Provider oder Link vorübergehend nicht verfügbar", "Provider or link temporarily unavailable"],
|
||||||
["Accounts zum Herunterladen verwenden", "Use accounts for downloads"], ["Download-Traffic übrig", "Download traffic remaining"], ["Benutzername", "Username"], ["E-Mail", "Email"], ["Verfallsdatum", "Expiration date"], ["Passwort/Zugang", "Password/access"],
|
["Accounts zum Herunterladen verwenden", "Use accounts for downloads"], ["Download-Traffic übrig", "Download traffic remaining"], ["Benutzername", "Username"], ["E-Mail", "Email"], ["Verfallsdatum", "Expiration date"], ["Passwort/Zugang", "Password/access"],
|
||||||
["Account hinzufügen", "Add account"], ["Ausgewählte prüfen", "Check selected"], ["Ausgewählte entfernen", "Remove selected"], ["Aktivieren", "Enable"], ["Deaktivieren", "Disable"], ["Noch nicht geprüft", "Not checked yet"],
|
["Account hinzufügen", "Add account"], ["Ausgewählte prüfen", "Check selected"], ["Ausgewählte entfernen", "Remove selected"], ["Aktivieren", "Enable"], ["Deaktivieren", "Disable"], ["Noch nicht geprüft", "Not checked yet"], ["Geprüft", "Checked"],
|
||||||
["Aktiviert", "Enabled"], ["Aktionen", "Actions"], ["Deaktiviert", "Disabled"], ["Premium aktiv", "Premium active"], ["API-Key aktiv", "API key active"], ["API-Account", "API account"], ["API-Key", "API key"],
|
["Aktiviert", "Enabled"], ["Aktionen", "Actions"], ["Deaktiviert", "Disabled"], ["Premium aktiv", "Premium active"], ["API-Key aktiv", "API key active"], ["API-Account", "API account"], ["API-Key", "API key"],
|
||||||
["Ungültiger API-Key (nicht autorisiert)", "Invalid API key (not authorized)"], ["Free Account", "Free account"], ["Unbeschränkt", "Unlimited"], ["Keine Accounts eingerichtet", "No accounts configured"],
|
["Ungültiger API-Key (nicht autorisiert)", "Invalid API key (not authorized)"], ["Free Account", "Free account"], ["Unbeschränkt", "Unlimited"], ["Keine Accounts eingerichtet", "No accounts configured"],
|
||||||
["Ablauf, Ziel, Tonspur, Ablageform und Leistung.", "Workflow, destination, audio track, layout and performance."], ["Ziel und Ablauf", "Destination and workflow"], ["Entpacken nach", "Extract to"],
|
["Ablauf, Ziel, Tonspur, Ablageform und Leistung.", "Workflow, destination, audio track, layout and performance."], ["Ziel und Ablauf", "Destination and workflow"], ["Entpacken nach", "Extract to"],
|
||||||
|
|||||||
@@ -373,9 +373,10 @@ function AccountRow({
|
|||||||
<small>{row.mode}</small>
|
<small>{row.mode}</small>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="settings-account-status" role="cell">
|
<span className="settings-account-status" role="cell">
|
||||||
<span className={`settings-account-status-badge is-${row.status.tone}`}>{row.status.text}</span>
|
<span className={`settings-account-status-badge is-${row.status.tone}`}>{row.status.text}</span>
|
||||||
</span>
|
{row.status.checkedAgo ? <small className="settings-account-status-checked"><span>Geprüft</span> {row.status.checkedAgo}</small> : null}
|
||||||
|
</span>
|
||||||
<span className="settings-account-traffic" role="cell">{row.traffic}</span>
|
<span className="settings-account-traffic" role="cell">{row.traffic}</span>
|
||||||
{renderAccountIdentityCell({ className: "settings-account-username", label: "Benutzername", onCopy: actions.onCopyIdentity, value: row.username })}
|
{renderAccountIdentityCell({ className: "settings-account-username", label: "Benutzername", onCopy: actions.onCopyIdentity, value: row.username })}
|
||||||
{renderAccountIdentityCell({ className: "settings-account-email", label: "E-Mail", onCopy: actions.onCopyIdentity, value: row.email })}
|
{renderAccountIdentityCell({ className: "settings-account-email", label: "E-Mail", onCopy: actions.onCopyIdentity, value: row.email })}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ export interface AccountRowSource {
|
|||||||
state: AccountStatusSourceState;
|
state: AccountStatusSourceState;
|
||||||
message: string;
|
message: string;
|
||||||
premiumUntilMs: number | null;
|
premiumUntilMs: number | null;
|
||||||
|
checkedAt?: number;
|
||||||
username?: string;
|
username?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
};
|
};
|
||||||
@@ -161,6 +162,7 @@ export interface AccountRowViewModel {
|
|||||||
status: {
|
status: {
|
||||||
tone: AccountStatusTone;
|
tone: AccountStatusTone;
|
||||||
text: string;
|
text: string;
|
||||||
|
checkedAgo: string;
|
||||||
};
|
};
|
||||||
traffic: string;
|
traffic: string;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -634,6 +636,26 @@ function formatExpiry(premiumUntilMs: number | null): string {
|
|||||||
return new Intl.DateTimeFormat("de-DE").format(new Date(premiumUntilMs));
|
return new Intl.DateTimeFormat("de-DE").format(new Date(premiumUntilMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCheckedAgo(checkedAt: number | undefined, nowMs: number): string {
|
||||||
|
if (!checkedAt || !Number.isFinite(checkedAt) || checkedAt <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const deltaMs = Math.max(0, nowMs - checkedAt);
|
||||||
|
const minutes = Math.floor(deltaMs / 60_000);
|
||||||
|
if (minutes < 1) {
|
||||||
|
return "gerade eben";
|
||||||
|
}
|
||||||
|
if (minutes < 60) {
|
||||||
|
return `vor ${minutes} Min`;
|
||||||
|
}
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
if (hours < 24) {
|
||||||
|
return `vor ${hours} Std`;
|
||||||
|
}
|
||||||
|
const days = Math.floor(hours / 24);
|
||||||
|
return `vor ${days} Tag${days === 1 ? "" : "en"}`;
|
||||||
|
}
|
||||||
|
|
||||||
function projectStatus(source: AccountRowSource): { tone: AccountStatusTone; text: string } {
|
function projectStatus(source: AccountRowSource): { tone: AccountStatusTone; text: string } {
|
||||||
if (!source.enabled || source.status.state === "disabled") {
|
if (!source.enabled || source.status.state === "disabled") {
|
||||||
return { tone: "disabled", text: "Deaktiviert" };
|
return { tone: "disabled", text: "Deaktiviert" };
|
||||||
@@ -691,7 +713,7 @@ export function projectAccountRows(
|
|||||||
icon: ACCOUNT_SERVICE_ICONS[source.service],
|
icon: ACCOUNT_SERVICE_ICONS[source.service],
|
||||||
enabled: source.enabled,
|
enabled: source.enabled,
|
||||||
selected: selected.has(id),
|
selected: selected.has(id),
|
||||||
status,
|
status: { ...status, checkedAgo: formatCheckedAgo(source.status.checkedAt, nowMs) },
|
||||||
traffic: formatTraffic(source.dailyLimitBytes, source.dailyUsageBytes),
|
traffic: formatTraffic(source.dailyLimitBytes, source.dailyUsageBytes),
|
||||||
username: identity.username,
|
username: identity.username,
|
||||||
email: identity.email,
|
email: identity.email,
|
||||||
|
|||||||
@@ -723,6 +723,20 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-account-status {
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-account-status-checked {
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--ui-text-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-account-status-badge::before {
|
.settings-account-status-badge::before {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ describe("renderer localization", () => {
|
|||||||
expect(translateUiText("Link kopieren", "en")).toBe("Copy Link");
|
expect(translateUiText("Link kopieren", "en")).toBe("Copy Link");
|
||||||
expect(translateUiText("example.test Klicken zum Kopieren", "en")).toBe("Click to copy example.test");
|
expect(translateUiText("example.test Klicken zum Kopieren", "en")).toBe("Click to copy example.test");
|
||||||
expect(translateUiText("Klicken zum Kopieren", "en")).toBe("Click to copy");
|
expect(translateUiText("Klicken zum Kopieren", "en")).toBe("Click to copy");
|
||||||
|
expect(translateUiText("Geprüft", "en")).toBe("Checked");
|
||||||
|
expect(translateUiText("gerade eben", "en")).toBe("just now");
|
||||||
expect(translateUiText("Geschwindigkeit verschieben", "en")).toBe("Move Speed");
|
expect(translateUiText("Geschwindigkeit verschieben", "en")).toBe("Move Speed");
|
||||||
expect(translateUiText("Geschwindigkeit nach links verschieben", "en")).toBe("Move Speed left");
|
expect(translateUiText("Geschwindigkeit nach links verschieben", "en")).toBe("Move Speed left");
|
||||||
expect(translateUiText("Move Speed right", "de")).toBe("Geschwindigkeit nach rechts verschieben");
|
expect(translateUiText("Move Speed right", "de")).toBe("Geschwindigkeit nach rechts verschieben");
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ function accountSources(): AccountRowSource[] {
|
|||||||
state: "premium",
|
state: "premium",
|
||||||
message: "Premium aktiv",
|
message: "Premium aktiv",
|
||||||
premiumUntilMs: NOW + 7 * 24 * 60 * 60 * 1000,
|
premiumUntilMs: NOW + 7 * 24 * 60 * 60 * 1000,
|
||||||
|
checkedAt: NOW - 5 * 60 * 1000,
|
||||||
email: "verified@example.test"
|
email: "verified@example.test"
|
||||||
},
|
},
|
||||||
dailyLimitBytes: 10 * GIB,
|
dailyLimitBytes: 10 * GIB,
|
||||||
@@ -390,10 +391,42 @@ describe("settings model", () => {
|
|||||||
"Noch nicht geprüft",
|
"Noch nicht geprüft",
|
||||||
"Deaktiviert"
|
"Deaktiviert"
|
||||||
]);
|
]);
|
||||||
|
expect(rows[0].status.checkedAgo).toBe("vor 5 Min");
|
||||||
expect(JSON.stringify(rows)).not.toContain("test-password");
|
expect(JSON.stringify(rows)).not.toContain("test-password");
|
||||||
expect(JSON.stringify(rows)).not.toContain("test-token");
|
expect(JSON.stringify(rows)).not.toContain("test-token");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("marks elapsed premium access as expired instead of active", () => {
|
||||||
|
expect(resolveAccountStatusState(false, {
|
||||||
|
valid: true,
|
||||||
|
isPremium: true,
|
||||||
|
premiumUntilMs: NOW - 1
|
||||||
|
}, NOW)).toBe("free");
|
||||||
|
expect(resolveAccountStatusState(false, {
|
||||||
|
valid: true,
|
||||||
|
isPremium: true,
|
||||||
|
premiumUntilMs: NOW + 1
|
||||||
|
}, NOW)).toBe("premium");
|
||||||
|
expect(resolveAccountStatusState(false, {
|
||||||
|
valid: true,
|
||||||
|
isPremium: true,
|
||||||
|
premiumUntilMs: null
|
||||||
|
}, NOW)).toBe("premium");
|
||||||
|
|
||||||
|
const [row] = projectAccountRows([{
|
||||||
|
...accountSources()[0],
|
||||||
|
status: {
|
||||||
|
...accountSources()[0].status,
|
||||||
|
state: "free",
|
||||||
|
premiumUntilMs: NOW - 1,
|
||||||
|
checkedAt: NOW - 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
}], [], NOW);
|
||||||
|
|
||||||
|
expect(row.status).toEqual({ tone: "free", text: "Free Account", checkedAgo: "vor 1 Std" });
|
||||||
|
expect(row.premiumUntilMs).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("sorts positive premium expirations first and prunes vanished selections", () => {
|
it("sorts positive premium expirations first and prunes vanished selections", () => {
|
||||||
const rows = projectAccountRows(accountSources(), [], NOW);
|
const rows = projectAccountRows(accountSources(), [], NOW);
|
||||||
const sorted = sortAccountRows(rows);
|
const sorted = sortAccountRows(rows);
|
||||||
@@ -675,6 +708,14 @@ describe("account workspace", () => {
|
|||||||
expect(html.match(/data-sliding-selection-active="true"/g)).toHaveLength(1);
|
expect(html.match(/data-sliding-selection-active="true"/g)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows the last account check directly below the status", () => {
|
||||||
|
const html = renderToStaticMarkup(<AccountWorkspace actions={workspaceActions()} model={workspaceModel()} />);
|
||||||
|
|
||||||
|
expect(html).toContain("settings-account-status-checked");
|
||||||
|
expect(html).toContain("Geprüft");
|
||||||
|
expect(html).toContain("vor 5 Min");
|
||||||
|
});
|
||||||
|
|
||||||
it("shows a live provider and account overview without exposing raw failure details", () => {
|
it("shows a live provider and account overview without exposing raw failure details", () => {
|
||||||
const html = renderToStaticMarkup(
|
const html = renderToStaticMarkup(
|
||||||
<AccountWorkspace
|
<AccountWorkspace
|
||||||
@@ -1237,7 +1278,7 @@ describe("settings App integration", () => {
|
|||||||
|
|
||||||
it("keeps unchecked single accounts honest without a positive status", () => {
|
it("keeps unchecked single accounts honest without a positive status", () => {
|
||||||
const block = sourceBlock(appSource, "const accountSources", "const selectedAccountViewId");
|
const block = sourceBlock(appSource, "const accountSources", "const selectedAccountViewId");
|
||||||
expect(block).toContain("resolveAccountStatusState(row.disabled, checkedStatus)");
|
expect(block).toContain("resolveAccountStatusState(row.disabled, checkedStatus, runtimeNow)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stores only the stable account row id in context-menu state", () => {
|
it("stores only the stable account row id in context-menu state", () => {
|
||||||
@@ -1255,6 +1296,7 @@ describe("settings App integration", () => {
|
|||||||
|
|
||||||
describe("settings geometry", () => {
|
describe("settings geometry", () => {
|
||||||
it("uses central focus and semantic status text tokens", () => {
|
it("uses central focus and semantic status text tokens", () => {
|
||||||
|
expect(settingsCss).toContain(".settings-account-status-checked");
|
||||||
expect(settingsCss).toMatch(/\.settings-theme-option:focus-visible,[^{]*\.settings-account-picker-row:focus-visible\s*{[^}]*outline:\s*2px solid var\(--ui-focus\);/s);
|
expect(settingsCss).toMatch(/\.settings-theme-option:focus-visible,[^{]*\.settings-account-picker-row:focus-visible\s*{[^}]*outline:\s*2px solid var\(--ui-focus\);/s);
|
||||||
expect(settingsCss).toMatch(/\.settings-save-state\.is-clean,[^{]*\.settings-save-state\.is-saved\s*{[^}]*color:\s*var\(--ui-success-text\);/s);
|
expect(settingsCss).toMatch(/\.settings-save-state\.is-clean,[^{]*\.settings-save-state\.is-saved\s*{[^}]*color:\s*var\(--ui-success-text\);/s);
|
||||||
expect(settingsCss).toMatch(/\.settings-save-state\.is-dirty,[^{]*\.settings-save-state\.is-saving\s*{[^}]*color:\s*var\(--ui-warning-text\);/s);
|
expect(settingsCss).toMatch(/\.settings-save-state\.is-dirty,[^{]*\.settings-save-state\.is-saving\s*{[^}]*color:\s*var\(--ui-warning-text\);/s);
|
||||||
|
|||||||
Reference in New Issue
Block a user