testomatio-editor-blocks 0.4.55 → 0.4.56
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.
|
@@ -22,7 +22,7 @@ const headingPrefixes = {
|
|
|
22
22
|
5: "#####",
|
|
23
23
|
6: "######",
|
|
24
24
|
};
|
|
25
|
-
const SPECIAL_CHAR_REGEX = /([*_
|
|
25
|
+
const SPECIAL_CHAR_REGEX = /([*_`~()<\\])/g;
|
|
26
26
|
const HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
|
|
27
27
|
const HTML_SPAN_REGEX = /<\/?span[^>]*>/g;
|
|
28
28
|
const HTML_UNDERLINE_REGEX = /<\/?u>/g;
|
|
@@ -43,6 +43,9 @@ function escapeMarkdown(text) {
|
|
|
43
43
|
result += text.slice(lastIndex).replace(SPECIAL_CHAR_REGEX, "\\$1");
|
|
44
44
|
return result;
|
|
45
45
|
}
|
|
46
|
+
function escapeStepContent(text) {
|
|
47
|
+
return text.replace(/\./g, "\\.");
|
|
48
|
+
}
|
|
46
49
|
function stripHtmlWrappers(text) {
|
|
47
50
|
return text
|
|
48
51
|
.replace(HTML_SPAN_REGEX, "")
|
|
@@ -57,7 +60,7 @@ function stripExpectedPrefix(text) {
|
|
|
57
60
|
let remainder = text.slice(label.length).trimStart();
|
|
58
61
|
const cleanupLeading = (value) => {
|
|
59
62
|
let result = value.trimStart();
|
|
60
|
-
result = result.replace(/^\\+(?=[*_`~:
|
|
63
|
+
result = result.replace(/^\\+(?=[*_`~:])/, "");
|
|
61
64
|
result = result.replace(/^(?:[*_`~]+)(?=\s|$)/, "");
|
|
62
65
|
return result.trimStart();
|
|
63
66
|
};
|
|
@@ -88,7 +91,7 @@ function stripLeadingFormatting(text) {
|
|
|
88
91
|
return result;
|
|
89
92
|
}
|
|
90
93
|
function unescapeMarkdown(text) {
|
|
91
|
-
return stripHtmlWrappers(text).replace(/\\([*_`~\[\]()
|
|
94
|
+
return stripHtmlWrappers(text).replace(/\\([*_`~\[\]()<>.\\])/g, "$1").replace(/\\>/g, ">");
|
|
92
95
|
}
|
|
93
96
|
function applyTextStyles(text, styles) {
|
|
94
97
|
var _a, _b, _c, _d;
|
|
@@ -348,14 +351,14 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
|
|
|
348
351
|
if (normalizedTitle.length > 0 || hasContent) {
|
|
349
352
|
const listStyle = (_m = block.props.listStyle) !== null && _m !== void 0 ? _m : "bullet";
|
|
350
353
|
const prefix = listStyle === "ordered" ? `${(stepIndex !== null && stepIndex !== void 0 ? stepIndex : 0) + 1}.` : "*";
|
|
351
|
-
lines.push(normalizedTitle.length > 0 ? `${prefix} ${normalizedTitle}` : `${prefix} `);
|
|
354
|
+
lines.push(normalizedTitle.length > 0 ? `${prefix} ${escapeStepContent(normalizedTitle)}` : `${prefix} `);
|
|
352
355
|
}
|
|
353
356
|
if (stepData.length > 0) {
|
|
354
357
|
const dataLines = stepData.split(/\r?\n/);
|
|
355
358
|
dataLines.forEach((dataLine) => {
|
|
356
359
|
const trimmedLine = dataLine.trim();
|
|
357
360
|
if (trimmedLine.length > 0) {
|
|
358
|
-
lines.push(` ${trimmedLine}`);
|
|
361
|
+
lines.push(` ${escapeStepContent(trimmedLine)}`);
|
|
359
362
|
}
|
|
360
363
|
else {
|
|
361
364
|
lines.push(" ");
|
|
@@ -372,10 +375,10 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
|
|
|
372
375
|
return;
|
|
373
376
|
}
|
|
374
377
|
if (index === 0) {
|
|
375
|
-
lines.push(` ${label}: ${trimmedLine}`);
|
|
378
|
+
lines.push(` ${label}: ${escapeStepContent(trimmedLine)}`);
|
|
376
379
|
}
|
|
377
380
|
else {
|
|
378
|
-
lines.push(` ${trimmedLine}`);
|
|
381
|
+
lines.push(` ${escapeStepContent(trimmedLine)}`);
|
|
379
382
|
}
|
|
380
383
|
});
|
|
381
384
|
}
|
|
@@ -841,6 +844,7 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
841
844
|
let next = index + 1;
|
|
842
845
|
let inExpectedResult = false;
|
|
843
846
|
let blankLineSeenOutsideCodeBlock = false;
|
|
847
|
+
let blankLineSeenInExpectedResult = false;
|
|
844
848
|
const stepIndent = current.length - current.trimStart().length;
|
|
845
849
|
while (next < lines.length) {
|
|
846
850
|
const line = lines[next];
|
|
@@ -848,14 +852,15 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
848
852
|
const hasIndent = lineIndent > stepIndent;
|
|
849
853
|
const rawTrimmed = line.trim();
|
|
850
854
|
if (!rawTrimmed) {
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
855
|
+
if (inExpectedResult) {
|
|
856
|
+
expectedResult += "\n";
|
|
857
|
+
blankLineSeenInExpectedResult = true;
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
if (stepDataLines.length > 0) {
|
|
856
861
|
stepDataLines.push("");
|
|
857
|
-
blankLineSeenOutsideCodeBlock = true;
|
|
858
862
|
}
|
|
863
|
+
blankLineSeenOutsideCodeBlock = true;
|
|
859
864
|
}
|
|
860
865
|
next += 1;
|
|
861
866
|
continue;
|
|
@@ -947,7 +952,13 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
947
952
|
continue;
|
|
948
953
|
}
|
|
949
954
|
if (inExpectedResult) {
|
|
950
|
-
// After
|
|
955
|
+
// After a blank line inside the expected result, a non-indented line
|
|
956
|
+
// belongs to the outer document (e.g. a trailing file block after the
|
|
957
|
+
// step list), so stop here and let the root parser handle it.
|
|
958
|
+
if (blankLineSeenInExpectedResult && !hasIndent) {
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
// Otherwise, indented lines are part of the expected result
|
|
951
962
|
if (hasIndent) {
|
|
952
963
|
const expectedContent = unescapeMarkdown(rawTrimmed);
|
|
953
964
|
if (expectedResult.length > 0) {
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
filterSuggestionItems,
|
|
12
12
|
insertOrUpdateBlock,
|
|
13
13
|
} from "@blocknote/core";
|
|
14
|
-
import {
|
|
14
|
+
import { flip, offset, shift, size } from "@floating-ui/react";
|
|
15
15
|
import {
|
|
16
16
|
blocksToMarkdown,
|
|
17
17
|
markdownToBlocks,
|
|
@@ -362,8 +362,13 @@ function CustomSlashMenu() {
|
|
|
362
362
|
floatingOptions={{
|
|
363
363
|
middleware: [
|
|
364
364
|
offset(10),
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
size({
|
|
366
|
+
apply({ elements }) {
|
|
367
|
+
Object.assign(elements.floating.style, { maxHeight: "" });
|
|
368
|
+
},
|
|
369
|
+
}),
|
|
370
|
+
flip({
|
|
371
|
+
fallbackPlacements: ["top-start"],
|
|
367
372
|
}),
|
|
368
373
|
shift(),
|
|
369
374
|
size({
|
|
@@ -196,10 +196,10 @@ describe("blocksToMarkdown", () => {
|
|
|
196
196
|
|
|
197
197
|
expect(blocksToMarkdown(blocks)).toBe(
|
|
198
198
|
[
|
|
199
|
-
"* Open the Login page
|
|
200
|
-
" *Expected result*: The Login page loads successfully
|
|
201
|
-
"* Enter a valid username
|
|
202
|
-
" *Expected result*: The username is accepted
|
|
199
|
+
"* Open the Login page\\.",
|
|
200
|
+
" *Expected result*: The Login page loads successfully\\.",
|
|
201
|
+
"* Enter a valid username\\.",
|
|
202
|
+
" *Expected result*: The username is accepted\\.",
|
|
203
203
|
].join("\n"),
|
|
204
204
|
);
|
|
205
205
|
});
|
|
@@ -417,7 +417,7 @@ describe("blocksToMarkdown", () => {
|
|
|
417
417
|
|
|
418
418
|
expect(blocksToMarkdown(blocks)).toBe(
|
|
419
419
|
[
|
|
420
|
-
"* Update an order status
|
|
420
|
+
"* Update an order status\\.",
|
|
421
421
|
" ```",
|
|
422
422
|
" SQL CREATE bnbmnbm mnbmb mm",
|
|
423
423
|
" mn,nm nm, m,nm,n,nn,m,",
|
|
@@ -428,8 +428,8 @@ describe("blocksToMarkdown", () => {
|
|
|
428
428
|
" ",
|
|
429
429
|
" asdsadas",
|
|
430
430
|
" ```",
|
|
431
|
-
" ",
|
|
432
|
+
" *Expected result*: The user receives a real-time notification for the order update\\.",
|
|
433
433
|
].join("\n"),
|
|
434
434
|
);
|
|
435
435
|
});
|
|
@@ -1061,7 +1061,7 @@ describe("markdownToBlocks", () => {
|
|
|
1061
1061
|
|
|
1062
1062
|
expect(markdownRoundTrip).toBe(
|
|
1063
1063
|
[
|
|
1064
|
-
"* Step 2: Update an order status
|
|
1064
|
+
"* Step 2: Update an order status\\.",
|
|
1065
1065
|
" ```",
|
|
1066
1066
|
" SQL CREATE bnbmnbm mnbmb mm",
|
|
1067
1067
|
" mn,nm nm, m,nm,n,nn,m,",
|
|
@@ -1072,8 +1072,8 @@ describe("markdownToBlocks", () => {
|
|
|
1072
1072
|
" ",
|
|
1073
1073
|
" asdsadas",
|
|
1074
1074
|
" ```",
|
|
1075
|
-
" ",
|
|
1076
|
+
" *Expected result*: The user receives a real-time notification for the order update\\.",
|
|
1077
1077
|
].join("\n"),
|
|
1078
1078
|
);
|
|
1079
1079
|
});
|
|
@@ -1098,6 +1098,85 @@ describe("markdownToBlocks", () => {
|
|
|
1098
1098
|
});
|
|
1099
1099
|
});
|
|
1100
1100
|
|
|
1101
|
+
it("does not include a file block after a blank line in step with expected result", () => {
|
|
1102
|
+
const markdown = [
|
|
1103
|
+
"### Steps",
|
|
1104
|
+
"",
|
|
1105
|
+
"* Open the Login page.",
|
|
1106
|
+
" *Expected*: The Login page loads successfully.",
|
|
1107
|
+
"",
|
|
1108
|
+
"[](https://example.com/file.pdf)",
|
|
1109
|
+
].join("\n");
|
|
1110
|
+
|
|
1111
|
+
const blocks = markdownToBlocks(markdown);
|
|
1112
|
+
const stepBlocks = blocks.filter((b) => b.type === "testStep");
|
|
1113
|
+
const fileBlocks = blocks.filter((b) => b.type === "file");
|
|
1114
|
+
|
|
1115
|
+
expect(stepBlocks).toHaveLength(1);
|
|
1116
|
+
expect(stepBlocks[0].props).toMatchObject({
|
|
1117
|
+
stepTitle: "Open the Login page.",
|
|
1118
|
+
stepData: "",
|
|
1119
|
+
});
|
|
1120
|
+
expect(((stepBlocks[0].props as any).expectedResult ?? "").trim()).toBe(
|
|
1121
|
+
"The Login page loads successfully.",
|
|
1122
|
+
);
|
|
1123
|
+
|
|
1124
|
+
expect(fileBlocks).toHaveLength(1);
|
|
1125
|
+
expect((fileBlocks[0].props as any).url).toBe("https://example.com/file.pdf");
|
|
1126
|
+
expect((fileBlocks[0].props as any).name).toBe("report.pdf");
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it("does not include a file block after a blank line in step with step data", () => {
|
|
1130
|
+
const markdown = [
|
|
1131
|
+
"### Steps",
|
|
1132
|
+
"",
|
|
1133
|
+
"* Open the Login page.",
|
|
1134
|
+
" Enter credentials",
|
|
1135
|
+
"",
|
|
1136
|
+
"[](https://example.com/file.pdf)",
|
|
1137
|
+
].join("\n");
|
|
1138
|
+
|
|
1139
|
+
const blocks = markdownToBlocks(markdown);
|
|
1140
|
+
const stepBlocks = blocks.filter((b) => b.type === "testStep");
|
|
1141
|
+
const fileBlocks = blocks.filter((b) => b.type === "file");
|
|
1142
|
+
|
|
1143
|
+
expect(stepBlocks).toHaveLength(1);
|
|
1144
|
+
expect(stepBlocks[0].props).toMatchObject({
|
|
1145
|
+
stepTitle: "Open the Login page.",
|
|
1146
|
+
stepData: "Enter credentials",
|
|
1147
|
+
expectedResult: "",
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
expect(fileBlocks).toHaveLength(1);
|
|
1151
|
+
expect((fileBlocks[0].props as any).url).toBe("https://example.com/file.pdf");
|
|
1152
|
+
expect((fileBlocks[0].props as any).name).toBe("report.pdf");
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
it("does not include a file block after a blank line in step with title only", () => {
|
|
1156
|
+
const markdown = [
|
|
1157
|
+
"### Steps",
|
|
1158
|
+
"",
|
|
1159
|
+
"* Open the Login page.",
|
|
1160
|
+
"",
|
|
1161
|
+
"[](https://example.com/file.pdf)",
|
|
1162
|
+
].join("\n");
|
|
1163
|
+
|
|
1164
|
+
const blocks = markdownToBlocks(markdown);
|
|
1165
|
+
const stepBlocks = blocks.filter((b) => b.type === "testStep");
|
|
1166
|
+
const fileBlocks = blocks.filter((b) => b.type === "file");
|
|
1167
|
+
|
|
1168
|
+
expect(stepBlocks).toHaveLength(1);
|
|
1169
|
+
expect(stepBlocks[0].props).toMatchObject({
|
|
1170
|
+
stepTitle: "Open the Login page.",
|
|
1171
|
+
stepData: "",
|
|
1172
|
+
expectedResult: "",
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
expect(fileBlocks).toHaveLength(1);
|
|
1176
|
+
expect((fileBlocks[0].props as any).url).toBe("https://example.com/file.pdf");
|
|
1177
|
+
expect((fileBlocks[0].props as any).name).toBe("report.pdf");
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1101
1180
|
it("parses bullet lists written with asterisk markers", () => {
|
|
1102
1181
|
const markdown = [
|
|
1103
1182
|
"### Preconditions",
|
|
@@ -1306,8 +1385,8 @@ describe("markdownToBlocks", () => {
|
|
|
1306
1385
|
|
|
1307
1386
|
expect(markdownRoundTrip).toBe(
|
|
1308
1387
|
[
|
|
1309
|
-
"* Display the generated report
|
|
1310
|
-
" *Expected result*: ",
|
|
1311
1390
|
].join("\n"),
|
|
1312
1391
|
);
|
|
1313
1392
|
});
|
|
@@ -1355,7 +1434,7 @@ describe("markdownToBlocks", () => {
|
|
|
1355
1434
|
[
|
|
1356
1435
|
"* Should open login screen",
|
|
1357
1436
|
" *Expected result*: Login should look like this",
|
|
1358
|
-
" ",
|
|
1359
1438
|
].join("\n"),
|
|
1360
1439
|
);
|
|
1361
1440
|
});
|
|
@@ -2057,10 +2136,10 @@ describe("markdownToBlocks", () => {
|
|
|
2057
2136
|
|
|
2058
2137
|
// Test round-trip conversion — numbered steps preserve their ordered style
|
|
2059
2138
|
const roundTripMarkdown = blocksToMarkdown(blocks as CustomEditorBlock[]);
|
|
2060
|
-
expect(roundTripMarkdown).toContain("1. Navigate to the product listing page
|
|
2061
|
-
expect(roundTripMarkdown).toContain("2. Select a product and click the \"Add to Cart\" button
|
|
2062
|
-
expect(roundTripMarkdown).toContain("3. Open the shopping cart page
|
|
2063
|
-
expect(roundTripMarkdown).toContain("4. Verify that the added item is displayed with the correct name, price, and quantity
|
|
2139
|
+
expect(roundTripMarkdown).toContain("1. Navigate to the product listing page\\.");
|
|
2140
|
+
expect(roundTripMarkdown).toContain("2. Select a product and click the \"Add to Cart\" button\\.");
|
|
2141
|
+
expect(roundTripMarkdown).toContain("3. Open the shopping cart page\\.");
|
|
2142
|
+
expect(roundTripMarkdown).toContain("4. Verify that the added item is displayed with the correct name, price, and quantity\\.");
|
|
2064
2143
|
// Check that step data is preserved
|
|
2065
2144
|
expect(roundTripMarkdown).toContain(" Expected open");
|
|
2066
2145
|
expect(roundTripMarkdown).toContain(" Expected result close");
|
|
@@ -60,7 +60,7 @@ const headingPrefixes: Record<number, string> = {
|
|
|
60
60
|
6: "######",
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
const SPECIAL_CHAR_REGEX = /([*_
|
|
63
|
+
const SPECIAL_CHAR_REGEX = /([*_`~()<\\])/g;
|
|
64
64
|
const HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
|
|
65
65
|
const HTML_SPAN_REGEX = /<\/?span[^>]*>/g;
|
|
66
66
|
const HTML_UNDERLINE_REGEX = /<\/?u>/g;
|
|
@@ -84,6 +84,10 @@ function escapeMarkdown(text: string): string {
|
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function escapeStepContent(text: string): string {
|
|
88
|
+
return text.replace(/\./g, "\\.");
|
|
89
|
+
}
|
|
90
|
+
|
|
87
91
|
function stripHtmlWrappers(text: string): string {
|
|
88
92
|
return text
|
|
89
93
|
.replace(HTML_SPAN_REGEX, "")
|
|
@@ -100,7 +104,7 @@ function stripExpectedPrefix(text: string): string {
|
|
|
100
104
|
|
|
101
105
|
const cleanupLeading = (value: string) => {
|
|
102
106
|
let result = value.trimStart();
|
|
103
|
-
result = result.replace(/^\\+(?=[*_`~:
|
|
107
|
+
result = result.replace(/^\\+(?=[*_`~:])/, "");
|
|
104
108
|
result = result.replace(/^(?:[*_`~]+)(?=\s|$)/, "");
|
|
105
109
|
return result.trimStart();
|
|
106
110
|
};
|
|
@@ -136,7 +140,7 @@ function stripLeadingFormatting(text: string): string {
|
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
function unescapeMarkdown(text: string): string {
|
|
139
|
-
return stripHtmlWrappers(text).replace(/\\([*_`~\[\]()
|
|
143
|
+
return stripHtmlWrappers(text).replace(/\\([*_`~\[\]()<>.\\])/g, "$1").replace(/\\>/g, ">");
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
function applyTextStyles(text: string, styles: EditorStyles | undefined): string {
|
|
@@ -428,7 +432,7 @@ function serializeBlock(
|
|
|
428
432
|
if (normalizedTitle.length > 0 || hasContent) {
|
|
429
433
|
const listStyle = (block.props as any).listStyle ?? "bullet";
|
|
430
434
|
const prefix = listStyle === "ordered" ? `${(stepIndex ?? 0) + 1}.` : "*";
|
|
431
|
-
lines.push(normalizedTitle.length > 0 ? `${prefix} ${normalizedTitle}` : `${prefix} `);
|
|
435
|
+
lines.push(normalizedTitle.length > 0 ? `${prefix} ${escapeStepContent(normalizedTitle)}` : `${prefix} `);
|
|
432
436
|
}
|
|
433
437
|
|
|
434
438
|
if (stepData.length > 0) {
|
|
@@ -436,7 +440,7 @@ function serializeBlock(
|
|
|
436
440
|
dataLines.forEach((dataLine: string) => {
|
|
437
441
|
const trimmedLine = dataLine.trim();
|
|
438
442
|
if (trimmedLine.length > 0) {
|
|
439
|
-
lines.push(` ${trimmedLine}`);
|
|
443
|
+
lines.push(` ${escapeStepContent(trimmedLine)}`);
|
|
440
444
|
} else {
|
|
441
445
|
lines.push(" ");
|
|
442
446
|
}
|
|
@@ -454,9 +458,9 @@ function serializeBlock(
|
|
|
454
458
|
}
|
|
455
459
|
|
|
456
460
|
if (index === 0) {
|
|
457
|
-
lines.push(` ${label}: ${trimmedLine}`);
|
|
461
|
+
lines.push(` ${label}: ${escapeStepContent(trimmedLine)}`);
|
|
458
462
|
} else {
|
|
459
|
-
lines.push(` ${trimmedLine}`);
|
|
463
|
+
lines.push(` ${escapeStepContent(trimmedLine)}`);
|
|
460
464
|
}
|
|
461
465
|
});
|
|
462
466
|
}
|
|
@@ -1017,6 +1021,7 @@ function parseTestStep(
|
|
|
1017
1021
|
let next = index + 1;
|
|
1018
1022
|
let inExpectedResult = false;
|
|
1019
1023
|
let blankLineSeenOutsideCodeBlock = false;
|
|
1024
|
+
let blankLineSeenInExpectedResult = false;
|
|
1020
1025
|
const stepIndent = current.length - current.trimStart().length;
|
|
1021
1026
|
|
|
1022
1027
|
while (next < lines.length) {
|
|
@@ -1026,13 +1031,14 @@ function parseTestStep(
|
|
|
1026
1031
|
const rawTrimmed = line.trim();
|
|
1027
1032
|
|
|
1028
1033
|
if (!rawTrimmed) {
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1034
|
+
if (inExpectedResult) {
|
|
1035
|
+
expectedResult += "\n";
|
|
1036
|
+
blankLineSeenInExpectedResult = true;
|
|
1037
|
+
} else {
|
|
1038
|
+
if (stepDataLines.length > 0) {
|
|
1033
1039
|
stepDataLines.push("");
|
|
1034
|
-
blankLineSeenOutsideCodeBlock = true;
|
|
1035
1040
|
}
|
|
1041
|
+
blankLineSeenOutsideCodeBlock = true;
|
|
1036
1042
|
}
|
|
1037
1043
|
next += 1;
|
|
1038
1044
|
continue;
|
|
@@ -1130,7 +1136,13 @@ function parseTestStep(
|
|
|
1130
1136
|
}
|
|
1131
1137
|
|
|
1132
1138
|
if (inExpectedResult) {
|
|
1133
|
-
// After
|
|
1139
|
+
// After a blank line inside the expected result, a non-indented line
|
|
1140
|
+
// belongs to the outer document (e.g. a trailing file block after the
|
|
1141
|
+
// step list), so stop here and let the root parser handle it.
|
|
1142
|
+
if (blankLineSeenInExpectedResult && !hasIndent) {
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
// Otherwise, indented lines are part of the expected result
|
|
1134
1146
|
if (hasIndent) {
|
|
1135
1147
|
const expectedContent = unescapeMarkdown(rawTrimmed);
|
|
1136
1148
|
if (expectedResult.length > 0) {
|