@getmicdrop/venue-calendar 3.3.1 → 3.3.3
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/dist/{VenueCalendar-Xppig0q_.js → VenueCalendar-qwLPhCFH.js} +2698 -2693
- package/dist/VenueCalendar-qwLPhCFH.js.map +1 -0
- package/dist/api/api.cjs +2 -0
- package/dist/api/api.cjs.map +1 -0
- package/dist/api/api.mjs +787 -0
- package/dist/api/api.mjs.map +1 -0
- package/dist/api/client.d.ts +46 -0
- package/dist/api/events.d.ts +102 -0
- package/dist/api/index.d.ts +38 -0
- package/dist/api/orders.d.ts +104 -0
- package/dist/api/promo.d.ts +45 -0
- package/dist/api/transformers/event.d.ts +86 -0
- package/dist/api/transformers/index.d.ts +9 -0
- package/dist/api/transformers/order.d.ts +105 -0
- package/dist/api/transformers/venue.d.ts +48 -0
- package/dist/api/venues.d.ts +33 -0
- package/dist/{index-BjErG0CG.js → index-TUrDiCfO.js} +2 -2
- package/dist/{index-BjErG0CG.js.map → index-TUrDiCfO.js.map} +1 -1
- package/dist/venue-calendar.css +1 -1
- package/dist/venue-calendar.es.js +1 -1
- package/dist/venue-calendar.iife.js +14 -14
- package/dist/venue-calendar.iife.js.map +1 -1
- package/dist/venue-calendar.umd.js +14 -14
- package/dist/venue-calendar.umd.js.map +1 -1
- package/package.json +96 -94
- package/dist/VenueCalendar-Xppig0q_.js.map +0 -1
- package/src/lib/api/client.ts +0 -210
- package/src/lib/api/events.ts +0 -358
- package/src/lib/api/index.ts +0 -182
- package/src/lib/api/orders.ts +0 -390
- package/src/lib/api/promo.ts +0 -164
- package/src/lib/api/transformers/event.ts +0 -248
- package/src/lib/api/transformers/index.ts +0 -29
- package/src/lib/api/transformers/order.ts +0 -207
- package/src/lib/api/transformers/venue.ts +0 -118
- package/src/lib/api/venues.ts +0 -100
- package/src/lib/utils/api.js +0 -790
- package/src/lib/utils/api.test.js +0 -1284
- package/src/lib/utils/constants.js +0 -8
- package/src/lib/utils/constants.test.js +0 -39
- package/src/lib/utils/datetime.js +0 -266
- package/src/lib/utils/datetime.test.js +0 -340
- package/src/lib/utils/event-transform.js +0 -464
- package/src/lib/utils/event-transform.test.js +0 -413
- package/src/lib/utils/logger.js +0 -105
- package/src/lib/utils/timezone.js +0 -109
- package/src/lib/utils/timezone.test.js +0 -222
- package/src/lib/utils/utils.js +0 -806
- package/src/lib/utils/utils.test.js +0 -959
- /package/{src/lib/api/types.ts → dist/api/types.d.ts} +0 -0
|
@@ -1,1284 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
createPaymentIntent,
|
|
4
|
-
fetchAllVenues,
|
|
5
|
-
fetchVenueEvents,
|
|
6
|
-
getMonthEvents,
|
|
7
|
-
getOrgMonthEvents,
|
|
8
|
-
transformEventData,
|
|
9
|
-
fetchEventDetails,
|
|
10
|
-
fetchEventTickets,
|
|
11
|
-
validatePromoCode,
|
|
12
|
-
hasPromoCodes,
|
|
13
|
-
extendCheckoutSession,
|
|
14
|
-
getSessionStatus,
|
|
15
|
-
completeReservation,
|
|
16
|
-
cancelReservation,
|
|
17
|
-
fetchEventPerformers,
|
|
18
|
-
getSeriesOccurrences,
|
|
19
|
-
checkEventPassword,
|
|
20
|
-
testNetworkConnection,
|
|
21
|
-
fetchSeriesOccurrences,
|
|
22
|
-
} from './api.js';
|
|
23
|
-
|
|
24
|
-
describe('api.js', () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
vi.restoreAllMocks();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// Note: Stripe publishable key is now fetched dynamically from the payment intent API response
|
|
30
|
-
// No more STRIPE_PUBLISHABLE_KEY or getStripePublishableKey exports
|
|
31
|
-
|
|
32
|
-
describe('createPaymentIntent', () => {
|
|
33
|
-
it('creates payment intent successfully', async () => {
|
|
34
|
-
global.fetch = vi.fn()
|
|
35
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
36
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ clientSecret: 'pi_secret' }) });
|
|
37
|
-
|
|
38
|
-
const result = await createPaymentIntent('cart-123', { 1: 2 });
|
|
39
|
-
expect(result.clientSecret).toBe('pi_secret');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('returns null on payment failure', async () => {
|
|
43
|
-
global.fetch = vi.fn()
|
|
44
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
45
|
-
.mockResolvedValueOnce({ ok: false, json: () => Promise.resolve({ error: 'Failed' }) });
|
|
46
|
-
|
|
47
|
-
const result = await createPaymentIntent('cart-123', {});
|
|
48
|
-
expect(result).toBeNull();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('returns null on network error', async () => {
|
|
52
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
53
|
-
|
|
54
|
-
const result = await createPaymentIntent('cart-123', {});
|
|
55
|
-
expect(result).toBeNull();
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('fetchAllVenues', () => {
|
|
60
|
-
it('returns venues array on success', async () => {
|
|
61
|
-
const mockVenues = [{ id: 1, name: 'Venue 1' }, { id: 2, name: 'Venue 2' }];
|
|
62
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
63
|
-
ok: true,
|
|
64
|
-
json: () => Promise.resolve(mockVenues),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const result = await fetchAllVenues(123);
|
|
68
|
-
expect(result).toEqual(mockVenues);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('returns empty array when no orgId', async () => {
|
|
72
|
-
const result = await fetchAllVenues(null);
|
|
73
|
-
expect(result).toEqual([]);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('returns empty array on API error', async () => {
|
|
77
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500, statusText: 'Error' });
|
|
78
|
-
|
|
79
|
-
const result = await fetchAllVenues(123);
|
|
80
|
-
expect(result).toEqual([]);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('returns empty array on network error', async () => {
|
|
84
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
85
|
-
|
|
86
|
-
const result = await fetchAllVenues(123);
|
|
87
|
-
expect(result).toEqual([]);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('returns empty array when API returns non-array', async () => {
|
|
91
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
92
|
-
ok: true,
|
|
93
|
-
json: () => Promise.resolve(null),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const result = await fetchAllVenues(123);
|
|
97
|
-
expect(result).toEqual([]);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe('fetchVenueEvents', () => {
|
|
102
|
-
it('returns events array on success', async () => {
|
|
103
|
-
const mockEvents = [{ id: 1, title: 'Event 1' }];
|
|
104
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
105
|
-
ok: true,
|
|
106
|
-
json: () => Promise.resolve(mockEvents),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const result = await fetchVenueEvents(456);
|
|
110
|
-
expect(result).toEqual(mockEvents);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('returns empty array when no venueId', async () => {
|
|
114
|
-
const result = await fetchVenueEvents(null);
|
|
115
|
-
expect(result).toEqual([]);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('returns empty array on API error', async () => {
|
|
119
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
|
|
120
|
-
|
|
121
|
-
const result = await fetchVenueEvents(456);
|
|
122
|
-
expect(result).toEqual([]);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('returns empty array on network error', async () => {
|
|
126
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
127
|
-
|
|
128
|
-
const result = await fetchVenueEvents(456);
|
|
129
|
-
expect(result).toEqual([]);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('getMonthEvents', () => {
|
|
134
|
-
it('returns events and pagination on success', async () => {
|
|
135
|
-
const mockData = {
|
|
136
|
-
events: [{ id: 1 }],
|
|
137
|
-
pagination: { hasPrevMonth: false, hasNextMonth: true },
|
|
138
|
-
year: 2025,
|
|
139
|
-
month: 6,
|
|
140
|
-
};
|
|
141
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
142
|
-
ok: true,
|
|
143
|
-
json: () => Promise.resolve(mockData),
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
const result = await getMonthEvents(456, 2025, 6);
|
|
147
|
-
expect(result.events).toHaveLength(1);
|
|
148
|
-
expect(result.pagination).toBeDefined();
|
|
149
|
-
expect(result.year).toBe(2025);
|
|
150
|
-
expect(result.month).toBe(6);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('returns empty when no venueId', async () => {
|
|
154
|
-
const result = await getMonthEvents(null, 2025, 6);
|
|
155
|
-
expect(result.events).toEqual([]);
|
|
156
|
-
expect(result.pagination).toBeNull();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('returns empty on API error', async () => {
|
|
160
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500 });
|
|
161
|
-
|
|
162
|
-
const result = await getMonthEvents(456, 2025, 6);
|
|
163
|
-
expect(result.events).toEqual([]);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('returns empty on network error', async () => {
|
|
167
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
168
|
-
|
|
169
|
-
const result = await getMonthEvents(456, 2025, 6);
|
|
170
|
-
expect(result.events).toEqual([]);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('getOrgMonthEvents', () => {
|
|
175
|
-
it('returns events and pagination on success', async () => {
|
|
176
|
-
const mockData = {
|
|
177
|
-
events: [{ id: 1 }],
|
|
178
|
-
pagination: { hasPrevMonth: true, hasNextMonth: false },
|
|
179
|
-
};
|
|
180
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
181
|
-
ok: true,
|
|
182
|
-
json: () => Promise.resolve(mockData),
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
const result = await getOrgMonthEvents(123, 2025, 7);
|
|
186
|
-
expect(result.events).toHaveLength(1);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('returns empty when no orgId', async () => {
|
|
190
|
-
const result = await getOrgMonthEvents(null, 2025, 7);
|
|
191
|
-
expect(result.events).toEqual([]);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('transformEventData', () => {
|
|
196
|
-
it('transforms API event to frontend format', () => {
|
|
197
|
-
const apiEvent = {
|
|
198
|
-
id: 123,
|
|
199
|
-
title: 'Test Event',
|
|
200
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
201
|
-
endDateTime: '2025-06-15T22:00:00Z',
|
|
202
|
-
timeZone: 'UTC',
|
|
203
|
-
image: '/poster.jpg',
|
|
204
|
-
status: 'On Sale',
|
|
205
|
-
venueId: 456,
|
|
206
|
-
description: 'A test event',
|
|
207
|
-
eventSeriesId: 789,
|
|
208
|
-
availableTickets: [
|
|
209
|
-
{ remainingCapacity: 50, totalCapacity: 100, salesChannel: 1 },
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const result = transformEventData(apiEvent);
|
|
214
|
-
expect(result.id).toBe(123);
|
|
215
|
-
expect(result.name).toBe('Test Event');
|
|
216
|
-
expect(result.image).toContain('https://moxy.sfo3.digitaloceanspaces.com');
|
|
217
|
-
expect(result.status).toBe('On Sale');
|
|
218
|
-
expect(result.venueId).toBe(456);
|
|
219
|
-
expect(result.eventSeriesId).toBe(789);
|
|
220
|
-
expect(result.ticketsRemaining).toBe(50);
|
|
221
|
-
expect(result.ticketsTotal).toBe(100);
|
|
222
|
-
expect(result.isSoldOut).toBe(false);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('handles event with no tickets', () => {
|
|
226
|
-
const apiEvent = { id: 1, title: 'No Tickets' };
|
|
227
|
-
const result = transformEventData(apiEvent);
|
|
228
|
-
expect(result.ticketsRemaining).toBe(0);
|
|
229
|
-
expect(result.ticketsTotal).toBe(0);
|
|
230
|
-
expect(result.isSoldOut).toBe(false);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('handles sold out event', () => {
|
|
234
|
-
const apiEvent = {
|
|
235
|
-
id: 1,
|
|
236
|
-
availableTickets: [
|
|
237
|
-
{ remainingCapacity: 0, totalCapacity: 100, salesChannel: 1 },
|
|
238
|
-
],
|
|
239
|
-
};
|
|
240
|
-
const result = transformEventData(apiEvent);
|
|
241
|
-
expect(result.isSoldOut).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('excludes door-only tickets from scarcity', () => {
|
|
245
|
-
const apiEvent = {
|
|
246
|
-
id: 1,
|
|
247
|
-
availableTickets: [
|
|
248
|
-
{ remainingCapacity: 0, totalCapacity: 50, salesChannel: 2 }, // Door only
|
|
249
|
-
{ remainingCapacity: 25, totalCapacity: 50, salesChannel: 1 },
|
|
250
|
-
],
|
|
251
|
-
};
|
|
252
|
-
const result = transformEventData(apiEvent);
|
|
253
|
-
expect(result.ticketsRemaining).toBe(25);
|
|
254
|
-
expect(result.ticketsTotal).toBe(50);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('handles various image URL formats', () => {
|
|
258
|
-
expect(transformEventData({ image: '/poster.jpg' }).image).toContain('digitaloceanspaces.com');
|
|
259
|
-
expect(transformEventData({ imageUrl: '/cover.jpg' }).image).toContain('digitaloceanspaces.com');
|
|
260
|
-
expect(transformEventData({ image_url: '/img.jpg' }).image).toContain('digitaloceanspaces.com');
|
|
261
|
-
expect(transformEventData({ image: 'https://example.com/img.jpg' }).image).toBe('https://example.com/img.jpg');
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('handles timezone conversion', () => {
|
|
265
|
-
const apiEvent = {
|
|
266
|
-
id: 1,
|
|
267
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
268
|
-
timeZone: 'California USA',
|
|
269
|
-
};
|
|
270
|
-
const result = transformEventData(apiEvent);
|
|
271
|
-
expect(result.timeZone).toBe('America/Los_Angeles');
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('uses fallback fields for name and date', () => {
|
|
275
|
-
const apiEvent1 = { name: 'Event Name' };
|
|
276
|
-
expect(transformEventData(apiEvent1).name).toBe('Event Name');
|
|
277
|
-
|
|
278
|
-
const apiEvent2 = { date: '2025-06-15' };
|
|
279
|
-
expect(transformEventData(apiEvent2).date).toBe('2025-06-15');
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('fetchEventDetails', () => {
|
|
284
|
-
it('fetches event details successfully', async () => {
|
|
285
|
-
const mockEvent = { id: 123, title: 'Test' };
|
|
286
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
287
|
-
ok: true,
|
|
288
|
-
json: () => Promise.resolve(mockEvent),
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const result = await fetchEventDetails(123, mockFetch);
|
|
292
|
-
expect(result).toEqual(mockEvent);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('throws on API error', async () => {
|
|
296
|
-
const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
|
|
297
|
-
|
|
298
|
-
await expect(fetchEventDetails(123, mockFetch)).rejects.toThrow();
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe('fetchEventTickets', () => {
|
|
303
|
-
it('fetches tickets successfully', async () => {
|
|
304
|
-
const mockTickets = [{ id: 1, name: 'GA' }];
|
|
305
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
306
|
-
ok: true,
|
|
307
|
-
json: () => Promise.resolve(mockTickets),
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
const result = await fetchEventTickets(123);
|
|
311
|
-
expect(result).toEqual(mockTickets);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('throws on API error', async () => {
|
|
315
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500 });
|
|
316
|
-
|
|
317
|
-
await expect(fetchEventTickets(123)).rejects.toThrow();
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('returns empty array for non-array response', async () => {
|
|
321
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
322
|
-
ok: true,
|
|
323
|
-
json: () => Promise.resolve(null),
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
const result = await fetchEventTickets(123);
|
|
327
|
-
expect(result).toEqual([]);
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
describe('validatePromoCode', () => {
|
|
332
|
-
it('returns valid response for good code', async () => {
|
|
333
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
334
|
-
ok: true,
|
|
335
|
-
json: () => Promise.resolve({ valid: true, discountType: 'percentage', amount: 10 }),
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const result = await validatePromoCode('event123', 'SAVE10');
|
|
339
|
-
expect(result.valid).toBe(true);
|
|
340
|
-
expect(result.discountType).toBe('percentage');
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it('returns invalid for bad code', async () => {
|
|
344
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 400 });
|
|
345
|
-
|
|
346
|
-
const result = await validatePromoCode('event123', 'BADCODE');
|
|
347
|
-
expect(result.valid).toBe(false);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it('returns invalid on error', async () => {
|
|
351
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
352
|
-
|
|
353
|
-
const result = await validatePromoCode('event123', 'CODE');
|
|
354
|
-
expect(result.valid).toBe(false);
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
describe('hasPromoCodes', () => {
|
|
359
|
-
it('returns true when promo codes exist', async () => {
|
|
360
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
361
|
-
ok: true,
|
|
362
|
-
json: () => Promise.resolve({ hasPromoCodes: true }),
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
const result = await hasPromoCodes('event123');
|
|
366
|
-
expect(result).toBe(true);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('returns false when no promo codes', async () => {
|
|
370
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
371
|
-
ok: true,
|
|
372
|
-
json: () => Promise.resolve({ hasPromoCodes: false }),
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
const result = await hasPromoCodes('event123');
|
|
376
|
-
expect(result).toBe(false);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('defaults to true on error', async () => {
|
|
380
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Error'));
|
|
381
|
-
|
|
382
|
-
const result = await hasPromoCodes('event123');
|
|
383
|
-
expect(result).toBe(true);
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it('defaults to true on API error', async () => {
|
|
387
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500 });
|
|
388
|
-
|
|
389
|
-
const result = await hasPromoCodes('event123');
|
|
390
|
-
expect(result).toBe(true);
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
describe('extendCheckoutSession', () => {
|
|
395
|
-
it('extends session successfully', async () => {
|
|
396
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
397
|
-
ok: true,
|
|
398
|
-
json: () => Promise.resolve({
|
|
399
|
-
newExpiryTime: '2025-01-15T12:15:00Z',
|
|
400
|
-
remainingExtensions: 2,
|
|
401
|
-
}),
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
const result = await extendCheckoutSession('order-uuid');
|
|
405
|
-
expect(result.success).toBe(true);
|
|
406
|
-
expect(result.newExpiryTime).toBeDefined();
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
it('returns error on failure', async () => {
|
|
410
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
411
|
-
ok: false,
|
|
412
|
-
json: () => Promise.resolve({ error: 'Max extensions reached' }),
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
const result = await extendCheckoutSession('order-uuid');
|
|
416
|
-
expect(result.success).toBe(false);
|
|
417
|
-
expect(result.error).toContain('extensions');
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it('handles network error', async () => {
|
|
421
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
422
|
-
|
|
423
|
-
const result = await extendCheckoutSession('order-uuid');
|
|
424
|
-
expect(result.success).toBe(false);
|
|
425
|
-
expect(result.error).toContain('Network');
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
describe('getSessionStatus', () => {
|
|
430
|
-
it('gets session status successfully', async () => {
|
|
431
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
432
|
-
ok: true,
|
|
433
|
-
json: () => Promise.resolve({
|
|
434
|
-
expiresAt: '2025-01-15T12:00:00Z',
|
|
435
|
-
extensionCount: 1,
|
|
436
|
-
remainingExtensions: 2,
|
|
437
|
-
canExtend: true,
|
|
438
|
-
}),
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
const result = await getSessionStatus('order-uuid');
|
|
442
|
-
expect(result.expiresAt).toBeDefined();
|
|
443
|
-
expect(result.canExtend).toBe(true);
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it('returns error for invalid session', async () => {
|
|
447
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
448
|
-
ok: false,
|
|
449
|
-
json: () => Promise.resolve({ error: 'No session' }),
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
const result = await getSessionStatus('order-uuid');
|
|
453
|
-
expect(result.error).toBeDefined();
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
describe('completeReservation', () => {
|
|
458
|
-
it('completes reservation successfully', async () => {
|
|
459
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
460
|
-
ok: true,
|
|
461
|
-
json: () => Promise.resolve({ message: 'Reservation completed' }),
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
const result = await completeReservation('order-uuid');
|
|
465
|
-
expect(result.success).toBe(true);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('returns error on failure', async () => {
|
|
469
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
470
|
-
ok: false,
|
|
471
|
-
json: () => Promise.resolve({ error: 'Failed' }),
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
const result = await completeReservation('order-uuid');
|
|
475
|
-
expect(result.success).toBe(false);
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
describe('cancelReservation', () => {
|
|
480
|
-
it('cancels reservation successfully', async () => {
|
|
481
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
482
|
-
ok: true,
|
|
483
|
-
json: () => Promise.resolve({ message: 'Cancelled' }),
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
const result = await cancelReservation('order-uuid');
|
|
487
|
-
expect(result.success).toBe(true);
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it('handles network error', async () => {
|
|
491
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
492
|
-
|
|
493
|
-
const result = await cancelReservation('order-uuid');
|
|
494
|
-
expect(result.success).toBe(false);
|
|
495
|
-
expect(result.error).toContain('Network');
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
describe('fetchEventPerformers', () => {
|
|
500
|
-
it('fetches performers successfully', async () => {
|
|
501
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
502
|
-
ok: true,
|
|
503
|
-
json: () => Promise.resolve({
|
|
504
|
-
performers: [{ id: 1, stageName: 'DJ Cool' }],
|
|
505
|
-
showPerformers: true,
|
|
506
|
-
}),
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
const result = await fetchEventPerformers(123);
|
|
510
|
-
expect(result.performers).toHaveLength(1);
|
|
511
|
-
expect(result.showPerformers).toBe(true);
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
it('returns empty when no eventId', async () => {
|
|
515
|
-
const result = await fetchEventPerformers(null);
|
|
516
|
-
expect(result.performers).toEqual([]);
|
|
517
|
-
expect(result.showPerformers).toBe(false);
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
it('returns empty on API error', async () => {
|
|
521
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
|
|
522
|
-
|
|
523
|
-
const result = await fetchEventPerformers(123);
|
|
524
|
-
expect(result.performers).toEqual([]);
|
|
525
|
-
});
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
describe('getSeriesOccurrences', () => {
|
|
529
|
-
it('gets series occurrences successfully', async () => {
|
|
530
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
531
|
-
ok: true,
|
|
532
|
-
json: () => Promise.resolve({
|
|
533
|
-
seriesId: 1,
|
|
534
|
-
seriesName: 'Weekly Show',
|
|
535
|
-
occurrences: [{ id: 1, startDateTime: '2025-01-15T20:00:00Z' }],
|
|
536
|
-
}),
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
const result = await getSeriesOccurrences(1);
|
|
540
|
-
expect(result.seriesId).toBe(1);
|
|
541
|
-
expect(result.occurrences).toHaveLength(1);
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
it('returns error for invalid series', async () => {
|
|
545
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
546
|
-
ok: false,
|
|
547
|
-
json: () => Promise.resolve({ error: 'Not found' }),
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
const result = await getSeriesOccurrences(999);
|
|
551
|
-
expect(result.error).toBeDefined();
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
describe('checkEventPassword', () => {
|
|
556
|
-
it('returns valid for correct password', async () => {
|
|
557
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
558
|
-
ok: true,
|
|
559
|
-
json: () => Promise.resolve({ valid: true }),
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
const result = await checkEventPassword('event123', 'secret');
|
|
563
|
-
expect(result.valid).toBe(true);
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it('returns invalid for wrong password', async () => {
|
|
567
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 401 });
|
|
568
|
-
|
|
569
|
-
const result = await checkEventPassword('event123', 'wrong');
|
|
570
|
-
expect(result.valid).toBe(false);
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
it('handles boolean response', async () => {
|
|
574
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
575
|
-
ok: true,
|
|
576
|
-
json: () => Promise.resolve(true),
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
const result = await checkEventPassword('event123', 'secret');
|
|
580
|
-
expect(result.valid).toBe(true);
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
it('returns invalid on error', async () => {
|
|
584
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Error'));
|
|
585
|
-
|
|
586
|
-
const result = await checkEventPassword('event123', 'secret');
|
|
587
|
-
expect(result.valid).toBe(false);
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
describe('testNetworkConnection', () => {
|
|
592
|
-
beforeEach(() => {
|
|
593
|
-
vi.useFakeTimers();
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
afterEach(() => {
|
|
597
|
-
vi.useRealTimers();
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
it('tests organization connection', async () => {
|
|
601
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: true });
|
|
602
|
-
|
|
603
|
-
const promise = testNetworkConnection(123, null);
|
|
604
|
-
vi.runAllTimers();
|
|
605
|
-
const result = await promise;
|
|
606
|
-
expect(result.organization).toBe(true);
|
|
607
|
-
expect(result.timestamp).toBeDefined();
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
it('tests venue connection', async () => {
|
|
611
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: true });
|
|
612
|
-
|
|
613
|
-
const promise = testNetworkConnection(null, 456);
|
|
614
|
-
vi.runAllTimers();
|
|
615
|
-
const result = await promise;
|
|
616
|
-
expect(result.venue).toBe(true);
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it('handles connection failures', async () => {
|
|
620
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Timeout'));
|
|
621
|
-
|
|
622
|
-
const promise = testNetworkConnection(123, 456);
|
|
623
|
-
vi.runAllTimers();
|
|
624
|
-
const result = await promise;
|
|
625
|
-
expect(result.organization).toBe(false);
|
|
626
|
-
expect(result.venue).toBe(false);
|
|
627
|
-
});
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
describe('fetchSeriesOccurrences', () => {
|
|
631
|
-
it('returns empty for invalid series ID', async () => {
|
|
632
|
-
const result = await fetchSeriesOccurrences(0, 456);
|
|
633
|
-
expect(result).toEqual([]);
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
it('returns empty for null series ID', async () => {
|
|
637
|
-
const result = await fetchSeriesOccurrences(null, 456);
|
|
638
|
-
expect(result).toEqual([]);
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
it('fetches occurrences from new API', async () => {
|
|
642
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
643
|
-
ok: true,
|
|
644
|
-
json: () => Promise.resolve({
|
|
645
|
-
seriesId: 1,
|
|
646
|
-
occurrences: [
|
|
647
|
-
{ id: 1, startDateTime: '2025-01-20T20:00:00Z', slug: 'event-1' },
|
|
648
|
-
{ id: 2, startDateTime: '2025-01-15T20:00:00Z', slug: 'event-2' },
|
|
649
|
-
],
|
|
650
|
-
}),
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
654
|
-
expect(result).toHaveLength(2);
|
|
655
|
-
// Should be sorted by date
|
|
656
|
-
expect(result[0].id).toBe(2);
|
|
657
|
-
expect(result[1].id).toBe(1);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it('falls back to listAllEvents when new API fails', async () => {
|
|
661
|
-
global.fetch = vi.fn()
|
|
662
|
-
// First call - getSeriesOccurrences fails
|
|
663
|
-
.mockResolvedValueOnce({
|
|
664
|
-
ok: false,
|
|
665
|
-
json: () => Promise.resolve({ error: 'Not found' }),
|
|
666
|
-
})
|
|
667
|
-
// Second call - listAllEvents
|
|
668
|
-
.mockResolvedValueOnce({
|
|
669
|
-
ok: true,
|
|
670
|
-
json: () => Promise.resolve([
|
|
671
|
-
{ id: 1, eventSeriesId: 1 },
|
|
672
|
-
{ id: 2, eventSeriesId: 2 }, // Different series
|
|
673
|
-
{ id: 3, eventSeriesId: 1 },
|
|
674
|
-
]),
|
|
675
|
-
})
|
|
676
|
-
// Third and fourth calls - getEvent for each matching event
|
|
677
|
-
.mockResolvedValueOnce({
|
|
678
|
-
ok: true,
|
|
679
|
-
json: () => Promise.resolve({
|
|
680
|
-
id: 1,
|
|
681
|
-
startDateTime: '2025-01-15T20:00:00Z',
|
|
682
|
-
availableTickets: [],
|
|
683
|
-
}),
|
|
684
|
-
})
|
|
685
|
-
.mockResolvedValueOnce({
|
|
686
|
-
ok: true,
|
|
687
|
-
json: () => Promise.resolve({
|
|
688
|
-
id: 3,
|
|
689
|
-
startDateTime: '2025-01-16T20:00:00Z',
|
|
690
|
-
availableTickets: [],
|
|
691
|
-
}),
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
695
|
-
expect(result.length).toBeGreaterThan(0);
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
it('handles network error gracefully', async () => {
|
|
699
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
700
|
-
|
|
701
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
702
|
-
expect(result).toEqual([]);
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
it('returns empty when no venueId for fallback', async () => {
|
|
706
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
707
|
-
ok: false,
|
|
708
|
-
json: () => Promise.resolve({ error: 'Not found' }),
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
const result = await fetchSeriesOccurrences(1, null);
|
|
712
|
-
expect(result).toEqual([]);
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
it('handles getEvent failure in fallback', async () => {
|
|
716
|
-
global.fetch = vi.fn()
|
|
717
|
-
// getSeriesOccurrences fails
|
|
718
|
-
.mockResolvedValueOnce({
|
|
719
|
-
ok: false,
|
|
720
|
-
json: () => Promise.resolve({ error: 'Not found' }),
|
|
721
|
-
})
|
|
722
|
-
// listAllEvents succeeds
|
|
723
|
-
.mockResolvedValueOnce({
|
|
724
|
-
ok: true,
|
|
725
|
-
json: () => Promise.resolve([
|
|
726
|
-
{ id: 1, eventSeriesId: 1 },
|
|
727
|
-
]),
|
|
728
|
-
})
|
|
729
|
-
// getEvent fails
|
|
730
|
-
.mockResolvedValueOnce({ ok: false, status: 500 });
|
|
731
|
-
|
|
732
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
733
|
-
// Should filter out null (failed) events
|
|
734
|
-
expect(result).toEqual([]);
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
it('handles listAllEvents failure in fallback', async () => {
|
|
738
|
-
global.fetch = vi.fn()
|
|
739
|
-
// getSeriesOccurrences fails
|
|
740
|
-
.mockResolvedValueOnce({
|
|
741
|
-
ok: false,
|
|
742
|
-
json: () => Promise.resolve({ error: 'Not found' }),
|
|
743
|
-
})
|
|
744
|
-
// listAllEvents also fails
|
|
745
|
-
.mockResolvedValueOnce({ ok: false, status: 500 });
|
|
746
|
-
|
|
747
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
748
|
-
expect(result).toEqual([]);
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
it('computes CTA state for sold out tickets', async () => {
|
|
752
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
753
|
-
ok: true,
|
|
754
|
-
json: () => Promise.resolve({
|
|
755
|
-
seriesId: 1,
|
|
756
|
-
occurrences: [
|
|
757
|
-
{
|
|
758
|
-
id: 1,
|
|
759
|
-
startDateTime: '2025-06-15T20:00:00Z',
|
|
760
|
-
availableTickets: [
|
|
761
|
-
{ remainingCapacity: 0, soldOut: true, salesChannel: 1 },
|
|
762
|
-
],
|
|
763
|
-
},
|
|
764
|
-
],
|
|
765
|
-
}),
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
769
|
-
expect(result).toHaveLength(1);
|
|
770
|
-
// ctaState should be computed
|
|
771
|
-
expect(result[0].ctaState).toBeDefined();
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
it('computes CTA state for sales ended', async () => {
|
|
775
|
-
const pastDate = new Date(Date.now() - 86400000).toISOString();
|
|
776
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
777
|
-
ok: true,
|
|
778
|
-
json: () => Promise.resolve({
|
|
779
|
-
seriesId: 1,
|
|
780
|
-
occurrences: [
|
|
781
|
-
{
|
|
782
|
-
id: 1,
|
|
783
|
-
startDateTime: pastDate,
|
|
784
|
-
endDateTime: pastDate,
|
|
785
|
-
availableTickets: [
|
|
786
|
-
{ remainingCapacity: 10, salesEnd: pastDate, salesChannel: 1 },
|
|
787
|
-
],
|
|
788
|
-
},
|
|
789
|
-
],
|
|
790
|
-
}),
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
794
|
-
expect(result).toHaveLength(1);
|
|
795
|
-
});
|
|
796
|
-
|
|
797
|
-
it('computes CTA state for coming soon tickets', async () => {
|
|
798
|
-
const futureDate = new Date(Date.now() + 86400000).toISOString();
|
|
799
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
800
|
-
ok: true,
|
|
801
|
-
json: () => Promise.resolve({
|
|
802
|
-
seriesId: 1,
|
|
803
|
-
occurrences: [
|
|
804
|
-
{
|
|
805
|
-
id: 1,
|
|
806
|
-
startDateTime: futureDate,
|
|
807
|
-
availableTickets: [
|
|
808
|
-
{ remainingCapacity: 100, salesBegin: futureDate, salesChannel: 1 },
|
|
809
|
-
],
|
|
810
|
-
},
|
|
811
|
-
],
|
|
812
|
-
}),
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
816
|
-
expect(result).toHaveLength(1);
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
it('uses occurrence ctaState if provided by API', async () => {
|
|
820
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
821
|
-
ok: true,
|
|
822
|
-
json: () => Promise.resolve({
|
|
823
|
-
seriesId: 1,
|
|
824
|
-
occurrences: [
|
|
825
|
-
{
|
|
826
|
-
id: 1,
|
|
827
|
-
startDateTime: '2025-06-15T20:00:00Z',
|
|
828
|
-
ctaState: { text: 'Get tickets', disabled: false, reason: 'available' },
|
|
829
|
-
},
|
|
830
|
-
],
|
|
831
|
-
}),
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
const result = await fetchSeriesOccurrences(1, 456);
|
|
835
|
-
expect(result[0].ctaState.text).toBe('Get tickets');
|
|
836
|
-
});
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
describe('transformEventData timezone handling', () => {
|
|
840
|
-
it('handles New York timezone', () => {
|
|
841
|
-
const apiEvent = {
|
|
842
|
-
id: 1,
|
|
843
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
844
|
-
timeZone: 'New York',
|
|
845
|
-
};
|
|
846
|
-
const result = transformEventData(apiEvent);
|
|
847
|
-
expect(result.timeZone).toBe('America/New_York');
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
it('handles Chicago timezone', () => {
|
|
851
|
-
const apiEvent = {
|
|
852
|
-
id: 1,
|
|
853
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
854
|
-
timeZone: 'Chicago USA',
|
|
855
|
-
};
|
|
856
|
-
const result = transformEventData(apiEvent);
|
|
857
|
-
expect(result.timeZone).toBe('America/Chicago');
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
it('handles Denver timezone', () => {
|
|
861
|
-
const apiEvent = {
|
|
862
|
-
id: 1,
|
|
863
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
864
|
-
timeZone: 'Denver',
|
|
865
|
-
};
|
|
866
|
-
const result = transformEventData(apiEvent);
|
|
867
|
-
expect(result.timeZone).toBe('America/Denver');
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
it('handles Phoenix timezone', () => {
|
|
871
|
-
const apiEvent = {
|
|
872
|
-
id: 1,
|
|
873
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
874
|
-
timeZone: 'Phoenix USA',
|
|
875
|
-
};
|
|
876
|
-
const result = transformEventData(apiEvent);
|
|
877
|
-
expect(result.timeZone).toBe('America/Phoenix');
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
it('handles UTC timezone', () => {
|
|
881
|
-
const apiEvent = {
|
|
882
|
-
id: 1,
|
|
883
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
884
|
-
timeZone: 'UTC',
|
|
885
|
-
};
|
|
886
|
-
const result = transformEventData(apiEvent);
|
|
887
|
-
expect(result.timeZone).toBe('UTC');
|
|
888
|
-
});
|
|
889
|
-
|
|
890
|
-
it('handles NaN timezone (defaults to LA)', () => {
|
|
891
|
-
const apiEvent = {
|
|
892
|
-
id: 1,
|
|
893
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
894
|
-
timeZone: 'NaN',
|
|
895
|
-
};
|
|
896
|
-
const result = transformEventData(apiEvent);
|
|
897
|
-
expect(result.timeZone).toBe('America/Los_Angeles');
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
it('handles null timezone (defaults to LA)', () => {
|
|
901
|
-
const apiEvent = {
|
|
902
|
-
id: 1,
|
|
903
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
904
|
-
timeZone: null,
|
|
905
|
-
};
|
|
906
|
-
const result = transformEventData(apiEvent);
|
|
907
|
-
expect(result.timeZone).toBe('America/Los_Angeles');
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
it('handles valid IANA timezone directly', () => {
|
|
911
|
-
const apiEvent = {
|
|
912
|
-
id: 1,
|
|
913
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
914
|
-
timeZone: 'Europe/London',
|
|
915
|
-
};
|
|
916
|
-
const result = transformEventData(apiEvent);
|
|
917
|
-
expect(result.timeZone).toBe('Europe/London');
|
|
918
|
-
});
|
|
919
|
-
|
|
920
|
-
it('handles invalid timezone string (defaults to LA)', () => {
|
|
921
|
-
const apiEvent = {
|
|
922
|
-
id: 1,
|
|
923
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
924
|
-
timeZone: 'Invalid/Timezone/Name',
|
|
925
|
-
};
|
|
926
|
-
const result = transformEventData(apiEvent);
|
|
927
|
-
expect(result.timeZone).toBe('America/Los_Angeles');
|
|
928
|
-
});
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
describe('transformEventData time formatting', () => {
|
|
932
|
-
it('formats start and end time', () => {
|
|
933
|
-
const apiEvent = {
|
|
934
|
-
id: 1,
|
|
935
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
936
|
-
endDateTime: '2025-06-15T22:00:00Z',
|
|
937
|
-
timeZone: 'UTC',
|
|
938
|
-
};
|
|
939
|
-
const result = transformEventData(apiEvent);
|
|
940
|
-
expect(result.timeline).toContain('-');
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
it('formats start time only when no end time', () => {
|
|
944
|
-
const apiEvent = {
|
|
945
|
-
id: 1,
|
|
946
|
-
startDateTime: '2025-06-15T19:00:00Z',
|
|
947
|
-
endDateTime: null,
|
|
948
|
-
timeZone: 'UTC',
|
|
949
|
-
};
|
|
950
|
-
const result = transformEventData(apiEvent);
|
|
951
|
-
expect(result.timeline).not.toContain('-');
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
it('uses fallback startTime/endTime fields', () => {
|
|
955
|
-
const apiEvent = {
|
|
956
|
-
id: 1,
|
|
957
|
-
startTime: '7:00 PM',
|
|
958
|
-
endTime: '10:00 PM',
|
|
959
|
-
};
|
|
960
|
-
const result = transformEventData(apiEvent);
|
|
961
|
-
expect(result.timeline).toBe('7:00 PM - 10:00 PM');
|
|
962
|
-
});
|
|
963
|
-
|
|
964
|
-
it('uses fallback startTime only', () => {
|
|
965
|
-
const apiEvent = {
|
|
966
|
-
id: 1,
|
|
967
|
-
startTime: '7:00 PM',
|
|
968
|
-
};
|
|
969
|
-
const result = transformEventData(apiEvent);
|
|
970
|
-
expect(result.timeline).toBe('7:00 PM');
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
it('returns empty timeline when no time data', () => {
|
|
974
|
-
const apiEvent = { id: 1 };
|
|
975
|
-
const result = transformEventData(apiEvent);
|
|
976
|
-
expect(result.timeline).toBe('');
|
|
977
|
-
});
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
describe('transformEventData ticket fields', () => {
|
|
981
|
-
it('handles quantityRemaining field', () => {
|
|
982
|
-
const apiEvent = {
|
|
983
|
-
id: 1,
|
|
984
|
-
availableTickets: [
|
|
985
|
-
{ quantityRemaining: 25, salesChannel: 1 },
|
|
986
|
-
],
|
|
987
|
-
};
|
|
988
|
-
const result = transformEventData(apiEvent);
|
|
989
|
-
expect(result.ticketsRemaining).toBe(25);
|
|
990
|
-
});
|
|
991
|
-
|
|
992
|
-
it('handles quantity field as fallback', () => {
|
|
993
|
-
const apiEvent = {
|
|
994
|
-
id: 1,
|
|
995
|
-
availableTickets: [
|
|
996
|
-
{ quantity: 30, salesChannel: 1 },
|
|
997
|
-
],
|
|
998
|
-
};
|
|
999
|
-
const result = transformEventData(apiEvent);
|
|
1000
|
-
expect(result.ticketsRemaining).toBe(30);
|
|
1001
|
-
});
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
describe('extendCheckoutSession edge cases', () => {
|
|
1005
|
-
it('handles json parse error on failure response', async () => {
|
|
1006
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1007
|
-
ok: false,
|
|
1008
|
-
json: () => Promise.reject(new Error('Parse error')),
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
const result = await extendCheckoutSession('order-uuid');
|
|
1012
|
-
expect(result.success).toBe(false);
|
|
1013
|
-
expect(result.error).toBe('Failed to extend session');
|
|
1014
|
-
});
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
describe('getSessionStatus edge cases', () => {
|
|
1018
|
-
it('handles network error', async () => {
|
|
1019
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
1020
|
-
|
|
1021
|
-
const result = await getSessionStatus('order-uuid');
|
|
1022
|
-
expect(result.error).toContain('Network');
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
it('handles json parse error on failure response', async () => {
|
|
1026
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1027
|
-
ok: false,
|
|
1028
|
-
json: () => Promise.reject(new Error('Parse error')),
|
|
1029
|
-
});
|
|
1030
|
-
|
|
1031
|
-
const result = await getSessionStatus('order-uuid');
|
|
1032
|
-
expect(result.error).toBe('No active session found');
|
|
1033
|
-
});
|
|
1034
|
-
});
|
|
1035
|
-
|
|
1036
|
-
describe('completeReservation edge cases', () => {
|
|
1037
|
-
it('handles json parse error on failure response', async () => {
|
|
1038
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1039
|
-
ok: false,
|
|
1040
|
-
json: () => Promise.reject(new Error('Parse error')),
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
const result = await completeReservation('order-uuid');
|
|
1044
|
-
expect(result.success).toBe(false);
|
|
1045
|
-
expect(result.error).toBe('Failed to complete reservation');
|
|
1046
|
-
});
|
|
1047
|
-
|
|
1048
|
-
it('handles network error', async () => {
|
|
1049
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
1050
|
-
|
|
1051
|
-
const result = await completeReservation('order-uuid');
|
|
1052
|
-
expect(result.success).toBe(false);
|
|
1053
|
-
expect(result.error).toContain('Network');
|
|
1054
|
-
});
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
describe('cancelReservation edge cases', () => {
|
|
1058
|
-
it('handles json parse error on failure response', async () => {
|
|
1059
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1060
|
-
ok: false,
|
|
1061
|
-
json: () => Promise.reject(new Error('Parse error')),
|
|
1062
|
-
});
|
|
1063
|
-
|
|
1064
|
-
const result = await cancelReservation('order-uuid');
|
|
1065
|
-
expect(result.success).toBe(false);
|
|
1066
|
-
expect(result.error).toBe('Failed to cancel reservation');
|
|
1067
|
-
});
|
|
1068
|
-
});
|
|
1069
|
-
|
|
1070
|
-
describe('getSeriesOccurrences edge cases', () => {
|
|
1071
|
-
it('handles network error', async () => {
|
|
1072
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
1073
|
-
|
|
1074
|
-
const result = await getSeriesOccurrences(123);
|
|
1075
|
-
expect(result.error).toContain('Network');
|
|
1076
|
-
});
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
describe('getMonthEvents edge cases', () => {
|
|
1080
|
-
it('handles non-array events response', async () => {
|
|
1081
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1082
|
-
ok: true,
|
|
1083
|
-
json: () => Promise.resolve({ events: null }),
|
|
1084
|
-
});
|
|
1085
|
-
|
|
1086
|
-
const result = await getMonthEvents(456, 2025, 6);
|
|
1087
|
-
expect(result.events).toEqual([]);
|
|
1088
|
-
});
|
|
1089
|
-
});
|
|
1090
|
-
|
|
1091
|
-
describe('getOrgMonthEvents edge cases', () => {
|
|
1092
|
-
it('handles API error', async () => {
|
|
1093
|
-
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 500 });
|
|
1094
|
-
|
|
1095
|
-
const result = await getOrgMonthEvents(123, 2025, 6);
|
|
1096
|
-
expect(result.events).toEqual([]);
|
|
1097
|
-
});
|
|
1098
|
-
|
|
1099
|
-
it('handles network error', async () => {
|
|
1100
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
1101
|
-
|
|
1102
|
-
const result = await getOrgMonthEvents(123, 2025, 6);
|
|
1103
|
-
expect(result.events).toEqual([]);
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
|
-
it('handles non-array events response', async () => {
|
|
1107
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1108
|
-
ok: true,
|
|
1109
|
-
json: () => Promise.resolve({ events: null }),
|
|
1110
|
-
});
|
|
1111
|
-
|
|
1112
|
-
const result = await getOrgMonthEvents(123, 2025, 6);
|
|
1113
|
-
expect(result.events).toEqual([]);
|
|
1114
|
-
});
|
|
1115
|
-
});
|
|
1116
|
-
|
|
1117
|
-
describe('fetchEventPerformers edge cases', () => {
|
|
1118
|
-
it('handles non-array performers response', async () => {
|
|
1119
|
-
global.fetch = vi.fn().mockResolvedValue({
|
|
1120
|
-
ok: true,
|
|
1121
|
-
json: () => Promise.resolve({ performers: null, showPerformers: true }),
|
|
1122
|
-
});
|
|
1123
|
-
|
|
1124
|
-
const result = await fetchEventPerformers(123);
|
|
1125
|
-
expect(result.performers).toEqual([]);
|
|
1126
|
-
});
|
|
1127
|
-
|
|
1128
|
-
it('handles network error', async () => {
|
|
1129
|
-
global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
1130
|
-
|
|
1131
|
-
const result = await fetchEventPerformers(123);
|
|
1132
|
-
expect(result.performers).toEqual([]);
|
|
1133
|
-
expect(result.showPerformers).toBe(false);
|
|
1134
|
-
});
|
|
1135
|
-
});
|
|
1136
|
-
|
|
1137
|
-
describe('createPaymentIntent edge cases', () => {
|
|
1138
|
-
it('handles json parse error on failure response', async () => {
|
|
1139
|
-
global.fetch = vi.fn()
|
|
1140
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1141
|
-
.mockResolvedValueOnce({
|
|
1142
|
-
ok: false,
|
|
1143
|
-
json: () => Promise.reject(new Error('Parse error')),
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
|
-
const result = await createPaymentIntent('cart-123', {});
|
|
1147
|
-
expect(result).toBeNull();
|
|
1148
|
-
});
|
|
1149
|
-
});
|
|
1150
|
-
|
|
1151
|
-
describe('Stripe publishable key in payment intent response', () => {
|
|
1152
|
-
// These tests verify that createPaymentIntent returns the stripe_publishable_key
|
|
1153
|
-
// from the API response. This is critical for third-party embeds (WordPress,
|
|
1154
|
-
// Squarespace, etc.) that cannot use environment variables.
|
|
1155
|
-
|
|
1156
|
-
it('returns stripe_publishable_key from API response', async () => {
|
|
1157
|
-
const mockResponse = {
|
|
1158
|
-
client_secret: 'pi_test_secret_123',
|
|
1159
|
-
stripe_publishable_key: 'pk_test_51ABC123xyz',
|
|
1160
|
-
amount_total: 5000,
|
|
1161
|
-
tax_amount_exclusive: 450,
|
|
1162
|
-
service_fee: 250,
|
|
1163
|
-
};
|
|
1164
|
-
|
|
1165
|
-
global.fetch = vi.fn()
|
|
1166
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1167
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1168
|
-
|
|
1169
|
-
const result = await createPaymentIntent('cart-123', { 1: 2 });
|
|
1170
|
-
|
|
1171
|
-
expect(result).not.toBeNull();
|
|
1172
|
-
expect(result.stripe_publishable_key).toBe('pk_test_51ABC123xyz');
|
|
1173
|
-
expect(result.client_secret).toBe('pi_test_secret_123');
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
it('returns live stripe_publishable_key from API response', async () => {
|
|
1177
|
-
const mockResponse = {
|
|
1178
|
-
client_secret: 'pi_live_secret_456',
|
|
1179
|
-
stripe_publishable_key: 'pk_live_51ABC123xyz',
|
|
1180
|
-
amount_total: 10000,
|
|
1181
|
-
};
|
|
1182
|
-
|
|
1183
|
-
global.fetch = vi.fn()
|
|
1184
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1185
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1186
|
-
|
|
1187
|
-
const result = await createPaymentIntent('cart-123', { 1: 1 });
|
|
1188
|
-
|
|
1189
|
-
expect(result).not.toBeNull();
|
|
1190
|
-
expect(result.stripe_publishable_key).toBe('pk_live_51ABC123xyz');
|
|
1191
|
-
expect(result.stripe_publishable_key.startsWith('pk_live_')).toBe(true);
|
|
1192
|
-
});
|
|
1193
|
-
|
|
1194
|
-
it('handles missing stripe_publishable_key gracefully', async () => {
|
|
1195
|
-
// API might return response without the key (e.g., misconfigured backend)
|
|
1196
|
-
const mockResponse = {
|
|
1197
|
-
client_secret: 'pi_test_secret_123',
|
|
1198
|
-
amount_total: 5000,
|
|
1199
|
-
// stripe_publishable_key is missing
|
|
1200
|
-
};
|
|
1201
|
-
|
|
1202
|
-
global.fetch = vi.fn()
|
|
1203
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1204
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1205
|
-
|
|
1206
|
-
const result = await createPaymentIntent('cart-123', { 1: 2 });
|
|
1207
|
-
|
|
1208
|
-
// Should still return the result (client code handles missing key)
|
|
1209
|
-
expect(result).not.toBeNull();
|
|
1210
|
-
expect(result.stripe_publishable_key).toBeUndefined();
|
|
1211
|
-
expect(result.client_secret).toBe('pi_test_secret_123');
|
|
1212
|
-
});
|
|
1213
|
-
|
|
1214
|
-
it('handles empty stripe_publishable_key', async () => {
|
|
1215
|
-
const mockResponse = {
|
|
1216
|
-
client_secret: 'pi_test_secret_123',
|
|
1217
|
-
stripe_publishable_key: '',
|
|
1218
|
-
amount_total: 5000,
|
|
1219
|
-
};
|
|
1220
|
-
|
|
1221
|
-
global.fetch = vi.fn()
|
|
1222
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1223
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1224
|
-
|
|
1225
|
-
const result = await createPaymentIntent('cart-123', { 1: 2 });
|
|
1226
|
-
|
|
1227
|
-
expect(result).not.toBeNull();
|
|
1228
|
-
expect(result.stripe_publishable_key).toBe('');
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
it('includes full payment intent response with all fields', async () => {
|
|
1232
|
-
const mockResponse = {
|
|
1233
|
-
client_secret: 'pi_test_secret_abc',
|
|
1234
|
-
stripe_publishable_key: 'pk_test_xyz789',
|
|
1235
|
-
amount_total: 7500,
|
|
1236
|
-
tax_amount_exclusive: 675,
|
|
1237
|
-
tax_amount_inclusive: 0,
|
|
1238
|
-
tax_rate: 9.0,
|
|
1239
|
-
service_fee: 375,
|
|
1240
|
-
tax_breakdown: [
|
|
1241
|
-
{ jurisdiction: 'CA', rate: 0.0725, amount: 500 },
|
|
1242
|
-
{ jurisdiction: 'City', rate: 0.0175, amount: 175 },
|
|
1243
|
-
],
|
|
1244
|
-
line_items: [
|
|
1245
|
-
{ ticket_type_id: 1, quantity: 2, unit_price: 2500 },
|
|
1246
|
-
{ ticket_type_id: 2, quantity: 1, unit_price: 2500 },
|
|
1247
|
-
],
|
|
1248
|
-
};
|
|
1249
|
-
|
|
1250
|
-
global.fetch = vi.fn()
|
|
1251
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1252
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1253
|
-
|
|
1254
|
-
const result = await createPaymentIntent('cart-123', { 1: 2, 2: 1 });
|
|
1255
|
-
|
|
1256
|
-
expect(result).not.toBeNull();
|
|
1257
|
-
expect(result.stripe_publishable_key).toBe('pk_test_xyz789');
|
|
1258
|
-
expect(result.amount_total).toBe(7500);
|
|
1259
|
-
expect(result.tax_rate).toBe(9.0);
|
|
1260
|
-
expect(result.tax_breakdown).toHaveLength(2);
|
|
1261
|
-
expect(result.line_items).toHaveLength(2);
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
it('stripe_publishable_key is distinct from client_secret', async () => {
|
|
1265
|
-
const mockResponse = {
|
|
1266
|
-
client_secret: 'pi_test_secret_abc123',
|
|
1267
|
-
stripe_publishable_key: 'pk_test_xyz789',
|
|
1268
|
-
};
|
|
1269
|
-
|
|
1270
|
-
global.fetch = vi.fn()
|
|
1271
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ ip: '1.2.3.4' }) })
|
|
1272
|
-
.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve(mockResponse) });
|
|
1273
|
-
|
|
1274
|
-
const result = await createPaymentIntent('cart-123', {});
|
|
1275
|
-
|
|
1276
|
-
// Publishable key should NOT be the same as client_secret
|
|
1277
|
-
expect(result.stripe_publishable_key).not.toBe(result.client_secret);
|
|
1278
|
-
// Publishable key should start with 'pk_'
|
|
1279
|
-
expect(result.stripe_publishable_key.startsWith('pk_')).toBe(true);
|
|
1280
|
-
// Client secret should start with 'pi_' (payment intent)
|
|
1281
|
-
expect(result.client_secret.startsWith('pi_')).toBe(true);
|
|
1282
|
-
});
|
|
1283
|
-
});
|
|
1284
|
-
});
|