@adobe/spacecat-shared-tokowaka-client 1.8.0 → 1.9.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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [@adobe/spacecat-shared-tokowaka-client-v1.9.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.8.0...@adobe/spacecat-shared-tokowaka-client-v1.9.0) (2026-02-24)
2
+
3
+ ### Features
4
+
5
+ * **AGENTCOM-266:** add commerce page enrichment mapper for tokowaka edge deployment ([#1369](https://github.com/adobe/spacecat-shared/issues/1369)) ([357f2f4](https://github.com/adobe/spacecat-shared/commit/357f2f44cd8da19272a7638b55870873388840c9))
6
+
1
7
  ## [@adobe/spacecat-shared-tokowaka-client-v1.8.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.7.8...@adobe/spacecat-shared-tokowaka-client-v1.8.0) (2026-02-18)
2
8
 
3
9
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-tokowaka-client",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Tokowaka Client for SpaceCat - Edge optimization config management",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,128 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { hasText } from '@adobe/spacecat-shared-utils';
14
+ import { TARGET_USER_AGENTS_CATEGORIES } from '../constants.js';
15
+ import BaseOpportunityMapper from './base-mapper.js';
16
+
17
+ const SCHEMA_ORG_DIRECT_FIELDS = {
18
+ sku: 'sku',
19
+ name: 'name',
20
+ material: 'material',
21
+ category: 'category',
22
+ color_family: 'color',
23
+ 'pdp.description_plain': 'description',
24
+ };
25
+
26
+ const EXCLUDED_FIELDS = new Set([
27
+ 'rationale',
28
+ ]);
29
+
30
+ function toJsonLd(enrichmentData) {
31
+ const jsonLd = {
32
+ '@context': 'https://schema.org',
33
+ '@type': 'Product',
34
+ };
35
+
36
+ const additionalProperties = [];
37
+
38
+ for (const [key, value] of Object.entries(enrichmentData)) {
39
+ if (!EXCLUDED_FIELDS.has(key) && value != null) {
40
+ if (key === 'brand' && typeof value === 'string') {
41
+ jsonLd.brand = { '@type': 'Brand', name: value };
42
+ } else if (key === 'facts.facets.category_path' && Array.isArray(value)) {
43
+ jsonLd.category = value.join(' > ');
44
+ } else if (SCHEMA_ORG_DIRECT_FIELDS[key]) {
45
+ jsonLd[SCHEMA_ORG_DIRECT_FIELDS[key]] = value;
46
+ } else {
47
+ additionalProperties.push({
48
+ '@type': 'PropertyValue',
49
+ name: key,
50
+ value,
51
+ });
52
+ }
53
+ }
54
+ }
55
+
56
+ if (additionalProperties.length > 0) {
57
+ jsonLd.additionalProperty = additionalProperties;
58
+ }
59
+
60
+ return jsonLd;
61
+ }
62
+
63
+ export default class CommercePageEnrichmentMapper extends BaseOpportunityMapper {
64
+ constructor(log) {
65
+ super(log);
66
+ this.opportunityType = 'commerce-product-page-enrichment';
67
+ this.prerenderRequired = true;
68
+ }
69
+
70
+ getOpportunityType() {
71
+ return this.opportunityType;
72
+ }
73
+
74
+ requiresPrerender() {
75
+ return this.prerenderRequired;
76
+ }
77
+
78
+ // eslint-disable-next-line class-methods-use-this
79
+ canDeploy(suggestion) {
80
+ const data = suggestion.getData();
81
+
82
+ if (!hasText(data?.patchValue)) {
83
+ return { eligible: false, reason: 'patchValue is required' };
84
+ }
85
+
86
+ if (!hasText(data?.url)) {
87
+ return { eligible: false, reason: 'url is required' };
88
+ }
89
+
90
+ try {
91
+ JSON.parse(data.patchValue);
92
+ } catch {
93
+ return { eligible: false, reason: 'patchValue must be valid JSON' };
94
+ }
95
+
96
+ return { eligible: true };
97
+ }
98
+
99
+ suggestionsToPatches(urlPath, suggestions, opportunityId) {
100
+ const patches = [];
101
+
102
+ suggestions.forEach((suggestion) => {
103
+ const eligibility = this.canDeploy(suggestion);
104
+ if (!eligibility.eligible) {
105
+ this.log.warn(`Commerce page enrichment suggestion ${suggestion.getId()} cannot be deployed: ${eligibility.reason}`);
106
+ return;
107
+ }
108
+
109
+ const data = suggestion.getData();
110
+ const enrichmentData = JSON.parse(data.patchValue);
111
+
112
+ const jsonLd = toJsonLd(enrichmentData);
113
+
114
+ patches.push({
115
+ ...this.createBasePatch(suggestion, opportunityId),
116
+ op: 'appendChild',
117
+ selector: 'head',
118
+ value: jsonLd,
119
+ valueFormat: 'json',
120
+ target: TARGET_USER_AGENTS_CATEGORIES.AI_BOTS,
121
+ tag: 'script',
122
+ attrs: { type: 'application/ld+json' },
123
+ });
124
+ });
125
+
126
+ return patches;
127
+ }
128
+ }
@@ -17,6 +17,7 @@ import ReadabilityMapper from './readability-mapper.js';
17
17
  import TocMapper from './toc-mapper.js';
18
18
  import GenericMapper from './generic-mapper.js';
19
19
  import PrerenderMapper from './prerender-mapper.js';
20
+ import CommercePageEnrichmentMapper from './commerce-page-enrichment-mapper.js';
20
21
  import SemanticValueVisibilityMapper from './semantic-value-visibility-mapper.js';
21
22
 
22
23
  /**
@@ -43,6 +44,7 @@ export default class MapperRegistry {
43
44
  TocMapper,
44
45
  GenericMapper,
45
46
  PrerenderMapper,
47
+ CommercePageEnrichmentMapper,
46
48
  SemanticValueVisibilityMapper,
47
49
  ];
48
50
 
@@ -0,0 +1,453 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ /* eslint-env mocha */
14
+
15
+ import { expect } from 'chai';
16
+ import sinon from 'sinon';
17
+ import CommercePageEnrichmentMapper from '../../src/mappers/commerce-page-enrichment-mapper.js';
18
+
19
+ describe('CommercePageEnrichmentMapper', () => {
20
+ let mapper;
21
+ let log;
22
+
23
+ beforeEach(() => {
24
+ log = {
25
+ debug: sinon.stub(),
26
+ info: sinon.stub(),
27
+ warn: sinon.stub(),
28
+ error: sinon.stub(),
29
+ };
30
+ mapper = new CommercePageEnrichmentMapper(log);
31
+ });
32
+
33
+ describe('getOpportunityType', () => {
34
+ it('should return commerce-product-page-enrichment', () => {
35
+ expect(mapper.getOpportunityType()).to.equal('commerce-product-page-enrichment');
36
+ });
37
+ });
38
+
39
+ describe('requiresPrerender', () => {
40
+ it('should return true', () => {
41
+ expect(mapper.requiresPrerender()).to.be.true;
42
+ });
43
+ });
44
+
45
+ describe('allowConfigsWithoutPatch', () => {
46
+ it('should return false', () => {
47
+ expect(mapper.allowConfigsWithoutPatch()).to.be.false;
48
+ });
49
+ });
50
+
51
+ describe('canDeploy', () => {
52
+ const validPatchValue = JSON.stringify({
53
+ sku: 'HT5695',
54
+ 'pdp.description_plain': 'A product description.',
55
+ });
56
+
57
+ it('should return eligible for suggestion with patchValue and url', () => {
58
+ const suggestion = {
59
+ getData: () => ({
60
+ patchValue: validPatchValue,
61
+ url: 'https://www.lovesac.com/products/seat-cover-set',
62
+ }),
63
+ };
64
+
65
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({ eligible: true });
66
+ });
67
+
68
+ it('should return eligible when transformRules is absent', () => {
69
+ const suggestion = {
70
+ getData: () => ({
71
+ patchValue: validPatchValue,
72
+ url: 'https://www.lovesac.com/products/seat-cover-set',
73
+ format: 'json',
74
+ tag: 'div',
75
+ }),
76
+ };
77
+
78
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({ eligible: true });
79
+ });
80
+
81
+ it('should return eligible when transformRules is null', () => {
82
+ const suggestion = {
83
+ getData: () => ({
84
+ patchValue: validPatchValue,
85
+ url: 'https://www.lovesac.com/products/seat-cover-set',
86
+ transformRules: null,
87
+ }),
88
+ };
89
+
90
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({ eligible: true });
91
+ });
92
+
93
+ it('should return ineligible when patchValue is missing', () => {
94
+ const suggestion = {
95
+ getData: () => ({
96
+ url: 'https://example.com/page',
97
+ }),
98
+ };
99
+
100
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
101
+ eligible: false,
102
+ reason: 'patchValue is required',
103
+ });
104
+ });
105
+
106
+ it('should return ineligible when patchValue is empty string', () => {
107
+ const suggestion = {
108
+ getData: () => ({
109
+ patchValue: '',
110
+ url: 'https://example.com/page',
111
+ }),
112
+ };
113
+
114
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
115
+ eligible: false,
116
+ reason: 'patchValue is required',
117
+ });
118
+ });
119
+
120
+ it('should return ineligible when url is missing', () => {
121
+ const suggestion = {
122
+ getData: () => ({
123
+ patchValue: validPatchValue,
124
+ }),
125
+ };
126
+
127
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
128
+ eligible: false,
129
+ reason: 'url is required',
130
+ });
131
+ });
132
+
133
+ it('should return ineligible when url is empty string', () => {
134
+ const suggestion = {
135
+ getData: () => ({
136
+ patchValue: validPatchValue,
137
+ url: '',
138
+ }),
139
+ };
140
+
141
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
142
+ eligible: false,
143
+ reason: 'url is required',
144
+ });
145
+ });
146
+
147
+ it('should return ineligible when patchValue is not valid JSON', () => {
148
+ const suggestion = {
149
+ getData: () => ({
150
+ patchValue: 'not valid json {{{',
151
+ url: 'https://example.com/page',
152
+ }),
153
+ };
154
+
155
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
156
+ eligible: false,
157
+ reason: 'patchValue must be valid JSON',
158
+ });
159
+ });
160
+
161
+ it('should return ineligible when data is null', () => {
162
+ const suggestion = {
163
+ getData: () => null,
164
+ };
165
+
166
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
167
+ eligible: false,
168
+ reason: 'patchValue is required',
169
+ });
170
+ });
171
+
172
+ it('should return ineligible when data is undefined', () => {
173
+ const suggestion = {
174
+ getData: () => undefined,
175
+ };
176
+
177
+ expect(mapper.canDeploy(suggestion)).to.deep.equal({
178
+ eligible: false,
179
+ reason: 'patchValue is required',
180
+ });
181
+ });
182
+ });
183
+
184
+ describe('suggestionsToPatches', () => {
185
+ const opportunityId = '03189e92-ac0f-4436-965a-0a7b55fc4101';
186
+ const suggestionId = '2fec9113-98e8-40b4-90e0-10aa9ce45abc';
187
+ const updatedAt = '2026-02-17T09:48:58.974Z';
188
+
189
+ function makeSuggestion(data, id = suggestionId) {
190
+ return {
191
+ getId: () => id,
192
+ getUpdatedAt: () => updatedAt,
193
+ getData: () => data,
194
+ };
195
+ }
196
+
197
+ it('should produce a JSON-LD patch appended to head', () => {
198
+ const suggestion = makeSuggestion({
199
+ patchValue: JSON.stringify({
200
+ sku: 'HT5695',
201
+ name: 'Seat Cover Set',
202
+ brand: 'Lovesac',
203
+ 'pdp.description_plain': 'A great product.',
204
+ }),
205
+ url: 'https://www.lovesac.com/products/seat-cover-set',
206
+ });
207
+
208
+ const patches = mapper.suggestionsToPatches(
209
+ '/products/seat-cover-set',
210
+ [suggestion],
211
+ opportunityId,
212
+ );
213
+
214
+ expect(patches).to.have.length(1);
215
+ const patch = patches[0];
216
+
217
+ expect(patch.op).to.equal('appendChild');
218
+ expect(patch.selector).to.equal('head');
219
+ expect(patch.valueFormat).to.equal('json');
220
+ expect(patch.target).to.equal('ai-bots');
221
+ expect(patch.tag).to.equal('script');
222
+ expect(patch.attrs).to.deep.equal({ type: 'application/ld+json' });
223
+ expect(patch.opportunityId).to.equal(opportunityId);
224
+ expect(patch.suggestionId).to.equal(suggestionId);
225
+ expect(patch.prerenderRequired).to.be.true;
226
+ expect(patch.lastUpdated).to.be.a('number');
227
+ });
228
+
229
+ it('should produce valid schema.org Product JSON-LD', () => {
230
+ const suggestion = makeSuggestion({
231
+ patchValue: JSON.stringify({
232
+ sku: 'HT5695',
233
+ name: 'Seat Cover Set',
234
+ brand: 'Lovesac',
235
+ 'pdp.description_plain': 'A great product.',
236
+ material: '100% polyester chenille',
237
+ 'facts.facets.category_path': ['Home', 'Sactionals', 'Covers'],
238
+ color_family: 'Blue',
239
+ }),
240
+ url: 'https://www.lovesac.com/products/seat-cover-set',
241
+ });
242
+
243
+ const patches = mapper.suggestionsToPatches(
244
+ '/products/seat-cover-set',
245
+ [suggestion],
246
+ opportunityId,
247
+ );
248
+
249
+ const jsonLd = patches[0].value;
250
+ expect(jsonLd['@context']).to.equal('https://schema.org');
251
+ expect(jsonLd['@type']).to.equal('Product');
252
+ expect(jsonLd.sku).to.equal('HT5695');
253
+ expect(jsonLd.name).to.equal('Seat Cover Set');
254
+ expect(jsonLd.brand).to.deep.equal({ '@type': 'Brand', name: 'Lovesac' });
255
+ expect(jsonLd.description).to.equal('A great product.');
256
+ expect(jsonLd.material).to.equal('100% polyester chenille');
257
+ expect(jsonLd.category).to.equal('Home > Sactionals > Covers');
258
+ expect(jsonLd.color).to.equal('Blue');
259
+ });
260
+
261
+ it('should map unmapped fields to additionalProperty', () => {
262
+ const suggestion = makeSuggestion({
263
+ patchValue: JSON.stringify({
264
+ sku: 'HT5695',
265
+ 'pdp.feature_bullets': ['Bullet 1', 'Bullet 2'],
266
+ 'facts.attributes.fabric_care': 'Machine washable',
267
+ audience_tags: ['homeowners', 'families'],
268
+ }),
269
+ url: 'https://www.lovesac.com/products/seat-cover-set',
270
+ });
271
+
272
+ const patches = mapper.suggestionsToPatches(
273
+ '/products/seat-cover-set',
274
+ [suggestion],
275
+ opportunityId,
276
+ );
277
+
278
+ const jsonLd = patches[0].value;
279
+ expect(jsonLd.additionalProperty).to.be.an('array');
280
+
281
+ const bulletsProp = jsonLd.additionalProperty.find((p) => p.name === 'pdp.feature_bullets');
282
+ expect(bulletsProp).to.exist;
283
+ expect(bulletsProp.value).to.deep.equal(['Bullet 1', 'Bullet 2']);
284
+
285
+ const careProp = jsonLd.additionalProperty.find((p) => p.name === 'facts.attributes.fabric_care');
286
+ expect(careProp).to.exist;
287
+ expect(careProp.value).to.equal('Machine washable');
288
+ });
289
+
290
+ it('should handle minimal enrichment data (only sku)', () => {
291
+ const suggestion = makeSuggestion({
292
+ patchValue: JSON.stringify({ sku: 'MINIMAL' }),
293
+ url: 'https://example.com/products/minimal',
294
+ });
295
+
296
+ const patches = mapper.suggestionsToPatches(
297
+ '/products/minimal',
298
+ [suggestion],
299
+ opportunityId,
300
+ );
301
+
302
+ expect(patches).to.have.length(1);
303
+ const jsonLd = patches[0].value;
304
+ expect(jsonLd['@context']).to.equal('https://schema.org');
305
+ expect(jsonLd['@type']).to.equal('Product');
306
+ expect(jsonLd.sku).to.equal('MINIMAL');
307
+ });
308
+
309
+ it('should handle rich enrichment data with all field types', () => {
310
+ const suggestion = makeSuggestion({
311
+ patchValue: JSON.stringify({
312
+ sku: '4Seats5Sides',
313
+ name: '4 Seats + 5 Sides Sactional',
314
+ category: 'Sectional / Modular Sofa',
315
+ brand: 'Lovesac',
316
+ 'pdp.description_plain': 'A modular sofa configuration.',
317
+ 'pdp.feature_bullets': ['Includes 4 Seats', 'StealthTech eligible'],
318
+ 'facts.facets.category_path': ['Furniture', 'Sectionals', 'Sactionals'],
319
+ 'facts.variants.summary': ['Multiple fabric options'],
320
+ material: 'Corded Velvet',
321
+ dimensions_or_capacity: '35" W x 29" D',
322
+ care_instructions: ['Machine washable'],
323
+ color_family: 'Grey',
324
+ audience_tags: ['homeowners'],
325
+ use_context: ['living room'],
326
+ style_tags: ['modern'],
327
+ keyword_synonyms: ['modular couch'],
328
+ persona_phrases: ['for families'],
329
+ }),
330
+ url: 'https://www.lovesac.com/products/4-seats-5-sides',
331
+ });
332
+
333
+ const patches = mapper.suggestionsToPatches(
334
+ '/products/4-seats-5-sides',
335
+ [suggestion],
336
+ opportunityId,
337
+ );
338
+
339
+ expect(patches).to.have.length(1);
340
+ const jsonLd = patches[0].value;
341
+ expect(jsonLd.sku).to.equal('4Seats5Sides');
342
+ expect(jsonLd.name).to.equal('4 Seats + 5 Sides Sactional');
343
+ expect(jsonLd.category).to.equal('Furniture > Sectionals > Sactionals');
344
+ expect(jsonLd.brand).to.deep.equal({ '@type': 'Brand', name: 'Lovesac' });
345
+ expect(jsonLd.description).to.equal('A modular sofa configuration.');
346
+ expect(jsonLd.material).to.equal('Corded Velvet');
347
+ expect(jsonLd.color).to.equal('Grey');
348
+ expect(jsonLd.additionalProperty).to.be.an('array');
349
+ expect(jsonLd.additionalProperty.length).to.be.greaterThan(0);
350
+ });
351
+
352
+ it('should skip ineligible suggestions and log warning', () => {
353
+ const eligible = makeSuggestion({
354
+ patchValue: JSON.stringify({ sku: 'GOOD' }),
355
+ url: 'https://example.com/good',
356
+ }, 'good-id');
357
+
358
+ const ineligible = makeSuggestion({
359
+ patchValue: '',
360
+ url: 'https://example.com/bad',
361
+ }, 'bad-id');
362
+
363
+ const patches = mapper.suggestionsToPatches(
364
+ '/test',
365
+ [eligible, ineligible],
366
+ opportunityId,
367
+ );
368
+
369
+ expect(patches).to.have.length(1);
370
+ expect(patches[0].suggestionId).to.equal('good-id');
371
+ expect(log.warn.calledOnce).to.be.true;
372
+ });
373
+
374
+ it('should produce multiple patches for multiple suggestions', () => {
375
+ const s1 = makeSuggestion({
376
+ patchValue: JSON.stringify({ sku: 'SKU1' }),
377
+ url: 'https://example.com/page',
378
+ }, 'id-1');
379
+
380
+ const s2 = makeSuggestion({
381
+ patchValue: JSON.stringify({ sku: 'SKU2' }),
382
+ url: 'https://example.com/page',
383
+ }, 'id-2');
384
+
385
+ const patches = mapper.suggestionsToPatches(
386
+ '/page',
387
+ [s1, s2],
388
+ opportunityId,
389
+ );
390
+
391
+ expect(patches).to.have.length(2);
392
+ expect(patches[0].suggestionId).to.equal('id-1');
393
+ expect(patches[1].suggestionId).to.equal('id-2');
394
+ });
395
+
396
+ it('should not include rationale field in JSON-LD output', () => {
397
+ const suggestion = makeSuggestion({
398
+ patchValue: JSON.stringify({
399
+ sku: 'HT5695',
400
+ rationale: 'This should not appear in output',
401
+ }),
402
+ url: 'https://example.com/page',
403
+ });
404
+
405
+ const patches = mapper.suggestionsToPatches(
406
+ '/page',
407
+ [suggestion],
408
+ opportunityId,
409
+ );
410
+
411
+ const jsonLd = patches[0].value;
412
+ expect(jsonLd.rationale).to.be.undefined;
413
+ const rationaleProp = (jsonLd.additionalProperty || []).find((p) => p.name === 'rationale');
414
+ expect(rationaleProp).to.be.undefined;
415
+ });
416
+
417
+ it('should join category_path array with > separator', () => {
418
+ const suggestion = makeSuggestion({
419
+ patchValue: JSON.stringify({
420
+ sku: 'TEST',
421
+ 'facts.facets.category_path': ['Home', 'Furniture', 'Sofas'],
422
+ }),
423
+ url: 'https://example.com/page',
424
+ });
425
+
426
+ const patches = mapper.suggestionsToPatches(
427
+ '/page',
428
+ [suggestion],
429
+ opportunityId,
430
+ );
431
+
432
+ expect(patches[0].value.category).to.equal('Home > Furniture > Sofas');
433
+ });
434
+
435
+ it('should use category field directly when facts.facets.category_path is absent', () => {
436
+ const suggestion = makeSuggestion({
437
+ patchValue: JSON.stringify({
438
+ sku: 'TEST',
439
+ category: 'Sectional / Modular Sofa',
440
+ }),
441
+ url: 'https://example.com/page',
442
+ });
443
+
444
+ const patches = mapper.suggestionsToPatches(
445
+ '/page',
446
+ [suggestion],
447
+ opportunityId,
448
+ );
449
+
450
+ expect(patches[0].value.category).to.equal('Sectional / Modular Sofa');
451
+ });
452
+ });
453
+ });
@@ -18,6 +18,7 @@ import sinon from 'sinon';
18
18
  import sinonChai from 'sinon-chai';
