mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-09-22 22:50:11 +08:00
!408 refactor(已安装应用): 合并 APM/Spark 分页为列表标签并新增统计
Merge pull request !408 from xiyidaiwa/Erotica
This commit is contained in:
@@ -26,21 +26,20 @@ const createApp = (overrides: Partial<App> = {}): App => ({
|
||||
});
|
||||
|
||||
describe("InstalledAppsModal", () => {
|
||||
const baseProps = {
|
||||
show: true,
|
||||
apps: [] as App[],
|
||||
loading: false,
|
||||
error: "",
|
||||
warning: "",
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
};
|
||||
|
||||
it("keeps scroll chaining inside the modal list", () => {
|
||||
const { container } = render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
apps: [],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
props: baseProps,
|
||||
});
|
||||
|
||||
expect(screen.getByText("已安装应用")).toBeTruthy();
|
||||
@@ -52,17 +51,8 @@ describe("InstalledAppsModal", () => {
|
||||
it("renders open and detail actions for a store-backed installed app", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
...baseProps,
|
||||
apps: [createApp()],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -70,20 +60,42 @@ describe("InstalledAppsModal", () => {
|
||||
expect(screen.getByRole("button", { name: "查看详情" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders the spark origin tag for spark apps", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [createApp({ origin: "spark", name: "Spark Notes" })],
|
||||
},
|
||||
});
|
||||
|
||||
// 精确匹配应用行内的来源标签(页头统计区也有 "Spark" 文案,getAllByText 过于宽泛)
|
||||
expect(screen.getByTestId("origin-tag-spark")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders the APM origin tag for APM apps", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [
|
||||
createApp({
|
||||
origin: "apm",
|
||||
name: "APM Container",
|
||||
pkgname: "amber-pm-container",
|
||||
version: "1.0.0",
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// 精确匹配应用行内的来源标签(页头统计区也有 "APM" 文案,getAllByText 过于宽泛)
|
||||
expect(screen.getByTestId("origin-tag-apm")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("emits open-app when clicking 打开", async () => {
|
||||
const rendered = render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
...baseProps,
|
||||
apps: [createApp()],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -98,17 +110,8 @@ describe("InstalledAppsModal", () => {
|
||||
it("emits open-detail when clicking 查看详情", async () => {
|
||||
const rendered = render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
...baseProps,
|
||||
apps: [createApp()],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -123,17 +126,8 @@ describe("InstalledAppsModal", () => {
|
||||
it("shows 查看详情 for metadata-rich unknown-category apps", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
...baseProps,
|
||||
apps: [createApp({ category: "unknown", more: "Has store metadata" })],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -143,17 +137,8 @@ describe("InstalledAppsModal", () => {
|
||||
it("hides 查看详情 for unknown-category apps", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
...baseProps,
|
||||
apps: [createApp({ category: "unknown" })],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -162,19 +147,7 @@ describe("InstalledAppsModal", () => {
|
||||
|
||||
it("requests login for cloud actions when logged out", async () => {
|
||||
const rendered = render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
apps: [],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
loggedIn: false,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
props: baseProps,
|
||||
});
|
||||
|
||||
await fireEvent.click(screen.getByRole("button", { name: "同步到账号" }));
|
||||
@@ -186,17 +159,8 @@ describe("InstalledAppsModal", () => {
|
||||
it("emits cloud sync and restore events when logged in", async () => {
|
||||
const rendered = render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
apps: [],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
...baseProps,
|
||||
loggedIn: true,
|
||||
syncing: false,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -210,17 +174,9 @@ describe("InstalledAppsModal", () => {
|
||||
it("disables sync button while syncing", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
apps: [],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
...baseProps,
|
||||
loggedIn: true,
|
||||
syncing: true,
|
||||
syncMessage: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -230,20 +186,94 @@ describe("InstalledAppsModal", () => {
|
||||
it("shows account sync feedback in the installed apps modal", () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
show: true,
|
||||
apps: [],
|
||||
loading: false,
|
||||
error: "",
|
||||
activeOrigin: "spark",
|
||||
storeFilter: "both",
|
||||
sparkAvailable: true,
|
||||
apmAvailable: true,
|
||||
...baseProps,
|
||||
loggedIn: true,
|
||||
syncing: false,
|
||||
syncMessage: "同步完成",
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("同步完成")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("filters installed apps by search query (name match)", async () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [
|
||||
createApp({ name: "Spark Notes", pkgname: "spark-notes" }),
|
||||
createApp({
|
||||
name: "Visual Studio Code",
|
||||
pkgname: "code",
|
||||
category: "dev",
|
||||
more: "https://code.visualstudio.com/",
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// 初始两条都在
|
||||
expect(screen.getByText("Spark Notes")).toBeTruthy();
|
||||
expect(screen.getByText("Visual Studio Code")).toBeTruthy();
|
||||
|
||||
const input = screen.getByPlaceholderText("搜索已安装应用…");
|
||||
await fireEvent.update(input, "code");
|
||||
|
||||
// "code" 只匹配到 pkgname 为 "code" 的项(名称不区分大小写)
|
||||
expect(screen.queryByText("Spark Notes")).toBeNull();
|
||||
expect(screen.getByText("Visual Studio Code")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("filters installed apps by search query (case-insensitive)", async () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [
|
||||
createApp({ name: "钉钉", pkgname: "com.alibaba.dingtalk" }),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByPlaceholderText("搜索已安装应用…");
|
||||
await fireEvent.update(input, "DINGTALK");
|
||||
|
||||
// 包名大写不区分大小写匹配
|
||||
expect(screen.getByText("钉钉")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows a no-match hint when search query has no results", async () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [createApp({ name: "Spark Notes" })],
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByPlaceholderText("搜索已安装应用…");
|
||||
await fireEvent.update(input, "不存在的关键字xyz");
|
||||
|
||||
expect(screen.queryByText("Spark Notes")).toBeNull();
|
||||
expect(screen.getByText(/未找到匹配/)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("clears the search when clicking the clear button", async () => {
|
||||
render(InstalledAppsModal, {
|
||||
props: {
|
||||
...baseProps,
|
||||
apps: [createApp()],
|
||||
},
|
||||
});
|
||||
|
||||
const input = screen.getByPlaceholderText(
|
||||
"搜索已安装应用…",
|
||||
) as HTMLInputElement;
|
||||
await fireEvent.update(input, "code");
|
||||
expect(input.value).toBe("code");
|
||||
|
||||
// 清除按钮存在(仅在有内容时显示)
|
||||
const clearBtn = screen.getByRole("button", { name: "清除搜索" });
|
||||
await fireEvent.click(clearBtn);
|
||||
|
||||
expect(input.value).toBe("");
|
||||
expect(screen.getByText("Spark Notes")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user