@reactionary/source 0.0.37 → 0.0.39

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.
Files changed (93) hide show
  1. package/.env-template +10 -0
  2. package/.github/workflows/pull-request.yml +5 -3
  3. package/core/package.json +1 -2
  4. package/core/src/client/client.ts +4 -2
  5. package/core/src/index.ts +3 -9
  6. package/core/src/providers/analytics.provider.ts +6 -1
  7. package/core/src/providers/base.provider.ts +33 -3
  8. package/core/src/providers/cart.provider.ts +7 -1
  9. package/core/src/providers/category.provider.ts +91 -0
  10. package/core/src/providers/identity.provider.ts +5 -1
  11. package/core/src/providers/inventory.provider.ts +4 -0
  12. package/core/src/providers/price.provider.ts +54 -0
  13. package/core/src/providers/product.provider.ts +4 -0
  14. package/core/src/providers/search.provider.ts +6 -0
  15. package/core/src/schemas/capabilities.schema.ts +3 -2
  16. package/core/src/schemas/models/base.model.ts +42 -1
  17. package/core/src/schemas/models/cart.model.ts +27 -3
  18. package/core/src/schemas/models/category.model.ts +23 -0
  19. package/core/src/schemas/models/identifiers.model.ts +29 -1
  20. package/core/src/schemas/models/inventory.model.ts +6 -2
  21. package/core/src/schemas/models/price.model.ts +11 -3
  22. package/core/src/schemas/models/search.model.ts +4 -2
  23. package/core/src/schemas/queries/category.query.ts +32 -0
  24. package/core/src/schemas/queries/index.ts +9 -0
  25. package/core/src/schemas/queries/inventory.query.ts +18 -3
  26. package/core/src/schemas/session.schema.ts +13 -2
  27. package/examples/next/.swcrc +30 -0
  28. package/examples/next/eslint.config.mjs +21 -0
  29. package/examples/next/index.d.ts +6 -0
  30. package/examples/next/next-env.d.ts +5 -0
  31. package/examples/next/next.config.js +20 -0
  32. package/examples/next/project.json +9 -0
  33. package/examples/next/public/.gitkeep +0 -0
  34. package/examples/next/public/favicon.ico +0 -0
  35. package/examples/next/src/app/global.css +0 -0
  36. package/examples/next/src/app/layout.tsx +18 -0
  37. package/examples/next/src/app/page.module.scss +2 -0
  38. package/examples/next/src/app/page.tsx +51 -0
  39. package/examples/next/src/instrumentation.ts +9 -0
  40. package/examples/next/tsconfig.json +44 -0
  41. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +0 -1
  42. package/examples/node/src/basic/basic-node-setup.spec.ts +0 -1
  43. package/otel/README.md +152 -172
  44. package/otel/package.json +0 -1
  45. package/otel/src/index.ts +15 -5
  46. package/otel/src/metrics.ts +3 -3
  47. package/otel/src/test/otel.spec.ts +8 -0
  48. package/otel/src/trace-decorator.ts +87 -108
  49. package/otel/src/tracer.ts +3 -3
  50. package/package.json +2 -1
  51. package/providers/commercetools/package.json +1 -0
  52. package/providers/commercetools/src/core/initialize.ts +7 -3
  53. package/providers/commercetools/src/providers/cart.provider.ts +84 -8
  54. package/providers/commercetools/src/providers/category.provider.ts +244 -0
  55. package/providers/commercetools/src/providers/index.ts +7 -0
  56. package/providers/commercetools/src/providers/inventory.provider.ts +31 -14
  57. package/providers/commercetools/src/providers/price.provider.ts +74 -18
  58. package/providers/commercetools/src/providers/product.provider.ts +19 -15
  59. package/providers/commercetools/src/providers/search.provider.ts +9 -7
  60. package/providers/commercetools/src/schema/capabilities.schema.ts +2 -1
  61. package/providers/commercetools/src/schema/configuration.schema.ts +1 -1
  62. package/providers/commercetools/src/test/cart.provider.spec.ts +119 -0
  63. package/providers/commercetools/src/test/category.provider.spec.ts +180 -0
  64. package/providers/commercetools/src/test/price.provider.spec.ts +80 -0
  65. package/providers/commercetools/src/test/product.provider.spec.ts +29 -14
  66. package/providers/commercetools/src/test/search.provider.spec.ts +51 -9
  67. package/providers/commercetools/src/test/test-utils.ts +35 -0
  68. package/providers/commercetools/tsconfig.lib.json +1 -1
  69. package/providers/fake/jest.config.ts +10 -0
  70. package/providers/fake/src/core/initialize.ts +15 -1
  71. package/providers/fake/src/index.ts +2 -9
  72. package/providers/fake/src/providers/cart.provider.ts +74 -15
  73. package/providers/fake/src/providers/category.provider.ts +152 -0
  74. package/providers/fake/src/providers/index.ts +8 -0
  75. package/providers/fake/src/providers/inventory.provider.ts +23 -9
  76. package/providers/fake/src/providers/price.provider.ts +46 -6
  77. package/providers/fake/src/providers/search.provider.ts +13 -4
  78. package/providers/fake/src/schema/capabilities.schema.ts +4 -2
  79. package/providers/fake/src/schema/configuration.schema.ts +5 -0
  80. package/providers/fake/src/test/cart.provider.spec.ts +126 -0
  81. package/providers/fake/src/test/category.provider.spec.ts +134 -0
  82. package/providers/fake/src/test/price.provider.spec.ts +80 -0
  83. package/providers/fake/src/test/test-utils.ts +42 -0
  84. package/providers/fake/tsconfig.json +4 -0
  85. package/providers/fake/tsconfig.lib.json +3 -1
  86. package/providers/fake/tsconfig.spec.json +16 -0
  87. package/trpc/package.json +1 -2
  88. package/trpc/src/client.ts +1 -3
  89. package/trpc/src/integration.spec.ts +16 -10
  90. package/trpc/src/transparent-client.spec.ts +23 -17
  91. package/tsconfig.base.json +2 -0
  92. package/core/src/decorators/trpc.decorators.ts +0 -144
  93. package/otel/src/sdk.ts +0 -57
