doordash-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +3 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +180 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +154 -0
- package/dist/direct-api.d.ts +530 -0
- package/dist/direct-api.js +2297 -0
- package/dist/direct-api.test.d.ts +1 -0
- package/dist/direct-api.test.js +701 -0
- package/dist/lib.d.ts +21 -0
- package/dist/lib.js +202 -0
- package/docs/examples.md +158 -0
- package/docs/install.md +63 -0
- package/man/dd-cli.1 +241 -0
- package/man/doordash-cli.1 +1 -0
- package/package.json +70 -0
- package/scripts/install-manpage.sh +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { buildAddConsumerAddressPayload, buildAddToCartPayload, buildUpdateCartPayload, extractExistingOrdersFromApolloCache, normalizeItemName, parseExistingOrderLifecycleStatus, parseExistingOrdersResponse, parseOptionSelectionsJson, parseSearchRestaurantRow, resolveAvailableAddressMatch, } from "./direct-api.js";
|
|
4
|
+
function configurableItemDetail() {
|
|
5
|
+
return {
|
|
6
|
+
success: true,
|
|
7
|
+
restaurantId: "1721744",
|
|
8
|
+
item: {
|
|
9
|
+
id: "546936015",
|
|
10
|
+
name: "Two roll selection",
|
|
11
|
+
description: "Spicy tuna, salmon avo, eel cuc, yellowtail scallion, California Roll.",
|
|
12
|
+
displayPrice: "+$18.98",
|
|
13
|
+
unitAmount: 1898,
|
|
14
|
+
currency: "USD",
|
|
15
|
+
decimalPlaces: 2,
|
|
16
|
+
menuId: "2181443",
|
|
17
|
+
specialInstructionsMaxLength: 500,
|
|
18
|
+
dietaryTags: [],
|
|
19
|
+
reviewData: null,
|
|
20
|
+
requiredOptionLists: [
|
|
21
|
+
{
|
|
22
|
+
id: "703393388",
|
|
23
|
+
name: "1st Roll Choice",
|
|
24
|
+
subtitle: "Select 1",
|
|
25
|
+
minNumOptions: 1,
|
|
26
|
+
maxNumOptions: 1,
|
|
27
|
+
numFreeOptions: 0,
|
|
28
|
+
isOptional: false,
|
|
29
|
+
options: [
|
|
30
|
+
{
|
|
31
|
+
id: "4716032529",
|
|
32
|
+
name: "California Roll",
|
|
33
|
+
displayPrice: "",
|
|
34
|
+
unitAmount: 0,
|
|
35
|
+
defaultQuantity: 0,
|
|
36
|
+
nextCursor: null,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "703393389",
|
|
42
|
+
name: "2nd Roll Choice",
|
|
43
|
+
subtitle: "Select 1",
|
|
44
|
+
minNumOptions: 1,
|
|
45
|
+
maxNumOptions: 1,
|
|
46
|
+
numFreeOptions: 0,
|
|
47
|
+
isOptional: false,
|
|
48
|
+
options: [
|
|
49
|
+
{
|
|
50
|
+
id: "4716042466",
|
|
51
|
+
name: "California Roll",
|
|
52
|
+
displayPrice: "",
|
|
53
|
+
unitAmount: 0,
|
|
54
|
+
defaultQuantity: 0,
|
|
55
|
+
nextCursor: null,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
optionLists: [
|
|
61
|
+
{
|
|
62
|
+
id: "703393388",
|
|
63
|
+
name: "1st Roll Choice",
|
|
64
|
+
subtitle: "Select 1",
|
|
65
|
+
minNumOptions: 1,
|
|
66
|
+
maxNumOptions: 1,
|
|
67
|
+
numFreeOptions: 0,
|
|
68
|
+
isOptional: false,
|
|
69
|
+
options: [
|
|
70
|
+
{
|
|
71
|
+
id: "4716032529",
|
|
72
|
+
name: "California Roll",
|
|
73
|
+
displayPrice: "",
|
|
74
|
+
unitAmount: 0,
|
|
75
|
+
defaultQuantity: 0,
|
|
76
|
+
nextCursor: null,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "703393389",
|
|
82
|
+
name: "2nd Roll Choice",
|
|
83
|
+
subtitle: "Select 1",
|
|
84
|
+
minNumOptions: 1,
|
|
85
|
+
maxNumOptions: 1,
|
|
86
|
+
numFreeOptions: 0,
|
|
87
|
+
isOptional: false,
|
|
88
|
+
options: [
|
|
89
|
+
{
|
|
90
|
+
id: "4716042466",
|
|
91
|
+
name: "California Roll",
|
|
92
|
+
displayPrice: "",
|
|
93
|
+
unitAmount: 0,
|
|
94
|
+
defaultQuantity: 0,
|
|
95
|
+
nextCursor: null,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
preferences: [],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
test("normalizeItemName trims and collapses whitespace", () => {
|
|
105
|
+
assert.equal(normalizeItemName(" Sushi premium "), "sushi premium");
|
|
106
|
+
});
|
|
107
|
+
test("parseSearchRestaurantRow extracts restaurant metadata from facet rows", () => {
|
|
108
|
+
const row = parseSearchRestaurantRow({
|
|
109
|
+
id: "row.store:24633898:0",
|
|
110
|
+
text: {
|
|
111
|
+
title: "Poke Bowl",
|
|
112
|
+
description: "$$ • Hawaiian, Seafood Restaurant",
|
|
113
|
+
custom: [
|
|
114
|
+
{ key: "delivery_fee_string", value: "$0 delivery fee over $7" },
|
|
115
|
+
{ key: "eta_display_string", value: "1.0 mi • 32 min" },
|
|
116
|
+
{ key: "is_retail", value: "false" },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
images: {
|
|
120
|
+
main: {
|
|
121
|
+
uri: "https://img.cdn4dd.com/example.jpeg",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
events: {
|
|
125
|
+
click: {
|
|
126
|
+
name: "navigate",
|
|
127
|
+
data: JSON.stringify({ domain: "https://www.doordash.com/", uri: "store/24633898/?pickup=false" }),
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
component: {
|
|
131
|
+
id: "row.store",
|
|
132
|
+
category: "row",
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
assert.deepEqual(row, {
|
|
136
|
+
id: "24633898",
|
|
137
|
+
name: "Poke Bowl",
|
|
138
|
+
description: "$$ • Hawaiian, Seafood Restaurant",
|
|
139
|
+
cuisines: ["Hawaiian, Seafood Restaurant"],
|
|
140
|
+
isRetail: false,
|
|
141
|
+
eta: "1.0 mi • 32 min",
|
|
142
|
+
deliveryFee: "$0 delivery fee over $7",
|
|
143
|
+
imageUrl: "https://img.cdn4dd.com/example.jpeg",
|
|
144
|
+
url: "https://www.doordash.com/store/24633898/?pickup=false",
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
test("parseOptionSelectionsJson parses structured recursive option selections", () => {
|
|
148
|
+
assert.deepEqual(parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529"},{"groupId":"recommended_option_546935995","optionId":"546936011","children":[{"groupId":"780057412","optionId":"4702669757","quantity":2}]}]'), [
|
|
149
|
+
{ groupId: "703393388", optionId: "4716032529" },
|
|
150
|
+
{
|
|
151
|
+
groupId: "recommended_option_546935995",
|
|
152
|
+
optionId: "546936011",
|
|
153
|
+
children: [{ groupId: "780057412", optionId: "4702669757", quantity: 2 }],
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
});
|
|
157
|
+
test("parseOptionSelectionsJson rejects malformed payloads", () => {
|
|
158
|
+
assert.throws(() => parseOptionSelectionsJson('{"groupId":"x"}'), /must be a JSON array/);
|
|
159
|
+
assert.throws(() => parseOptionSelectionsJson('[{"groupId":"703393388"}]'), /must include string groupId and optionId/);
|
|
160
|
+
assert.throws(() => parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529","quantity":0}]'), /Invalid option quantity/);
|
|
161
|
+
assert.throws(() => parseOptionSelectionsJson('[{"groupId":"703393388","optionId":"4716032529","children":{}}]'), /children must be an array/);
|
|
162
|
+
});
|
|
163
|
+
test("resolveAvailableAddressMatch prefers a saved address id from autocomplete/get-or-create", () => {
|
|
164
|
+
const match = resolveAvailableAddressMatch({
|
|
165
|
+
input: "350 5th Ave, New York, NY 10118",
|
|
166
|
+
availableAddresses: [
|
|
167
|
+
{
|
|
168
|
+
id: "5266870966",
|
|
169
|
+
addressId: "1387447699",
|
|
170
|
+
printableAddress: "350 5th Ave, New York, NY 10118, USA",
|
|
171
|
+
shortname: "350 5th Ave",
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
prediction: {
|
|
175
|
+
geo_address_id: "1387447699",
|
|
176
|
+
formatted_address: "350 5th Ave, New York, NY 10118, USA",
|
|
177
|
+
},
|
|
178
|
+
createdAddress: {
|
|
179
|
+
id: "1387447699",
|
|
180
|
+
formatted_address: "350 5th Ave, New York, NY 10118, USA",
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
assert.deepEqual(match, {
|
|
184
|
+
id: "5266870966",
|
|
185
|
+
printableAddress: "350 5th Ave, New York, NY 10118, USA",
|
|
186
|
+
source: "autocomplete-address-id",
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
test("resolveAvailableAddressMatch falls back to printable/shortname text matching", () => {
|
|
190
|
+
const match = resolveAvailableAddressMatch({
|
|
191
|
+
input: "350 5th Ave, New York, NY 10118",
|
|
192
|
+
availableAddresses: [
|
|
193
|
+
{
|
|
194
|
+
id: "5266870966",
|
|
195
|
+
addressId: "1387447699",
|
|
196
|
+
printableAddress: "350 5th Ave, New York, NY 10118, USA",
|
|
197
|
+
shortname: "350 5th Ave",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
assert.deepEqual(match, {
|
|
202
|
+
id: "5266870966",
|
|
203
|
+
printableAddress: "350 5th Ave, New York, NY 10118, USA",
|
|
204
|
+
source: "saved-address",
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
test("buildAddConsumerAddressPayload maps autocomplete/get-or-create data into addConsumerAddressV2 variables", () => {
|
|
208
|
+
const payload = buildAddConsumerAddressPayload({
|
|
209
|
+
requestedAddress: "11 Wall St, New York, NY 10005",
|
|
210
|
+
prediction: {
|
|
211
|
+
source_place_id: "ChIJ8fw4t0hawokRk1YdVjndM9w",
|
|
212
|
+
formatted_address: "11 Wall St, New York, NY 10005, USA",
|
|
213
|
+
formatted_address_short: "11 Wall St",
|
|
214
|
+
locality: "New York",
|
|
215
|
+
administrative_area_level1: "NY",
|
|
216
|
+
postal_code: "10005",
|
|
217
|
+
lat: 40.707757,
|
|
218
|
+
lng: -74.010045,
|
|
219
|
+
},
|
|
220
|
+
createdAddress: {
|
|
221
|
+
id: "1386875882",
|
|
222
|
+
formatted_address: "11 Wall St, New York, NY 10005, USA",
|
|
223
|
+
formatted_address_short: "11 Wall St",
|
|
224
|
+
locality: "New York",
|
|
225
|
+
administrative_area_level1: "NY",
|
|
226
|
+
postal_code: "10005",
|
|
227
|
+
lat: 40.707757,
|
|
228
|
+
lng: -74.010045,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
assert.deepEqual(payload, {
|
|
232
|
+
lat: 40.707757,
|
|
233
|
+
lng: -74.010045,
|
|
234
|
+
city: "New York",
|
|
235
|
+
state: "NY",
|
|
236
|
+
zipCode: "10005",
|
|
237
|
+
printableAddress: "11 Wall St, New York, NY 10005, USA",
|
|
238
|
+
shortname: "11 Wall St",
|
|
239
|
+
googlePlaceId: "ChIJ8fw4t0hawokRk1YdVjndM9w",
|
|
240
|
+
subpremise: null,
|
|
241
|
+
driverInstructions: null,
|
|
242
|
+
dropoffOptionId: null,
|
|
243
|
+
manualLat: null,
|
|
244
|
+
manualLng: null,
|
|
245
|
+
addressLinkType: "ADDRESS_LINK_TYPE_UNSPECIFIED",
|
|
246
|
+
buildingName: null,
|
|
247
|
+
entryCode: null,
|
|
248
|
+
personalAddressLabel: null,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
test("parseExistingOrderLifecycleStatus derives active, fulfilled, and cancelled states", () => {
|
|
252
|
+
assert.equal(parseExistingOrderLifecycleStatus({ createdAt: "2026-03-01T12:00:00Z" }), "draft");
|
|
253
|
+
assert.equal(parseExistingOrderLifecycleStatus({ createdAt: "2026-03-01T12:00:00Z", submittedAt: "2026-03-01T12:01:00Z" }), "submitted");
|
|
254
|
+
assert.equal(parseExistingOrderLifecycleStatus({ pollingInterval: 30, submittedAt: "2026-03-01T12:01:00Z" }), "in-progress");
|
|
255
|
+
assert.equal(parseExistingOrderLifecycleStatus({ fulfilledAt: "2026-03-01T12:45:00Z" }), "fulfilled");
|
|
256
|
+
assert.equal(parseExistingOrderLifecycleStatus({ cancelledAt: "2026-03-01T12:10:00Z" }), "cancelled");
|
|
257
|
+
});
|
|
258
|
+
test("parseExistingOrdersResponse normalizes DoorDash order history payloads", () => {
|
|
259
|
+
const orders = parseExistingOrdersResponse([
|
|
260
|
+
{
|
|
261
|
+
id: "order-row-2",
|
|
262
|
+
orderUuid: "order-uuid-2",
|
|
263
|
+
deliveryUuid: "delivery-uuid-2",
|
|
264
|
+
createdAt: "2026-03-02T12:00:00Z",
|
|
265
|
+
fulfilledAt: "2026-03-02T12:50:00Z",
|
|
266
|
+
pollingInterval: null,
|
|
267
|
+
isReorderable: true,
|
|
268
|
+
isGift: false,
|
|
269
|
+
isPickup: false,
|
|
270
|
+
isRetail: false,
|
|
271
|
+
isMerchantShipping: false,
|
|
272
|
+
containsAlcohol: false,
|
|
273
|
+
fulfillmentType: "DELIVERY",
|
|
274
|
+
shoppingProtocol: "STANDARD",
|
|
275
|
+
orderFilterType: "PAST",
|
|
276
|
+
store: {
|
|
277
|
+
id: "store-2",
|
|
278
|
+
name: "Sushi Place",
|
|
279
|
+
business: { name: "Sushi Place" },
|
|
280
|
+
},
|
|
281
|
+
grandTotal: {
|
|
282
|
+
unitAmount: 2599,
|
|
283
|
+
currency: "USD",
|
|
284
|
+
decimalPlaces: 2,
|
|
285
|
+
displayString: "$25.99",
|
|
286
|
+
sign: null,
|
|
287
|
+
},
|
|
288
|
+
orders: [
|
|
289
|
+
{
|
|
290
|
+
id: "sub-order-2",
|
|
291
|
+
items: [
|
|
292
|
+
{
|
|
293
|
+
id: "line-2",
|
|
294
|
+
name: "Salmon Roll",
|
|
295
|
+
quantity: 2,
|
|
296
|
+
specialInstructions: "extra ginger",
|
|
297
|
+
substitutionPreferences: "substitute",
|
|
298
|
+
originalItemPrice: 1299,
|
|
299
|
+
purchaseType: "PURCHASE_TYPE_UNIT",
|
|
300
|
+
purchaseQuantity: {
|
|
301
|
+
discreteQuantity: { quantity: 2, unit: "ea" },
|
|
302
|
+
},
|
|
303
|
+
fulfillQuantity: {
|
|
304
|
+
discreteQuantity: { quantity: 2, unit: "ea" },
|
|
305
|
+
},
|
|
306
|
+
orderItemExtras: [
|
|
307
|
+
{
|
|
308
|
+
menuItemExtraId: "extra-1",
|
|
309
|
+
name: "Sauces",
|
|
310
|
+
orderItemExtraOptions: [
|
|
311
|
+
{
|
|
312
|
+
menuExtraOptionId: "option-1",
|
|
313
|
+
name: "Soy Sauce",
|
|
314
|
+
description: "low sodium",
|
|
315
|
+
price: 0,
|
|
316
|
+
quantity: 1,
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: "order-row-1",
|
|
328
|
+
orderUuid: "order-uuid-1",
|
|
329
|
+
deliveryUuid: "delivery-uuid-1",
|
|
330
|
+
createdAt: "2026-03-03T12:00:00Z",
|
|
331
|
+
submittedAt: "2026-03-03T12:01:00Z",
|
|
332
|
+
pollingInterval: 30,
|
|
333
|
+
isReorderable: false,
|
|
334
|
+
isGift: false,
|
|
335
|
+
isPickup: false,
|
|
336
|
+
isRetail: false,
|
|
337
|
+
isMerchantShipping: false,
|
|
338
|
+
containsAlcohol: false,
|
|
339
|
+
fulfillmentType: "DELIVERY",
|
|
340
|
+
shoppingProtocol: "STANDARD",
|
|
341
|
+
orderFilterType: "ACTIVE",
|
|
342
|
+
store: {
|
|
343
|
+
id: "store-1",
|
|
344
|
+
name: "Burger Spot",
|
|
345
|
+
business: { name: "Burger Spot" },
|
|
346
|
+
},
|
|
347
|
+
orders: [{ id: "sub-order-1", items: [{ id: "line-1", name: "Burger", quantity: 1 }] }],
|
|
348
|
+
},
|
|
349
|
+
]);
|
|
350
|
+
assert.equal(orders[0]?.orderUuid, "order-uuid-1");
|
|
351
|
+
assert.equal(orders[0]?.lifecycleStatus, "in-progress");
|
|
352
|
+
assert.equal(orders[0]?.isActive, true);
|
|
353
|
+
assert.equal(orders[1]?.grandTotal?.displayString, "$25.99");
|
|
354
|
+
assert.equal(orders[1]?.items[0]?.extras[0]?.options[0]?.name, "Soy Sauce");
|
|
355
|
+
});
|
|
356
|
+
test("extractExistingOrdersFromApolloCache resolves Apollo refs from the orders page cache", () => {
|
|
357
|
+
const orders = extractExistingOrdersFromApolloCache({
|
|
358
|
+
ROOT_QUERY: {
|
|
359
|
+
'getConsumerOrdersWithDetails({"includeCancelled":true,"limit":10,"offset":0})': [{ __ref: "ConsumerOrder:1" }],
|
|
360
|
+
},
|
|
361
|
+
'ConsumerOrder:1': {
|
|
362
|
+
id: "row-1",
|
|
363
|
+
orderUuid: "order-uuid-1",
|
|
364
|
+
deliveryUuid: "delivery-uuid-1",
|
|
365
|
+
createdAt: "2026-03-04T12:00:00Z",
|
|
366
|
+
submittedAt: "2026-03-04T12:01:00Z",
|
|
367
|
+
pollingInterval: 20,
|
|
368
|
+
isReorderable: false,
|
|
369
|
+
isGift: false,
|
|
370
|
+
isPickup: false,
|
|
371
|
+
isRetail: false,
|
|
372
|
+
isMerchantShipping: false,
|
|
373
|
+
containsAlcohol: false,
|
|
374
|
+
fulfillmentType: "DELIVERY",
|
|
375
|
+
shoppingProtocol: "STANDARD",
|
|
376
|
+
orderFilterType: "ACTIVE",
|
|
377
|
+
store: { __ref: "Store:1" },
|
|
378
|
+
orders: [{ __ref: "GroupedOrder:1" }],
|
|
379
|
+
grandTotal: { displayString: "$19.99", unitAmount: 1999, currency: "USD", decimalPlaces: 2, sign: null },
|
|
380
|
+
likelyOosItems: [],
|
|
381
|
+
},
|
|
382
|
+
'Store:1': {
|
|
383
|
+
id: "store-1",
|
|
384
|
+
name: "Burger Spot",
|
|
385
|
+
business: { name: "Burger Spot" },
|
|
386
|
+
},
|
|
387
|
+
'GroupedOrder:1': {
|
|
388
|
+
id: "group-1",
|
|
389
|
+
items: [{ __ref: "OrderItem:1" }],
|
|
390
|
+
},
|
|
391
|
+
'OrderItem:1': {
|
|
392
|
+
id: "line-1",
|
|
393
|
+
name: "Burger",
|
|
394
|
+
quantity: 1,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
assert.equal(orders.length, 1);
|
|
398
|
+
assert.equal(orders[0]?.store?.name, "Burger Spot");
|
|
399
|
+
assert.equal(orders[0]?.items[0]?.name, "Burger");
|
|
400
|
+
assert.equal(orders[0]?.hasLiveTracking, true);
|
|
401
|
+
});
|
|
402
|
+
test("buildAddToCartPayload blocks required-option items when no selections are provided", async () => {
|
|
403
|
+
await assert.rejects(() => buildAddToCartPayload({
|
|
404
|
+
restaurantId: "1721744",
|
|
405
|
+
cartId: "",
|
|
406
|
+
quantity: 1,
|
|
407
|
+
specialInstructions: null,
|
|
408
|
+
optionSelections: [],
|
|
409
|
+
item: {
|
|
410
|
+
id: "546936015",
|
|
411
|
+
name: "Two roll selection",
|
|
412
|
+
description: "Spicy tuna, salmon avo",
|
|
413
|
+
displayPrice: "$18.98",
|
|
414
|
+
imageUrl: null,
|
|
415
|
+
nextCursor: null,
|
|
416
|
+
storeId: "1721744",
|
|
417
|
+
},
|
|
418
|
+
itemDetail: configurableItemDetail(),
|
|
419
|
+
}), /required option groups/);
|
|
420
|
+
});
|
|
421
|
+
test("buildAddToCartPayload preserves the captured DoorDash request shape for quick-add items", async () => {
|
|
422
|
+
const payload = await buildAddToCartPayload({
|
|
423
|
+
restaurantId: "1721744",
|
|
424
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
425
|
+
quantity: 2,
|
|
426
|
+
specialInstructions: "extra napkins",
|
|
427
|
+
optionSelections: [],
|
|
428
|
+
item: {
|
|
429
|
+
id: "876658890",
|
|
430
|
+
name: " Sushi premium",
|
|
431
|
+
description: "10pc sushi & NegiToro roll.",
|
|
432
|
+
displayPrice: "$49.00",
|
|
433
|
+
imageUrl: null,
|
|
434
|
+
nextCursor: null,
|
|
435
|
+
storeId: "1721744",
|
|
436
|
+
},
|
|
437
|
+
itemDetail: {
|
|
438
|
+
success: true,
|
|
439
|
+
restaurantId: "1721744",
|
|
440
|
+
item: {
|
|
441
|
+
id: "876658890",
|
|
442
|
+
name: " Sushi premium",
|
|
443
|
+
description: "10pc sushi & NegiToro roll.",
|
|
444
|
+
displayPrice: "+$49.00",
|
|
445
|
+
unitAmount: 4900,
|
|
446
|
+
currency: "USD",
|
|
447
|
+
decimalPlaces: 2,
|
|
448
|
+
menuId: "2181443",
|
|
449
|
+
specialInstructionsMaxLength: 500,
|
|
450
|
+
dietaryTags: [],
|
|
451
|
+
reviewData: null,
|
|
452
|
+
requiredOptionLists: [],
|
|
453
|
+
optionLists: [],
|
|
454
|
+
preferences: [],
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
assert.deepEqual(payload, {
|
|
459
|
+
addCartItemInput: {
|
|
460
|
+
storeId: "1721744",
|
|
461
|
+
menuId: "2181443",
|
|
462
|
+
itemId: "876658890",
|
|
463
|
+
itemName: " Sushi premium",
|
|
464
|
+
itemDescription: "10pc sushi & NegiToro roll.",
|
|
465
|
+
currency: "USD",
|
|
466
|
+
quantity: 2,
|
|
467
|
+
nestedOptions: "[]",
|
|
468
|
+
specialInstructions: "extra napkins",
|
|
469
|
+
substitutionPreference: "substitute",
|
|
470
|
+
isBundle: false,
|
|
471
|
+
bundleType: "BUNDLE_TYPE_UNSPECIFIED",
|
|
472
|
+
unitPrice: 4900,
|
|
473
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
474
|
+
},
|
|
475
|
+
lowPriorityBatchAddCartItemInput: [],
|
|
476
|
+
fulfillmentContext: {
|
|
477
|
+
shouldUpdateFulfillment: false,
|
|
478
|
+
fulfillmentType: "Delivery",
|
|
479
|
+
},
|
|
480
|
+
monitoringContext: {
|
|
481
|
+
isGroup: false,
|
|
482
|
+
},
|
|
483
|
+
cartContext: {
|
|
484
|
+
isBundle: false,
|
|
485
|
+
},
|
|
486
|
+
returnCartFromOrderService: false,
|
|
487
|
+
shouldKeepOnlyOneActiveCart: false,
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
test("buildAddToCartPayload builds validated nestedOptions for configurable items", async () => {
|
|
491
|
+
const payload = await buildAddToCartPayload({
|
|
492
|
+
restaurantId: "1721744",
|
|
493
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
494
|
+
quantity: 1,
|
|
495
|
+
specialInstructions: null,
|
|
496
|
+
optionSelections: [
|
|
497
|
+
{ groupId: "703393388", optionId: "4716032529" },
|
|
498
|
+
{ groupId: "703393389", optionId: "4716042466" },
|
|
499
|
+
],
|
|
500
|
+
item: {
|
|
501
|
+
id: "546936015",
|
|
502
|
+
name: "Two roll selection",
|
|
503
|
+
description: "Spicy tuna, salmon avo, eel cuc, yellowtail scallion, California Roll.",
|
|
504
|
+
displayPrice: "$18.98",
|
|
505
|
+
imageUrl: null,
|
|
506
|
+
nextCursor: null,
|
|
507
|
+
storeId: "1721744",
|
|
508
|
+
},
|
|
509
|
+
itemDetail: configurableItemDetail(),
|
|
510
|
+
});
|
|
511
|
+
assert.deepEqual(JSON.parse(payload.addCartItemInput.nestedOptions), [
|
|
512
|
+
{
|
|
513
|
+
id: "4716032529",
|
|
514
|
+
quantity: 1,
|
|
515
|
+
options: [],
|
|
516
|
+
itemExtraOption: {
|
|
517
|
+
id: "4716032529",
|
|
518
|
+
name: "California Roll",
|
|
519
|
+
description: "California Roll",
|
|
520
|
+
price: 0,
|
|
521
|
+
itemExtraName: null,
|
|
522
|
+
chargeAbove: 0,
|
|
523
|
+
defaultQuantity: 0,
|
|
524
|
+
itemExtraId: "703393388",
|
|
525
|
+
itemExtraNumFreeOptions: 0,
|
|
526
|
+
menuItemExtraOptionPrice: 0,
|
|
527
|
+
menuItemExtraOptionBasePrice: null,
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
id: "4716042466",
|
|
532
|
+
quantity: 1,
|
|
533
|
+
options: [],
|
|
534
|
+
itemExtraOption: {
|
|
535
|
+
id: "4716042466",
|
|
536
|
+
name: "California Roll",
|
|
537
|
+
description: "California Roll",
|
|
538
|
+
price: 0,
|
|
539
|
+
itemExtraName: null,
|
|
540
|
+
chargeAbove: 0,
|
|
541
|
+
defaultQuantity: 0,
|
|
542
|
+
itemExtraId: "703393389",
|
|
543
|
+
itemExtraNumFreeOptions: 0,
|
|
544
|
+
menuItemExtraOptionPrice: 0,
|
|
545
|
+
menuItemExtraOptionBasePrice: null,
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
]);
|
|
549
|
+
});
|
|
550
|
+
test("buildAddToCartPayload routes standalone recommended next-cursor items into lowPriorityBatchAddCartItemInput", async () => {
|
|
551
|
+
const detail = configurableItemDetail();
|
|
552
|
+
detail.item.optionLists.push({
|
|
553
|
+
id: "recommended_option_546935995",
|
|
554
|
+
name: "Recommended Beverages",
|
|
555
|
+
subtitle: null,
|
|
556
|
+
minNumOptions: 0,
|
|
557
|
+
maxNumOptions: 10,
|
|
558
|
+
numFreeOptions: 0,
|
|
559
|
+
isOptional: true,
|
|
560
|
+
options: [
|
|
561
|
+
{
|
|
562
|
+
id: "546936011",
|
|
563
|
+
name: "Sake (salmon)",
|
|
564
|
+
displayPrice: "+$5.00",
|
|
565
|
+
unitAmount: 500,
|
|
566
|
+
defaultQuantity: 0,
|
|
567
|
+
nextCursor: "opaque-next-cursor",
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
});
|
|
571
|
+
const payload = await buildAddToCartPayload({
|
|
572
|
+
restaurantId: "1721744",
|
|
573
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
574
|
+
quantity: 1,
|
|
575
|
+
specialInstructions: null,
|
|
576
|
+
optionSelections: [
|
|
577
|
+
{ groupId: "703393388", optionId: "4716032529" },
|
|
578
|
+
{ groupId: "703393389", optionId: "4716042466" },
|
|
579
|
+
{
|
|
580
|
+
groupId: "recommended_option_546935995",
|
|
581
|
+
optionId: "546936011",
|
|
582
|
+
children: [{ groupId: "780057412", optionId: "4702669757" }],
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
item: {
|
|
586
|
+
id: "546936015",
|
|
587
|
+
name: "Two roll selection",
|
|
588
|
+
description: "Spicy tuna, salmon avo, eel cuc, yellowtail scallion, California Roll.",
|
|
589
|
+
displayPrice: "$18.98",
|
|
590
|
+
imageUrl: null,
|
|
591
|
+
nextCursor: null,
|
|
592
|
+
storeId: "1721744",
|
|
593
|
+
},
|
|
594
|
+
itemDetail: detail,
|
|
595
|
+
resolveNestedOptionLists: async () => [
|
|
596
|
+
{
|
|
597
|
+
id: "780057412",
|
|
598
|
+
name: "Choice",
|
|
599
|
+
subtitle: "Select 1",
|
|
600
|
+
minNumOptions: 1,
|
|
601
|
+
maxNumOptions: 1,
|
|
602
|
+
numFreeOptions: 0,
|
|
603
|
+
isOptional: false,
|
|
604
|
+
options: [
|
|
605
|
+
{
|
|
606
|
+
id: "4702669757",
|
|
607
|
+
name: "sashimi",
|
|
608
|
+
displayPrice: "",
|
|
609
|
+
unitAmount: 0,
|
|
610
|
+
defaultQuantity: 0,
|
|
611
|
+
nextCursor: null,
|
|
612
|
+
},
|
|
613
|
+
],
|
|
614
|
+
},
|
|
615
|
+
],
|
|
616
|
+
});
|
|
617
|
+
assert.deepEqual(payload.lowPriorityBatchAddCartItemInput, [
|
|
618
|
+
{
|
|
619
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
620
|
+
storeId: "1721744",
|
|
621
|
+
menuId: "2181443",
|
|
622
|
+
itemId: "546936011",
|
|
623
|
+
itemName: "Sake (salmon)",
|
|
624
|
+
currency: "USD",
|
|
625
|
+
quantity: 1,
|
|
626
|
+
unitPrice: 500,
|
|
627
|
+
isBundle: false,
|
|
628
|
+
bundleType: "BUNDLE_TYPE_UNSPECIFIED",
|
|
629
|
+
nestedOptions: JSON.stringify([
|
|
630
|
+
{
|
|
631
|
+
id: "4702669757",
|
|
632
|
+
quantity: 1,
|
|
633
|
+
options: [],
|
|
634
|
+
itemExtraOption: {
|
|
635
|
+
id: "4702669757",
|
|
636
|
+
name: "sashimi",
|
|
637
|
+
description: "sashimi",
|
|
638
|
+
price: 0,
|
|
639
|
+
chargeAbove: 0,
|
|
640
|
+
defaultQuantity: 0,
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
]),
|
|
644
|
+
},
|
|
645
|
+
]);
|
|
646
|
+
});
|
|
647
|
+
test("buildAddToCartPayload still fails closed for non-recommended next-cursor groups", async () => {
|
|
648
|
+
const detail = configurableItemDetail();
|
|
649
|
+
const nestedOption = detail.item.optionLists[1]?.options[0];
|
|
650
|
+
assert.ok(nestedOption);
|
|
651
|
+
nestedOption.nextCursor = "opaque-next-cursor";
|
|
652
|
+
await assert.rejects(() => buildAddToCartPayload({
|
|
653
|
+
restaurantId: "1721744",
|
|
654
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
655
|
+
quantity: 1,
|
|
656
|
+
specialInstructions: null,
|
|
657
|
+
optionSelections: [
|
|
658
|
+
{ groupId: "703393388", optionId: "4716032529" },
|
|
659
|
+
{ groupId: "703393389", optionId: "4716042466" },
|
|
660
|
+
],
|
|
661
|
+
item: {
|
|
662
|
+
id: "546936015",
|
|
663
|
+
name: "Two roll selection",
|
|
664
|
+
description: "Spicy tuna, salmon avo, eel cuc, yellowtail scallion, California Roll.",
|
|
665
|
+
displayPrice: "$18.98",
|
|
666
|
+
imageUrl: null,
|
|
667
|
+
nextCursor: null,
|
|
668
|
+
storeId: "1721744",
|
|
669
|
+
},
|
|
670
|
+
itemDetail: detail,
|
|
671
|
+
resolveNestedOptionLists: async () => [],
|
|
672
|
+
}), /safe direct cart shape is only confirmed for standalone recommended add-on groups/);
|
|
673
|
+
});
|
|
674
|
+
test("buildUpdateCartPayload preserves the captured DoorDash request shape", () => {
|
|
675
|
+
const payload = buildUpdateCartPayload({
|
|
676
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
677
|
+
cartItemId: "3b231d03-5a72-4636-8d12-c8769d706d45",
|
|
678
|
+
itemId: "876658890",
|
|
679
|
+
quantity: 1,
|
|
680
|
+
storeId: "1721744",
|
|
681
|
+
});
|
|
682
|
+
assert.deepEqual(payload, {
|
|
683
|
+
updateCartItemApiParams: {
|
|
684
|
+
cartId: "90a554a1-cc69-462b-8860-911ddf2d7f88",
|
|
685
|
+
cartItemId: "3b231d03-5a72-4636-8d12-c8769d706d45",
|
|
686
|
+
itemId: "876658890",
|
|
687
|
+
quantity: 1,
|
|
688
|
+
storeId: "1721744",
|
|
689
|
+
purchaseTypeOptions: {
|
|
690
|
+
purchaseType: "PURCHASE_TYPE_UNSPECIFIED",
|
|
691
|
+
continuousQuantity: 0,
|
|
692
|
+
unit: null,
|
|
693
|
+
},
|
|
694
|
+
cartFilter: null,
|
|
695
|
+
},
|
|
696
|
+
fulfillmentContext: {
|
|
697
|
+
shouldUpdateFulfillment: false,
|
|
698
|
+
},
|
|
699
|
+
returnCartFromOrderService: false,
|
|
700
|
+
});
|
|
701
|
+
});
|