@labdigital/commercetools-mock 2.14.2 → 2.16.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.
@@ -330,4 +330,44 @@ describe('Customer Update Actions', () => {
330
330
  },
331
331
  ])
332
332
  })
333
+
334
+ test('setCustomerNumber', async () => {
335
+ assert(customer, 'customer not created')
336
+
337
+ ctMock.project('dummy').add('customer', customer)
338
+
339
+ const response = await supertest(ctMock.app)
340
+ .post(`/dummy/customers/${customer.id}`)
341
+ .send({
342
+ version: 1,
343
+ actions: [
344
+ { action: 'setCustomerNumber', customerNumber: 'CUSTOMER-001' },
345
+ ],
346
+ })
347
+ expect(response.status).toBe(200)
348
+ expect(response.body.version).toBe(2)
349
+ expect(response.body.customerNumber).toBe('CUSTOMER-001')
350
+ })
351
+
352
+ test('setCustomerNumber error when already have a customer number', async () => {
353
+ assert(customer, 'customer not created')
354
+
355
+ ctMock.project('dummy').add('customer', {
356
+ ...customer,
357
+ customerNumber: 'CUSTOMER-002',
358
+ })
359
+
360
+ const response = await supertest(ctMock.app)
361
+ .post(`/dummy/customers/${customer.id}`)
362
+ .send({
363
+ version: 1,
364
+ actions: [
365
+ { action: 'setCustomerNumber', customerNumber: 'CUSTOMER-001' },
366
+ ],
367
+ })
368
+ expect(response.status).toBe(500)
369
+ expect(response.body.error).toBe(
370
+ 'A Customer number already exists and cannot be set again.'
371
+ )
372
+ })
333
373
  })
