@adobe/helix-importer 1.0.0 → 1.2.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 +21 -0
- package/package.json +33 -17
- package/src/importer/PageImporter.js +23 -23
- package/src/index.js +4 -0
- package/src/utils/Blocks.js +96 -0
- package/src/utils/DOMUtils.js +37 -0
- package/test/utils/Blocks.spec.js +138 -0
- package/test/utils/DOMUtils.spec.js +170 -80
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [1.2.1](https://github.com/adobe/helix-importer/compare/v1.2.0...v1.2.1) (2022-01-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* export MemoryHandler ([#12](https://github.com/adobe/helix-importer/issues/12)) ([3a83a16](https://github.com/adobe/helix-importer/commit/3a83a1620b0eb4752698ee9b994e8b0fd7ce085f))
|
|
7
|
+
|
|
8
|
+
# [1.2.0](https://github.com/adobe/helix-importer/compare/v1.1.0...v1.2.0) (2022-01-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* export new Blocks helper ([#7](https://github.com/adobe/helix-importer/issues/7)) ([0d7bfa7](https://github.com/adobe/helix-importer/commit/0d7bfa7bd7a98a6fb19f6aec6f8e29be0549e53e))
|
|
14
|
+
|
|
15
|
+
# [1.1.0](https://github.com/adobe/helix-importer/compare/v1.0.0...v1.1.0) (2022-01-20)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* backport helpers from project into library ([#5](https://github.com/adobe/helix-importer/issues/5)) ([3c93126](https://github.com/adobe/helix-importer/commit/3c931269be98b1accfcc983d19bb7a5abdb6e36a))
|
|
21
|
+
|
|
1
22
|
# 1.0.0 (2022-01-19)
|
|
2
23
|
|
|
3
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-importer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Helix Importer tool: create md / docx from html",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -10,37 +10,53 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"lint": "eslint .",
|
|
13
|
-
"test": "mocha
|
|
14
|
-
"
|
|
13
|
+
"test": "c8 mocha",
|
|
14
|
+
"test-ci": "c8 mocha && codecov",
|
|
15
|
+
"semantic-release": "semantic-release",
|
|
16
|
+
"prepare": "npx husky install"
|
|
17
|
+
},
|
|
18
|
+
"mocha": {
|
|
19
|
+
"recursive": "true",
|
|
20
|
+
"spec": "test/**/*.spec.js",
|
|
21
|
+
"reporter": "mocha-multi-reporters",
|
|
22
|
+
"reporter-options": "configFile=.mocha-multi.json"
|
|
23
|
+
},
|
|
24
|
+
"lint-staged": {
|
|
25
|
+
"*.js": "eslint"
|
|
15
26
|
},
|
|
16
27
|
"devDependencies": {
|
|
17
28
|
"@adobe/eslint-config-helix": "1.3.2",
|
|
18
|
-
"@adobe/helix-docx2md": "1.0.
|
|
19
|
-
"@adobe/helix-mediahandler": "1.0.
|
|
29
|
+
"@adobe/helix-docx2md": "1.0.6",
|
|
30
|
+
"@adobe/helix-mediahandler": "1.0.2",
|
|
20
31
|
"@semantic-release/changelog": "6.0.1",
|
|
21
32
|
"@semantic-release/exec": "6.0.3",
|
|
22
33
|
"@semantic-release/git": "10.0.1",
|
|
23
|
-
"eslint": "8.
|
|
34
|
+
"eslint": "8.7.0",
|
|
24
35
|
"eslint-plugin-header": "3.1.1",
|
|
25
36
|
"eslint-plugin-import": "2.25.4",
|
|
26
|
-
"mocha": "9.
|
|
27
|
-
"mock-fs": "
|
|
28
|
-
"
|
|
37
|
+
"mocha": "9.2.0",
|
|
38
|
+
"mock-fs": "5.1.2",
|
|
39
|
+
"c8": "7.11.0",
|
|
40
|
+
"codecov": "3.8.3",
|
|
41
|
+
"husky": "7.0.4",
|
|
42
|
+
"lint-staged": "12.3.1",
|
|
43
|
+
"mocha-multi-reporters": "1.5.1",
|
|
44
|
+
"semantic-release": "19.0.2"
|
|
29
45
|
},
|
|
30
46
|
"author": "",
|
|
31
47
|
"license": "Apache-2.0",
|
|
32
48
|
"dependencies": {
|
|
33
|
-
"@adobe/helix-md2docx": "1.
|
|
49
|
+
"@adobe/helix-md2docx": "1.2.2",
|
|
34
50
|
"form-data": "4.0.0",
|
|
35
|
-
"fs-extra": "
|
|
51
|
+
"fs-extra": "10.0.0",
|
|
36
52
|
"hast-util-to-html": "8.0.3",
|
|
37
53
|
"hast-util-to-mdast": "8.3.0",
|
|
38
|
-
"jsdom": "
|
|
39
|
-
"node-fetch": "2.
|
|
40
|
-
"rehype-parse": "
|
|
41
|
-
"rehype-remark": "
|
|
42
|
-
"remark-stringify": "
|
|
54
|
+
"jsdom": "19.0.0",
|
|
55
|
+
"node-fetch": "3.2.0",
|
|
56
|
+
"rehype-parse": "8.0.3",
|
|
57
|
+
"rehype-remark": "9.1.2",
|
|
58
|
+
"remark-stringify": "10.0.2",
|
|
43
59
|
"sanitize-filename": "1.6.3",
|
|
44
|
-
"unified": "
|
|
60
|
+
"unified": "10.1.1"
|
|
45
61
|
}
|
|
46
62
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { JSDOM } from 'jsdom';
|
|
16
16
|
|
|
17
17
|
import path from 'path';
|
|
18
|
-
import unified from 'unified';
|
|
18
|
+
import { unified } from 'unified';
|
|
19
19
|
import parse from 'rehype-parse';
|
|
20
20
|
import { toHtml } from 'hast-util-to-html';
|
|
21
21
|
import rehype2remark from 'rehype-remark';
|
|
@@ -50,7 +50,7 @@ export default class PageImporter {
|
|
|
50
50
|
const { name } = resource;
|
|
51
51
|
const { directory } = resource;
|
|
52
52
|
const sanitizedName = FileUtils.sanitizeFilename(name);
|
|
53
|
-
this.logger.log(`Computing
|
|
53
|
+
this.logger.log(`Computing Markdown for ${directory}/${sanitizedName}`);
|
|
54
54
|
|
|
55
55
|
const processor = unified()
|
|
56
56
|
.use(parse, { emitParseErrors: true })
|
|
@@ -73,36 +73,36 @@ export default class PageImporter {
|
|
|
73
73
|
.use(() => {
|
|
74
74
|
// use custom tag and rendering because text is always encoded by default
|
|
75
75
|
// we need the raw url
|
|
76
|
-
processor.Compiler.prototype.visitors.hlxembed = (node) => node.value;
|
|
76
|
+
// processor.Compiler.prototype.visitors.hlxembed = (node) => node.value;
|
|
77
77
|
})
|
|
78
78
|
.use(() => {
|
|
79
|
-
processor.Compiler.prototype.visitors.table = (node) => node.value;
|
|
79
|
+
// processor.Compiler.prototype.visitors.table = (node) => node.value;
|
|
80
80
|
})
|
|
81
81
|
.use(() => {
|
|
82
|
-
processor.Compiler.prototype.visitors.u = (node) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
};
|
|
82
|
+
// processor.Compiler.prototype.visitors.u = (node) => {
|
|
83
|
+
// // u handling: remove the u is the first element is a link
|
|
84
|
+
// if (node.children && node.children.length > 0) {
|
|
85
|
+
// const children = node.children.map((child) => processor.stringify(child));
|
|
86
|
+
// if (node.children[0].type === 'link') {
|
|
87
|
+
// // first element in the <u> is a link: remove the <u> - unsupported case
|
|
88
|
+
// return `${children.join()}`;
|
|
89
|
+
// }
|
|
90
|
+
// return `<u>${children.join()}</u>`;
|
|
91
|
+
// }
|
|
92
|
+
// return '';
|
|
93
|
+
// };
|
|
94
94
|
})
|
|
95
95
|
.use(() => {
|
|
96
|
-
const originalEmphasis = processor.Compiler.prototype.visitors.emphasis;
|
|
97
|
-
processor.Compiler.prototype.visitors.emphasis = (node) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
};
|
|
96
|
+
// const originalEmphasis = processor.Compiler.prototype.visitors.emphasis;
|
|
97
|
+
// processor.Compiler.prototype.visitors.emphasis = (node) => {
|
|
98
|
+
// // @ts-ignore
|
|
99
|
+
// const ori = originalEmphasis.apply(processor.Compiler(), [node]);
|
|
100
|
+
// return ori;
|
|
101
|
+
// };
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
const file = await processor.process(resource.document.innerHTML);
|
|
105
|
-
let contents = file
|
|
105
|
+
let contents = String(file);
|
|
106
106
|
|
|
107
107
|
// process image links
|
|
108
108
|
const { document } = resource;
|
package/src/index.js
CHANGED
|
@@ -17,7 +17,9 @@ import PageImporterParams from './importer/PageImporterParams.js';
|
|
|
17
17
|
import PageImporterResource from './importer/PageImporterResource.js';
|
|
18
18
|
|
|
19
19
|
import FSHandler from './storage/FSHandler.js';
|
|
20
|
+
import MemoryHandler from './storage/MemoryHandler.js';
|
|
20
21
|
|
|
22
|
+
import Blocks from './utils/Blocks.js';
|
|
21
23
|
import CSV from './utils/CSV.js';
|
|
22
24
|
import DOMUtils from './utils/DOMUtils.js';
|
|
23
25
|
import FileUtils from './utils/FileUtils.js';
|
|
@@ -35,6 +37,8 @@ export {
|
|
|
35
37
|
PageImporterParams,
|
|
36
38
|
PageImporterResource,
|
|
37
39
|
FSHandler,
|
|
40
|
+
MemoryHandler,
|
|
41
|
+
Blocks,
|
|
38
42
|
CSV,
|
|
39
43
|
DOMUtils,
|
|
40
44
|
FileUtils,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 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
|
+
import DOMUtils from './DOMUtils.js';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_COLSPAN = 2;
|
|
15
|
+
|
|
16
|
+
export default class Blocks {
|
|
17
|
+
static getMetadataBlock(document, metadata) {
|
|
18
|
+
const table = document.createElement('table');
|
|
19
|
+
|
|
20
|
+
let row = document.createElement('tr');
|
|
21
|
+
table.append(row);
|
|
22
|
+
|
|
23
|
+
const hCell = document.createElement('th');
|
|
24
|
+
row.append(hCell);
|
|
25
|
+
|
|
26
|
+
hCell.innerHTML = 'Metadata';
|
|
27
|
+
hCell.setAttribute('colspan', DEFAULT_COLSPAN);
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line no-restricted-syntax, guard-for-in
|
|
30
|
+
for (const key in metadata) {
|
|
31
|
+
row = document.createElement('tr');
|
|
32
|
+
table.append(row);
|
|
33
|
+
const keyCell = document.createElement('td');
|
|
34
|
+
row.append(keyCell);
|
|
35
|
+
keyCell.textContent = key;
|
|
36
|
+
const valueCell = document.createElement('td');
|
|
37
|
+
row.append(valueCell);
|
|
38
|
+
const value = metadata[key];
|
|
39
|
+
if (value) {
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
let list = '';
|
|
42
|
+
value.forEach((v) => {
|
|
43
|
+
// p tags in table are stripped out
|
|
44
|
+
// list must receive special hlx_replaceTag command to be post-processed
|
|
45
|
+
list += `hlx_replaceTag(p)${v}hlx_replaceTag(/p)`;
|
|
46
|
+
});
|
|
47
|
+
valueCell.textContent = list;
|
|
48
|
+
} else if (typeof value === 'string') {
|
|
49
|
+
valueCell.textContent = value;
|
|
50
|
+
} else {
|
|
51
|
+
valueCell.append(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return table;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static computeBlockName(str) {
|
|
60
|
+
return str
|
|
61
|
+
.replace(/-/g, ' ')
|
|
62
|
+
.replace(/\s(.)/g, (s) => s.toUpperCase())
|
|
63
|
+
.replace(/^(.)/g, (s) => s.toUpperCase());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static convertBlocksToTables(element, document) {
|
|
67
|
+
element.querySelectorAll('main > div:nth-child(4) > div[class]').forEach((block) => {
|
|
68
|
+
const name = Blocks.computeBlockName(block.className);
|
|
69
|
+
const data = [[name]];
|
|
70
|
+
const divs = block.querySelectorAll(':scope > div');
|
|
71
|
+
if (divs) {
|
|
72
|
+
divs.forEach((div) => {
|
|
73
|
+
const subDivs = div.querySelectorAll(':scope > div');
|
|
74
|
+
if (subDivs && subDivs.length > 0) {
|
|
75
|
+
const rowData = [];
|
|
76
|
+
subDivs.forEach((cell) => {
|
|
77
|
+
if (cell.nodeName === 'DIV') {
|
|
78
|
+
// remove transparent divs
|
|
79
|
+
const cellContent = [];
|
|
80
|
+
Array.from(cell.childNodes).forEach((c) => cellContent.push(c));
|
|
81
|
+
rowData.push(cellContent);
|
|
82
|
+
} else {
|
|
83
|
+
rowData.push(cell);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
data.push(rowData);
|
|
87
|
+
} else {
|
|
88
|
+
data.push([div.innerHTML]);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const table = DOMUtils.createTable(data, document);
|
|
93
|
+
block.replaceWith(table);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/utils/DOMUtils.js
CHANGED
|
@@ -162,6 +162,31 @@ export default class DOMUtils {
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
static createTable(data, document) {
|
|
166
|
+
const table = document.createElement('table');
|
|
167
|
+
|
|
168
|
+
data.forEach((row, index) => {
|
|
169
|
+
const tr = document.createElement('tr');
|
|
170
|
+
|
|
171
|
+
row.forEach((cell) => {
|
|
172
|
+
const t = document.createElement(index === 0 ? 'th' : 'td');
|
|
173
|
+
if (typeof cell === 'string') {
|
|
174
|
+
t.innerHTML = cell;
|
|
175
|
+
} else if (Array.isArray(cell)) {
|
|
176
|
+
cell.forEach((c) => {
|
|
177
|
+
t.append(c);
|
|
178
|
+
});
|
|
179
|
+
} else {
|
|
180
|
+
t.append(cell);
|
|
181
|
+
}
|
|
182
|
+
tr.appendChild(t);
|
|
183
|
+
});
|
|
184
|
+
table.appendChild(tr);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return table;
|
|
188
|
+
}
|
|
189
|
+
|
|
165
190
|
static generateEmbed(url) {
|
|
166
191
|
return JSDOM.fragment(`<table><tr><th>Embed</th></tr><tr><td><a href="${url}">${url}</a></td></tr></table>`);
|
|
167
192
|
}
|
|
@@ -204,4 +229,16 @@ export default class DOMUtils {
|
|
|
204
229
|
}
|
|
205
230
|
});
|
|
206
231
|
}
|
|
232
|
+
|
|
233
|
+
static replaceBackgroundByImg(element, document) {
|
|
234
|
+
const url = element?.style?.['background-image'];
|
|
235
|
+
if (url) {
|
|
236
|
+
const src = url.replace(/url\(/gm, '').replace(/'/gm, '').replace(/\)/gm, '');
|
|
237
|
+
const img = document.createElement('img');
|
|
238
|
+
img.src = src;
|
|
239
|
+
element.replaceWith(img);
|
|
240
|
+
return img;
|
|
241
|
+
}
|
|
242
|
+
return element;
|
|
243
|
+
}
|
|
207
244
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010 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 { strictEqual } from 'assert';
|
|
14
|
+
import { describe, it } from 'mocha';
|
|
15
|
+
|
|
16
|
+
import { JSDOM } from 'jsdom';
|
|
17
|
+
|
|
18
|
+
import Blocks from '../../src/utils/Blocks.js';
|
|
19
|
+
|
|
20
|
+
describe('Blocks#computeBlockName tests', () => {
|
|
21
|
+
it('computeBlockName - can convert', () => {
|
|
22
|
+
strictEqual(Blocks.computeBlockName('promotion'), 'Promotion');
|
|
23
|
+
strictEqual(Blocks.computeBlockName('hero-animation'), 'Hero Animation');
|
|
24
|
+
strictEqual(Blocks.computeBlockName('how-to-carousel'), 'How To Carousel');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const trim = (html) => html
|
|
29
|
+
.replace(/^\s*/gm, '')
|
|
30
|
+
.replace(/\s*$/gm, '')
|
|
31
|
+
.replace(/\n/gm, '')
|
|
32
|
+
.replace(/\/>\s*</gm, '/><');
|
|
33
|
+
|
|
34
|
+
describe('Blocks#convertBlocksToTables tests', () => {
|
|
35
|
+
const test = (input, expected) => {
|
|
36
|
+
const { document } = (new JSDOM(input)).window;
|
|
37
|
+
Blocks.convertBlocksToTables(document, document);
|
|
38
|
+
strictEqual(trim(document.body.innerHTML), trim(expected));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const div = '<div></div>'; // ignored div for the tests
|
|
42
|
+
|
|
43
|
+
it('convertBlocksToTables basic block', () => {
|
|
44
|
+
test(
|
|
45
|
+
`<main>${div}${div}${div}
|
|
46
|
+
<div>
|
|
47
|
+
<div class="a-block">
|
|
48
|
+
<div>cell11</div>
|
|
49
|
+
<div>cell21</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</main>`,
|
|
53
|
+
`<main>${div}${div}${div}
|
|
54
|
+
<div>
|
|
55
|
+
<table>
|
|
56
|
+
<tr><th>A Block</th></tr>
|
|
57
|
+
<tr><td>cell11</td></tr>
|
|
58
|
+
<tr><td>cell21</td></tr>
|
|
59
|
+
</table>
|
|
60
|
+
</div>
|
|
61
|
+
</main>`,
|
|
62
|
+
);
|
|
63
|
+
test(
|
|
64
|
+
`<main>${div}${div}${div}
|
|
65
|
+
<div>
|
|
66
|
+
<div class="another-block">
|
|
67
|
+
<div>
|
|
68
|
+
<div>cell11</div>
|
|
69
|
+
<div>cell12</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div>
|
|
72
|
+
<div>cell21</div>
|
|
73
|
+
<div>cell22</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div>
|
|
76
|
+
<div><img src="https://www.sample.com/image.jpeg"></div>
|
|
77
|
+
<div><a href="https://www.sample.com/">A link</a></div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</main>`,
|
|
82
|
+
`<main>${div}${div}${div}
|
|
83
|
+
<div>
|
|
84
|
+
<table>
|
|
85
|
+
<tr><th>Another Block</th></tr>
|
|
86
|
+
<tr><td>cell11</td><td>cell12</td></tr>
|
|
87
|
+
<tr><td>cell21</td><td>cell22</td></tr>
|
|
88
|
+
<tr><td><img src="https://www.sample.com/image.jpeg"></td><td><a href="https://www.sample.com/">A link</a></td></tr>
|
|
89
|
+
</table>
|
|
90
|
+
</div>
|
|
91
|
+
</main>`,
|
|
92
|
+
);
|
|
93
|
+
test(
|
|
94
|
+
`<main>${div}${div}${div}
|
|
95
|
+
<div>
|
|
96
|
+
<div class="promotion">
|
|
97
|
+
<div>
|
|
98
|
+
<div><a href="https://blog.adobe.com/en/promotions/doc-cloud-education.html">https://blog.adobe.com/en/promotions/doc-cloud-education.html</a></div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</main>`,
|
|
102
|
+
`<main>${div}${div}${div}
|
|
103
|
+
<div>
|
|
104
|
+
<table>
|
|
105
|
+
<tr><th>Promotion</th></tr>
|
|
106
|
+
<tr><td><a href="https://blog.adobe.com/en/promotions/doc-cloud-education.html">https://blog.adobe.com/en/promotions/doc-cloud-education.html</a></td></tr>
|
|
107
|
+
</table>
|
|
108
|
+
</div>
|
|
109
|
+
</main>`,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('Blocks#getMetadataBlock tests', () => {
|
|
115
|
+
const test = (metadata, expected) => {
|
|
116
|
+
const { document } = (new JSDOM()).window;
|
|
117
|
+
const table = Blocks.getMetadataBlock(document, metadata);
|
|
118
|
+
strictEqual(trim(table.outerHTML), trim(expected));
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
it('getMetadataBlock string meta', () => {
|
|
122
|
+
test({ title: 'Some title' }, '<table><tr><th colspan="2">Metadata</th></tr><tr><td>title</td><td>Some title</td></tr></table>');
|
|
123
|
+
test({ Author: 'Name of the author', 'Creation Date': '2022/01/01' }, '<table><tr><th colspan="2">Metadata</th></tr><tr><td>Author</td><td>Name of the author</td></tr><tr><td>Creation Date</td><td>2022/01/01</td></tr></table>');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('getMetadataBlock element meta', () => {
|
|
127
|
+
const { document } = (new JSDOM()).window;
|
|
128
|
+
const img = document.createElement('img');
|
|
129
|
+
img.src = 'https://www.sample.com/image.jpeg';
|
|
130
|
+
img.title = 'Image title';
|
|
131
|
+
img.alt = 'Image alt';
|
|
132
|
+
test({ title: 'Some title', Image: img }, '<table><tr><th colspan="2">Metadata</th></tr><tr><td>title</td><td>Some title</td></tr><tr><td>Image</td><td><img src="https://www.sample.com/image.jpeg" title="Image title" alt="Image alt"></td></tr></table>');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('getMetadataBlock lists', () => {
|
|
136
|
+
test({ title: 'Some title', Tags: ['Creative', 'Experience Cloud', 'Photography'] }, '<table><tr><th colspan="2">Metadata</th></tr><tr><td>title</td><td>Some title</td></tr><tr><td>Tags</td><td>hlx_replaceTag(p)Creativehlx_replaceTag(/p)hlx_replaceTag(p)Experience Cloudhlx_replaceTag(/p)hlx_replaceTag(p)Photographyhlx_replaceTag(/p)</td></tr></table>');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -170,101 +170,191 @@ describe('DOMUtils#removeCommments tests', () => {
|
|
|
170
170
|
test('<p><!-- useless comment \n multiline \n multiline --></p>', '<p></p>');
|
|
171
171
|
test('<!-- useless comment --><p>The content stays</p><!-- another useless comment with \n line break -->', '<p>The content stays</p>');
|
|
172
172
|
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('DOMUtils#removeSpans tests', () => {
|
|
176
|
+
const test = (input, expected) => {
|
|
177
|
+
const { document } = (new JSDOM(input)).window;
|
|
178
|
+
DOMUtils.removeSpans(document);
|
|
179
|
+
strictEqual(document.body.innerHTML, expected);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
it('remove spans', () => {
|
|
183
|
+
// do nothing
|
|
184
|
+
test('<p></p>', '<p></p>');
|
|
185
|
+
|
|
186
|
+
// remove spans
|
|
187
|
+
test('<p><span></span></p>', '<p></p>');
|
|
188
|
+
test('<p><span>Content should remain</span> the same</p>', '<p>Content should remain the same</p>');
|
|
189
|
+
test('<p>Spacing<span> should</span> remain the same</p>', '<p>Spacing should remain the same</p>');
|
|
190
|
+
test('<p>Spacing<span> should</span> remain the <span>same even</span> with<span> multiple spans</span></p>', '<p>Spacing should remain the same even with multiple spans</p>');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('DOMUtils#removeNoscripts tests', () => {
|
|
195
|
+
const test = (input, expected) => {
|
|
196
|
+
strictEqual(DOMUtils.removeNoscripts(input), expected);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
it('remove no scripts', () => {
|
|
200
|
+
// do nothing
|
|
201
|
+
test('<p>Some content</p>', '<p>Some content</p>');
|
|
202
|
+
|
|
203
|
+
// remove noscript
|
|
204
|
+
test('<body>Do A<noscript>Do Z</noscript></body>', '<body>Do A</body>');
|
|
205
|
+
test('<body>Do A<noscript>Do Z</noscript> but also do B<noscript>and X</noscript></body>', '<body>Do A but also do B</body>');
|
|
206
|
+
test('<body>Do A<noscript>Do Z\n Do X</noscript> but also do B<noscript>and W \n and Y</noscript></body>', '<body>Do A but also do B</body>');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('DOMUtils#replaceByCaptions tests', () => {
|
|
211
|
+
const test = (input, selectors, expected) => {
|
|
212
|
+
const { document } = (new JSDOM(input)).window;
|
|
213
|
+
DOMUtils.replaceByCaptions(document, selectors);
|
|
214
|
+
strictEqual(document.body.innerHTML, expected);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
it('replace by captions', () => {
|
|
218
|
+
// do nothing
|
|
219
|
+
test('<p>Some content</p>', ['i'], '<p>Some content</p>');
|
|
173
220
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const { document } = (new JSDOM(input)).window;
|
|
177
|
-
DOMUtils.removeSpans(document);
|
|
178
|
-
strictEqual(document.body.innerHTML, expected);
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
it('remove spans', () => {
|
|
182
|
-
// do nothing
|
|
183
|
-
test('<p></p>', '<p></p>');
|
|
184
|
-
|
|
185
|
-
// remove spans
|
|
186
|
-
test('<p><span></span></p>', '<p></p>');
|
|
187
|
-
test('<p><span>Content should remain</span> the same</p>', '<p>Content should remain the same</p>');
|
|
188
|
-
test('<p>Spacing<span> should</span> remain the same</p>', '<p>Spacing should remain the same</p>');
|
|
189
|
-
test('<p>Spacing<span> should</span> remain the <span>same even</span> with<span> multiple spans</span></p>', '<p>Spacing should remain the same even with multiple spans</p>');
|
|
190
|
-
});
|
|
221
|
+
test('<p>Some content</p><img src="image.png"><figcaption>Copyright to author.</figcaption><p>Some more content</p>', ['figcaption'], '<p>Some content</p><img src="image.png"><p><em>Copyright to author.</em></p><p>Some more content</p>');
|
|
222
|
+
test('<p>Some content</p><img src="image.png"><figcaption>Copyright to author.</figcaption><div class="custom-caption">Another copyright to author.</div><p>Some more content</p>', ['figcaption', '.custom-caption'], '<p>Some content</p><img src="image.png"><p><em>Copyright to author.</em></p><p><em>Another copyright to author.</em></p><p>Some more content</p>');
|
|
191
223
|
});
|
|
224
|
+
});
|
|
192
225
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
226
|
+
describe('DOMUtils#replaceEmbeds', () => {
|
|
227
|
+
const test = (input, expected) => {
|
|
228
|
+
const { document } = (new JSDOM(input)).window;
|
|
229
|
+
DOMUtils.replaceEmbeds(document);
|
|
230
|
+
strictEqual(document.body.innerHTML, expected);
|
|
231
|
+
};
|
|
197
232
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
233
|
+
it('replace embeds', () => {
|
|
234
|
+
// do nothing
|
|
235
|
+
test('<p>Some content</p>', '<p>Some content</p>');
|
|
236
|
+
});
|
|
201
237
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
});
|
|
238
|
+
it('replace embeds deals with iframes', () => {
|
|
239
|
+
test('<p>Some content</p><iframe src="https://www.youtube.com/xyz"></iframe>', '<p>Some content</p><table><tbody><tr><th>Embed</th></tr><tr><td><a href="https://www.youtube.com/xyz">https://www.youtube.com/xyz</a></td></tr></tbody></table>');
|
|
240
|
+
test('<p>Some content</p><iframe data-src="https://www.youtube.com/xyz"></iframe>', '<p>Some content</p><table><tbody><tr><th>Embed</th></tr><tr><td><a href="https://www.youtube.com/xyz">https://www.youtube.com/xyz</a></td></tr></tbody></table>');
|
|
241
|
+
test('<p>Some content</p><iframe data-src="https://www.youtube.com/data-src" src="https://www.youtube.com/src"></iframe>', '<p>Some content</p><table><tbody><tr><th>Embed</th></tr><tr><td><a href="https://www.youtube.com/data-src">https://www.youtube.com/data-src</a></td></tr></tbody></table>');
|
|
207
242
|
});
|
|
208
243
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
DOMUtils.replaceByCaptions(document, selectors);
|
|
213
|
-
strictEqual(document.body.innerHTML, expected);
|
|
214
|
-
};
|
|
244
|
+
it('replace embeds deals video tag / content blocks', () => {
|
|
245
|
+
// Video block
|
|
246
|
+
test('<p>Some content</p><video src="https://www.server.com/video.mp4"></video>', '<p>Some content</p><table><tbody><tr><th>Video</th></tr><tr><td><video src="https://www.server.com/video.mp4"></video></td></tr></tbody></table>');
|
|
215
247
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
248
|
+
// Animation block
|
|
249
|
+
test('<p>Some content</p><video src="https://www.server.com/video.mp4" autoplay="true"></video>', '<p>Some content</p><table><tbody><tr><th>Animation</th></tr><tr><td><video src="https://www.server.com/video.mp4" autoplay="true"></video></td></tr></tbody></table>');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('DOMUtils#encodeImagesForTable', () => {
|
|
254
|
+
const test = (input, expected) => {
|
|
255
|
+
const { document } = (new JSDOM(input)).window;
|
|
256
|
+
DOMUtils.encodeImagesForTable(document);
|
|
257
|
+
strictEqual(document.body.innerHTML, expected);
|
|
258
|
+
};
|
|
219
259
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
260
|
+
it('encode images for table', () => {
|
|
261
|
+
// do nothing
|
|
262
|
+
test('<p>Some content</p>', '<p>Some content</p>');
|
|
263
|
+
|
|
264
|
+
// encode pipe if image is in table
|
|
265
|
+
test('<p>Some content</p><table><tr><td><img src="https://www.server.com/image.jpg" title="Some title | which contains a pipe"></td></tr></table>', '<p>Some content</p><table><tbody><tr><td><img src="https://www.server.com/image.jpg" title="Some title \\| which contains a pipe"></td></tr></tbody></table>');
|
|
266
|
+
|
|
267
|
+
// don't encode pipe if image is not in a table
|
|
268
|
+
test('<p>Some content</p><img src="https://www.server.com/image.jpg" title="Some title | which contains a pipe">', '<p>Some content</p><img src="https://www.server.com/image.jpg" title="Some title | which contains a pipe">');
|
|
223
269
|
});
|
|
270
|
+
});
|
|
224
271
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
272
|
+
describe('DOM#createTable tests', () => {
|
|
273
|
+
const test = (data, expected) => {
|
|
274
|
+
const { document } = (new JSDOM()).window;
|
|
275
|
+
const table = DOMUtils.createTable(data, document);
|
|
276
|
+
strictEqual(table.outerHTML, expected);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
it('createTable - basic tables', () => {
|
|
280
|
+
test(
|
|
281
|
+
[[]],
|
|
282
|
+
'<table><tr></tr></table>',
|
|
283
|
+
);
|
|
284
|
+
test(
|
|
285
|
+
[['header']],
|
|
286
|
+
'<table><tr><th>header</th></tr></table>',
|
|
287
|
+
);
|
|
288
|
+
test(
|
|
289
|
+
[['header'], ['cell']],
|
|
290
|
+
'<table><tr><th>header</th></tr><tr><td>cell</td></tr></table>',
|
|
291
|
+
);
|
|
292
|
+
test(
|
|
293
|
+
[['header1', 'header2'], ['cell11', 'cell12'], ['cell21', 'cell22']],
|
|
294
|
+
'<table><tr><th>header1</th><th>header2</th></tr><tr><td>cell11</td><td>cell12</td></tr><tr><td>cell21</td><td>cell22</td></tr></table>',
|
|
295
|
+
);
|
|
296
|
+
// TODO deal with colspan ?
|
|
297
|
+
test(
|
|
298
|
+
[['header1'], ['cell11', 'cell12'], ['cell21', 'cell22']],
|
|
299
|
+
'<table><tr><th>header1</th></tr><tr><td>cell11</td><td>cell12</td></tr><tr><td>cell21</td><td>cell22</td></tr></table>',
|
|
300
|
+
);
|
|
250
301
|
});
|
|
251
302
|
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
303
|
+
it('createTable - deals with Elements', () => {
|
|
304
|
+
const { document } = (new JSDOM()).window;
|
|
305
|
+
|
|
306
|
+
const img = document.createElement('img');
|
|
307
|
+
img.src = 'https://www.sample.com/image.jpeg';
|
|
308
|
+
|
|
309
|
+
const a = document.createElement('a');
|
|
310
|
+
a.href = 'https://www.sample.com/';
|
|
311
|
+
|
|
312
|
+
test(
|
|
313
|
+
[['header'], [img]],
|
|
314
|
+
'<table><tr><th>header</th></tr><tr><td><img src="https://www.sample.com/image.jpeg"></td></tr></table>',
|
|
315
|
+
);
|
|
316
|
+
test(
|
|
317
|
+
[['header'], [img, a, 'some text']],
|
|
318
|
+
'<table><tr><th>header</th></tr><tr><td><img src="https://www.sample.com/image.jpeg"></td><td><a href="https://www.sample.com/"></a></td><td>some text</td></tr></table>',
|
|
319
|
+
);
|
|
320
|
+
test(
|
|
321
|
+
[['header'], [[img, a, 'some text']]],
|
|
322
|
+
'<table><tr><th>header</th></tr><tr><td><img src="https://www.sample.com/image.jpeg"><a href="https://www.sample.com/"></a>some text</td></tr></table>',
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('DOMUtils#replaceBackgroundByImg', () => {
|
|
328
|
+
const createElement = (tag, attrs, styles, innerHTML) => {
|
|
329
|
+
const { document } = (new JSDOM()).window;
|
|
330
|
+
const element = document.createElement(tag);
|
|
331
|
+
// eslint-disable-next-line no-restricted-syntax, guard-for-in
|
|
332
|
+
for (const a in attrs) {
|
|
333
|
+
element.setAttribute(a, attrs[a]);
|
|
334
|
+
}
|
|
335
|
+
// eslint-disable-next-line no-restricted-syntax, guard-for-in
|
|
336
|
+
for (const p in styles) {
|
|
337
|
+
element.style[p] = styles[p];
|
|
338
|
+
}
|
|
339
|
+
element.innerHTML = innerHTML;
|
|
340
|
+
return element;
|
|
341
|
+
};
|
|
258
342
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
343
|
+
const test = (element, expected) => {
|
|
344
|
+
const { document } = (new JSDOM()).window;
|
|
345
|
+
const ret = DOMUtils.replaceBackgroundByImg(element, document);
|
|
346
|
+
strictEqual(ret.outerHTML, expected);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
it('no background-image style', () => {
|
|
350
|
+
test(createElement('p', {}, {}, 'Some content'), '<p>Some content</p>');
|
|
262
351
|
|
|
263
|
-
|
|
264
|
-
|
|
352
|
+
test(createElement('img', { src: 'https://www.server.com/image.jpg', title: 'Some title' }, {}, ''), '<img src="https://www.server.com/image.jpg" title="Some title">');
|
|
353
|
+
});
|
|
265
354
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
});
|
|
355
|
+
it('with background-image style', () => {
|
|
356
|
+
test(createElement('p', {}, { 'background-image': 'url(https://www.server.com/image.jpg)' }, 'Some content'), '<img src="https://www.server.com/image.jpg">');
|
|
357
|
+
test(createElement('p', { class: 'class-is-lost' }, { 'background-image': 'url("https://www.server.com/image.jpg")' }, 'Some content'), '<img src="https://www.server.com/image.jpg">');
|
|
358
|
+
test(createElement('div', { class: 'class-is-lost' }, { 'background-image': 'url("https://www.server.com/image.jpg")' }, '<div><div>Some divs</div><div>More divs</div></div>'), '<img src="https://www.server.com/image.jpg">');
|
|
269
359
|
});
|
|
270
360
|
});
|