testomatio-editor-blocks 0.4.34 → 0.4.36
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/package/editor/blocks/step.js +0 -29
- package/package/editor/blocks/stepField.js +167 -34
- package/package/editor/customMarkdownConverter.js +47 -25
- package/package/styles.css +8 -1
- package/package.json +1 -1
- package/src/App.tsx +2 -7
- package/src/editor/blocks/step.tsx +0 -37
- package/src/editor/blocks/stepField.tsx +172 -34
- package/src/editor/customMarkdownConverter.test.ts +371 -0
- package/src/editor/customMarkdownConverter.ts +49 -25
- package/src/editor/styles.css +8 -1
|
@@ -105,6 +105,67 @@ describe("blocksToMarkdown", () => {
|
|
|
105
105
|
);
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
it("serializes a step with empty title but with stepData", () => {
|
|
109
|
+
const blocks: CustomEditorBlock[] = [
|
|
110
|
+
{
|
|
111
|
+
id: "s1",
|
|
112
|
+
type: "testStep",
|
|
113
|
+
props: {
|
|
114
|
+
stepTitle: "",
|
|
115
|
+
stepData: "Navigate to the page",
|
|
116
|
+
expectedResult: "",
|
|
117
|
+
listStyle: "bullet",
|
|
118
|
+
},
|
|
119
|
+
content: undefined,
|
|
120
|
+
children: [],
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
expect(blocksToMarkdown(blocks)).toBe(
|
|
125
|
+
["* ", " Navigate to the page"].join("\n"),
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("serializes a step with empty title but with expectedResult", () => {
|
|
130
|
+
const blocks: CustomEditorBlock[] = [
|
|
131
|
+
{
|
|
132
|
+
id: "s1",
|
|
133
|
+
type: "testStep",
|
|
134
|
+
props: {
|
|
135
|
+
stepTitle: "",
|
|
136
|
+
stepData: "",
|
|
137
|
+
expectedResult: "Login form visible",
|
|
138
|
+
listStyle: "bullet",
|
|
139
|
+
},
|
|
140
|
+
content: undefined,
|
|
141
|
+
children: [],
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
expect(blocksToMarkdown(blocks)).toBe(
|
|
146
|
+
["* ", " *Expected*: Login form visible"].join("\n"),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("drops completely empty step (no title, no data, no expected)", () => {
|
|
151
|
+
const blocks: CustomEditorBlock[] = [
|
|
152
|
+
{
|
|
153
|
+
id: "s1",
|
|
154
|
+
type: "testStep",
|
|
155
|
+
props: {
|
|
156
|
+
stepTitle: "",
|
|
157
|
+
stepData: "",
|
|
158
|
+
expectedResult: "",
|
|
159
|
+
listStyle: "bullet",
|
|
160
|
+
},
|
|
161
|
+
content: undefined,
|
|
162
|
+
children: [],
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
expect(blocksToMarkdown(blocks)).toBe("");
|
|
167
|
+
});
|
|
168
|
+
|
|
108
169
|
it("serializes a snippet block with prefixed title", () => {
|
|
109
170
|
const blocks: CustomEditorBlock[] = [
|
|
110
171
|
{
|
|
@@ -328,6 +389,116 @@ describe("blocksToMarkdown", () => {
|
|
|
328
389
|
);
|
|
329
390
|
});
|
|
330
391
|
|
|
392
|
+
it("serializes table cells containing newlines as <br/>", () => {
|
|
393
|
+
const blocks: CustomEditorBlock[] = [
|
|
394
|
+
{
|
|
395
|
+
id: "tbl2",
|
|
396
|
+
type: "table",
|
|
397
|
+
props: { textColor: "default" },
|
|
398
|
+
content: {
|
|
399
|
+
type: "tableContent",
|
|
400
|
+
columnWidths: [undefined, undefined],
|
|
401
|
+
headerRows: 1,
|
|
402
|
+
rows: [
|
|
403
|
+
{
|
|
404
|
+
cells: [
|
|
405
|
+
{
|
|
406
|
+
type: "tableCell",
|
|
407
|
+
props: cellProps,
|
|
408
|
+
content: [{ type: "text", text: "Steps", styles: {} }],
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
type: "tableCell",
|
|
412
|
+
props: cellProps,
|
|
413
|
+
content: [{ type: "text", text: "Expected Results", styles: {} }],
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
cells: [
|
|
419
|
+
{
|
|
420
|
+
type: "tableCell",
|
|
421
|
+
props: cellProps,
|
|
422
|
+
content: [{ type: "text", text: "line1\nline2", styles: {} }],
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
type: "tableCell",
|
|
426
|
+
props: cellProps,
|
|
427
|
+
content: [{ type: "text", text: "ok", styles: {} }],
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
},
|
|
433
|
+
children: [],
|
|
434
|
+
},
|
|
435
|
+
];
|
|
436
|
+
|
|
437
|
+
expect(blocksToMarkdown(blocks)).toBe(
|
|
438
|
+
[
|
|
439
|
+
"| Steps | Expected Results |",
|
|
440
|
+
"| --- | --- |",
|
|
441
|
+
"| line1<br/>line2 | ok |",
|
|
442
|
+
].join("\n"),
|
|
443
|
+
);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it("serializes table cells with styled text and newlines without trapping <br/> inside markers", () => {
|
|
447
|
+
const blocks: CustomEditorBlock[] = [
|
|
448
|
+
{
|
|
449
|
+
id: "tbl3",
|
|
450
|
+
type: "table",
|
|
451
|
+
props: { textColor: "default" },
|
|
452
|
+
content: {
|
|
453
|
+
type: "tableContent",
|
|
454
|
+
columnWidths: [undefined, undefined],
|
|
455
|
+
headerRows: 1,
|
|
456
|
+
rows: [
|
|
457
|
+
{
|
|
458
|
+
cells: [
|
|
459
|
+
{
|
|
460
|
+
type: "tableCell",
|
|
461
|
+
props: cellProps,
|
|
462
|
+
content: [{ type: "text", text: "Col A", styles: {} }],
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
type: "tableCell",
|
|
466
|
+
props: cellProps,
|
|
467
|
+
content: [{ type: "text", text: "Col B", styles: {} }],
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
cells: [
|
|
473
|
+
{
|
|
474
|
+
type: "tableCell",
|
|
475
|
+
props: cellProps,
|
|
476
|
+
content: [{ type: "text", text: "ok", styles: {} }],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
type: "tableCell",
|
|
480
|
+
props: cellProps,
|
|
481
|
+
content: [
|
|
482
|
+
{ type: "text", text: "opened\nnewline", styles: { bold: true } },
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
},
|
|
489
|
+
children: [],
|
|
490
|
+
},
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
expect(blocksToMarkdown(blocks)).toBe(
|
|
494
|
+
[
|
|
495
|
+
"| Col A | Col B |",
|
|
496
|
+
"| --- | --- |",
|
|
497
|
+
"| ok | **opened**<br/>**newline** |",
|
|
498
|
+
].join("\n"),
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
331
502
|
it("parses a test step with inline image in the title, moving the image to step data", () => {
|
|
332
503
|
const markdown = [
|
|
333
504
|
"## Steps",
|
|
@@ -365,6 +536,55 @@ describe("markdownToBlocks", () => {
|
|
|
365
536
|
]);
|
|
366
537
|
});
|
|
367
538
|
|
|
539
|
+
it("parses a step with empty title but with step data", () => {
|
|
540
|
+
const markdown = ["* ", " Navigate to the page"].join("\n");
|
|
541
|
+
|
|
542
|
+
expect(markdownToBlocks(markdown)).toEqual([
|
|
543
|
+
{
|
|
544
|
+
type: "testStep",
|
|
545
|
+
props: {
|
|
546
|
+
stepTitle: "",
|
|
547
|
+
stepData: "Navigate to the page",
|
|
548
|
+
expectedResult: "",
|
|
549
|
+
listStyle: "bullet",
|
|
550
|
+
},
|
|
551
|
+
children: [],
|
|
552
|
+
},
|
|
553
|
+
]);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("round-trips a title-less step with data", () => {
|
|
557
|
+
const blocks: CustomEditorBlock[] = [
|
|
558
|
+
{
|
|
559
|
+
id: "s1",
|
|
560
|
+
type: "testStep",
|
|
561
|
+
props: {
|
|
562
|
+
stepTitle: "",
|
|
563
|
+
stepData: "Open the browser",
|
|
564
|
+
expectedResult: "Page loads",
|
|
565
|
+
listStyle: "bullet",
|
|
566
|
+
},
|
|
567
|
+
content: undefined,
|
|
568
|
+
children: [],
|
|
569
|
+
},
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const md = blocksToMarkdown(blocks);
|
|
573
|
+
const parsed = markdownToBlocks(md);
|
|
574
|
+
expect(parsed).toEqual([
|
|
575
|
+
{
|
|
576
|
+
type: "testStep",
|
|
577
|
+
props: {
|
|
578
|
+
stepTitle: "",
|
|
579
|
+
stepData: "Open the browser",
|
|
580
|
+
expectedResult: "Page loads",
|
|
581
|
+
listStyle: "bullet",
|
|
582
|
+
},
|
|
583
|
+
children: [],
|
|
584
|
+
},
|
|
585
|
+
]);
|
|
586
|
+
});
|
|
587
|
+
|
|
368
588
|
it("parses snippet markdown into snippet blocks", () => {
|
|
369
589
|
const markdown = [
|
|
370
590
|
"<!-- begin snippet #501 -->",
|
|
@@ -1212,6 +1432,114 @@ describe("markdownToBlocks", () => {
|
|
|
1212
1432
|
]);
|
|
1213
1433
|
});
|
|
1214
1434
|
|
|
1435
|
+
it("parses <br/> in table cells back to newline", () => {
|
|
1436
|
+
const markdown = [
|
|
1437
|
+
"| A | B |",
|
|
1438
|
+
"| --- | --- |",
|
|
1439
|
+
"| line1<br/>line2 | ok |",
|
|
1440
|
+
].join("\n");
|
|
1441
|
+
|
|
1442
|
+
const blocks = markdownToBlocks(markdown);
|
|
1443
|
+
expect(blocks).toEqual([
|
|
1444
|
+
{
|
|
1445
|
+
type: "table",
|
|
1446
|
+
props: { textColor: "default" },
|
|
1447
|
+
content: {
|
|
1448
|
+
type: "tableContent",
|
|
1449
|
+
columnWidths: [undefined, undefined],
|
|
1450
|
+
headerRows: 1,
|
|
1451
|
+
rows: [
|
|
1452
|
+
{
|
|
1453
|
+
cells: [
|
|
1454
|
+
{
|
|
1455
|
+
type: "tableCell",
|
|
1456
|
+
props: cellProps,
|
|
1457
|
+
content: [{ type: "text", text: "A", styles: {} }],
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
type: "tableCell",
|
|
1461
|
+
props: cellProps,
|
|
1462
|
+
content: [{ type: "text", text: "B", styles: {} }],
|
|
1463
|
+
},
|
|
1464
|
+
],
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
cells: [
|
|
1468
|
+
{
|
|
1469
|
+
type: "tableCell",
|
|
1470
|
+
props: cellProps,
|
|
1471
|
+
content: [{ type: "text", text: "line1\nline2", styles: {} }],
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
type: "tableCell",
|
|
1475
|
+
props: cellProps,
|
|
1476
|
+
content: [{ type: "text", text: "ok", styles: {} }],
|
|
1477
|
+
},
|
|
1478
|
+
],
|
|
1479
|
+
},
|
|
1480
|
+
],
|
|
1481
|
+
},
|
|
1482
|
+
children: [],
|
|
1483
|
+
},
|
|
1484
|
+
]);
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
it("round-trips newlines in table cells", () => {
|
|
1488
|
+
const blocks: CustomEditorBlock[] = [
|
|
1489
|
+
{
|
|
1490
|
+
id: "tbl3",
|
|
1491
|
+
type: "table",
|
|
1492
|
+
props: { textColor: "default" },
|
|
1493
|
+
content: {
|
|
1494
|
+
type: "tableContent",
|
|
1495
|
+
columnWidths: [undefined, undefined],
|
|
1496
|
+
headerRows: 1,
|
|
1497
|
+
rows: [
|
|
1498
|
+
{
|
|
1499
|
+
cells: [
|
|
1500
|
+
{
|
|
1501
|
+
type: "tableCell",
|
|
1502
|
+
props: cellProps,
|
|
1503
|
+
content: [{ type: "text", text: "Header", styles: {} }],
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
type: "tableCell",
|
|
1507
|
+
props: cellProps,
|
|
1508
|
+
content: [{ type: "text", text: "Info", styles: {} }],
|
|
1509
|
+
},
|
|
1510
|
+
],
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
cells: [
|
|
1514
|
+
{
|
|
1515
|
+
type: "tableCell",
|
|
1516
|
+
props: cellProps,
|
|
1517
|
+
content: [{ type: "text", text: "first\nsecond\nthird", styles: {} }],
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
type: "tableCell",
|
|
1521
|
+
props: cellProps,
|
|
1522
|
+
content: [{ type: "text", text: "value", styles: {} }],
|
|
1523
|
+
},
|
|
1524
|
+
],
|
|
1525
|
+
},
|
|
1526
|
+
],
|
|
1527
|
+
},
|
|
1528
|
+
children: [],
|
|
1529
|
+
},
|
|
1530
|
+
];
|
|
1531
|
+
|
|
1532
|
+
const markdown = blocksToMarkdown(blocks);
|
|
1533
|
+
expect(markdown).toContain("first<br/>second<br/>third");
|
|
1534
|
+
|
|
1535
|
+
const parsed = markdownToBlocks(markdown);
|
|
1536
|
+
const row = (parsed[0] as any).content.rows[1];
|
|
1537
|
+
const cellContent = row.cells[0].content;
|
|
1538
|
+
expect(cellContent).toEqual([
|
|
1539
|
+
{ type: "text", text: "first\nsecond\nthird", styles: {} },
|
|
1540
|
+
]);
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1215
1543
|
it("parses expected result lines written with bold 'Expected Result' prefix for compatibility", () => {
|
|
1216
1544
|
const markdown = [
|
|
1217
1545
|
"* Step 1: Send a chat message to the user.",
|
|
@@ -1823,3 +2151,46 @@ describe("file block parsing", () => {
|
|
|
1823
2151
|
expect(md).toBe(markdown);
|
|
1824
2152
|
});
|
|
1825
2153
|
});
|
|
2154
|
+
|
|
2155
|
+
describe("video/audio block serialization", () => {
|
|
2156
|
+
it("serializes a video block using the file format", () => {
|
|
2157
|
+
const blocks: CustomEditorBlock[] = [
|
|
2158
|
+
{
|
|
2159
|
+
id: "1",
|
|
2160
|
+
type: "video",
|
|
2161
|
+
props: {
|
|
2162
|
+
...baseProps,
|
|
2163
|
+
url: "https://example.com/video.mp4",
|
|
2164
|
+
name: "recording.mp4",
|
|
2165
|
+
caption: "/images/file-type-icons/mp4.svg",
|
|
2166
|
+
showPreview: true,
|
|
2167
|
+
previewWidth: 512,
|
|
2168
|
+
},
|
|
2169
|
+
content: undefined as any,
|
|
2170
|
+
children: [],
|
|
2171
|
+
},
|
|
2172
|
+
];
|
|
2173
|
+
const md = blocksToMarkdown(blocks);
|
|
2174
|
+
expect(md).toBe("[](https://example.com/video.mp4)");
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
it("serializes an audio block using the file format", () => {
|
|
2178
|
+
const blocks: CustomEditorBlock[] = [
|
|
2179
|
+
{
|
|
2180
|
+
id: "1",
|
|
2181
|
+
type: "audio",
|
|
2182
|
+
props: {
|
|
2183
|
+
...baseProps,
|
|
2184
|
+
url: "https://example.com/sound.mp3",
|
|
2185
|
+
name: "sound.mp3",
|
|
2186
|
+
caption: "/images/file-type-icons/file.svg",
|
|
2187
|
+
showPreview: true,
|
|
2188
|
+
},
|
|
2189
|
+
content: undefined as any,
|
|
2190
|
+
children: [],
|
|
2191
|
+
},
|
|
2192
|
+
];
|
|
2193
|
+
const md = blocksToMarkdown(blocks);
|
|
2194
|
+
expect(md).toBe("[](https://example.com/sound.mp3)");
|
|
2195
|
+
});
|
|
2196
|
+
});
|
|
@@ -174,12 +174,21 @@ function applyTextStyles(text: string, styles: EditorStyles | undefined): string
|
|
|
174
174
|
});
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
// Split on newlines so that style markers wrap each line individually.
|
|
178
|
+
// This prevents <br/> (inserted by table cell formatting) from being
|
|
179
|
+
// trapped inside markers like **bold<br/>text**.
|
|
180
|
+
const segments = result.split("\n");
|
|
181
|
+
const wrapped = segments.map((segment) => {
|
|
182
|
+
if (!segment) return segment;
|
|
183
|
+
let s = segment;
|
|
184
|
+
for (const wrapper of wrappers) {
|
|
185
|
+
const suffix = wrapper.suffix ?? wrapper.prefix;
|
|
186
|
+
s = `${wrapper.prefix}${s}${suffix}`;
|
|
187
|
+
}
|
|
188
|
+
return s;
|
|
189
|
+
});
|
|
181
190
|
|
|
182
|
-
return
|
|
191
|
+
return wrapped.join("\n");
|
|
183
192
|
}
|
|
184
193
|
|
|
185
194
|
function inlineToMarkdown(content: CustomEditorBlock["content"]): string {
|
|
@@ -347,7 +356,9 @@ function serializeBlock(
|
|
|
347
356
|
}
|
|
348
357
|
return flattenWithBlankLine(lines, true);
|
|
349
358
|
}
|
|
350
|
-
case "file":
|
|
359
|
+
case "file":
|
|
360
|
+
case "video":
|
|
361
|
+
case "audio": {
|
|
351
362
|
const url = (block.props as any).url || "";
|
|
352
363
|
const name = (block.props as any).name || "";
|
|
353
364
|
const caption = (block.props as any).caption || "";
|
|
@@ -390,18 +401,19 @@ function serializeBlock(
|
|
|
390
401
|
return flattenWithBlankLine(lines, true);
|
|
391
402
|
}
|
|
392
403
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
404
|
+
const normalizedTitle = stepTitle
|
|
405
|
+
.split(/\r?\n/)
|
|
406
|
+
.map((segment: string) => segment.trim())
|
|
407
|
+
.filter((segment: string) => segment.length > 0)
|
|
408
|
+
.join(" ");
|
|
409
|
+
|
|
410
|
+
const normalizedExpectedForCheck = stripExpectedPrefix(expectedResult).trim();
|
|
411
|
+
const hasContent = stepData.length > 0 || normalizedExpectedForCheck.length > 0;
|
|
412
|
+
|
|
413
|
+
if (normalizedTitle.length > 0 || hasContent) {
|
|
414
|
+
const listStyle = (block.props as any).listStyle ?? "bullet";
|
|
415
|
+
const prefix = listStyle === "ordered" ? `${(stepIndex ?? 0) + 1}.` : "*";
|
|
416
|
+
lines.push(normalizedTitle.length > 0 ? `${prefix} ${normalizedTitle}` : `${prefix} `);
|
|
405
417
|
}
|
|
406
418
|
|
|
407
419
|
if (stepData.length > 0) {
|
|
@@ -511,7 +523,10 @@ function serializeBlock(
|
|
|
511
523
|
};
|
|
512
524
|
|
|
513
525
|
const formattedRows = rows.map(normalizeRow);
|
|
514
|
-
const formatCell = (value: string) =>
|
|
526
|
+
const formatCell = (value: string) => {
|
|
527
|
+
if (!value.length) return " ";
|
|
528
|
+
return value.replace(/\n/g, "<br/>");
|
|
529
|
+
};
|
|
515
530
|
const toAlignmentToken = (alignment: string) => {
|
|
516
531
|
switch (alignment) {
|
|
517
532
|
case "center":
|
|
@@ -694,6 +709,13 @@ function parseInlineMarkdown(text: string): EditorInline[] {
|
|
|
694
709
|
}
|
|
695
710
|
}
|
|
696
711
|
|
|
712
|
+
const brMatch = cleaned.slice(i).match(/^<br\s*\/?\s*>/i);
|
|
713
|
+
if (brMatch) {
|
|
714
|
+
buffer += "\n";
|
|
715
|
+
i += brMatch[0].length;
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
|
|
697
719
|
buffer += cleaned[i];
|
|
698
720
|
i += 1;
|
|
699
721
|
}
|
|
@@ -722,7 +744,7 @@ function detectListType(trimmed: string): "bullet" | "numbered" | "check" | null
|
|
|
722
744
|
if (/^\d+[.)]\s+/.test(trimmed)) {
|
|
723
745
|
return "numbered";
|
|
724
746
|
}
|
|
725
|
-
if (/^[-*+]\s
|
|
747
|
+
if (/^[-*+](\s+|$)/.test(trimmed)) {
|
|
726
748
|
return "bullet";
|
|
727
749
|
}
|
|
728
750
|
return null;
|
|
@@ -838,7 +860,7 @@ function parseList(
|
|
|
838
860
|
});
|
|
839
861
|
} else {
|
|
840
862
|
const bulletMatch = trimmed.match(/^[-*+]\s+(.*)$/);
|
|
841
|
-
const text = bulletMatch?.[1] ?? trimmed.slice(2);
|
|
863
|
+
const text = bulletMatch?.[1] ?? (trimmed.length <= 1 ? "" : trimmed.slice(2));
|
|
842
864
|
items.push({
|
|
843
865
|
type: "bulletListItem",
|
|
844
866
|
props: cloneBaseProps(),
|
|
@@ -868,7 +890,7 @@ function isLikelyStep(lines: string[], index: number): boolean {
|
|
|
868
890
|
if (hasIndent) return true;
|
|
869
891
|
|
|
870
892
|
// Stop at new list items, headings, or other block-level elements (only if not indented)
|
|
871
|
-
if (/^[-*+]\s/.test(trimmed) || /^\d+[.)]\s/.test(trimmed)) break;
|
|
893
|
+
if (/^[-*+](\s|$)/.test(trimmed) || /^\d+[.)]\s/.test(trimmed)) break;
|
|
872
894
|
if (trimmed.startsWith("#") || trimmed.startsWith(">") || trimmed.startsWith("|") || trimmed.startsWith("```") || trimmed.startsWith(":::")) break;
|
|
873
895
|
|
|
874
896
|
// Check for expected result markers
|
|
@@ -887,7 +909,7 @@ function parseTestStep(
|
|
|
887
909
|
): { block: CustomPartialBlock; nextIndex: number } | null {
|
|
888
910
|
const current = lines[index];
|
|
889
911
|
const trimmed = current.trim();
|
|
890
|
-
const isBullet = trimmed.startsWith("* ") || trimmed.startsWith("- ");
|
|
912
|
+
const isBullet = trimmed.startsWith("* ") || trimmed.startsWith("- ") || trimmed === "*" || trimmed === "-";
|
|
891
913
|
const isNumbered = /^\d+[.)]\s+/.test(trimmed);
|
|
892
914
|
|
|
893
915
|
if (!isBullet && !isNumbered) {
|
|
@@ -903,7 +925,9 @@ function parseTestStep(
|
|
|
903
925
|
|
|
904
926
|
let rawTitle: string;
|
|
905
927
|
if (isBullet) {
|
|
906
|
-
rawTitle = unescapeMarkdown(
|
|
928
|
+
rawTitle = unescapeMarkdown(
|
|
929
|
+
(trimmed.startsWith("* ") || trimmed.startsWith("- ")) ? trimmed.slice(2) : trimmed.slice(1)
|
|
930
|
+
).trim();
|
|
907
931
|
} else {
|
|
908
932
|
// For numbered lists, remove the number and delimiter
|
|
909
933
|
rawTitle = unescapeMarkdown(trimmed.replace(/^\d+[.)]\s+/, "")).trim();
|
|
@@ -951,7 +975,7 @@ function parseTestStep(
|
|
|
951
975
|
}
|
|
952
976
|
const isNumberedStep = NUMBERED_STEP_REGEX.test(rawTrimmed);
|
|
953
977
|
const isNewStep =
|
|
954
|
-
(!hasIndent && (rawTrimmed.startsWith("* ") || rawTrimmed.startsWith("- "))) ||
|
|
978
|
+
(!hasIndent && (rawTrimmed.startsWith("* ") || rawTrimmed.startsWith("- ") || rawTrimmed === "*" || rawTrimmed === "-")) ||
|
|
955
979
|
(!hasIndent && isNumberedStep);
|
|
956
980
|
|
|
957
981
|
if (isNewStep) {
|
package/src/editor/styles.css
CHANGED
|
@@ -1074,7 +1074,8 @@ html.dark .bn-step-image-preview__content {
|
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
1076
|
.bn-step-editor .overtype-wrapper .overtype-preview strong.step-preview-bold {
|
|
1077
|
-
|
|
1077
|
+
-webkit-text-stroke: 0.5px currentColor;
|
|
1078
|
+
font-weight: inherit !important;
|
|
1078
1079
|
color: inherit !important;
|
|
1079
1080
|
}
|
|
1080
1081
|
|
|
@@ -1083,6 +1084,12 @@ html.dark .bn-step-image-preview__content {
|
|
|
1083
1084
|
color: inherit !important;
|
|
1084
1085
|
}
|
|
1085
1086
|
|
|
1087
|
+
.bn-step-editor .overtype-wrapper .overtype-preview code.step-preview-code {
|
|
1088
|
+
background-color: rgba(135, 131, 120, 0.15) !important;
|
|
1089
|
+
border-radius: 3px !important;
|
|
1090
|
+
color: inherit !important;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1086
1093
|
.bn-step-custom-caret {
|
|
1087
1094
|
display: none;
|
|
1088
1095
|
position: absolute;
|