testomatio-editor-blocks 0.4.33 → 0.4.34
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/customMarkdownConverter.d.ts +5 -1
- package/package/editor/customMarkdownConverter.js +6 -1
- package/package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/editor/customMarkdownConverter.test.ts +99 -0
- package/src/editor/customMarkdownConverter.ts +11 -1
- package/src/index.ts +1 -0
|
@@ -5,5 +5,9 @@ export type CustomEditorBlock = Block<Schema["blockSchema"], Schema["inlineConte
|
|
|
5
5
|
export type CustomPartialBlock = PartialBlock<Schema["blockSchema"], Schema["inlineContentSchema"], Schema["styleSchema"]>;
|
|
6
6
|
export declare function blocksToMarkdown(blocks: CustomEditorBlock[]): string;
|
|
7
7
|
export declare function fixMalformedImageBlocks(blocks: CustomPartialBlock[]): CustomPartialBlock[];
|
|
8
|
-
export
|
|
8
|
+
export interface MarkdownToBlocksOptions {
|
|
9
|
+
/** When true, every blank line produces an empty paragraph block. */
|
|
10
|
+
preserveBlankLines?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function markdownToBlocks(markdown: string, options?: MarkdownToBlocksOptions): CustomPartialBlock[];
|
|
9
13
|
export {};
|
|
@@ -1147,7 +1147,7 @@ export function fixMalformedImageBlocks(blocks) {
|
|
|
1147
1147
|
}
|
|
1148
1148
|
return result;
|
|
1149
1149
|
}
|
|
1150
|
-
export function markdownToBlocks(markdown) {
|
|
1150
|
+
export function markdownToBlocks(markdown, options) {
|
|
1151
1151
|
var _a, _b, _c;
|
|
1152
1152
|
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
1153
1153
|
const lines = normalized.split("\n");
|
|
@@ -1157,6 +1157,11 @@ export function markdownToBlocks(markdown) {
|
|
|
1157
1157
|
while (index < lines.length) {
|
|
1158
1158
|
const line = lines[index];
|
|
1159
1159
|
if (!line.trim()) {
|
|
1160
|
+
if (options === null || options === void 0 ? void 0 : options.preserveBlankLines) {
|
|
1161
|
+
blocks.push({ type: "paragraph", content: [], children: [] });
|
|
1162
|
+
index += 1;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1160
1165
|
index += 1;
|
|
1161
1166
|
// Count consecutive blank lines
|
|
1162
1167
|
let blankCount = 1;
|
package/package/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, }
|
|
|
2
2
|
export { stepBlock, canInsertStepOrSnippet, isStepsHeading, addStepsBlock } from "./editor/blocks/step";
|
|
3
3
|
export { snippetBlock } from "./editor/blocks/snippet";
|
|
4
4
|
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
|
|
5
|
-
export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, } from "./editor/customMarkdownConverter";
|
|
5
|
+
export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, type MarkdownToBlocksOptions, } from "./editor/customMarkdownConverter";
|
|
6
6
|
export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
|
|
7
7
|
export { useStepImageUpload, setImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
|
|
8
8
|
export { setFileDisplayUrlResolver, resolveFileDisplayUrl, type FileDisplayUrlResolver, } from "./editor/fileDisplayUrl";
|
package/package.json
CHANGED
|
@@ -995,6 +995,105 @@ describe("markdownToBlocks", () => {
|
|
|
995
995
|
});
|
|
996
996
|
});
|
|
997
997
|
|
|
998
|
+
it("parses all bullet items as steps when blank line follows Steps heading", () => {
|
|
999
|
+
const markdown = [
|
|
1000
|
+
"### Requirements",
|
|
1001
|
+
"",
|
|
1002
|
+
"### Steps",
|
|
1003
|
+
"",
|
|
1004
|
+
"* next",
|
|
1005
|
+
" *Expected*: expected",
|
|
1006
|
+
"* next 22",
|
|
1007
|
+
"* next 3",
|
|
1008
|
+
].join("\n");
|
|
1009
|
+
|
|
1010
|
+
const blocks = markdownToBlocks(markdown);
|
|
1011
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
1012
|
+
|
|
1013
|
+
expect(stepBlocks).toHaveLength(3);
|
|
1014
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "next", expectedResult: "expected" });
|
|
1015
|
+
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "next 22" });
|
|
1016
|
+
expect(stepBlocks[2].props).toMatchObject({ stepTitle: "next 3" });
|
|
1017
|
+
|
|
1018
|
+
// Ensure no bullet list items leaked
|
|
1019
|
+
const bulletBlocks = blocks.filter((block) => block.type === "bulletListItem");
|
|
1020
|
+
expect(bulletBlocks).toHaveLength(0);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it("parses all bullet items as steps WITHOUT blank line after Steps heading", () => {
|
|
1024
|
+
const markdown = [
|
|
1025
|
+
"### Requirements",
|
|
1026
|
+
"",
|
|
1027
|
+
"### Steps",
|
|
1028
|
+
"* next",
|
|
1029
|
+
" *Expected*: expected",
|
|
1030
|
+
"* next 22",
|
|
1031
|
+
"* next 3",
|
|
1032
|
+
].join("\n");
|
|
1033
|
+
|
|
1034
|
+
const blocks = markdownToBlocks(markdown);
|
|
1035
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
1036
|
+
|
|
1037
|
+
expect(stepBlocks).toHaveLength(3);
|
|
1038
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "next", expectedResult: "expected" });
|
|
1039
|
+
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "next 22" });
|
|
1040
|
+
expect(stepBlocks[2].props).toMatchObject({ stepTitle: "next 3" });
|
|
1041
|
+
|
|
1042
|
+
const bulletBlocks = blocks.filter((block) => block.type === "bulletListItem");
|
|
1043
|
+
expect(bulletBlocks).toHaveLength(0);
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
it("round-trips steps with blank line after Steps heading", () => {
|
|
1047
|
+
const markdown = [
|
|
1048
|
+
"### Requirements",
|
|
1049
|
+
"",
|
|
1050
|
+
"### Steps",
|
|
1051
|
+
"",
|
|
1052
|
+
"* next",
|
|
1053
|
+
" *Expected*: expected",
|
|
1054
|
+
"* next 22",
|
|
1055
|
+
"* next 3",
|
|
1056
|
+
].join("\n");
|
|
1057
|
+
|
|
1058
|
+
const blocks = markdownToBlocks(markdown);
|
|
1059
|
+
const md = blocksToMarkdown(blocks as CustomEditorBlock[]);
|
|
1060
|
+
const blocks2 = markdownToBlocks(md);
|
|
1061
|
+
const stepBlocks2 = blocks2.filter((block) => block.type === "testStep");
|
|
1062
|
+
expect(stepBlocks2).toHaveLength(3);
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
it("preserveBlankLines: creates empty paragraphs for each blank line", () => {
|
|
1066
|
+
const markdown = [
|
|
1067
|
+
"### Requirements",
|
|
1068
|
+
"",
|
|
1069
|
+
"### Steps",
|
|
1070
|
+
"",
|
|
1071
|
+
"* next",
|
|
1072
|
+
" *Expected*: expected",
|
|
1073
|
+
"* next 22",
|
|
1074
|
+
"* next 3",
|
|
1075
|
+
].join("\n");
|
|
1076
|
+
|
|
1077
|
+
const blocks = markdownToBlocks(markdown, { preserveBlankLines: true });
|
|
1078
|
+
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
1079
|
+
const emptyParas = blocks.filter(
|
|
1080
|
+
(block) => block.type === "paragraph" && (!block.content || (block.content as any[]).length === 0),
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
// All 3 items should be test steps
|
|
1084
|
+
expect(stepBlocks).toHaveLength(3);
|
|
1085
|
+
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "next" });
|
|
1086
|
+
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "next 22" });
|
|
1087
|
+
expect(stepBlocks[2].props).toMatchObject({ stepTitle: "next 3" });
|
|
1088
|
+
|
|
1089
|
+
// Each blank line should produce an empty paragraph
|
|
1090
|
+
expect(emptyParas.length).toBeGreaterThanOrEqual(2);
|
|
1091
|
+
|
|
1092
|
+
// No bullet list items
|
|
1093
|
+
const bulletBlocks = blocks.filter((block) => block.type === "bulletListItem");
|
|
1094
|
+
expect(bulletBlocks).toHaveLength(0);
|
|
1095
|
+
});
|
|
1096
|
+
|
|
998
1097
|
it("round-trips simple blocks", () => {
|
|
999
1098
|
const blocks: CustomEditorBlock[] = [
|
|
1000
1099
|
{
|
|
@@ -1372,7 +1372,12 @@ export function fixMalformedImageBlocks(blocks: CustomPartialBlock[]): CustomPar
|
|
|
1372
1372
|
return result;
|
|
1373
1373
|
}
|
|
1374
1374
|
|
|
1375
|
-
export
|
|
1375
|
+
export interface MarkdownToBlocksOptions {
|
|
1376
|
+
/** When true, every blank line produces an empty paragraph block. */
|
|
1377
|
+
preserveBlankLines?: boolean;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
export function markdownToBlocks(markdown: string, options?: MarkdownToBlocksOptions): CustomPartialBlock[] {
|
|
1376
1381
|
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
1377
1382
|
const lines = normalized.split("\n");
|
|
1378
1383
|
const blocks: CustomPartialBlock[] = [];
|
|
@@ -1382,6 +1387,11 @@ export function markdownToBlocks(markdown: string): CustomPartialBlock[] {
|
|
|
1382
1387
|
while (index < lines.length) {
|
|
1383
1388
|
const line = lines[index];
|
|
1384
1389
|
if (!line.trim()) {
|
|
1390
|
+
if (options?.preserveBlankLines) {
|
|
1391
|
+
blocks.push({ type: "paragraph", content: [], children: [] } as CustomPartialBlock);
|
|
1392
|
+
index += 1;
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1385
1395
|
index += 1;
|
|
1386
1396
|
// Count consecutive blank lines
|
|
1387
1397
|
let blankCount = 1;
|