@@ -0,0 +1,352 @@
1
+ import {
2
+ Product,
3
+ ShoppingList,
4
+ ShoppingListDraft,
5
+ } from '@commercetools/platform-sdk'
6
+ import supertest from 'supertest'
7
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest'
8
+ import { CommercetoolsMock } from '../ctMock'
9
+
10
+ const shoppingList: ShoppingList = {
11
+ id: 'f15b4a80-7def-4381-bf6a-c66cab258a2b',
12
+ version: 1,
13
+ lineItems: [
14
+ {
15
+ addedAt: '2021-08-03T14:19:29.496Z',
16
+ productType: { typeId: 'product-type', id: 'product-type-id' },
17
+ id: '42ea3c57-aced-49ea-ae70-7005a47c7463',
18
+ productId: '303bf5d8-1201-4fb9-8157-ff6efb8c04b4',
19
+ name: {},
20
+ quantity: 1,
21
+ productSlug: {},
22
+ variantId: 2,
23
+ },
24
+ ],
25
+ textLineItems: [],
26
+ createdAt: '2021-07-22T12:23:33.472Z',
27
+ lastModifiedAt: '2021-08-03T14:19:29.496Z',
28
+ name: {},
29
+ }
30
+
31
+ export const product: Product = {
32
+ id: '303bf5d8-1201-4fb9-8157-ff6efb8c04b4',
33
+ createdAt: '2022-05-30T13:21:26.777Z',
34
+ lastModifiedAt: '2022-05-30T13:21:26.777Z',
35
+ version: 1,
36
+ productType: {
37
+ typeId: 'product-type',
38
+ id: '303bf5d8-1201-4fb9-8157-ff6efb8c04b4',
39
+ },
40
+ masterData: {
41
+ staged: {
42
+ name: {},
43
+ categories: [],
44
+ slug: {},
45
+ masterVariant: {
46
+ id: 1,
47
+ sku: '1',
48
+ },
49
+ variants: [],
50
+ searchKeywords: {},
51
+ },
52
+ current: {
53
+ name: {},
54
+ slug: {},
55
+ categories: [],
56
+ masterVariant: {
57
+ id: 1,
58
+ availability: {},
59
+ },
60
+ variants: [
61
+ {
62
+ id: 2,
63
+ sku: '22241940260',
64
+ },
65
+ ],
66
+ searchKeywords: {},
67
+ },
68
+ hasStagedChanges: true,
69
+ published: true,
70
+ },
71
+ }
72
+
73
+ describe('Shopping List', () => {
74
+ const ctMock = new CommercetoolsMock({
75
+ defaultProjectKey: 'dummy',
76
+ })
77
+
78
+ beforeEach(() => {
79
+ ctMock.project().add('product', product)
80
+ ctMock.project().add('shopping-list', shoppingList)
81
+ })
82
+
83
+ test('Adds variant ID on lineItems when creating', async () => {
84
+ const draft: ShoppingListDraft = {
85
+ name: {},
86
+ lineItems: [{ sku: '22241940260' }],
87
+ }
88
+ const response = await supertest(ctMock.app)
89
+ .post('/dummy/shopping-lists')
90
+ .send(draft)
91
+
92
+ expect(response.status).toBe(201)
93
+ expect(response.body.lineItems[0].variantId).toBe(2)
94
+ })
95
+
96
+ test('Expands variant on lineItems', async () => {
97
+ const response = await supertest(ctMock.app)
98
+ .get(`/dummy/shopping-lists/${shoppingList.id}`)
99
+ .query({ expand: 'lineItems[*].variant' })
100
+
101
+ expect(response.status).toBe(200)
102
+ expect(response.body.lineItems[0].variant).toEqual({
103
+ id: 2,
104
+ sku: '22241940260',
105
+ })
106
+ })
107
+ })
108
+
109
+ describe('Shopping List Update Actions', () => {
110
+ const ctMock = new CommercetoolsMock({
111
+ defaultProjectKey: 'dummy',
112
+ })
113
+
114
+ beforeEach(() => {
115
+ ctMock.project().add('product', product)
116
+ ctMock.project().add('shopping-list', shoppingList)
117
+ })
118
+
119
+ afterEach(() => {
120
+ ctMock.clear()
121
+ })
122
+
123
+ test('addLineItem by productID & variantID', async () => {
124
+ ctMock.clear()
125
+ ctMock.project().add('product', product)
126
+ ctMock.project().add('shopping-list', { ...shoppingList, lineItems: [] })
127
+
128
+ const response = await supertest(ctMock.app)
129
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
130
+ .send({
131
+ version: 1,
132
+ actions: [
133
+ {
134
+ action: 'addLineItem',
135
+ productId: product.id,
136
+ variantId: product.masterData.current.variants[0].id,
137
+ },
138
+ ],
139
+ })
140
+ expect(response.status).toBe(200)
141
+ expect(response.body.version).toBe(2)
142
+ expect(response.body.lineItems).toHaveLength(1)
143
+ expect(response.body.lineItems[0].variantId).toEqual(2)
144
+ })
145
+
146
+ test('addLineItem by productID', async () => {
147
+ ctMock.clear()
148
+ ctMock.project().add('product', product)
149
+ ctMock.project().add('shopping-list', { ...shoppingList, lineItems: [] })
150
+
151
+ const response = await supertest(ctMock.app)
152
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
153
+ .send({
154
+ version: 1,
155
+ actions: [
156
+ {
157
+ action: 'addLineItem',
158
+ productId: product.id,
159
+ },
160
+ ],
161
+ })
162
+ expect(response.status).toBe(200)
163
+ expect(response.body.version).toBe(2)
164
+ expect(response.body.lineItems).toHaveLength(1)
165
+ expect(response.body.lineItems[0].variantId).toEqual(1)
166
+ })
167
+
168
+ test('addLineItem by sku', async () => {
169
+ ctMock.clear()
170
+ ctMock.project().add('product', product)
171
+ ctMock.project().add('shopping-list', { ...shoppingList, lineItems: [] })
172
+
173
+ const response = await supertest(ctMock.app)
174
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
175
+ .send({
176
+ version: 1,
177
+ actions: [
178
+ {
179
+ action: 'addLineItem',
180
+ sku: '22241940260',
181
+ },
182
+ ],
183
+ })
184
+ expect(response.status).toBe(200)
185
+ expect(response.body.version).toBe(2)
186
+ expect(response.body.lineItems).toHaveLength(1)
187
+ expect(response.body.lineItems[0].variantId).toEqual(2)
188
+ })
189
+
190
+ test('addLineItem increases quantity', async () => {
191
+ const response = await supertest(ctMock.app)
192
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
193
+ .send({
194
+ version: 1,
195
+ actions: [
196
+ {
197
+ action: 'addLineItem',
198
+ sku: '22241940260',
199
+ },
200
+ ],
201
+ })
202
+ expect(response.status).toBe(200)
203
+ expect(response.body.version).toBe(2)
204
+ expect(response.body.lineItems).toHaveLength(1)
205
+ expect(response.body.lineItems[0].variantId).toEqual(2)
206
+ expect(response.body.lineItems[0].quantity).toEqual(2)
207
+ })
208
+
209
+ test('addLineItem unknown product', async () => {
210
+ const response = await supertest(ctMock.app)
211
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
212
+ .send({
213
+ version: 1,
214
+ actions: [{ action: 'addLineItem', productId: '123', variantId: 1 }],
215
+ })
216
+ expect(response.status).toBe(400)
217
+ expect(response.body.message).toBe("A product with ID '123' not found.")
218
+ })
219
+
220
+ test('removeLineItem', async () => {
221
+ const response = await supertest(ctMock.app)
222
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
223
+ .send({
224
+ version: 1,
225
+ actions: [
226
+ {
227
+ action: 'removeLineItem',
228
+ lineItemId: shoppingList.lineItems[0].id,
229
+ },
230
+ ],
231
+ })
232
+ expect(response.status).toBe(200)
233
+ expect(response.body.version).toBe(2)
234
+ expect(response.body.lineItems).toHaveLength(0)
235
+ })
236
+
237
+ test('removeLineItem decreases quantity', async () => {
238
+ await supertest(ctMock.app)
239
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
240
+ .send({
241
+ version: 1,
242
+ actions: [
243
+ {
244
+ action: 'addLineItem',
245
+ sku: '22241940260',
246
+ },
247
+ ],
248
+ })
249
+
250
+ const response = await supertest(ctMock.app)
251
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
252
+ .send({
253
+ version: 2,
254
+ actions: [
255
+ {
256
+ action: 'removeLineItem',
257
+ lineItemId: shoppingList.lineItems[0].id,
258
+ quantity: 1,
259
+ },
260
+ ],
261
+ })
262
+ expect(response.status).toBe(200)
263
+ expect(response.body.version).toBe(3)
264
+ expect(response.body.lineItems).toHaveLength(1)
265
+ expect(response.body.lineItems[0].quantity).toBe(1)
266
+ })
267
+
268
+ test('changeLineItemQuantity sets quantity', async () => {
269
+ const response = await supertest(ctMock.app)
270
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
271
+ .send({
272
+ version: 1,
273
+ actions: [
274
+ {
275
+ action: 'changeLineItemQuantity',
276
+ lineItemId: shoppingList.lineItems[0].id,
277
+ quantity: 2,
278
+ },
279
+ ],
280
+ })
281
+ expect(response.status).toBe(200)
282
+ expect(response.body.version).toBe(2)
283
+ expect(response.body.lineItems.length).toBe(1)
284
+ expect(response.body.lineItems[0].quantity).toBe(2)
285
+ })
286
+
287
+ test('changeLineItemQuantity removes line item if quantity is 0', async () => {
288
+ const response = await supertest(ctMock.app)
289
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
290
+ .send({
291
+ version: 1,
292
+ actions: [
293
+ {
294
+ action: 'changeLineItemQuantity',
295
+ lineItemId: shoppingList.lineItems[0].id,
296
+ quantity: 0,
297
+ },
298
+ ],
299
+ })
300
+ expect(response.status).toBe(200)
301
+ expect(response.body.version).toBe(2)
302
+ expect(response.body.lineItems.length).toBe(0)
303
+ })
304
+
305
+ test('various setters', async () => {
306
+ const response = await supertest(ctMock.app)
307
+ .post(`/dummy/shopping-lists/${shoppingList.id}`)
308
+ .send({
309
+ version: 1,
310
+ actions: [
311
+ {
312
+ action: 'setKey',
313
+ key: 'new-key',
314
+ },
315
+ {
316
+ action: 'setSlug',
317
+ slug: 'new-slug',
318
+ },
319
+ {
320
+ action: 'changeName',
321
+ name: { en: 'new name' },
322
+ },
323
+ {
324
+ action: 'setDescription',
325
+ description: { en: 'new description' },
326
+ },
327
+ {
328
+ action: 'setCustomer',
329
+ customer: { typeId: 'customer', id: 'customer-id' },
330
+ },
331
+ { action: 'setStore', store: { typeId: 'store', key: 'store-key' } },
332
+ { action: 'setAnonymousId', anonymousId: 'new-anonymous-id' },
333
+ {
334
+ action: 'setDeleteDaysAfterLastModification',
335
+ deleteDaysAfterLastModification: 1,
336
+ },
337
+ ],
338
+ })
339
+ expect(response.status).toBe(200)
340
+ expect(response.body.key).toBe('new-key')
341
+ expect(response.body.slug).toBe('new-slug')
342
+ expect(response.body.name).toEqual({ en: 'new name' })
343
+ expect(response.body.description).toEqual({ en: 'new description' })
344
+ expect(response.body.customer).toEqual({
345
+ typeId: 'customer',
346
+ id: 'customer-id',
347
+ })
348
+ expect(response.body.store).toEqual({ typeId: 'store', key: 'store-key' })
349
+ expect(response.body.anonymousId).toEqual('new-anonymous-id')
350
+ expect(response.body.deleteDaysAfterLastModification).toEqual(1)
351
+ })
352
+ })
@@ -1,5 +1,7 @@
1
1
  import {
2
+ InvalidJsonInputError,
2
3
  ReferencedResourceNotFoundError,
4
+ ShoppingListLineItem,
3
5
  type AssociateRole,
4
6
  type AttributeGroup,
5
7
  type BusinessUnit,
@@ -7,9 +9,9 @@ import {
7
9
  type CartDiscount,
8
10
  type Category,
9
11
  type Channel,
12
+ type CustomObject,
10
13
  type Customer,
11
14
  type CustomerGroup,
12
- type CustomObject,
13
15
  type DiscountCode,
14
16
  type Extension,
15
17
  type InvalidInputError,
@@ -35,7 +37,6 @@ import {
35
37
  type TaxCategory,
36
38
  type Type,
37
39
  type Zone,
38
- InvalidJsonInputError,
39
40
  } from '@commercetools/platform-sdk'
40
41
  import assert from 'assert'
41
42
  import { CommercetoolsError } from '../exceptions.js'
@@ -408,6 +409,23 @@ export class InMemoryStorage extends AbstractStorage {
408
409
  private _resolveResource = (projectKey: string, obj: any, expand: string) => {
409
410
  const params = parseExpandClause(expand)
410
411
 
412
+ // 'lineItems[*].variant' on ShoppingList is an exception, these variants are not references
413
+ if (params.index === '*') {
414
+ const reference = obj[params.element]
415
+ if (
416
+ params.element === 'lineItems' &&
417
+ params.rest?.startsWith('variant') &&
418
+ reference.every(
419
+ (item: any) =>
420
+ item.variant === undefined && item.variantId !== undefined
421
+ )
422
+ ) {
423
+ reference.forEach((item: ShoppingListLineItem) => {
424
+ this._resolveShoppingListLineItemVariant(projectKey, item)
425
+ })
426
+ }
427
+ }
428
+
411
429
  if (!params.index) {
412
430
  const reference = obj[params.element]
413
431
  if (reference === undefined) {
@@ -456,4 +474,24 @@ export class InMemoryStorage extends AbstractStorage {
456
474
  }
457
475
  }
458
476
  }
477
+ private _resolveShoppingListLineItemVariant(
478
+ projectKey: string,
479
+ lineItem: ShoppingListLineItem
480
+ ) {
481
+ const product = this.getByResourceIdentifier(projectKey, {
482
+ typeId: 'product',
483
+ id: lineItem.productId,
484
+ }) as Product | undefined
485
+
486
+ if (!product) {
487
+ return
488
+ }
489
+
490
+ const variant = [
491
+ product.masterData.current.masterVariant,
492
+ ...product.masterData.current.variants,
493
+ ].find((e) => e.id === lineItem.variantId)
494
+ // @ts-ignore
495
+ lineItem.variant = variant
496
+ }
459
497
  }