@@ -10,16 +10,22 @@ import {
10
10
  } from '@reactionary/core';
11
11
  import z from 'zod';
12
12
  import { FakeConfiguration } from '../schema/configuration.schema';
13
+ import { Faker, en, base } from '@faker-js/faker';
13
14
 
14
15
  export class FakeCartProvider<
15
16
  T extends Cart = Cart
16
17
  > extends CartProvider<T> {
17
18
  protected config: FakeConfiguration;
18
19
  private carts: Map<string, T> = new Map();
20
+ private generator: Faker;
21
+
19
22
 
20
23
  constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: Cache) {
21
24
  super(schema, cache);
22
-
25
+ this.generator = new Faker({
26
+ locale: [en, base],
27
+ seed: config.seeds.product
28
+ });
23
29
  this.config = config;
24
30
  }
25
31
 
@@ -28,7 +34,16 @@ export class FakeCartProvider<
28
34
  _session: Session
29
35
  ): Promise<T> {
30
36
  const cartId = payload.cart.key;
31
-
37
+
38
+ if (payload.cart.key === '') {
39
+ const result = this.newModel();
40
+ result.meta = {
41
+ cache: { hit: false, key: 'empty' },
42
+ placeholder: true
43
+ };
44
+ return this.assert(result);
45
+ }
46
+
32
47
  if (!this.carts.has(cartId)) {
33
48
  const model = this.newModel();
34
49
  Object.assign(model, {
@@ -44,7 +59,7 @@ export class FakeCartProvider<
44
59
  });
45
60
  this.carts.set(cartId, this.assert(model));
46
61
  }
47
-
62
+
48
63
  const cart = this.carts.get(cartId);
49
64
  if (!cart) {
50
65
  throw new Error(`Cart with id ${cartId} not found`);
@@ -56,22 +71,46 @@ export class FakeCartProvider<
56
71
  payload: CartMutationItemAdd,
57
72
  session: Session
58
73
  ): Promise<T> {
59
- const cart = await this.getById({ cart: payload.cart }, session);
60
-
74
+
75
+ const cartId = payload.cart.key || `cart-${this.generator.string.uuid()}`;
76
+ const cart = await this.getById({ cart: { key: cartId } }, session);
77
+
61
78
  const existingItemIndex = cart.items.findIndex(
62
79
  item => item.product.key === payload.product.key
63
80
  );
64
-
81
+
65
82
  if (existingItemIndex >= 0) {
66
83
  cart.items[existingItemIndex].quantity += payload.quantity;
67
84
  } else {
85
+ const price = this.generator.number.int({ min: 100, max: 100000 }) / 100;
86
+
68
87
  cart.items.push({
69
88
  identifier: { key: `item-${Date.now()}` },
70
89
  product: payload.product,
71
90
  quantity: payload.quantity,
91
+ price: {
92
+ unitPrice: {
93
+ value: price,
94
+ currency: session.languageContext.currencyCode,
95
+ },
96
+ totalPrice: {
97
+ value: 0, // Will be calculated below
98
+ currency: session.languageContext.currencyCode,
99
+ },
100
+ totalDiscount: {
101
+ value: 0,
102
+ currency: session.languageContext.currencyCode,
103
+ },
104
+ unitDiscount: {
105
+ value: 0,
106
+ currency: session.languageContext.currencyCode,
107
+ },
108
+ },
72
109
  });
73
110
  }
74
-
111
+
112
+ this.recalculateCart(cart);
113
+
75
114
  return this.assert(cart);
76
115
  }
77
116
 
@@ -79,12 +118,13 @@ export class FakeCartProvider<
79
118
  payload: CartMutationItemRemove,
80
119
  session: Session
81
120
  ): Promise<T> {
82
- const cart = await this.getById({ cart: payload.cart }, session);
83
-
121
+ const cartId = payload.cart.key || `cart-${this.generator.string.uuid()}`;
122
+ const cart = await this.getById({ cart: { key: cartId } }, session);
123
+
84
124
  cart.items = cart.items.filter(
85
125
  item => item.identifier.key !== payload.item.key
86
126
  );
87
-
127
+ this.recalculateCart(cart);
88
128
  return this.assert(cart);
89
129
  }
