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.
@@ -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 declare function markdownToBlocks(markdown: string): CustomPartialBlock[];
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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-editor-blocks",
3
- "version": "0.4.33",
3
+ "version": "0.4.34",
4
4
  "description": "Custom BlockNote schema, markdown conversion helpers, and UI for Testomatio-style test cases and steps.",
5
5
  "type": "module",
6
6
  "main": "./package/index.js",
@@ -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 function markdownToBlocks(markdown: string): CustomPartialBlock[] {
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;
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export {
13
13
  markdownToBlocks,
14
14
  type CustomEditorBlock,
15
15
  type CustomPartialBlock,
16
+ type MarkdownToBlocksOptions,
16
17
  } from "./editor/customMarkdownConverter";
17
18
 
18
19
  export {