@shopbite-de/storefront 1.6.2 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ import { createAdminAPIClient } from "@shopware/api-client";
2
+ import type { operations } from "@shopware/api-client/admin-api-types";
3
+
4
+ /**
5
+ * Creates a Shopware Admin API client
6
+ */
7
+ export function createAdminApiClient(baseURL: string, accessToken: string) {
8
+ return createAdminAPIClient<operations>({
9
+ baseURL,
10
+ accessToken,
11
+ });
12
+ }
13
+
14
+ /**
15
+ * Fetches sales channels using the Admin API client
16
+ */
17
+ export async function getSalesChannels(client: any) {
18
+ const response = await client.invoke({
19
+ method: "GET",
20
+ path: "/api/v3/sales-channel",
21
+ });
22
+
23
+ return response.data;
24
+ }
@@ -1,23 +1,17 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { mountSuspended } from "@nuxt/test-utils/runtime";
3
- import { mockNuxtImport } from "@nuxt/test-utils/runtime";
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
4
3
  import Header from "~/components/Header.vue";
5
- import { ref, reactive, toRefs } from "vue";
4
+ import { ref } from "vue";
6
5
 
7
- const mocks = vi.hoisted(() => ({
8
- state: {
9
- isLoggedIn: false,
10
- isGuestSession: false,
11
- user: null as any,
12
- },
13
- logout: vi.fn(),
6
+ mockNuxtImport("useHeaderNavigation", () => () => ({
7
+ navi: ref([{ label: "Home", to: "/" }]),
14
8
  }));
15
9
 
16
- const reactiveState = reactive(mocks.state);
17
-
18
10
  mockNuxtImport("useUser", () => () => ({
19
- ...toRefs(reactiveState),
20
- logout: mocks.logout,
11
+ isLoggedIn: ref(false),
12
+ isGuestSession: ref(false),
13
+ user: ref(null),
14
+ logout: vi.fn(),
21
15
  }));
22
16
 
23
17
  mockNuxtImport("useShopBiteConfig", () => () => ({
@@ -28,97 +22,46 @@ mockNuxtImport("useCart", () => () => ({
28
22
  count: ref(0),
29
23
  }));
30
24
 
25
+ mockNuxtImport("useToast", () => () => ({
26
+ add: vi.fn(),
27
+ }));
28
+
31
29
  mockNuxtImport("useRuntimeConfig", () => () => ({
32
- app: {
33
- baseURL: "/",
34
- },
35
- public: {
36
- site: {
37
- name: "ShopBite",
38
- },
39
- shopware: {
40
- devStorefrontUrl: "http://localhost:3000",
41
- },
42
- },
30
+ app: { baseURL: "/" },
31
+ public: { site: { name: "ShopBite" } },
43
32
  }));
44
33
 
45
34
  // Mock Nuxt Content queryCollection
46
35
  mockNuxtImport("queryCollection", () => (collection: string) => ({
47
36
  first: () =>
48
37
  Promise.resolve({
49
- main: [],
50
38
  account: {
51
- loggedIn: [
52
- [{ label: "Mein Konto", type: "label" }],
53
- [
54
- { label: "Konto", icon: "i-lucide-user", to: "/konto" },
55
- {
56
- label: "Bestellungen",
57
- icon: "i-lucide-pizza",
58
- to: "/konto/bestellungen",
59
- },
60
- ],
61
- [{ label: "Abmelden", icon: "i-lucide-log-out", action: "logout" }],
62
- ],
63
- loggedOut: [
64
- [{ label: "Jetzt anmelden", type: "label" }],
65
- [{ label: "Zur Anmeldung", icon: "i-lucide-user", to: "/anmelden" }],
66
- ],
39
+ loggedIn: [],
40
+ loggedOut: [],
67
41
  },
68
42
  }),
69
43
  }));
70
44
 
71
45
  describe("Header", () => {
72
- beforeEach(() => {
73
- reactiveState.isLoggedIn = false;
74
- reactiveState.isGuestSession = false;
75
- reactiveState.user = null;
76
- vi.clearAllMocks();
77
- });
78
-
79
- it("renders correctly when logged out", async () => {
46
+ it("renders structure correctly", async () => {
80
47
  const component = await mountSuspended(Header);
81
- // Should show user icon
82
- expect(component.html()).toContain("i-lucide:user");
83
- // Should NOT show checkmark chip
84
- expect(component.html()).not.toContain("✓");
85
- });
86
48
 
87
- it("renders correctly when logged in", async () => {
88
- reactiveState.isLoggedIn = true;
89
- reactiveState.user = { firstName: "John", lastName: "Doe" };
90
- const component = await mountSuspended(Header);
49
+ const srOnlySiteName = component.find(".sr-only");
50
+ expect(srOnlySiteName.exists()).toBe(true);
51
+ expect(srOnlySiteName.text()).toBe("ShopBite");
52
+ expect(component.html()).toContain("i-lucide:phone");
53
+ expect(component.html()).toContain("i-lucide:shopping-cart");
91
54
 
92
- // Should show checkmark chip
93
- expect(component.html()).toContain("✓");
55
+ const navMenu = component.findComponent({ name: "UNavigationMenu" });
56
+ expect(navMenu.exists()).toBe(true);
94
57
  });
95
58
 
96
- it("renders correctly for guest session", async () => {
97
- reactiveState.isGuestSession = true;
98
- reactiveState.user = { firstName: "Guest", lastName: "User" };
59
+ it("renders login slideover", async () => {
99
60
  const component = await mountSuspended(Header);
100
61
 
101
- // Should show checkmark chip for guest session too
102
- expect(component.html()).toContain("");
103
- });
104
-
105
- it("filters menu items for guest session", async () => {
106
- reactiveState.isGuestSession = true;
107
- reactiveState.user = { firstName: "Guest", lastName: "User" };
108
- const component = await mountSuspended(Header);
109
-
110
- // Find the dropdown menu
111
- const dropdown = component.findComponent({ name: "UDropdownMenu" });
112
- expect(dropdown.exists()).toBe(true);
113
-
114
- const items = dropdown.props("items");
115
-
116
- // Group 0 should have the user label
117
- // Group 1 should be gone (filtered out because it only contained links)
118
- // Group 2 (now index 1) should have logout
119
- expect(items.length).toBe(2);
120
- expect(items[0][0].label).toBe("Guest User");
121
- expect(items[1][0].label).toBe("Abmelden");
122
- expect(items[1][0].onSelect).toBeDefined();
62
+ // USlideover might not be directly in HTML if it uses teleport and is closed
63
+ const slideover = component.findComponent({ name: "USlideover" });
64
+ expect(slideover.exists()).toBe(true);
65
+ expect(slideover.props("title")).toBe("Konto");
123
66
  });
124
67
  });
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
3
+ import HeaderBody from "~/components/Header/Body.vue";
4
+ import { ref } from "vue";
5
+
6
+ const mockNavi = [
7
+ { label: "Home", to: "/", icon: "i-lucide-home" },
8
+ { label: "Products", to: "/products", icon: "i-lucide-package" },
9
+ ];
10
+
11
+ mockNuxtImport("useHeaderNavigation", () => () => ({
12
+ navi: ref(mockNavi),
13
+ }));
14
+
15
+ describe("HeaderBody", () => {
16
+ it("renders navigation menu with correct items", async () => {
17
+ const component = await mountSuspended(HeaderBody);
18
+ const navMenu = component.findComponent({ name: "UNavigationMenu" });
19
+
20
+ expect(navMenu.exists()).toBe(true);
21
+ expect(navMenu.props("items")).toEqual(mockNavi);
22
+ expect(navMenu.props("orientation")).toBe("vertical");
23
+ });
24
+
25
+ it("renders SalesChannelSwitch", async () => {
26
+ const component = await mountSuspended(HeaderBody);
27
+ const switchComponent = component.findComponent({
28
+ name: "SalesChannelSwitch",
29
+ });
30
+
31
+ expect(switchComponent.exists()).toBe(true);
32
+ });
33
+ });
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
3
+ import HeaderRight from "~/components/Header/Right.vue";
4
+ import { ref, reactive, computed } from "vue";
5
+
6
+ const mocks = vi.hoisted(() => ({
7
+ state: {
8
+ isLoggedIn: false,
9
+ isGuestSession: false,
10
+ user: null as any,
11
+ },
12
+ toastAddCalled: { value: false },
13
+ }));
14
+
15
+ const reactiveState = reactive(mocks.state);
16
+
17
+ mockNuxtImport("useUser", () => () => ({
18
+ isLoggedIn: computed(() => reactiveState.isLoggedIn),
19
+ isGuestSession: computed(() => reactiveState.isGuestSession),
20
+ user: computed(() => reactiveState.user),
21
+ logout: () => {
22
+ reactiveState.isLoggedIn = false;
23
+ reactiveState.user = null;
24
+ },
25
+ }));
26
+
27
+ mockNuxtImport("useToast", () => () => ({
28
+ add: (payload: any) => {
29
+ if (typeof global !== "undefined" && (global as any).toastAddCalled) {
30
+ (global as any).toastAddCalled.value = true;
31
+ }
32
+ },
33
+ }));
34
+
35
+ mockNuxtImport("useShopBiteConfig", () => () => ({
36
+ isCheckoutEnabled: ref(true),
37
+ }));
38
+
39
+ mockNuxtImport("useCart", () => () => ({
40
+ count: ref(5),
41
+ }));
42
+
43
+ mockNuxtImport("useRuntimeConfig", () => () => ({
44
+ app: { baseURL: "/" },
45
+ }));
46
+
47
+ // Mock Nuxt Content queryCollection
48
+ mockNuxtImport("queryCollection", () => (collection: string) => ({
49
+ first: () =>
50
+ Promise.resolve({
51
+ account: {
52
+ loggedIn: [
53
+ [{ label: "Mein Konto", type: "label" }],
54
+ [
55
+ {
56
+ label: "Bestellungen",
57
+ icon: "i-lucide-pizza",
58
+ to: "/konto/bestellungen",
59
+ },
60
+ ],
61
+ [{ label: "Abmelden", icon: "i-lucide-log-out", action: "logout" }],
62
+ ],
63
+ loggedOut: [
64
+ [{ label: "Jetzt anmelden", type: "label" }],
65
+ [{ label: "Zur Anmeldung", icon: "i-lucide-user", to: "/anmelden" }],
66
+ ],
67
+ },
68
+ }),
69
+ }));
70
+
71
+ describe("HeaderRight", () => {
72
+ beforeEach(() => {
73
+ reactiveState.isLoggedIn = false;
74
+ reactiveState.isGuestSession = false;
75
+ reactiveState.user = null;
76
+ mocks.toastAddCalled.value = false;
77
+ (global as any).toastAddCalled = mocks.toastAddCalled;
78
+ vi.clearAllMocks();
79
+ });
80
+
81
+ it("renders phone link", async () => {
82
+ const component = await mountSuspended(HeaderRight);
83
+ const phoneButton = component.find('a[href="tel:+49610471427"]');
84
+ expect(phoneButton.exists()).toBe(true);
85
+ });
86
+
87
+ it("shows logged out dropdown items when not logged in", async () => {
88
+ const component = await mountSuspended(HeaderRight);
89
+ const dropdown = component.findComponent({ name: "UDropdownMenu" });
90
+ const items = dropdown.props("items");
91
+
92
+ expect(items[0][0].label).toBe("Jetzt anmelden");
93
+ expect(items[1][0].to).toBe("/anmelden");
94
+ });
95
+
96
+ it("shows logged in dropdown items when logged in", async () => {
97
+ reactiveState.isLoggedIn = true;
98
+ reactiveState.user = { firstName: "Jane", lastName: "Doe" };
99
+
100
+ const component = await mountSuspended(HeaderRight);
101
+ const dropdown = component.findComponent({ name: "UDropdownMenu" });
102
+ const items = dropdown.props("items");
103
+
104
+ expect(items[0][0].label).toBe("Jane Doe");
105
+ expect(items[1][0].to).toBe("/konto/bestellungen");
106
+ expect(items[2][0].label).toBe("Abmelden");
107
+ });
108
+
109
+ it("shows cart drawer with correct count when checkout is enabled", async () => {
110
+ const component = await mountSuspended(HeaderRight);
111
+ const drawer = component.findComponent({ name: "UDrawer" });
112
+ expect(drawer.exists()).toBe(true);
113
+
114
+ const chip = component.findComponent({ name: "UChip" });
115
+ expect(chip.props("text")).toBe(5);
116
+ });
117
+
118
+ it("calls logout and updates state when logout is selected", async () => {
119
+ reactiveState.isLoggedIn = true;
120
+ const component = await mountSuspended(HeaderRight);
121
+ const dropdown = component.findComponent({ name: "UDropdownMenu" });
122
+ const items = dropdown.props("items");
123
+
124
+ // Find the item with logout handler
125
+ let logoutItem;
126
+ for (const group of items) {
127
+ for (const item of group) {
128
+ if (item.label === "Abmelden") {
129
+ logoutItem = item;
130
+ break;
131
+ }
132
+ }
133
+ }
134
+
135
+ expect(logoutItem).toBeDefined();
136
+ logoutItem.onSelect();
137
+
138
+ expect(reactiveState.isLoggedIn).toBe(false);
139
+ expect(mocks.toastAddCalled.value).toBe(true);
140
+ });
141
+ });
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
3
+ import HeaderTitle from "~/components/Header/Title.vue";
4
+
5
+ const { mockRuntimeConfig } = vi.hoisted(() => ({
6
+ mockRuntimeConfig: {
7
+ app: {
8
+ baseURL: "/",
9
+ },
10
+ public: {
11
+ site: {
12
+ name: "ShopBite Test Store",
13
+ },
14
+ },
15
+ },
16
+ }));
17
+
18
+ mockNuxtImport("useRuntimeConfig", () => () => mockRuntimeConfig);
19
+
20
+ describe("HeaderTitle", () => {
21
+ it("renders the site name for screen readers", async () => {
22
+ const component = await mountSuspended(HeaderTitle);
23
+ const srOnly = component.find(".sr-only");
24
+ expect(srOnly.exists()).toBe(true);
25
+ expect(srOnly.text()).toBe("ShopBite Test Store");
26
+ });
27
+
28
+ it("renders a link to the home page", async () => {
29
+ const component = await mountSuspended(HeaderTitle);
30
+ const link = component.findComponent({ name: "NuxtLink" });
31
+ expect(link.exists()).toBe(true);
32
+ expect(link.props("to")).toBe("/");
33
+ });
34
+
35
+ it("renders the logo image", async () => {
36
+ const component = await mountSuspended(HeaderTitle);
37
+ const image = component.findComponent({ name: "UColorModeImage" });
38
+ expect(image.exists()).toBe(true);
39
+ expect(image.props("light")).toBe("/light/Logo.png");
40
+ expect(image.props("dark")).toBe("/dark/Logo.png");
41
+ });
42
+ });
@@ -0,0 +1,201 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
3
+ import SalesChannelSwitch from "~/components/SalesChannelSwitch.vue";
4
+ import { ref } from "vue";
5
+
6
+ // Mock window.location
7
+ const mockWindowLocation = {
8
+ href: "",
9
+ };
10
+
11
+ // Mock the sales channels data that would be returned by useFetch
12
+ const mockSalesChannels = [
13
+ {
14
+ label: "Main Store",
15
+ value: "https://main-store.example.com",
16
+ },
17
+ {
18
+ label: "Secondary Store",
19
+ value: "https://secondary-store.example.com",
20
+ },
21
+ ];
22
+
23
+ // Use vi.hoisted to avoid hoisting issues
24
+ const { mockUseFetch, mockRuntimeConfig } = vi.hoisted(() => ({
25
+ mockUseFetch: vi.fn(() => {
26
+ return {
27
+ data: ref(mockSalesChannels),
28
+ status: ref("success"),
29
+ };
30
+ }),
31
+ mockRuntimeConfig: {
32
+ public: {
33
+ shopBite: {
34
+ feature: {
35
+ multiChannel: true,
36
+ },
37
+ },
38
+ storeUrl: "https://main-store.example.com",
39
+ },
40
+ },
41
+ }));
42
+
43
+ // Set up the mocks at the top level
44
+ mockNuxtImport("useRuntimeConfig", () => () => mockRuntimeConfig);
45
+ mockNuxtImport("useFetch", () => mockUseFetch);
46
+
47
+ describe("SalesChannelSwitch", () => {
48
+ beforeEach(() => {
49
+ // Reset window.location mock
50
+ mockWindowLocation.href = "";
51
+ global.window = { location: mockWindowLocation } as any;
52
+ vi.clearAllMocks();
53
+
54
+ // Reset default mock implementation for useFetch
55
+ mockUseFetch.mockImplementation(() => {
56
+ return {
57
+ data: ref(mockSalesChannels),
58
+ status: ref("success"),
59
+ };
60
+ });
61
+ });
62
+
63
+ it("should not render when multiChannel feature is disabled", async () => {
64
+ // Temporarily disable multiChannel
65
+ mockRuntimeConfig.public.shopBite.feature.multiChannel = false;
66
+
67
+ const component = await mountSuspended(SalesChannelSwitch);
68
+
69
+ // Should not render the select menu
70
+ expect(component.findComponent({ name: "USelectMenu" }).exists()).toBe(
71
+ false,
72
+ );
73
+
74
+ // Re-enable for other tests
75
+ mockRuntimeConfig.public.shopBite.feature.multiChannel = true;
76
+ });
77
+
78
+ it("should render select menu when multiChannel feature is enabled", async () => {
79
+ const component = await mountSuspended(SalesChannelSwitch);
80
+
81
+ // Should render the select menu
82
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
83
+ expect(selectMenu.exists()).toBe(true);
84
+ expect(selectMenu.props("icon")).toBe("i-lucide-store");
85
+ });
86
+
87
+ it("should load and transform sales channels correctly", async () => {
88
+ const component = await mountSuspended(SalesChannelSwitch);
89
+
90
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
91
+ const items = selectMenu.props("items");
92
+
93
+ expect(items).toHaveLength(2);
94
+ expect(items[0]).toEqual({
95
+ label: "Main Store",
96
+ value: "https://main-store.example.com",
97
+ });
98
+ expect(items[1]).toEqual({
99
+ label: "Secondary Store",
100
+ value: "https://secondary-store.example.com",
101
+ });
102
+ });
103
+
104
+ it("should select the current store URL by default", async () => {
105
+ const component = await mountSuspended(SalesChannelSwitch);
106
+
107
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
108
+ const modelValue = selectMenu.props("modelValue");
109
+
110
+ expect(modelValue).toEqual({
111
+ label: "Main Store",
112
+ value: "https://main-store.example.com",
113
+ });
114
+ });
115
+
116
+ it("should show loading state when fetching data", async () => {
117
+ // Mock pending state
118
+ mockUseFetch.mockImplementationOnce(() => {
119
+ return {
120
+ data: ref(null),
121
+ status: ref("pending"),
122
+ };
123
+ });
124
+
125
+ const component = await mountSuspended(SalesChannelSwitch);
126
+
127
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
128
+ expect(selectMenu.props("loading")).toBe(true);
129
+ });
130
+
131
+ it("should redirect to new store URL when selection changes", async () => {
132
+ const component = await mountSuspended(SalesChannelSwitch);
133
+
134
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
135
+
136
+ // Change the selection
137
+ await selectMenu.setValue({
138
+ label: "Secondary Store",
139
+ value: "https://secondary-store.example.com",
140
+ });
141
+
142
+ // Should have redirected
143
+ expect(mockWindowLocation.href).toBe("https://secondary-store.example.com");
144
+ });
145
+
146
+ it("should not redirect on initial selection", async () => {
147
+ // Mock that no initial selection was made
148
+ mockRuntimeConfig.public.storeUrl = "https://unknown-store.example.com";
149
+
150
+ const component = await mountSuspended(SalesChannelSwitch);
151
+
152
+ // Should not redirect immediately
153
+ expect(mockWindowLocation.href).toBe("");
154
+
155
+ // Restore original store URL
156
+ mockRuntimeConfig.public.storeUrl = "https://main-store.example.com";
157
+ });
158
+
159
+ it("should handle empty sales channels gracefully", async () => {
160
+ // Mock empty sales channels
161
+ mockUseFetch.mockImplementationOnce(() => {
162
+ return {
163
+ data: ref([]),
164
+ status: ref("success"),
165
+ };
166
+ });
167
+
168
+ const component = await mountSuspended(SalesChannelSwitch);
169
+
170
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
171
+ const items = selectMenu.props("items");
172
+
173
+ expect(items).toHaveLength(0);
174
+ });
175
+
176
+ it("should handle sales channels without domains", async () => {
177
+ const mockChannelsWithoutDomains = [
178
+ {
179
+ label: "Store Without Domain",
180
+ value: "",
181
+ },
182
+ ];
183
+
184
+ mockUseFetch.mockImplementationOnce(() => {
185
+ return {
186
+ data: ref(mockChannelsWithoutDomains),
187
+ status: ref("success"),
188
+ };
189
+ });
190
+
191
+ const component = await mountSuspended(SalesChannelSwitch);
192
+
193
+ const selectMenu = component.findComponent({ name: "USelectMenu" });
194
+ const items = selectMenu.props("items");
195
+
196
+ expect(items[0]).toEqual({
197
+ label: "Store Without Domain",
198
+ value: "",
199
+ });
200
+ });
201
+ });
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import {
3
+ createAdminApiClient,
4
+ getSalesChannels,
5
+ } from "../../server/utils/shopware/adminApiClient";
6
+
7
+ describe("Shopware Admin API Client", () => {
8
+ describe("createAdminApiClient", () => {
9
+ it("should create a client with correct configuration", () => {
10
+ const endpoint = "https://shopware.example.com/api";
11
+ const accessToken = "test-token";
12
+
13
+ const client = createAdminApiClient(endpoint, accessToken);
14
+
15
+ expect(client).toBeDefined();
16
+ // The client should have the invoke method
17
+ expect(client).toHaveProperty("invoke");
18
+ expect(typeof client.invoke).toBe("function");
19
+ });
20
+ });
21
+
22
+ describe("getSalesChannels", () => {
23
+ it("should fetch sales channels successfully", async () => {
24
+ // Mock the client
25
+ const mockClient = {
26
+ invoke: vi.fn().mockResolvedValue({
27
+ data: [
28
+ { id: "1", name: "Storefront", typeId: "storefront" },
29
+ { id: "2", name: "Headless", typeId: "headless" },
30
+ ],
31
+ }),
32
+ };
33
+
34
+ const result = await getSalesChannels(mockClient);
35
+
36
+ expect(result).toEqual([
37
+ { id: "1", name: "Storefront", typeId: "storefront" },
38
+ { id: "2", name: "Headless", typeId: "headless" },
39
+ ]);
40
+
41
+ expect(mockClient.invoke).toHaveBeenCalledWith({
42
+ method: "GET",
43
+ path: "/api/v3/sales-channel",
44
+ });
45
+ });
46
+
47
+ it("should handle errors when fetching sales channels", async () => {
48
+ const mockClient = {
49
+ invoke: vi.fn().mockRejectedValue(new Error("API error")),
50
+ };
51
+
52
+ await expect(getSalesChannels(mockClient)).rejects.toThrow("API error");
53
+ });
54
+
55
+ it("should handle empty response gracefully", async () => {
56
+ const mockClient = {
57
+ invoke: vi.fn().mockResolvedValue({
58
+ data: [],
59
+ }),
60
+ };
61
+
62
+ const result = await getSalesChannels(mockClient);
63
+ expect(result).toEqual([]);
64
+ });
65
+ });
66
+ });