@adobe/spacecat-shared-tokowaka-client 1.0.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/.mocha-multi.json +7 -0
- package/.nycrc.json +15 -0
- package/.releaserc.cjs +17 -0
- package/CHANGELOG.md +24 -0
- package/CODE_OF_CONDUCT.md +75 -0
- package/CONTRIBUTING.md +74 -0
- package/LICENSE.txt +203 -0
- package/README.md +101 -0
- package/package.json +53 -0
- package/src/cdn/base-cdn-client.js +50 -0
- package/src/cdn/cdn-client-registry.js +87 -0
- package/src/cdn/cloudfront-cdn-client.js +128 -0
- package/src/constants.js +17 -0
- package/src/index.d.ts +289 -0
- package/src/index.js +447 -0
- package/src/mappers/base-mapper.js +86 -0
- package/src/mappers/content-summarization-mapper.js +106 -0
- package/src/mappers/headings-mapper.js +118 -0
- package/src/mappers/mapper-registry.js +87 -0
- package/test/cdn/base-cdn-client.test.js +52 -0
- package/test/cdn/cdn-client-registry.test.js +179 -0
- package/test/cdn/cloudfront-cdn-client.test.js +330 -0
- package/test/index.test.js +1142 -0
- package/test/mappers/base-mapper.test.js +110 -0
- package/test/mappers/content-mapper.test.js +355 -0
- package/test/mappers/headings-mapper.test.js +428 -0
- package/test/mappers/mapper-registry.test.js +197 -0
- package/test/setup-env.js +18 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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 HeadingsMapper from '../../src/mappers/headings-mapper.js';
|
|
17
|
+
|
|
18
|
+
describe('HeadingsMapper', () => {
|
|
19
|
+
let mapper;
|
|
20
|
+
let log;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
log = {
|
|
24
|
+
debug: () => {},
|
|
25
|
+
info: () => {},
|
|
26
|
+
warn: () => {},
|
|
27
|
+
error: () => {},
|
|
28
|
+
};
|
|
29
|
+
mapper = new HeadingsMapper(log);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('getOpportunityType', () => {
|
|
33
|
+
it('should return headings', () => {
|
|
34
|
+
expect(mapper.getOpportunityType()).to.equal('headings');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('requiresPrerender', () => {
|
|
39
|
+
it('should return true', () => {
|
|
40
|
+
expect(mapper.requiresPrerender()).to.be.true;
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('canDeploy', () => {
|
|
45
|
+
it('should return eligible for heading-empty checkType', () => {
|
|
46
|
+
const suggestion = {
|
|
47
|
+
getData: () => ({
|
|
48
|
+
checkType: 'heading-empty',
|
|
49
|
+
recommendedAction: 'New Heading',
|
|
50
|
+
transformRules: {
|
|
51
|
+
action: 'replace',
|
|
52
|
+
selector: 'h1',
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const result = mapper.canDeploy(suggestion);
|
|
58
|
+
|
|
59
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return eligible for heading-missing-h1 checkType', () => {
|
|
63
|
+
const suggestion = {
|
|
64
|
+
getData: () => ({
|
|
65
|
+
checkType: 'heading-missing-h1',
|
|
66
|
+
recommendedAction: 'New H1',
|
|
67
|
+
transformRules: {
|
|
68
|
+
action: 'insertAfter',
|
|
69
|
+
selector: '#header',
|
|
70
|
+
tag: 'h1',
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const result = mapper.canDeploy(suggestion);
|
|
76
|
+
|
|
77
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return eligible for heading-h1-length checkType', () => {
|
|
81
|
+
const suggestion = {
|
|
82
|
+
getData: () => ({
|
|
83
|
+
checkType: 'heading-h1-length',
|
|
84
|
+
recommendedAction: 'Better H1',
|
|
85
|
+
transformRules: {
|
|
86
|
+
action: 'replace',
|
|
87
|
+
selector: 'h1',
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = mapper.canDeploy(suggestion);
|
|
93
|
+
|
|
94
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should return ineligible for unknown checkType', () => {
|
|
98
|
+
const suggestion = {
|
|
99
|
+
getData: () => ({ checkType: 'unknown-type' }),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const result = mapper.canDeploy(suggestion);
|
|
103
|
+
|
|
104
|
+
expect(result).to.deep.equal({
|
|
105
|
+
eligible: false,
|
|
106
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: unknown-type',
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return ineligible when checkType is missing', () => {
|
|
111
|
+
const suggestion = {
|
|
112
|
+
getData: () => ({}),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const result = mapper.canDeploy(suggestion);
|
|
116
|
+
|
|
117
|
+
expect(result).to.deep.equal({
|
|
118
|
+
eligible: false,
|
|
119
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: undefined',
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return ineligible when data is null', () => {
|
|
124
|
+
const suggestion = {
|
|
125
|
+
getData: () => null,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const result = mapper.canDeploy(suggestion);
|
|
129
|
+
|
|
130
|
+
expect(result).to.deep.equal({
|
|
131
|
+
eligible: false,
|
|
132
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: undefined',
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should return ineligible when recommendedAction is missing', () => {
|
|
137
|
+
const suggestion = {
|
|
138
|
+
getData: () => ({
|
|
139
|
+
checkType: 'heading-empty',
|
|
140
|
+
transformRules: {
|
|
141
|
+
action: 'replace',
|
|
142
|
+
selector: 'h1',
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const result = mapper.canDeploy(suggestion);
|
|
148
|
+
|
|
149
|
+
expect(result).to.deep.equal({
|
|
150
|
+
eligible: false,
|
|
151
|
+
reason: 'recommendedAction is required',
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should return ineligible when transformRules.selector is missing', () => {
|
|
156
|
+
const suggestion = {
|
|
157
|
+
getData: () => ({
|
|
158
|
+
checkType: 'heading-empty',
|
|
159
|
+
recommendedAction: 'New Heading',
|
|
160
|
+
transformRules: {
|
|
161
|
+
action: 'replace',
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const result = mapper.canDeploy(suggestion);
|
|
167
|
+
|
|
168
|
+
expect(result).to.deep.equal({
|
|
169
|
+
eligible: false,
|
|
170
|
+
reason: 'transformRules.selector is required',
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should return ineligible for heading-missing-h1 with invalid action', () => {
|
|
175
|
+
const suggestion = {
|
|
176
|
+
getData: () => ({
|
|
177
|
+
checkType: 'heading-missing-h1',
|
|
178
|
+
recommendedAction: 'New H1',
|
|
179
|
+
transformRules: {
|
|
180
|
+
action: 'replace',
|
|
181
|
+
selector: '#header',
|
|
182
|
+
tag: 'h1',
|
|
183
|
+
},
|
|
184
|
+
}),
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const result = mapper.canDeploy(suggestion);
|
|
188
|
+
|
|
189
|
+
expect(result).to.deep.equal({
|
|
190
|
+
eligible: false,
|
|
191
|
+
reason: 'transformRules.action must be insertBefore or insertAfter for heading-missing-h1',
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should return ineligible for heading-missing-h1 without tag', () => {
|
|
196
|
+
const suggestion = {
|
|
197
|
+
getData: () => ({
|
|
198
|
+
checkType: 'heading-missing-h1',
|
|
199
|
+
recommendedAction: 'New H1',
|
|
200
|
+
transformRules: {
|
|
201
|
+
action: 'insertAfter',
|
|
202
|
+
selector: '#header',
|
|
203
|
+
},
|
|
204
|
+
}),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const result = mapper.canDeploy(suggestion);
|
|
208
|
+
|
|
209
|
+
expect(result).to.deep.equal({
|
|
210
|
+
eligible: false,
|
|
211
|
+
reason: 'transformRules.tag is required for heading-missing-h1',
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should return ineligible for heading-h1-length with invalid action', () => {
|
|
216
|
+
const suggestion = {
|
|
217
|
+
getData: () => ({
|
|
218
|
+
checkType: 'heading-h1-length',
|
|
219
|
+
recommendedAction: 'New H1',
|
|
220
|
+
transformRules: {
|
|
221
|
+
action: 'insertAfter',
|
|
222
|
+
selector: 'h1',
|
|
223
|
+
},
|
|
224
|
+
}),
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const result = mapper.canDeploy(suggestion);
|
|
228
|
+
|
|
229
|
+
expect(result).to.deep.equal({
|
|
230
|
+
eligible: false,
|
|
231
|
+
reason: 'transformRules.action must be replace for heading-h1-length',
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('suggestionToPatch', () => {
|
|
237
|
+
it('should create patch for heading-empty with transformRules', () => {
|
|
238
|
+
const suggestion = {
|
|
239
|
+
getId: () => 'sugg-123',
|
|
240
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
241
|
+
getData: () => ({
|
|
242
|
+
checkType: 'heading-empty',
|
|
243
|
+
recommendedAction: 'New Heading',
|
|
244
|
+
transformRules: {
|
|
245
|
+
action: 'replace',
|
|
246
|
+
selector: 'h1',
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-123');
|
|
252
|
+
|
|
253
|
+
expect(patch).to.deep.include({
|
|
254
|
+
op: 'replace',
|
|
255
|
+
selector: 'h1',
|
|
256
|
+
value: 'New Heading',
|
|
257
|
+
opportunityId: 'opp-123',
|
|
258
|
+
suggestionId: 'sugg-123',
|
|
259
|
+
prerenderRequired: true,
|
|
260
|
+
});
|
|
261
|
+
expect(patch.lastUpdated).to.be.a('number');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should create patch with custom selector', () => {
|
|
265
|
+
const suggestion = {
|
|
266
|
+
getId: () => 'sugg-123',
|
|
267
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
268
|
+
getData: () => ({
|
|
269
|
+
checkType: 'heading-empty',
|
|
270
|
+
recommendedAction: 'New Heading',
|
|
271
|
+
transformRules: {
|
|
272
|
+
action: 'replace',
|
|
273
|
+
selector: 'body > h1',
|
|
274
|
+
},
|
|
275
|
+
}),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-123');
|
|
279
|
+
|
|
280
|
+
expect(patch).to.deep.include({
|
|
281
|
+
op: 'replace',
|
|
282
|
+
selector: 'body > h1',
|
|
283
|
+
value: 'New Heading',
|
|
284
|
+
opportunityId: 'opp-123',
|
|
285
|
+
suggestionId: 'sugg-123',
|
|
286
|
+
prerenderRequired: true,
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should create patch for heading-missing-h1 with transformRules', () => {
|
|
291
|
+
const suggestion = {
|
|
292
|
+
getId: () => 'sugg-456',
|
|
293
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
294
|
+
getData: () => ({
|
|
295
|
+
checkType: 'heading-missing-h1',
|
|
296
|
+
recommendedAction: 'Exclusive Flight Booking Deals & Partner Discounts.',
|
|
297
|
+
transformRules: {
|
|
298
|
+
action: 'insertAfter',
|
|
299
|
+
selector: '#text-85a9876220 > h2:nth-of-type(1)',
|
|
300
|
+
tag: 'h1',
|
|
301
|
+
},
|
|
302
|
+
}),
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-456');
|
|
306
|
+
|
|
307
|
+
expect(patch).to.deep.include({
|
|
308
|
+
op: 'insertAfter',
|
|
309
|
+
selector: '#text-85a9876220 > h2:nth-of-type(1)',
|
|
310
|
+
value: 'Exclusive Flight Booking Deals & Partner Discounts.',
|
|
311
|
+
tag: 'h1',
|
|
312
|
+
opportunityId: 'opp-456',
|
|
313
|
+
suggestionId: 'sugg-456',
|
|
314
|
+
prerenderRequired: true,
|
|
315
|
+
});
|
|
316
|
+
expect(patch.lastUpdated).to.be.a('number');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should create patch for heading-h1-length with transformRules', () => {
|
|
320
|
+
const suggestion = {
|
|
321
|
+
getId: () => 'sugg-789',
|
|
322
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
323
|
+
getData: () => ({
|
|
324
|
+
checkType: 'heading-h1-length',
|
|
325
|
+
recommendedAction: 'New H1 Heading',
|
|
326
|
+
transformRules: {
|
|
327
|
+
action: 'replace',
|
|
328
|
+
selector: 'body > h1',
|
|
329
|
+
},
|
|
330
|
+
}),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-789');
|
|
334
|
+
|
|
335
|
+
expect(patch).to.deep.include({
|
|
336
|
+
op: 'replace',
|
|
337
|
+
selector: 'body > h1',
|
|
338
|
+
value: 'New H1 Heading',
|
|
339
|
+
opportunityId: 'opp-789',
|
|
340
|
+
suggestionId: 'sugg-789',
|
|
341
|
+
prerenderRequired: true,
|
|
342
|
+
});
|
|
343
|
+
expect(patch.lastUpdated).to.be.a('number');
|
|
344
|
+
expect(patch.tag).to.be.undefined;
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should return null for heading-missing-h1 without transformRules', () => {
|
|
348
|
+
const suggestion = {
|
|
349
|
+
getId: () => 'sugg-999',
|
|
350
|
+
getData: () => ({
|
|
351
|
+
checkType: 'heading-missing-h1',
|
|
352
|
+
recommendedAction: 'New Heading',
|
|
353
|
+
}),
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-999');
|
|
357
|
+
|
|
358
|
+
expect(patch).to.be.null;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should return null for heading-h1-length without selector in transformRules', () => {
|
|
362
|
+
const suggestion = {
|
|
363
|
+
getId: () => 'sugg-888',
|
|
364
|
+
getData: () => ({
|
|
365
|
+
checkType: 'heading-h1-length',
|
|
366
|
+
recommendedAction: 'New Heading',
|
|
367
|
+
transformRules: {
|
|
368
|
+
action: 'insertAt',
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const patch = mapper.suggestionToPatch(suggestion, 'opp-888');
|
|
374
|
+
|
|
375
|
+
expect(patch).to.be.null;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should log warning for heading-missing-h1 with missing transformRules - validation path', () => {
|
|
379
|
+
let warnLogged = false;
|
|
380
|
+
const warnLog = {
|
|
381
|
+
debug: () => {},
|
|
382
|
+
info: () => {},
|
|
383
|
+
warn: () => { warnLogged = true; },
|
|
384
|
+
error: () => {},
|
|
385
|
+
};
|
|
386
|
+
const warnMapper = new HeadingsMapper(warnLog);
|
|
387
|
+
|
|
388
|
+
const suggestion = {
|
|
389
|
+
getId: () => 'sugg-warn',
|
|
390
|
+
getData: () => ({
|
|
391
|
+
checkType: 'heading-missing-h1',
|
|
392
|
+
recommendedAction: 'New Heading',
|
|
393
|
+
}),
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const patch = warnMapper.suggestionToPatch(suggestion, 'opp-warn');
|
|
397
|
+
|
|
398
|
+
expect(patch).to.be.null;
|
|
399
|
+
expect(warnLogged).to.be.true;
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should log warning for heading-missing-h1 with invalid transformRules', () => {
|
|
403
|
+
let warnMessage = '';
|
|
404
|
+
const warnLog = {
|
|
405
|
+
debug: () => {},
|
|
406
|
+
info: () => {},
|
|
407
|
+
warn: (msg) => { warnMessage = msg; },
|
|
408
|
+
error: () => {},
|
|
409
|
+
};
|
|
410
|
+
const warnMapper = new HeadingsMapper(warnLog);
|
|
411
|
+
|
|
412
|
+
const suggestion = {
|
|
413
|
+
getId: () => 'sugg-defensive',
|
|
414
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
415
|
+
getData: () => ({
|
|
416
|
+
checkType: 'heading-missing-h1',
|
|
417
|
+
recommendedAction: 'New Heading',
|
|
418
|
+
// Missing transformRules
|
|
419
|
+
}),
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const patch = warnMapper.suggestionToPatch(suggestion, 'opp-defensive');
|
|
423
|
+
|
|
424
|
+
expect(patch).to.be.null;
|
|
425
|
+
expect(warnMessage).to.include('cannot be deployed');
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
/* eslint-disable max-classes-per-file */
|
|
15
|
+
|
|
16
|
+
import { expect, use } from 'chai';
|
|
17
|
+
import sinon from 'sinon';
|
|
18
|
+
import sinonChai from 'sinon-chai';
|
|
19
|
+
import MapperRegistry from '../../src/mappers/mapper-registry.js';
|
|
20
|
+
import HeadingsMapper from '../../src/mappers/headings-mapper.js';
|
|
21
|
+
import BaseOpportunityMapper from '../../src/mappers/base-mapper.js';
|
|
22
|
+
|
|
23
|
+
use(sinonChai);
|
|
24
|
+
|
|
25
|
+
describe('MapperRegistry', () => {
|
|
26
|
+
let registry;
|
|
27
|
+
let log;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
log = {
|
|
31
|
+
info: sinon.stub(),
|
|
32
|
+
warn: sinon.stub(),
|
|
33
|
+
error: sinon.stub(),
|
|
34
|
+
debug: sinon.stub(),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
registry = new MapperRegistry(log);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
sinon.restore();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('constructor', () => {
|
|
45
|
+
it('should create an instance and register default mappers', () => {
|
|
46
|
+
expect(registry).to.be.instanceOf(MapperRegistry);
|
|
47
|
+
expect(registry.mappers).to.be.instanceOf(Map);
|
|
48
|
+
expect(registry.mappers.size).to.be.greaterThan(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('registerMapper', () => {
|
|
53
|
+
it('should register a custom mapper', () => {
|
|
54
|
+
class CustomMapper extends BaseOpportunityMapper {
|
|
55
|
+
// eslint-disable-next-line class-methods-use-this
|
|
56
|
+
getOpportunityType() {
|
|
57
|
+
return 'custom';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line class-methods-use-this
|
|
61
|
+
requiresPrerender() {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// eslint-disable-next-line class-methods-use-this
|
|
66
|
+
suggestionToPatch() {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// eslint-disable-next-line class-methods-use-this
|
|
71
|
+
validateSuggestionData() {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
registry.registerMapper(new CustomMapper(log));
|
|
77
|
+
|
|
78
|
+
expect(registry.mappers.has('custom')).to.be.true;
|
|
79
|
+
expect(registry.getSupportedOpportunityTypes()).to.include('custom');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should log debug message when overriding existing mapper', () => {
|
|
83
|
+
class CustomMapper extends BaseOpportunityMapper {
|
|
84
|
+
// eslint-disable-next-line class-methods-use-this
|
|
85
|
+
getOpportunityType() {
|
|
86
|
+
return 'headings'; // Override existing headings mapper
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// eslint-disable-next-line class-methods-use-this
|
|
90
|
+
requiresPrerender() {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// eslint-disable-next-line class-methods-use-this
|
|
95
|
+
suggestionToPatch() {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
registry.registerMapper(new CustomMapper(log));
|
|
101
|
+
|
|
102
|
+
expect(log.debug).to.have.been.calledWith(
|
|
103
|
+
'Mapper for opportunity type "headings" is being overridden',
|
|
104
|
+
);
|
|
105
|
+
expect(log.info).to.have.been.calledWith(
|
|
106
|
+
'Registered mapper for opportunity type: headings',
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('getMapper', () => {
|
|
112
|
+
it('should return headings mapper for headings opportunity type', () => {
|
|
113
|
+
const mapper = registry.getMapper('headings');
|
|
114
|
+
|
|
115
|
+
expect(mapper).to.be.instanceOf(HeadingsMapper);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should return null for unsupported opportunity type', () => {
|
|
119
|
+
const mapper = registry.getMapper('unsupported-type');
|
|
120
|
+
|
|
121
|
+
expect(mapper).to.be.null;
|
|
122
|
+
expect(log.warn).to.have.been.calledWith(
|
|
123
|
+
'No mapper found for opportunity type: unsupported-type',
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return null when opportunity type is empty', () => {
|
|
128
|
+
const mapper = registry.getMapper('');
|
|
129
|
+
|
|
130
|
+
expect(mapper).to.be.null;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should return null when opportunity type is null', () => {
|
|
134
|
+
const mapper = registry.getMapper(null);
|
|
135
|
+
|
|
136
|
+
expect(mapper).to.be.null;
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('getSupportedOpportunityTypes', () => {
|
|
141
|
+
it('should return list of supported opportunity types', () => {
|
|
142
|
+
const types = registry.getSupportedOpportunityTypes();
|
|
143
|
+
|
|
144
|
+
expect(types).to.be.an('array');
|
|
145
|
+
expect(types).to.include('headings');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should include custom registered mappers', () => {
|
|
149
|
+
class CustomMapper extends BaseOpportunityMapper {
|
|
150
|
+
// eslint-disable-next-line class-methods-use-this
|
|
151
|
+
getOpportunityType() {
|
|
152
|
+
return 'custom';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// eslint-disable-next-line class-methods-use-this
|
|
156
|
+
requiresPrerender() {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// eslint-disable-next-line class-methods-use-this
|
|
161
|
+
suggestionToPatch() {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// eslint-disable-next-line class-methods-use-this
|
|
166
|
+
validateSuggestionData() {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
registry.registerMapper(new CustomMapper(log));
|
|
172
|
+
|
|
173
|
+
const types = registry.getSupportedOpportunityTypes();
|
|
174
|
+
|
|
175
|
+
expect(types).to.include('custom');
|
|
176
|
+
expect(types).to.include('headings');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('hasMapper', () => {
|
|
181
|
+
it('should return true for supported opportunity type', () => {
|
|
182
|
+
expect(registry.hasMapper('headings')).to.be.true;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should return false for unsupported opportunity type', () => {
|
|
186
|
+
expect(registry.hasMapper('unsupported')).to.be.false;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return false for null opportunity type', () => {
|
|
190
|
+
expect(registry.hasMapper(null)).to.be.false;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should return false for undefined opportunity type', () => {
|
|
194
|
+
expect(registry.hasMapper(undefined)).to.be.false;
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
// eslint-disable-next-line no-console
|
|
13
|
+
console.log('Forcing HTTP/1.1 for Adobe Fetch');
|
|
14
|
+
process.env.HELIX_FETCH_FORCE_HTTP1 = 'true';
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.log('Disabling AWS XRay');
|
|
17
|
+
process.env.AWS_XRAY_SDK_ENABLED = 'false';
|
|
18
|
+
process.env.AWS_XRAY_CONTEXT_MISSING = 'IGNORE_ERROR';
|