testomatio-editor-blocks 0.1.0 → 0.1.1
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/README.md +51 -0
- package/package/editor/customMarkdownConverter.js +28 -17
- package/package/editor/customSchema.js +263 -16
- package/package/editor/stepAutocomplete.d.ts +35 -0
- package/package/editor/stepAutocomplete.js +84 -0
- package/package/editor/stepImageUpload.d.ts +5 -0
- package/package/editor/stepImageUpload.js +12 -0
- package/package/index.d.ts +2 -0
- package/package/index.js +2 -0
- package/package/styles.css +52 -0
- package/package.json +1 -1
- package/src/App.css +41 -0
- package/src/App.tsx +252 -8
- package/src/editor/customMarkdownConverter.test.ts +59 -25
- package/src/editor/customMarkdownConverter.ts +30 -17
- package/src/editor/customSchema.test.ts +8 -0
- package/src/editor/customSchema.tsx +369 -12
- package/src/editor/stepAutocomplete.test.ts +83 -0
- package/src/editor/stepAutocomplete.tsx +130 -0
- package/src/editor/stepImageUpload.tsx +19 -0
- package/src/editor/styles.css +52 -0
- package/src/index.ts +15 -0
package/package/styles.css
CHANGED
|
@@ -140,6 +140,7 @@
|
|
|
140
140
|
display: flex;
|
|
141
141
|
flex-direction: column;
|
|
142
142
|
gap: 0.35rem;
|
|
143
|
+
position: relative;
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
.bn-step-field__top {
|
|
@@ -150,6 +151,9 @@
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
.bn-step-field__label {
|
|
154
|
+
display: inline-flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
gap: 0.35rem;
|
|
153
157
|
font-size: 0.8rem;
|
|
154
158
|
text-transform: uppercase;
|
|
155
159
|
letter-spacing: 0.08em;
|
|
@@ -216,6 +220,54 @@
|
|
|
216
220
|
pointer-events: none;
|
|
217
221
|
}
|
|
218
222
|
|
|
223
|
+
.bn-step-suggestions {
|
|
224
|
+
position: absolute;
|
|
225
|
+
left: 0;
|
|
226
|
+
right: 0;
|
|
227
|
+
top: calc(100% + 0.25rem);
|
|
228
|
+
background: rgba(255, 255, 255, 0.98);
|
|
229
|
+
border-radius: 0.65rem;
|
|
230
|
+
border: 1px solid rgba(37, 99, 235, 0.25);
|
|
231
|
+
box-shadow: 0 18px 35px rgba(15, 23, 42, 0.16);
|
|
232
|
+
padding: 0.25rem;
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
gap: 0.15rem;
|
|
236
|
+
z-index: 5;
|
|
237
|
+
max-height: 12rem;
|
|
238
|
+
overflow-y: auto;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.bn-step-suggestion {
|
|
242
|
+
border: none;
|
|
243
|
+
background: transparent;
|
|
244
|
+
border-radius: 0.5rem;
|
|
245
|
+
padding: 0.45rem 0.75rem;
|
|
246
|
+
text-align: left;
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: space-between;
|
|
250
|
+
gap: 0.5rem;
|
|
251
|
+
cursor: pointer;
|
|
252
|
+
color: #0f172a;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.bn-step-suggestion:hover,
|
|
256
|
+
.bn-step-suggestion--active {
|
|
257
|
+
background: rgba(59, 130, 246, 0.1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.bn-step-suggestion__title {
|
|
261
|
+
font-weight: 600;
|
|
262
|
+
font-size: 0.95rem;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.bn-step-suggestion__meta {
|
|
266
|
+
font-size: 0.75rem;
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
color: rgba(15, 23, 42, 0.65);
|
|
269
|
+
}
|
|
270
|
+
|
|
219
271
|
.bn-inline-image {
|
|
220
272
|
display: block;
|
|
221
273
|
max-width: 100%;
|
package/package.json
CHANGED
package/src/App.css
CHANGED
|
@@ -147,6 +147,47 @@
|
|
|
147
147
|
color: #f87171;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
.app__panel--light {
|
|
151
|
+
background: #ffffff;
|
|
152
|
+
color: #0f172a;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.app__panel-text {
|
|
156
|
+
margin: 0;
|
|
157
|
+
color: rgba(15, 23, 42, 0.85);
|
|
158
|
+
font-size: 0.9rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.app__step-list {
|
|
162
|
+
list-style: none;
|
|
163
|
+
margin: 0;
|
|
164
|
+
padding: 0;
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-direction: column;
|
|
167
|
+
gap: 0.35rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.app__step-list li {
|
|
171
|
+
display: flex;
|
|
172
|
+
justify-content: space-between;
|
|
173
|
+
gap: 0.5rem;
|
|
174
|
+
padding: 0.4rem 0.5rem;
|
|
175
|
+
border-radius: 0.5rem;
|
|
176
|
+
background: rgba(37, 99, 235, 0.08);
|
|
177
|
+
color: inherit;
|
|
178
|
+
font-size: 0.95rem;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.app__step-title {
|
|
182
|
+
font-weight: 600;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.app__step-meta {
|
|
186
|
+
font-size: 0.8rem;
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
color: rgba(15, 23, 42, 0.6);
|
|
189
|
+
}
|
|
190
|
+
|
|
150
191
|
@media (max-width: 960px) {
|
|
151
192
|
#root {
|
|
152
193
|
padding: 1.5rem;
|
package/src/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, useState } from "react";
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { BlockNoteView } from "@blocknote/mantine";
|
|
3
3
|
import {
|
|
4
4
|
useCreateBlockNote,
|
|
@@ -17,9 +17,39 @@ import {
|
|
|
17
17
|
type CustomEditorBlock,
|
|
18
18
|
type CustomPartialBlock,
|
|
19
19
|
} from "./editor/customMarkdownConverter";
|
|
20
|
-
import { customSchema } from "./editor/customSchema";
|
|
20
|
+
import { customSchema, type CustomEditor } from "./editor/customSchema";
|
|
21
|
+
import { setGlobalStepSuggestionsFetcher, type StepJsonApiDocument } from "./editor/stepAutocomplete";
|
|
22
|
+
import { setGlobalStepImageUploadHandler } from "./editor/stepImageUpload";
|
|
21
23
|
import "./App.css";
|
|
22
24
|
|
|
25
|
+
const focusTestStepTitle = (editor: CustomEditor | null | undefined, blockId?: string) => {
|
|
26
|
+
if (!editor || !blockId) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const focus = () => {
|
|
31
|
+
const stepTitle = document.querySelector<HTMLElement>(
|
|
32
|
+
`[data-block-id="${blockId}"] [data-step-field="title"]`,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (stepTitle) {
|
|
36
|
+
stepTitle.focus();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
editor.setSelection(blockId, blockId);
|
|
41
|
+
editor.setTextCursorPosition(blockId, "end");
|
|
42
|
+
editor.focus();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (typeof requestAnimationFrame === "function") {
|
|
46
|
+
requestAnimationFrame(focus);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setTimeout(focus, 0);
|
|
51
|
+
};
|
|
52
|
+
|
|
23
53
|
type Schema = typeof customSchema;
|
|
24
54
|
|
|
25
55
|
const DEFAULT_BLOCK_PROPS = {
|
|
@@ -28,6 +58,151 @@ const DEFAULT_BLOCK_PROPS = {
|
|
|
28
58
|
backgroundColor: "default" as const,
|
|
29
59
|
};
|
|
30
60
|
|
|
61
|
+
const DEMO_STEP_FIXTURES: StepJsonApiDocument = {
|
|
62
|
+
data: [
|
|
63
|
+
{
|
|
64
|
+
id: "145",
|
|
65
|
+
type: "step",
|
|
66
|
+
attributes: {
|
|
67
|
+
labels: [],
|
|
68
|
+
title: "Donec placerat, dui vitae",
|
|
69
|
+
kind: "manual",
|
|
70
|
+
description: null,
|
|
71
|
+
keywords: [],
|
|
72
|
+
"is-snippet": null,
|
|
73
|
+
"usage-count": 23,
|
|
74
|
+
"comments-count": 0,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "146",
|
|
79
|
+
type: "step",
|
|
80
|
+
attributes: {
|
|
81
|
+
labels: [],
|
|
82
|
+
title: "Ut auctor mi erat ac dolor.",
|
|
83
|
+
kind: "manual",
|
|
84
|
+
description: null,
|
|
85
|
+
keywords: [],
|
|
86
|
+
"is-snippet": null,
|
|
87
|
+
"usage-count": 23,
|
|
88
|
+
"comments-count": 0,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "147",
|
|
93
|
+
type: "step",
|
|
94
|
+
attributes: {
|
|
95
|
+
labels: [],
|
|
96
|
+
title: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",
|
|
97
|
+
kind: "manual",
|
|
98
|
+
description: null,
|
|
99
|
+
keywords: [],
|
|
100
|
+
"is-snippet": null,
|
|
101
|
+
"usage-count": 23,
|
|
102
|
+
"comments-count": 0,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "148",
|
|
107
|
+
type: "step",
|
|
108
|
+
attributes: {
|
|
109
|
+
labels: [],
|
|
110
|
+
title: "Felis libero varius orci, in vulputate",
|
|
111
|
+
kind: "manual",
|
|
112
|
+
description: null,
|
|
113
|
+
keywords: [],
|
|
114
|
+
"is-snippet": null,
|
|
115
|
+
"usage-count": 19,
|
|
116
|
+
"comments-count": 0,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "149",
|
|
121
|
+
type: "step",
|
|
122
|
+
attributes: {
|
|
123
|
+
labels: [],
|
|
124
|
+
title: "Massa turpis scelerisque diam.",
|
|
125
|
+
kind: "manual",
|
|
126
|
+
description: null,
|
|
127
|
+
keywords: [],
|
|
128
|
+
"is-snippet": null,
|
|
129
|
+
"usage-count": 19,
|
|
130
|
+
"comments-count": 0,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "150",
|
|
135
|
+
type: "step",
|
|
136
|
+
attributes: {
|
|
137
|
+
labels: [],
|
|
138
|
+
title: "Nunc et felis est. Phasellus laoreet nibh vel augue",
|
|
139
|
+
kind: "manual",
|
|
140
|
+
description: null,
|
|
141
|
+
keywords: [],
|
|
142
|
+
"is-snippet": null,
|
|
143
|
+
"usage-count": 19,
|
|
144
|
+
"comments-count": 0,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "151",
|
|
149
|
+
type: "step",
|
|
150
|
+
attributes: {
|
|
151
|
+
labels: [],
|
|
152
|
+
title: "Suspendisse interdum sem non sem cursus consequat.",
|
|
153
|
+
kind: "manual",
|
|
154
|
+
description: null,
|
|
155
|
+
keywords: [],
|
|
156
|
+
"is-snippet": null,
|
|
157
|
+
"usage-count": 17,
|
|
158
|
+
"comments-count": 0,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "152",
|
|
163
|
+
type: "step",
|
|
164
|
+
attributes: {
|
|
165
|
+
labels: [],
|
|
166
|
+
title: "Aliquam tempor, nibh sed facilisis lacinia, nisl velit aliquet nunc.",
|
|
167
|
+
kind: "manual",
|
|
168
|
+
description: null,
|
|
169
|
+
keywords: [],
|
|
170
|
+
"is-snippet": null,
|
|
171
|
+
"usage-count": 16,
|
|
172
|
+
"comments-count": 0,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "153",
|
|
177
|
+
type: "step",
|
|
178
|
+
attributes: {
|
|
179
|
+
labels: [],
|
|
180
|
+
title: "Praesent tellus neque, efficitur vel hendrerit sed, porta id sapien.",
|
|
181
|
+
kind: "manual",
|
|
182
|
+
description: null,
|
|
183
|
+
keywords: [],
|
|
184
|
+
"is-snippet": null,
|
|
185
|
+
"usage-count": 14,
|
|
186
|
+
"comments-count": 0,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "154",
|
|
191
|
+
type: "step",
|
|
192
|
+
attributes: {
|
|
193
|
+
labels: [],
|
|
194
|
+
title: "Maecenas suscipit lacus vitae viverra fermentum.",
|
|
195
|
+
kind: "manual",
|
|
196
|
+
description: null,
|
|
197
|
+
keywords: [],
|
|
198
|
+
"is-snippet": null,
|
|
199
|
+
"usage-count": 12,
|
|
200
|
+
"comments-count": 0,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
|
|
31
206
|
function CustomSlashMenu() {
|
|
32
207
|
const editor = useBlockNoteEditor<Schema["blockSchema"], Schema["inlineContentSchema"], Schema["styleSchema"]>();
|
|
33
208
|
|
|
@@ -46,7 +221,7 @@ function CustomSlashMenu() {
|
|
|
46
221
|
icon: <span className="bn-suggestion-icon">TS</span>,
|
|
47
222
|
aliases: ["step", "test step", "expected"],
|
|
48
223
|
onItemClick: () => {
|
|
49
|
-
insertOrUpdateBlock(editor, {
|
|
224
|
+
const inserted = insertOrUpdateBlock(editor, {
|
|
50
225
|
type: "testStep",
|
|
51
226
|
props: {
|
|
52
227
|
stepTitle: "",
|
|
@@ -54,6 +229,7 @@ function CustomSlashMenu() {
|
|
|
54
229
|
expectedResult: "",
|
|
55
230
|
},
|
|
56
231
|
});
|
|
232
|
+
focusTestStepTitle(editor, inserted.id);
|
|
57
233
|
},
|
|
58
234
|
};
|
|
59
235
|
|
|
@@ -113,6 +289,9 @@ function App() {
|
|
|
113
289
|
const [copyStatus, setCopyStatus] = useState<"idle" | "copied" | "failed">(
|
|
114
290
|
"idle",
|
|
115
291
|
);
|
|
292
|
+
const [copyBlocksStatus, setCopyBlocksStatus] = useState<"idle" | "copied" | "failed">(
|
|
293
|
+
"idle",
|
|
294
|
+
);
|
|
116
295
|
|
|
117
296
|
useEditorChange((editorInstance) => {
|
|
118
297
|
try {
|
|
@@ -130,6 +309,53 @@ function App() {
|
|
|
130
309
|
}
|
|
131
310
|
}, editor);
|
|
132
311
|
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
if (!editor) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const unsubscribe = editor.onChange((instance, context) => {
|
|
318
|
+
const changes = context.getChanges();
|
|
319
|
+
const newlyInsertedStep = changes.find(({ type, block, source }) => {
|
|
320
|
+
if (type !== "insert") {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (source?.type === "yjs-remote") {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return block.type === "testStep" && ((block.props as any)?.stepTitle ?? "") === "";
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (newlyInsertedStep) {
|
|
332
|
+
focusTestStepTitle(instance, newlyInsertedStep.block.id);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return () => {
|
|
337
|
+
if (typeof unsubscribe === "function") {
|
|
338
|
+
unsubscribe();
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}, [editor]);
|
|
342
|
+
|
|
343
|
+
const uploadStepImage = useMemo(
|
|
344
|
+
() => async (_image: Blob) => ({ url: `https://placehold.co/600x400?text=Uploaded+${Date.now()}` }),
|
|
345
|
+
[],
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
useEffect(() => {
|
|
349
|
+
// Demo defaults: configure global handlers so the editor works without manual providers.
|
|
350
|
+
setGlobalStepSuggestionsFetcher(() => DEMO_STEP_FIXTURES);
|
|
351
|
+
setGlobalStepImageUploadHandler(uploadStepImage);
|
|
352
|
+
|
|
353
|
+
return () => {
|
|
354
|
+
setGlobalStepSuggestionsFetcher(null);
|
|
355
|
+
setGlobalStepImageUploadHandler(null);
|
|
356
|
+
};
|
|
357
|
+
}, [uploadStepImage]);
|
|
358
|
+
|
|
133
359
|
const createTestCaseBlock = useMemo<() => CustomPartialBlock>(() => {
|
|
134
360
|
return () => ({
|
|
135
361
|
type: "testCase",
|
|
@@ -173,7 +399,11 @@ function App() {
|
|
|
173
399
|
return;
|
|
174
400
|
}
|
|
175
401
|
|
|
176
|
-
editor.insertBlocks([createBlock()], referenceId, "after");
|
|
402
|
+
const inserted = editor.insertBlocks([createBlock()], referenceId, "after");
|
|
403
|
+
const firstInserted = inserted[0];
|
|
404
|
+
if (firstInserted) {
|
|
405
|
+
focusTestStepTitle(editor, firstInserted.id);
|
|
406
|
+
}
|
|
177
407
|
};
|
|
178
408
|
|
|
179
409
|
const insertTestCase = () => insertBlockAfterSelection(createTestCaseBlock);
|
|
@@ -214,10 +444,6 @@ function App() {
|
|
|
214
444
|
}
|
|
215
445
|
};
|
|
216
446
|
|
|
217
|
-
const [copyBlocksStatus, setCopyBlocksStatus] = useState<
|
|
218
|
-
"idle" | "copied" | "failed"
|
|
219
|
-
>("idle");
|
|
220
|
-
|
|
221
447
|
const handleCopyBlocks = async () => {
|
|
222
448
|
if (conversionError) {
|
|
223
449
|
return;
|
|
@@ -317,6 +543,24 @@ function App() {
|
|
|
317
543
|
<pre>{markdown}</pre>
|
|
318
544
|
)}
|
|
319
545
|
</div>
|
|
546
|
+
<div className="app__panel app__panel--light">
|
|
547
|
+
<div className="app__panel-header">
|
|
548
|
+
<h2>Autocomplete Steps</h2>
|
|
549
|
+
</div>
|
|
550
|
+
<p className="app__panel-text">
|
|
551
|
+
Start typing in the Step Title field to filter this list instantly.
|
|
552
|
+
</p>
|
|
553
|
+
<ol className="app__step-list">
|
|
554
|
+
{(DEMO_STEP_FIXTURES.data ?? []).map((step) => (
|
|
555
|
+
<li key={step.id ?? step.attributes?.title}>
|
|
556
|
+
<span className="app__step-title">{step.attributes?.title}</span>
|
|
557
|
+
{typeof step.attributes?.["usage-count"] === "number" && (
|
|
558
|
+
<span className="app__step-meta">{step.attributes?.["usage-count"]} uses</span>
|
|
559
|
+
)}
|
|
560
|
+
</li>
|
|
561
|
+
))}
|
|
562
|
+
</ol>
|
|
563
|
+
</div>
|
|
320
564
|
<div className="app__panel">
|
|
321
565
|
<div className="app__panel-header">
|
|
322
566
|
<h2>Blocks JSON</h2>
|
|
@@ -94,9 +94,9 @@ describe("blocksToMarkdown", () => {
|
|
|
94
94
|
expect(blocksToMarkdown(blocks)).toBe(
|
|
95
95
|
[
|
|
96
96
|
"* Open the Login page.",
|
|
97
|
-
" *Expected
|
|
97
|
+
" *Expected*: The Login page loads successfully.",
|
|
98
98
|
"* Enter a valid username.",
|
|
99
|
-
" *Expected
|
|
99
|
+
" *Expected*: The username is accepted.",
|
|
100
100
|
].join("\n"),
|
|
101
101
|
);
|
|
102
102
|
});
|
|
@@ -119,7 +119,7 @@ describe("blocksToMarkdown", () => {
|
|
|
119
119
|
expect(blocksToMarkdown(blocks)).toBe(
|
|
120
120
|
[
|
|
121
121
|
"* **Click** the _Login_ button",
|
|
122
|
-
|
|
122
|
+
" *Expected*: **Success** is shown",
|
|
123
123
|
" Second line with <u>underline</u>",
|
|
124
124
|
].join("\n"),
|
|
125
125
|
);
|
|
@@ -140,15 +140,15 @@ describe("blocksToMarkdown", () => {
|
|
|
140
140
|
},
|
|
141
141
|
];
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
expect(blocksToMarkdown(blocks)).toBe(
|
|
144
|
+
[
|
|
145
|
+
"* Navigate to login",
|
|
146
|
+
" Open browser",
|
|
147
|
+
" Go to login page",
|
|
148
|
+
" *Expected*: Login form visible",
|
|
149
|
+
].join("\n"),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
152
|
|
|
153
153
|
it("serializes step data containing code fences, blank lines, and images", () => {
|
|
154
154
|
const blocks: CustomEditorBlock[] = [
|
|
@@ -191,7 +191,7 @@ describe("blocksToMarkdown", () => {
|
|
|
191
191
|
" asdsadas",
|
|
192
192
|
" ```",
|
|
193
193
|
" ",
|
|
194
|
-
" *Expected
|
|
194
|
+
" *Expected*: The user receives a real-time notification for the order update.",
|
|
195
195
|
].join("\n"),
|
|
196
196
|
);
|
|
197
197
|
});
|
|
@@ -275,13 +275,28 @@ describe("blocksToMarkdown", () => {
|
|
|
275
275
|
].join("\n"),
|
|
276
276
|
);
|
|
277
277
|
});
|
|
278
|
+
|
|
279
|
+
it("parses a test step with inline image in the title, moving the image to step data", () => {
|
|
280
|
+
const markdown = [
|
|
281
|
+
"## Steps",
|
|
282
|
+
"* asdsadsad aaaaa asd ",
|
|
283
|
+
].join("\n");
|
|
284
|
+
|
|
285
|
+
const blocks = markdownToBlocks(markdown);
|
|
286
|
+
const step = blocks.find((b) => b.type === "testStep") as any;
|
|
287
|
+
|
|
288
|
+
expect(step).toBeTruthy();
|
|
289
|
+
expect(step.props.stepTitle).toBe("asdsadsad aaaaa asd !");
|
|
290
|
+
expect(step.props.stepData).toBe("");
|
|
291
|
+
expect(step.props.expectedResult).toBe("");
|
|
292
|
+
});
|
|
278
293
|
});
|
|
279
294
|
|
|
280
295
|
describe("markdownToBlocks", () => {
|
|
281
296
|
it("parses test steps and test cases", () => {
|
|
282
297
|
const markdown = [
|
|
283
298
|
"* Open the Login page.",
|
|
284
|
-
" *Expected
|
|
299
|
+
" *Expected*: The Login page loads successfully.",
|
|
285
300
|
"",
|
|
286
301
|
":::test-case status=\"ready\" reference=\"QA-7\"",
|
|
287
302
|
"Run the smoke tests.",
|
|
@@ -325,13 +340,13 @@ describe("markdownToBlocks", () => {
|
|
|
325
340
|
"### Steps",
|
|
326
341
|
"",
|
|
327
342
|
"* Step 1: Send a chat message to the user.",
|
|
328
|
-
"**Expected
|
|
343
|
+
"**Expected**: The user receives a real-time notification for the chat message.",
|
|
329
344
|
"* Step 2: Update an order status.",
|
|
330
|
-
"**Expected
|
|
345
|
+
"**Expected**: The user receives a real-time notification for the order update.",
|
|
331
346
|
"* Step 3: Send a file to the user.",
|
|
332
|
-
"**Expected
|
|
347
|
+
"**Expected**: The user receives a real-time notification for the file received.",
|
|
333
348
|
"* Step 4: Verify that the notifications are displayed correctly in the application's notification panel.",
|
|
334
|
-
"**Expected
|
|
349
|
+
"**Expected**: All notifications (chat message, order update, file received) are listed in the notification panel with the correct information (e.g., timestamp, message content).",
|
|
335
350
|
"",
|
|
336
351
|
"### Postconditions",
|
|
337
352
|
"* The user has received and viewed the notifications.",
|
|
@@ -395,7 +410,7 @@ describe("markdownToBlocks", () => {
|
|
|
395
410
|
" asdsadas",
|
|
396
411
|
" ```",
|
|
397
412
|
" ",
|
|
398
|
-
"**Expected
|
|
413
|
+
"**Expected**: The user receives a real-time notification for the order update.",
|
|
399
414
|
].join("\n");
|
|
400
415
|
|
|
401
416
|
const expectedData = [
|
|
@@ -452,7 +467,7 @@ describe("markdownToBlocks", () => {
|
|
|
452
467
|
" asdsadas",
|
|
453
468
|
" ```",
|
|
454
469
|
" ",
|
|
455
|
-
" *Expected
|
|
470
|
+
" *Expected*: The user receives a real-time notification for the order update.",
|
|
456
471
|
].join("\n"),
|
|
457
472
|
);
|
|
458
473
|
});
|
|
@@ -537,7 +552,7 @@ describe("markdownToBlocks", () => {
|
|
|
537
552
|
it("parses expected result prefixes with emphasis", () => {
|
|
538
553
|
const markdown = [
|
|
539
554
|
"* Open the form.",
|
|
540
|
-
" **Expected
|
|
555
|
+
" **Expected:** The form opens.",
|
|
541
556
|
" Expected: Fields are empty.",
|
|
542
557
|
].join("\n");
|
|
543
558
|
|
|
@@ -559,7 +574,7 @@ describe("markdownToBlocks", () => {
|
|
|
559
574
|
"* Navigate to login",
|
|
560
575
|
" Open browser",
|
|
561
576
|
" Go to login page",
|
|
562
|
-
" *Expected
|
|
577
|
+
" *Expected*: Login form visible",
|
|
563
578
|
].join("\n");
|
|
564
579
|
|
|
565
580
|
expect(markdownToBlocks(markdown)).toEqual([
|
|
@@ -580,7 +595,7 @@ describe("markdownToBlocks", () => {
|
|
|
580
595
|
"* Prepare test fixtures",
|
|
581
596
|
"Collect user accounts from staging.",
|
|
582
597
|
"Reset passwords for all test accounts.",
|
|
583
|
-
"*Expected
|
|
598
|
+
"*Expected*: Test accounts are ready for execution.",
|
|
584
599
|
].join("\n");
|
|
585
600
|
|
|
586
601
|
expect(markdownToBlocks(markdown)).toEqual([
|
|
@@ -599,7 +614,7 @@ describe("markdownToBlocks", () => {
|
|
|
599
614
|
it("parses expected result containing a markdown image", () => {
|
|
600
615
|
const markdown = [
|
|
601
616
|
"* Display the generated report.",
|
|
602
|
-
" *Expected
|
|
617
|
+
" *Expected*: ",
|
|
603
618
|
].join("\n");
|
|
604
619
|
|
|
605
620
|
expect(markdownToBlocks(markdown)).toEqual([
|
|
@@ -667,7 +682,7 @@ describe("markdownToBlocks", () => {
|
|
|
667
682
|
expect(roundTrip).toBe(
|
|
668
683
|
[
|
|
669
684
|
"* Should open login screen",
|
|
670
|
-
" *Expected
|
|
685
|
+
" *Expected*: Login should look like this",
|
|
671
686
|
" ",
|
|
672
687
|
].join("\n"),
|
|
673
688
|
);
|
|
@@ -746,4 +761,23 @@ describe("markdownToBlocks", () => {
|
|
|
746
761
|
},
|
|
747
762
|
]);
|
|
748
763
|
});
|
|
764
|
+
|
|
765
|
+
it("parses expected result lines written with bold 'Expected Result' prefix for compatibility", () => {
|
|
766
|
+
const markdown = [
|
|
767
|
+
"* Step 1: Send a chat message to the user.",
|
|
768
|
+
"**Expected Result**: The user receives a real-time notification for the chat message.",
|
|
769
|
+
].join("\n");
|
|
770
|
+
|
|
771
|
+
expect(markdownToBlocks(markdown)).toEqual([
|
|
772
|
+
{
|
|
773
|
+
type: "testStep",
|
|
774
|
+
props: {
|
|
775
|
+
stepTitle: "Step 1: Send a chat message to the user.",
|
|
776
|
+
stepData: "",
|
|
777
|
+
expectedResult: "The user receives a real-time notification for the chat message.",
|
|
778
|
+
},
|
|
779
|
+
children: [],
|
|
780
|
+
},
|
|
781
|
+
]);
|
|
782
|
+
});
|
|
749
783
|
});
|