@session-services/web-elements 0.1.21 → 0.1.23
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/cjs/session-services-ticketing.cjs.entry.js +104 -76
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +136 -68
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +46 -23
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js +33 -26
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.test.js +106 -14
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js +46 -2
- package/dist/components/session-services-ticketing.js +104 -76
- package/dist/esm/session-services-ticketing.entry.js +104 -76
- package/dist/session-services-elements/p-ddf8ca4b.entry.js +1 -0
- package/dist/session-services-elements/session-services-elements.esm.js +1 -1
- package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +1 -0
- package/dist/types/components/session-services-ticketing/session-services-ticketing.logic.d.ts +4 -1
- package/package.json +4 -4
- package/dist/session-services-elements/p-b8703bf3.entry.js +0 -1
|
@@ -158,6 +158,92 @@ describe("session-services-ticketing logic", () => {
|
|
|
158
158
|
expect(state.disabled).toBe(true);
|
|
159
159
|
expect(state.label?.text).toContain("On sale");
|
|
160
160
|
});
|
|
161
|
+
it("treats sold-out sale endings as sold out instead of a price label", () => {
|
|
162
|
+
const data = event({
|
|
163
|
+
admissions: [
|
|
164
|
+
admission({
|
|
165
|
+
saleEndsAt: pastEnd,
|
|
166
|
+
saleEndsBehavior: "SOLD_OUT",
|
|
167
|
+
}),
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
const state = getAdmissionDisplayState({
|
|
171
|
+
admission: data.admissions[0],
|
|
172
|
+
event: data,
|
|
173
|
+
});
|
|
174
|
+
expect(state.disabled).toBe(true);
|
|
175
|
+
expect(state.label).toBeNull();
|
|
176
|
+
expect(state.soldOut).toBe(true);
|
|
177
|
+
expect(state.statusLabel).toBe("Sold Out");
|
|
178
|
+
expect(state.statusTone).toBe("destructive");
|
|
179
|
+
});
|
|
180
|
+
it("moves post-sale labels into admission status", () => {
|
|
181
|
+
const data = event({
|
|
182
|
+
admissions: [
|
|
183
|
+
admission({
|
|
184
|
+
saleEndsAt: pastEnd,
|
|
185
|
+
saleEndsBehavior: "SALE_ENDED",
|
|
186
|
+
}),
|
|
187
|
+
admission({
|
|
188
|
+
id: "adm_door",
|
|
189
|
+
saleEndsAt: pastEnd,
|
|
190
|
+
saleEndsBehavior: "DOOR",
|
|
191
|
+
}),
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
const ended = getAdmissionDisplayState({
|
|
195
|
+
admission: data.admissions[0],
|
|
196
|
+
event: data,
|
|
197
|
+
});
|
|
198
|
+
const door = getAdmissionDisplayState({
|
|
199
|
+
admission: data.admissions[1],
|
|
200
|
+
event: data,
|
|
201
|
+
});
|
|
202
|
+
expect(ended.label).toBeNull();
|
|
203
|
+
expect(ended.statusLabel).toBe("Sale Ended");
|
|
204
|
+
expect(ended.statusTone).toBe("destructive");
|
|
205
|
+
expect(door.label).toBeNull();
|
|
206
|
+
expect(door.statusLabel).toBe("Available At Door");
|
|
207
|
+
expect(door.statusTone).toBe("success");
|
|
208
|
+
});
|
|
209
|
+
it("shows sold-out door admissions as available at door while online sales continue", () => {
|
|
210
|
+
const data = event({
|
|
211
|
+
admissions: [
|
|
212
|
+
admission({
|
|
213
|
+
id: "adm_first_release",
|
|
214
|
+
available: 0,
|
|
215
|
+
sold: 54,
|
|
216
|
+
capacity: 54,
|
|
217
|
+
order: 20,
|
|
218
|
+
saleEndsAt: futureEnd,
|
|
219
|
+
saleEndsBehavior: "DOOR",
|
|
220
|
+
}),
|
|
221
|
+
admission({
|
|
222
|
+
id: "adm_final_release",
|
|
223
|
+
available: 997,
|
|
224
|
+
sold: 3,
|
|
225
|
+
capacity: 1000,
|
|
226
|
+
order: 40,
|
|
227
|
+
saleEndsAt: futureEnd,
|
|
228
|
+
saleEndsBehavior: "DOOR",
|
|
229
|
+
}),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
const first = getAdmissionDisplayState({
|
|
233
|
+
admission: data.admissions[0],
|
|
234
|
+
event: data,
|
|
235
|
+
});
|
|
236
|
+
const final = getAdmissionDisplayState({
|
|
237
|
+
admission: data.admissions[1],
|
|
238
|
+
event: data,
|
|
239
|
+
});
|
|
240
|
+
expect(first.disabled).toBe(true);
|
|
241
|
+
expect(first.soldOut).toBe(true);
|
|
242
|
+
expect(first.statusLabel).toBe("Available At Door");
|
|
243
|
+
expect(first.statusTone).toBe("success");
|
|
244
|
+
expect(final.disabled).toBe(false);
|
|
245
|
+
expect(final.statusLabel).toBeNull();
|
|
246
|
+
});
|
|
161
247
|
it("orders top-level sections and unsectioned admissions together", () => {
|
|
162
248
|
const main = section({ id: "sec_main", order: 2 });
|
|
163
249
|
const early = admission({ id: "adm_early", order: 1 });
|
|
@@ -185,12 +271,13 @@ describe("session-services-ticketing logic", () => {
|
|
|
185
271
|
quoteItem: quoteItem(),
|
|
186
272
|
});
|
|
187
273
|
expect(price).toEqual({
|
|
274
|
+
base: 1000,
|
|
188
275
|
discounted: true,
|
|
189
|
-
|
|
190
|
-
|
|
276
|
+
fees: 200,
|
|
277
|
+
gross: 2000,
|
|
191
278
|
});
|
|
192
279
|
});
|
|
193
|
-
it("uses quote item
|
|
280
|
+
it("uses quote item base pricing without discount styling", () => {
|
|
194
281
|
const price = getAdmissionPriceDisplay({
|
|
195
282
|
admission: admission(),
|
|
196
283
|
mode: "default",
|
|
@@ -208,19 +295,20 @@ describe("session-services-ticketing logic", () => {
|
|
|
208
295
|
}),
|
|
209
296
|
});
|
|
210
297
|
expect(price).toEqual({
|
|
298
|
+
base: 2000,
|
|
211
299
|
discounted: false,
|
|
212
|
-
|
|
300
|
+
fees: 200,
|
|
213
301
|
});
|
|
214
302
|
});
|
|
215
|
-
it("
|
|
303
|
+
it("separates admission base and booking fees for every mode", () => {
|
|
216
304
|
expect(getAdmissionPriceDisplay({
|
|
217
305
|
admission: admission(),
|
|
218
306
|
mode: "default",
|
|
219
|
-
})
|
|
307
|
+
})).toMatchObject({ base: 2000, fees: 200 });
|
|
220
308
|
expect(getAdmissionPriceDisplay({
|
|
221
309
|
admission: admission(),
|
|
222
310
|
mode: "door",
|
|
223
|
-
})
|
|
311
|
+
})).toMatchObject({ base: 2000, fees: 200 });
|
|
224
312
|
});
|
|
225
313
|
it("uses discounted initial preview pricing when available", () => {
|
|
226
314
|
const price = getAdmissionPriceDisplay({
|
|
@@ -233,20 +321,23 @@ describe("session-services-ticketing logic", () => {
|
|
|
233
321
|
mode: "default",
|
|
234
322
|
});
|
|
235
323
|
expect(price).toEqual({
|
|
324
|
+
base: 1000,
|
|
236
325
|
discounted: true,
|
|
237
|
-
|
|
238
|
-
|
|
326
|
+
fees: 200,
|
|
327
|
+
gross: 2000,
|
|
239
328
|
});
|
|
240
329
|
});
|
|
241
330
|
it("calculates admission savings from discounted display prices", () => {
|
|
242
331
|
expect(admissionPriceSavings({
|
|
332
|
+
base: 1000,
|
|
243
333
|
discounted: true,
|
|
244
|
-
|
|
245
|
-
|
|
334
|
+
fees: 200,
|
|
335
|
+
gross: 2000,
|
|
246
336
|
})).toBe(1000);
|
|
247
337
|
expect(admissionPriceSavings({
|
|
338
|
+
base: 2200,
|
|
248
339
|
discounted: false,
|
|
249
|
-
|
|
340
|
+
fees: 200,
|
|
250
341
|
})).toBe(0);
|
|
251
342
|
});
|
|
252
343
|
it("divides preview quote pricing by preview quantity", () => {
|
|
@@ -268,9 +359,10 @@ describe("session-services-ticketing logic", () => {
|
|
|
268
359
|
}),
|
|
269
360
|
});
|
|
270
361
|
expect(price).toEqual({
|
|
362
|
+
base: 1000,
|
|
271
363
|
discounted: true,
|
|
272
|
-
|
|
273
|
-
|
|
364
|
+
fees: 200,
|
|
365
|
+
gross: 2000,
|
|
274
366
|
});
|
|
275
367
|
});
|
|
276
368
|
});
|
package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { SessionsTicketing } from "./session-services-ticketing";
|
|
3
|
-
import { selectionKey, } from "./session-services-ticketing.logic";
|
|
3
|
+
import { getTicketingState, selectionKey, } from "./session-services-ticketing.logic";
|
|
4
4
|
vi.mock("@stencil/core", () => ({
|
|
5
5
|
Component: () => () => undefined,
|
|
6
6
|
Element: () => () => undefined,
|
|
@@ -9,7 +9,7 @@ vi.mock("@stencil/core", () => ({
|
|
|
9
9
|
Prop: () => () => undefined,
|
|
10
10
|
State: () => () => undefined,
|
|
11
11
|
Watch: () => () => undefined,
|
|
12
|
-
h: () =>
|
|
12
|
+
h: (tag, props, ...children) => ({ tag, props: props ?? {}, children }),
|
|
13
13
|
}));
|
|
14
14
|
const futureStart = "2030-01-01T20:00:00";
|
|
15
15
|
const futureEnd = "2030-01-01T23:00:00";
|
|
@@ -115,6 +115,25 @@ function deferred() {
|
|
|
115
115
|
});
|
|
116
116
|
return { promise, resolve };
|
|
117
117
|
}
|
|
118
|
+
function isRenderNode(value) {
|
|
119
|
+
if (!value || typeof value !== "object")
|
|
120
|
+
return false;
|
|
121
|
+
return "tag" in value && "props" in value && "children" in value;
|
|
122
|
+
}
|
|
123
|
+
function rendered(value, predicate) {
|
|
124
|
+
if (Array.isArray(value)) {
|
|
125
|
+
return value.flatMap((item) => rendered(item, predicate));
|
|
126
|
+
}
|
|
127
|
+
if (!isRenderNode(value))
|
|
128
|
+
return [];
|
|
129
|
+
const matches = predicate(value) ? [value] : [];
|
|
130
|
+
return matches.concat(value.children.flatMap((item) => rendered(item, predicate)));
|
|
131
|
+
}
|
|
132
|
+
function hasClass(node, name) {
|
|
133
|
+
return String(node.props.class ?? "")
|
|
134
|
+
.split(/\s+/)
|
|
135
|
+
.includes(name);
|
|
136
|
+
}
|
|
118
137
|
function widget() {
|
|
119
138
|
const item = new SessionsTicketing();
|
|
120
139
|
item.eventId = "evt_1";
|
|
@@ -131,6 +150,7 @@ function widget() {
|
|
|
131
150
|
item.quoteLoading = false;
|
|
132
151
|
item.previewLoading = false;
|
|
133
152
|
item.passcodeDenied = false;
|
|
153
|
+
item.stickyCheckout = false;
|
|
134
154
|
item.error = null;
|
|
135
155
|
item.activePasscode = "";
|
|
136
156
|
item.cartUpdate = { emit: vi.fn() };
|
|
@@ -246,6 +266,30 @@ describe("session-services-ticketing component", () => {
|
|
|
246
266
|
});
|
|
247
267
|
expect(item.getCheckoutButtonText()).toBe("Continue");
|
|
248
268
|
});
|
|
269
|
+
it("renders external ticketing in the sticky checkout bar", () => {
|
|
270
|
+
const item = widget();
|
|
271
|
+
item.event = event({ url: "https://tickets.example" });
|
|
272
|
+
item.stickyCheckout = true;
|
|
273
|
+
const state = getTicketingState(item.event);
|
|
274
|
+
const links = rendered(item.renderStickyExternalTicketing(state), (node) => node.tag === "a");
|
|
275
|
+
expect(links).toHaveLength(1);
|
|
276
|
+
expect(links[0].props.href).toBe("https://tickets.example");
|
|
277
|
+
expect(hasClass(links[0], "external-ticket-button")).toBe(true);
|
|
278
|
+
expect(hasClass(links[0], "sticky-checkout-btn")).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
it("keeps external ticketing under the sticky checkout host state", () => {
|
|
281
|
+
const item = widget();
|
|
282
|
+
item.event = event({ url: "https://tickets.example" });
|
|
283
|
+
item.stickyCheckout = true;
|
|
284
|
+
const view = item.renderReadyState();
|
|
285
|
+
const hosts = rendered(view, (node) => node.tag === "mock-host");
|
|
286
|
+
const links = rendered(view, (node) => node.tag === "a");
|
|
287
|
+
expect(getTicketingState(item.event).showExternalTicketing).toBe(true);
|
|
288
|
+
expect(hosts[0].props["data-sticky-checkout"]).toBe("true");
|
|
289
|
+
expect(links).toHaveLength(2);
|
|
290
|
+
expect(links.some((link) => hasClass(link, "inline-external-ticket-button"))).toBe(true);
|
|
291
|
+
expect(links.some((link) => hasClass(link, "sticky-checkout-btn"))).toBe(true);
|
|
292
|
+
});
|
|
249
293
|
it("classifies only passcode access failures as passcode errors", async () => {
|
|
250
294
|
const item = widget();
|
|
251
295
|
item.client.event.get = vi
|