testomatio-editor-blocks 0.4.1 → 0.4.6
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.d.ts +6 -0
- package/package/editor/blocks/step.js +3 -0
- package/package/editor/customMarkdownConverter.js +18 -6
- package/package/editor/customSchema.d.ts +6 -0
- package/package/styles.css +7 -3
- package/package.json +1 -1
- package/src/editor/blocks/step.tsx +3 -0
- package/src/editor/customMarkdownConverter.test.ts +157 -34
- package/src/editor/customMarkdownConverter.ts +16 -2
- package/src/editor/markdownToBlocks.test.ts +2 -0
- package/src/editor/styles.css +7 -3
|
@@ -12,6 +12,9 @@ export declare const stepBlock: {
|
|
|
12
12
|
readonly expectedResult: {
|
|
13
13
|
readonly default: "";
|
|
14
14
|
};
|
|
15
|
+
readonly listStyle: {
|
|
16
|
+
readonly default: "bullet";
|
|
17
|
+
};
|
|
15
18
|
};
|
|
16
19
|
};
|
|
17
20
|
implementation: import("@blocknote/core").TiptapBlockImplementation<{
|
|
@@ -27,6 +30,9 @@ export declare const stepBlock: {
|
|
|
27
30
|
readonly expectedResult: {
|
|
28
31
|
readonly default: "";
|
|
29
32
|
};
|
|
33
|
+
readonly listStyle: {
|
|
34
|
+
readonly default: "bullet";
|
|
35
|
+
};
|
|
30
36
|
};
|
|
31
37
|
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
32
38
|
};
|
|
@@ -190,8 +190,8 @@ function flattenWithBlankLine(lines, appendBlank = false) {
|
|
|
190
190
|
}
|
|
191
191
|
return lines;
|
|
192
192
|
}
|
|
193
|
-
function serializeBlock(block, ctx, orderedIndex) {
|
|
194
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
193
|
+
function serializeBlock(block, ctx, orderedIndex, stepIndex) {
|
|
194
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
195
195
|
const lines = [];
|
|
196
196
|
const indent = ctx.listDepth > 0 ? " ".repeat(ctx.listDepth) : "";
|
|
197
197
|
switch (block.type) {
|
|
@@ -292,7 +292,9 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
292
292
|
.filter((segment) => segment.length > 0)
|
|
293
293
|
.join(" ");
|
|
294
294
|
if (normalizedTitle.length > 0) {
|
|
295
|
-
|
|
295
|
+
const listStyle = (_m = block.props.listStyle) !== null && _m !== void 0 ? _m : "bullet";
|
|
296
|
+
const prefix = listStyle === "ordered" ? `${(stepIndex !== null && stepIndex !== void 0 ? stepIndex : 0) + 1}.` : "*";
|
|
297
|
+
lines.push(`${prefix} ${normalizedTitle}`);
|
|
296
298
|
}
|
|
297
299
|
}
|
|
298
300
|
if (stepData.length > 0) {
|
|
@@ -348,7 +350,7 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
348
350
|
return flattenWithBlankLine(lines, true);
|
|
349
351
|
}
|
|
350
352
|
const headerRowCount = rows.length
|
|
351
|
-
? Math.min(rows.length, Math.max((
|
|
353
|
+
? Math.min(rows.length, Math.max((_o = tableContent.headerRows) !== null && _o !== void 0 ? _o : 1, 1))
|
|
352
354
|
: 0;
|
|
353
355
|
const columnAlignments = new Array(columnCount).fill("left");
|
|
354
356
|
const getCellAlignment = (cell) => {
|
|
@@ -425,6 +427,7 @@ function serializeBlock(block, ctx, orderedIndex) {
|
|
|
425
427
|
function serializeBlocks(blocks, ctx) {
|
|
426
428
|
const lines = [];
|
|
427
429
|
let orderedIndex = null;
|
|
430
|
+
let stepIndex = 0;
|
|
428
431
|
for (const block of blocks) {
|
|
429
432
|
if (block.type === "numberedListItem") {
|
|
430
433
|
if (typeof block.props.start === "number") {
|
|
@@ -437,6 +440,13 @@ function serializeBlocks(blocks, ctx) {
|
|
|
437
440
|
orderedIndex += 1;
|
|
438
441
|
continue;
|
|
439
442
|
}
|
|
443
|
+
if (block.type === "testStep") {
|
|
444
|
+
lines.push(...serializeBlock(block, ctx, undefined, stepIndex));
|
|
445
|
+
stepIndex += 1;
|
|
446
|
+
orderedIndex = null;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
stepIndex = 0;
|
|
440
450
|
orderedIndex = null;
|
|
441
451
|
lines.push(...serializeBlock(block, ctx));
|
|
442
452
|
}
|
|
@@ -616,7 +626,7 @@ function parseList(lines, startIndex, listType, indentLevel, allowEmptySteps = f
|
|
|
616
626
|
// Only try to parse as testStep for top-level items (indentLevel === 0)
|
|
617
627
|
// when we're under a Steps heading AND the list type is bullet
|
|
618
628
|
// Numbered lists under Steps heading are only parsed as test steps if they look like test steps
|
|
619
|
-
if (indentLevel === 0 && allowEmptySteps) {
|
|
629
|
+
if (indentLevel === 0 && (allowEmptySteps || listType === "bullet")) {
|
|
620
630
|
// For bullet lists, always try to parse as test steps
|
|
621
631
|
// For numbered lists, only try if they have step-like characteristics
|
|
622
632
|
const looksLikeTestStep = listType === "bullet" ||
|
|
@@ -855,7 +865,8 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
855
865
|
.map((line) => line.trimEnd())
|
|
856
866
|
.join("\n")
|
|
857
867
|
.trim();
|
|
858
|
-
if (!
|
|
868
|
+
if (!isBullet &&
|
|
869
|
+
!isLikelyStep &&
|
|
859
870
|
!expectedResult &&
|
|
860
871
|
stepDataLines.length === 0 &&
|
|
861
872
|
!(allowEmpty && titleWithPlaceholders.length > 0)) {
|
|
@@ -875,6 +886,7 @@ function parseTestStep(lines, index, allowEmpty = false, snippetId) {
|
|
|
875
886
|
stepTitle: titleWithPlaceholders,
|
|
876
887
|
stepData: stepDataWithImages,
|
|
877
888
|
expectedResult,
|
|
889
|
+
listStyle: isNumbered ? "ordered" : "bullet",
|
|
878
890
|
};
|
|
879
891
|
const parsedBlock = {
|
|
880
892
|
type: blockType,
|
|
@@ -15,6 +15,9 @@ export declare const customSchema: BlockNoteSchema<import("@blocknote/core").Blo
|
|
|
15
15
|
readonly expectedResult: {
|
|
16
16
|
readonly default: "";
|
|
17
17
|
};
|
|
18
|
+
readonly listStyle: {
|
|
19
|
+
readonly default: "bullet";
|
|
20
|
+
};
|
|
18
21
|
};
|
|
19
22
|
};
|
|
20
23
|
implementation: import("@blocknote/core").TiptapBlockImplementation<{
|
|
@@ -30,6 +33,9 @@ export declare const customSchema: BlockNoteSchema<import("@blocknote/core").Blo
|
|
|
30
33
|
readonly expectedResult: {
|
|
31
34
|
readonly default: "";
|
|
32
35
|
};
|
|
36
|
+
readonly listStyle: {
|
|
37
|
+
readonly default: "bullet";
|
|
38
|
+
};
|
|
33
39
|
};
|
|
34
40
|
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
35
41
|
};
|
package/package/styles.css
CHANGED
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
padding-bottom: 8px;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
.bn-teststep__header + .bn-step-field {
|
|
253
|
-
margin-top: calc(
|
|
252
|
+
.bn-teststep__content > .bn-teststep__header + .bn-step-field {
|
|
253
|
+
margin-top: calc(4px - var(--step-section-gap));
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
.bn-teststep__header {
|
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
.bn-step-field {
|
|
580
580
|
display: flex;
|
|
581
581
|
flex-direction: column;
|
|
582
|
-
gap:
|
|
582
|
+
gap: 4px;
|
|
583
583
|
position: relative;
|
|
584
584
|
}
|
|
585
585
|
|
|
@@ -801,6 +801,10 @@
|
|
|
801
801
|
font-weight: 400 !important;
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
+
.bn-step-editor .overtype-wrapper .overtype-placeholder {
|
|
805
|
+
display: none !important;
|
|
806
|
+
}
|
|
807
|
+
|
|
804
808
|
.bn-step-editor .overtype-wrapper .overtype-preview {
|
|
805
809
|
color: var(--text-primary) !important;
|
|
806
810
|
}
|
package/package.json
CHANGED
|
@@ -75,6 +75,7 @@ describe("blocksToMarkdown", () => {
|
|
|
75
75
|
stepTitle: "Open the Login page.",
|
|
76
76
|
stepData: "",
|
|
77
77
|
expectedResult: "The Login page loads successfully.",
|
|
78
|
+
listStyle: "bullet",
|
|
78
79
|
},
|
|
79
80
|
content: undefined,
|
|
80
81
|
children: [],
|
|
@@ -86,6 +87,7 @@ describe("blocksToMarkdown", () => {
|
|
|
86
87
|
stepTitle: "Enter a valid username.",
|
|
87
88
|
stepData: "",
|
|
88
89
|
expectedResult: "The username is accepted.",
|
|
90
|
+
listStyle: "bullet",
|
|
89
91
|
},
|
|
90
92
|
content: undefined,
|
|
91
93
|
children: [],
|
|
@@ -182,6 +184,7 @@ describe("blocksToMarkdown", () => {
|
|
|
182
184
|
stepTitle: "**Click** the _Login_ button",
|
|
183
185
|
stepData: "",
|
|
184
186
|
expectedResult: "**Success** is shown\nSecond line with <u>underline</u>",
|
|
187
|
+
listStyle: "bullet",
|
|
185
188
|
},
|
|
186
189
|
content: undefined,
|
|
187
190
|
children: [],
|
|
@@ -206,6 +209,7 @@ describe("blocksToMarkdown", () => {
|
|
|
206
209
|
stepTitle: "Navigate to login",
|
|
207
210
|
stepData: "Open browser\nGo to login page",
|
|
208
211
|
expectedResult: "Login form visible",
|
|
212
|
+
listStyle: "bullet",
|
|
209
213
|
},
|
|
210
214
|
content: undefined,
|
|
211
215
|
children: [],
|
|
@@ -243,6 +247,7 @@ describe("blocksToMarkdown", () => {
|
|
|
243
247
|
"",
|
|
244
248
|
].join("\n"),
|
|
245
249
|
expectedResult: "The user receives a real-time notification for the order update.",
|
|
250
|
+
listStyle: "bullet",
|
|
246
251
|
},
|
|
247
252
|
content: undefined,
|
|
248
253
|
children: [],
|
|
@@ -352,6 +357,7 @@ describe("markdownToBlocks", () => {
|
|
|
352
357
|
stepTitle: "Open the Login page.",
|
|
353
358
|
stepData: "",
|
|
354
359
|
expectedResult: "The Login page loads successfully.",
|
|
360
|
+
listStyle: "bullet",
|
|
355
361
|
},
|
|
356
362
|
children: [],
|
|
357
363
|
},
|
|
@@ -458,12 +464,43 @@ describe("markdownToBlocks", () => {
|
|
|
458
464
|
const stepBlocks = blocks.filter((block) => block.type === "testStep");
|
|
459
465
|
|
|
460
466
|
expect(stepBlocks).toEqual([
|
|
467
|
+
{
|
|
468
|
+
type: "testStep",
|
|
469
|
+
props: {
|
|
470
|
+
stepTitle: "The user is logged into the application.",
|
|
471
|
+
stepData: "",
|
|
472
|
+
expectedResult: "",
|
|
473
|
+
listStyle: "bullet",
|
|
474
|
+
},
|
|
475
|
+
children: [],
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
type: "testStep",
|
|
479
|
+
props: {
|
|
480
|
+
stepTitle: "The user has the necessary permissions to receive notifications.",
|
|
481
|
+
stepData: "",
|
|
482
|
+
expectedResult: "",
|
|
483
|
+
listStyle: "bullet",
|
|
484
|
+
},
|
|
485
|
+
children: [],
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
type: "testStep",
|
|
489
|
+
props: {
|
|
490
|
+
stepTitle: "The application is configured to send real-time notifications.",
|
|
491
|
+
stepData: "",
|
|
492
|
+
expectedResult: "",
|
|
493
|
+
listStyle: "bullet",
|
|
494
|
+
},
|
|
495
|
+
children: [],
|
|
496
|
+
},
|
|
461
497
|
{
|
|
462
498
|
type: "testStep",
|
|
463
499
|
props: {
|
|
464
500
|
stepTitle: "Step 1: Send a chat message to the user.",
|
|
465
501
|
stepData: "",
|
|
466
502
|
expectedResult: "The user receives a real-time notification for the chat message.",
|
|
503
|
+
listStyle: "bullet",
|
|
467
504
|
},
|
|
468
505
|
children: [],
|
|
469
506
|
},
|
|
@@ -473,6 +510,7 @@ describe("markdownToBlocks", () => {
|
|
|
473
510
|
stepTitle: "Step 2: Update an order status.",
|
|
474
511
|
stepData: "",
|
|
475
512
|
expectedResult: "The user receives a real-time notification for the order update.",
|
|
513
|
+
listStyle: "bullet",
|
|
476
514
|
},
|
|
477
515
|
children: [],
|
|
478
516
|
},
|
|
@@ -482,6 +520,7 @@ describe("markdownToBlocks", () => {
|
|
|
482
520
|
stepTitle: "Step 3: Send a file to the user.",
|
|
483
521
|
stepData: "",
|
|
484
522
|
expectedResult: "The user receives a real-time notification for the file received.",
|
|
523
|
+
listStyle: "bullet",
|
|
485
524
|
},
|
|
486
525
|
children: [],
|
|
487
526
|
},
|
|
@@ -491,6 +530,27 @@ describe("markdownToBlocks", () => {
|
|
|
491
530
|
stepTitle: "Step 4: Verify that the notifications are displayed correctly in the application's notification panel.",
|
|
492
531
|
stepData: "",
|
|
493
532
|
expectedResult: "All notifications (chat message, order update, file received) are listed in the notification panel with the correct information (e.g., timestamp, message content).\n",
|
|
533
|
+
listStyle: "bullet",
|
|
534
|
+
},
|
|
535
|
+
children: [],
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
type: "testStep",
|
|
539
|
+
props: {
|
|
540
|
+
stepTitle: "The user has received and viewed the notifications.",
|
|
541
|
+
stepData: "",
|
|
542
|
+
expectedResult: "",
|
|
543
|
+
listStyle: "bullet",
|
|
544
|
+
},
|
|
545
|
+
children: [],
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
type: "testStep",
|
|
549
|
+
props: {
|
|
550
|
+
stepTitle: "The application continues to function as expected after receiving and processing the notifications.",
|
|
551
|
+
stepData: "",
|
|
552
|
+
expectedResult: "",
|
|
553
|
+
listStyle: "bullet",
|
|
494
554
|
},
|
|
495
555
|
children: [],
|
|
496
556
|
},
|
|
@@ -535,6 +595,7 @@ describe("markdownToBlocks", () => {
|
|
|
535
595
|
stepTitle: "Step 2: Update an order status.",
|
|
536
596
|
stepData: expectedData,
|
|
537
597
|
expectedResult: "The user receives a real-time notification for the order update.",
|
|
598
|
+
listStyle: "bullet",
|
|
538
599
|
},
|
|
539
600
|
children: [],
|
|
540
601
|
},
|
|
@@ -548,6 +609,7 @@ describe("markdownToBlocks", () => {
|
|
|
548
609
|
stepTitle: "Step 2: Update an order status.",
|
|
549
610
|
stepData: expectedData,
|
|
550
611
|
expectedResult: "The user receives a real-time notification for the order update.",
|
|
612
|
+
listStyle: "bullet",
|
|
551
613
|
},
|
|
552
614
|
content: undefined,
|
|
553
615
|
children: [],
|
|
@@ -590,39 +652,33 @@ describe("markdownToBlocks", () => {
|
|
|
590
652
|
children: [],
|
|
591
653
|
},
|
|
592
654
|
{
|
|
593
|
-
type: "
|
|
594
|
-
props:
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
},
|
|
601
|
-
],
|
|
655
|
+
type: "testStep",
|
|
656
|
+
props: {
|
|
657
|
+
stepTitle: "The user is logged into the application.",
|
|
658
|
+
stepData: "",
|
|
659
|
+
expectedResult: "",
|
|
660
|
+
listStyle: "bullet",
|
|
661
|
+
},
|
|
602
662
|
children: [],
|
|
603
663
|
},
|
|
604
664
|
{
|
|
605
|
-
type: "
|
|
606
|
-
props:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
},
|
|
613
|
-
],
|
|
665
|
+
type: "testStep",
|
|
666
|
+
props: {
|
|
667
|
+
stepTitle: "The user has the necessary permissions to receive notifications.",
|
|
668
|
+
stepData: "",
|
|
669
|
+
expectedResult: "",
|
|
670
|
+
listStyle: "bullet",
|
|
671
|
+
},
|
|
614
672
|
children: [],
|
|
615
673
|
},
|
|
616
674
|
{
|
|
617
|
-
type: "
|
|
618
|
-
props:
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
},
|
|
625
|
-
],
|
|
675
|
+
type: "testStep",
|
|
676
|
+
props: {
|
|
677
|
+
stepTitle: "The application is configured to send real-time notifications.",
|
|
678
|
+
stepData: "",
|
|
679
|
+
expectedResult: "",
|
|
680
|
+
listStyle: "bullet",
|
|
681
|
+
},
|
|
626
682
|
children: [],
|
|
627
683
|
},
|
|
628
684
|
]);
|
|
@@ -664,6 +720,7 @@ describe("markdownToBlocks", () => {
|
|
|
664
720
|
stepTitle: "Open the form.",
|
|
665
721
|
stepData: "",
|
|
666
722
|
expectedResult: "** The form opens.\nFields are empty.",
|
|
723
|
+
listStyle: "bullet",
|
|
667
724
|
},
|
|
668
725
|
children: [],
|
|
669
726
|
},
|
|
@@ -685,6 +742,7 @@ describe("markdownToBlocks", () => {
|
|
|
685
742
|
stepTitle: "Navigate to login",
|
|
686
743
|
stepData: "Open browser\nGo to login page",
|
|
687
744
|
expectedResult: "Login form visible",
|
|
745
|
+
listStyle: "bullet",
|
|
688
746
|
},
|
|
689
747
|
children: [],
|
|
690
748
|
},
|
|
@@ -706,6 +764,7 @@ describe("markdownToBlocks", () => {
|
|
|
706
764
|
stepTitle: "Prepare test fixtures",
|
|
707
765
|
stepData: "Collect user accounts from staging.\nReset passwords for all test accounts.",
|
|
708
766
|
expectedResult: "Test accounts are ready for execution.",
|
|
767
|
+
listStyle: "bullet",
|
|
709
768
|
},
|
|
710
769
|
children: [],
|
|
711
770
|
},
|
|
@@ -725,6 +784,7 @@ describe("markdownToBlocks", () => {
|
|
|
725
784
|
stepTitle: "Display the generated report.",
|
|
726
785
|
stepData: "",
|
|
727
786
|
expectedResult: "",
|
|
787
|
+
listStyle: "bullet",
|
|
728
788
|
},
|
|
729
789
|
children: [],
|
|
730
790
|
},
|
|
@@ -738,6 +798,7 @@ describe("markdownToBlocks", () => {
|
|
|
738
798
|
stepTitle: "Display the generated report.",
|
|
739
799
|
stepData: "",
|
|
740
800
|
expectedResult: "",
|
|
801
|
+
listStyle: "bullet",
|
|
741
802
|
},
|
|
742
803
|
content: undefined,
|
|
743
804
|
children: [],
|
|
@@ -761,6 +822,7 @@ describe("markdownToBlocks", () => {
|
|
|
761
822
|
stepTitle: "Should open login screen",
|
|
762
823
|
stepData: "",
|
|
763
824
|
expectedResult: "Login should look like this\n",
|
|
825
|
+
listStyle: "bullet",
|
|
764
826
|
},
|
|
765
827
|
children: [],
|
|
766
828
|
},
|
|
@@ -774,6 +836,7 @@ describe("markdownToBlocks", () => {
|
|
|
774
836
|
stepTitle: "Should open login screen",
|
|
775
837
|
stepData: "",
|
|
776
838
|
expectedResult: "Login should look like this\n",
|
|
839
|
+
listStyle: "bullet",
|
|
777
840
|
},
|
|
778
841
|
content: undefined,
|
|
779
842
|
children: [],
|
|
@@ -810,6 +873,7 @@ describe("markdownToBlocks", () => {
|
|
|
810
873
|
stepTitle: "Pass onboarding as mobile user",
|
|
811
874
|
stepData: "",
|
|
812
875
|
expectedResult: "",
|
|
876
|
+
listStyle: "bullet",
|
|
813
877
|
},
|
|
814
878
|
children: [],
|
|
815
879
|
},
|
|
@@ -820,6 +884,7 @@ describe("markdownToBlocks", () => {
|
|
|
820
884
|
"Navigate to More tab -≻ My Profile -≻ Log into the app with user from preconditions",
|
|
821
885
|
stepData: "",
|
|
822
886
|
expectedResult: "* Upsell SS screen is displayed",
|
|
887
|
+
listStyle: "bullet",
|
|
823
888
|
},
|
|
824
889
|
children: [],
|
|
825
890
|
},
|
|
@@ -829,6 +894,7 @@ describe("markdownToBlocks", () => {
|
|
|
829
894
|
stepTitle: "Close SS",
|
|
830
895
|
stepData: "",
|
|
831
896
|
expectedResult: "* My Course and More tab are displayed",
|
|
897
|
+
listStyle: "bullet",
|
|
832
898
|
},
|
|
833
899
|
children: [],
|
|
834
900
|
},
|
|
@@ -860,6 +926,7 @@ describe("markdownToBlocks", () => {
|
|
|
860
926
|
stepTitle: "Existing email + invalid password",
|
|
861
927
|
stepData: "",
|
|
862
928
|
expectedResult: "'Oops, wrong email or password' is displayed",
|
|
929
|
+
listStyle: "bullet",
|
|
863
930
|
},
|
|
864
931
|
children: [],
|
|
865
932
|
});
|
|
@@ -870,6 +937,7 @@ describe("markdownToBlocks", () => {
|
|
|
870
937
|
stepTitle: "Not existing email + valid password",
|
|
871
938
|
stepData: "",
|
|
872
939
|
expectedResult: "'Oops, wrong email or password' is displayed",
|
|
940
|
+
listStyle: "bullet",
|
|
873
941
|
},
|
|
874
942
|
children: [],
|
|
875
943
|
});
|
|
@@ -910,7 +978,55 @@ describe("markdownToBlocks", () => {
|
|
|
910
978
|
|
|
911
979
|
const markdown = blocksToMarkdown(blocks);
|
|
912
980
|
const parsed = markdownToBlocks(markdown);
|
|
913
|
-
expect(parsed).toEqual(
|
|
981
|
+
expect(parsed).toEqual([
|
|
982
|
+
{
|
|
983
|
+
type: "paragraph",
|
|
984
|
+
props: baseProps,
|
|
985
|
+
content: [{ type: "text", text: "Paragraph", styles: {} }],
|
|
986
|
+
children: [],
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: "testStep",
|
|
990
|
+
props: {
|
|
991
|
+
stepTitle: "Bullet",
|
|
992
|
+
stepData: "",
|
|
993
|
+
expectedResult: "",
|
|
994
|
+
listStyle: "bullet",
|
|
995
|
+
},
|
|
996
|
+
children: [],
|
|
997
|
+
},
|
|
998
|
+
]);
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
it("round-trips bullet steps preserving bullet style", () => {
|
|
1002
|
+
const markdown = [
|
|
1003
|
+
"* Open the page",
|
|
1004
|
+
" *Expected*: Page loads",
|
|
1005
|
+
"* Click button",
|
|
1006
|
+
].join("\n");
|
|
1007
|
+
|
|
1008
|
+
const blocks = markdownToBlocks(markdown);
|
|
1009
|
+
const output = blocksToMarkdown(blocks as CustomEditorBlock[]);
|
|
1010
|
+
|
|
1011
|
+
expect(output).toContain("* Open the page");
|
|
1012
|
+
expect(output).toContain("* Click button");
|
|
1013
|
+
expect(output).not.toMatch(/^\d+\./m);
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
it("round-trips ordered steps preserving ordered style", () => {
|
|
1017
|
+
const markdown = [
|
|
1018
|
+
"### Steps",
|
|
1019
|
+
"",
|
|
1020
|
+
"1. Open the page",
|
|
1021
|
+
" *Expected*: Page loads",
|
|
1022
|
+
"2. Click button",
|
|
1023
|
+
].join("\n");
|
|
1024
|
+
|
|
1025
|
+
const blocks = markdownToBlocks(markdown);
|
|
1026
|
+
const output = blocksToMarkdown(blocks as CustomEditorBlock[]);
|
|
1027
|
+
|
|
1028
|
+
expect(output).toContain("1. Open the page");
|
|
1029
|
+
expect(output).toContain("2. Click button");
|
|
914
1030
|
});
|
|
915
1031
|
|
|
916
1032
|
it("parses markdown tables", () => {
|
|
@@ -977,6 +1093,7 @@ describe("markdownToBlocks", () => {
|
|
|
977
1093
|
stepTitle: "Step 1: Send a chat message to the user.",
|
|
978
1094
|
stepData: "",
|
|
979
1095
|
expectedResult: "The user receives a real-time notification for the chat message.",
|
|
1096
|
+
listStyle: "bullet",
|
|
980
1097
|
},
|
|
981
1098
|
children: [],
|
|
982
1099
|
},
|
|
@@ -1009,6 +1126,7 @@ describe("markdownToBlocks", () => {
|
|
|
1009
1126
|
stepTitle: "Swipe Back",
|
|
1010
1127
|
stepData: "",
|
|
1011
1128
|
expectedResult: "",
|
|
1129
|
+
listStyle: "bullet",
|
|
1012
1130
|
},
|
|
1013
1131
|
children: [],
|
|
1014
1132
|
});
|
|
@@ -1019,6 +1137,7 @@ describe("markdownToBlocks", () => {
|
|
|
1019
1137
|
stepTitle: "Check UI of Sleep score info screen",
|
|
1020
1138
|
stepData: "- Back button\nHeader: Sleep Score Info\nText: Ever wonder if 6, 8, or 9 hours of sleep are enough? Sleep score takes the guesswork out of your ZZZ's and shows you how well you slept last night based on duration, efficiency, and consistency.",
|
|
1021
1139
|
expectedResult: "* - 1st block:\n* - 2nd block:\n* - 3d block:",
|
|
1140
|
+
listStyle: "bullet",
|
|
1022
1141
|
},
|
|
1023
1142
|
children: [],
|
|
1024
1143
|
});
|
|
@@ -1029,6 +1148,7 @@ describe("markdownToBlocks", () => {
|
|
|
1029
1148
|
stepTitle: "Tap 'Back' button",
|
|
1030
1149
|
stepData: "",
|
|
1031
1150
|
expectedResult: "",
|
|
1151
|
+
listStyle: "bullet",
|
|
1032
1152
|
},
|
|
1033
1153
|
children: [],
|
|
1034
1154
|
});
|
|
@@ -1124,6 +1244,7 @@ describe("markdownToBlocks", () => {
|
|
|
1124
1244
|
stepTitle: "Navigate to the product listing page.",
|
|
1125
1245
|
stepData: "Expected open",
|
|
1126
1246
|
expectedResult: "",
|
|
1247
|
+
listStyle: "ordered",
|
|
1127
1248
|
},
|
|
1128
1249
|
children: [],
|
|
1129
1250
|
});
|
|
@@ -1135,6 +1256,7 @@ describe("markdownToBlocks", () => {
|
|
|
1135
1256
|
stepTitle: "Select a product and click the \"Add to Cart\" button.",
|
|
1136
1257
|
stepData: "Expected result close",
|
|
1137
1258
|
expectedResult: "",
|
|
1259
|
+
listStyle: "ordered",
|
|
1138
1260
|
},
|
|
1139
1261
|
children: [],
|
|
1140
1262
|
});
|
|
@@ -1146,6 +1268,7 @@ describe("markdownToBlocks", () => {
|
|
|
1146
1268
|
stepTitle: "Open the shopping cart page.",
|
|
1147
1269
|
stepData: "**Expected** edit",
|
|
1148
1270
|
expectedResult: "",
|
|
1271
|
+
listStyle: "ordered",
|
|
1149
1272
|
},
|
|
1150
1273
|
children: [],
|
|
1151
1274
|
});
|
|
@@ -1157,17 +1280,17 @@ describe("markdownToBlocks", () => {
|
|
|
1157
1280
|
stepTitle: "Verify that the added item is displayed with the correct name, price, and quantity.",
|
|
1158
1281
|
stepData: "_Expected_ close",
|
|
1159
1282
|
expectedResult: "",
|
|
1283
|
+
listStyle: "ordered",
|
|
1160
1284
|
},
|
|
1161
1285
|
children: [],
|
|
1162
1286
|
});
|
|
1163
1287
|
|
|
1164
|
-
// Test round-trip conversion
|
|
1165
|
-
// Note: Test steps are always serialized as bullet lists, not numbered lists
|
|
1288
|
+
// Test round-trip conversion — numbered steps preserve their ordered style
|
|
1166
1289
|
const roundTripMarkdown = blocksToMarkdown(blocks as CustomEditorBlock[]);
|
|
1167
|
-
expect(roundTripMarkdown).toContain("
|
|
1168
|
-
expect(roundTripMarkdown).toContain("
|
|
1169
|
-
expect(roundTripMarkdown).toContain("
|
|
1170
|
-
expect(roundTripMarkdown).toContain("
|
|
1290
|
+
expect(roundTripMarkdown).toContain("1. Navigate to the product listing page.");
|
|
1291
|
+
expect(roundTripMarkdown).toContain("2. Select a product and click the \"Add to Cart\" button.");
|
|
1292
|
+
expect(roundTripMarkdown).toContain("3. Open the shopping cart page.");
|
|
1293
|
+
expect(roundTripMarkdown).toContain("4. Verify that the added item is displayed with the correct name, price, and quantity.");
|
|
1171
1294
|
// Check that step data is preserved
|
|
1172
1295
|
expect(roundTripMarkdown).toContain(" Expected open");
|
|
1173
1296
|
expect(roundTripMarkdown).toContain(" Expected result close");
|
|
@@ -266,6 +266,7 @@ function serializeBlock(
|
|
|
266
266
|
block: CustomEditorBlock,
|
|
267
267
|
ctx: MarkdownContext,
|
|
268
268
|
orderedIndex?: number,
|
|
269
|
+
stepIndex?: number,
|
|
269
270
|
): string[] {
|
|
270
271
|
const lines: string[] = [];
|
|
271
272
|
const indent = ctx.listDepth > 0 ? " ".repeat(ctx.listDepth) : "";
|
|
@@ -375,7 +376,9 @@ function serializeBlock(
|
|
|
375
376
|
.join(" ");
|
|
376
377
|
|
|
377
378
|
if (normalizedTitle.length > 0) {
|
|
378
|
-
|
|
379
|
+
const listStyle = (block.props as any).listStyle ?? "bullet";
|
|
380
|
+
const prefix = listStyle === "ordered" ? `${(stepIndex ?? 0) + 1}.` : "*";
|
|
381
|
+
lines.push(`${prefix} ${normalizedTitle}`);
|
|
379
382
|
}
|
|
380
383
|
}
|
|
381
384
|
|
|
@@ -533,6 +536,7 @@ function serializeBlock(
|
|
|
533
536
|
function serializeBlocks(blocks: CustomEditorBlock[], ctx: MarkdownContext): string[] {
|
|
534
537
|
const lines: string[] = [];
|
|
535
538
|
let orderedIndex: number | null = null;
|
|
539
|
+
let stepIndex = 0;
|
|
536
540
|
|
|
537
541
|
for (const block of blocks) {
|
|
538
542
|
if (block.type === "numberedListItem") {
|
|
@@ -547,6 +551,14 @@ function serializeBlocks(blocks: CustomEditorBlock[], ctx: MarkdownContext): str
|
|
|
547
551
|
continue;
|
|
548
552
|
}
|
|
549
553
|
|
|
554
|
+
if (block.type === "testStep") {
|
|
555
|
+
lines.push(...serializeBlock(block, ctx, undefined, stepIndex));
|
|
556
|
+
stepIndex += 1;
|
|
557
|
+
orderedIndex = null;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
stepIndex = 0;
|
|
550
562
|
orderedIndex = null;
|
|
551
563
|
lines.push(...serializeBlock(block, ctx));
|
|
552
564
|
}
|
|
@@ -767,7 +779,7 @@ function parseList(
|
|
|
767
779
|
// Only try to parse as testStep for top-level items (indentLevel === 0)
|
|
768
780
|
// when we're under a Steps heading AND the list type is bullet
|
|
769
781
|
// Numbered lists under Steps heading are only parsed as test steps if they look like test steps
|
|
770
|
-
if (indentLevel === 0 && allowEmptySteps) {
|
|
782
|
+
if (indentLevel === 0 && (allowEmptySteps || listType === "bullet")) {
|
|
771
783
|
// For bullet lists, always try to parse as test steps
|
|
772
784
|
// For numbered lists, only try if they have step-like characteristics
|
|
773
785
|
const looksLikeTestStep = listType === "bullet" ||
|
|
@@ -1036,6 +1048,7 @@ function parseTestStep(
|
|
|
1036
1048
|
.trim();
|
|
1037
1049
|
|
|
1038
1050
|
if (
|
|
1051
|
+
!isBullet &&
|
|
1039
1052
|
!isLikelyStep &&
|
|
1040
1053
|
!expectedResult &&
|
|
1041
1054
|
stepDataLines.length === 0 &&
|
|
@@ -1060,6 +1073,7 @@ function parseTestStep(
|
|
|
1060
1073
|
stepTitle: titleWithPlaceholders,
|
|
1061
1074
|
stepData: stepDataWithImages,
|
|
1062
1075
|
expectedResult,
|
|
1076
|
+
listStyle: isNumbered ? "ordered" : "bullet",
|
|
1063
1077
|
};
|
|
1064
1078
|
|
|
1065
1079
|
const parsedBlock: CustomPartialBlock = {
|
|
@@ -21,6 +21,7 @@ describe("markdownToBlocks", () => {
|
|
|
21
21
|
stepTitle: "Open the Login page.",
|
|
22
22
|
stepData: "",
|
|
23
23
|
expectedResult: "The Login page loads successfully.",
|
|
24
|
+
listStyle: "bullet",
|
|
24
25
|
},
|
|
25
26
|
children: [],
|
|
26
27
|
},
|
|
@@ -99,6 +100,7 @@ describe("markdownToBlocks", () => {
|
|
|
99
100
|
stepTitle: "Step 1: Send a chat message to the user.",
|
|
100
101
|
stepData: "",
|
|
101
102
|
expectedResult: "The user receives a real-time notification for the chat message.",
|
|
103
|
+
listStyle: "bullet",
|
|
102
104
|
},
|
|
103
105
|
children: [],
|
|
104
106
|
});
|
package/src/editor/styles.css
CHANGED
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
padding-bottom: 8px;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
.bn-teststep__header + .bn-step-field {
|
|
253
|
-
margin-top: calc(
|
|
252
|
+
.bn-teststep__content > .bn-teststep__header + .bn-step-field {
|
|
253
|
+
margin-top: calc(4px - var(--step-section-gap));
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
.bn-teststep__header {
|
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
.bn-step-field {
|
|
580
580
|
display: flex;
|
|
581
581
|
flex-direction: column;
|
|
582
|
-
gap:
|
|
582
|
+
gap: 4px;
|
|
583
583
|
position: relative;
|
|
584
584
|
}
|
|
585
585
|
|
|
@@ -801,6 +801,10 @@
|
|
|
801
801
|
font-weight: 400 !important;
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
+
.bn-step-editor .overtype-wrapper .overtype-placeholder {
|
|
805
|
+
display: none !important;
|
|
806
|
+
}
|
|
807
|
+
|
|
804
808
|
.bn-step-editor .overtype-wrapper .overtype-preview {
|
|
805
809
|
color: var(--text-primary) !important;
|
|
806
810
|
}
|