@labdigital/commercetools-mock 2.14.1 → 2.15.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.
@@ -3,7 +3,11 @@ import { CustomerRepository } from '../repositories/customer.js'
3
3
  import { getRepositoryContext } from '../repositories/helpers.js'
4
4
  import AbstractService from './abstract.js'
5
5
  import { hashPassword } from '../lib/password.js'
6
- import { Customer, Update } from '@commercetools/platform-sdk'
6
+ import {
7
+ Customer,
8
+ Update,
9
+ InvalidCurrentPasswordError,
10
+ } from '@commercetools/platform-sdk'
7
11
 
8
12
  export class MyCustomerService extends AbstractService {
9
13
  public repository: CustomerRepository
@@ -93,9 +97,9 @@ export class MyCustomerService extends AbstractService {
93
97
  message: 'Account with the given credentials not found.',
94
98
  errors: [
95
99
  {
96
- code: 'InvalidCredentials',
100
+ code: 'InvalidCurrentPassword',
97
101
  message: 'Account with the given credentials not found.',
98
- },
102
+ } as InvalidCurrentPasswordError,
99
103
  ],
100
104
  })
101
105
  }
@@ -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
  }