react-markdown-table-ts 1.2.2 → 1.3.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/build.yml +35 -0
- package/.github/workflows/publish.yml +31 -0
- package/.github/workflows/test.yml +42 -0
- package/.gitignore +12 -0
- package/.storybook/main.ts +13 -0
- package/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +14 -0
- package/.storybook/theme.ts +8 -0
- package/README.md +19 -10
- package/jest.config.js +38 -0
- package/package.json +31 -67
- package/public/dark.png +0 -0
- package/public/light.png +0 -0
- package/src/index.tsx +176 -0
- package/src/prism.d.ts +6 -0
- package/src/stories/MarkdownTable.stories.tsx +212 -0
- package/src/tests/__mocks__/styleMock.js +2 -0
- package/src/tests/index.test.tsx +646 -0
- package/src/tests/integration.test.tsx +392 -0
- package/src/tests/setup.ts +31 -0
- package/src/tests/utils.test.ts +484 -0
- package/src/tests/validation.test.ts +166 -0
- package/src/types.ts +138 -0
- package/src/utils.ts +180 -0
- package/src/validation.ts +31 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +7 -0
- package/dist/cellFormatter.d.ts +0 -9
- package/dist/index.cjs.js +0 -4331
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.esm.js +0 -4323
- package/dist/index.esm.js.map +0 -1
- package/dist/types.d.ts +0 -144
- package/dist/utils.d.ts +0 -10
- package/dist/validation.d.ts +0 -19
@@ -0,0 +1,484 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Comprehensive tests for utility functions and classes used in
|
3
|
+
* markdown table generation.
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { generateMarkdownTableString, generateAlphabetHeaders } from '../utils';
|
7
|
+
import type { InputData, Alignment } from '../types';
|
8
|
+
|
9
|
+
describe('generateAlphabetHeaders', () => {
|
10
|
+
it('should generate single letter headers for small column counts', () => {
|
11
|
+
expect(generateAlphabetHeaders(1)).toEqual(['A']);
|
12
|
+
expect(generateAlphabetHeaders(3)).toEqual(['A', 'B', 'C']);
|
13
|
+
expect(generateAlphabetHeaders(5)).toEqual(['A', 'B', 'C', 'D', 'E']);
|
14
|
+
});
|
15
|
+
|
16
|
+
it('should generate all 26 letters for 26 columns', () => {
|
17
|
+
const headers = generateAlphabetHeaders(26);
|
18
|
+
expect(headers).toHaveLength(26);
|
19
|
+
expect(headers[0]).toBe('A');
|
20
|
+
expect(headers[25]).toBe('Z');
|
21
|
+
});
|
22
|
+
|
23
|
+
it('should generate double letter headers after Z', () => {
|
24
|
+
const headers = generateAlphabetHeaders(27);
|
25
|
+
expect(headers).toHaveLength(27);
|
26
|
+
expect(headers[26]).toBe('AA');
|
27
|
+
});
|
28
|
+
|
29
|
+
it('should generate correct headers for extended columns', () => {
|
30
|
+
const headers = generateAlphabetHeaders(30);
|
31
|
+
expect(headers[26]).toBe('AA');
|
32
|
+
expect(headers[27]).toBe('AB');
|
33
|
+
expect(headers[28]).toBe('AC');
|
34
|
+
expect(headers[29]).toBe('AD');
|
35
|
+
});
|
36
|
+
|
37
|
+
it('should handle 52 columns (through AZ)', () => {
|
38
|
+
const headers = generateAlphabetHeaders(52);
|
39
|
+
expect(headers[0]).toBe('A');
|
40
|
+
expect(headers[25]).toBe('Z');
|
41
|
+
expect(headers[26]).toBe('AA');
|
42
|
+
expect(headers[51]).toBe('AZ');
|
43
|
+
});
|
44
|
+
|
45
|
+
it('should handle triple letter columns', () => {
|
46
|
+
const headers = generateAlphabetHeaders(703);
|
47
|
+
expect(headers[702]).toBe('AAA');
|
48
|
+
});
|
49
|
+
|
50
|
+
it('should return empty array for 0 columns', () => {
|
51
|
+
expect(generateAlphabetHeaders(0)).toEqual([]);
|
52
|
+
});
|
53
|
+
|
54
|
+
it('should handle negative numbers gracefully', () => {
|
55
|
+
expect(generateAlphabetHeaders(-1)).toEqual([]);
|
56
|
+
});
|
57
|
+
});
|
58
|
+
|
59
|
+
describe('generateMarkdownTableString', () => {
|
60
|
+
describe('basic table generation', () => {
|
61
|
+
it('should generate a simple table with header and body', () => {
|
62
|
+
const inputData: InputData = {
|
63
|
+
inputDataHeader: ['Name', 'Age'],
|
64
|
+
inputDataBody: [['John', '30'], ['Jane', '25']],
|
65
|
+
};
|
66
|
+
|
67
|
+
const result = generateMarkdownTableString(inputData, []);
|
68
|
+
|
69
|
+
expect(result).toContain('| Name | Age |');
|
70
|
+
expect(result).toContain('| John | 30 |');
|
71
|
+
expect(result).toContain('| Jane | 25 |');
|
72
|
+
});
|
73
|
+
|
74
|
+
it('should generate table with single row', () => {
|
75
|
+
const inputData: InputData = {
|
76
|
+
inputDataHeader: ['A', 'B', 'C'],
|
77
|
+
inputDataBody: [['1', '2', '3']],
|
78
|
+
};
|
79
|
+
|
80
|
+
const result = generateMarkdownTableString(inputData, []);
|
81
|
+
|
82
|
+
expect(result).toContain('| A');
|
83
|
+
expect(result).toContain('| B');
|
84
|
+
expect(result).toContain('| C');
|
85
|
+
expect(result).toContain('| 1');
|
86
|
+
expect(result).toContain('| 2');
|
87
|
+
expect(result).toContain('| 3');
|
88
|
+
});
|
89
|
+
|
90
|
+
it('should generate table with single column', () => {
|
91
|
+
const inputData: InputData = {
|
92
|
+
inputDataHeader: ['Header'],
|
93
|
+
inputDataBody: [['Value1'], ['Value2']],
|
94
|
+
};
|
95
|
+
|
96
|
+
const result = generateMarkdownTableString(inputData, []);
|
97
|
+
|
98
|
+
expect(result).toContain('| Header |');
|
99
|
+
expect(result).toContain('| Value1 |');
|
100
|
+
expect(result).toContain('| Value2 |');
|
101
|
+
});
|
102
|
+
|
103
|
+
it('should handle empty body', () => {
|
104
|
+
const inputData: InputData = {
|
105
|
+
inputDataHeader: ['A', 'B'],
|
106
|
+
inputDataBody: [],
|
107
|
+
};
|
108
|
+
|
109
|
+
const result = generateMarkdownTableString(inputData, []);
|
110
|
+
|
111
|
+
expect(result).toContain('| A');
|
112
|
+
expect(result).toContain('| B');
|
113
|
+
expect(result.split('\n')).toHaveLength(2); // Header and alignment row only
|
114
|
+
});
|
115
|
+
|
116
|
+
it('should handle empty cells', () => {
|
117
|
+
const inputData: InputData = {
|
118
|
+
inputDataHeader: ['Col1', 'Col2'],
|
119
|
+
inputDataBody: [['', 'Value'], ['Value', '']],
|
120
|
+
};
|
121
|
+
|
122
|
+
const result = generateMarkdownTableString(inputData, []);
|
123
|
+
|
124
|
+
expect(result).toContain('Value');
|
125
|
+
const lines = result.split('\n');
|
126
|
+
expect(lines[2]).toMatch(/\|\s+\|\s+Value\s+\|/);
|
127
|
+
expect(lines[3]).toMatch(/\|\s+Value\s+\|\s+\|/);
|
128
|
+
});
|
129
|
+
});
|
130
|
+
|
131
|
+
describe('alignment options', () => {
|
132
|
+
const inputData: InputData = {
|
133
|
+
inputDataHeader: ['Left', 'Center', 'Right', 'None'],
|
134
|
+
inputDataBody: [['A', 'B', 'C', 'D']],
|
135
|
+
};
|
136
|
+
|
137
|
+
it('should apply left alignment', () => {
|
138
|
+
const alignments: Alignment[] = ['left'];
|
139
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
140
|
+
|
141
|
+
expect(result).toMatch(/:\-+/);
|
142
|
+
});
|
143
|
+
|
144
|
+
it('should apply center alignment', () => {
|
145
|
+
const alignments: Alignment[] = ['none', 'center'];
|
146
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
147
|
+
|
148
|
+
expect(result).toMatch(/:\-+:/);
|
149
|
+
});
|
150
|
+
|
151
|
+
it('should apply right alignment', () => {
|
152
|
+
const alignments: Alignment[] = ['none', 'none', 'right'];
|
153
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
154
|
+
|
155
|
+
expect(result).toMatch(/\-+:/);
|
156
|
+
});
|
157
|
+
|
158
|
+
it('should apply none alignment (no colons)', () => {
|
159
|
+
const alignments: Alignment[] = ['none'];
|
160
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
161
|
+
|
162
|
+
const lines = result.split('\n');
|
163
|
+
const alignmentRow = lines[1];
|
164
|
+
expect(alignmentRow).toMatch(/^\| ---- \|/);
|
165
|
+
});
|
166
|
+
|
167
|
+
it('should handle mixed alignments', () => {
|
168
|
+
const alignments: Alignment[] = ['left', 'center', 'right', 'none'];
|
169
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
170
|
+
|
171
|
+
const lines = result.split('\n');
|
172
|
+
const alignmentRow = lines[1];
|
173
|
+
|
174
|
+
expect(alignmentRow).toMatch(/:\-+/); // left
|
175
|
+
expect(alignmentRow).toMatch(/:\-+:/); // center
|
176
|
+
expect(alignmentRow).toMatch(/\-+:/); // right
|
177
|
+
expect(alignmentRow).toMatch(/\-+/); // none (dashes without colons)
|
178
|
+
});
|
179
|
+
|
180
|
+
it('should default to none alignment for unspecified columns', () => {
|
181
|
+
const alignments: Alignment[] = ['left'];
|
182
|
+
const result = generateMarkdownTableString(inputData, alignments);
|
183
|
+
|
184
|
+
const lines = result.split('\n');
|
185
|
+
const alignmentRow = lines[1];
|
186
|
+
|
187
|
+
expect(alignmentRow).toMatch(/:\-+/); // First column left
|
188
|
+
expect(alignmentRow).toMatch(/\-+/); // Others have dashes
|
189
|
+
});
|
190
|
+
});
|
191
|
+
|
192
|
+
describe('column width adjustment', () => {
|
193
|
+
it('should adjust column widths when enabled', () => {
|
194
|
+
const inputData: InputData = {
|
195
|
+
inputDataHeader: ['Short', 'VeryLongHeader'],
|
196
|
+
inputDataBody: [['A', 'B'], ['CCCCC', 'D']],
|
197
|
+
};
|
198
|
+
|
199
|
+
const result = generateMarkdownTableString(inputData, [], true);
|
200
|
+
|
201
|
+
const lines = result.split('\n');
|
202
|
+
expect(lines[0]).toContain('Short');
|
203
|
+
expect(lines[0]).toContain('VeryLongHeader');
|
204
|
+
|
205
|
+
// Check that columns are adjusted
|
206
|
+
expect(lines[2]).toContain('A');
|
207
|
+
expect(lines[3]).toContain('CCCCC');
|
208
|
+
});
|
209
|
+
|
210
|
+
it('should not adjust column widths when disabled', () => {
|
211
|
+
const inputData: InputData = {
|
212
|
+
inputDataHeader: ['H1', 'H2'],
|
213
|
+
inputDataBody: [['Short', 'VeryLongValue']],
|
214
|
+
};
|
215
|
+
|
216
|
+
const result = generateMarkdownTableString(inputData, [], false);
|
217
|
+
|
218
|
+
// When not adjusting, widths should match content exactly
|
219
|
+
expect(result).toContain('| H1 |');
|
220
|
+
expect(result).toContain('| Short |');
|
221
|
+
});
|
222
|
+
|
223
|
+
it('should handle varying row lengths', () => {
|
224
|
+
const inputData: InputData = {
|
225
|
+
inputDataHeader: ['A', 'B', 'C'],
|
226
|
+
inputDataBody: [
|
227
|
+
['1', '2'],
|
228
|
+
['3', '4', '5', '6'],
|
229
|
+
['7'],
|
230
|
+
],
|
231
|
+
};
|
232
|
+
|
233
|
+
const result = generateMarkdownTableString(inputData, [], true);
|
234
|
+
|
235
|
+
// Should pad all rows to match max column count (4)
|
236
|
+
const lines = result.split('\n');
|
237
|
+
lines.forEach(line => {
|
238
|
+
if (line.trim()) {
|
239
|
+
const columns = line.split('|').filter(col => col !== '');
|
240
|
+
expect(columns.length).toBe(4);
|
241
|
+
}
|
242
|
+
});
|
243
|
+
});
|
244
|
+
});
|
245
|
+
|
246
|
+
describe('tab option', () => {
|
247
|
+
it('should add tabs when enabled', () => {
|
248
|
+
const inputData: InputData = {
|
249
|
+
inputDataHeader: ['A', 'B'],
|
250
|
+
inputDataBody: [['1', '2']],
|
251
|
+
};
|
252
|
+
|
253
|
+
const result = generateMarkdownTableString(inputData, [], true, true);
|
254
|
+
|
255
|
+
expect(result).toContain('\t');
|
256
|
+
});
|
257
|
+
|
258
|
+
it('should not add tabs when disabled', () => {
|
259
|
+
const inputData: InputData = {
|
260
|
+
inputDataHeader: ['A', 'B'],
|
261
|
+
inputDataBody: [['1', '2']],
|
262
|
+
};
|
263
|
+
|
264
|
+
const result = generateMarkdownTableString(inputData, [], true, false);
|
265
|
+
|
266
|
+
expect(result).not.toContain('\t');
|
267
|
+
});
|
268
|
+
});
|
269
|
+
|
270
|
+
describe('newline replacement', () => {
|
271
|
+
it('should replace newlines with <br> when enabled', () => {
|
272
|
+
const inputData: InputData = {
|
273
|
+
inputDataHeader: ['Header'],
|
274
|
+
inputDataBody: [['Line1\nLine2\nLine3']],
|
275
|
+
};
|
276
|
+
|
277
|
+
const result = generateMarkdownTableString(inputData, [], true, false, true);
|
278
|
+
|
279
|
+
expect(result).toContain('Line1<br>Line2<br>Line3');
|
280
|
+
expect(result).not.toMatch(/Line1\nLine2/);
|
281
|
+
});
|
282
|
+
|
283
|
+
it('should not replace newlines when disabled', () => {
|
284
|
+
const inputData: InputData = {
|
285
|
+
inputDataHeader: ['Header'],
|
286
|
+
inputDataBody: [['Line1\nLine2']],
|
287
|
+
};
|
288
|
+
|
289
|
+
const result = generateMarkdownTableString(inputData, [], true, false, false);
|
290
|
+
|
291
|
+
expect(result).toContain('Line1\nLine2');
|
292
|
+
expect(result).not.toContain('<br>');
|
293
|
+
});
|
294
|
+
|
295
|
+
it('should handle multiple consecutive newlines', () => {
|
296
|
+
const inputData: InputData = {
|
297
|
+
inputDataHeader: ['H'],
|
298
|
+
inputDataBody: [['A\n\n\nB']],
|
299
|
+
};
|
300
|
+
|
301
|
+
const result = generateMarkdownTableString(inputData, [], true, false, true);
|
302
|
+
|
303
|
+
expect(result).toContain('A<br><br><br>B');
|
304
|
+
});
|
305
|
+
});
|
306
|
+
|
307
|
+
describe('padding option', () => {
|
308
|
+
it('should add padding when enabled', () => {
|
309
|
+
const inputData: InputData = {
|
310
|
+
inputDataHeader: ['A', 'B'],
|
311
|
+
inputDataBody: [['1', '2']],
|
312
|
+
};
|
313
|
+
|
314
|
+
const result = generateMarkdownTableString(inputData, [], false, false, false, true);
|
315
|
+
|
316
|
+
expect(result).toContain('| A | B |');
|
317
|
+
expect(result).toContain('| 1 | 2 |');
|
318
|
+
});
|
319
|
+
|
320
|
+
it('should not add padding when disabled', () => {
|
321
|
+
const inputData: InputData = {
|
322
|
+
inputDataHeader: ['A', 'B'],
|
323
|
+
inputDataBody: [['1', '2']],
|
324
|
+
};
|
325
|
+
|
326
|
+
const result = generateMarkdownTableString(inputData, [], false, false, false, false);
|
327
|
+
|
328
|
+
expect(result).toContain('|A|B|');
|
329
|
+
expect(result).toContain('|1|2|');
|
330
|
+
});
|
331
|
+
});
|
332
|
+
|
333
|
+
describe('complex scenarios', () => {
|
334
|
+
it('should handle unicode characters', () => {
|
335
|
+
const inputData: InputData = {
|
336
|
+
inputDataHeader: ['日本語', '한국어'],
|
337
|
+
inputDataBody: [['こんにちは', '안녕하세요']],
|
338
|
+
};
|
339
|
+
|
340
|
+
const result = generateMarkdownTableString(inputData, []);
|
341
|
+
|
342
|
+
expect(result).toContain('日本語');
|
343
|
+
expect(result).toContain('한국어');
|
344
|
+
expect(result).toContain('こんにちは');
|
345
|
+
expect(result).toContain('안녕하세요');
|
346
|
+
});
|
347
|
+
|
348
|
+
it('should handle special markdown characters', () => {
|
349
|
+
const inputData: InputData = {
|
350
|
+
inputDataHeader: ['Code', 'Symbol'],
|
351
|
+
inputDataBody: [['`code`', '**bold**'], ['*italic*', '[link]']],
|
352
|
+
};
|
353
|
+
|
354
|
+
const result = generateMarkdownTableString(inputData, []);
|
355
|
+
|
356
|
+
expect(result).toContain('`code`');
|
357
|
+
expect(result).toContain('**bold**');
|
358
|
+
expect(result).toContain('*italic*');
|
359
|
+
expect(result).toContain('[link]');
|
360
|
+
});
|
361
|
+
|
362
|
+
it('should handle very long content', () => {
|
363
|
+
const longString = 'A'.repeat(1000);
|
364
|
+
const inputData: InputData = {
|
365
|
+
inputDataHeader: ['Header'],
|
366
|
+
inputDataBody: [[longString]],
|
367
|
+
};
|
368
|
+
|
369
|
+
const result = generateMarkdownTableString(inputData, [], true);
|
370
|
+
|
371
|
+
expect(result).toContain(longString);
|
372
|
+
});
|
373
|
+
|
374
|
+
it('should generate valid markdown with all options enabled', () => {
|
375
|
+
const inputData: InputData = {
|
376
|
+
inputDataHeader: ['Name', 'Description'],
|
377
|
+
inputDataBody: [
|
378
|
+
['Item1', 'Short'],
|
379
|
+
['Item2', 'Simple'],
|
380
|
+
],
|
381
|
+
};
|
382
|
+
|
383
|
+
const result = generateMarkdownTableString(
|
384
|
+
inputData,
|
385
|
+
['left', 'center'],
|
386
|
+
true,
|
387
|
+
true,
|
388
|
+
false,
|
389
|
+
true
|
390
|
+
);
|
391
|
+
|
392
|
+
expect(result).toContain('\t');
|
393
|
+
expect(result).toContain(':');
|
394
|
+
expect(result.split('\n')).toHaveLength(4); // Header, alignment, 2 rows
|
395
|
+
});
|
396
|
+
|
397
|
+
it('should handle empty header and body with different lengths', () => {
|
398
|
+
const inputData: InputData = {
|
399
|
+
inputDataHeader: ['A'],
|
400
|
+
inputDataBody: [['1', '2', '3']],
|
401
|
+
};
|
402
|
+
|
403
|
+
const result = generateMarkdownTableString(inputData, [], true);
|
404
|
+
|
405
|
+
const lines = result.split('\n');
|
406
|
+
const headerCols = lines[0].split('|').filter(col => col !== '').length;
|
407
|
+
const bodyCols = lines[2].split('|').filter(col => col !== '').length;
|
408
|
+
|
409
|
+
expect(headerCols).toBe(bodyCols);
|
410
|
+
expect(headerCols).toBe(3); // Should match body's column count
|
411
|
+
});
|
412
|
+
|
413
|
+
it('should trim trailing whitespace at end of output', () => {
|
414
|
+
const inputData: InputData = {
|
415
|
+
inputDataHeader: ['A'],
|
416
|
+
inputDataBody: [['1']],
|
417
|
+
};
|
418
|
+
|
419
|
+
const result = generateMarkdownTableString(inputData, []);
|
420
|
+
|
421
|
+
expect(result).toBe(result.trimEnd());
|
422
|
+
expect(result).not.toMatch(/\n$/);
|
423
|
+
});
|
424
|
+
|
425
|
+
it('should handle minimum width columns', () => {
|
426
|
+
const inputData: InputData = {
|
427
|
+
inputDataHeader: ['A'],
|
428
|
+
inputDataBody: [['']],
|
429
|
+
};
|
430
|
+
|
431
|
+
const result = generateMarkdownTableString(inputData, [], true);
|
432
|
+
|
433
|
+
const lines = result.split('\n');
|
434
|
+
const alignmentRow = lines[1];
|
435
|
+
|
436
|
+
// Should have at least minimum width of 3 for alignment indicators
|
437
|
+
expect(alignmentRow).toMatch(/\|\s*---\s*\|/);
|
438
|
+
});
|
439
|
+
});
|
440
|
+
|
441
|
+
describe('cell formatting with different alignments', () => {
|
442
|
+
it('should right-align content correctly', () => {
|
443
|
+
const inputData: InputData = {
|
444
|
+
inputDataHeader: ['Number'],
|
445
|
+
inputDataBody: [['1'], ['100']],
|
446
|
+
};
|
447
|
+
|
448
|
+
const result = generateMarkdownTableString(inputData, ['right'], true);
|
449
|
+
|
450
|
+
const lines = result.split('\n');
|
451
|
+
// In right alignment, shorter content should be padded on the left
|
452
|
+
expect(lines[2]).toMatch(/\|\s+1\s+\|/);
|
453
|
+
expect(lines[3]).toMatch(/\|\s+100\s+\|/);
|
454
|
+
});
|
455
|
+
|
456
|
+
it('should center-align content correctly', () => {
|
457
|
+
const inputData: InputData = {
|
458
|
+
inputDataHeader: ['Centered'],
|
459
|
+
inputDataBody: [['ABC']],
|
460
|
+
};
|
461
|
+
|
462
|
+
const result = generateMarkdownTableString(inputData, ['center'], true);
|
463
|
+
|
464
|
+
// Should have roughly equal padding on both sides
|
465
|
+
const lines = result.split('\n');
|
466
|
+
expect(lines[0]).toMatch(/\|\s+Centered\s+\|/);
|
467
|
+
});
|
468
|
+
|
469
|
+
it('should left-align content correctly', () => {
|
470
|
+
const inputData: InputData = {
|
471
|
+
inputDataHeader: ['Text'],
|
472
|
+
inputDataBody: [['Hi'], ['Hello']],
|
473
|
+
};
|
474
|
+
|
475
|
+
const result = generateMarkdownTableString(inputData, ['left'], true);
|
476
|
+
|
477
|
+
const lines = result.split('\n');
|
478
|
+
// In left alignment, shorter content should be padded on the right
|
479
|
+
expect(lines[2]).toMatch(/\|\s+Hi\s+\|/);
|
480
|
+
expect(lines[3]).toMatch(/\|\s+Hello\s+\|/);
|
481
|
+
});
|
482
|
+
});
|
483
|
+
});
|
484
|
+
|
@@ -0,0 +1,166 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Comprehensive tests for validation utilities and custom error
|
3
|
+
* class.
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { validateInputData, MarkdownTableError } from '../validation';
|
7
|
+
|
8
|
+
describe('MarkdownTableError', () => {
|
9
|
+
describe('constructor', () => {
|
10
|
+
it('should create an error with the given message', () => {
|
11
|
+
const message = 'Test error message';
|
12
|
+
const error = new MarkdownTableError(message);
|
13
|
+
|
14
|
+
expect(error).toBeInstanceOf(Error);
|
15
|
+
expect(error).toBeInstanceOf(MarkdownTableError);
|
16
|
+
expect(error.message).toBe(message);
|
17
|
+
expect(error.name).toBe('MarkdownTableError');
|
18
|
+
});
|
19
|
+
|
20
|
+
it('should support error chaining with cause option', () => {
|
21
|
+
const originalError = new Error('Original error');
|
22
|
+
const message = 'Wrapped error';
|
23
|
+
const error = new MarkdownTableError(message, { cause: originalError });
|
24
|
+
|
25
|
+
expect(error.message).toBe(message);
|
26
|
+
expect(error.cause).toBe(originalError);
|
27
|
+
});
|
28
|
+
|
29
|
+
it('should have correct prototype chain', () => {
|
30
|
+
const error = new MarkdownTableError('Test');
|
31
|
+
|
32
|
+
expect(Object.getPrototypeOf(error)).toBe(MarkdownTableError.prototype);
|
33
|
+
});
|
34
|
+
|
35
|
+
it('should be catchable as Error', () => {
|
36
|
+
try {
|
37
|
+
throw new MarkdownTableError('Test error');
|
38
|
+
} catch (error) {
|
39
|
+
expect(error).toBeInstanceOf(Error);
|
40
|
+
expect(error).toBeInstanceOf(MarkdownTableError);
|
41
|
+
}
|
42
|
+
});
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
describe('validateInputData', () => {
|
47
|
+
describe('valid input', () => {
|
48
|
+
it('should accept a valid 2D array with one row', () => {
|
49
|
+
const validData = [['A', 'B', 'C']];
|
50
|
+
expect(() => validateInputData(validData)).not.toThrow();
|
51
|
+
});
|
52
|
+
|
53
|
+
it('should accept a valid 2D array with multiple rows', () => {
|
54
|
+
const validData = [
|
55
|
+
['Header1', 'Header2', 'Header3'],
|
56
|
+
['Row1Col1', 'Row1Col2', 'Row1Col3'],
|
57
|
+
['Row2Col1', 'Row2Col2', 'Row2Col3'],
|
58
|
+
];
|
59
|
+
expect(() => validateInputData(validData)).not.toThrow();
|
60
|
+
});
|
61
|
+
|
62
|
+
it('should accept an array with empty string cells', () => {
|
63
|
+
const validData = [['', 'B', ''], ['D', '', 'F']];
|
64
|
+
expect(() => validateInputData(validData)).not.toThrow();
|
65
|
+
});
|
66
|
+
|
67
|
+
it('should accept an array with rows of different lengths', () => {
|
68
|
+
const validData = [
|
69
|
+
['A', 'B', 'C'],
|
70
|
+
['D', 'E'],
|
71
|
+
['F', 'G', 'H', 'I'],
|
72
|
+
];
|
73
|
+
expect(() => validateInputData(validData)).not.toThrow();
|
74
|
+
});
|
75
|
+
|
76
|
+
it('should accept an array with a single cell', () => {
|
77
|
+
const validData = [['A']];
|
78
|
+
expect(() => validateInputData(validData)).not.toThrow();
|
79
|
+
});
|
80
|
+
});
|
81
|
+
|
82
|
+
describe('null and undefined input', () => {
|
83
|
+
it('should throw MarkdownTableError for null input', () => {
|
84
|
+
expect(() => validateInputData(null)).toThrow(MarkdownTableError);
|
85
|
+
expect(() => validateInputData(null)).toThrow(
|
86
|
+
"The 'data' prop must be a two-dimensional array."
|
87
|
+
);
|
88
|
+
});
|
89
|
+
|
90
|
+
it('should throw MarkdownTableError for undefined input', () => {
|
91
|
+
expect(() => validateInputData(undefined)).toThrow(MarkdownTableError);
|
92
|
+
expect(() => validateInputData(undefined)).toThrow(
|
93
|
+
"The 'data' prop must be a two-dimensional array."
|
94
|
+
);
|
95
|
+
});
|
96
|
+
});
|
97
|
+
|
98
|
+
describe('non-array input', () => {
|
99
|
+
it('should throw MarkdownTableError for string input', () => {
|
100
|
+
expect(() => validateInputData('not an array')).toThrow(MarkdownTableError);
|
101
|
+
expect(() => validateInputData('not an array')).toThrow(
|
102
|
+
"The 'data' prop must be a two-dimensional array."
|
103
|
+
);
|
104
|
+
});
|
105
|
+
|
106
|
+
it('should throw MarkdownTableError for number input', () => {
|
107
|
+
expect(() => validateInputData(123)).toThrow(MarkdownTableError);
|
108
|
+
});
|
109
|
+
|
110
|
+
it('should throw MarkdownTableError for boolean input', () => {
|
111
|
+
expect(() => validateInputData(true)).toThrow(MarkdownTableError);
|
112
|
+
expect(() => validateInputData(false)).toThrow(MarkdownTableError);
|
113
|
+
});
|
114
|
+
|
115
|
+
it('should throw MarkdownTableError for object input', () => {
|
116
|
+
expect(() => validateInputData({ key: 'value' })).toThrow(MarkdownTableError);
|
117
|
+
});
|
118
|
+
|
119
|
+
it('should throw MarkdownTableError for function input', () => {
|
120
|
+
expect(() => validateInputData(() => {})).toThrow(MarkdownTableError);
|
121
|
+
});
|
122
|
+
});
|
123
|
+
|
124
|
+
describe('empty array input', () => {
|
125
|
+
it('should throw MarkdownTableError for empty array', () => {
|
126
|
+
expect(() => validateInputData([])).toThrow(MarkdownTableError);
|
127
|
+
expect(() => validateInputData([])).toThrow(
|
128
|
+
"The 'data' array must contain at least one row."
|
129
|
+
);
|
130
|
+
});
|
131
|
+
});
|
132
|
+
|
133
|
+
describe('edge cases', () => {
|
134
|
+
it('should accept array with nested empty arrays', () => {
|
135
|
+
const data = [[]];
|
136
|
+
expect(() => validateInputData(data)).not.toThrow();
|
137
|
+
});
|
138
|
+
|
139
|
+
it('should accept array with multiple empty nested arrays', () => {
|
140
|
+
const data = [[], [], []];
|
141
|
+
expect(() => validateInputData(data)).not.toThrow();
|
142
|
+
});
|
143
|
+
|
144
|
+
it('should accept array with special characters', () => {
|
145
|
+
const data = [['!@#$%', '^&*()', '{}[]<>']];
|
146
|
+
expect(() => validateInputData(data)).not.toThrow();
|
147
|
+
});
|
148
|
+
|
149
|
+
it('should accept array with unicode characters', () => {
|
150
|
+
const data = [['日本語', '한국어', 'العربية']];
|
151
|
+
expect(() => validateInputData(data)).not.toThrow();
|
152
|
+
});
|
153
|
+
|
154
|
+
it('should accept array with newline characters in cells', () => {
|
155
|
+
const data = [['Line1\nLine2', 'Normal', 'Another\nMultiline']];
|
156
|
+
expect(() => validateInputData(data)).not.toThrow();
|
157
|
+
});
|
158
|
+
|
159
|
+
it('should accept array with very long strings', () => {
|
160
|
+
const longString = 'A'.repeat(10000);
|
161
|
+
const data = [[longString, 'B', 'C']];
|
162
|
+
expect(() => validateInputData(data)).not.toThrow();
|
163
|
+
});
|
164
|
+
});
|
165
|
+
});
|
166
|
+
|