90
130
 
@@ -92,16 +132,35 @@ export class FakeCartProvider<
92
132
  payload: CartMutationItemQuantityChange,
93
133
  session: Session
94
134
  ): Promise<T> {
95
- const cart = await this.getById({ cart: payload.cart }, session);
96
-
135
+ const cartId = payload.cart.key || `cart-${this.generator.string.uuid()}`;
136
+ const cart = await this.getById({ cart: { key: cartId } }, session);
137
+
97
138
  const item = cart.items.find(
98
139
  item => item.identifier.key === payload.item.key
99
140
  );
100
-
141
+
101
142
  if (item) {
102
143
  item.quantity = payload.quantity;
103
144
  }
104
-
145
+ this.recalculateCart(cart);
105
146
  return this.assert(cart);
106
147
  }
107
- }
148
+
149
+
150
+
151
+ protected recalculateCart(cart: T) {
152
+ cart.items.forEach(item => {
153
+ item.price.totalPrice.value = item.price.unitPrice.value * item.quantity;
154
+ });
155
+
156
+ cart.price.totalProductPrice = {
157
+ value: cart.items.reduce((sum, item) => sum + item.price.totalPrice.value, 0),
158
+ currency: cart.items[0]?.price.unitPrice.currency || 'USD',
159
+ }
160
+
161
+ cart.price.grandTotal = {
162
+ value: cart.items.reduce((sum, item) => sum + item.price.totalPrice.value, 0),
163
+ currency: cart.items[0]?.price.unitPrice.currency || 'USD',
164
+ };
165
+ }
166
+ }
@@ -0,0 +1,152 @@
1
+ import { Category, CategoryProvider, CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories, Session } from "@reactionary/core";
2
+ import { FakeConfiguration } from "../schema/configuration.schema";
3
+ import { Cache as ReactionaryCache } from "@reactionary/core";
4
+ import z from "zod";
5
+ import { Faker, en, base } from '@faker-js/faker';
6
+ export class FakeCategoryProvider<
7
+ T extends Category = Category
8
+ > extends CategoryProvider<T> {
9
+
10
+ protected config: FakeConfiguration;
11
+
12
+ protected topCategories = new Array<T>();
13
+ protected childCategories = new Map<string, Array<T>>();
14
+ protected allCategories = new Map<string, T>();
15
+
16
+ protected categoryGenerator: Faker;
17
+
18
+ protected generateFakeCategory(parent: Category | undefined, index: number): T {
19
+
20
+ let name: string;
21
+ if (!parent) {
22
+ name = this.categoryGenerator.commerce.department();
23
+ } else {
24
+ name = `${parent.name}-${index}`;
25
+ }
26
+
27
+ const category: T = this.newModel();
28
+ category.identifier = { key: name.toLowerCase().replace(/\s+/g, '-') };
29
+ category.name = name;
30
+ category.text = this.categoryGenerator.lorem.sentences(3);
31
+ category.slug = category.identifier.key + '-slug';
32
+ if (parent) {
33
+ category.parentCategory = parent.identifier;
34
+ }
35
+ this.allCategories.set(category.identifier.key, category);
36
+ return category;
37
+ }
38
+
39
+
40
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactionaryCache) {
41
+ super(schema, cache);
42
+ this.config = config;
43
+ this.categoryGenerator = new Faker({
44
+ seed: this.config.seeds.category,
45
+ locale: [en, base],
46
+ });
47
+
48
+ // Generate some top-level categories
49
+ for (let i = 0; i < 6; i++) {
50
+ const category: T = this.generateFakeCategory(undefined, i);
51
+ this.topCategories.push(category);
52
+ }
53
+
54
+ // Generate two levels of child categories
55
+ this.topCategories.forEach((parentCategory) => {
56
+ const children = new Array<T>();
57
+ for (let j = 0; j < 5; j++) {
58
+ const childCategory: T = this.generateFakeCategory(parentCategory, j);
59
+ children.push(childCategory);
60
+
61
+
62
+ const subCategoryChildren = new Array<T>();
63
+ for(let k = 0; k < 5; k++) {
64
+ const subChildCategory: T = this.generateFakeCategory(childCategory, k);
65
+ subCategoryChildren.push(subChildCategory);
66
+ }
67
+ this.childCategories.set(childCategory.identifier.key, subCategoryChildren);
68
+ }
69
+ this.childCategories.set(parentCategory.identifier.key, children);
70
+ });
71
+ }
72
+
73
+
74
+
75
+ public override async getById(payload: CategoryQueryById, session: Session): Promise<T> {
76
+ const category = this.allCategories.get(payload.id.key);
77
+ if(!category) {
78
+ const dummyCategory = this.newModel();
79
+ dummyCategory.meta.placeholder = true;
80
+ dummyCategory.identifier = { key: payload.id.key };
81
+ return dummyCategory;
82
+ }
83
+ return category;
84
+ }
85
+ public override getBySlug(payload: CategoryQueryBySlug, session: Session): Promise<T | null> {
86
+ for(const p of this.allCategories.values()) {
87
+ if(p.slug === payload.slug) {
88
+ return Promise.resolve(p as T);
89
+ }
90
+ }
91
+ return Promise.resolve(null);
92
+ }
93
+
94
+ public override getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, session: Session): Promise<T[]> {
95
+ const path = new Array<T>();
96
+ let category = this.allCategories.get(payload.id.key);
97
+ path.push(category as T);
98
+ while(category?.parentCategory) {
99
+ category = this.allCategories.get(category.parentCategory.key);
100
+ if(category) {
101
+ path.unshift(category as T);
102
+ }
103
+ }
104
+ return Promise.resolve(path);
105
+ }
106
+
107
+ public override async findChildCategories(payload: CategoryQueryForChildCategories, session: Session): Promise<ReturnType<typeof this.parsePaginatedResult>> {
108
+ const children = this.childCategories.get(payload.parentId.key);
109
+ const page = children?.slice((payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize, payload.paginationOptions.pageNumber * payload.paginationOptions.pageSize);
110
+
111
+
112
+ const res = {
113
+ meta: {
114
+ placeholder: false,
115
+ cache: {
116
+ hit: false,
117
+ key: 'child-categories-' + payload.parentId.key + '-' + payload.paginationOptions.pageNumber + '-' + payload.paginationOptions.pageSize,
118
+ },
119
+ },
120
+ items: page ? page as T[] : [],
121
+ totalCount: children ? children.length : 0,
122
+ pageNumber: payload.paginationOptions.pageNumber,
123
+ pageSize: payload.paginationOptions.pageSize,
124
+ totalPages: children ? Math.ceil(children.length / payload.paginationOptions.pageSize) : 1,
125
+ };
126
+
127
+ return Promise.resolve(res);
128
+ }
129
+ public override findTopCategories(payload: CategoryQueryForTopCategories, session: Session): Promise<ReturnType<typeof this.parsePaginatedResult>> {
130
+ const children = this.topCategories;
131
+ const page = children?.slice((payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize, payload.paginationOptions.pageNumber * payload.paginationOptions.pageSize);
132
+
133
+
134
+ const res = {
135
+ meta: {
136
+ placeholder: false,
137
+ cache: {
138
+ hit: false,
139
+ key: 'top' + '-' + payload.paginationOptions.pageNumber + '-' + payload.paginationOptions.pageSize,
140
+ },
141
+ },
142
+ items: page ? page as T[] : [],
143
+ totalCount: children ? children.length : 0,
144
+ pageNumber: payload.paginationOptions.pageNumber,
145
+ pageSize: payload.paginationOptions.pageSize,
146
+ totalPages: children ? Math.ceil(children.length / payload.paginationOptions.pageSize) : 1,
147
+ };
148
+
149
+ return Promise.resolve(res);
150
+ }
151
+
152
+ }
@@ -0,0 +1,8 @@
1
+ export * from './analytics.provider';
2
+ export * from './cart.provider';
3
+ export * from './category.provider';
4
+ export * from './identity.provider';
5
+ export * from './inventory.provider';
6
+ export * from './price.provider';
7
+ export * from './product.provider';
8
+ export * from './search.provider';
@@ -26,29 +26,43 @@ export class FakeInventoryProvider<
26
26
  ): Promise<T> {
27
27
  // Generate a simple hash from the SKU string for seeding
28
28
  let hash = 0;
29
- const skuString = payload.sku;
29
+ const skuString = payload.sku.key;
30
30
  for (let i = 0; i < skuString.length; i++) {
31
31
  hash = ((hash << 5) - hash) + skuString.charCodeAt(i);
32
32
  hash = hash & hash; // Convert to 32bit integer
33
33
  }
34
-
34
+
35
35
  const generator = new Faker({
36
36
  seed: hash || 42,
37
37
  locale: [en, base],
38
38
  });
39
39
 
40
40
  const model = this.newModel();
41
- Object.assign(model, {
42
- quantity: generator.number.int({ min: 0, max: 100 }),
43
- meta: {
41
+
42
+ model.identifier = {
43
+ sku: { key: skuString},
44
+ channelId: {
45
+ key: 'online'
46
+ },
47
+ };
48
+ model.sku = skuString;
49
+
50
+ model.quantity = generator.number.int({ min: 0, max: 100 });
51
+ if (model.quantity > 0 ) {
52
+ model.status = 'inStock';
53
+ } else {
54
+ model.status = 'outOfStock';
55
+ }
56
+
57
+
58
+ model.meta = {
44
59
  cache: {
45
60
  hit: false,
46
- key: payload.sku,
61
+ key: this.generateCacheKeySingle(model.identifier, _session)
47
62
  },
48
63
  placeholder: false,
49
- },
50
- });
64
+ };
51
65
 
52
66
  return this.assert(model);
53
67
  }
