@shopbite-de/storefront 1.4.0 → 1.4.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.
- package/app/app.vue +0 -1
- package/app/utils/businessHours.ts +41 -46
- package/app/utils/holidays.ts +5 -24
- package/app/utils/storeHours.ts +2 -2
- package/nuxt.config.ts +1 -1
- package/package.json +1 -1
- package/playwright.config.ts +2 -2
- package/test/nuxt/useAddToCart.test.ts +10 -7
- package/test/nuxt/useDeliveryTime.test.ts +12 -5
- package/test/nuxt/useProductConfigurator.test.ts +14 -16
- package/test/nuxt/useProductVariants.test.ts +17 -17
- package/test/nuxt/useProductVariantsZwei.test.ts +7 -7
- package/test/nuxt/useScrollAnimation.test.ts +8 -6
- package/test/nuxt/useShopBiteConfig.test.ts +9 -11
- package/test/nuxt/useTopSellers.test.ts +6 -3
- package/test/nuxt/useWishlistActions.test.ts +26 -10
package/app/app.vue
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { setTime } from "./time";
|
|
2
|
-
import { isClosedHoliday } from "~/utils/holidays";
|
|
3
|
-
|
|
4
1
|
export type ServiceInterval = { start: Date; end: Date };
|
|
5
2
|
|
|
6
3
|
export function isTuesday(date: Date): boolean {
|
|
@@ -45,54 +42,52 @@ export function getEarliestSelectableTime(
|
|
|
45
42
|
export function getNextOpeningTime(now: Ref<Date>): string | null {
|
|
46
43
|
const currentDate = now.value;
|
|
47
44
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check today's intervals
|
|
54
|
-
const todayIntervals = getServiceIntervals(currentDate);
|
|
55
|
-
const currentTime = currentDate.getTime();
|
|
56
|
-
|
|
57
|
-
// Find next opening today
|
|
58
|
-
for (const interval of todayIntervals) {
|
|
59
|
-
if (interval.start.getTime() > currentTime) {
|
|
60
|
-
const hours = interval.start.getHours().toString().padStart(2, "0");
|
|
61
|
-
const minutes = interval.start.getMinutes().toString().padStart(2, "0");
|
|
62
|
-
return `${hours}:${minutes} Uhr`;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check tomorrow
|
|
67
|
-
const tomorrow = new Date(currentDate);
|
|
68
|
-
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
69
|
-
tomorrow.setHours(0, 0, 0, 0);
|
|
70
|
-
|
|
71
|
-
// Try up to 7 days ahead to find next opening
|
|
72
|
-
for (let i = 0; i < 7; i++) {
|
|
73
|
-
const checkDate = new Date(tomorrow);
|
|
45
|
+
// Try up to 60 days ahead to find next opening (covers long holiday periods)
|
|
46
|
+
for (let i = 0; i < 60; i++) {
|
|
47
|
+
const checkDate = new Date(currentDate);
|
|
74
48
|
checkDate.setDate(checkDate.getDate() + i);
|
|
49
|
+
checkDate.setHours(12, 0, 0, 0); // Set to midday to avoid timezone issues
|
|
50
|
+
|
|
51
|
+
// Skip holidays
|
|
52
|
+
if (isClosedHoliday(checkDate)) continue;
|
|
75
53
|
|
|
76
54
|
const intervals = getServiceIntervals(checkDate);
|
|
77
|
-
if (intervals.length
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (i === 0) {
|
|
92
|
-
return `morgen um ${hours}:${minutes} Uhr`;
|
|
55
|
+
if (intervals.length === 0) continue;
|
|
56
|
+
|
|
57
|
+
// For today, check if there's still an opening coming
|
|
58
|
+
if (i === 0) {
|
|
59
|
+
for (const interval of intervals) {
|
|
60
|
+
if (interval.start.getTime() > currentDate.getTime()) {
|
|
61
|
+
const hours = interval.start.getHours().toString().padStart(2, "0");
|
|
62
|
+
const minutes = interval.start
|
|
63
|
+
.getMinutes()
|
|
64
|
+
.toString()
|
|
65
|
+
.padStart(2, "0");
|
|
66
|
+
return `${hours}:${minutes} Uhr`;
|
|
67
|
+
}
|
|
93
68
|
}
|
|
94
|
-
|
|
69
|
+
continue; // Today's openings have passed, check next days
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const nextOpen = intervals[0].start;
|
|
73
|
+
const day = nextOpen.getDate().toString().padStart(2, "0");
|
|
74
|
+
const month = (nextOpen.getMonth() + 1).toString().padStart(2, "0");
|
|
75
|
+
const dayName = [
|
|
76
|
+
"Sonntag",
|
|
77
|
+
"Montag",
|
|
78
|
+
"Dienstag",
|
|
79
|
+
"Mittwoch",
|
|
80
|
+
"Donnerstag",
|
|
81
|
+
"Freitag",
|
|
82
|
+
"Samstag",
|
|
83
|
+
][nextOpen.getDay()];
|
|
84
|
+
const hours = nextOpen.getHours().toString().padStart(2, "0");
|
|
85
|
+
const minutes = nextOpen.getMinutes().toString().padStart(2, "0");
|
|
86
|
+
|
|
87
|
+
if (i === 1) {
|
|
88
|
+
return `morgen um ${hours}:${minutes} Uhr`;
|
|
95
89
|
}
|
|
90
|
+
return `${dayName}, ${day}.${month}. um ${hours}:${minutes} Uhr`;
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
return null;
|
package/app/utils/holidays.ts
CHANGED
|
@@ -1,30 +1,11 @@
|
|
|
1
|
-
export function isClosedHoliday(date
|
|
1
|
+
export function isClosedHoliday(date?: Date): boolean {
|
|
2
|
+
// Use provided date or current date
|
|
3
|
+
const checkDate = date ?? new Date();
|
|
2
4
|
// Format date as YYYY-MM-DD for comparison
|
|
3
|
-
const formattedDate = formatDateYYYYMMDD(
|
|
5
|
+
const formattedDate = formatDateYYYYMMDD(checkDate);
|
|
4
6
|
|
|
5
7
|
// List of holidays (YYYY-MM-DD format)
|
|
6
|
-
const holidays = [
|
|
7
|
-
"2025-07-21",
|
|
8
|
-
"2025-07-22",
|
|
9
|
-
"2025-07-23",
|
|
10
|
-
"2025-07-24",
|
|
11
|
-
"2025-07-25",
|
|
12
|
-
"2025-07-26",
|
|
13
|
-
"2025-07-27",
|
|
14
|
-
"2025-07-28",
|
|
15
|
-
"2025-07-29",
|
|
16
|
-
"2025-07-30",
|
|
17
|
-
"2025-07-31",
|
|
18
|
-
"2025-08-01",
|
|
19
|
-
"2025-08-02",
|
|
20
|
-
"2025-08-03",
|
|
21
|
-
"2025-08-04",
|
|
22
|
-
"2025-08-05",
|
|
23
|
-
"2025-08-06",
|
|
24
|
-
"2025-08-07",
|
|
25
|
-
"2025-08-08",
|
|
26
|
-
"2025-08-09",
|
|
27
|
-
];
|
|
8
|
+
const holidays = ["2025-12-31", "2026-01-01"];
|
|
28
9
|
|
|
29
10
|
return holidays.includes(formattedDate);
|
|
30
11
|
}
|
package/app/utils/storeHours.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// utils/storeHours.ts
|
|
2
|
-
import { getServiceIntervals } from "~/utils/businessHours";
|
|
3
|
-
|
|
4
2
|
export function isStoreOpen(date: Date = new Date()): boolean {
|
|
3
|
+
if (isClosedHoliday(date)) return false;
|
|
4
|
+
|
|
5
5
|
const intervals = getServiceIntervals(date);
|
|
6
6
|
if (intervals.length === 0) return false;
|
|
7
7
|
return intervals.some(({ start, end }) => date >= start && date <= end);
|
package/nuxt.config.ts
CHANGED
package/package.json
CHANGED
package/playwright.config.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { defineConfig, devices } from
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
2
|
import type { ConfigOptions } from "@nuxt/test-utils/playwright";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import dotenv from "dotenv";
|
|
5
|
-
import path from
|
|
5
|
+
import path from "path";
|
|
6
6
|
|
|
7
7
|
dotenv.config({ path: path.resolve(".env.test") });
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
mockRefreshCart,
|
|
11
11
|
mockToastAdd,
|
|
12
12
|
mockTriggerProductAdded,
|
|
13
|
-
mockTrackEvent
|
|
13
|
+
mockTrackEvent,
|
|
14
14
|
} = vi.hoisted(() => ({
|
|
15
15
|
mockAddProducts: vi.fn(),
|
|
16
16
|
mockRefreshCart: vi.fn(),
|
|
@@ -43,17 +43,17 @@ mockNuxtImport("useTrackEvent", () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
// Provide the mocks globally or in a way that they are picked up
|
|
46
|
-
vi.stubGlobal(
|
|
46
|
+
vi.stubGlobal("useCart", () => ({
|
|
47
47
|
addProducts: mockAddProducts,
|
|
48
48
|
refreshCart: mockRefreshCart,
|
|
49
49
|
}));
|
|
50
|
-
vi.stubGlobal(
|
|
50
|
+
vi.stubGlobal("useToast", () => ({
|
|
51
51
|
add: mockToastAdd,
|
|
52
52
|
}));
|
|
53
|
-
vi.stubGlobal(
|
|
53
|
+
vi.stubGlobal("useProductEvents", () => ({
|
|
54
54
|
triggerProductAdded: mockTriggerProductAdded,
|
|
55
55
|
}));
|
|
56
|
-
vi.stubGlobal(
|
|
56
|
+
vi.stubGlobal("useTrackEvent", mockTrackEvent);
|
|
57
57
|
|
|
58
58
|
describe("useAddToCart", () => {
|
|
59
59
|
const mockProduct = {
|
|
@@ -114,7 +114,7 @@ describe("useAddToCart", () => {
|
|
|
114
114
|
it("should add simple product to cart", async () => {
|
|
115
115
|
const { setSelectedProduct, addToCart, isLoading } = useAddToCart();
|
|
116
116
|
setSelectedProduct(mockProduct);
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
mockAddProducts.mockResolvedValue({ id: "cart-123" });
|
|
119
119
|
mockRefreshCart.mockResolvedValue({});
|
|
120
120
|
|
|
@@ -131,7 +131,10 @@ describe("useAddToCart", () => {
|
|
|
131
131
|
expect(mockRefreshCart).toHaveBeenCalled();
|
|
132
132
|
expect(mockToastAdd).toHaveBeenCalled();
|
|
133
133
|
expect(mockTriggerProductAdded).toHaveBeenCalled();
|
|
134
|
-
expect(mockTrackEvent).toHaveBeenCalledWith(
|
|
134
|
+
expect(mockTrackEvent).toHaveBeenCalledWith(
|
|
135
|
+
"add_to_cart",
|
|
136
|
+
expect.any(Object),
|
|
137
|
+
);
|
|
135
138
|
});
|
|
136
139
|
|
|
137
140
|
it("should add product with extras to cart as container", async () => {
|
|
@@ -42,7 +42,9 @@ describe("useDeliveryTime", () => {
|
|
|
42
42
|
|
|
43
43
|
it("should return error for invalid format", () => {
|
|
44
44
|
const { validate } = useDeliveryTime(now);
|
|
45
|
-
expect(validate("invalid")).toBe(
|
|
45
|
+
expect(validate("invalid")).toBe(
|
|
46
|
+
"Bitte eine gültige Uhrzeit im Format HH:MM eingeben.",
|
|
47
|
+
);
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
it("should return error for time before minTime", () => {
|
|
@@ -52,10 +54,15 @@ describe("useDeliveryTime", () => {
|
|
|
52
54
|
const [hours, mins] = minTime.value.split(":").map(Number);
|
|
53
55
|
const earlyTime = new Date(now.value);
|
|
54
56
|
earlyTime.setHours(hours as number);
|
|
55
|
-
earlyTime.setMinutes(mins as number - 5);
|
|
56
|
-
const earlyTimeStr =
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
earlyTime.setMinutes((mins as number) - 5);
|
|
58
|
+
const earlyTimeStr =
|
|
59
|
+
earlyTime.getHours().toString().padStart(2, "0") +
|
|
60
|
+
":" +
|
|
61
|
+
earlyTime.getMinutes().toString().padStart(2, "0");
|
|
62
|
+
|
|
63
|
+
expect(validate(earlyTimeStr)).toContain(
|
|
64
|
+
"vor dem frühestmöglichen Zeitpunkt",
|
|
65
|
+
);
|
|
59
66
|
}
|
|
60
67
|
});
|
|
61
68
|
});
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { useProductConfigurator } from "../../app/composables/useProductConfigurator";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
mockInvoke,
|
|
7
|
-
mockConfigurator,
|
|
8
|
-
mockProduct,
|
|
9
|
-
} = vi.hoisted(() => ({
|
|
4
|
+
const { mockInvoke, mockConfigurator, mockProduct } = vi.hoisted(() => ({
|
|
10
5
|
mockInvoke: vi.fn(),
|
|
11
6
|
mockConfigurator: { value: [] },
|
|
12
7
|
mockProduct: { value: { id: "p1", optionIds: [], options: [] } },
|
|
@@ -35,7 +30,7 @@ describe("useProductConfigurator", () => {
|
|
|
35
30
|
mockProduct.value = {
|
|
36
31
|
id: "p1",
|
|
37
32
|
optionIds: [],
|
|
38
|
-
options: []
|
|
33
|
+
options: [],
|
|
39
34
|
} as any;
|
|
40
35
|
});
|
|
41
36
|
|
|
@@ -44,14 +39,14 @@ describe("useProductConfigurator", () => {
|
|
|
44
39
|
{
|
|
45
40
|
id: "g1",
|
|
46
41
|
name: "Size",
|
|
47
|
-
options: [{ id: "o1", name: "Small" }]
|
|
48
|
-
}
|
|
42
|
+
options: [{ id: "o1", name: "Small" }],
|
|
43
|
+
},
|
|
49
44
|
] as any;
|
|
50
45
|
mockProduct.value = {
|
|
51
46
|
id: "p1-v1",
|
|
52
47
|
parentId: "p1",
|
|
53
48
|
optionIds: ["o1"],
|
|
54
|
-
options: [{ id: "o1" }]
|
|
49
|
+
options: [{ id: "o1" }],
|
|
55
50
|
} as any;
|
|
56
51
|
|
|
57
52
|
const { isLoadingOptions } = useProductConfigurator();
|
|
@@ -62,23 +57,26 @@ describe("useProductConfigurator", () => {
|
|
|
62
57
|
mockProduct.value = { parentId: "parent-1" } as any;
|
|
63
58
|
mockInvoke.mockResolvedValue({
|
|
64
59
|
data: {
|
|
65
|
-
elements: [{ id: "variant-1" }]
|
|
66
|
-
}
|
|
60
|
+
elements: [{ id: "variant-1" }],
|
|
61
|
+
},
|
|
67
62
|
});
|
|
68
63
|
|
|
69
64
|
const { findVariantForSelectedOptions } = useProductConfigurator();
|
|
70
|
-
const result = await findVariantForSelectedOptions({
|
|
65
|
+
const result = await findVariantForSelectedOptions({ Size: "o1" });
|
|
71
66
|
|
|
72
|
-
expect(mockInvoke).toHaveBeenCalledWith(
|
|
67
|
+
expect(mockInvoke).toHaveBeenCalledWith(
|
|
68
|
+
"readProduct post /product",
|
|
69
|
+
expect.any(Object),
|
|
70
|
+
);
|
|
73
71
|
expect(result).toEqual({ id: "variant-1" });
|
|
74
72
|
});
|
|
75
73
|
|
|
76
74
|
it("should return undefined on error in findVariantForSelectedOptions", async () => {
|
|
77
75
|
mockInvoke.mockRejectedValue(new Error("API Error"));
|
|
78
|
-
const consoleSpy = vi.spyOn(console,
|
|
76
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
79
77
|
|
|
80
78
|
const { findVariantForSelectedOptions } = useProductConfigurator();
|
|
81
|
-
const result = await findVariantForSelectedOptions({
|
|
79
|
+
const result = await findVariantForSelectedOptions({ Size: "o1" });
|
|
82
80
|
|
|
83
81
|
expect(result).toBeUndefined();
|
|
84
82
|
expect(consoleSpy).toHaveBeenCalled();
|
|
@@ -18,10 +18,10 @@ describe("useProductVariants", () => {
|
|
|
18
18
|
group: {
|
|
19
19
|
id: "group-size",
|
|
20
20
|
name: "Size",
|
|
21
|
-
translated: { name: "Größe" }
|
|
21
|
+
translated: { name: "Größe" },
|
|
22
22
|
},
|
|
23
|
-
translated: { name: "Klein" }
|
|
24
|
-
}
|
|
23
|
+
translated: { name: "Klein" },
|
|
24
|
+
},
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
option: {
|
|
@@ -30,10 +30,10 @@ describe("useProductVariants", () => {
|
|
|
30
30
|
group: {
|
|
31
31
|
id: "group-size",
|
|
32
32
|
name: "Size",
|
|
33
|
-
translated: { name: "Größe" }
|
|
33
|
+
translated: { name: "Größe" },
|
|
34
34
|
},
|
|
35
|
-
translated: { name: "Groß" }
|
|
36
|
-
}
|
|
35
|
+
translated: { name: "Groß" },
|
|
36
|
+
},
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
option: {
|
|
@@ -42,22 +42,22 @@ describe("useProductVariants", () => {
|
|
|
42
42
|
group: {
|
|
43
43
|
id: "group-color",
|
|
44
44
|
name: "Color",
|
|
45
|
-
translated: { name: "Farbe" }
|
|
45
|
+
translated: { name: "Farbe" },
|
|
46
46
|
},
|
|
47
|
-
translated: { name: "Rot" }
|
|
48
|
-
}
|
|
49
|
-
}
|
|
47
|
+
translated: { name: "Rot" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
50
|
]);
|
|
51
51
|
|
|
52
52
|
const { variants } = useProductVariants(settings as any);
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
expect(variants.value["group-size"]).toBeDefined();
|
|
55
55
|
expect(variants.value["group-size"].name).toBe("Größe");
|
|
56
56
|
expect(variants.value["group-size"].options).toHaveLength(2);
|
|
57
57
|
expect(variants.value["group-size"].options[0]).toEqual({
|
|
58
58
|
label: "Klein",
|
|
59
59
|
value: "opt-1",
|
|
60
|
-
productId: "opt-1"
|
|
60
|
+
productId: "opt-1",
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
expect(variants.value["group-color"]).toBeDefined();
|
|
@@ -71,16 +71,16 @@ describe("useProductVariants", () => {
|
|
|
71
71
|
option: {
|
|
72
72
|
id: "opt-1",
|
|
73
73
|
name: "Small",
|
|
74
|
-
group: { id: "group-size", name: "Size" }
|
|
75
|
-
}
|
|
74
|
+
group: { id: "group-size", name: "Size" },
|
|
75
|
+
},
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
option: {
|
|
79
79
|
id: "opt-1",
|
|
80
80
|
name: "Small",
|
|
81
|
-
group: { id: "group-size", name: "Size" }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
81
|
+
group: { id: "group-size", name: "Size" },
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
84
|
]);
|
|
85
85
|
|
|
86
86
|
const { variants } = useProductVariants(settings as any);
|
|
@@ -17,13 +17,13 @@ describe("useProductVariantsZwei", () => {
|
|
|
17
17
|
translated: { name: "Größe" },
|
|
18
18
|
options: [
|
|
19
19
|
{ id: "opt-1", name: "Small", translated: { name: "Klein" } },
|
|
20
|
-
{ id: "opt-2", name: "Large", translated: { name: "Groß" } }
|
|
21
|
-
]
|
|
22
|
-
}
|
|
20
|
+
{ id: "opt-2", name: "Large", translated: { name: "Groß" } },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
25
|
const { variants } = useProductVariantsZwei(settings);
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
expect(variants.value["group-size"]).toBeDefined();
|
|
28
28
|
expect(variants.value["group-size"]?.name).toBe("Größe");
|
|
29
29
|
expect(variants.value["group-size"]?.options).toHaveLength(2);
|
|
@@ -37,9 +37,9 @@ describe("useProductVariantsZwei", () => {
|
|
|
37
37
|
name: "Size",
|
|
38
38
|
options: [
|
|
39
39
|
{ id: "opt-1", name: "Small" },
|
|
40
|
-
{ id: "opt-1", name: "Small" }
|
|
41
|
-
]
|
|
42
|
-
}
|
|
40
|
+
{ id: "opt-1", name: "Small" },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
43
|
]);
|
|
44
44
|
|
|
45
45
|
const { variants } = useProductVariantsZwei(settings);
|
|
@@ -15,12 +15,14 @@ describe("useScrollAnimation", () => {
|
|
|
15
15
|
disconnectMock = vi.fn();
|
|
16
16
|
|
|
17
17
|
// Mock IntersectionObserver
|
|
18
|
-
global.IntersectionObserver = vi
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
global.IntersectionObserver = vi
|
|
19
|
+
.fn()
|
|
20
|
+
.mockImplementation(function (callback) {
|
|
21
|
+
intersectionCallback = callback;
|
|
22
|
+
this.observe = observeMock;
|
|
23
|
+
this.unobserve = unobserveMock;
|
|
24
|
+
this.disconnect = disconnectMock;
|
|
25
|
+
}) as any;
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
it("should initialize with isVisible false", () => {
|
|
@@ -2,15 +2,13 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
2
2
|
import { mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
3
3
|
import { useShopBiteConfig } from "~/composables/useShopBiteConfig";
|
|
4
4
|
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
mockIsCheckoutEnabled: { value: false },
|
|
13
|
-
}));
|
|
5
|
+
const { mockInvoke, mockDeliveryTime, mockIsCheckoutEnabled } = vi.hoisted(
|
|
6
|
+
() => ({
|
|
7
|
+
mockInvoke: vi.fn(),
|
|
8
|
+
mockDeliveryTime: { value: 0 },
|
|
9
|
+
mockIsCheckoutEnabled: { value: false },
|
|
10
|
+
}),
|
|
11
|
+
);
|
|
14
12
|
|
|
15
13
|
mockNuxtImport("useShopwareContext", () => () => ({
|
|
16
14
|
apiClient: {
|
|
@@ -43,8 +41,8 @@ describe("useShopBiteConfig", () => {
|
|
|
43
41
|
mockInvoke.mockResolvedValue({
|
|
44
42
|
data: {
|
|
45
43
|
deliveryTime: 45,
|
|
46
|
-
isCheckoutEnabled: true
|
|
47
|
-
}
|
|
44
|
+
isCheckoutEnabled: true,
|
|
45
|
+
},
|
|
48
46
|
});
|
|
49
47
|
|
|
50
48
|
const { refresh, deliveryTime, isCheckoutEnabled } = useShopBiteConfig();
|
|
@@ -30,15 +30,18 @@ describe("useTopSellers", () => {
|
|
|
30
30
|
const { loadTopSellers } = useTopSellers();
|
|
31
31
|
const result = await loadTopSellers();
|
|
32
32
|
|
|
33
|
-
expect(mockInvoke).toHaveBeenCalledWith(
|
|
33
|
+
expect(mockInvoke).toHaveBeenCalledWith(
|
|
34
|
+
"getTopSellers post /product",
|
|
35
|
+
expect.any(Object),
|
|
36
|
+
);
|
|
34
37
|
expect(result).toEqual(mockElements);
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
it("should return empty array on error", async () => {
|
|
38
41
|
mockInvoke.mockRejectedValue(new Error("Network error"));
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
// Silence console.error for this test
|
|
41
|
-
const consoleSpy = vi.spyOn(console,
|
|
44
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
42
45
|
|
|
43
46
|
const { loadTopSellers } = useTopSellers();
|
|
44
47
|
const result = await loadTopSellers();
|
|
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
2
2
|
import { mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
3
3
|
import { useWishlistActions } from "../../app/composables/useWishlistActions";
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
const {
|
|
7
6
|
mockAddProducts,
|
|
8
7
|
mockRefreshCart,
|
|
@@ -53,7 +52,9 @@ describe("useWishlistActions", () => {
|
|
|
53
52
|
const { clearWishlistHandler, isLoading } = useWishlistActions();
|
|
54
53
|
await clearWishlistHandler();
|
|
55
54
|
expect(mockClearWishlist).toHaveBeenCalled();
|
|
56
|
-
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
55
|
+
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
56
|
+
expect.objectContaining({ title: "Merkliste geleert" }),
|
|
57
|
+
);
|
|
57
58
|
expect(isLoading.value).toBe(false);
|
|
58
59
|
});
|
|
59
60
|
|
|
@@ -63,11 +64,18 @@ describe("useWishlistActions", () => {
|
|
|
63
64
|
|
|
64
65
|
await addSingleItemToCart(mockProduct);
|
|
65
66
|
|
|
66
|
-
expect(mockAddProducts).toHaveBeenCalledWith([
|
|
67
|
+
expect(mockAddProducts).toHaveBeenCalledWith([
|
|
68
|
+
{ id: "prod-1", quantity: 1, type: "product" },
|
|
69
|
+
]);
|
|
67
70
|
expect(mockRefreshCart).toHaveBeenCalled();
|
|
68
71
|
expect(mockTriggerProductAdded).toHaveBeenCalled();
|
|
69
|
-
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
70
|
-
|
|
72
|
+
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
73
|
+
expect.objectContaining({ title: "In den Warenkorb gelegt" }),
|
|
74
|
+
);
|
|
75
|
+
expect(mockTrackEvent).toHaveBeenCalledWith(
|
|
76
|
+
"add_to_cart",
|
|
77
|
+
expect.any(Object),
|
|
78
|
+
);
|
|
71
79
|
});
|
|
72
80
|
|
|
73
81
|
it("should warn when adding a base product with variants", async () => {
|
|
@@ -77,7 +85,9 @@ describe("useWishlistActions", () => {
|
|
|
77
85
|
await addSingleItemToCart(baseProduct);
|
|
78
86
|
|
|
79
87
|
expect(mockAddProducts).not.toHaveBeenCalled();
|
|
80
|
-
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
88
|
+
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
89
|
+
expect.objectContaining({ title: "Variante erforderlich" }),
|
|
90
|
+
);
|
|
81
91
|
});
|
|
82
92
|
|
|
83
93
|
it("should add all items to cart", async () => {
|
|
@@ -94,7 +104,9 @@ describe("useWishlistActions", () => {
|
|
|
94
104
|
{ id: "p1", quantity: 1, type: "product" },
|
|
95
105
|
{ id: "p2", quantity: 1, type: "product" },
|
|
96
106
|
]);
|
|
97
|
-
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
107
|
+
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
108
|
+
expect.objectContaining({ title: "Produkte hinzugefügt" }),
|
|
109
|
+
);
|
|
98
110
|
expect(isAddingToCart.value).toBe(false);
|
|
99
111
|
});
|
|
100
112
|
|
|
@@ -117,8 +129,12 @@ describe("useWishlistActions", () => {
|
|
|
117
129
|
expect(mockAddProducts).toHaveBeenCalledWith([
|
|
118
130
|
{ id: "p1", quantity: 1, type: "product" },
|
|
119
131
|
]);
|
|
120
|
-
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
expect(mockToastAdd).toHaveBeenCalledWith(
|
|
133
|
+
expect.objectContaining({
|
|
134
|
+
description: expect.stringContaining(
|
|
135
|
+
"1 Produkte hinzugefügt. 1 Produkt(e) übersprungen",
|
|
136
|
+
),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
123
139
|
});
|
|
124
140
|
});
|