@shopbite-de/storefront 1.5.3 → 1.6.0

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,242 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { createRegistrationSchema } from "~/validation/registrationSchema";
3
+
4
+ describe("registrationSchema", () => {
5
+ const baseState = {
6
+ accountType: "private",
7
+ guest: false,
8
+ isShippingAddressDifferent: false,
9
+ password: "password123",
10
+ };
11
+
12
+ const validAddress = {
13
+ street: "Musterstraße 1",
14
+ city: "Musterstadt",
15
+ countryId: "country-id",
16
+ phoneNumber: "123456789",
17
+ zipcode: "12345",
18
+ };
19
+
20
+ const validData = {
21
+ accountType: "private",
22
+ email: "test@example.com",
23
+ firstName: "John",
24
+ lastName: "Doe",
25
+ guest: false,
26
+ acceptedDataProtection: true,
27
+ password: "password123",
28
+ passwordConfirm: "password123",
29
+ billingAddress: validAddress,
30
+ };
31
+
32
+ it("should validate a valid private account", () => {
33
+ const schema = createRegistrationSchema(baseState);
34
+ const result = schema.safeParse(validData);
35
+ expect(result.success).toBe(true);
36
+ });
37
+
38
+ it("should fail if company is missing for business account", () => {
39
+ const businessState = { ...baseState, accountType: "business" };
40
+ const businessData = {
41
+ ...validData,
42
+ accountType: "business",
43
+ billingAddress: { ...validAddress },
44
+ };
45
+ const schema = createRegistrationSchema(businessState);
46
+ const result = schema.safeParse(businessData);
47
+ expect(result.success).toBe(false);
48
+ if (!result.success) {
49
+ expect(result.error.issues[0].message).toBe(
50
+ "Als Geschäftskunde ist der Firmenname ein Pflichtfeld.",
51
+ );
52
+ expect(result.error.issues[0].path).toContain("billingAddress");
53
+ expect(result.error.issues[0].path).toContain("company");
54
+ }
55
+ });
56
+
57
+ it("should validate a valid business account", () => {
58
+ const businessState = { ...baseState, accountType: "business" };
59
+ const businessData = {
60
+ ...validData,
61
+ accountType: "business",
62
+ billingAddress: { ...validAddress, company: "Test Corp" },
63
+ };
64
+ const schema = createRegistrationSchema(businessState);
65
+ const result = schema.safeParse(businessData);
66
+ expect(result.success).toBe(true);
67
+ });
68
+
69
+ it("should require shipping address fields if different from billing address", () => {
70
+ const shippingState = { ...baseState, isShippingAddressDifferent: true };
71
+ const shippingData = {
72
+ ...validData,
73
+ isShippingAddressDifferent: true,
74
+ shippingAddress: {
75
+ firstName: "",
76
+ lastName: "",
77
+ street: "",
78
+ city: "",
79
+ phoneNumber: "",
80
+ company: "",
81
+ },
82
+ };
83
+ const schema = createRegistrationSchema(shippingState);
84
+ const result = schema.safeParse(shippingData);
85
+ expect(result.success).toBe(false);
86
+ if (!result.success) {
87
+ const messages = result.error.issues.map((i) => i.message);
88
+ expect(messages).toContain(
89
+ "Bitte geben Sie den Vornamen für die Lieferadresse an.",
90
+ );
91
+ expect(messages).toContain(
92
+ "Bitte geben Sie den Nachnamen für die Lieferadresse an.",
93
+ );
94
+ expect(messages).toContain(
95
+ "Bitte geben Sie die Straße für die Lieferadresse an.",
96
+ );
97
+ expect(messages).toContain(
98
+ "Bitte geben Sie den Ort für die Lieferadresse an.",
99
+ );
100
+ }
101
+ });
102
+
103
+ it("should require company in shipping address for business account if different", () => {
104
+ const shippingState = {
105
+ ...baseState,
106
+ accountType: "business",
107
+ isShippingAddressDifferent: true,
108
+ };
109
+ const shippingData = {
110
+ ...validData,
111
+ accountType: "business",
112
+ isShippingAddressDifferent: true,
113
+ billingAddress: { ...validAddress, company: "Billing Corp" },
114
+ shippingAddress: {
115
+ firstName: "Jane",
116
+ lastName: "Doe",
117
+ street: "Shipping St 1",
118
+ city: "ShipCity",
119
+ phoneNumber: "987654321",
120
+ company: "",
121
+ },
122
+ };
123
+ const schema = createRegistrationSchema(shippingState);
124
+ const result = schema.safeParse(shippingData);
125
+ expect(result.success).toBe(false);
126
+ if (!result.success) {
127
+ const messages = result.error.issues.map((i) => i.message);
128
+ expect(messages).toContain(
129
+ "Bitte geben Sie den Firmennamen für die Lieferadresse an.",
130
+ );
131
+ }
132
+ });
133
+
134
+ it("should validate different shipping address for business", () => {
135
+ const shippingState = {
136
+ ...baseState,
137
+ accountType: "business",
138
+ isShippingAddressDifferent: true,
139
+ };
140
+ const shippingData = {
141
+ ...validData,
142
+ accountType: "business",
143
+ isShippingAddressDifferent: true,
144
+ billingAddress: { ...validAddress, company: "Billing Corp" },
145
+ shippingAddress: {
146
+ firstName: "Jane",
147
+ lastName: "Doe",
148
+ street: "Shipping St 1",
149
+ city: "ShipCity",
150
+ phoneNumber: "987654321",
151
+ company: "Shipping Corp",
152
+ zipcode: "54321",
153
+ countryId: "country-id",
154
+ },
155
+ };
156
+ const schema = createRegistrationSchema(shippingState);
157
+ const result = schema.safeParse(shippingData);
158
+ expect(result.success).toBe(true);
159
+ });
160
+
161
+ it("should not require password for guest", () => {
162
+ const guestState = { ...baseState, guest: true };
163
+ const guestData = {
164
+ ...validData,
165
+ guest: true,
166
+ password: "",
167
+ passwordConfirm: "",
168
+ };
169
+ const schema = createRegistrationSchema(guestState);
170
+ const result = schema.safeParse(guestData);
171
+ expect(result.success).toBe(true);
172
+ });
173
+
174
+ it("should require password for non-guest", () => {
175
+ const schema = createRegistrationSchema(baseState);
176
+ const result = schema.safeParse({
177
+ ...validData,
178
+ password: "",
179
+ passwordConfirm: "",
180
+ });
181
+ expect(result.success).toBe(false);
182
+ });
183
+
184
+ it("should fail if data protection is not accepted", () => {
185
+ const schema = createRegistrationSchema(baseState);
186
+ const result = schema.safeParse({
187
+ ...validData,
188
+ acceptedDataProtection: false,
189
+ });
190
+ expect(result.success).toBe(false);
191
+ if (!result.success) {
192
+ expect(result.error.issues[0].message).toBe(
193
+ "Bitte akzeptieren Sie die Datenschutzbestimmungen.",
194
+ );
195
+ expect(result.error.issues[0].path).toContain("acceptedDataProtection");
196
+ }
197
+ });
198
+
199
+ it("should fail if street does not contain a house number", () => {
200
+ const schema = createRegistrationSchema(baseState);
201
+ const result = schema.safeParse({
202
+ ...validData,
203
+ billingAddress: {
204
+ ...validAddress,
205
+ street: "Musterstraße",
206
+ },
207
+ });
208
+ expect(result.success).toBe(false);
209
+ if (!result.success) {
210
+ expect(result.error.issues[0].message).toBe(
211
+ "Bitte geben Sie Ihre Straße und Hausnummer an.",
212
+ );
213
+ expect(result.error.issues[0].path).toContain("billingAddress");
214
+ expect(result.error.issues[0].path).toContain("street");
215
+ }
216
+ });
217
+
218
+ it("should fail if shipping street does not contain a house number when different", () => {
219
+ const shippingState = { ...baseState, isShippingAddressDifferent: true };
220
+ const shippingData = {
221
+ ...validData,
222
+ isShippingAddressDifferent: true,
223
+ shippingAddress: {
224
+ ...validAddress,
225
+ street: "Shipping Street",
226
+ },
227
+ };
228
+ const schema = createRegistrationSchema(shippingState);
229
+ const result = schema.safeParse(shippingData);
230
+ expect(result.success).toBe(false);
231
+ if (!result.success) {
232
+ const messages = result.error.issues.map((i) => i.message);
233
+ expect(messages).toContain(
234
+ "Bitte geben Sie Ihre Straße und Hausnummer an.",
235
+ );
236
+ const streetIssue = result.error.issues.find((i) =>
237
+ i.path.includes("street"),
238
+ );
239
+ expect(streetIssue?.path).toContain("shippingAddress");
240
+ }
241
+ });
242
+ });
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { mockNuxtImport } from "@nuxt/test-utils/runtime";
3
+ import { useAddressAutocomplete } from "~/composables/useAddressAutocomplete";
4
+ import { ref } from "vue";
5
+
6
+ // Mock useFetch
7
+ const { mockUseFetch } = vi.hoisted(() => ({
8
+ mockUseFetch: vi.fn(),
9
+ }));
10
+ mockNuxtImport("useFetch", () => mockUseFetch);
11
+
12
+ // Mock useRuntimeConfig
13
+ mockNuxtImport("useRuntimeConfig", () => () => ({
14
+ geoapifyApiKey: "test-api-key",
15
+ }));
16
+
17
+ // Mock useValidCitiesForDelivery
18
+ const { mockBoundingBoxCoordinates } = vi.hoisted(() => ({
19
+ mockBoundingBoxCoordinates: {
20
+ value: "8.822251,50.055026,8.899077,50.104327",
21
+ },
22
+ }));
23
+ mockNuxtImport("useValidCitiesForDelivery", () => () => ({
24
+ validCities: ref(["Obertshausen", "Lämmerspiel", "Hausen"]),
25
+ boundingBoxCoordinates: mockBoundingBoxCoordinates,
26
+ }));
27
+
28
+ describe("useAddressAutocomplete", () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ });
32
+
33
+ it("should return empty suggestions for short input", async () => {
34
+ const { getSuggestions } = useAddressAutocomplete();
35
+ const suggestions = await getSuggestions("ab");
36
+ expect(suggestions).toEqual([]);
37
+ expect(mockUseFetch).not.toHaveBeenCalled();
38
+ });
39
+
40
+ it("should fetch suggestions from Geoapify", async () => {
41
+ const mockResponse = {
42
+ features: [
43
+ {
44
+ properties: {
45
+ street: "Main St",
46
+ housenumber: "1",
47
+ city: "Obertshausen",
48
+ postcode: "63179",
49
+ formatted: "Main St 1, 63179 Obertshausen, Germany",
50
+ },
51
+ },
52
+ ],
53
+ };
54
+
55
+ mockUseFetch.mockResolvedValue({
56
+ data: ref(mockResponse),
57
+ });
58
+
59
+ const { getSuggestions } = useAddressAutocomplete();
60
+ const suggestions = await getSuggestions("Main St 1");
61
+
62
+ expect(mockUseFetch).toHaveBeenCalledWith(
63
+ "/api/address/autocomplete",
64
+ expect.objectContaining({
65
+ query: expect.objectContaining({
66
+ text: "Main St 1",
67
+ lang: "de",
68
+ limit: 5,
69
+ filter: "rect:8.822251,50.055026,8.899077,50.104327",
70
+ }),
71
+ }),
72
+ expect.any(String),
73
+ );
74
+ expect(suggestions).toHaveLength(1);
75
+ expect(suggestions[0]).toEqual({
76
+ street: "Main St 1",
77
+ city: "Obertshausen",
78
+ zipcode: "63179",
79
+ label: "Main St 1, 63179 Obertshausen, Germany",
80
+ });
81
+ });
82
+
83
+ it("should handle missing features in API response", async () => {
84
+ mockUseFetch.mockResolvedValue({
85
+ data: ref({}),
86
+ });
87
+
88
+ const { getSuggestions } = useAddressAutocomplete();
89
+ const suggestions = await getSuggestions("Some address");
90
+ expect(suggestions).toEqual([]);
91
+ });
92
+
93
+ it("should handle fetch error", async () => {
94
+ mockUseFetch.mockRejectedValue(new Error("Network error"));
95
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
96
+
97
+ const { getSuggestions } = useAddressAutocomplete();
98
+ const suggestions = await getSuggestions("Some address");
99
+
100
+ expect(suggestions).toEqual([]);
101
+ expect(consoleSpy).toHaveBeenCalled();
102
+ consoleSpy.mockRestore();
103
+ });
104
+ it("should filter suggestions based on valid cities", async () => {
105
+ const mockResponse = {
106
+ features: [
107
+ {
108
+ properties: {
109
+ street: "Main St",
110
+ housenumber: "1",
111
+ city: "Obertshausen",
112
+ postcode: "63179",
113
+ formatted: "Main St 1, 63179 Obertshausen, Germany",
114
+ },
115
+ },
116
+ {
117
+ properties: {
118
+ street: "Other St",
119
+ housenumber: "2",
120
+ city: "Berlin",
121
+ postcode: "10115",
122
+ formatted: "Other St 2, 10115 Berlin, Germany",
123
+ },
124
+ },
125
+ ],
126
+ };
127
+
128
+ mockUseFetch.mockResolvedValue({
129
+ data: ref(mockResponse),
130
+ });
131
+
132
+ const { getSuggestions } = useAddressAutocomplete();
133
+ const suggestions = await getSuggestions("Main St 1");
134
+
135
+ expect(suggestions).toHaveLength(1);
136
+ expect(suggestions[0]?.city).toBe("Obertshausen");
137
+ });
138
+
139
+ it("should not include filter when boundingBoxCoordinates is empty", async () => {
140
+ mockBoundingBoxCoordinates.value = "";
141
+ mockUseFetch.mockResolvedValue({
142
+ data: ref({ features: [] }),
143
+ });
144
+
145
+ const { getSuggestions } = useAddressAutocomplete();
146
+ await getSuggestions("Main St 1");
147
+
148
+ expect(mockUseFetch).toHaveBeenCalledWith(
149
+ "/api/address/autocomplete",
150
+ expect.objectContaining({
151
+ query: expect.not.objectContaining({
152
+ filter: expect.anything(),
153
+ }),
154
+ }),
155
+ expect.any(String),
156
+ );
157
+
158
+ // Reset for other tests
159
+ mockBoundingBoxCoordinates.value = "8.822251,50.055026,8.899077,50.104327";
160
+ });
161
+ });