@elisra-devops/docgen-skins 0.23.0 → 0.24.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/.github/workflows/ci.yml +18 -9
- package/bin/index.d.ts +5 -1
- package/bin/index.js +43 -16
- package/bin/index.js.map +1 -1
- package/bin/models/json/JSONRun.js.map +1 -1
- package/bin/models/json/file/JSONFile.js.map +1 -1
- package/bin/models/json/paragraph/JSONParagraph.js.map +1 -1
- package/bin/models/json/paragraph/JSONRichTextParagraph.js.map +1 -1
- package/bin/models/json/paragraph/JSONTestSuiteHeaderParagraph.js.map +1 -1
- package/bin/models/json/table/JSONTable.d.ts +2 -1
- package/bin/models/json/table/JSONTable.js +20 -6
- package/bin/models/json/table/JSONTable.js.map +1 -1
- package/bin/models/json/table/JSONTableCell.js.map +1 -1
- package/bin/models/json/table/JSONTableRow.js.map +1 -1
- package/bin/services/logger.js +17 -7
- package/bin/services/logger.js.map +1 -1
- package/bin/test/JSONFile.test.js +48 -0
- package/bin/test/JSONFile.test.js.map +1 -0
- package/bin/test/JSONRun.test.d.ts +1 -0
- package/bin/test/JSONRun.test.js +93 -0
- package/bin/test/JSONRun.test.js.map +1 -0
- package/bin/test/JSONTable.test.d.ts +1 -0
- package/bin/test/JSONTable.test.js +79 -0
- package/bin/test/JSONTable.test.js.map +1 -0
- package/bin/test/JSONTableCell.test.d.ts +1 -0
- package/bin/test/JSONTableCell.test.js +129 -0
- package/bin/test/JSONTableCell.test.js.map +1 -0
- package/bin/test/JSONTableRow.test.d.ts +1 -0
- package/bin/test/JSONTableRow.test.js +40 -0
- package/bin/test/JSONTableRow.test.js.map +1 -0
- package/bin/test/JSONTestSuiteHeaderParagraph.test.d.ts +1 -0
- package/bin/test/JSONTestSuiteHeaderParagraph.test.js +39 -0
- package/bin/test/JSONTestSuiteHeaderParagraph.test.js.map +1 -0
- package/bin/test/SkinsEdgeCases.test.d.ts +1 -0
- package/bin/test/SkinsEdgeCases.test.js +399 -0
- package/bin/test/SkinsEdgeCases.test.js.map +1 -0
- package/bin/test/integration/skins.integration.test.d.ts +1 -0
- package/bin/test/integration/skins.integration.test.js +492 -0
- package/bin/test/integration/skins.integration.test.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +45 -10
- package/src/models/json/table/JSONTable.ts +19 -3
- package/src/test/JSONFile.test.ts +59 -0
- package/src/test/JSONRun.test.ts +118 -0
- package/src/test/JSONTable.test.ts +126 -0
- package/src/test/JSONTableCell.test.ts +150 -0
- package/src/test/JSONTableRow.test.ts +42 -0
- package/src/test/JSONTestSuiteHeaderParagraph.test.ts +40 -0
- package/src/test/SkinsEdgeCases.test.ts +530 -0
- package/src/test/integration/skins.integration.test.ts +608 -0
- package/tsconfig.json +2 -2
- package/bin/test/skins.test.js +0 -126
- package/bin/test/skins.test.js.map +0 -1
- package/src/test/skins.test.ts +0 -189
- /package/bin/test/{skins.test.d.ts → JSONFile.test.d.ts} +0 -0
|
@@ -12,6 +12,7 @@ import JSONTableRow from './JSONTableRow';
|
|
|
12
12
|
export default class JSONTable {
|
|
13
13
|
tableTemplate: Table;
|
|
14
14
|
tableStyles: StyleOptions;
|
|
15
|
+
private readonly enableVerticalMerge: boolean;
|
|
15
16
|
|
|
16
17
|
constructor(
|
|
17
18
|
data: WIQueryResults,
|
|
@@ -29,8 +30,10 @@ export default class JSONTable {
|
|
|
29
30
|
shading?: Shading;
|
|
30
31
|
leftShading?: Shading;
|
|
31
32
|
rightShading?: Shading;
|
|
32
|
-
}
|
|
33
|
+
},
|
|
34
|
+
enableVerticalMerge: boolean = false
|
|
33
35
|
) {
|
|
36
|
+
this.enableVerticalMerge = enableVerticalMerge;
|
|
34
37
|
this.tableStyles = tableStyles;
|
|
35
38
|
this.tableTemplate = {
|
|
36
39
|
type: 'table',
|
|
@@ -106,8 +109,10 @@ export default class JSONTable {
|
|
|
106
109
|
rows.push(row.getRow());
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
if (this.enableVerticalMerge) {
|
|
113
|
+
// Apply vertical merging for grouped source cells (first row shows value; subsequent are empty)
|
|
114
|
+
this.applyVerticalMerges(rows);
|
|
115
|
+
}
|
|
111
116
|
|
|
112
117
|
return rows;
|
|
113
118
|
} //generateJsonRows
|
|
@@ -179,6 +184,17 @@ export default class JSONTable {
|
|
|
179
184
|
|
|
180
185
|
const getCellText = (cell: any): string => {
|
|
181
186
|
try {
|
|
187
|
+
const htmlRaw = String(cell?.Html?.Html || '').trim();
|
|
188
|
+
if (htmlRaw) {
|
|
189
|
+
const htmlText = htmlRaw
|
|
190
|
+
.replace(/<[^>]*>/g, ' ')
|
|
191
|
+
.replace(/ /gi, ' ')
|
|
192
|
+
.replace(/\s+/g, ' ')
|
|
193
|
+
.trim();
|
|
194
|
+
if (htmlText) {
|
|
195
|
+
return htmlText;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
182
198
|
const runs = cell?.Paragraphs?.[0]?.Runs || [];
|
|
183
199
|
return runs.map((r: any) => (r?.text ?? '')).join('').trim();
|
|
184
200
|
} catch {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import JSONFile from '../models/json/file/JSONFile';
|
|
2
|
+
import logger from '../services/logger';
|
|
3
|
+
|
|
4
|
+
jest.mock('../services/logger', () => ({
|
|
5
|
+
__esModule: true,
|
|
6
|
+
default: {
|
|
7
|
+
debug: jest.fn(),
|
|
8
|
+
info: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const getMockLogger = () =>
|
|
15
|
+
logger as unknown as {
|
|
16
|
+
debug: jest.Mock;
|
|
17
|
+
info: jest.Mock;
|
|
18
|
+
warn: jest.Mock;
|
|
19
|
+
error: jest.Mock;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('JSONFile', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('removeFileExtension returns original name when there is no dot', () => {
|
|
28
|
+
const data: any = {
|
|
29
|
+
attachmentLink: 'http://server/file',
|
|
30
|
+
attachmentFileName: 'file',
|
|
31
|
+
attachmentType: 'asEmbedded',
|
|
32
|
+
includeAttachmentContent: true,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const jsonFile = new JSONFile(data);
|
|
36
|
+
const attachment = jsonFile.getFileAttachment as any;
|
|
37
|
+
|
|
38
|
+
expect(attachment.type).toBe('File');
|
|
39
|
+
expect(attachment.name).toBe('file');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('isPictureUri handles invalid uri and logs error', () => {
|
|
43
|
+
const data: any = {
|
|
44
|
+
attachmentLink: undefined,
|
|
45
|
+
attachmentFileName: 'file.docx',
|
|
46
|
+
attachmentType: 'asEmbedded',
|
|
47
|
+
includeAttachmentContent: true,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const jsonFile = new JSONFile(data);
|
|
51
|
+
const attachment = jsonFile.getFileAttachment as any;
|
|
52
|
+
|
|
53
|
+
expect(attachment.type).toBe('File');
|
|
54
|
+
expect(attachment.name).toBe('file');
|
|
55
|
+
expect(getMockLogger().error).toHaveBeenCalledWith(
|
|
56
|
+
expect.stringContaining('error parsing file extension for')
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import JSONRun from '../models/json/JSONRun';
|
|
2
|
+
import { defaultJsonRun } from '../models/json/default';
|
|
3
|
+
import logger from '../services/logger';
|
|
4
|
+
|
|
5
|
+
jest.mock('../services/logger', () => ({
|
|
6
|
+
__esModule: true,
|
|
7
|
+
default: {
|
|
8
|
+
debug: jest.fn(),
|
|
9
|
+
info: jest.fn(),
|
|
10
|
+
warn: jest.fn(),
|
|
11
|
+
error: jest.fn(),
|
|
12
|
+
silly: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const getMockLogger = () =>
|
|
17
|
+
logger as unknown as {
|
|
18
|
+
debug: jest.Mock;
|
|
19
|
+
info: jest.Mock;
|
|
20
|
+
warn: jest.Mock;
|
|
21
|
+
error: jest.Mock;
|
|
22
|
+
silly: jest.Mock;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const baseStyles = {
|
|
26
|
+
isBold: true,
|
|
27
|
+
IsItalic: false,
|
|
28
|
+
IsUnderline: false,
|
|
29
|
+
Size: 12,
|
|
30
|
+
Uri: 'http://example.com',
|
|
31
|
+
Font: 'Arial',
|
|
32
|
+
InsertLineBreak: false,
|
|
33
|
+
InsertSpace: true,
|
|
34
|
+
InsertPageBreak: false as boolean | undefined,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('JSONRun', () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('splits multi-line text into separate runs with correct line breaks', () => {
|
|
43
|
+
const value = 'line1\nline2\nline3';
|
|
44
|
+
|
|
45
|
+
const jsonRun = new JSONRun(value, { ...baseStyles });
|
|
46
|
+
const runs = jsonRun.getRun();
|
|
47
|
+
|
|
48
|
+
expect(runs).toHaveLength(3);
|
|
49
|
+
|
|
50
|
+
expect(runs[0].InsertLineBreak).toBe(true);
|
|
51
|
+
expect(runs[1].InsertLineBreak).toBe(true);
|
|
52
|
+
expect(runs[2].InsertLineBreak).toBe(baseStyles.InsertLineBreak);
|
|
53
|
+
|
|
54
|
+
runs.forEach((r) => {
|
|
55
|
+
expect(r.Bold).toBe(baseStyles.isBold);
|
|
56
|
+
expect(r.Italic).toBe(baseStyles.IsItalic);
|
|
57
|
+
expect(r.Underline).toBe(baseStyles.IsUnderline);
|
|
58
|
+
expect(r.Size).toBe(baseStyles.Size);
|
|
59
|
+
expect(r.Font).toBe(baseStyles.Font);
|
|
60
|
+
expect(r.InsertSpace).toBe(baseStyles.InsertSpace);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('uses striphtml replaceBr callback to convert <br> to newline', () => {
|
|
65
|
+
const value = 'Hello<br>World';
|
|
66
|
+
|
|
67
|
+
const jsonRun = new JSONRun(value, { ...baseStyles });
|
|
68
|
+
const runs = jsonRun.getRun();
|
|
69
|
+
|
|
70
|
+
expect(runs).toHaveLength(1);
|
|
71
|
+
expect(runs[0].text).toContain('\n');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('sets Uri to null when default run Uri is empty string in normal flow', () => {
|
|
75
|
+
const prevUri = defaultJsonRun.Uri;
|
|
76
|
+
defaultJsonRun.Uri = '' as any;
|
|
77
|
+
|
|
78
|
+
const jsonRun = new JSONRun('plain text', { ...baseStyles });
|
|
79
|
+
const runs = jsonRun.getRun();
|
|
80
|
+
|
|
81
|
+
runs.forEach((r) => {
|
|
82
|
+
expect(r.Uri).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
defaultJsonRun.Uri = prevUri;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('does not strip HTML when fieldtype is SuiteHeaderParagraphTitle', () => {
|
|
89
|
+
const value = '<b>Bold Title</b>';
|
|
90
|
+
|
|
91
|
+
const jsonRun = new JSONRun(value, { ...baseStyles }, 'SuiteHeaderParagraphTitle');
|
|
92
|
+
const runs = jsonRun.getRun();
|
|
93
|
+
|
|
94
|
+
expect(runs).toHaveLength(1);
|
|
95
|
+
expect(runs[0].text).toContain('<b>Bold Title</b>');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('falls back to default run and logs when value cannot be split', () => {
|
|
99
|
+
const badValue: any = null;
|
|
100
|
+
|
|
101
|
+
const prevUri = defaultJsonRun.Uri;
|
|
102
|
+
defaultJsonRun.Uri = '' as any;
|
|
103
|
+
|
|
104
|
+
const jsonRun = new JSONRun(badValue, { ...baseStyles });
|
|
105
|
+
const runs = jsonRun.getRun();
|
|
106
|
+
|
|
107
|
+
expect(getMockLogger().silly).toHaveBeenCalled();
|
|
108
|
+
expect(runs).toHaveLength(1);
|
|
109
|
+
expect(runs[0].text).toBe('');
|
|
110
|
+
expect(runs[0].Bold).toBe(baseStyles.isBold);
|
|
111
|
+
expect(runs[0].Italic).toBe(baseStyles.IsItalic);
|
|
112
|
+
expect(runs[0].Underline).toBe(baseStyles.IsUnderline);
|
|
113
|
+
expect(runs[0].Size).toBe(baseStyles.Size);
|
|
114
|
+
expect(runs[0].Uri).toBeNull();
|
|
115
|
+
|
|
116
|
+
defaultJsonRun.Uri = prevUri;
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import JSONTable from '../models/json/table/JSONTable';
|
|
2
|
+
|
|
3
|
+
const baseStyles = {
|
|
4
|
+
isBold: false,
|
|
5
|
+
IsItalic: false,
|
|
6
|
+
IsUnderline: false,
|
|
7
|
+
Size: 10,
|
|
8
|
+
Uri: null as string | null,
|
|
9
|
+
Font: 'Arial',
|
|
10
|
+
InsertLineBreak: false,
|
|
11
|
+
InsertSpace: false,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const headerStyles = {
|
|
15
|
+
...baseStyles,
|
|
16
|
+
isBold: true,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const buildData = (secondColValue: string) => [
|
|
20
|
+
{
|
|
21
|
+
url: '',
|
|
22
|
+
Source: 1,
|
|
23
|
+
level: 1,
|
|
24
|
+
fields: [
|
|
25
|
+
{ name: '#', value: 1, width: '8%' },
|
|
26
|
+
{ name: 'Description', value: 'No description' },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
url: '',
|
|
31
|
+
Source: 2,
|
|
32
|
+
level: 1,
|
|
33
|
+
fields: [
|
|
34
|
+
{ name: '#', value: 2, width: '8%' },
|
|
35
|
+
{ name: 'Description', value: secondColValue },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
describe('JSONTable vertical merge behavior', () => {
|
|
41
|
+
test('uses explicit grouped-header column spans when provided', () => {
|
|
42
|
+
const table = new JSONTable(
|
|
43
|
+
[
|
|
44
|
+
{
|
|
45
|
+
url: '',
|
|
46
|
+
Source: 1,
|
|
47
|
+
level: 1,
|
|
48
|
+
fields: [
|
|
49
|
+
{ name: 'Req ID', value: 1 },
|
|
50
|
+
{ name: 'Title', value: 'Req' },
|
|
51
|
+
{ name: 'Customer ID', value: 'C1' },
|
|
52
|
+
{ name: 'Test Case ID', value: 11 },
|
|
53
|
+
{ name: 'Title', value: 'TC' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
] as any,
|
|
57
|
+
headerStyles as any,
|
|
58
|
+
baseStyles as any,
|
|
59
|
+
0,
|
|
60
|
+
false,
|
|
61
|
+
false,
|
|
62
|
+
false,
|
|
63
|
+
{
|
|
64
|
+
leftLabel: 'Requirement',
|
|
65
|
+
rightLabel: 'Test Case',
|
|
66
|
+
leftColumns: 3,
|
|
67
|
+
rightColumns: 2,
|
|
68
|
+
},
|
|
69
|
+
false
|
|
70
|
+
).getJSONTable() as any;
|
|
71
|
+
|
|
72
|
+
expect(table.Rows[0].Cells[0].gridSpan).toBe(3);
|
|
73
|
+
expect(table.Rows[0].Cells[1].gridSpan).toBe(2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('does not merge empty cells when vertical merge is disabled', () => {
|
|
77
|
+
const table = new JSONTable(
|
|
78
|
+
buildData('') as any,
|
|
79
|
+
headerStyles as any,
|
|
80
|
+
baseStyles as any,
|
|
81
|
+
0,
|
|
82
|
+
false,
|
|
83
|
+
false,
|
|
84
|
+
false,
|
|
85
|
+
undefined,
|
|
86
|
+
false
|
|
87
|
+
).getJSONTable() as any;
|
|
88
|
+
|
|
89
|
+
expect(table.Rows[1].Cells[1].vMerge).toBeUndefined();
|
|
90
|
+
expect(table.Rows[2].Cells[1].vMerge).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('merges empty cells when vertical merge is enabled', () => {
|
|
94
|
+
const table = new JSONTable(
|
|
95
|
+
buildData('') as any,
|
|
96
|
+
headerStyles as any,
|
|
97
|
+
baseStyles as any,
|
|
98
|
+
0,
|
|
99
|
+
false,
|
|
100
|
+
false,
|
|
101
|
+
false,
|
|
102
|
+
undefined,
|
|
103
|
+
true
|
|
104
|
+
).getJSONTable() as any;
|
|
105
|
+
|
|
106
|
+
expect(table.Rows[1].Cells[1].vMerge).toBe('restart');
|
|
107
|
+
expect(table.Rows[2].Cells[1].vMerge).toBe('continue');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('does not treat HTML cell content as empty during merge detection', () => {
|
|
111
|
+
const table = new JSONTable(
|
|
112
|
+
buildData('<div>Some Description for suite</div>') as any,
|
|
113
|
+
headerStyles as any,
|
|
114
|
+
baseStyles as any,
|
|
115
|
+
0,
|
|
116
|
+
false,
|
|
117
|
+
false,
|
|
118
|
+
false,
|
|
119
|
+
undefined,
|
|
120
|
+
true
|
|
121
|
+
).getJSONTable() as any;
|
|
122
|
+
|
|
123
|
+
expect(table.Rows[2].Cells[1].vMerge).toBeUndefined();
|
|
124
|
+
expect(table.Rows[2].Cells[1].Html?.Html).toBe('<div>Some Description for suite</div>');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import JSONTableCell from '../models/json/table/JSONTableCell';
|
|
2
|
+
import logger from '../services/logger';
|
|
3
|
+
|
|
4
|
+
jest.mock('../services/logger', () => ({
|
|
5
|
+
__esModule: true,
|
|
6
|
+
default: {
|
|
7
|
+
debug: jest.fn(),
|
|
8
|
+
info: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const getMockLogger = () =>
|
|
15
|
+
logger as unknown as {
|
|
16
|
+
debug: jest.Mock;
|
|
17
|
+
info: jest.Mock;
|
|
18
|
+
warn: jest.Mock;
|
|
19
|
+
error: jest.Mock;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const baseStyles = {
|
|
23
|
+
isBold: false,
|
|
24
|
+
IsItalic: false,
|
|
25
|
+
IsUnderline: false,
|
|
26
|
+
Size: 10,
|
|
27
|
+
Uri: null as string | null,
|
|
28
|
+
Font: 'Arial',
|
|
29
|
+
InsertLineBreak: false,
|
|
30
|
+
InsertSpace: true,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('JSONTableCell', () => {
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
jest.clearAllMocks();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('creates picture attachment when value contains picture link and respects isFlattened', () => {
|
|
39
|
+
const shading = { color: 'auto', fill: 'FF0000', themeFillShade: 'BF' } as any;
|
|
40
|
+
|
|
41
|
+
const data: any = {
|
|
42
|
+
name: 'AttachmentCell',
|
|
43
|
+
value: [
|
|
44
|
+
{
|
|
45
|
+
attachmentLink: 'http://server/image.png',
|
|
46
|
+
tableCellAttachmentLink: 'http://server/image-render.png',
|
|
47
|
+
attachmentFileName: 'image.png',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
attachmentType: 'asEmbedded',
|
|
51
|
+
includeAttachmentContent: true,
|
|
52
|
+
width: '50%',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const cell = new JSONTableCell(data, { ...baseStyles }, false, shading, true);
|
|
56
|
+
const json = cell.getJsonCell() as any;
|
|
57
|
+
|
|
58
|
+
expect(json.shading).toEqual(shading);
|
|
59
|
+
expect(json.attachments).toHaveLength(1);
|
|
60
|
+
expect(json.attachments[0]).toMatchObject({
|
|
61
|
+
type: 'Picture',
|
|
62
|
+
path: 'http://server/image-render.png',
|
|
63
|
+
name: 'image',
|
|
64
|
+
isFlattened: true,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('creates file attachment with link flags for non-picture attachment', () => {
|
|
69
|
+
const data: any = {
|
|
70
|
+
name: 'AttachmentCell',
|
|
71
|
+
value: [
|
|
72
|
+
{
|
|
73
|
+
attachmentLink: 'http://server/doc1.pdf',
|
|
74
|
+
attachmentFileName: 'doc1.pdf',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
attachmentType: 'asLink',
|
|
78
|
+
includeAttachmentContent: false,
|
|
79
|
+
width: '50%',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const cell = new JSONTableCell(data, { ...baseStyles }, false, undefined, true);
|
|
83
|
+
const json = cell.getJsonCell() as any;
|
|
84
|
+
|
|
85
|
+
expect(json.attachments).toHaveLength(1);
|
|
86
|
+
expect(json.attachments[0]).toMatchObject({
|
|
87
|
+
type: 'File',
|
|
88
|
+
path: 'http://server/doc1.pdf',
|
|
89
|
+
name: 'doc1',
|
|
90
|
+
isLinkedFile: true,
|
|
91
|
+
includeAttachmentContent: false,
|
|
92
|
+
isFlattened: true,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('creates HTML cell when value contains HTML markup', () => {
|
|
97
|
+
const shading = { color: 'auto', fill: '00FF00' } as any;
|
|
98
|
+
|
|
99
|
+
const data: any = {
|
|
100
|
+
name: 'HtmlCell',
|
|
101
|
+
value: '<p>Hello</p>',
|
|
102
|
+
width: '30%',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const cell = new JSONTableCell(data, { ...baseStyles }, false, shading, false);
|
|
106
|
+
const json = cell.getJsonCell() as any;
|
|
107
|
+
|
|
108
|
+
expect(json.Html).toMatchObject({
|
|
109
|
+
type: 'html',
|
|
110
|
+
Html: '<p>Hello</p>',
|
|
111
|
+
font: 'Arial',
|
|
112
|
+
fontSize: 10,
|
|
113
|
+
});
|
|
114
|
+
expect(json.shading).toEqual(shading);
|
|
115
|
+
expect(json.attachments).toHaveLength(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('creates text runs when value is simple text', () => {
|
|
119
|
+
const data: any = {
|
|
120
|
+
name: 'TextCell',
|
|
121
|
+
value: 'Plain text',
|
|
122
|
+
width: '20%',
|
|
123
|
+
url: 'http://example.com/field',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const cell = new JSONTableCell(data, { ...baseStyles }, false, undefined, false);
|
|
127
|
+
const json = cell.getJsonCell() as any;
|
|
128
|
+
|
|
129
|
+
expect(json.Html).toBeUndefined();
|
|
130
|
+
expect(json.attachments).toHaveLength(0);
|
|
131
|
+
expect(json.Paragraphs[0].Runs.length).toBeGreaterThan(0);
|
|
132
|
+
const text = json.Paragraphs[0].Runs.map((r: any) => r.text).join('');
|
|
133
|
+
expect(text).toContain('Plain text');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('logs error and falls back to error run when value is invalid non-array object', () => {
|
|
137
|
+
const data: any = {
|
|
138
|
+
name: 'BadCell',
|
|
139
|
+
value: { not: 'an array' },
|
|
140
|
+
width: '10%',
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const cell = new JSONTableCell(data, { ...baseStyles }, false, undefined, false);
|
|
144
|
+
const json = cell.getJsonCell() as any;
|
|
145
|
+
|
|
146
|
+
expect(getMockLogger().error).toHaveBeenCalled();
|
|
147
|
+
const text = json.Paragraphs[0].Runs.map((r: any) => r.text).join('');
|
|
148
|
+
expect(text).toContain('Docgen Error: Invalid data value');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import JSONTableRow from '../models/json/table/JSONTableRow';
|
|
2
|
+
import logger from '../services/logger';
|
|
3
|
+
|
|
4
|
+
jest.mock('../services/logger', () => ({
|
|
5
|
+
__esModule: true,
|
|
6
|
+
default: {
|
|
7
|
+
debug: jest.fn(),
|
|
8
|
+
info: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const baseStyles = {
|
|
15
|
+
isBold: false,
|
|
16
|
+
IsItalic: false,
|
|
17
|
+
IsUnderline: false,
|
|
18
|
+
Size: 10,
|
|
19
|
+
Uri: null as string | null,
|
|
20
|
+
Font: 'Arial',
|
|
21
|
+
InsertLineBreak: false,
|
|
22
|
+
InsertSpace: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('JSONTableRow', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('throws error when WIData has no fields', () => {
|
|
31
|
+
const data: any = {
|
|
32
|
+
url: '',
|
|
33
|
+
level: 0,
|
|
34
|
+
Source: 1,
|
|
35
|
+
// fields is intentionally missing to trigger the error path
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
expect(() => new JSONTableRow(data, { ...baseStyles } as any)).toThrow(
|
|
39
|
+
'no fields to add for the table row'
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import JSONTestSuiteHeaderParagraph from '../models/json/paragraph/JSONTestSuiteHeaderParagraph';
|
|
2
|
+
import logger from '../services/logger';
|
|
3
|
+
|
|
4
|
+
jest.mock('../services/logger', () => ({
|
|
5
|
+
__esModule: true,
|
|
6
|
+
default: {
|
|
7
|
+
debug: jest.fn(),
|
|
8
|
+
info: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const baseStyles = {
|
|
15
|
+
isBold: true,
|
|
16
|
+
IsItalic: false,
|
|
17
|
+
IsUnderline: false,
|
|
18
|
+
Size: 14,
|
|
19
|
+
Uri: null as string | null,
|
|
20
|
+
Font: 'Arial',
|
|
21
|
+
InsertLineBreak: false,
|
|
22
|
+
InsertSpace: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('JSONTestSuiteHeaderParagraph', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('creates runs for non-title/id/description fields and sets heading level', () => {
|
|
31
|
+
const fields: any[] = [{ name: 'Custom', value: 'Value', url: 'http://example.com' }];
|
|
32
|
+
|
|
33
|
+
const paragraph = new JSONTestSuiteHeaderParagraph(fields, { ...baseStyles }, 1, 2);
|
|
34
|
+
const json = paragraph.getJSONParagraph() as any;
|
|
35
|
+
|
|
36
|
+
expect(json.type).toBe('paragraph');
|
|
37
|
+
expect(json.headingLevel).toBe(2);
|
|
38
|
+
expect(json.runs.length).toBeGreaterThan(0);
|
|
39
|
+
});
|
|
40
|
+
});
|