@curless/hotel-storefront 0.27.5 → 0.27.6
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/README.md +18 -8
- package/dist/fence.d.ts +12 -1
- package/dist/fence.d.ts.map +1 -1
- package/dist/fence.js +0 -0
- package/dist/fence.js.map +1 -1
- package/dist/legal.js +1 -1
- package/dist/legal.js.map +1 -1
- package/dist/money.d.ts +2 -0
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +43 -6
- package/dist/money.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -1
- package/dist/server.js.map +1 -1
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +3 -2
- package/dist/sessions.js.map +1 -1
- package/dist/surface.d.ts +2 -0
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +10 -1
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +4 -4
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +21 -8
- package/dist/tools.js.map +1 -1
- package/dist/trip-ids.js +1 -1
- package/dist/trip-ids.js.map +1 -1
- package/package.json +2 -2
- package/dist/__tests__/__fixtures__/huazhu-fake.d.ts +0 -2
- package/dist/__tests__/__fixtures__/huazhu-fake.d.ts.map +0 -1
- package/dist/__tests__/__fixtures__/huazhu-fake.js +0 -3
- package/dist/__tests__/__fixtures__/huazhu-fake.js.map +0 -1
- package/dist/__tests__/__helpers__/sessions.d.ts +0 -10
- package/dist/__tests__/__helpers__/sessions.d.ts.map +0 -1
- package/dist/__tests__/__helpers__/sessions.js +0 -40
- package/dist/__tests__/__helpers__/sessions.js.map +0 -1
- package/dist/__tests__/audit-p0.test.d.ts +0 -2
- package/dist/__tests__/audit-p0.test.d.ts.map +0 -1
- package/dist/__tests__/audit-p0.test.js +0 -274
- package/dist/__tests__/audit-p0.test.js.map +0 -1
- package/dist/__tests__/audit-p2.test.d.ts +0 -2
- package/dist/__tests__/audit-p2.test.d.ts.map +0 -1
- package/dist/__tests__/audit-p2.test.js +0 -98
- package/dist/__tests__/audit-p2.test.js.map +0 -1
- package/dist/__tests__/card-contract.test.d.ts +0 -2
- package/dist/__tests__/card-contract.test.d.ts.map +0 -1
- package/dist/__tests__/card-contract.test.js +0 -282
- package/dist/__tests__/card-contract.test.js.map +0 -1
- package/dist/__tests__/data.test.d.ts +0 -2
- package/dist/__tests__/data.test.d.ts.map +0 -1
- package/dist/__tests__/data.test.js +0 -210
- package/dist/__tests__/data.test.js.map +0 -1
- package/dist/__tests__/dates.test.d.ts +0 -2
- package/dist/__tests__/dates.test.d.ts.map +0 -1
- package/dist/__tests__/dates.test.js +0 -107
- package/dist/__tests__/dates.test.js.map +0 -1
- package/dist/__tests__/demo-hotels.test.d.ts +0 -2
- package/dist/__tests__/demo-hotels.test.d.ts.map +0 -1
- package/dist/__tests__/demo-hotels.test.js +0 -765
- package/dist/__tests__/demo-hotels.test.js.map +0 -1
- package/dist/__tests__/huazhu-audit.test.d.ts +0 -2
- package/dist/__tests__/huazhu-audit.test.d.ts.map +0 -1
- package/dist/__tests__/huazhu-audit.test.js +0 -234
- package/dist/__tests__/huazhu-audit.test.js.map +0 -1
- package/dist/__tests__/huazhu-backend.test.d.ts +0 -2
- package/dist/__tests__/huazhu-backend.test.d.ts.map +0 -1
- package/dist/__tests__/huazhu-backend.test.js +0 -1326
- package/dist/__tests__/huazhu-backend.test.js.map +0 -1
- package/dist/__tests__/huazhu-cancel-policy.test.d.ts +0 -2
- package/dist/__tests__/huazhu-cancel-policy.test.d.ts.map +0 -1
- package/dist/__tests__/huazhu-cancel-policy.test.js +0 -82
- package/dist/__tests__/huazhu-cancel-policy.test.js.map +0 -1
- package/dist/__tests__/huazhu-sandbox.test.d.ts +0 -2
- package/dist/__tests__/huazhu-sandbox.test.d.ts.map +0 -1
- package/dist/__tests__/huazhu-sandbox.test.js +0 -195
- package/dist/__tests__/huazhu-sandbox.test.js.map +0 -1
- package/dist/__tests__/huazhu-sellable.test.d.ts +0 -2
- package/dist/__tests__/huazhu-sellable.test.d.ts.map +0 -1
- package/dist/__tests__/huazhu-sellable.test.js +0 -192
- package/dist/__tests__/huazhu-sellable.test.js.map +0 -1
- package/dist/__tests__/present.test.d.ts +0 -2
- package/dist/__tests__/present.test.d.ts.map +0 -1
- package/dist/__tests__/present.test.js +0 -367
- package/dist/__tests__/present.test.js.map +0 -1
- package/dist/__tests__/prose.test.d.ts +0 -2
- package/dist/__tests__/prose.test.d.ts.map +0 -1
- package/dist/__tests__/prose.test.js +0 -171
- package/dist/__tests__/prose.test.js.map +0 -1
- package/dist/__tests__/server.test.d.ts +0 -2
- package/dist/__tests__/server.test.d.ts.map +0 -1
- package/dist/__tests__/server.test.js +0 -557
- package/dist/__tests__/server.test.js.map +0 -1
- package/dist/__tests__/state.test.d.ts +0 -2
- package/dist/__tests__/state.test.d.ts.map +0 -1
- package/dist/__tests__/state.test.js +0 -204
- package/dist/__tests__/state.test.js.map +0 -1
- package/dist/__tests__/tool-descriptions.test.d.ts +0 -2
- package/dist/__tests__/tool-descriptions.test.d.ts.map +0 -1
- package/dist/__tests__/tool-descriptions.test.js +0 -220
- package/dist/__tests__/tool-descriptions.test.js.map +0 -1
- package/dist/__tests__/tools.test.d.ts +0 -2
- package/dist/__tests__/tools.test.d.ts.map +0 -1
- package/dist/__tests__/tools.test.js +0 -1598
- package/dist/__tests__/tools.test.js.map +0 -1
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFileSync, readdirSync } from 'node:fs';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { REQUEST_KINDS } from '../backend.js';
|
|
6
|
-
import { HOTEL_CARD_HTML, HOTEL_CARD_SOURCE_SHA } from '../card.generated.js';
|
|
7
|
-
import { toolNames } from '../config.js';
|
|
8
|
-
import { buildOutputSchemas } from '../tools.js';
|
|
9
|
-
// The card is served from `card.generated.ts`, not from `widget/`. Two things
|
|
10
|
-
// can quietly come apart, and both look fine until a guest is looking at the
|
|
11
|
-
// card:
|
|
12
|
-
//
|
|
13
|
-
// 1. the widget is edited and not rebuilt - the committed HTML is then last
|
|
14
|
-
// week's card, and nothing else in the repo notices;
|
|
15
|
-
// 2. a tool learns a new `view` and the card has no renderer for it - the
|
|
16
|
-
// guest gets "this card could not be drawn" for an answer the server was
|
|
17
|
-
// happy with.
|
|
18
|
-
//
|
|
19
|
-
// One test each.
|
|
20
|
-
const widget = fileURLToPath(new URL('../../widget/', import.meta.url));
|
|
21
|
-
describe('the built card', () => {
|
|
22
|
-
it('is the card the widget sources currently produce', () => {
|
|
23
|
-
const dir = `${widget}src`;
|
|
24
|
-
const h = createHash('sha256');
|
|
25
|
-
for (const name of readdirSync(dir).sort()) {
|
|
26
|
-
h.update(name);
|
|
27
|
-
h.update(readFileSync(`${dir}/${name}`));
|
|
28
|
-
}
|
|
29
|
-
h.update('index.html');
|
|
30
|
-
h.update(readFileSync(`${widget}index.html`));
|
|
31
|
-
expect(h.digest('hex'), 'widget/ has changed since the card was built - run `npm run build` in widget/').toBe(HOTEL_CARD_SOURCE_SHA);
|
|
32
|
-
});
|
|
33
|
-
it('is one self-contained page, with nothing fetched from anywhere', () => {
|
|
34
|
-
expect(HOTEL_CARD_HTML.startsWith('<!doctype html>')).toBe(true);
|
|
35
|
-
expect(HOTEL_CARD_HTML).not.toMatch(/<script[^>]+src=["']http/i);
|
|
36
|
-
expect(HOTEL_CARD_HTML).not.toMatch(/<link[^>]+href=["']http/i);
|
|
37
|
-
});
|
|
38
|
-
it('draws every view the tools can answer with', () => {
|
|
39
|
-
const names = toolNames({});
|
|
40
|
-
const schemas = buildOutputSchemas(names);
|
|
41
|
-
const views = new Set();
|
|
42
|
-
for (const [tool, schema] of Object.entries(schemas)) {
|
|
43
|
-
// The look-up draws nothing by design - its answer is text for the model.
|
|
44
|
-
if (tool === names.lookUp)
|
|
45
|
-
continue;
|
|
46
|
-
const v = schema
|
|
47
|
-
.properties?.view;
|
|
48
|
-
for (const name of v?.enum ?? (v?.const ? [v.const] : []))
|
|
49
|
-
views.add(name);
|
|
50
|
-
}
|
|
51
|
-
expect(views.size).toBeGreaterThan(10);
|
|
52
|
-
for (const view of views) {
|
|
53
|
-
// `error` is the card's fallback branch rather than a named renderer.
|
|
54
|
-
if (view === 'error')
|
|
55
|
-
continue;
|
|
56
|
-
// A view whose name is a valid identifier comes out of the minifier as a
|
|
57
|
-
// BARE key - `extras:someFn` - so looking only for the quoted string
|
|
58
|
-
// reports a renderer missing that is right there.
|
|
59
|
-
const drawn = HOTEL_CARD_HTML.includes(`"${view}"`) ||
|
|
60
|
-
new RegExp(`\\b${view}\\s*:`).test(HOTEL_CARD_HTML);
|
|
61
|
-
expect(drawn, `the card has no renderer for ${view}`).toBe(true);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
// ⚠️ TWICE a component's own `display` beat the UA's `[hidden]` at equal
|
|
65
|
-
// specificity, because ours is the later sheet: the guest form was on screen
|
|
66
|
-
// from the start, and the trip button stayed up on an empty trip - which is
|
|
67
|
-
// how a guest reached a page with no way back. One blunt rule fixes the
|
|
68
|
-
// class; this keeps it there.
|
|
69
|
-
it('lets nothing outrank the hidden attribute', () => {
|
|
70
|
-
expect(HOTEL_CARD_HTML.replace(/\s+/g, '')).toContain('[hidden]{display:none!important');
|
|
71
|
-
});
|
|
72
|
-
// A native dropdown paints itself in the OS's own chrome - a blue macOS
|
|
73
|
-
// highlight in the middle of a card that follows the host's theme.
|
|
74
|
-
it('has no native dropdown in it', () => {
|
|
75
|
-
expect(HOTEL_CARD_HTML).not.toMatch(/<select\b/i);
|
|
76
|
-
});
|
|
77
|
-
// ⚠️ A CARD THAT NAVIGATES HAS TO COME BACK. Tapping a column of a comparison
|
|
78
|
-
// replaced the comparison with that hotel and the comparison was gone - the
|
|
79
|
-
// brand in the header goes HOME, which is a different answer, not the one the
|
|
80
|
-
// guest was reading. robin tapped the second of three and could not get back
|
|
81
|
-
// to the three (2026-09-21).
|
|
82
|
-
it('has a way back to the answer a tap navigated away from', () => {
|
|
83
|
-
expect(HOTEL_CARD_HTML).toContain('id="backbtn"');
|
|
84
|
-
expect(HOTEL_CARD_HTML).toContain('data-act="back"');
|
|
85
|
-
});
|
|
86
|
-
// ⚠️ THE STRIP'S CHIPS ARE THE DATE BAR'S OWN ACTION. Each carries the hotel
|
|
87
|
-
// and its own nights, so tapping one goes through the path that already
|
|
88
|
-
// exists - no second way to change the dates, which is how the two would
|
|
89
|
-
// come apart.
|
|
90
|
-
it('draws the nearby dates as chips that carry their own nights', () => {
|
|
91
|
-
expect(HOTEL_CARD_HTML).toContain('flex-dates');
|
|
92
|
-
expect(HOTEL_CARD_HTML).toMatch(/data-ci=/);
|
|
93
|
-
});
|
|
94
|
-
// ⚠️ A CHIP THE SERVER DROPS IS A PROMISE THE CARD MADE AND NOBODY KEPT. The
|
|
95
|
-
// guest ticks "quiet room", the booking goes through, the confirmation lists
|
|
96
|
-
// everything except that - and nothing anywhere says why. One list, both
|
|
97
|
-
// sides.
|
|
98
|
-
it('offers exactly the preferences the server understands', () => {
|
|
99
|
-
const src = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
100
|
-
const list = src.slice(src.indexOf('const REQUESTS'), src.indexOf('const OPPOSITES'));
|
|
101
|
-
const ids = [...list.matchAll(/id: '([a-z-]+)'/g)].map((m) => m[1]);
|
|
102
|
-
expect(ids).toEqual(REQUEST_KINDS);
|
|
103
|
-
});
|
|
104
|
-
// ⚠️ THE CARD DOES NO ARITHMETIC ON MONEY. Every price it holds is a string
|
|
105
|
-
// the server formatted; "HK$8,588" and "HK$10,305.60" cannot be compared
|
|
106
|
-
// without parsing money out of text, and a card that parsed it would be one
|
|
107
|
-
// locale away from marking the wrong date as the cheapest. The server sends
|
|
108
|
-
// the mark.
|
|
109
|
-
it('takes the cheapest date as a flag rather than working it out', () => {
|
|
110
|
-
const src = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
111
|
-
const strip = src.slice(src.indexOf('const dateStrip'), src.indexOf('const hotelView'));
|
|
112
|
-
expect(strip).toContain('d.best');
|
|
113
|
-
expect(strip, 'the strip is parsing money out of a formatted price').not.toMatch(/parseFloat|parseInt|Number\(|replace\(/);
|
|
114
|
-
});
|
|
115
|
-
it('says the two things this connector must never stop saying', () => {
|
|
116
|
-
expect(HOTEL_CARD_HTML).toContain('Nothing has been charged');
|
|
117
|
-
expect(HOTEL_CARD_HTML).toContain('No card is taken');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
// The history, read from the source: these are rules about what the card does,
|
|
121
|
-
// and a minified build keeps the strings but not the shape. Each one is a bug
|
|
122
|
-
// the 2026-09-28 audit walked into on the live connector.
|
|
123
|
-
describe("the card's history", () => {
|
|
124
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
125
|
-
const CSS = readFileSync(`${widget}src/card.css`, 'utf8');
|
|
126
|
-
// ⚠️ A CHIP THAT IS SEVERAL CALLS makes them in turn, and Back returns to
|
|
127
|
-
// where it was pressed once, not once per stay.
|
|
128
|
-
it('runs a chip’s calls one after another, pushing Back only for the first', () => {
|
|
129
|
-
expect(SRC).toContain('await call(String(a.tool), a.args ?? {}, { push: i === 0 });');
|
|
130
|
-
expect(SRC).toContain('x?.ask || x?.asks?.length');
|
|
131
|
-
// Check-out day is a line, not another stay.
|
|
132
|
-
expect(SRC).toContain('Check out · ${esc(it.name)}');
|
|
133
|
-
});
|
|
134
|
-
// ⚠️ SHORT BY DEFAULT (robin, 2026-09-29: 「这个首页选择的还是太长了」): the top
|
|
135
|
-
// cities and one row of regions, each region opening its own countries.
|
|
136
|
-
it('opens the places page on the top cities and a row of regions, folded', () => {
|
|
137
|
-
expect(SRC).toContain('data-region-body="${i}" hidden');
|
|
138
|
-
expect(SRC).toContain('Top cities');
|
|
139
|
-
expect(SRC).toContain('const COUNTRIES_SHOWN = 6;');
|
|
140
|
-
});
|
|
141
|
-
it('pushes a step only when the guest moves, never for a refresh of the same view', () => {
|
|
142
|
-
expect(SRC).toContain('placeOf(next) === placeOf(lastPayload)');
|
|
143
|
-
});
|
|
144
|
-
// ⚠️ THE CHAT SCROLLS, NOT THE CARD: a page change pressed from low on the
|
|
145
|
-
// card opened the new page with its top above the screen (robin,
|
|
146
|
-
// 2026-09-29). Only when the header is off screen - `nearest` - so a guest
|
|
147
|
-
// who never scrolled sees nothing move.
|
|
148
|
-
it('brings the top of a new page into view when the guest moves', () => {
|
|
149
|
-
expect(SRC).toContain("head.scrollIntoView({ block: 'nearest'");
|
|
150
|
-
expect(SRC).toContain('if (moved) showTop();');
|
|
151
|
-
});
|
|
152
|
-
// ⚠️ A LIST IS PAGED IN WHOLE ROWS, IN PLACE (robin, 2026-09-29): three
|
|
153
|
-
// rows of however many fit across - six in a chat - Previous and Next, and
|
|
154
|
-
// the lot only in full screen - never "Show more" growing the card inline
|
|
155
|
-
// without end.
|
|
156
|
-
it('pages a list three whole rows at a time, and shows the lot only full screen', () => {
|
|
157
|
-
expect(SRC).toContain('const PAGE_ROWS = 3;');
|
|
158
|
-
expect(SRC).toContain('data-act="page-next"');
|
|
159
|
-
expect(SRC).toContain('data-act="page-prev"');
|
|
160
|
-
expect(SRC).toMatch(/fullscreen\(\)\s*\?\s*\(d\.hotels \?\? \[\]\)\s*:\s*\(d\.hotels \?\? \[\]\)\.slice\(0, pageDrawn\)/);
|
|
161
|
-
expect(SRC).toContain("availableDisplayModes: ['inline', 'fullscreen']");
|
|
162
|
-
});
|
|
163
|
-
// ⚠️ THE PLAN IS ONE CARD WORTH READING (robin, 2026-09-29): the day in order,
|
|
164
|
-
// the hotel once per stay as a hotel, the nights after it as one line.
|
|
165
|
-
it('draws the day in order, and each stay once with its picture and its nights', () => {
|
|
166
|
-
expect(SRC).toContain('<ol class="moments">');
|
|
167
|
-
expect(SRC).toContain('class="plan-stay"');
|
|
168
|
-
expect(SRC).toContain('class="plan-night"');
|
|
169
|
-
});
|
|
170
|
-
// ⚠️ THE HEADS OF A COMPARISON LINE UP ROW BY ROW: the "Recommended" mark
|
|
171
|
-
// pushed its own column's picture down and nothing lined up (2026-09-29).
|
|
172
|
-
it('gives every comparison head the same rows, a mark or its empty place', () => {
|
|
173
|
-
expect(SRC).toContain("pickmark${c.recommended ? '' : ' none'}");
|
|
174
|
-
expect(CSS).toContain('grid-template-rows: subgrid;');
|
|
175
|
-
});
|
|
176
|
-
it('asks a step again when something has changed since it was drawn', () => {
|
|
177
|
-
expect(SRC).toContain('step.version === stateVersion');
|
|
178
|
-
expect(SRC).toMatch(/CHANGES_STATE = new Set\(\[[^\]]*'requestBooking'/s);
|
|
179
|
-
});
|
|
180
|
-
it('does not offer back to a booked trip or a finished change', () => {
|
|
181
|
-
expect(SRC).toContain("forget(['trip', 'needs-dates', 'needs-guest'])");
|
|
182
|
-
expect(SRC).toMatch(/const noBack = new Set\(\[[^\]]*'changed'/s);
|
|
183
|
-
});
|
|
184
|
-
it('confirms a change of room count in both directions', () => {
|
|
185
|
-
// `rooms` used to go only when it was above one: "2 rooms → 1" could not be made.
|
|
186
|
-
expect(SRC).not.toContain('Number(to.rooms) > 1');
|
|
187
|
-
});
|
|
188
|
-
it('draws every booking of a trip of more than one stay, and a refusal in words', () => {
|
|
189
|
-
expect(SRC).toContain('d.bookings');
|
|
190
|
-
expect(SRC).toContain('error: errorView');
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
describe('a trip that cannot be requested', () => {
|
|
194
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
195
|
-
it('draws no booking form under an add-on with no room', () => {
|
|
196
|
-
expect(SRC).toContain('d.needsRoom.length');
|
|
197
|
-
expect(SRC).toContain('Goes with a stay');
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
describe('a list longer than a page', () => {
|
|
201
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
202
|
-
it('offers the places it counted and the next page, folded into this one', () => {
|
|
203
|
-
expect(SRC).toContain('data-place=');
|
|
204
|
-
expect(SRC).toContain("act === 'more-hotels'");
|
|
205
|
-
// The next page joins the list on screen; it does not replace it.
|
|
206
|
-
expect(SRC).toContain('hotels: [...(shown.hotels ?? []), ...(next.hotels ?? [])]');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
describe("a list's order", () => {
|
|
210
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
211
|
-
it('is a row at the top of the list, with no price order across currencies', () => {
|
|
212
|
-
expect(SRC).toContain('data-sort=');
|
|
213
|
-
expect(SRC).toContain("priced || !k.startsWith('price')");
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
describe('where, then when', () => {
|
|
217
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
218
|
-
it('opens on a place and the dates, and prices a tile for the stay', () => {
|
|
219
|
-
expect(SRC).toContain('id="dci"');
|
|
220
|
-
expect(SRC).toContain('data-act="find"');
|
|
221
|
-
expect(SRC).toContain('const tilePrice = ');
|
|
222
|
-
expect(SRC).toContain('data-act="find-booking"');
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
describe("a list's filters", () => {
|
|
226
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
227
|
-
it('fold behind one button that counts them, with a way to clear them', () => {
|
|
228
|
-
expect(SRC).toContain('data-panel="${key}"');
|
|
229
|
-
expect(SRC).toContain('data-act="clear-filters"');
|
|
230
|
-
expect(SRC).toContain('data-filter=');
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
// Three things a guest read wrong off a room's price, and one view that showed
|
|
234
|
-
// demonstration money unmarked (2026-10-05 audit).
|
|
235
|
-
describe("a room's price and its terms", () => {
|
|
236
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
237
|
-
it('strikes through the public rate for the same number of rooms as the price under it', () => {
|
|
238
|
-
expect(SRC).toContain('r.publicRoomsTotal ?? r.publicStayTotal');
|
|
239
|
-
});
|
|
240
|
-
it('shows the free-cancellation deadline as the server wrote it, hour and all', () => {
|
|
241
|
-
expect(SRC).toContain('r.freeCancellationLocal');
|
|
242
|
-
expect(SRC).not.toMatch(/freeCancellationUntil\)\.slice\(0, 10\)/);
|
|
243
|
-
});
|
|
244
|
-
it('does not call a before-tax nightly rate "incl. tax"', () => {
|
|
245
|
-
expect(SRC).not.toContain('· incl. tax');
|
|
246
|
-
expect(SRC).toContain('/ night + tax');
|
|
247
|
-
});
|
|
248
|
-
it('marks the cancellation fee as demonstration money', () => {
|
|
249
|
-
const cancel = SRC.slice(SRC.indexOf('const cancelView'), SRC.indexOf('const cancelView') + 1200);
|
|
250
|
-
expect(cancel).toContain('demoStrip()');
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
// A trip id opens the trip, not the bookings (2026-10-05 audit, P0-3): the
|
|
254
|
-
// card asks for a booking by its code and the email it was shown with it.
|
|
255
|
-
describe("the card's booking buttons", () => {
|
|
256
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
257
|
-
it('send the code and its email, never the code alone', () => {
|
|
258
|
-
for (const call of [
|
|
259
|
-
"call('previewCancellation', byCode(",
|
|
260
|
-
"call('cancelBooking', { ...byCode(",
|
|
261
|
-
"call('listExtras', byCode(",
|
|
262
|
-
]) {
|
|
263
|
-
expect(SRC).toContain(call);
|
|
264
|
-
}
|
|
265
|
-
expect(SRC).not.toMatch(/call\('(previewCancellation|cancelBooking)', \{ booking:/);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
// One pager for every Curless card (card-kit/pager.ts, robin 2026-10-05): the
|
|
269
|
-
// hotel list pages with the shelf's markup, so it takes the shelf's look.
|
|
270
|
-
describe("the hotel list's pages", () => {
|
|
271
|
-
const SRC = readFileSync(`${widget}src/card.ts`, 'utf8');
|
|
272
|
-
const CSS = readFileSync(`${widget}src/card.css`, 'utf8');
|
|
273
|
-
it('use the shared pager and its "show more", with our own data-act', () => {
|
|
274
|
-
expect(SRC).toContain('pagerHtml({');
|
|
275
|
-
expect(SRC).toContain('prev: \'data-act="page-prev"\'');
|
|
276
|
-
expect(SRC).toContain('next: \'data-act="page-next"\'');
|
|
277
|
-
expect(SRC).toContain('moreHtml(');
|
|
278
|
-
expect(SRC).not.toContain('class="pager"');
|
|
279
|
-
expect(CSS).not.toMatch(/^\.pager\b/m);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
//# sourceMappingURL=card-contract.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"card-contract.test.js","sourceRoot":"","sources":["../../src/__tests__/card-contract.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/data.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { loadHotelData, parseHotelData } from '../data.js';
|
|
5
|
-
import { formatMoney, money, taxOn, toMinor } from '../money.js';
|
|
6
|
-
const FIXTURE = fileURLToPath(new URL('./__fixtures__/brand.json', import.meta.url));
|
|
7
|
-
const hotel = (over = {}) => ({
|
|
8
|
-
id: 'h1',
|
|
9
|
-
name: 'A Hotel',
|
|
10
|
-
brand: 'Brand',
|
|
11
|
-
city: 'Hong Kong',
|
|
12
|
-
cityId: 'hong-kong',
|
|
13
|
-
summary: 'Somewhere to sleep.',
|
|
14
|
-
amenities: ['Pool'],
|
|
15
|
-
currency: 'HKD',
|
|
16
|
-
taxRate: 0.13,
|
|
17
|
-
weekendUplift: 0.2,
|
|
18
|
-
rooms: [
|
|
19
|
-
{
|
|
20
|
-
id: 'r1',
|
|
21
|
-
name: 'Deluxe',
|
|
22
|
-
sleeps: 2,
|
|
23
|
-
breakfast: false,
|
|
24
|
-
nightly: 1000,
|
|
25
|
-
roomsLeft: 3,
|
|
26
|
-
refundable: true,
|
|
27
|
-
freeCancelHours: 48,
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
...over,
|
|
31
|
-
});
|
|
32
|
-
const file = (over = {}) => ({ brand: 'Brand', hotels: [hotel(over)] });
|
|
33
|
-
describe('the brand file we actually ship', () => {
|
|
34
|
-
// ⚠️ THESE ARE THE PACKAGE'S TESTS, ON THE PACKAGE'S OWN FIXTURE. Claims about
|
|
35
|
-
// a BRAND's data - how many hotels it has, whether its search hint's numbers
|
|
36
|
-
// are true, whether every property carries an address - left with the data
|
|
37
|
-
// when Shangri-La became a customer (2026-09-21). They are claims about their
|
|
38
|
-
// file and they belong in their repository; what stays here is the LOADER,
|
|
39
|
-
// which is ours.
|
|
40
|
-
it('parses a brand file and hands back what the tools need', () => {
|
|
41
|
-
const data = loadHotelData(FIXTURE);
|
|
42
|
-
expect(data.brand).toBe('Shangri-La');
|
|
43
|
-
expect(data.hotels.length).toBeGreaterThan(5);
|
|
44
|
-
for (const h of data.hotels) {
|
|
45
|
-
expect(h.currency).toMatch(/^[A-Z]{3}$/);
|
|
46
|
-
expect(h.rooms.length).toBeGreaterThan(0);
|
|
47
|
-
}
|
|
48
|
-
// More than one currency in one file, which is what makes the trip's
|
|
49
|
-
// one-currency refusal and the comparison's currency note reachable at all.
|
|
50
|
-
expect(new Set(data.hotels.map((h) => h.currency)).size).toBeGreaterThan(1);
|
|
51
|
-
});
|
|
52
|
-
it('keeps the hand-written hotels exactly as they were written', () => {
|
|
53
|
-
const island = loadHotelData(FIXTURE).hotels.find((h) => h.id === 'hong-kong-island');
|
|
54
|
-
expect(island?.name).toBe('Island Shangri-La, Hong Kong');
|
|
55
|
-
expect(island?.area).toBe('Admiralty');
|
|
56
|
-
expect(island?.rooms.find((r) => r.id === 'deluxe-city')?.nightly).toBe(3800);
|
|
57
|
-
expect(island?.rating?.count).toBe(3184);
|
|
58
|
-
});
|
|
59
|
-
// ⚠️ A ROOM'S PICTURE HAS TO BE THAT ROOM'S. These came from each room type's
|
|
60
|
-
// own page on the group's site, one per room - the easy wrong version is the
|
|
61
|
-
// hotel's hero shot repeated down the list, which puts a lobby under the word
|
|
62
|
-
// "Deluxe Peak View Room" and reads as a photograph of a room.
|
|
63
|
-
it('gives the hand-written hotels a picture per ROOM, none of them shared', () => {
|
|
64
|
-
const curated = new Set([
|
|
65
|
-
'hong-kong-island',
|
|
66
|
-
'hong-kong-kowloon',
|
|
67
|
-
'hong-kong-kerry',
|
|
68
|
-
'shanghai-pudong',
|
|
69
|
-
'shanghai-jingan',
|
|
70
|
-
'beijing-china-world',
|
|
71
|
-
'beijing-kerry',
|
|
72
|
-
'shenzhen-futian',
|
|
73
|
-
]);
|
|
74
|
-
// ⚠️ COMPARED WITHOUT THE QUERY STRING. The generator appends `?w=960` to a
|
|
75
|
-
// HOTEL's photograph and not to a room's, so the same picture in both places
|
|
76
|
-
// compared unequal and this guard slept through it: Kerry Beijing's hero was
|
|
77
|
-
// its own Questers family room, hotel hero and room thumbnail the same
|
|
78
|
-
// image (found when the brand data left the repo, 2026-09-21).
|
|
79
|
-
const same = (u) => String(u ?? '').split('?')[0] ?? '';
|
|
80
|
-
const seen = new Map();
|
|
81
|
-
for (const h of loadHotelData(FIXTURE).hotels) {
|
|
82
|
-
if (!curated.has(h.id))
|
|
83
|
-
continue;
|
|
84
|
-
for (const r of h.rooms) {
|
|
85
|
-
expect(r.imageUrl, `${h.id}/${r.id} has no photograph`).toBeTruthy();
|
|
86
|
-
expect(same(r.imageUrl), `${h.id}/${r.id} reuses the hotel's own picture`).not.toBe(same(h.imageUrl));
|
|
87
|
-
const where = seen.get(same(r.imageUrl));
|
|
88
|
-
expect(where, `${h.id}/${r.id} shares a photograph with ${where}`).toBeUndefined();
|
|
89
|
-
seen.set(same(r.imageUrl), `${h.id}/${r.id}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
// ⚠️ AN ADDRESS IS A FACT OR IT IS NOTHING. Generated hotels had none at all
|
|
94
|
-
// for two days - the card showed a real property with no street - and the one
|
|
95
|
-
// tempting fix is to write "in the centre of <city>", which is a sentence
|
|
96
|
-
// nobody can post a letter to. These come off each property's own page.
|
|
97
|
-
it('gives every hotel the address its own page publishes', () => {
|
|
98
|
-
for (const h of loadHotelData(FIXTURE).hotels) {
|
|
99
|
-
expect(h.address, `${h.id} has no address`).toBeTruthy();
|
|
100
|
-
expect(h.address?.length, `${h.id}'s address is too short to be one`).toBeGreaterThan(12);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
it('gives every hotel a photograph, because a wall of grey plates is not a storefront', () => {
|
|
104
|
-
const missing = loadHotelData(FIXTURE)
|
|
105
|
-
.hotels.filter((h) => !h.imageUrl)
|
|
106
|
-
.map((h) => h.id);
|
|
107
|
-
// JEN Hong Kong is not on the group's property list under a slug we could
|
|
108
|
-
// harvest; it is the only one allowed to fall back to its drawn plate.
|
|
109
|
-
expect(missing).toEqual(['hong-kong-jen']);
|
|
110
|
-
});
|
|
111
|
-
// ⚠️ THE POLICY AND THE ROOMS HAVE TO AGREE. The generated cancellation text
|
|
112
|
-
// asserted a flat "free until 48 hours before" while the hotels carry 24, 48
|
|
113
|
-
// and 72 hour rates - so a guest holding a 24-hour club rate was told, in the
|
|
114
|
-
// hotel's own voice, that they had twice the time the card was showing them
|
|
115
|
-
// on the same screen.
|
|
116
|
-
it('never promises a cancellation window no room actually has', () => {
|
|
117
|
-
for (const h of loadHotelData(FIXTURE).hotels) {
|
|
118
|
-
const text = h.policies?.cancellation ?? '';
|
|
119
|
-
const real = new Set(h.rooms.filter((r) => r.refundable).map((r) => String(r.freeCancelHours)));
|
|
120
|
-
const claimed = [...text.matchAll(/(\d+)\s*(?:,|or|and|hours)/g)].map((m) => m[1]);
|
|
121
|
-
for (const hours of claimed)
|
|
122
|
-
expect(real, `${h.id} names ${hours} hours; its rooms have ${[...real].join(', ')}`).toContain(hours);
|
|
123
|
-
// …and the SHORTEST window is the one that has to be named. Naming only
|
|
124
|
-
// the long one is how a guest on a 24-hour rate reads "free until 48
|
|
125
|
-
// hours before" in the hotel's own voice while the card, on the same
|
|
126
|
-
// screen, shows them a deadline a day earlier.
|
|
127
|
-
if (claimed.length && real.size) {
|
|
128
|
-
const shortest = Math.min(...[...real].map(Number));
|
|
129
|
-
expect(claimed.map(Number), `${h.id} names ${claimed.join('/')} hours but has a ${shortest}-hour rate`).toContain(shortest);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
// ⚠️ POINTS ARE EARNED PER UNIT OF ONE CURRENCY. A hotel quoting in a
|
|
134
|
-
// currency the programme cannot convert either earns at 1:1 - ¥150,000
|
|
135
|
-
// becoming 150,000 points - or earns nothing at all. Both are a number on a
|
|
136
|
-
// card nobody can defend, so the file is refused.
|
|
137
|
-
it('refuses a hotel charging in a currency the programme cannot convert', () => {
|
|
138
|
-
const data = JSON.parse(readFileSync(FIXTURE, 'utf8'));
|
|
139
|
-
const broken = { ...data, hotels: [{ ...data.hotels[0], id: 'x', currency: 'NZD' }] };
|
|
140
|
-
expect(() => parseHotelData(broken)).toThrow(/no earn rate/);
|
|
141
|
-
});
|
|
142
|
-
// ⚠️ A BRAND'S OWN PRODUCT NAMES BELONG TO THAT BRAND. Four generated hotels
|
|
143
|
-
// sold a "Horizon Club Room" under a Traders or Kerry sign - a Shangri-La
|
|
144
|
-
// lounge those brands do not run, printed on a card next to a real hotel's
|
|
145
|
-
// real photograph.
|
|
146
|
-
it('does not sell one brand’s named product under another brand’s sign', () => {
|
|
147
|
-
for (const h of loadHotelData(FIXTURE).hotels) {
|
|
148
|
-
if (h.brand === 'Shangri-La')
|
|
149
|
-
continue;
|
|
150
|
-
for (const r of h.rooms)
|
|
151
|
-
expect(r.name, `${h.id} is a ${h.brand} and sells "${r.name}"`).not.toMatch(/Horizon/i);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
it('gives every room a cancellation story, one way or the other', () => {
|
|
155
|
-
for (const h of loadHotelData(FIXTURE).hotels) {
|
|
156
|
-
for (const r of h.rooms) {
|
|
157
|
-
expect(r.refundable ? typeof r.freeCancelHours : 'undefined').toBe(r.refundable ? 'number' : 'undefined');
|
|
158
|
-
}
|
|
159
|
-
expect(h.policies?.cancellation, `${h.id} must publish a cancellation policy`).toBeTruthy();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
describe('what the validator refuses', () => {
|
|
164
|
-
it('a tax rate written as a percentage - 16 would be a 1600% tax that still renders', () => {
|
|
165
|
-
expect(() => parseHotelData(file({ taxRate: 16 }))).toThrow(/taxRate is 16/);
|
|
166
|
-
});
|
|
167
|
-
it('a rate typed in minor units, which would look like a real if eye-watering price', () => {
|
|
168
|
-
expect(() => parseHotelData(file({ rooms: [{ ...hotel().rooms[0], nightly: 380_000 }] }))).toThrow(/nightly is 380000/);
|
|
169
|
-
});
|
|
170
|
-
it('"free to cancel" with no deadline, and a deadline on a rate that has none', () => {
|
|
171
|
-
expect(() => parseHotelData(file({ rooms: [{ ...hotel().rooms[0], freeCancelHours: undefined }] }))).toThrow(/must say freeCancelHours/);
|
|
172
|
-
expect(() => parseHotelData(file({ rooms: [{ ...hotel().rooms[0], refundable: false }] }))).toThrow(/free window that does not exist/);
|
|
173
|
-
});
|
|
174
|
-
it('two hotels or two room types sharing an id, which would hold the wrong room', () => {
|
|
175
|
-
expect(() => parseHotelData({ brand: 'B', hotels: [hotel(), hotel()] })).toThrow(/share the id h1/);
|
|
176
|
-
expect(() => parseHotelData(file({ rooms: [hotel().rooms[0], { ...hotel().rooms[0], name: 'Other' }] }))).toThrow(/share the id r1/);
|
|
177
|
-
});
|
|
178
|
-
it('a hotel nobody can book, and a policy topic nothing would display', () => {
|
|
179
|
-
expect(() => parseHotelData(file({ rooms: [] }))).toThrow(/no room types/);
|
|
180
|
-
expect(() => parseHotelData(file({ policies: { parking: 'Free.' } }))).toThrow(/topic parking/);
|
|
181
|
-
});
|
|
182
|
-
it('a rating with no count behind it', () => {
|
|
183
|
-
expect(() => parseHotelData(file({ rating: { value: 4.8, scaleMax: 5 } }))).toThrow(/rating.count/);
|
|
184
|
-
});
|
|
185
|
-
it('a file that is empty, or missing the brand it belongs to', () => {
|
|
186
|
-
expect(() => parseHotelData({ hotels: [] })).toThrow(/no hotels/);
|
|
187
|
-
expect(() => parseHotelData({ hotels: [hotel()] })).toThrow(/brand/);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
describe('money', () => {
|
|
191
|
-
it('holds prices as integer minor units, so a tax rate cannot produce 4293.999999', () => {
|
|
192
|
-
expect(toMinor(3800, 'HKD')).toBe(380_000);
|
|
193
|
-
expect(toMinor(3800, 'JPY')).toBe(3800);
|
|
194
|
-
expect(taxOn(money(380_000, 'HKD'), 0.13)).toEqual({ minor: 49_400, currency: 'HKD' });
|
|
195
|
-
});
|
|
196
|
-
it('writes money the way the guest reads it', () => {
|
|
197
|
-
expect(formatMoney(money(380_000, 'HKD'))).toBe('HK$3,800');
|
|
198
|
-
expect(formatMoney(money(1_030_560, 'HKD'))).toBe('HK$10,305.60');
|
|
199
|
-
expect(formatMoney(money(145_000, 'CNY'))).toBe('¥1,450');
|
|
200
|
-
expect(formatMoney(money(12_000, 'JPY'))).toBe('JP¥12,000');
|
|
201
|
-
expect(formatMoney(money(5000, 'AED'))).toBe('50 AED');
|
|
202
|
-
});
|
|
203
|
-
// A trip in two currencies is totalled per currency and never summed; the
|
|
204
|
-
// symbols are all that tells the two totals apart.
|
|
205
|
-
it('never writes two currencies with the same symbol', () => {
|
|
206
|
-
const written = ['HKD', 'CNY', 'SGD', 'USD', 'JPY', 'EUR', 'GBP'].map((c) => formatMoney(money(100, c)).replace(/[\d.,]/g, ''));
|
|
207
|
-
expect(new Set(written).size).toBe(written.length);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
//# sourceMappingURL=data.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data.test.js","sourceRoot":"","sources":["../../src/__tests__/data.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErF,MAAM,KAAK,GAAG,CAAC,OAAgC,EAAE,EAAE,EAAE,CAAC,CAAC;IACrD,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,qBAAqB;IAC9B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,GAAG;IAClB,KAAK,EAAE;QACL;YACE,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,IAAI;YAChB,eAAe,EAAE,EAAE;SACpB;KACF;IACD,GAAG,IAAI;CACR,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,CAAC,OAAgC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAEjG,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,+EAA+E;IAC/E,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,iBAAiB;IACjB,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACzC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,qEAAqE;QACrE,4EAA4E;QAC5E,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,CAAC;QACtF,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9E,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,+DAA+D;IAC/D,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC;YACtB,kBAAkB;YAClB,mBAAmB;YACnB,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,qBAAqB;YACrB,eAAe;YACf,iBAAiB;SAClB,CAAC,CAAC;QACH,4EAA4E;QAC5E,6EAA6E;QAC7E,6EAA6E;QAC7E,uEAAuE;QACvE,+DAA+D;QAC/D,MAAM,IAAI,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,SAAS;YACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxB,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,UAAU,EAAE,CAAC;gBACrE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,iCAAiC,CAAC,CAAC,GAAG,CAAC,IAAI,CACjF,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CACjB,CAAC;gBACF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACzC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,6BAA6B,KAAK,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;gBACnF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,wEAAwE;IACxE,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9C,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,UAAU,EAAE,CAAC;YACzD,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,mCAAmC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC3F,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC;aACnC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpB,0EAA0E;QAC1E,uEAAuE;QACvE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,4EAA4E;IAC5E,sBAAsB;IACtB,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,IAAI,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAC1E,CAAC;YACF,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,KAAK,MAAM,KAAK,IAAI,OAAO;gBACzB,MAAM,CACJ,IAAI,EACJ,GAAG,CAAC,CAAC,EAAE,UAAU,KAAK,0BAA0B,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACvE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACrB,wEAAwE;YACxE,qEAAqE;YACrE,qEAAqE;YACrE,+CAA+C;YAC/C,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;gBACpD,MAAM,CACJ,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EACnB,GAAG,CAAC,CAAC,EAAE,UAAU,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,QAAQ,YAAY,CAC3E,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sEAAsE;IACtE,uEAAuE;IACvE,4EAA4E;IAC5E,kDAAkD;IAClD,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAE1B,CAAC;QAC5B,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACtF,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,0EAA0E;IAC1E,2EAA2E;IAC3E,mBAAmB;IACnB,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9C,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY;gBAAE,SAAS;YACvC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;gBACrB,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxB,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAChE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CACtC,CAAC;YACJ,CAAC;YACD,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC,EAAE,qCAAqC,CAAC,CAAC,UAAU,EAAE,CAAC;QAC9F,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,CAAC,GAAG,EAAE,CACV,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAC7E,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,CAAC,GAAG,EAAE,CACV,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CACvF,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,EAAE,CACV,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAC9E,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAC9E,iBAAiB,CAClB,CAAC;QACF,MAAM,CAAC,GAAG,EAAE,CACV,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAC5F,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CACjF,cAAc,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,mDAAmD;IACnD,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1E,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAClD,CAAC;QACF,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dates.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dates.test.ts"],"names":[],"mappings":""}
|