54
- }
68
+ }
@@ -20,10 +20,22 @@ export class FakePriceProvider<
20
20
  this.config = config;
21
21
  }
22
22
 
23
+ public override async getBySKUs(payload: PriceQueryBySku[], session: Session): Promise<T[]> {
24
+
25
+ const promises = payload.map(p => this.getBySKU(p, session));
26
+ const result = await Promise.all(promises);
27
+ return result;
28
+ }
29
+
23
30
  public override async getBySKU(
24
31
  payload: PriceQueryBySku,
25
32
  _session: Session
26
33
  ): Promise<T> {
34
+
35
+ if (payload.sku.key === 'unknown-sku') {
36
+ return this.getEmptyPriceResult(payload.sku.key, _session.languageContext.currencyCode);
37
+ }
38
+
27
39
  // Generate a simple hash from the SKU key string for seeding
28
40
  let hash = 0;
29
41
  const skuString = payload.sku.key;
@@ -31,30 +43,58 @@ export class FakePriceProvider<
31
43
  hash = ((hash << 5) - hash) + skuString.charCodeAt(i);
32
44
  hash = hash & hash; // Convert to 32bit integer
33
45
  }
34
-
46
+
35
47
  const generator = new Faker({
36
48
  seed: hash || 42,
37
49
  locale: [en, base],
38
50
  });
