mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-05-30 01:31:06 +08:00
fix(update-center): load aptss updates reliably
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/vue";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import App from "@/App.vue";
|
||||
|
||||
const invoke = vi.fn();
|
||||
const updateCenterOpen = vi.fn();
|
||||
|
||||
vi.mock("axios", () => {
|
||||
const get = vi.fn(async () => ({ data: [] }));
|
||||
|
||||
return {
|
||||
default: {
|
||||
create: () => ({ get }),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("App update center runtime", () => {
|
||||
beforeEach(() => {
|
||||
invoke.mockReset();
|
||||
updateCenterOpen.mockReset();
|
||||
|
||||
invoke.mockImplementation(async (channel: string) => {
|
||||
if (channel === "get-store-filter") return "both";
|
||||
if (channel === "check-spark-available") return true;
|
||||
if (channel === "check-apm-available") return true;
|
||||
if (channel === "get-app-version") return "5.0.0";
|
||||
return [];
|
||||
});
|
||||
|
||||
Object.assign(window.ipcRenderer, {
|
||||
invoke,
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
send: vi.fn(),
|
||||
});
|
||||
|
||||
Object.assign(window, {
|
||||
updateCenter: {
|
||||
open: updateCenterOpen.mockResolvedValue({
|
||||
items: [],
|
||||
tasks: [],
|
||||
warnings: [],
|
||||
hasRunningTasks: false,
|
||||
}),
|
||||
refresh: vi.fn(),
|
||||
ignore: vi.fn(),
|
||||
unignore: vi.fn(),
|
||||
start: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
getState: vi.fn(),
|
||||
onState: vi.fn(),
|
||||
offState: vi.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
window.apm_store.arch = "amd64";
|
||||
|
||||
vi.stubGlobal(
|
||||
"matchMedia",
|
||||
vi.fn(() => ({
|
||||
matches: false,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
it("opens update center with an empty snapshot without throwing", async () => {
|
||||
render(App);
|
||||
|
||||
await fireEvent.click(await screen.findByText("软件更新"));
|
||||
|
||||
expect(updateCenterOpen).toHaveBeenCalledWith("both");
|
||||
expect(await screen.findByText("暂无可展示的更新任务")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/vue";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import App from "@/App.vue";
|
||||
|
||||
const invoke = vi.fn();
|
||||
const open = vi.fn();
|
||||
|
||||
vi.mock("axios", () => {
|
||||
const get = vi.fn(async (url: string) => {
|
||||
if (url.includes("categories.json")) {
|
||||
return { data: {} };
|
||||
}
|
||||
|
||||
if (url.includes("homelinks.json") || url.includes("homelist.json")) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
if (url.includes("applist.json")) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
return { data: [] };
|
||||
});
|
||||
|
||||
return {
|
||||
default: {
|
||||
create: () => ({ get }),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/modules/updateCenter", () => ({
|
||||
createUpdateCenterStore: () => ({
|
||||
isOpen: { value: false },
|
||||
showCloseConfirm: { value: false },
|
||||
showMigrationConfirm: { value: false },
|
||||
searchQuery: { value: "" },
|
||||
selectedTaskKeys: { value: new Set<string>() },
|
||||
snapshot: {
|
||||
value: { items: [], tasks: [], warnings: [], hasRunningTasks: false },
|
||||
},
|
||||
filteredItems: { value: [] },
|
||||
allSelected: { value: false },
|
||||
someSelected: { value: false },
|
||||
bind: vi.fn(),
|
||||
unbind: vi.fn(),
|
||||
open,
|
||||
refresh: vi.fn(),
|
||||
ignoreItem: vi.fn(),
|
||||
unignoreItem: vi.fn(),
|
||||
toggleSelection: vi.fn(),
|
||||
toggleSelectAll: vi.fn(),
|
||||
getSelectedItems: vi.fn(() => []),
|
||||
closeNow: vi.fn(),
|
||||
startSelected: vi.fn(),
|
||||
requestClose: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("App update center entry", () => {
|
||||
beforeEach(() => {
|
||||
invoke.mockReset();
|
||||
open.mockReset();
|
||||
|
||||
invoke.mockImplementation(async (channel: string) => {
|
||||
if (channel === "get-store-filter") return "both";
|
||||
if (channel === "check-spark-available") return true;
|
||||
if (channel === "check-apm-available") return true;
|
||||
if (channel === "get-app-version") return "5.0.0";
|
||||
return [];
|
||||
});
|
||||
|
||||
Object.assign(window.ipcRenderer, {
|
||||
invoke,
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
send: vi.fn(),
|
||||
});
|
||||
|
||||
window.apm_store.arch = "amd64";
|
||||
|
||||
vi.stubGlobal(
|
||||
"matchMedia",
|
||||
vi.fn(() => ({
|
||||
matches: false,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
it("opens update center when clicking the sidebar action", async () => {
|
||||
render(App);
|
||||
|
||||
await fireEvent.click(await screen.findByText("软件更新"));
|
||||
|
||||
expect(open).toHaveBeenCalledWith("both");
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ type RemoteStoreResponse =
|
||||
| Array<Record<string, unknown>>;
|
||||
|
||||
const APTSS_LIST_UPGRADABLE_KEY =
|
||||
"bash -lc env LANGUAGE=en_US /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf list --upgradable -o Dir::Etc::sourcelist=/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/aptss.list -o Dir::Etc::sourceparts=/dev/null -o APT::Get::List-Cleanup=0";
|
||||
"bash -lc env LANGUAGE=en_US aptss list --upgradable";
|
||||
|
||||
const DPKG_QUERY_INSTALLED_KEY =
|
||||
"dpkg-query -W -f=${Package}\t${db:Status-Want} ${db:Status-Status} ${db:Status-Eflag}\n";
|
||||
|
||||
@@ -28,6 +28,25 @@ describe("update-center query", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses aptss wrapper output with ansi noise before package lines", () => {
|
||||
const output = [
|
||||
"\u001b[1;32m信息:正在使用非 Root 权限模式启动!若出现问题,请尝试使用 Root 权限执行命令。\u001b[0m",
|
||||
"正在列表...",
|
||||
"spark-weather/stable 2.0.0 amd64 [upgradable from: 1.9.0]",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
expect(parseAptssUpgradableOutput(output)).toEqual([
|
||||
{
|
||||
pkgname: "spark-weather",
|
||||
source: "aptss",
|
||||
currentVersion: "1.9.0",
|
||||
nextVersion: "2.0.0",
|
||||
arch: "amd64",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses the legacy from variant in upgradable output", () => {
|
||||
const aptssOutput = "spark-clock/stable 1.2.0 amd64 [from: 1.1.0]";
|
||||
const apmOutput = "spark-player/main 2.0.0 amd64 [from: 1.5.0]";
|
||||
|
||||
@@ -69,6 +69,18 @@ describe("updateCenter store", () => {
|
||||
expect(store.filteredItems.value).toEqual(snapshot.items);
|
||||
});
|
||||
|
||||
it("reuses the last store filter when refreshing without an explicit filter", async () => {
|
||||
const snapshot = createSnapshot();
|
||||
open.mockResolvedValue(snapshot);
|
||||
refresh.mockResolvedValue(snapshot);
|
||||
const store = createUpdateCenterStore();
|
||||
|
||||
await store.open("apm");
|
||||
await store.refresh();
|
||||
|
||||
expect(refresh).toHaveBeenCalledWith("apm");
|
||||
});
|
||||
|
||||
it("starts only the selected non-ignored items", async () => {
|
||||
const snapshot = createSnapshot({
|
||||
items: [
|
||||
|
||||
@@ -59,6 +59,7 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
const searchQuery = ref("");
|
||||
const selectedTaskKeys = ref(new Set<string>());
|
||||
const snapshot = ref<UpdateCenterSnapshot>(EMPTY_SNAPSHOT);
|
||||
let lastStoreFilter: StoreFilter = "both";
|
||||
|
||||
const resetSessionState = (): void => {
|
||||
showCloseConfirm.value = false;
|
||||
@@ -131,13 +132,17 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
};
|
||||
|
||||
const open = async (storeFilter: StoreFilter = "both"): Promise<void> => {
|
||||
lastStoreFilter = storeFilter;
|
||||
resetSessionState();
|
||||
const nextSnapshot = await window.updateCenter.open(storeFilter);
|
||||
applySnapshot(nextSnapshot);
|
||||
isOpen.value = true;
|
||||
};
|
||||
|
||||
const refresh = async (storeFilter: StoreFilter = "both"): Promise<void> => {
|
||||
const refresh = async (
|
||||
storeFilter: StoreFilter = lastStoreFilter,
|
||||
): Promise<void> => {
|
||||
lastStoreFilter = storeFilter;
|
||||
const nextSnapshot = await window.updateCenter.refresh(storeFilter);
|
||||
applySnapshot(nextSnapshot);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user