@session-services/web-elements 0.1.24 → 0.1.26

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.
@@ -138,6 +138,7 @@ function widget() {
138
138
  const item = new SessionsTicketing();
139
139
  item.eventId = "evt_1";
140
140
  item.event = event();
141
+ item.enableAnalytics = true;
141
142
  item.host = {
142
143
  getBoundingClientRect: () => ({ top: 0 }),
143
144
  };
@@ -197,6 +198,44 @@ describe("session-services-ticketing component", () => {
197
198
  expect(item.couponError).toBe("Failed to apply coupon. Please check the code.");
198
199
  expect(item.client.order.quote).toHaveBeenCalledWith(expect.objectContaining({ couponCode: "BAD" }));
199
200
  });
201
+ it("does not commit preview coupons when event reload fails", async () => {
202
+ const item = widget();
203
+ item.appliedCouponCode = "SAVE10";
204
+ item.couponInput = "bad";
205
+ item.loadEventData = vi.fn().mockRejectedValue(new Error("Down"));
206
+ await item.applyCoupon();
207
+ expect(item.appliedCouponCode).toBe("SAVE10");
208
+ expect(item.couponInput).toBe("BAD");
209
+ expect(item.couponError).toBe("Failed to apply coupon. Please check the code.");
210
+ });
211
+ it("does not apply stale cart coupons as preview coupons", async () => {
212
+ const item = widget();
213
+ const firstLoad = deferred();
214
+ const secondLoad = deferred();
215
+ item.couponInput = "old";
216
+ item.selectedItems = new Map([[selectionKey("adm_general", null), 1]]);
217
+ item.loadEventData = vi
218
+ .fn()
219
+ .mockReturnValueOnce(firstLoad.promise)
220
+ .mockReturnValueOnce(secondLoad.promise)
221
+ .mockResolvedValue(undefined);
222
+ item.client.order.quote = vi.fn().mockResolvedValue({ quote: quote() });
223
+ const stale = item.applyCoupon();
224
+ await Promise.resolve();
225
+ expect(item.appliedCouponCode).toBe("OLD");
226
+ item.couponInput = "new";
227
+ const latest = item.applyCoupon();
228
+ await Promise.resolve();
229
+ expect(item.appliedCouponCode).toBe("NEW");
230
+ item.selectedItems = new Map();
231
+ firstLoad.resolve();
232
+ await stale;
233
+ expect(item.appliedCouponCode).toBe("NEW");
234
+ expect(item.client.order.quote).not.toHaveBeenCalledWith(expect.objectContaining({ couponCode: "OLD" }));
235
+ secondLoad.resolve();
236
+ await latest;
237
+ expect(item.appliedCouponCode).toBe("NEW");
238
+ });
200
239
  it("keeps preview quote loading separate from cart quote loading", async () => {
201
240
  const item = widget();
202
241
  const next = deferred();
@@ -269,6 +308,18 @@ describe("session-services-ticketing component", () => {
269
308
  });
270
309
  expect(item.getCheckoutButtonText()).toBe("Continue");
271
310
  });
311
+ it("renders stable checkout e2e hooks", () => {
312
+ const item = widget();
313
+ const view = item.renderReadyState();
314
+ const cards = rendered(view, (node) => node.props["data-testid"] === "event-admission-card");
315
+ const inputs = rendered(view, (node) => node.props["data-testid"] === "event-admission-quantity-input");
316
+ const increments = rendered(view, (node) => node.props["data-testid"] === "event-admission-increment");
317
+ const checkout = rendered(view, (node) => node.props["data-testid"] === "event-checkout-cta-desktop");
318
+ expect(cards[0].props["data-admission-id"]).toBe("adm_general");
319
+ expect(inputs).toHaveLength(1);
320
+ expect(increments).toHaveLength(1);
321
+ expect(checkout).toHaveLength(1);
322
+ });
272
323
  it("renders external ticketing in the sticky checkout bar", () => {
273
324
  const item = widget();
274
325
  item.event = event({ url: "https://tickets.example" });
@@ -296,6 +347,35 @@ describe("session-services-ticketing component", () => {
296
347
  expect(links.some((link) => hasClass(link, "inline-external-ticket-button"))).toBe(true);
297
348
  expect(links.some((link) => hasClass(link, "sticky-checkout-btn"))).toBe(true);
298
349
  });
350
+ it("tracks external ticket clicks by surface", () => {
351
+ const item = widget();
352
+ const track = vi.fn();
353
+ item.analytics = { track };
354
+ item.event = event({ url: "https://tickets.example/event" });
355
+ item.stickyCheckout = true;
356
+ const links = rendered(item.renderReadyState(), (node) => node.tag === "a");
357
+ const inline = links.find((link) => hasClass(link, "inline-external-ticket-button"));
358
+ const sticky = links.find((link) => hasClass(link, "sticky-checkout-btn"));
359
+ expect(typeof inline?.props.onClick).toBe("function");
360
+ expect(typeof sticky?.props.onClick).toBe("function");
361
+ (inline?.props.onClick)();
362
+ (sticky?.props.onClick)();
363
+ expect(track).toHaveBeenCalledWith("Checkout Started", expect.objectContaining({
364
+ event_id: "evt_1",
365
+ team_id: "team_1",
366
+ promoter_id: "pro_1",
367
+ ticketing_mode: "external",
368
+ external_ticket_url: "https://tickets.example/event",
369
+ external_ticket_domain: "tickets.example",
370
+ cta_surface: "inline",
371
+ }));
372
+ expect(track).toHaveBeenCalledWith("Checkout Started", expect.objectContaining({
373
+ ticketing_mode: "external",
374
+ external_ticket_url: "https://tickets.example/event",
375
+ external_ticket_domain: "tickets.example",
376
+ cta_surface: "sticky",
377
+ }));
378
+ });
299
379
  it("scrolls to the ticket selector with a 100px top offset", () => {
300
380
  const item = widget();
301
381
  const scrollTo = vi.fn();