39
51
 
52
+
40
53
  const model = this.newModel();
41
54
  Object.assign(model, {
42
55
  identifier: {
43
56
  sku: payload.sku,
44
57
  },
45
- value: {
46
- cents: generator.number.int({ min: 100, max: 100000 }),
47
- currency: 'USD',
58
+ unitPrice: {
59
+ value: generator.number.int({ min: 300, max: 100000 }) / 100,
60
+ currency: _session.languageContext.currencyCode,
48
61
  },
49
62
  meta: {
50
63
  cache: {
51
64
  hit: false,
52
- key: payload.sku,
65
+ key: payload.sku.key,
53
66
  },
54
67
  placeholder: false,
55
68
  },
56
69
  });
57
70
 
71
+ if (skuString.includes('with-tiers')) {
72
+ const unitPrice = model.unitPrice?.value || 0;
73
+ // Ensure tiered prices are less than the unit price
74
+ const tier1Price = unitPrice * 0.8;
75
+ const tier2Price = tier1Price * 0.8;
76
+ model.tieredPrices = [
77
+ {
78
+ minimumQuantity: generator.number.int({ min: 2, max: 5 }),
79
+ price: {
80
+ value: tier1Price,
81
+ currency: _session.languageContext.currencyCode,
82
+ }
83
+ },
84
+ {
85
+ minimumQuantity: generator.number.int({ min: 6, max: 10 }),
86
+ price: {
87
+ value: tier2Price,
88
+ currency: _session.languageContext.currencyCode,
89
+ }
90
+ }
91
+ ];
92
+ } else {
93
+ model.tieredPrices = [];
94
+ }
95
+
96
+
97
+
58
98
  return this.assert(model);
59
99
  }
