@adobe/spacecat-shared-tokowaka-client 1.2.4 → 1.3.1
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 +14 -0
- package/package.json +1 -1
- package/src/index.d.ts +20 -1
- package/src/index.js +59 -5
- package/src/mappers/base-mapper.js +12 -0
- package/src/mappers/generic-mapper.js +117 -0
- package/src/mappers/headings-mapper.js +16 -1
- package/src/mappers/mapper-registry.js +6 -0
- package/src/mappers/prerender-mapper.js +78 -0
- package/src/mappers/toc-mapper.js +116 -0
- package/test/index.test.js +243 -0
- package/test/mappers/generic-mapper.test.js +671 -0
- package/test/mappers/headings-mapper.test.js +154 -3
- package/test/mappers/prerender-mapper.test.js +216 -0
- package/test/mappers/toc-mapper.test.js +616 -0
|
@@ -94,6 +94,24 @@ describe('HeadingsMapper', () => {
|
|
|
94
94
|
expect(result).to.deep.equal({ eligible: true });
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
it('should return eligible for heading-order-invalid checkType', () => {
|
|
98
|
+
const suggestion = {
|
|
99
|
+
getData: () => ({
|
|
100
|
+
checkType: 'heading-order-invalid',
|
|
101
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
102
|
+
transformRules: {
|
|
103
|
+
action: 'replaceWith',
|
|
104
|
+
selector: '.invalid-section',
|
|
105
|
+
valueFormat: 'hast',
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const result = mapper.canDeploy(suggestion);
|
|
111
|
+
|
|
112
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
113
|
+
});
|
|
114
|
+
|
|
97
115
|
it('should return ineligible for unknown checkType', () => {
|
|
98
116
|
const suggestion = {
|
|
99
117
|
getData: () => ({ checkType: 'unknown-type' }),
|
|
@@ -103,7 +121,7 @@ describe('HeadingsMapper', () => {
|
|
|
103
121
|
|
|
104
122
|
expect(result).to.deep.equal({
|
|
105
123
|
eligible: false,
|
|
106
|
-
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: unknown-type',
|
|
124
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length, heading-order-invalid can be deployed. This suggestion has checkType: unknown-type',
|
|
107
125
|
});
|
|
108
126
|
});
|
|
109
127
|
|
|
@@ -116,7 +134,7 @@ describe('HeadingsMapper', () => {
|
|
|
116
134
|
|
|
117
135
|
expect(result).to.deep.equal({
|
|
118
136
|
eligible: false,
|
|
119
|
-
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: undefined',
|
|
137
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length, heading-order-invalid can be deployed. This suggestion has checkType: undefined',
|
|
120
138
|
});
|
|
121
139
|
});
|
|
122
140
|
|
|
@@ -129,7 +147,7 @@ describe('HeadingsMapper', () => {
|
|
|
129
147
|
|
|
130
148
|
expect(result).to.deep.equal({
|
|
131
149
|
eligible: false,
|
|
132
|
-
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length can be deployed. This suggestion has checkType: undefined',
|
|
150
|
+
reason: 'Only heading-empty, heading-missing-h1, heading-h1-length, heading-order-invalid can be deployed. This suggestion has checkType: undefined',
|
|
133
151
|
});
|
|
134
152
|
});
|
|
135
153
|
|
|
@@ -231,6 +249,68 @@ describe('HeadingsMapper', () => {
|
|
|
231
249
|
reason: 'transformRules.action must be replace for heading-h1-length',
|
|
232
250
|
});
|
|
233
251
|
});
|
|
252
|
+
|
|
253
|
+
it('should return ineligible for heading-order-invalid with invalid action', () => {
|
|
254
|
+
const suggestion = {
|
|
255
|
+
getData: () => ({
|
|
256
|
+
checkType: 'heading-order-invalid',
|
|
257
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
258
|
+
transformRules: {
|
|
259
|
+
action: 'replace',
|
|
260
|
+
selector: '.invalid-section',
|
|
261
|
+
valueFormat: 'hast',
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const result = mapper.canDeploy(suggestion);
|
|
267
|
+
|
|
268
|
+
expect(result).to.deep.equal({
|
|
269
|
+
eligible: false,
|
|
270
|
+
reason: 'transformRules.action must be replaceWith for heading-order-invalid',
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should return ineligible for heading-order-invalid with missing valueFormat', () => {
|
|
275
|
+
const suggestion = {
|
|
276
|
+
getData: () => ({
|
|
277
|
+
checkType: 'heading-order-invalid',
|
|
278
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
279
|
+
transformRules: {
|
|
280
|
+
action: 'replaceWith',
|
|
281
|
+
selector: '.invalid-section',
|
|
282
|
+
},
|
|
283
|
+
}),
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const result = mapper.canDeploy(suggestion);
|
|
287
|
+
|
|
288
|
+
expect(result).to.deep.equal({
|
|
289
|
+
eligible: false,
|
|
290
|
+
reason: 'transformRules.valueFormat must be hast for heading-order-invalid',
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should return ineligible for heading-order-invalid with invalid valueFormat', () => {
|
|
295
|
+
const suggestion = {
|
|
296
|
+
getData: () => ({
|
|
297
|
+
checkType: 'heading-order-invalid',
|
|
298
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
299
|
+
transformRules: {
|
|
300
|
+
action: 'replaceWith',
|
|
301
|
+
selector: '.invalid-section',
|
|
302
|
+
valueFormat: 'text',
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const result = mapper.canDeploy(suggestion);
|
|
308
|
+
|
|
309
|
+
expect(result).to.deep.equal({
|
|
310
|
+
eligible: false,
|
|
311
|
+
reason: 'transformRules.valueFormat must be hast for heading-order-invalid',
|
|
312
|
+
});
|
|
313
|
+
});
|
|
234
314
|
});
|
|
235
315
|
|
|
236
316
|
describe('suggestionsToPatches', () => {
|
|
@@ -352,6 +432,77 @@ describe('HeadingsMapper', () => {
|
|
|
352
432
|
expect(patch.tag).to.be.undefined;
|
|
353
433
|
});
|
|
354
434
|
|
|
435
|
+
it('should create patch for heading-order-invalid with transformRules', () => {
|
|
436
|
+
const hastValue = {
|
|
437
|
+
type: 'element',
|
|
438
|
+
tagName: 'div',
|
|
439
|
+
children: [
|
|
440
|
+
{ type: 'element', tagName: 'h2', children: [{ type: 'text', value: 'Section Title' }] },
|
|
441
|
+
{ type: 'element', tagName: 'h3', children: [{ type: 'text', value: 'Subsection' }] },
|
|
442
|
+
],
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const suggestion = {
|
|
446
|
+
getId: () => 'sugg-101',
|
|
447
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
448
|
+
getData: () => ({
|
|
449
|
+
checkType: 'heading-order-invalid',
|
|
450
|
+
recommendedAction: hastValue,
|
|
451
|
+
transformRules: {
|
|
452
|
+
action: 'replaceWith',
|
|
453
|
+
selector: '.content-section',
|
|
454
|
+
valueFormat: 'hast',
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const patches = mapper.suggestionsToPatches('/path', [suggestion], 'opp-101');
|
|
460
|
+
expect(patches.length).to.equal(1);
|
|
461
|
+
const patch = patches[0];
|
|
462
|
+
|
|
463
|
+
expect(patch).to.deep.include({
|
|
464
|
+
op: 'replaceWith',
|
|
465
|
+
selector: '.content-section',
|
|
466
|
+
value: hastValue,
|
|
467
|
+
opportunityId: 'opp-101',
|
|
468
|
+
suggestionId: 'sugg-101',
|
|
469
|
+
prerenderRequired: true,
|
|
470
|
+
});
|
|
471
|
+
expect(patch.lastUpdated).to.be.a('number');
|
|
472
|
+
expect(patch.tag).to.be.undefined;
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should return empty array for heading-order-invalid without transformRules', () => {
|
|
476
|
+
const suggestion = {
|
|
477
|
+
getId: () => 'sugg-102',
|
|
478
|
+
getData: () => ({
|
|
479
|
+
checkType: 'heading-order-invalid',
|
|
480
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
481
|
+
}),
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const patches = mapper.suggestionsToPatches('/path', [suggestion], 'opp-102');
|
|
485
|
+
expect(patches.length).to.equal(0);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should return empty array for heading-order-invalid with invalid action', () => {
|
|
489
|
+
const suggestion = {
|
|
490
|
+
getId: () => 'sugg-103',
|
|
491
|
+
getData: () => ({
|
|
492
|
+
checkType: 'heading-order-invalid',
|
|
493
|
+
recommendedAction: { type: 'element', tagName: 'h2', children: [] },
|
|
494
|
+
transformRules: {
|
|
495
|
+
action: 'replace',
|
|
496
|
+
selector: '.content-section',
|
|
497
|
+
valueFormat: 'hast',
|
|
498
|
+
},
|
|
499
|
+
}),
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const patches = mapper.suggestionsToPatches('/path', [suggestion], 'opp-103');
|
|
503
|
+
expect(patches.length).to.equal(0);
|
|
504
|
+
});
|
|
505
|
+
|
|
355
506
|
it('should return empty array for heading-missing-h1 without transformRules', () => {
|
|
356
507
|
const suggestion = {
|
|
357
508
|
getId: () => 'sugg-999',
|
|
@@ -0,0 +1,216 @@
|
|
|
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 PrerenderMapper from '../../src/mappers/prerender-mapper.js';
|
|
17
|
+
|
|
18
|
+
describe('PrerenderMapper', () => {
|
|
19
|
+
let mapper;
|
|
20
|
+
let log;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
log = {
|
|
24
|
+
debug: () => {},
|
|
25
|
+
info: () => {},
|
|
26
|
+
warn: () => {},
|
|
27
|
+
error: () => {},
|
|
28
|
+
};
|
|
29
|
+
mapper = new PrerenderMapper(log);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('getOpportunityType', () => {
|
|
33
|
+
it('should return prerender', () => {
|
|
34
|
+
expect(mapper.getOpportunityType()).to.equal('prerender');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('requiresPrerender', () => {
|
|
39
|
+
it('should return true', () => {
|
|
40
|
+
expect(mapper.requiresPrerender()).to.be.true;
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('allowConfigsWithoutPatch', () => {
|
|
45
|
+
it('should return true for prerender mapper', () => {
|
|
46
|
+
expect(mapper.allowConfigsWithoutPatch()).to.be.true;
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('suggestionsToPatches', () => {
|
|
51
|
+
it('should return empty array for prerender suggestions', () => {
|
|
52
|
+
const suggestion = {
|
|
53
|
+
getId: () => 'test-suggestion-id',
|
|
54
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
55
|
+
getData: () => ({
|
|
56
|
+
url: 'https://example.com/page',
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const patches = mapper.suggestionsToPatches(
|
|
61
|
+
'/page',
|
|
62
|
+
[suggestion],
|
|
63
|
+
'test-opportunity-id',
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(patches).to.be.an('array');
|
|
67
|
+
expect(patches).to.be.empty;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return empty array even with multiple suggestions', () => {
|
|
71
|
+
const suggestions = [
|
|
72
|
+
{
|
|
73
|
+
getId: () => 'suggestion-1',
|
|
74
|
+
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
75
|
+
getData: () => ({
|
|
76
|
+
url: 'https://example.com/page1',
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
getId: () => 'suggestion-2',
|
|
81
|
+
getUpdatedAt: () => '2025-01-15T11:00:00.000Z',
|
|
82
|
+
getData: () => ({
|
|
83
|
+
url: 'https://example.com/page2',
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const patches = mapper.suggestionsToPatches(
|
|
89
|
+
'/page',
|
|
90
|
+
suggestions,
|
|
91
|
+
'test-opportunity-id',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(patches).to.be.an('array');
|
|
95
|
+
expect(patches).to.be.empty;
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('canDeploy', () => {
|
|
100
|
+
it('should return eligible for valid suggestion with URL', () => {
|
|
101
|
+
const suggestion = {
|
|
102
|
+
getData: () => ({
|
|
103
|
+
url: 'https://example.com/page',
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const result = mapper.canDeploy(suggestion);
|
|
108
|
+
|
|
109
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should return ineligible when URL is missing', () => {
|
|
113
|
+
const suggestion = {
|
|
114
|
+
getData: () => ({}),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const result = mapper.canDeploy(suggestion);
|
|
118
|
+
|
|
119
|
+
expect(result).to.deep.equal({
|
|
120
|
+
eligible: false,
|
|
121
|
+
reason: 'url is required',
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should return ineligible when URL is empty string', () => {
|
|
126
|
+
const suggestion = {
|
|
127
|
+
getData: () => ({
|
|
128
|
+
url: '',
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const result = mapper.canDeploy(suggestion);
|
|
133
|
+
|
|
134
|
+
expect(result).to.deep.equal({
|
|
135
|
+
eligible: false,
|
|
136
|
+
reason: 'url is required',
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return ineligible when URL is null', () => {
|
|
141
|
+
const suggestion = {
|
|
142
|
+
getData: () => ({
|
|
143
|
+
url: null,
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const result = mapper.canDeploy(suggestion);
|
|
148
|
+
|
|
149
|
+
expect(result).to.deep.equal({
|
|
150
|
+
eligible: false,
|
|
151
|
+
reason: 'url is required',
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should return ineligible when URL is undefined', () => {
|
|
156
|
+
const suggestion = {
|
|
157
|
+
getData: () => ({
|
|
158
|
+
url: undefined,
|
|
159
|
+
}),
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = mapper.canDeploy(suggestion);
|
|
163
|
+
|
|
164
|
+
expect(result).to.deep.equal({
|
|
165
|
+
eligible: false,
|
|
166
|
+
reason: 'url is required',
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return ineligible when data is null', () => {
|
|
171
|
+
const suggestion = {
|
|
172
|
+
getData: () => null,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const result = mapper.canDeploy(suggestion);
|
|
176
|
+
|
|
177
|
+
expect(result).to.deep.equal({
|
|
178
|
+
eligible: false,
|
|
179
|
+
reason: 'url is required',
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should return ineligible when data is undefined', () => {
|
|
184
|
+
const suggestion = {
|
|
185
|
+
getData: () => undefined,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const result = mapper.canDeploy(suggestion);
|
|
189
|
+
|
|
190
|
+
expect(result).to.deep.equal({
|
|
191
|
+
eligible: false,
|
|
192
|
+
reason: 'url is required',
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should return eligible for various valid URL formats', () => {
|
|
197
|
+
const urls = [
|
|
198
|
+
'https://example.com',
|
|
199
|
+
'https://example.com/path',
|
|
200
|
+
'https://subdomain.example.com/path/to/page',
|
|
201
|
+
'https://example.com/path?query=value',
|
|
202
|
+
'https://example.com/path#hash',
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
urls.forEach((url) => {
|
|
206
|
+
const suggestion = {
|
|
207
|
+
getData: () => ({ url }),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const result = mapper.canDeploy(suggestion);
|
|
211
|
+
|
|
212
|
+
expect(result).to.deep.equal({ eligible: true });
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|