fix(update): 统一忽略更新配置到用户目录

This commit is contained in:
2026-04-15 11:44:18 +08:00
parent 51664619f5
commit 36f5d3831e
18 changed files with 486 additions and 104 deletions
@@ -69,10 +69,14 @@ const createStore = (
selectedTaskKeys,
snapshot,
filteredItems: computed(() => snapshot.value.items),
allSelected: computed(() => false),
someSelected: computed(() => selectedTaskKeys.value.size > 0),
bind: vi.fn(),
unbind: vi.fn(),
open: vi.fn(),
refresh: vi.fn(),
ignoreItem: vi.fn(),
unignoreItem: vi.fn(),
toggleSelection: vi.fn(),
getSelectedItems: vi.fn(() =>
snapshot.value.items.filter(
@@ -87,7 +91,7 @@ const createStore = (
};
describe("UpdateCenterModal", () => {
it("renders source tags, running state, warnings, migration marker, and close confirmation", () => {
it("renders source tags, running state, warnings, and migration marker", () => {
const store = createStore();
render(UpdateCenterModal, {
@@ -104,24 +108,6 @@ describe("UpdateCenterModal", () => {
expect(screen.getByText("更新过程中请勿关闭商店")).toBeTruthy();
expect(screen.getByText("下载中")).toBeTruthy();
expect(screen.getByText("42%")).toBeTruthy();
expect(screen.getByText(/确定关闭/)).toBeTruthy();
});
it("close confirmation exposes a confirm-close path", async () => {
const onConfirmClose = vi.fn();
const store = createStore();
render(UpdateCenterModal, {
props: {
show: true,
store,
onConfirmClose,
},
});
await fireEvent.click(screen.getByRole("button", { name: "确认关闭" }));
expect(onConfirmClose).toHaveBeenCalledTimes(1);
});
it("renders ignored items as disabled instead of normal selectable actions", () => {
@@ -148,7 +134,34 @@ describe("UpdateCenterModal", () => {
});
expect(screen.getByText("已忽略")).toBeTruthy();
expect(screen.getByRole("checkbox")).toBeDisabled();
expect(screen.getAllByRole("checkbox").at(-1)).toBeDisabled();
expect(screen.getByRole("button", { name: "取消忽略" })).toBeTruthy();
});
it("renders ignore action for normal items", () => {
const store = createStore({
items: [
createItem({
taskKey: "aptss:spark-weather",
packageName: "spark-weather",
displayName: "Spark Weather",
source: "aptss",
ignored: false,
}),
],
tasks: [],
warnings: [],
hasRunningTasks: false,
});
render(UpdateCenterModal, {
props: {
show: true,
store,
},
});
expect(screen.getByRole("button", { name: "忽略更新" })).toBeTruthy();
});
it("renders migration confirmation when requested", () => {
@@ -1,4 +1,5 @@
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { tmpdir } from "node:os";
@@ -6,7 +7,7 @@ import { describe, expect, it } from "vitest";
import type { UpdateCenterItem } from "../../../../electron/main/backend/update-center/types";
import {
LEGACY_IGNORE_CONFIG_PATH,
IGNORE_CONFIG_PATH,
applyIgnoredEntries,
createIgnoreKey,
loadIgnoredEntries,
@@ -15,9 +16,9 @@ import {
} from "../../../../electron/main/backend/update-center/ignore-config";
describe("update-center ignore config", () => {
it("round-trips the legacy package|version format", async () => {
expect(LEGACY_IGNORE_CONFIG_PATH).toBe(
"/etc/spark-store/ignored_apps.conf",
it("round-trips the package|version format at the user config path", async () => {
expect(IGNORE_CONFIG_PATH).toBe(
join(homedir(), ".config", "spark-store", "ignored_apps.conf"),
);
const entries = new Set([
+27 -4
View File
@@ -24,6 +24,8 @@ const createSnapshot = (overrides = {}) => ({
describe("updateCenter store", () => {
const open = vi.fn();
const refresh = vi.fn();
const ignore = vi.fn();
const unignore = vi.fn();
const start = vi.fn();
const onState = vi.fn();
const offState = vi.fn();
@@ -31,6 +33,8 @@ describe("updateCenter store", () => {
beforeEach(() => {
open.mockReset();
refresh.mockReset();
ignore.mockReset();
unignore.mockReset();
start.mockReset();
onState.mockReset();
offState.mockReset();
@@ -41,8 +45,8 @@ describe("updateCenter store", () => {
value: {
open,
refresh,
ignore: vi.fn(),
unignore: vi.fn(),
ignore,
unignore,
start,
cancel: vi.fn(),
getState: vi.fn(),
@@ -132,6 +136,25 @@ describe("updateCenter store", () => {
);
});
it("forwards ignore and unignore actions with the package and target version", async () => {
const snapshot = createSnapshot();
open.mockResolvedValue(snapshot);
const store = createUpdateCenterStore();
await store.open();
await store.ignoreItem("spark-weather", "2.0.0");
await store.unignoreItem("spark-weather", "2.0.0");
expect(ignore).toHaveBeenCalledWith({
packageName: "spark-weather",
newVersion: "2.0.0",
});
expect(unignore).toHaveBeenCalledWith({
packageName: "spark-weather",
newVersion: "2.0.0",
});
});
it("assigns update-center download ids from a separate range", async () => {
downloads.value = [
{
@@ -178,8 +201,8 @@ describe("updateCenter store", () => {
store.requestClose();
expect(store.isOpen.value).toBe(true);
expect(store.showCloseConfirm.value).toBe(true);
expect(store.isOpen.value).toBe(false);
expect(store.showCloseConfirm.value).toBe(false);
});
it("applies pushed snapshots from the main process", () => {
+2
View File
@@ -48,6 +48,8 @@
:tasks="store.snapshot.value.tasks"
:selected-task-keys="store.selectedTaskKeys.value"
@toggle-selection="emit('toggle-selection', $event)"
@ignore-item="store.ignoreItem"
@unignore-item="store.unignoreItem"
/>
</div>
@@ -63,6 +63,29 @@
</div>
</div>
<div class="flex justify-end">
<button
v-if="item.ignored === true"
type="button"
class="inline-flex items-center gap-2 rounded-2xl border border-slate-300/80 px-3 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
aria-label="取消忽略"
@click.stop="$emit('unignore-item')"
>
<i class="fas fa-rotate-left"></i>
取消忽略
</button>
<button
v-else
type="button"
class="inline-flex items-center gap-2 rounded-2xl border border-amber-300/80 px-3 py-2 text-sm font-semibold text-amber-700 transition hover:bg-amber-50 dark:border-amber-500/40 dark:text-amber-300 dark:hover:bg-amber-500/10"
aria-label="忽略更新"
@click.stop="$emit('ignore-item')"
>
<i class="fas fa-eye-slash"></i>
忽略更新
</button>
</div>
<div v-if="showProgress" class="space-y-2">
<div
class="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800"
@@ -96,6 +119,8 @@ const iconIndex = ref(0);
defineEmits<{
(e: "toggle-selection"): void;
(e: "ignore-item"): void;
(e: "unignore-item"): void;
}>();
const normalizeIconSrc = (icon: string): string => {
@@ -16,6 +16,10 @@
:task="taskMap.get(item.taskKey)"
:selected="selectedTaskKeys.has(item.taskKey)"
@toggle-selection="$emit('toggle-selection', item.taskKey)"
@ignore-item="$emit('ignore-item', item.packageName, item.newVersion)"
@unignore-item="
$emit('unignore-item', item.packageName, item.newVersion)
"
/>
</div>
</div>
@@ -39,6 +43,8 @@ const props = defineProps<{
defineEmits<{
(e: "toggle-selection", taskKey: string): void;
(e: "ignore-item", packageName: string, newVersion: string): void;
(e: "unignore-item", packageName: string, newVersion: string): void;
}>();
const taskMap = computed(() => {
+18
View File
@@ -30,6 +30,8 @@ export interface UpdateCenterStore {
unbind: () => void;
open: () => Promise<void>;
refresh: () => Promise<void>;
ignoreItem: (packageName: string, newVersion: string) => Promise<void>;
unignoreItem: (packageName: string, newVersion: string) => Promise<void>;
toggleSelection: (taskKey: string) => void;
toggleSelectAll: () => void;
getSelectedItems: () => UpdateCenterItem[];
@@ -139,6 +141,20 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
applySnapshot(nextSnapshot);
};
const ignoreItem = async (
packageName: string,
newVersion: string,
): Promise<void> => {
await window.updateCenter.ignore({ packageName, newVersion });
};
const unignoreItem = async (
packageName: string,
newVersion: string,
): Promise<void> => {
await window.updateCenter.unignore({ packageName, newVersion });
};
const toggleSelection = (taskKey: string): void => {
const item = snapshot.value.items.find(
(entry) => entry.taskKey === taskKey,
@@ -260,6 +276,8 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
unbind,
open,
refresh,
ignoreItem,
unignoreItem,
toggleSelection,
toggleSelectAll,
getSelectedItems,