60
- }
100
+ }
@@ -1,28 +1,29 @@
1
1
  import {
2
2
  SearchProvider,
3
- SearchQueryByTerm,
4
3
  SearchResult,
5
4
  SearchResultFacet,
6
5
  SearchResultProduct,
7
- Session,
8
- Cache as ReactinaryCache,
6
+ Cache as ReactionaryCache,
9
7
  } from '@reactionary/core';
8
+ import type { SearchQueryByTerm, Session } from '@reactionary/core';
10
9
  import z from 'zod';
11
10
  import { FakeConfiguration } from '../schema/configuration.schema';
12
11
  import { Faker, en, base } from '@faker-js/faker';
13
12
  import { jitter } from '../utilities/jitter';
13
+ import { traced } from '@reactionary/otel';
14
14
 
15
15
  export class FakeSearchProvider<
16
16
  T extends SearchResult = SearchResult
17
17
  > extends SearchProvider<T> {
18
18
  protected config: FakeConfiguration;
19
19
 
20
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactinaryCache) {
20
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactionaryCache) {
21
21
  super(schema, cache);
22
22
 
23
23
  this.config = config;
24
24
  }
25
25
 
26
+ @traced()
26
27
  public override async queryByTerm(
27
28
  payload: SearchQueryByTerm,
28
29
  _session: Session
@@ -119,6 +120,14 @@ export class FakeSearchProvider<
119
120
  },