19
19
  import MapperRegistry from '../../src/mappers/mapper-registry.js';
20
20
  import HeadingsMapper from '../../src/mappers/headings-mapper.js';
21
+ import CommercePageEnrichmentMapper from '../../src/mappers/commerce-page-enrichment-mapper.js';
21
22
  import BaseOpportunityMapper from '../../src/mappers/base-mapper.js';
22
23
 
23
24
  use(sinonChai);
@@ -115,6 +116,12 @@ describe('MapperRegistry', () => {
115
116
  expect(mapper).to.be.instanceOf(HeadingsMapper);
116
117
  });
117
118
 
119
+ it('should return commerce page enrichment mapper', () => {
120
+ const mapper = registry.getMapper('commerce-product-page-enrichment');
121
+
122
+ expect(mapper).to.be.instanceOf(CommercePageEnrichmentMapper);
123
+ });
124
+
118
125
  it('should return null for unsupported opportunity type', () => {
119
126
  const mapper = registry.getMapper('unsupported-type');
120
127
 
@@ -143,6 +150,7 @@ describe('MapperRegistry', () => {
143
150
 
144
151
  expect(types).to.be.an('array');
145
152
  expect(types).to.include('headings');
153
+ expect(types).to.include('commerce-product-page-enrichment');
146
154
  });
147
155
 
148
156
  it('should include custom registered mappers', () => {