120
121
  } satisfies SearchResult;
121
122
 
123
+ const foo = this.childFunction();
124
+
122
125
  return this.schema.parse(result);
123
126
  }
127
+
128
+ @traced()
129
+ protected childFunction() {
130
+ const foo = 42;
131
+ return foo;
132
+ }
124
133
  }
@@ -4,7 +4,9 @@ import { z } from 'zod';
4
4
  export const FakeCapabilitiesSchema = CapabilitiesSchema.pick({
5
5
  product: true,
6
6
  search: true,
7
- identity: true
7
+ identity: true,
8
+ category: true,
9
+ cart: true,
8
10
  }).partial();
9
11
 
10
- export type FakeCapabilities = z.infer<typeof FakeCapabilitiesSchema>;
12
+ export type FakeCapabilities = z.infer<typeof FakeCapabilitiesSchema>;
@@ -10,6 +10,11 @@ export const FakeConfigurationSchema = z.looseInterface({
10
10
  mean: 0,
11
11
  deviation: 0,
12
12
  }),
13
+ seeds: z.looseInterface({
14
+ product: z.number().min(0).max(10000).default(1),
15
+ search: z.number().min(0).max(10000).default(1),
16
+ category: z.number().min(0).max(10000).default(1),
17
+ }),
13
18
  });
14
19
 
15
20
  export type FakeConfiguration = z.infer<typeof FakeConfigurationSchema>;
@@ -0,0 +1,126 @@
1
+ import 'dotenv/config';
2
+ import { CartSchema, CategorySchema, IdentitySchema, NoOpCache, ProductSchema, Session } from '@reactionary/core';
3
+ import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
4
+ import { FakeCartProvider } from '../providers/cart.provider';
5
+ import { FakeIdentityProvider } from '../providers';
6
+
7
+
8
+ const testData = {
9
+ skuWithoutTiers: 'SGB-01',
10
+ skuWithTiers: 'GMCT-01-with-tiers'
11
+ }
12
+
13
+ describe('Fake Cart Provider', () => {
14
+ let provider: FakeCartProvider;
15
+ let identityProvider: FakeIdentityProvider;
16
+ let session: Session;
17
+
18
+ beforeAll( () => {
19
+ provider = new FakeCartProvider(getFakerTestConfiguration(), CartSchema, new NoOpCache());
20
+ identityProvider = new FakeIdentityProvider(getFakerTestConfiguration(), IdentitySchema, new NoOpCache());
21
+ });
22
+
23
+ beforeEach( () => {
24
+ session = createAnonymousTestSession()
25
+ });
26
+
27
+ describe('anonymous sessions', () => {
28
+ it('should be able to get an empty cart', async () => {
29
+ const cart = await provider.getById({
30
+ cart: { key: '' },
31
+ }, session);
32
+
33
+ expect(cart.identifier.key).toBeFalsy();
34
+ expect(cart.items.length).toBe(0);
35
+ expect(cart.meta?.placeholder).toBe(true);
36
+
37
+ });
38
+
39
+ it('should be able to add an item to a cart', async () => {
40
+ const cart = await provider.add({
41
+ cart: { key: '' },
42
+ product: {
43
+ key: testData.skuWithoutTiers,
44
+ },
45
+ quantity: 1
46
+ }, session);
47
+
48
+ expect(cart.identifier.key).toBeDefined();
49
+ expect(cart.items.length).toBe(1);
50
+ expect(cart.items[0].product.key).toBe(testData.skuWithoutTiers);
51
+ expect(cart.items[0].quantity).toBe(1);
52
+
53
+ expect(cart.items[0].price.totalPrice.value).toBeGreaterThan(0);
54
+ expect(cart.items[0].price.totalPrice.currency).toBe(session.languageContext.currencyCode);
55
+
56
+ expect(cart.price.grandTotal.value).toBeGreaterThan(0);
57
+ expect(cart.price.grandTotal.currency).toBe(session.languageContext.currencyCode);
58
+
59
+ expect(cart.price.grandTotal.value).toBe(cart.items[0].price.totalPrice.value);
60
+
61
+
62
+ expect(cart.meta?.placeholder).toBeFalsy();
63
+
64
+ });
65
+
66
+
67
+ it('should be able to change quantity of an item in a cart', async () => {
68
+
69
+ const cart = await provider.add({
70
+ cart: { key: '' },
71
+ product: {
72
+ key: testData.skuWithoutTiers,
73
+ },
74
+ quantity: 1
75
+ }, session);
76
+
77
+ const updatedCart = await provider.changeQuantity({
78
+ cart: cart.identifier,
79
+ item: cart.items[0].identifier,
80
+ quantity: 3
81
+ }, session);
82
+
83
+ expect(updatedCart.identifier.key).toBe(cart.identifier.key);
84
+ expect(updatedCart.items.length).toBe(1);
85
+ expect(updatedCart.items[0].product.key).toBe(testData.skuWithoutTiers);
86
+ expect(updatedCart.items[0].quantity).toBe(3);
87
+
88
+ expect(updatedCart.items[0].price.totalPrice.value).toBe(cart.items[0].price.totalPrice.value * 3);
89
+ expect(updatedCart.items[0].price.unitPrice.value).toBe(cart.items[0].price.unitPrice.value);
90
+
91
+
92
+ });
93
+
94
+ it('should be able to remove an item from a cart', async () => {
95
+
96
+ const cart = await provider.add({
97
+ cart: { key: '' },
98
+ product: {
99
+ key: testData.skuWithoutTiers,
100
+ },
101
+ quantity: 1
102
+ }, session);
103
+
104
+ const updatedCart = await provider.remove({
105
+ cart: cart.identifier,
106
+ item: cart.items[0].identifier,
107
+ }, session);
108
+ expect(updatedCart.identifier.key).toBe(cart.identifier.key);
109
+ expect(updatedCart.items.length).toBe(0);
110
+ });
111
+
112
+ /**
113
+ it('should be able to create a cart for an anonymous user, then login and merge the cart', async () => {
114
+ });
115
+
116
+ it('should be able to create a cart for an anonymous user, then register and merge the cart', async () => {
117
+ });
118
+
119
+ it('should be able to clear the cart', async () => { });
120
+
121
+ it('should be able to check out the cart', async () => { });
122
+ */
123
+
124
+ });
125
+
126
+ });