lib-e2e-cypress-for-dummys-ts 0.2.0 → 0.5.0
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 +634 -466
- package/dist/{selector-picker-VJOLGZ5H.js → chunk-FDMUIHDZ.js} +95 -38
- package/dist/chunk-FDMUIHDZ.js.map +1 -0
- package/dist/index.cjs +1238 -187
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +235 -47
- package/dist/index.d.ts +235 -47
- package/dist/index.js +1174 -143
- package/dist/index.js.map +1 -1
- package/dist/runner.js +107 -0
- package/dist/selector-picker-NKYFFFGK.js +7 -0
- package/dist/selector-picker-NKYFFFGK.js.map +1 -0
- package/package.json +4 -1
- package/dist/chunk-DRNRKHXN.js +0 -60
- package/dist/chunk-DRNRKHXN.js.map +0 -1
- package/dist/selector-picker-VJOLGZ5H.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FORBIDDEN_ID_PREFIXES,
|
|
3
|
+
SelectorPickerElement,
|
|
3
4
|
escAttr,
|
|
4
5
|
escHtml
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FDMUIHDZ.js";
|
|
6
7
|
|
|
7
8
|
// src/models/lang.model.ts
|
|
8
9
|
var SUPPORTED_LANGS = ["es", "en", "fr", "it", "de"];
|
|
9
10
|
function isLang(value) {
|
|
10
11
|
return SUPPORTED_LANGS.includes(value);
|
|
11
12
|
}
|
|
13
|
+
var LOCALE_BY_LANG = {
|
|
14
|
+
es: "es-ES",
|
|
15
|
+
en: "en-GB",
|
|
16
|
+
fr: "fr-FR",
|
|
17
|
+
it: "it-IT",
|
|
18
|
+
de: "de-DE"
|
|
19
|
+
};
|
|
20
|
+
function localeForLang(lang) {
|
|
21
|
+
return isLang(lang) ? LOCALE_BY_LANG[lang] : "es-ES";
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
// src/models/input-types.model.ts
|
|
14
25
|
var INPUT_TYPES = [
|
|
@@ -25,7 +36,7 @@ var INPUT_TYPES = [
|
|
|
25
36
|
// src/models/db-schema.model.ts
|
|
26
37
|
var DB_SCHEMA = {
|
|
27
38
|
name: "E2ECypressDB",
|
|
28
|
-
version:
|
|
39
|
+
version: 11,
|
|
29
40
|
stores: [
|
|
30
41
|
{
|
|
31
42
|
name: "tests",
|
|
@@ -65,11 +76,45 @@ var DB_SCHEMA = {
|
|
|
65
76
|
{ name: "extendedHttpCommands", keyPath: "extendedHttpCommands", unique: false },
|
|
66
77
|
{ name: "allowReadWriteFiles", keyPath: "allowReadWriteFiles", unique: false }
|
|
67
78
|
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
// Single-record store (fixed key id=1) holding the live, in-progress
|
|
82
|
+
// recording session so it survives a micro-frontend crossing or reload.
|
|
83
|
+
// See docs/specs/006-cross-app-recording-continuity.md.
|
|
84
|
+
name: "activeSession",
|
|
85
|
+
keyPath: "id",
|
|
86
|
+
autoIncrement: false,
|
|
87
|
+
indexes: []
|
|
68
88
|
}
|
|
69
89
|
]
|
|
70
90
|
};
|
|
71
91
|
var DB_STORE_NAMES = DB_SCHEMA.stores.map((s) => s.name);
|
|
72
92
|
|
|
93
|
+
// src/models/active-session.model.ts
|
|
94
|
+
var ACTIVE_SESSION_BREADCRUMB_KEY = "e2e-active-session";
|
|
95
|
+
var RESUME_TTL_CONFIG_KEY = "resumeRecencyTtlMinutes";
|
|
96
|
+
var DEFAULT_RESUME_TTL_MINUTES = 30;
|
|
97
|
+
|
|
98
|
+
// src/utils/subject.ts
|
|
99
|
+
var Subject = class {
|
|
100
|
+
_value;
|
|
101
|
+
listeners = /* @__PURE__ */ new Set();
|
|
102
|
+
constructor(initialValue) {
|
|
103
|
+
this._value = initialValue;
|
|
104
|
+
}
|
|
105
|
+
next(value) {
|
|
106
|
+
this._value = value;
|
|
107
|
+
this.listeners.forEach((l) => l(value));
|
|
108
|
+
}
|
|
109
|
+
getValue() {
|
|
110
|
+
return this._value;
|
|
111
|
+
}
|
|
112
|
+
subscribe(fn) {
|
|
113
|
+
this.listeners.add(fn);
|
|
114
|
+
return () => this.listeners.delete(fn);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
73
118
|
// src/i18n/es.ts
|
|
74
119
|
var I18N_ES = {
|
|
75
120
|
MAIN_FRAME: {
|
|
@@ -92,7 +137,9 @@ var I18N_ES = {
|
|
|
92
137
|
NO_TAGS: "Sin etiquetas",
|
|
93
138
|
SAVE_BTN: "\u{1F4BE} Guardar",
|
|
94
139
|
SAVE_AND_EDIT: "\u{1F4DD} Guardar y editar",
|
|
95
|
-
REMOVE_TAG_TITLE: "Quitar"
|
|
140
|
+
REMOVE_TAG_TITLE: "Quitar",
|
|
141
|
+
NOTES_LABEL: "Descripci\xF3n / Notas (opcional):",
|
|
142
|
+
NOTES_PLACEHOLDER: "Qu\xE9 valida este test, condiciones previas, etc."
|
|
96
143
|
},
|
|
97
144
|
TEST_EDITOR: {
|
|
98
145
|
NO_TAGS: "Sin etiquetas",
|
|
@@ -102,8 +149,6 @@ var I18N_ES = {
|
|
|
102
149
|
COPY_DESCRIBE: "\u{1F5C2} Copiar describe()",
|
|
103
150
|
MULTI_SELECT: "\u{1F5C2} Multi-select",
|
|
104
151
|
CANCEL_SELECT: "\u2715 Cancelar",
|
|
105
|
-
SECTION_COMMANDS: "Comandos",
|
|
106
|
-
SECTION_INTERCEPTORS: "Interceptores",
|
|
107
152
|
COPY_CMDS_BTN: "\u{1F4CB} Copiar comandos",
|
|
108
153
|
COPY_ICPS_BTN: "\u{1F4CB} Copiar interceptores",
|
|
109
154
|
DEFAULT_DESCRIBE: "Suite de tests",
|
|
@@ -132,6 +177,18 @@ var I18N_ES = {
|
|
|
132
177
|
DATA_SECTION: "\u{1F4BE} Datos",
|
|
133
178
|
DATA_DESC: "Exporta todos tus tests a JSON o importa una copia de seguridad.",
|
|
134
179
|
EXPORT_BTN: "\u2B06\uFE0F Exportar tests",
|
|
180
|
+
EXPORT_DIALOG_TITLE: "\u2B06\uFE0F Exportar tests",
|
|
181
|
+
EXPORT_MODE_ALL: "Todo",
|
|
182
|
+
EXPORT_MODE_MANUAL: "Selecci\xF3n manual",
|
|
183
|
+
EXPORT_MODE_TAGS: "Por tags",
|
|
184
|
+
EXPORT_ALL_DESC: "Se exportar\xE1n todos los tests guardados.",
|
|
185
|
+
EXPORT_COUNT: "A exportar:",
|
|
186
|
+
EXPORT_CONFIRM: "\u2B06\uFE0F Exportar",
|
|
187
|
+
EXPORT_CANCEL: "Cancelar",
|
|
188
|
+
EXPORT_EMPTY: "No hay tests para exportar.",
|
|
189
|
+
EXPORT_NO_TAGS: "No hay tags disponibles.",
|
|
190
|
+
EXPORT_TAGS_HINT: "Selecciona uno o m\xE1s tags para ver qu\xE9 pruebas se exportar\xE1n.",
|
|
191
|
+
EXPORT_RESULT_LABEL: "Pruebas que se exportar\xE1n:",
|
|
135
192
|
IMPORT_BTN: "\u2B07\uFE0F Importar tests",
|
|
136
193
|
FOLDER_UPDATED_TOAST: "\u2713 Carpeta de Cypress actualizada",
|
|
137
194
|
FOLDER_ERROR_TOAST: "Error al acceder a la carpeta",
|
|
@@ -141,7 +198,13 @@ var I18N_ES = {
|
|
|
141
198
|
JSON_BAD_FORMAT: "El archivo no tiene el formato esperado.",
|
|
142
199
|
SMART_SELECTOR_SECTION: "\u{1F50D} Selector inteligente",
|
|
143
200
|
SMART_SELECTOR_TITLE: "Selector inteligente",
|
|
144
|
-
SMART_SELECTOR_SUB: "Muestra un picker al hacer click en elementos sin selector v\xE1lido."
|
|
201
|
+
SMART_SELECTOR_SUB: "Muestra un picker al hacer click en elementos sin selector v\xE1lido.",
|
|
202
|
+
START_HIDDEN_SECTION: "\u{1F441} Visibilidad del widget",
|
|
203
|
+
START_HIDDEN_TITLE: "Iniciar oculto",
|
|
204
|
+
START_HIDDEN_SUB: "El widget arranca invisible. Usa Ctrl+Shift+E para mostrarlo u ocultarlo.",
|
|
205
|
+
RESUME_TTL_SECTION: "\u23F1 Continuidad de grabaci\xF3n",
|
|
206
|
+
RESUME_TTL_LABEL: "Reanudar sin preguntar (minutos)",
|
|
207
|
+
RESUME_TTL_HINT: "Si recargas o cambias de proyecto, una grabaci\xF3n activa se reanuda sin preguntar dentro de este margen. Pasado ese tiempo, te preguntar\xE1 si continuar o descartar."
|
|
145
208
|
},
|
|
146
209
|
SELECTOR_PICKER: {
|
|
147
210
|
TITLE: "Selecciona un elemento del DOM",
|
|
@@ -182,8 +245,10 @@ var I18N_ES = {
|
|
|
182
245
|
EDIT_MANUAL_BTN: "\u270F\uFE0F Editar manualmente",
|
|
183
246
|
CLOSE_BTN: "\u2715 Cerrar",
|
|
184
247
|
NEW_FILE_BTN: "+ Nuevo archivo",
|
|
248
|
+
NEW_FOLDER_BTN: "+ Nueva carpeta",
|
|
185
249
|
REFRESH_BTN: "\u21BB Actualizar",
|
|
186
250
|
NEW_FILE_PLACEHOLDER: "nombre-del-test",
|
|
251
|
+
NEW_FOLDER_PLACEHOLDER: "nombre-de-carpeta",
|
|
187
252
|
NEW_FILE_CONFIRM: "Crear",
|
|
188
253
|
NEW_FILE_CANCEL: "Cancelar"
|
|
189
254
|
},
|
|
@@ -196,10 +261,19 @@ var I18N_ES = {
|
|
|
196
261
|
NO_CHANGES: "Sin cambios respecto al original",
|
|
197
262
|
NO_CHANGES_SHORT: "Sin cambios",
|
|
198
263
|
LAUNCH_BTN: "\u25B6 Lanzar test",
|
|
264
|
+
LAUNCH_LOCAL_ONLY: "Mu\xE9velo a local para poder probar",
|
|
265
|
+
LAUNCH_RUNNING: "\u23F3 Ejecutando\u2026",
|
|
266
|
+
LAUNCH_PASSED: "\u2713 Prueba superada",
|
|
267
|
+
LAUNCH_FAILED: "\u2717 Prueba fallida",
|
|
268
|
+
LAUNCH_NO_RUNNER: "No se detect\xF3 el runner local (\xBFlo has arrancado?)",
|
|
199
269
|
DIFF_BTN: "\u{1F4CA} Diff",
|
|
200
270
|
SAVE_BTN: "\u{1F4BE} Guardar",
|
|
201
271
|
CLOSE_BTN: "\u2715 Cerrar",
|
|
202
|
-
BACK_TO_EDITOR: "\u2190 Volver al editor"
|
|
272
|
+
BACK_TO_EDITOR: "\u2190 Volver al editor",
|
|
273
|
+
INSERT_BTN: "\u{1FA84} Insertar bloques",
|
|
274
|
+
INSERT_TITLE: "Insertar it() y beforeEach() en el contenido del editor",
|
|
275
|
+
INSERT_DONE: "Bloques insertados en el editor",
|
|
276
|
+
INSERT_ERROR: "No se encontr\xF3 un bloque describe() v\xE1lido en el archivo"
|
|
203
277
|
},
|
|
204
278
|
RECORDER: {
|
|
205
279
|
FS_TITLE: "\u{1F4C1} Acceso a ficheros",
|
|
@@ -228,7 +302,12 @@ var I18N_ES = {
|
|
|
228
302
|
BADGE_PAUSED: "\u23F8 PAUSA",
|
|
229
303
|
BADGE_REC: "\u25CF REC",
|
|
230
304
|
STOP_TITLE: "Detener (Ctrl+R)",
|
|
231
|
-
START_TITLE: "Grabar (Ctrl+R)"
|
|
305
|
+
START_TITLE: "Grabar (Ctrl+R)",
|
|
306
|
+
SESSION_RESUME_TITLE: "\u23FA Grabaci\xF3n en curso",
|
|
307
|
+
SESSION_RESUME_TEXT: "Hay una grabaci\xF3n activa sin terminar.",
|
|
308
|
+
SESSION_RESUME_COUNT: "comandos capturados",
|
|
309
|
+
SESSION_CONTINUE_BTN: "Continuar grabando",
|
|
310
|
+
SESSION_DISCARD_BTN: "Descartar"
|
|
232
311
|
}
|
|
233
312
|
};
|
|
234
313
|
|
|
@@ -254,7 +333,9 @@ var I18N_EN = {
|
|
|
254
333
|
NO_TAGS: "No tags",
|
|
255
334
|
SAVE_BTN: "\u{1F4BE} Save",
|
|
256
335
|
SAVE_AND_EDIT: "\u{1F4DD} Save and edit",
|
|
257
|
-
REMOVE_TAG_TITLE: "Remove"
|
|
336
|
+
REMOVE_TAG_TITLE: "Remove",
|
|
337
|
+
NOTES_LABEL: "Description / Notes (optional):",
|
|
338
|
+
NOTES_PLACEHOLDER: "What this test validates, preconditions, etc."
|
|
258
339
|
},
|
|
259
340
|
TEST_EDITOR: {
|
|
260
341
|
NO_TAGS: "No tags",
|
|
@@ -264,8 +345,6 @@ var I18N_EN = {
|
|
|
264
345
|
COPY_DESCRIBE: "\u{1F5C2} Copy describe()",
|
|
265
346
|
MULTI_SELECT: "\u{1F5C2} Multi-select",
|
|
266
347
|
CANCEL_SELECT: "\u2715 Cancel",
|
|
267
|
-
SECTION_COMMANDS: "Commands",
|
|
268
|
-
SECTION_INTERCEPTORS: "Interceptors",
|
|
269
348
|
COPY_CMDS_BTN: "\u{1F4CB} Copy commands",
|
|
270
349
|
COPY_ICPS_BTN: "\u{1F4CB} Copy interceptors",
|
|
271
350
|
DEFAULT_DESCRIBE: "Test suite",
|
|
@@ -294,6 +373,18 @@ var I18N_EN = {
|
|
|
294
373
|
DATA_SECTION: "\u{1F4BE} Data",
|
|
295
374
|
DATA_DESC: "Export all your tests to JSON or import a backup.",
|
|
296
375
|
EXPORT_BTN: "\u2B06\uFE0F Export tests",
|
|
376
|
+
EXPORT_DIALOG_TITLE: "\u2B06\uFE0F Export tests",
|
|
377
|
+
EXPORT_MODE_ALL: "All",
|
|
378
|
+
EXPORT_MODE_MANUAL: "Manual selection",
|
|
379
|
+
EXPORT_MODE_TAGS: "By tags",
|
|
380
|
+
EXPORT_ALL_DESC: "All saved tests will be exported.",
|
|
381
|
+
EXPORT_COUNT: "To export:",
|
|
382
|
+
EXPORT_CONFIRM: "\u2B06\uFE0F Export",
|
|
383
|
+
EXPORT_CANCEL: "Cancel",
|
|
384
|
+
EXPORT_EMPTY: "No tests to export.",
|
|
385
|
+
EXPORT_NO_TAGS: "No tags available.",
|
|
386
|
+
EXPORT_TAGS_HINT: "Select one or more tags to preview which tests will be exported.",
|
|
387
|
+
EXPORT_RESULT_LABEL: "Tests that will be exported:",
|
|
297
388
|
IMPORT_BTN: "\u2B07\uFE0F Import tests",
|
|
298
389
|
FOLDER_UPDATED_TOAST: "\u2713 Cypress folder updated",
|
|
299
390
|
FOLDER_ERROR_TOAST: "Error accessing the folder",
|
|
@@ -303,7 +394,13 @@ var I18N_EN = {
|
|
|
303
394
|
JSON_BAD_FORMAT: "The file does not have the expected format.",
|
|
304
395
|
SMART_SELECTOR_SECTION: "\u{1F50D} Smart selector",
|
|
305
396
|
SMART_SELECTOR_TITLE: "Smart selector",
|
|
306
|
-
SMART_SELECTOR_SUB: "Shows a picker when clicking elements with no valid selector."
|
|
397
|
+
SMART_SELECTOR_SUB: "Shows a picker when clicking elements with no valid selector.",
|
|
398
|
+
START_HIDDEN_SECTION: "\u{1F441} Widget visibility",
|
|
399
|
+
START_HIDDEN_TITLE: "Start hidden",
|
|
400
|
+
START_HIDDEN_SUB: "The widget starts invisible. Use Ctrl+Shift+E to show or hide it.",
|
|
401
|
+
RESUME_TTL_SECTION: "\u23F1 Recording continuity",
|
|
402
|
+
RESUME_TTL_LABEL: "Resume without asking (minutes)",
|
|
403
|
+
RESUME_TTL_HINT: "If you reload or switch projects, an active recording resumes without asking within this window. After that, you will be asked to continue or discard."
|
|
307
404
|
},
|
|
308
405
|
SELECTOR_PICKER: {
|
|
309
406
|
TITLE: "Select a DOM element",
|
|
@@ -344,8 +441,10 @@ var I18N_EN = {
|
|
|
344
441
|
EDIT_MANUAL_BTN: "\u270F\uFE0F Edit manually",
|
|
345
442
|
CLOSE_BTN: "\u2715 Close",
|
|
346
443
|
NEW_FILE_BTN: "+ New file",
|
|
444
|
+
NEW_FOLDER_BTN: "+ New folder",
|
|
347
445
|
REFRESH_BTN: "\u21BB Refresh",
|
|
348
446
|
NEW_FILE_PLACEHOLDER: "test-name",
|
|
447
|
+
NEW_FOLDER_PLACEHOLDER: "folder-name",
|
|
349
448
|
NEW_FILE_CONFIRM: "Create",
|
|
350
449
|
NEW_FILE_CANCEL: "Cancel"
|
|
351
450
|
},
|
|
@@ -358,10 +457,19 @@ var I18N_EN = {
|
|
|
358
457
|
NO_CHANGES: "No changes from original",
|
|
359
458
|
NO_CHANGES_SHORT: "No changes",
|
|
360
459
|
LAUNCH_BTN: "\u25B6 Run test",
|
|
460
|
+
LAUNCH_LOCAL_ONLY: "Move it to localhost to run tests",
|
|
461
|
+
LAUNCH_RUNNING: "\u23F3 Running\u2026",
|
|
462
|
+
LAUNCH_PASSED: "\u2713 Test passed",
|
|
463
|
+
LAUNCH_FAILED: "\u2717 Test failed",
|
|
464
|
+
LAUNCH_NO_RUNNER: "No local runner detected (did you start it?)",
|
|
361
465
|
DIFF_BTN: "\u{1F4CA} Diff",
|
|
362
466
|
SAVE_BTN: "\u{1F4BE} Save",
|
|
363
467
|
CLOSE_BTN: "\u2715 Close",
|
|
364
|
-
BACK_TO_EDITOR: "\u2190 Back to editor"
|
|
468
|
+
BACK_TO_EDITOR: "\u2190 Back to editor",
|
|
469
|
+
INSERT_BTN: "\u{1FA84} Insert blocks",
|
|
470
|
+
INSERT_TITLE: "Insert it() and beforeEach() into the editor content",
|
|
471
|
+
INSERT_DONE: "Blocks inserted into the editor",
|
|
472
|
+
INSERT_ERROR: "No valid describe() block found in the file"
|
|
365
473
|
},
|
|
366
474
|
RECORDER: {
|
|
367
475
|
FS_TITLE: "\u{1F4C1} File access",
|
|
@@ -390,7 +498,12 @@ var I18N_EN = {
|
|
|
390
498
|
BADGE_PAUSED: "\u23F8 PAUSED",
|
|
391
499
|
BADGE_REC: "\u25CF REC",
|
|
392
500
|
STOP_TITLE: "Stop (Ctrl+R)",
|
|
393
|
-
START_TITLE: "Record (Ctrl+R)"
|
|
501
|
+
START_TITLE: "Record (Ctrl+R)",
|
|
502
|
+
SESSION_RESUME_TITLE: "\u23FA Recording in progress",
|
|
503
|
+
SESSION_RESUME_TEXT: "There is an unfinished active recording.",
|
|
504
|
+
SESSION_RESUME_COUNT: "commands captured",
|
|
505
|
+
SESSION_CONTINUE_BTN: "Keep recording",
|
|
506
|
+
SESSION_DISCARD_BTN: "Discard"
|
|
394
507
|
}
|
|
395
508
|
};
|
|
396
509
|
|
|
@@ -416,7 +529,9 @@ var I18N_FR = {
|
|
|
416
529
|
NO_TAGS: "Sans \xE9tiquettes",
|
|
417
530
|
SAVE_BTN: "\u{1F4BE} Enregistrer",
|
|
418
531
|
SAVE_AND_EDIT: "\u{1F4DD} Enregistrer et \xE9diter",
|
|
419
|
-
REMOVE_TAG_TITLE: "Retirer"
|
|
532
|
+
REMOVE_TAG_TITLE: "Retirer",
|
|
533
|
+
NOTES_LABEL: "Description / Notes (optionnel) :",
|
|
534
|
+
NOTES_PLACEHOLDER: "Ce que ce test valide, conditions pr\xE9alables, etc."
|
|
420
535
|
},
|
|
421
536
|
TEST_EDITOR: {
|
|
422
537
|
NO_TAGS: "Sans \xE9tiquettes",
|
|
@@ -426,8 +541,6 @@ var I18N_FR = {
|
|
|
426
541
|
COPY_DESCRIBE: "\u{1F5C2} Copier describe()",
|
|
427
542
|
MULTI_SELECT: "\u{1F5C2} Multi-s\xE9lection",
|
|
428
543
|
CANCEL_SELECT: "\u2715 Annuler",
|
|
429
|
-
SECTION_COMMANDS: "Commandes",
|
|
430
|
-
SECTION_INTERCEPTORS: "Intercepteurs",
|
|
431
544
|
COPY_CMDS_BTN: "\u{1F4CB} Copier les commandes",
|
|
432
545
|
COPY_ICPS_BTN: "\u{1F4CB} Copier les intercepteurs",
|
|
433
546
|
DEFAULT_DESCRIBE: "Suite de tests",
|
|
@@ -456,6 +569,18 @@ var I18N_FR = {
|
|
|
456
569
|
DATA_SECTION: "\u{1F4BE} Donn\xE9es",
|
|
457
570
|
DATA_DESC: "Exportez tous vos tests en JSON ou importez une sauvegarde.",
|
|
458
571
|
EXPORT_BTN: "\u2B06\uFE0F Exporter les tests",
|
|
572
|
+
EXPORT_DIALOG_TITLE: "\u2B06\uFE0F Exporter les tests",
|
|
573
|
+
EXPORT_MODE_ALL: "Tout",
|
|
574
|
+
EXPORT_MODE_MANUAL: "S\xE9lection manuelle",
|
|
575
|
+
EXPORT_MODE_TAGS: "Par tags",
|
|
576
|
+
EXPORT_ALL_DESC: "Tous les tests enregistr\xE9s seront export\xE9s.",
|
|
577
|
+
EXPORT_COUNT: "\xC0 exporter :",
|
|
578
|
+
EXPORT_CONFIRM: "\u2B06\uFE0F Exporter",
|
|
579
|
+
EXPORT_CANCEL: "Annuler",
|
|
580
|
+
EXPORT_EMPTY: "Aucun test \xE0 exporter.",
|
|
581
|
+
EXPORT_NO_TAGS: "Aucun tag disponible.",
|
|
582
|
+
EXPORT_TAGS_HINT: "S\xE9lectionnez un ou plusieurs tags pour voir les tests qui seront export\xE9s.",
|
|
583
|
+
EXPORT_RESULT_LABEL: "Tests qui seront export\xE9s :",
|
|
459
584
|
IMPORT_BTN: "\u2B07\uFE0F Importer les tests",
|
|
460
585
|
FOLDER_UPDATED_TOAST: "\u2713 Dossier Cypress mis \xE0 jour",
|
|
461
586
|
FOLDER_ERROR_TOAST: "Erreur lors de l'acc\xE8s au dossier",
|
|
@@ -465,7 +590,13 @@ var I18N_FR = {
|
|
|
465
590
|
JSON_BAD_FORMAT: "Le fichier n'a pas le format attendu.",
|
|
466
591
|
SMART_SELECTOR_SECTION: "\u{1F50D} S\xE9lecteur intelligent",
|
|
467
592
|
SMART_SELECTOR_TITLE: "S\xE9lecteur intelligent",
|
|
468
|
-
SMART_SELECTOR_SUB: "Affiche un s\xE9lecteur lors du clic sur des \xE9l\xE9ments sans s\xE9lecteur valide."
|
|
593
|
+
SMART_SELECTOR_SUB: "Affiche un s\xE9lecteur lors du clic sur des \xE9l\xE9ments sans s\xE9lecteur valide.",
|
|
594
|
+
START_HIDDEN_SECTION: "\u{1F441} Visibilit\xE9 du widget",
|
|
595
|
+
START_HIDDEN_TITLE: "D\xE9marrer masqu\xE9",
|
|
596
|
+
START_HIDDEN_SUB: "Le widget d\xE9marre invisible. Utilisez Ctrl+Shift+E pour l'afficher ou le masquer.",
|
|
597
|
+
RESUME_TTL_SECTION: "\u23F1 Continuit\xE9 d'enregistrement",
|
|
598
|
+
RESUME_TTL_LABEL: "Reprendre sans demander (minutes)",
|
|
599
|
+
RESUME_TTL_HINT: "Si vous rechargez ou changez de projet, un enregistrement actif reprend sans demander dans ce d\xE9lai. Au-del\xE0, il vous demandera de continuer ou d'ignorer."
|
|
469
600
|
},
|
|
470
601
|
SELECTOR_PICKER: {
|
|
471
602
|
TITLE: "S\xE9lectionnez un \xE9l\xE9ment du DOM",
|
|
@@ -506,8 +637,10 @@ var I18N_FR = {
|
|
|
506
637
|
EDIT_MANUAL_BTN: "\u270F\uFE0F \xC9diter manuellement",
|
|
507
638
|
CLOSE_BTN: "\u2715 Fermer",
|
|
508
639
|
NEW_FILE_BTN: "+ Nouveau fichier",
|
|
640
|
+
NEW_FOLDER_BTN: "+ Nouveau dossier",
|
|
509
641
|
REFRESH_BTN: "\u21BB Actualiser",
|
|
510
642
|
NEW_FILE_PLACEHOLDER: "nom-du-test",
|
|
643
|
+
NEW_FOLDER_PLACEHOLDER: "nom-du-dossier",
|
|
511
644
|
NEW_FILE_CONFIRM: "Cr\xE9er",
|
|
512
645
|
NEW_FILE_CANCEL: "Annuler"
|
|
513
646
|
},
|
|
@@ -520,10 +653,19 @@ var I18N_FR = {
|
|
|
520
653
|
NO_CHANGES: "Aucun changement par rapport \xE0 l'original",
|
|
521
654
|
NO_CHANGES_SHORT: "Aucun changement",
|
|
522
655
|
LAUNCH_BTN: "\u25B6 Lancer le test",
|
|
656
|
+
LAUNCH_LOCAL_ONLY: "Passe en local pour pouvoir tester",
|
|
657
|
+
LAUNCH_RUNNING: "\u23F3 Ex\xE9cution\u2026",
|
|
658
|
+
LAUNCH_PASSED: "\u2713 Test r\xE9ussi",
|
|
659
|
+
LAUNCH_FAILED: "\u2717 Test \xE9chou\xE9",
|
|
660
|
+
LAUNCH_NO_RUNNER: "Aucun runner local d\xE9tect\xE9 (l'avez-vous d\xE9marr\xE9 ?)",
|
|
523
661
|
DIFF_BTN: "\u{1F4CA} Diff",
|
|
524
662
|
SAVE_BTN: "\u{1F4BE} Enregistrer",
|
|
525
663
|
CLOSE_BTN: "\u2715 Fermer",
|
|
526
|
-
BACK_TO_EDITOR: "\u2190 Retour \xE0 l'\xE9diteur"
|
|
664
|
+
BACK_TO_EDITOR: "\u2190 Retour \xE0 l'\xE9diteur",
|
|
665
|
+
INSERT_BTN: "\u{1FA84} Ins\xE9rer les blocs",
|
|
666
|
+
INSERT_TITLE: "Ins\xE9rer it() et beforeEach() dans le contenu de l'\xE9diteur",
|
|
667
|
+
INSERT_DONE: "Blocs ins\xE9r\xE9s dans l'\xE9diteur",
|
|
668
|
+
INSERT_ERROR: "Aucun bloc describe() valide trouv\xE9 dans le fichier"
|
|
527
669
|
},
|
|
528
670
|
RECORDER: {
|
|
529
671
|
FS_TITLE: "\u{1F4C1} Acc\xE8s aux fichiers",
|
|
@@ -552,7 +694,12 @@ var I18N_FR = {
|
|
|
552
694
|
BADGE_PAUSED: "\u23F8 PAUSE",
|
|
553
695
|
BADGE_REC: "\u25CF REC",
|
|
554
696
|
STOP_TITLE: "Arr\xEAter (Ctrl+R)",
|
|
555
|
-
START_TITLE: "Enregistrer (Ctrl+R)"
|
|
697
|
+
START_TITLE: "Enregistrer (Ctrl+R)",
|
|
698
|
+
SESSION_RESUME_TITLE: "\u23FA Enregistrement en cours",
|
|
699
|
+
SESSION_RESUME_TEXT: "Un enregistrement actif est inachev\xE9.",
|
|
700
|
+
SESSION_RESUME_COUNT: "commandes captur\xE9es",
|
|
701
|
+
SESSION_CONTINUE_BTN: "Continuer l'enregistrement",
|
|
702
|
+
SESSION_DISCARD_BTN: "Ignorer"
|
|
556
703
|
}
|
|
557
704
|
};
|
|
558
705
|
|
|
@@ -578,7 +725,9 @@ var I18N_IT = {
|
|
|
578
725
|
NO_TAGS: "Nessuna etichetta",
|
|
579
726
|
SAVE_BTN: "\u{1F4BE} Salva",
|
|
580
727
|
SAVE_AND_EDIT: "\u{1F4DD} Salva e modifica",
|
|
581
|
-
REMOVE_TAG_TITLE: "Rimuovi"
|
|
728
|
+
REMOVE_TAG_TITLE: "Rimuovi",
|
|
729
|
+
NOTES_LABEL: "Descrizione / Note (opzionale):",
|
|
730
|
+
NOTES_PLACEHOLDER: "Cosa valida questo test, condizioni preliminari, ecc."
|
|
582
731
|
},
|
|
583
732
|
TEST_EDITOR: {
|
|
584
733
|
NO_TAGS: "Nessuna etichetta",
|
|
@@ -588,8 +737,6 @@ var I18N_IT = {
|
|
|
588
737
|
COPY_DESCRIBE: "\u{1F5C2} Copia describe()",
|
|
589
738
|
MULTI_SELECT: "\u{1F5C2} Multi-selezione",
|
|
590
739
|
CANCEL_SELECT: "\u2715 Annulla",
|
|
591
|
-
SECTION_COMMANDS: "Comandi",
|
|
592
|
-
SECTION_INTERCEPTORS: "Interceptor",
|
|
593
740
|
COPY_CMDS_BTN: "\u{1F4CB} Copia comandi",
|
|
594
741
|
COPY_ICPS_BTN: "\u{1F4CB} Copia interceptor",
|
|
595
742
|
DEFAULT_DESCRIBE: "Suite di test",
|
|
@@ -618,6 +765,18 @@ var I18N_IT = {
|
|
|
618
765
|
DATA_SECTION: "\u{1F4BE} Dati",
|
|
619
766
|
DATA_DESC: "Esporta tutti i tuoi test in JSON o importa un backup.",
|
|
620
767
|
EXPORT_BTN: "\u2B06\uFE0F Esporta test",
|
|
768
|
+
EXPORT_DIALOG_TITLE: "\u2B06\uFE0F Esporta test",
|
|
769
|
+
EXPORT_MODE_ALL: "Tutto",
|
|
770
|
+
EXPORT_MODE_MANUAL: "Selezione manuale",
|
|
771
|
+
EXPORT_MODE_TAGS: "Per tag",
|
|
772
|
+
EXPORT_ALL_DESC: "Verranno esportati tutti i test salvati.",
|
|
773
|
+
EXPORT_COUNT: "Da esportare:",
|
|
774
|
+
EXPORT_CONFIRM: "\u2B06\uFE0F Esporta",
|
|
775
|
+
EXPORT_CANCEL: "Annulla",
|
|
776
|
+
EXPORT_EMPTY: "Nessun test da esportare.",
|
|
777
|
+
EXPORT_NO_TAGS: "Nessun tag disponibile.",
|
|
778
|
+
EXPORT_TAGS_HINT: "Seleziona uno o pi\xF9 tag per vedere quali test verranno esportati.",
|
|
779
|
+
EXPORT_RESULT_LABEL: "Test che verranno esportati:",
|
|
621
780
|
IMPORT_BTN: "\u2B07\uFE0F Importa test",
|
|
622
781
|
FOLDER_UPDATED_TOAST: "\u2713 Cartella Cypress aggiornata",
|
|
623
782
|
FOLDER_ERROR_TOAST: "Errore durante l'accesso alla cartella",
|
|
@@ -627,7 +786,13 @@ var I18N_IT = {
|
|
|
627
786
|
JSON_BAD_FORMAT: "Il file non ha il formato previsto.",
|
|
628
787
|
SMART_SELECTOR_SECTION: "\u{1F50D} Selettore intelligente",
|
|
629
788
|
SMART_SELECTOR_TITLE: "Selettore intelligente",
|
|
630
|
-
SMART_SELECTOR_SUB: "Mostra un selettore quando si fa clic su elementi senza selettore valido."
|
|
789
|
+
SMART_SELECTOR_SUB: "Mostra un selettore quando si fa clic su elementi senza selettore valido.",
|
|
790
|
+
START_HIDDEN_SECTION: "\u{1F441} Visibilit\xE0 del widget",
|
|
791
|
+
START_HIDDEN_TITLE: "Avvia nascosto",
|
|
792
|
+
START_HIDDEN_SUB: "Il widget si avvia invisibile. Usa Ctrl+Shift+E per mostrarlo o nasconderlo.",
|
|
793
|
+
RESUME_TTL_SECTION: "\u23F1 Continuit\xE0 di registrazione",
|
|
794
|
+
RESUME_TTL_LABEL: "Riprendi senza chiedere (minuti)",
|
|
795
|
+
RESUME_TTL_HINT: "Se ricarichi o cambi progetto, una registrazione attiva riprende senza chiedere entro questo intervallo. Trascorso, ti chieder\xE0 se continuare o annullare."
|
|
631
796
|
},
|
|
632
797
|
SELECTOR_PICKER: {
|
|
633
798
|
TITLE: "Seleziona un elemento DOM",
|
|
@@ -668,8 +833,10 @@ var I18N_IT = {
|
|
|
668
833
|
EDIT_MANUAL_BTN: "\u270F\uFE0F Modifica manualmente",
|
|
669
834
|
CLOSE_BTN: "\u2715 Chiudi",
|
|
670
835
|
NEW_FILE_BTN: "+ Nuovo file",
|
|
836
|
+
NEW_FOLDER_BTN: "+ Nuova cartella",
|
|
671
837
|
REFRESH_BTN: "\u21BB Aggiorna",
|
|
672
838
|
NEW_FILE_PLACEHOLDER: "nome-del-test",
|
|
839
|
+
NEW_FOLDER_PLACEHOLDER: "nome-cartella",
|
|
673
840
|
NEW_FILE_CONFIRM: "Crea",
|
|
674
841
|
NEW_FILE_CANCEL: "Annulla"
|
|
675
842
|
},
|
|
@@ -682,10 +849,19 @@ var I18N_IT = {
|
|
|
682
849
|
NO_CHANGES: "Nessuna modifica rispetto all'originale",
|
|
683
850
|
NO_CHANGES_SHORT: "Nessuna modifica",
|
|
684
851
|
LAUNCH_BTN: "\u25B6 Esegui test",
|
|
852
|
+
LAUNCH_LOCAL_ONLY: "Spostati in locale per poter testare",
|
|
853
|
+
LAUNCH_RUNNING: "\u23F3 Esecuzione\u2026",
|
|
854
|
+
LAUNCH_PASSED: "\u2713 Test superato",
|
|
855
|
+
LAUNCH_FAILED: "\u2717 Test fallito",
|
|
856
|
+
LAUNCH_NO_RUNNER: "Nessun runner locale rilevato (l'hai avviato?)",
|
|
685
857
|
DIFF_BTN: "\u{1F4CA} Diff",
|
|
686
858
|
SAVE_BTN: "\u{1F4BE} Salva",
|
|
687
859
|
CLOSE_BTN: "\u2715 Chiudi",
|
|
688
|
-
BACK_TO_EDITOR: "\u2190 Torna all'editor"
|
|
860
|
+
BACK_TO_EDITOR: "\u2190 Torna all'editor",
|
|
861
|
+
INSERT_BTN: "\u{1FA84} Inserisci blocchi",
|
|
862
|
+
INSERT_TITLE: "Inserisci it() e beforeEach() nel contenuto dell'editor",
|
|
863
|
+
INSERT_DONE: "Blocchi inseriti nell'editor",
|
|
864
|
+
INSERT_ERROR: "Nessun blocco describe() valido trovato nel file"
|
|
689
865
|
},
|
|
690
866
|
RECORDER: {
|
|
691
867
|
FS_TITLE: "\u{1F4C1} Accesso ai file",
|
|
@@ -714,7 +890,12 @@ var I18N_IT = {
|
|
|
714
890
|
BADGE_PAUSED: "\u23F8 PAUSA",
|
|
715
891
|
BADGE_REC: "\u25CF REC",
|
|
716
892
|
STOP_TITLE: "Ferma (Ctrl+R)",
|
|
717
|
-
START_TITLE: "Registra (Ctrl+R)"
|
|
893
|
+
START_TITLE: "Registra (Ctrl+R)",
|
|
894
|
+
SESSION_RESUME_TITLE: "\u23FA Registrazione in corso",
|
|
895
|
+
SESSION_RESUME_TEXT: "C'\xE8 una registrazione attiva non terminata.",
|
|
896
|
+
SESSION_RESUME_COUNT: "comandi catturati",
|
|
897
|
+
SESSION_CONTINUE_BTN: "Continua a registrare",
|
|
898
|
+
SESSION_DISCARD_BTN: "Annulla"
|
|
718
899
|
}
|
|
719
900
|
};
|
|
720
901
|
|
|
@@ -740,7 +921,9 @@ var I18N_DE = {
|
|
|
740
921
|
NO_TAGS: "Keine Tags",
|
|
741
922
|
SAVE_BTN: "\u{1F4BE} Speichern",
|
|
742
923
|
SAVE_AND_EDIT: "\u{1F4DD} Speichern und bearbeiten",
|
|
743
|
-
REMOVE_TAG_TITLE: "Entfernen"
|
|
924
|
+
REMOVE_TAG_TITLE: "Entfernen",
|
|
925
|
+
NOTES_LABEL: "Beschreibung / Notizen (optional):",
|
|
926
|
+
NOTES_PLACEHOLDER: "Was dieser Test validiert, Voraussetzungen usw."
|
|
744
927
|
},
|
|
745
928
|
TEST_EDITOR: {
|
|
746
929
|
NO_TAGS: "Keine Tags",
|
|
@@ -750,8 +933,6 @@ var I18N_DE = {
|
|
|
750
933
|
COPY_DESCRIBE: "\u{1F5C2} describe() kopieren",
|
|
751
934
|
MULTI_SELECT: "\u{1F5C2} Mehrfachauswahl",
|
|
752
935
|
CANCEL_SELECT: "\u2715 Abbrechen",
|
|
753
|
-
SECTION_COMMANDS: "Befehle",
|
|
754
|
-
SECTION_INTERCEPTORS: "Interceptors",
|
|
755
936
|
COPY_CMDS_BTN: "\u{1F4CB} Befehle kopieren",
|
|
756
937
|
COPY_ICPS_BTN: "\u{1F4CB} Interceptors kopieren",
|
|
757
938
|
DEFAULT_DESCRIBE: "Test-Suite",
|
|
@@ -780,6 +961,18 @@ var I18N_DE = {
|
|
|
780
961
|
DATA_SECTION: "\u{1F4BE} Daten",
|
|
781
962
|
DATA_DESC: "Exportieren Sie alle Tests als JSON oder importieren Sie ein Backup.",
|
|
782
963
|
EXPORT_BTN: "\u2B06\uFE0F Tests exportieren",
|
|
964
|
+
EXPORT_DIALOG_TITLE: "\u2B06\uFE0F Tests exportieren",
|
|
965
|
+
EXPORT_MODE_ALL: "Alle",
|
|
966
|
+
EXPORT_MODE_MANUAL: "Manuelle Auswahl",
|
|
967
|
+
EXPORT_MODE_TAGS: "Nach Tags",
|
|
968
|
+
EXPORT_ALL_DESC: "Alle gespeicherten Tests werden exportiert.",
|
|
969
|
+
EXPORT_COUNT: "Zu exportieren:",
|
|
970
|
+
EXPORT_CONFIRM: "\u2B06\uFE0F Exportieren",
|
|
971
|
+
EXPORT_CANCEL: "Abbrechen",
|
|
972
|
+
EXPORT_EMPTY: "Keine Tests zum Exportieren.",
|
|
973
|
+
EXPORT_NO_TAGS: "Keine Tags verf\xFCgbar.",
|
|
974
|
+
EXPORT_TAGS_HINT: "W\xE4hle ein oder mehrere Tags, um zu sehen, welche Tests exportiert werden.",
|
|
975
|
+
EXPORT_RESULT_LABEL: "Diese Tests werden exportiert:",
|
|
783
976
|
IMPORT_BTN: "\u2B07\uFE0F Tests importieren",
|
|
784
977
|
FOLDER_UPDATED_TOAST: "\u2713 Cypress-Ordner aktualisiert",
|
|
785
978
|
FOLDER_ERROR_TOAST: "Fehler beim Zugriff auf den Ordner",
|
|
@@ -789,7 +982,13 @@ var I18N_DE = {
|
|
|
789
982
|
JSON_BAD_FORMAT: "Die Datei hat nicht das erwartete Format.",
|
|
790
983
|
SMART_SELECTOR_SECTION: "\u{1F50D} Intelligenter Selektor",
|
|
791
984
|
SMART_SELECTOR_TITLE: "Intelligenter Selektor",
|
|
792
|
-
SMART_SELECTOR_SUB: "Zeigt ein Auswahlfeld, wenn auf Elemente ohne g\xFCltigen Selektor geklickt wird."
|
|
985
|
+
SMART_SELECTOR_SUB: "Zeigt ein Auswahlfeld, wenn auf Elemente ohne g\xFCltigen Selektor geklickt wird.",
|
|
986
|
+
START_HIDDEN_SECTION: "\u{1F441} Widget-Sichtbarkeit",
|
|
987
|
+
START_HIDDEN_TITLE: "Versteckt starten",
|
|
988
|
+
START_HIDDEN_SUB: "Das Widget startet unsichtbar. Verwenden Sie Ctrl+Shift+E zum Ein-/Ausblenden.",
|
|
989
|
+
RESUME_TTL_SECTION: "\u23F1 Aufzeichnungskontinuit\xE4t",
|
|
990
|
+
RESUME_TTL_LABEL: "Ohne Nachfrage fortsetzen (Minuten)",
|
|
991
|
+
RESUME_TTL_HINT: "Wenn Sie neu laden oder das Projekt wechseln, wird eine aktive Aufzeichnung innerhalb dieses Zeitfensters ohne Nachfrage fortgesetzt. Danach werden Sie gefragt, ob fortsetzen oder verwerfen."
|
|
793
992
|
},
|
|
794
993
|
SELECTOR_PICKER: {
|
|
795
994
|
TITLE: "DOM-Element ausw\xE4hlen",
|
|
@@ -830,8 +1029,10 @@ var I18N_DE = {
|
|
|
830
1029
|
EDIT_MANUAL_BTN: "\u270F\uFE0F Manuell bearbeiten",
|
|
831
1030
|
CLOSE_BTN: "\u2715 Schlie\xDFen",
|
|
832
1031
|
NEW_FILE_BTN: "+ Neue Datei",
|
|
1032
|
+
NEW_FOLDER_BTN: "+ Neuer Ordner",
|
|
833
1033
|
REFRESH_BTN: "\u21BB Aktualisieren",
|
|
834
1034
|
NEW_FILE_PLACEHOLDER: "test-name",
|
|
1035
|
+
NEW_FOLDER_PLACEHOLDER: "ordnername",
|
|
835
1036
|
NEW_FILE_CONFIRM: "Erstellen",
|
|
836
1037
|
NEW_FILE_CANCEL: "Abbrechen"
|
|
837
1038
|
},
|
|
@@ -844,10 +1045,19 @@ var I18N_DE = {
|
|
|
844
1045
|
NO_CHANGES: "Keine \xC4nderungen gegen\xFCber dem Original",
|
|
845
1046
|
NO_CHANGES_SHORT: "Keine \xC4nderungen",
|
|
846
1047
|
LAUNCH_BTN: "\u25B6 Test starten",
|
|
1048
|
+
LAUNCH_LOCAL_ONLY: "Wechsle zu localhost, um Tests auszuf\xFChren",
|
|
1049
|
+
LAUNCH_RUNNING: "\u23F3 L\xE4uft\u2026",
|
|
1050
|
+
LAUNCH_PASSED: "\u2713 Test bestanden",
|
|
1051
|
+
LAUNCH_FAILED: "\u2717 Test fehlgeschlagen",
|
|
1052
|
+
LAUNCH_NO_RUNNER: "Kein lokaler Runner erkannt (gestartet?)",
|
|
847
1053
|
DIFF_BTN: "\u{1F4CA} Diff",
|
|
848
1054
|
SAVE_BTN: "\u{1F4BE} Speichern",
|
|
849
1055
|
CLOSE_BTN: "\u2715 Schlie\xDFen",
|
|
850
|
-
BACK_TO_EDITOR: "\u2190 Zur\xFCck zum Editor"
|
|
1056
|
+
BACK_TO_EDITOR: "\u2190 Zur\xFCck zum Editor",
|
|
1057
|
+
INSERT_BTN: "\u{1FA84} Bl\xF6cke einf\xFCgen",
|
|
1058
|
+
INSERT_TITLE: "it() und beforeEach() in den Editor-Inhalt einf\xFCgen",
|
|
1059
|
+
INSERT_DONE: "Bl\xF6cke in den Editor eingef\xFCgt",
|
|
1060
|
+
INSERT_ERROR: "Kein g\xFCltiger describe()-Block in der Datei gefunden"
|
|
851
1061
|
},
|
|
852
1062
|
RECORDER: {
|
|
853
1063
|
FS_TITLE: "\u{1F4C1} Dateizugriff",
|
|
@@ -876,13 +1086,18 @@ var I18N_DE = {
|
|
|
876
1086
|
BADGE_PAUSED: "\u23F8 PAUSE",
|
|
877
1087
|
BADGE_REC: "\u25CF REC",
|
|
878
1088
|
STOP_TITLE: "Stopp (Ctrl+R)",
|
|
879
|
-
START_TITLE: "Aufzeichnen (Ctrl+R)"
|
|
1089
|
+
START_TITLE: "Aufzeichnen (Ctrl+R)",
|
|
1090
|
+
SESSION_RESUME_TITLE: "\u23FA Aufzeichnung l\xE4uft",
|
|
1091
|
+
SESSION_RESUME_TEXT: "Es gibt eine nicht abgeschlossene aktive Aufzeichnung.",
|
|
1092
|
+
SESSION_RESUME_COUNT: "erfasste Befehle",
|
|
1093
|
+
SESSION_CONTINUE_BTN: "Weiter aufzeichnen",
|
|
1094
|
+
SESSION_DISCARD_BTN: "Verwerfen"
|
|
880
1095
|
}
|
|
881
1096
|
};
|
|
882
1097
|
|
|
883
1098
|
// src/services/translation.service.ts
|
|
884
1099
|
var TranslationService = class {
|
|
885
|
-
lang
|
|
1100
|
+
lang$;
|
|
886
1101
|
translations = {
|
|
887
1102
|
es: I18N_ES,
|
|
888
1103
|
en: I18N_EN,
|
|
@@ -891,17 +1106,21 @@ var TranslationService = class {
|
|
|
891
1106
|
de: I18N_DE
|
|
892
1107
|
};
|
|
893
1108
|
constructor() {
|
|
894
|
-
this.lang = this.detectLang();
|
|
1109
|
+
this.lang$ = new Subject(this.detectLang());
|
|
895
1110
|
}
|
|
896
1111
|
setLang(lang) {
|
|
897
|
-
this.lang
|
|
1112
|
+
this.lang$.next(lang);
|
|
898
1113
|
}
|
|
899
1114
|
getLang() {
|
|
900
|
-
return this.lang;
|
|
1115
|
+
return this.lang$.getValue();
|
|
1116
|
+
}
|
|
1117
|
+
/** Subscribe to language changes; returns an unsubscribe function. */
|
|
1118
|
+
onLangChange(fn) {
|
|
1119
|
+
return this.lang$.subscribe(fn);
|
|
901
1120
|
}
|
|
902
1121
|
translate(key) {
|
|
903
1122
|
const keys = key.split(".");
|
|
904
|
-
let value = this.translations[this.lang];
|
|
1123
|
+
let value = this.translations[this.lang$.getValue()];
|
|
905
1124
|
for (const k of keys) {
|
|
906
1125
|
value = value?.[k];
|
|
907
1126
|
if (value === void 0) return key;
|
|
@@ -915,14 +1134,41 @@ var TranslationService = class {
|
|
|
915
1134
|
};
|
|
916
1135
|
var translationService = new TranslationService();
|
|
917
1136
|
|
|
1137
|
+
// src/utils/code-format.utils.ts
|
|
1138
|
+
function gcd(a, b) {
|
|
1139
|
+
while (b) {
|
|
1140
|
+
const t = b;
|
|
1141
|
+
b = a % b;
|
|
1142
|
+
a = t;
|
|
1143
|
+
}
|
|
1144
|
+
return a;
|
|
1145
|
+
}
|
|
1146
|
+
function escapeSingleQuotes(value) {
|
|
1147
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1148
|
+
}
|
|
1149
|
+
function normalizeBlock(code, baseIndent) {
|
|
1150
|
+
const lines = code.split("\n").map((l) => l.trimEnd());
|
|
1151
|
+
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
1152
|
+
if (!nonEmpty.length) return "";
|
|
1153
|
+
const indents = nonEmpty.map((l) => l.length - l.trimStart().length);
|
|
1154
|
+
const minIndent = Math.min(...indents);
|
|
1155
|
+
const relIndents = [...new Set(indents.map((n) => n - minIndent).filter((n) => n > 0))];
|
|
1156
|
+
const step = relIndents.length > 0 ? relIndents.reduce(gcd) : 2;
|
|
1157
|
+
return lines.map((l) => {
|
|
1158
|
+
if (!l.trim()) return "";
|
|
1159
|
+
const level = Math.round((l.length - l.trimStart().length - minIndent) / step);
|
|
1160
|
+
return baseIndent + " ".repeat(level) + l.trimStart();
|
|
1161
|
+
}).join("\n");
|
|
1162
|
+
}
|
|
1163
|
+
|
|
918
1164
|
// src/services/transformation.service.ts
|
|
919
1165
|
var TransformationService = class {
|
|
920
1166
|
toLang(lang) {
|
|
921
1167
|
return isLang(lang) ? lang : "en";
|
|
922
1168
|
}
|
|
923
1169
|
generateItDescription(description, commands) {
|
|
924
|
-
const commandsBlock = commands.map((cmd) =>
|
|
925
|
-
return `it('${description}', () => {
|
|
1170
|
+
const commandsBlock = commands.map((cmd) => normalizeBlock(cmd, " ")).join("\n");
|
|
1171
|
+
return `it('${escapeSingleQuotes(description)}', () => {
|
|
926
1172
|
${commandsBlock}
|
|
927
1173
|
});`;
|
|
928
1174
|
}
|
|
@@ -952,6 +1198,13 @@ ${interceptors} })
|
|
|
952
1198
|
}
|
|
953
1199
|
return content.slice(0, idx) + "\n" + itBlock + "\n" + content.slice(idx);
|
|
954
1200
|
}
|
|
1201
|
+
buildBlockComment(notes) {
|
|
1202
|
+
if (!notes.trim()) return "";
|
|
1203
|
+
const lines = notes.split("\n").map((l) => ` * ${l}`).join("\n");
|
|
1204
|
+
return `/**
|
|
1205
|
+
${lines}
|
|
1206
|
+
*/`;
|
|
1207
|
+
}
|
|
955
1208
|
isFile(file) {
|
|
956
1209
|
return !!file && file.kind === "file";
|
|
957
1210
|
}
|
|
@@ -969,28 +1222,14 @@ ${interceptors} })
|
|
|
969
1222
|
};
|
|
970
1223
|
var advancedTestTransformationService = new AdvancedTestTransformationService();
|
|
971
1224
|
|
|
972
|
-
// src/utils/subject.ts
|
|
973
|
-
var Subject = class {
|
|
974
|
-
_value;
|
|
975
|
-
listeners = /* @__PURE__ */ new Set();
|
|
976
|
-
constructor(initialValue) {
|
|
977
|
-
this._value = initialValue;
|
|
978
|
-
}
|
|
979
|
-
next(value) {
|
|
980
|
-
this._value = value;
|
|
981
|
-
this.listeners.forEach((l) => l(value));
|
|
982
|
-
}
|
|
983
|
-
getValue() {
|
|
984
|
-
return this._value;
|
|
985
|
-
}
|
|
986
|
-
subscribe(fn) {
|
|
987
|
-
this.listeners.add(fn);
|
|
988
|
-
return () => this.listeners.delete(fn);
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
|
|
992
1225
|
// src/services/recording.service.ts
|
|
993
1226
|
var OWN_SELECTOR = '[data-cy="lib-e2e-cypress-for-dummys"]';
|
|
1227
|
+
function createSessionId() {
|
|
1228
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1229
|
+
return crypto.randomUUID();
|
|
1230
|
+
}
|
|
1231
|
+
return `sess-${Date.now().toString(36)}-${Math.floor(Math.random() * 1e9).toString(36)}`;
|
|
1232
|
+
}
|
|
994
1233
|
var RecordingService = class {
|
|
995
1234
|
commands$ = new Subject([]);
|
|
996
1235
|
interceptors$ = new Subject([]);
|
|
@@ -1000,6 +1239,9 @@ var RecordingService = class {
|
|
|
1000
1239
|
inputDebounceTimers = /* @__PURE__ */ new Map();
|
|
1001
1240
|
abort = new AbortController();
|
|
1002
1241
|
selectorStrategy = "data-cy";
|
|
1242
|
+
/** Stable id of the current live session (null when none has started). */
|
|
1243
|
+
sessionId = null;
|
|
1244
|
+
startedAt = 0;
|
|
1003
1245
|
// Stored originals for history patching cleanup
|
|
1004
1246
|
origPushState = history.pushState.bind(history);
|
|
1005
1247
|
origReplaceState = history.replaceState.bind(history);
|
|
@@ -1011,12 +1253,42 @@ var RecordingService = class {
|
|
|
1011
1253
|
}
|
|
1012
1254
|
// ── Public API ────────────────────────────────────────────────────────────
|
|
1013
1255
|
startRecording() {
|
|
1256
|
+
this.sessionId = createSessionId();
|
|
1257
|
+
this.startedAt = Date.now();
|
|
1014
1258
|
this.isPaused$.next(false);
|
|
1015
1259
|
this.isRecording$.next(true);
|
|
1016
1260
|
this.addCommand(`cy.viewport(1900, 1200)`);
|
|
1017
1261
|
this.addCommand(`cy.visit('${window.location.pathname}')`);
|
|
1018
1262
|
this.addCommand(`cy.get('[data-cy="lib-e2e-cypress-for-dummys"]').invoke('hide');`);
|
|
1019
1263
|
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Rehydrates a previously persisted session WITHOUT running the startRecording
|
|
1266
|
+
* bootstrap (no viewport/visit/hide). Used to continue a recording across a
|
|
1267
|
+
* micro-frontend crossing or a same-origin reload.
|
|
1268
|
+
* See docs/specs/006-cross-app-recording-continuity.md.
|
|
1269
|
+
*/
|
|
1270
|
+
restoreSession(state) {
|
|
1271
|
+
this.sessionId = state.sessionId;
|
|
1272
|
+
this.startedAt = state.startedAt;
|
|
1273
|
+
this.selectorStrategy = state.selectorStrategy;
|
|
1274
|
+
this.commands$.next([...state.commands]);
|
|
1275
|
+
this.interceptors$.next([...state.interceptors]);
|
|
1276
|
+
this.isPaused$.next(state.isPaused);
|
|
1277
|
+
this.isRecording$.next(state.isRecording);
|
|
1278
|
+
}
|
|
1279
|
+
/** Full snapshot of the live session for persistence. */
|
|
1280
|
+
getSessionSnapshot() {
|
|
1281
|
+
return {
|
|
1282
|
+
sessionId: this.sessionId ?? createSessionId(),
|
|
1283
|
+
isRecording: this.isRecording$.getValue(),
|
|
1284
|
+
isPaused: this.isPaused$.getValue(),
|
|
1285
|
+
commands: this.commands$.getValue(),
|
|
1286
|
+
interceptors: this.interceptors$.getValue(),
|
|
1287
|
+
selectorStrategy: this.selectorStrategy,
|
|
1288
|
+
startedAt: this.startedAt,
|
|
1289
|
+
updatedAt: Date.now()
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1020
1292
|
stopRecording() {
|
|
1021
1293
|
this.isPaused$.next(false);
|
|
1022
1294
|
this.isRecording$.next(false);
|
|
@@ -1103,6 +1375,21 @@ var RecordingService = class {
|
|
|
1103
1375
|
onPauseChange(fn) {
|
|
1104
1376
|
return this.isPaused$.subscribe(fn);
|
|
1105
1377
|
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Fires a full session snapshot whenever any persisted field changes
|
|
1380
|
+
* (commands, interceptors, recording or paused state). Drives the debounced
|
|
1381
|
+
* persistence of the live session. Returns a combined unsubscribe.
|
|
1382
|
+
*/
|
|
1383
|
+
onSessionChange(fn) {
|
|
1384
|
+
const emit = () => fn(this.getSessionSnapshot());
|
|
1385
|
+
const unsubs = [
|
|
1386
|
+
this.commands$.subscribe(emit),
|
|
1387
|
+
this.interceptors$.subscribe(emit),
|
|
1388
|
+
this.isRecording$.subscribe(emit),
|
|
1389
|
+
this.isPaused$.subscribe(emit)
|
|
1390
|
+
];
|
|
1391
|
+
return () => unsubs.forEach((u) => u());
|
|
1392
|
+
}
|
|
1106
1393
|
onSelectorNotFound(fn) {
|
|
1107
1394
|
return this.selectorNotFound$.subscribe((v) => {
|
|
1108
1395
|
if (v) fn(v.target, v.action);
|
|
@@ -1334,6 +1621,7 @@ var RecordingService = class {
|
|
|
1334
1621
|
|
|
1335
1622
|
// src/services/persistence.service.ts
|
|
1336
1623
|
import { openDB } from "idb";
|
|
1624
|
+
var ACTIVE_SESSION_ID = 1;
|
|
1337
1625
|
var PersistenceService = class {
|
|
1338
1626
|
// dbName is overridable so tests can use unique names for isolation.
|
|
1339
1627
|
constructor(dbName = DB_SCHEMA.name) {
|
|
@@ -1362,9 +1650,9 @@ var PersistenceService = class {
|
|
|
1362
1650
|
return this._db;
|
|
1363
1651
|
}
|
|
1364
1652
|
// ── Tests ─────────────────────────────────────────────────────────────────
|
|
1365
|
-
async insertTest(name, commands = [], interceptors = [], tags = []) {
|
|
1653
|
+
async insertTest(name, commands = [], interceptors = [], tags = [], notes) {
|
|
1366
1654
|
const db = await this.getDB();
|
|
1367
|
-
const record = { name, createdAt: Date.now(), ...tags.length ? { tags } : {} };
|
|
1655
|
+
const record = { name, createdAt: Date.now(), ...tags.length ? { tags } : {}, ...notes ? { notes } : {} };
|
|
1368
1656
|
const id = await db.add("tests", record);
|
|
1369
1657
|
if (commands.length) await this.insertCommands(commands, id);
|
|
1370
1658
|
if (interceptors.length) await this.insertInterceptors(interceptors, id);
|
|
@@ -1387,10 +1675,10 @@ var PersistenceService = class {
|
|
|
1387
1675
|
if (!record) return null;
|
|
1388
1676
|
const commands = await this.getCommandStrings(testId);
|
|
1389
1677
|
const interceptors = await this.getInterceptorStrings(testId);
|
|
1390
|
-
const itBlock = `it('${record.name}', () => {
|
|
1391
|
-
|
|
1678
|
+
const itBlock = `it('${escapeSingleQuotes(record.name)}', () => {
|
|
1679
|
+
${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
1392
1680
|
});`;
|
|
1393
|
-
const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => " "
|
|
1681
|
+
const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => normalizeBlock(i, " ")).join("\n") + "\n" : "";
|
|
1394
1682
|
return { ...record, commands, interceptors, cypressCommands: commands, itBlock, interceptorsBlock };
|
|
1395
1683
|
}
|
|
1396
1684
|
async deleteTest(id) {
|
|
@@ -1468,6 +1756,25 @@ var PersistenceService = class {
|
|
|
1468
1756
|
const records = await db.getAll("configuration");
|
|
1469
1757
|
return records[0] ?? null;
|
|
1470
1758
|
}
|
|
1759
|
+
// ── Active recording session ──────────────────────────────────────────────
|
|
1760
|
+
/** Upserts the live recording session (single record, fixed key). */
|
|
1761
|
+
async saveActiveSession(state) {
|
|
1762
|
+
const db = await this.getDB();
|
|
1763
|
+
await db.put("activeSession", { ...state, id: ACTIVE_SESSION_ID });
|
|
1764
|
+
}
|
|
1765
|
+
/** Returns the persisted live session, or null when none is stored. */
|
|
1766
|
+
async getActiveSession() {
|
|
1767
|
+
const db = await this.getDB();
|
|
1768
|
+
const record = await db.get("activeSession", ACTIVE_SESSION_ID);
|
|
1769
|
+
if (!record) return null;
|
|
1770
|
+
const { id: _id, ...state } = record;
|
|
1771
|
+
return state;
|
|
1772
|
+
}
|
|
1773
|
+
/** Removes the live session record. Safe to call when none exists. */
|
|
1774
|
+
async clearActiveSession() {
|
|
1775
|
+
const db = await this.getDB();
|
|
1776
|
+
await db.delete("activeSession", ACTIVE_SESSION_ID);
|
|
1777
|
+
}
|
|
1471
1778
|
// ── Bulk operations ───────────────────────────────────────────────────────
|
|
1472
1779
|
async clearAllData() {
|
|
1473
1780
|
const db = await this.getDB();
|
|
@@ -1478,10 +1785,18 @@ var PersistenceService = class {
|
|
|
1478
1785
|
]);
|
|
1479
1786
|
}
|
|
1480
1787
|
async ingestFileData(tests, interceptors) {
|
|
1481
|
-
|
|
1482
|
-
this.
|
|
1483
|
-
|
|
1484
|
-
|
|
1788
|
+
if (Array.isArray(tests)) {
|
|
1789
|
+
const db = await this.getDB();
|
|
1790
|
+
for (const test of tests) {
|
|
1791
|
+
const { id: _id, commands, interceptors: testInterceptors, ...record } = test;
|
|
1792
|
+
const newId = await db.add("tests", record);
|
|
1793
|
+
const cmds = Array.isArray(commands) ? commands : [];
|
|
1794
|
+
const icps = Array.isArray(testInterceptors) ? testInterceptors : [];
|
|
1795
|
+
if (cmds.length) await this.insertCommands(cmds, newId);
|
|
1796
|
+
if (icps.length) await this.insertInterceptors(icps, newId);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
await this.bulkInsertWithoutId("interceptors", interceptors);
|
|
1485
1800
|
}
|
|
1486
1801
|
async requestDirectoryPermissions() {
|
|
1487
1802
|
if (!("showDirectoryPicker" in window)) {
|
|
@@ -1924,6 +2239,25 @@ function showToast(message, isSuccess = true) {
|
|
|
1924
2239
|
setTimeout(() => toast.remove(), 3e3);
|
|
1925
2240
|
}
|
|
1926
2241
|
|
|
2242
|
+
// src/utils/export-selection.utils.ts
|
|
2243
|
+
function selectTestsForExport(tests, mode, opts = {}) {
|
|
2244
|
+
if (mode === "all") return [...tests];
|
|
2245
|
+
if (mode === "manual") {
|
|
2246
|
+
const ids = new Set(opts.ids ?? []);
|
|
2247
|
+
return tests.filter((t) => ids.has(t.id));
|
|
2248
|
+
}
|
|
2249
|
+
const tags = new Set(opts.tags ?? []);
|
|
2250
|
+
if (tags.size === 0) return [];
|
|
2251
|
+
return tests.filter((t) => (t.tags ?? []).some((tag) => tags.has(tag)));
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// src/utils/host.utils.ts
|
|
2255
|
+
function isLocalHost(hostname) {
|
|
2256
|
+
if (!hostname) return true;
|
|
2257
|
+
const h = hostname.toLowerCase();
|
|
2258
|
+
return h === "localhost" || h.endsWith(".localhost") || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0";
|
|
2259
|
+
}
|
|
2260
|
+
|
|
1927
2261
|
// src/components/test-previsualizer/test-previsualizer.styles.ts
|
|
1928
2262
|
var TEST_PREVISUALIZER_STYLES = `
|
|
1929
2263
|
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; overflow: hidden; }
|
|
@@ -2129,7 +2463,8 @@ if (!customElements.get("test-previsualizer")) {
|
|
|
2129
2463
|
var SAVE_TEST_STYLES = `
|
|
2130
2464
|
:host { display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; }
|
|
2131
2465
|
* { box-sizing: border-box; }
|
|
2132
|
-
.container { padding: 24px 28px; text-align:
|
|
2466
|
+
.container { padding: 24px 28px; text-align: left; }
|
|
2467
|
+
.btn-row, p { text-align: center; }
|
|
2133
2468
|
p { margin: 0 0 20px; font-size: 14px; color: #8b949e; line-height: 1.5; }
|
|
2134
2469
|
.btn-row { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }
|
|
2135
2470
|
button {
|
|
@@ -2151,6 +2486,15 @@ var SAVE_TEST_STYLES = `
|
|
|
2151
2486
|
}
|
|
2152
2487
|
input[type="text"]:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.15); }
|
|
2153
2488
|
input[type="text"]::placeholder { color: #484f58; }
|
|
2489
|
+
textarea {
|
|
2490
|
+
width: 100%; padding: 10px 14px; border: 1px solid #30363d;
|
|
2491
|
+
border-radius: 8px; background: #0d1117; color: #e6edf3;
|
|
2492
|
+
font-size: 14px; outline: none; margin-bottom: 4px;
|
|
2493
|
+
font-family: inherit; resize: vertical; line-height: 1.5;
|
|
2494
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
2495
|
+
}
|
|
2496
|
+
textarea:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.15); }
|
|
2497
|
+
textarea::placeholder { color: #484f58; }
|
|
2154
2498
|
.tag-label {
|
|
2155
2499
|
font-size: 11px; color: #484f58; text-align: left; margin-bottom: 5px; display: block;
|
|
2156
2500
|
}
|
|
@@ -2189,7 +2533,7 @@ function renderSaveTestAsk(t) {
|
|
|
2189
2533
|
</div>
|
|
2190
2534
|
</div>`;
|
|
2191
2535
|
}
|
|
2192
|
-
function renderSaveTestDesc(description, tags, t) {
|
|
2536
|
+
function renderSaveTestDesc(description, notes, tags, t) {
|
|
2193
2537
|
const chipsHtml = tags.map(
|
|
2194
2538
|
(tag) => `<span class="chip">${escHtml(tag)}<button class="chip-del" data-tag="${escAttr(tag)}" title="${t("SAVE_TEST.REMOVE_TAG_TITLE")}">\u2715</button></span>`
|
|
2195
2539
|
).join("");
|
|
@@ -2198,6 +2542,8 @@ function renderSaveTestDesc(description, tags, t) {
|
|
|
2198
2542
|
<p>${t("SAVE_TEST.DESC_LABEL")} (<code>it()</code>):</p>
|
|
2199
2543
|
<input id="desc-input" type="text" placeholder="${t("SAVE_TEST.DESC_PLACEHOLDER")}"
|
|
2200
2544
|
value="${escAttr(description)}" autocomplete="off" />
|
|
2545
|
+
<span class="tag-label">${t("SAVE_TEST.NOTES_LABEL")}</span>
|
|
2546
|
+
<textarea id="notes-input" rows="3" placeholder="${escAttr(t("SAVE_TEST.NOTES_PLACEHOLDER"))}">${escHtml(notes)}</textarea>
|
|
2201
2547
|
<span class="tag-label">${t("SAVE_TEST.TAGS_LABEL")}</span>
|
|
2202
2548
|
<div class="tag-input-row">
|
|
2203
2549
|
<input id="tag-input" type="text" placeholder="${t("SAVE_TEST.TAGS_PLACEHOLDER")}" autocomplete="off" />
|
|
@@ -2217,6 +2563,7 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
2217
2563
|
shadow;
|
|
2218
2564
|
_step = "ask";
|
|
2219
2565
|
description = "";
|
|
2566
|
+
notes = "";
|
|
2220
2567
|
tags = [];
|
|
2221
2568
|
translation = translationService;
|
|
2222
2569
|
constructor() {
|
|
@@ -2234,17 +2581,18 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
2234
2581
|
this.render();
|
|
2235
2582
|
}
|
|
2236
2583
|
confirmSave() {
|
|
2237
|
-
this.dispatch("savetest", { description: this.description.trim(), tags: [...this.tags] });
|
|
2584
|
+
this.dispatch("savetest", { description: this.description.trim(), notes: this.notes, tags: [...this.tags] });
|
|
2238
2585
|
}
|
|
2239
2586
|
confirmSaveAndExport() {
|
|
2240
|
-
this.dispatch("saveandexport", { description: this.description.trim(), tags: [...this.tags] });
|
|
2587
|
+
this.dispatch("saveandexport", { description: this.description.trim(), notes: this.notes, tags: [...this.tags] });
|
|
2241
2588
|
}
|
|
2242
2589
|
cancel() {
|
|
2243
|
-
this.dispatch("savetest", { description: null, tags: [] });
|
|
2590
|
+
this.dispatch("savetest", { description: null, notes: "", tags: [] });
|
|
2244
2591
|
}
|
|
2245
2592
|
restartComponent() {
|
|
2246
2593
|
this._step = "ask";
|
|
2247
2594
|
this.description = "";
|
|
2595
|
+
this.notes = "";
|
|
2248
2596
|
this.tags = [];
|
|
2249
2597
|
this.render();
|
|
2250
2598
|
}
|
|
@@ -2271,12 +2619,16 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
2271
2619
|
this.shadow.getElementById("btn-yes")?.addEventListener("click", () => this.askSave());
|
|
2272
2620
|
this.shadow.getElementById("btn-no")?.addEventListener("click", () => this.cancel());
|
|
2273
2621
|
} else {
|
|
2274
|
-
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestDesc(this.description, this.tags, this.t.bind(this))}`;
|
|
2622
|
+
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestDesc(this.description, this.notes, this.tags, this.t.bind(this))}`;
|
|
2275
2623
|
const descInput = this.shadow.getElementById("desc-input");
|
|
2624
|
+
const notesInput = this.shadow.getElementById("notes-input");
|
|
2276
2625
|
const tagInput = this.shadow.getElementById("tag-input");
|
|
2277
2626
|
descInput.addEventListener("input", () => {
|
|
2278
2627
|
this.description = descInput.value;
|
|
2279
2628
|
});
|
|
2629
|
+
notesInput.addEventListener("input", () => {
|
|
2630
|
+
this.notes = notesInput.value;
|
|
2631
|
+
});
|
|
2280
2632
|
const tryAddTag = () => {
|
|
2281
2633
|
this.addTag(tagInput.value);
|
|
2282
2634
|
tagInput.value = "";
|
|
@@ -2382,18 +2734,127 @@ var TEST_EDITOR_STYLES = `
|
|
|
2382
2734
|
background: #0d1117; padding: 10px 14px;
|
|
2383
2735
|
border-top: 1px solid #21262d;
|
|
2384
2736
|
}
|
|
2385
|
-
.
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
.
|
|
2737
|
+
.test-notes {
|
|
2738
|
+
margin: 0 0 10px; padding: 8px 12px;
|
|
2739
|
+
background: rgba(47,129,247,0.06); border-left: 3px solid rgba(47,129,247,0.4);
|
|
2740
|
+
border-radius: 0 6px 6px 0; font-size: 12px; color: #8b949e;
|
|
2741
|
+
line-height: 1.6; white-space: pre-wrap; word-break: break-word;
|
|
2742
|
+
}
|
|
2743
|
+
.code-preview {
|
|
2744
|
+
margin: 0; padding: 10px 12px; background: #161b22;
|
|
2745
|
+
border-radius: 6px; border: 1px solid #21262d;
|
|
2746
|
+
font-size: 11px; color: #c9d1d9; line-height: 1.7;
|
|
2747
|
+
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
2748
|
+
overflow-x: auto; white-space: pre;
|
|
2749
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
2750
|
+
}
|
|
2751
|
+
.code-preview::-webkit-scrollbar { height: 4px; }
|
|
2752
|
+
.code-preview::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
|
|
2753
|
+
.code-preview-icp { margin-top: 8px; }
|
|
2754
|
+
.sh-kw { color: #ff7b72; }
|
|
2755
|
+
.sh-bi { color: #d2a8ff; }
|
|
2756
|
+
.sh-str { color: #a5d6ff; }
|
|
2757
|
+
.sh-cmt { color: #8b949e; font-style: italic; }
|
|
2758
|
+
.sh-num { color: #79c0ff; }
|
|
2392
2759
|
`;
|
|
2393
2760
|
|
|
2761
|
+
// src/utils/syntax-highlight.utils.ts
|
|
2762
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
2763
|
+
"describe",
|
|
2764
|
+
"it",
|
|
2765
|
+
"xit",
|
|
2766
|
+
"fit",
|
|
2767
|
+
"xdescribe",
|
|
2768
|
+
"fdescribe",
|
|
2769
|
+
"beforeEach",
|
|
2770
|
+
"afterEach",
|
|
2771
|
+
"before",
|
|
2772
|
+
"after",
|
|
2773
|
+
"context",
|
|
2774
|
+
"const",
|
|
2775
|
+
"let",
|
|
2776
|
+
"var",
|
|
2777
|
+
"function",
|
|
2778
|
+
"return",
|
|
2779
|
+
"if",
|
|
2780
|
+
"else",
|
|
2781
|
+
"import",
|
|
2782
|
+
"export",
|
|
2783
|
+
"from",
|
|
2784
|
+
"new",
|
|
2785
|
+
"this",
|
|
2786
|
+
"null",
|
|
2787
|
+
"undefined",
|
|
2788
|
+
"true",
|
|
2789
|
+
"false",
|
|
2790
|
+
"async",
|
|
2791
|
+
"await",
|
|
2792
|
+
"of",
|
|
2793
|
+
"in",
|
|
2794
|
+
"typeof",
|
|
2795
|
+
"void"
|
|
2796
|
+
]);
|
|
2797
|
+
var BUILTINS = /* @__PURE__ */ new Set(["cy", "expect", "assert", "Cypress", "chai"]);
|
|
2798
|
+
function syntaxHighlight(code) {
|
|
2799
|
+
return code.split("\n").map(highlightLine).join("\n");
|
|
2800
|
+
}
|
|
2801
|
+
function highlightLine(line) {
|
|
2802
|
+
let result = "";
|
|
2803
|
+
let i = 0;
|
|
2804
|
+
const len = line.length;
|
|
2805
|
+
while (i < len) {
|
|
2806
|
+
if (line[i] === "/" && i + 1 < len && line[i + 1] === "/") {
|
|
2807
|
+
result += `<span class="sh-cmt">${escHtml(line.slice(i))}</span>`;
|
|
2808
|
+
return result;
|
|
2809
|
+
}
|
|
2810
|
+
const q = line[i];
|
|
2811
|
+
if (q === "'" || q === '"' || q === "`") {
|
|
2812
|
+
let j = i + 1;
|
|
2813
|
+
while (j < len) {
|
|
2814
|
+
if (line[j] === "\\") {
|
|
2815
|
+
j += 2;
|
|
2816
|
+
continue;
|
|
2817
|
+
}
|
|
2818
|
+
if (line[j] === q) {
|
|
2819
|
+
j++;
|
|
2820
|
+
break;
|
|
2821
|
+
}
|
|
2822
|
+
j++;
|
|
2823
|
+
}
|
|
2824
|
+
result += `<span class="sh-str">${escHtml(line.slice(i, j))}</span>`;
|
|
2825
|
+
i = j;
|
|
2826
|
+
continue;
|
|
2827
|
+
}
|
|
2828
|
+
if (/[0-9]/.test(line[i]) && (i === 0 || !/[a-zA-Z_$]/.test(line[i - 1]))) {
|
|
2829
|
+
let j = i;
|
|
2830
|
+
while (j < len && /[0-9.]/.test(line[j])) j++;
|
|
2831
|
+
result += `<span class="sh-num">${escHtml(line.slice(i, j))}</span>`;
|
|
2832
|
+
i = j;
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
if (/[a-zA-Z_$]/.test(line[i])) {
|
|
2836
|
+
let j = i;
|
|
2837
|
+
while (j < len && /[a-zA-Z0-9_$]/.test(line[j])) j++;
|
|
2838
|
+
const word = line.slice(i, j);
|
|
2839
|
+
if (KEYWORDS.has(word)) {
|
|
2840
|
+
result += `<span class="sh-kw">${escHtml(word)}</span>`;
|
|
2841
|
+
} else if (BUILTINS.has(word)) {
|
|
2842
|
+
result += `<span class="sh-bi">${escHtml(word)}</span>`;
|
|
2843
|
+
} else {
|
|
2844
|
+
result += escHtml(word);
|
|
2845
|
+
}
|
|
2846
|
+
i = j;
|
|
2847
|
+
continue;
|
|
2848
|
+
}
|
|
2849
|
+
result += escHtml(line[i]);
|
|
2850
|
+
i++;
|
|
2851
|
+
}
|
|
2852
|
+
return result;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2394
2855
|
// src/components/test-editor/test-editor.template.ts
|
|
2395
2856
|
function renderTestEditor(state, t) {
|
|
2396
|
-
const { tags, visible, selectedVisible, activeTag, selectMode, selectedIds, describeName, expandedIndex, interceptorsByTest } = state;
|
|
2857
|
+
const { tags, visible, selectedVisible, activeTag, selectMode, selectedIds, describeName, expandedIndex, interceptorsByTest, locale } = state;
|
|
2397
2858
|
const tagFilterHtml = tags.length ? `<div class="tag-filter">
|
|
2398
2859
|
${tags.map((tag) => `<button class="tag-chip${activeTag === tag ? " active" : ""}" data-filter-tag="${escAttr(tag)}">${escHtml(tag)}</button>`).join("")}
|
|
2399
2860
|
</div>` : `<div class="tag-filter" style="color:#484f58;font-size:11px">${t("TEST_EDITOR.NO_TAGS")}</div>`;
|
|
@@ -2404,22 +2865,24 @@ function renderTestEditor(state, t) {
|
|
|
2404
2865
|
</div>` : "";
|
|
2405
2866
|
const rows = visible.map((test, i) => {
|
|
2406
2867
|
const expanded = expandedIndex === i;
|
|
2407
|
-
const date = new Date(test.createdAt).toLocaleString(
|
|
2868
|
+
const date = new Date(test.createdAt).toLocaleString(locale, { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
2408
2869
|
const icps = interceptorsByTest[test.id] ?? test.interceptors ?? [];
|
|
2409
2870
|
const tagsHtml = (test.tags ?? []).length ? `<span class="test-tags">${(test.tags ?? []).map((tag) => `<span class="test-tag">${escHtml(tag)}</span>`).join("")}</span>` : "";
|
|
2410
2871
|
const checkbox = selectMode ? `<input type="checkbox" ${selectedIds.has(test.id) ? "checked" : ""} data-select="${test.id}" />` : "";
|
|
2872
|
+
const hasIcps = (test.interceptors ?? []).length > 0;
|
|
2873
|
+
const itBlockCode = `it('${escapeSingleQuotes(test.name)}', () => {
|
|
2874
|
+
${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
2875
|
+
});`;
|
|
2876
|
+
const icpBlockCode = icps.length ? `beforeEach(() => {
|
|
2877
|
+
// Auto-generated Cypress interceptors
|
|
2878
|
+
${icps.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
2879
|
+
});` : "";
|
|
2880
|
+
const notesHtml = expanded && test.notes ? `<p class="test-notes">${escHtml(test.notes)}</p>` : "";
|
|
2411
2881
|
const body = expanded ? `
|
|
2412
2882
|
<div class="row-body">
|
|
2413
|
-
|
|
2414
|
-
<
|
|
2415
|
-
${
|
|
2416
|
-
<div class="section-title" style="margin-top:8px">${t("TEST_EDITOR.SECTION_INTERCEPTORS")}</div>
|
|
2417
|
-
${icps.map(escHtml).join("<br>")}
|
|
2418
|
-
</div>` : ""}
|
|
2419
|
-
<div class="copy-row">
|
|
2420
|
-
<button class="btn-icon" data-action="copy-cmds" data-idx="${i}">${t("TEST_EDITOR.COPY_CMDS_BTN")}</button>
|
|
2421
|
-
${icps.length ? `<button class="btn-icon" data-action="copy-icps" data-idx="${i}">${t("TEST_EDITOR.COPY_ICPS_BTN")}</button>` : ""}
|
|
2422
|
-
</div>
|
|
2883
|
+
${notesHtml}
|
|
2884
|
+
<pre class="code-preview">${syntaxHighlight(itBlockCode)}</pre>
|
|
2885
|
+
${icpBlockCode ? `<pre class="code-preview code-preview-icp">${syntaxHighlight(icpBlockCode)}</pre>` : ""}
|
|
2423
2886
|
</div>` : "";
|
|
2424
2887
|
return `
|
|
2425
2888
|
<div class="row${selectedIds.has(test.id) ? " selected-row" : ""}">
|
|
@@ -2428,6 +2891,8 @@ function renderTestEditor(state, t) {
|
|
|
2428
2891
|
<span class="test-name">${escHtml(test.name)}</span>
|
|
2429
2892
|
${tagsHtml}
|
|
2430
2893
|
<span class="test-date">${date}</span>
|
|
2894
|
+
<button class="btn-icon" data-action="copy-cmds" data-idx="${i}" title="${t("TEST_EDITOR.COPY_CMDS_BTN")}">${t("TEST_EDITOR.COPY_CMDS_BTN")}</button>
|
|
2895
|
+
${hasIcps ? `<button class="btn-icon" data-action="copy-icps" data-idx="${i}" title="${t("TEST_EDITOR.COPY_ICPS_BTN")}">${t("TEST_EDITOR.COPY_ICPS_BTN")}</button>` : ""}
|
|
2431
2896
|
<button class="btn-icon btn-del" data-action="delete" data-id="${test.id}" title="${t("TEST_EDITOR.DELETE_TITLE")}">\u{1F5D1}</button>
|
|
2432
2897
|
</div>
|
|
2433
2898
|
${body}
|
|
@@ -2513,11 +2978,11 @@ var TestEditorElement = class extends HTMLElement {
|
|
|
2513
2978
|
` : "";
|
|
2514
2979
|
const itBlocks = selected.map((t) => {
|
|
2515
2980
|
const cmds = (t.commands ?? []).map((c) => ` ${c}`).join("\n");
|
|
2516
|
-
return ` it('${(t.name ?? "")
|
|
2981
|
+
return ` it('${escapeSingleQuotes(t.name ?? "")}', () => {
|
|
2517
2982
|
${cmds}
|
|
2518
2983
|
});`;
|
|
2519
2984
|
}).join("\n\n");
|
|
2520
|
-
const block = `describe('${name
|
|
2985
|
+
const block = `describe('${escapeSingleQuotes(name)}', () => {
|
|
2521
2986
|
${beforeEach}${itBlocks}
|
|
2522
2987
|
});`;
|
|
2523
2988
|
navigator.clipboard?.writeText(block);
|
|
@@ -2551,7 +3016,8 @@ ${beforeEach}${itBlocks}
|
|
|
2551
3016
|
selectedIds: this.selectedIds,
|
|
2552
3017
|
describeName: this.describeName,
|
|
2553
3018
|
expandedIndex: this.expandedIndex,
|
|
2554
|
-
interceptorsByTest: this.interceptorsByTest
|
|
3019
|
+
interceptorsByTest: this.interceptorsByTest,
|
|
3020
|
+
locale: localeForLang(this.translation.getLang())
|
|
2555
3021
|
}, this.t.bind(this))}`;
|
|
2556
3022
|
this.shadow.getElementById("btn-select-mode")?.addEventListener("click", () => this.toggleSelectMode());
|
|
2557
3023
|
this.shadow.querySelectorAll("[data-filter-tag]").forEach((el) => {
|
|
@@ -2606,7 +3072,7 @@ ${beforeEach}${itBlocks}
|
|
|
2606
3072
|
ev.stopPropagation();
|
|
2607
3073
|
const idx = Number(el.dataset["idx"]);
|
|
2608
3074
|
const t = visible[idx];
|
|
2609
|
-
this.copyToClipboard((this.interceptorsByTest[t?.id] ?? []).join("\n"));
|
|
3075
|
+
this.copyToClipboard((this.interceptorsByTest[t?.id] ?? t?.interceptors ?? []).join("\n"));
|
|
2610
3076
|
});
|
|
2611
3077
|
});
|
|
2612
3078
|
}
|
|
@@ -2708,6 +3174,71 @@ var CONFIGURATION_STYLES = `
|
|
|
2708
3174
|
|
|
2709
3175
|
/* \u2500\u2500 Data section desc \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2710
3176
|
.data-desc { font-size: 11px; color: #484f58; margin-bottom: 10px; line-height: 1.5; }
|
|
3177
|
+
|
|
3178
|
+
/* \u2500\u2500 Export overlay (modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
3179
|
+
.export-overlay {
|
|
3180
|
+
position: fixed; inset: 0; z-index: 100000;
|
|
3181
|
+
background: rgba(1,4,9,0.7);
|
|
3182
|
+
display: flex; align-items: center; justify-content: center; padding: 20px;
|
|
3183
|
+
}
|
|
3184
|
+
.export-modal {
|
|
3185
|
+
width: 640px; max-width: 100%; max-height: 86vh;
|
|
3186
|
+
display: flex; flex-direction: column;
|
|
3187
|
+
background: #161b22; border: 1px solid #30363d; border-radius: 12px;
|
|
3188
|
+
box-shadow: 0 12px 40px rgba(0,0,0,0.5); overflow: hidden;
|
|
3189
|
+
}
|
|
3190
|
+
.export-hd {
|
|
3191
|
+
padding: 14px 18px; font-size: 13px; font-weight: 700;
|
|
3192
|
+
border-bottom: 1px solid #21262d; color: #e6edf3;
|
|
3193
|
+
}
|
|
3194
|
+
.export-modes { display: flex; gap: 6px; padding: 12px 18px 0; }
|
|
3195
|
+
.export-mode {
|
|
3196
|
+
flex: 1; padding: 7px 10px; font-size: 11px; border-radius: 6px;
|
|
3197
|
+
background: #0d1117; border: 1px solid #30363d; color: #8b949e;
|
|
3198
|
+
}
|
|
3199
|
+
.export-mode:hover { background: #21262d; color: #e6edf3; }
|
|
3200
|
+
.export-mode.active { background: rgba(47,129,247,0.15); border-color: #2f81f7; color: #2f81f7; }
|
|
3201
|
+
.export-body {
|
|
3202
|
+
padding: 14px 18px; overflow-y: auto; flex: 1; min-height: 160px;
|
|
3203
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
3204
|
+
}
|
|
3205
|
+
.export-tag-hint { font-size: 12px; color: #484f58; margin-top: 12px; }
|
|
3206
|
+
.export-result-label {
|
|
3207
|
+
font-size: 10px; color: #484f58; text-transform: uppercase; letter-spacing: 0.6px;
|
|
3208
|
+
font-weight: 600; margin: 14px 0 6px;
|
|
3209
|
+
}
|
|
3210
|
+
.export-row-static { cursor: default; }
|
|
3211
|
+
.export-row-static:hover { background: transparent; }
|
|
3212
|
+
.export-all-desc { font-size: 12px; color: #8b949e; }
|
|
3213
|
+
.export-empty { font-size: 12px; color: #484f58; text-align: center; padding: 20px; }
|
|
3214
|
+
.export-list { display: flex; flex-direction: column; gap: 4px; }
|
|
3215
|
+
.export-row {
|
|
3216
|
+
display: flex; align-items: center; gap: 8px;
|
|
3217
|
+
padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 12px; color: #c9d1d9;
|
|
3218
|
+
}
|
|
3219
|
+
.export-row:hover { background: #0d1117; }
|
|
3220
|
+
.export-row-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
3221
|
+
.export-row-tag {
|
|
3222
|
+
font-size: 9px; color: #8b949e; background: #0d1117;
|
|
3223
|
+
border: 1px solid #30363d; border-radius: 10px; padding: 1px 7px; flex-shrink: 0;
|
|
3224
|
+
}
|
|
3225
|
+
.export-tags { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
3226
|
+
.export-tag {
|
|
3227
|
+
padding: 4px 10px; font-size: 11px; border-radius: 12px;
|
|
3228
|
+
background: #0d1117; border: 1px solid #30363d; color: #8b949e;
|
|
3229
|
+
}
|
|
3230
|
+
.export-tag:hover { background: #21262d; color: #e6edf3; }
|
|
3231
|
+
.export-tag.active { background: rgba(47,129,247,0.15); border-color: #2f81f7; color: #2f81f7; }
|
|
3232
|
+
.export-ft {
|
|
3233
|
+
display: flex; align-items: center; justify-content: space-between; gap: 10px;
|
|
3234
|
+
padding: 12px 18px; border-top: 1px solid #21262d;
|
|
3235
|
+
}
|
|
3236
|
+
.export-count { font-size: 11px; color: #8b949e; }
|
|
3237
|
+
.export-count b { color: #e6edf3; }
|
|
3238
|
+
.export-ft-actions { display: flex; gap: 8px; }
|
|
3239
|
+
.btn-export-confirm { background: #238636; border-color: #238636; color: #fff; }
|
|
3240
|
+
.btn-export-confirm:hover:not(:disabled) { background: #2ea043; border-color: #2ea043; }
|
|
3241
|
+
.btn-export-confirm:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
2711
3242
|
`;
|
|
2712
3243
|
|
|
2713
3244
|
// src/components/configuration/configuration.template.ts
|
|
@@ -2718,8 +3249,64 @@ var LANGS = [
|
|
|
2718
3249
|
{ value: "it", label: "Italiano" },
|
|
2719
3250
|
{ value: "de", label: "Deutsch" }
|
|
2720
3251
|
];
|
|
3252
|
+
function renderExportOverlay(state, t) {
|
|
3253
|
+
const { isExporting, exportMode, exportTests, exportSelectedIds, exportSelectedTags } = state;
|
|
3254
|
+
if (!isExporting) return "";
|
|
3255
|
+
const count = selectTestsForExport(exportTests, exportMode, {
|
|
3256
|
+
ids: exportSelectedIds,
|
|
3257
|
+
tags: exportSelectedTags
|
|
3258
|
+
}).length;
|
|
3259
|
+
const modeBtn = (mode, key) => `<button class="export-mode ${exportMode === mode ? "active" : ""}" data-export-mode="${mode}">${t(key)}</button>`;
|
|
3260
|
+
let body;
|
|
3261
|
+
if (exportTests.length === 0) {
|
|
3262
|
+
body = `<div class="export-empty">${t("CONFIG.EXPORT_EMPTY")}</div>`;
|
|
3263
|
+
} else if (exportMode === "all") {
|
|
3264
|
+
body = `<div class="export-all-desc">${t("CONFIG.EXPORT_ALL_DESC")}</div>`;
|
|
3265
|
+
} else if (exportMode === "manual") {
|
|
3266
|
+
body = `<div class="export-list">${exportTests.map((test) => `
|
|
3267
|
+
<label class="export-row">
|
|
3268
|
+
<input type="checkbox" data-export-test="${test.id}" ${exportSelectedIds.has(test.id) ? "checked" : ""} />
|
|
3269
|
+
<span class="export-row-name">${escHtml(test.name)}</span>
|
|
3270
|
+
${(test.tags ?? []).map((tag) => `<span class="export-row-tag">${escHtml(tag)}</span>`).join("")}
|
|
3271
|
+
</label>`).join("")}</div>`;
|
|
3272
|
+
} else {
|
|
3273
|
+
const allTags = [...new Set(exportTests.flatMap((test) => test.tags ?? []))].sort();
|
|
3274
|
+
if (!allTags.length) {
|
|
3275
|
+
body = `<div class="export-empty">${t("CONFIG.EXPORT_NO_TAGS")}</div>`;
|
|
3276
|
+
} else {
|
|
3277
|
+
const chips = `<div class="export-tags">${allTags.map((tag) => `<button class="export-tag ${exportSelectedTags.has(tag) ? "active" : ""}" data-export-tag="${escAttr(tag)}">${escHtml(tag)}</button>`).join("")}</div>`;
|
|
3278
|
+
const matched = selectTestsForExport(exportTests, "tags", { tags: exportSelectedTags });
|
|
3279
|
+
const preview = exportSelectedTags.size === 0 ? `<div class="export-tag-hint">${t("CONFIG.EXPORT_TAGS_HINT")}</div>` : `<div class="export-result-label">${t("CONFIG.EXPORT_RESULT_LABEL")}</div>
|
|
3280
|
+
<div class="export-list">${matched.map((test) => `
|
|
3281
|
+
<div class="export-row export-row-static">
|
|
3282
|
+
<span class="export-row-name">${escHtml(test.name)}</span>
|
|
3283
|
+
${(test.tags ?? []).map((tag) => `<span class="export-row-tag">${escHtml(tag)}</span>`).join("")}
|
|
3284
|
+
</div>`).join("")}</div>`;
|
|
3285
|
+
body = chips + preview;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
return `
|
|
3289
|
+
<div class="export-overlay" id="export-overlay">
|
|
3290
|
+
<div class="export-modal">
|
|
3291
|
+
<div class="export-hd">${t("CONFIG.EXPORT_DIALOG_TITLE")}</div>
|
|
3292
|
+
${exportTests.length ? `<div class="export-modes">
|
|
3293
|
+
${modeBtn("all", "CONFIG.EXPORT_MODE_ALL")}
|
|
3294
|
+
${modeBtn("manual", "CONFIG.EXPORT_MODE_MANUAL")}
|
|
3295
|
+
${modeBtn("tags", "CONFIG.EXPORT_MODE_TAGS")}
|
|
3296
|
+
</div>` : ""}
|
|
3297
|
+
<div class="export-body">${body}</div>
|
|
3298
|
+
<div class="export-ft">
|
|
3299
|
+
<span class="export-count">${t("CONFIG.EXPORT_COUNT")} <b>${count}</b></span>
|
|
3300
|
+
<span class="export-ft-actions">
|
|
3301
|
+
<button id="btn-export-confirm" class="btn-export-confirm" ${count === 0 ? "disabled" : ""}>${t("CONFIG.EXPORT_CONFIRM")}</button>
|
|
3302
|
+
<button id="btn-export-cancel">${t("CONFIG.EXPORT_CANCEL")}</button>
|
|
3303
|
+
</span>
|
|
3304
|
+
</div>
|
|
3305
|
+
</div>
|
|
3306
|
+
</div>`;
|
|
3307
|
+
}
|
|
2721
3308
|
function renderConfiguration(state, t) {
|
|
2722
|
-
const { selectedLanguage, advancedHttpConfig, selectorStrategy, filesystemGranted, cypressFolderName, smartSelectorEnabled } = state;
|
|
3309
|
+
const { selectedLanguage, advancedHttpConfig, selectorStrategy, filesystemGranted, cypressFolderName, smartSelectorEnabled, startHidden, resumeTtlMinutes } = state;
|
|
2723
3310
|
const langOptions = LANGS.map(
|
|
2724
3311
|
(l) => `<option value="${l.value}" ${selectedLanguage === l.value ? "selected" : ""}>${l.label}</option>`
|
|
2725
3312
|
).join("");
|
|
@@ -2759,6 +3346,29 @@ function renderConfiguration(state, t) {
|
|
|
2759
3346
|
</label>
|
|
2760
3347
|
</div>
|
|
2761
3348
|
|
|
3349
|
+
<!-- Start Hidden -->
|
|
3350
|
+
<div class="card">
|
|
3351
|
+
<div class="card-hd">${t("CONFIG.START_HIDDEN_SECTION")}</div>
|
|
3352
|
+
<label class="check-row">
|
|
3353
|
+
<input type="checkbox" id="start-hidden-toggle" ${startHidden ? "checked" : ""} />
|
|
3354
|
+
<div>
|
|
3355
|
+
<div class="check-title">${t("CONFIG.START_HIDDEN_TITLE")}</div>
|
|
3356
|
+
<div class="check-sub">${t("CONFIG.START_HIDDEN_SUB")}</div>
|
|
3357
|
+
</div>
|
|
3358
|
+
</label>
|
|
3359
|
+
</div>
|
|
3360
|
+
|
|
3361
|
+
<!-- Recording continuity (cross-app resume TTL) -->
|
|
3362
|
+
<div class="card">
|
|
3363
|
+
<div class="card-hd">${t("CONFIG.RESUME_TTL_SECTION")}</div>
|
|
3364
|
+
<div class="field-row">
|
|
3365
|
+
<span class="field-label">${t("CONFIG.RESUME_TTL_LABEL")}</span>
|
|
3366
|
+
<input type="number" id="resume-ttl-input" min="1" step="1" value="${resumeTtlMinutes}"
|
|
3367
|
+
style="width:80px;padding:5px 8px;background:#161b22;border:1px solid #30363d;border-radius:5px;color:#c9d1d9;font-size:12px;outline:none" />
|
|
3368
|
+
</div>
|
|
3369
|
+
<div class="check-sub" style="margin-top:8px">${t("CONFIG.RESUME_TTL_HINT")}</div>
|
|
3370
|
+
</div>
|
|
3371
|
+
|
|
2762
3372
|
<!-- Selector Strategy -->
|
|
2763
3373
|
<div class="card card-wide">
|
|
2764
3374
|
<div class="card-hd">${t("CONFIG.SELECTOR_SECTION")}</div>
|
|
@@ -2778,13 +3388,13 @@ function renderConfiguration(state, t) {
|
|
|
2778
3388
|
<div class="card card-wide">
|
|
2779
3389
|
<div class="card-hd">${t("CONFIG.FOLDER_SECTION")}</div>
|
|
2780
3390
|
<div class="fs-layout">
|
|
2781
|
-
<pre class="fs-tree">cypress/ <span style="color:#484f58"
|
|
3391
|
+
<pre class="fs-tree">cypress/ <span style="color:#484f58">${t("RECORDER.FS_TREE_PICK_HINT")}</span>
|
|
2782
3392
|
\u2514\u2500\u2500 e2e/
|
|
2783
3393
|
\u2514\u2500\u2500 *.cy.ts</pre>
|
|
2784
3394
|
<div class="fs-right">
|
|
2785
3395
|
<div class="fs-status">
|
|
2786
3396
|
<span class="fs-dot ${filesystemGranted ? "on" : "off"}"></span>
|
|
2787
|
-
${filesystemGranted && cypressFolderName ? `<span>${t("CONFIG.FOLDER_CONNECTED")}</span> <span class="fs-folder">\u{1F4C1} ${cypressFolderName}</span>` : `<span>${t("CONFIG.FOLDER_NOT_SET")}</span>`}
|
|
3397
|
+
${filesystemGranted && cypressFolderName ? `<span>${t("CONFIG.FOLDER_CONNECTED")}</span> <span class="fs-folder">\u{1F4C1} ${escHtml(cypressFolderName)}</span>` : `<span>${t("CONFIG.FOLDER_NOT_SET")}</span>`}
|
|
2788
3398
|
</div>
|
|
2789
3399
|
<div class="btn-row">
|
|
2790
3400
|
<button id="btn-change-folder">
|
|
@@ -2809,7 +3419,7 @@ function renderConfiguration(state, t) {
|
|
|
2809
3419
|
</div>
|
|
2810
3420
|
</div>
|
|
2811
3421
|
|
|
2812
|
-
</div
|
|
3422
|
+
</div>${renderExportOverlay(state, t)}`;
|
|
2813
3423
|
}
|
|
2814
3424
|
|
|
2815
3425
|
// src/components/configuration/configuration.ts
|
|
@@ -2821,6 +3431,13 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2821
3431
|
advancedHttpConfig = false;
|
|
2822
3432
|
selectorStrategy = "data-cy";
|
|
2823
3433
|
smartSelectorEnabled = true;
|
|
3434
|
+
startHidden = false;
|
|
3435
|
+
resumeTtlMinutes = DEFAULT_RESUME_TTL_MINUTES;
|
|
3436
|
+
isExporting = false;
|
|
3437
|
+
exportMode = "all";
|
|
3438
|
+
exportTests = [];
|
|
3439
|
+
exportSelectedIds = /* @__PURE__ */ new Set();
|
|
3440
|
+
exportSelectedTags = /* @__PURE__ */ new Set();
|
|
2824
3441
|
filesystemGranted = false;
|
|
2825
3442
|
cypressFolderName = null;
|
|
2826
3443
|
constructor() {
|
|
@@ -2846,6 +3463,12 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2846
3463
|
this.advancedHttpConfig = localStorage.getItem("extendedHttpCommands") === "true";
|
|
2847
3464
|
this.selectorStrategy = config?.["selectorStrategy"] ?? "data-cy";
|
|
2848
3465
|
this.smartSelectorEnabled = config?.["smartSelectorEnabled"] !== "false";
|
|
3466
|
+
this.startHidden = config?.["startHidden"] === "true";
|
|
3467
|
+
const ttlRaw = config?.[RESUME_TTL_CONFIG_KEY];
|
|
3468
|
+
if (ttlRaw !== void 0 && ttlRaw !== null) {
|
|
3469
|
+
const ttl = Number(ttlRaw);
|
|
3470
|
+
this.resumeTtlMinutes = Number.isFinite(ttl) && ttl > 0 ? ttl : DEFAULT_RESUME_TTL_MINUTES;
|
|
3471
|
+
}
|
|
2849
3472
|
this.filesystemGranted = config?.["allowReadWriteFiles"] === "true";
|
|
2850
3473
|
const handle = config?.["cypressDirectoryHandle"];
|
|
2851
3474
|
this.cypressFolderName = handle?.name ?? null;
|
|
@@ -2863,6 +3486,18 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2863
3486
|
this.persistence.setConfig({ extendedHttpCommands: checked ? "true" : "false" });
|
|
2864
3487
|
this.render();
|
|
2865
3488
|
}
|
|
3489
|
+
async onStartHiddenChange(checked) {
|
|
3490
|
+
this.startHidden = checked;
|
|
3491
|
+
await this.persistence.setConfig({ startHidden: checked ? "true" : "false" });
|
|
3492
|
+
this.dispatchEvent(new CustomEvent("starthiddenchange", { detail: checked, bubbles: true, composed: true }));
|
|
3493
|
+
this.render();
|
|
3494
|
+
}
|
|
3495
|
+
async onResumeTtlChange(minutes) {
|
|
3496
|
+
const safe = Number.isFinite(minutes) && minutes > 0 ? Math.round(minutes) : DEFAULT_RESUME_TTL_MINUTES;
|
|
3497
|
+
this.resumeTtlMinutes = safe;
|
|
3498
|
+
await this.persistence.setConfig({ [RESUME_TTL_CONFIG_KEY]: safe });
|
|
3499
|
+
this.render();
|
|
3500
|
+
}
|
|
2866
3501
|
async onSmartSelectorChange(enabled) {
|
|
2867
3502
|
this.smartSelectorEnabled = enabled;
|
|
2868
3503
|
await this.persistence.setConfig({ smartSelectorEnabled: enabled ? "true" : "false" });
|
|
@@ -2892,8 +3527,7 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2892
3527
|
this.cypressFolderName = null;
|
|
2893
3528
|
this.render();
|
|
2894
3529
|
}
|
|
2895
|
-
|
|
2896
|
-
const tests = await this.persistence.getAllTests();
|
|
3530
|
+
downloadTests(tests) {
|
|
2897
3531
|
const blob = new Blob([JSON.stringify({ tests, interceptors: [] }, null, 2)], { type: "application/json" });
|
|
2898
3532
|
const url = URL.createObjectURL(blob);
|
|
2899
3533
|
const a = document.createElement("a");
|
|
@@ -2902,6 +3536,53 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2902
3536
|
a.click();
|
|
2903
3537
|
URL.revokeObjectURL(url);
|
|
2904
3538
|
}
|
|
3539
|
+
/** Downloads every saved test (used by the "Todo" mode and as a direct API). */
|
|
3540
|
+
async exportAllData() {
|
|
3541
|
+
const tests = await this.persistence.getAllTests();
|
|
3542
|
+
this.downloadTests(tests);
|
|
3543
|
+
}
|
|
3544
|
+
/** Opens the export selection dialog, loading the current tests. */
|
|
3545
|
+
async openExportDialog() {
|
|
3546
|
+
this.exportTests = await this.persistence.getAllTests();
|
|
3547
|
+
this.exportMode = "all";
|
|
3548
|
+
this.exportSelectedIds = /* @__PURE__ */ new Set();
|
|
3549
|
+
this.exportSelectedTags = /* @__PURE__ */ new Set();
|
|
3550
|
+
this.isExporting = true;
|
|
3551
|
+
this.render();
|
|
3552
|
+
}
|
|
3553
|
+
cancelExport() {
|
|
3554
|
+
this.isExporting = false;
|
|
3555
|
+
this.exportSelectedIds.clear();
|
|
3556
|
+
this.exportSelectedTags.clear();
|
|
3557
|
+
this.render();
|
|
3558
|
+
}
|
|
3559
|
+
setExportMode(mode) {
|
|
3560
|
+
this.exportMode = mode;
|
|
3561
|
+
this.render();
|
|
3562
|
+
}
|
|
3563
|
+
toggleExportTest(id) {
|
|
3564
|
+
if (this.exportSelectedIds.has(id)) this.exportSelectedIds.delete(id);
|
|
3565
|
+
else this.exportSelectedIds.add(id);
|
|
3566
|
+
this.render();
|
|
3567
|
+
}
|
|
3568
|
+
toggleExportTag(tag) {
|
|
3569
|
+
if (this.exportSelectedTags.has(tag)) this.exportSelectedTags.delete(tag);
|
|
3570
|
+
else this.exportSelectedTags.add(tag);
|
|
3571
|
+
this.render();
|
|
3572
|
+
}
|
|
3573
|
+
/** Downloads the tests resolved by the current mode + selection. No-op if empty. */
|
|
3574
|
+
confirmExport() {
|
|
3575
|
+
const subset = selectTestsForExport(this.exportTests, this.exportMode, {
|
|
3576
|
+
ids: this.exportSelectedIds,
|
|
3577
|
+
tags: this.exportSelectedTags
|
|
3578
|
+
});
|
|
3579
|
+
if (!subset.length) return;
|
|
3580
|
+
this.downloadTests(subset);
|
|
3581
|
+
this.isExporting = false;
|
|
3582
|
+
this.exportSelectedIds.clear();
|
|
3583
|
+
this.exportSelectedTags.clear();
|
|
3584
|
+
this.render();
|
|
3585
|
+
}
|
|
2905
3586
|
async importAllData(file) {
|
|
2906
3587
|
const text = await file.text();
|
|
2907
3588
|
let data;
|
|
@@ -2913,7 +3594,6 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2913
3594
|
if (!data || !Array.isArray(data.tests) || !Array.isArray(data.interceptors)) {
|
|
2914
3595
|
throw new Error(this.t("CONFIG.JSON_BAD_FORMAT"));
|
|
2915
3596
|
}
|
|
2916
|
-
await this.persistence.clearAllData();
|
|
2917
3597
|
await this.persistence.ingestFileData(data.tests, data.interceptors);
|
|
2918
3598
|
}
|
|
2919
3599
|
render() {
|
|
@@ -2923,7 +3603,14 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2923
3603
|
selectorStrategy: this.selectorStrategy,
|
|
2924
3604
|
filesystemGranted: this.filesystemGranted,
|
|
2925
3605
|
cypressFolderName: this.cypressFolderName,
|
|
2926
|
-
smartSelectorEnabled: this.smartSelectorEnabled
|
|
3606
|
+
smartSelectorEnabled: this.smartSelectorEnabled,
|
|
3607
|
+
startHidden: this.startHidden,
|
|
3608
|
+
resumeTtlMinutes: this.resumeTtlMinutes,
|
|
3609
|
+
isExporting: this.isExporting,
|
|
3610
|
+
exportMode: this.exportMode,
|
|
3611
|
+
exportTests: this.exportTests,
|
|
3612
|
+
exportSelectedIds: this.exportSelectedIds,
|
|
3613
|
+
exportSelectedTags: this.exportSelectedTags
|
|
2927
3614
|
}, this.t.bind(this))}`;
|
|
2928
3615
|
;
|
|
2929
3616
|
this.shadow.getElementById("lang-select").addEventListener(
|
|
@@ -2938,13 +3625,32 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
2938
3625
|
"change",
|
|
2939
3626
|
(e) => this.onSmartSelectorChange(e.target.checked)
|
|
2940
3627
|
);
|
|
3628
|
+
this.shadow.getElementById("start-hidden-toggle").addEventListener(
|
|
3629
|
+
"change",
|
|
3630
|
+
(e) => this.onStartHiddenChange(e.target.checked)
|
|
3631
|
+
);
|
|
3632
|
+
this.shadow.getElementById("resume-ttl-input").addEventListener(
|
|
3633
|
+
"change",
|
|
3634
|
+
(e) => this.onResumeTtlChange(Number(e.target.value))
|
|
3635
|
+
);
|
|
2941
3636
|
this.shadow.getElementById("selector-strategy").addEventListener(
|
|
2942
3637
|
"change",
|
|
2943
3638
|
(e) => this.onSelectorStrategyChange(e.target.value)
|
|
2944
3639
|
);
|
|
2945
3640
|
this.shadow.getElementById("btn-change-folder")?.addEventListener("click", () => this.changeFolder());
|
|
2946
3641
|
this.shadow.getElementById("btn-revoke")?.addEventListener("click", () => this.revokeAccess());
|
|
2947
|
-
this.shadow.getElementById("btn-export")?.addEventListener("click", () => this.
|
|
3642
|
+
this.shadow.getElementById("btn-export")?.addEventListener("click", () => this.openExportDialog());
|
|
3643
|
+
this.shadow.getElementById("btn-export-confirm")?.addEventListener("click", () => this.confirmExport());
|
|
3644
|
+
this.shadow.getElementById("btn-export-cancel")?.addEventListener("click", () => this.cancelExport());
|
|
3645
|
+
this.shadow.querySelectorAll("[data-export-mode]").forEach(
|
|
3646
|
+
(el) => el.addEventListener("click", () => this.setExportMode(el.dataset["exportMode"]))
|
|
3647
|
+
);
|
|
3648
|
+
this.shadow.querySelectorAll("[data-export-test]").forEach(
|
|
3649
|
+
(el) => el.addEventListener("change", () => this.toggleExportTest(Number(el.dataset["exportTest"])))
|
|
3650
|
+
);
|
|
3651
|
+
this.shadow.querySelectorAll("[data-export-tag]").forEach(
|
|
3652
|
+
(el) => el.addEventListener("click", () => this.toggleExportTag(el.dataset["exportTag"] ?? ""))
|
|
3653
|
+
);
|
|
2948
3654
|
this.shadow.getElementById("file-input").addEventListener("change", async (e) => {
|
|
2949
3655
|
const file = e.target.files?.[0];
|
|
2950
3656
|
if (!file) return;
|
|
@@ -3010,12 +3716,21 @@ var ADVANCED_TEST_EDITOR_STYLES = `
|
|
|
3010
3716
|
.file-name { font-size: 11px; color: #484f58; margin-bottom: 8px; font-family: monospace; }
|
|
3011
3717
|
pre {
|
|
3012
3718
|
background: #0d1117; padding: 12px; border-radius: 8px;
|
|
3013
|
-
font-size: 11px; color: #c9d1d9;
|
|
3014
|
-
white-space: pre-wrap; word-break: break-all; margin: 0;
|
|
3719
|
+
font-size: 11px; color: #c9d1d9; margin: 0;
|
|
3015
3720
|
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
|
|
3016
|
-
line-height: 1.7;
|
|
3017
|
-
|
|
3721
|
+
line-height: 1.7; border: 1px solid #21262d;
|
|
3722
|
+
white-space: pre; overflow-x: auto; overflow-y: auto;
|
|
3723
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
3018
3724
|
}
|
|
3725
|
+
pre::-webkit-scrollbar { width: 4px; height: 4px; }
|
|
3726
|
+
pre::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
|
|
3727
|
+
.pre-block { font-size: 10.5px; }
|
|
3728
|
+
.pre-icp .sh-str { color: #85e89d; }
|
|
3729
|
+
.sh-kw { color: #ff7b72; }
|
|
3730
|
+
.sh-bi { color: #d2a8ff; }
|
|
3731
|
+
.sh-str { color: #a5d6ff; }
|
|
3732
|
+
.sh-cmt { color: #8b949e; font-style: italic; }
|
|
3733
|
+
.sh-num { color: #79c0ff; }
|
|
3019
3734
|
.footer {
|
|
3020
3735
|
padding: 10px 14px; border-top: 1px solid #21262d;
|
|
3021
3736
|
display: flex; gap: 8px; justify-content: flex-end; background: #161b22;
|
|
@@ -3031,6 +3746,12 @@ var ADVANCED_TEST_EDITOR_STYLES = `
|
|
|
3031
3746
|
.btn-save:hover { background: #1f6feb; border-color: #1f6feb; color: #fff; }
|
|
3032
3747
|
.btn-save:disabled { background: #21262d; border-color: #30363d; color: #8b949e; }
|
|
3033
3748
|
.placeholder { color: #484f58; font-size: 13px; padding: 28px; text-align: center; }
|
|
3749
|
+
.test-notes {
|
|
3750
|
+
margin: 0 0 10px; padding: 8px 12px;
|
|
3751
|
+
background: rgba(47,129,247,0.06); border-left: 3px solid rgba(47,129,247,0.4);
|
|
3752
|
+
border-radius: 0 6px 6px 0; font-size: 12px; color: #8b949e;
|
|
3753
|
+
line-height: 1.6; white-space: pre-wrap; word-break: break-word;
|
|
3754
|
+
}
|
|
3034
3755
|
.block-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
|
|
3035
3756
|
.btn-copy {
|
|
3036
3757
|
padding: 3px 10px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer;
|
|
@@ -3039,18 +3760,21 @@ var ADVANCED_TEST_EDITOR_STYLES = `
|
|
|
3039
3760
|
}
|
|
3040
3761
|
.btn-copy:hover { background: #30363d; color: #e6edf3; }
|
|
3041
3762
|
.sidebar-toolbar {
|
|
3042
|
-
display: flex; gap: 4px; padding: 6px 6px 4px;
|
|
3763
|
+
display: flex; flex-wrap: wrap; gap: 4px; padding: 6px 6px 4px;
|
|
3043
3764
|
border-bottom: 1px solid #21262d; flex-shrink: 0; background: #0d1117;
|
|
3044
3765
|
}
|
|
3045
3766
|
.btn-toolbar {
|
|
3046
|
-
flex: 1; padding: 4px 6px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer;
|
|
3767
|
+
flex: 1 1 calc(50% - 2px); padding: 4px 6px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer;
|
|
3047
3768
|
font-size: 10px; font-weight: 500; background: #161b22; color: #8b949e;
|
|
3048
3769
|
transition: background 0.12s, color 0.12s; white-space: nowrap; overflow: hidden;
|
|
3049
3770
|
text-overflow: ellipsis;
|
|
3050
3771
|
}
|
|
3772
|
+
.btn-toolbar.btn-full { flex: 1 1 100%; }
|
|
3051
3773
|
.btn-toolbar:hover { background: #30363d; color: #e6edf3; }
|
|
3052
3774
|
.btn-toolbar.btn-new { color: #3fb950; border-color: #238636; }
|
|
3053
3775
|
.btn-toolbar.btn-new:hover { background: #238636; color: #fff; }
|
|
3776
|
+
.btn-toolbar.btn-new-folder { color: #d29922; border-color: #9e6a03; }
|
|
3777
|
+
.btn-toolbar.btn-new-folder:hover { background: #9e6a03; color: #fff; }
|
|
3054
3778
|
.new-file-form {
|
|
3055
3779
|
padding: 6px; border-bottom: 1px solid #21262d; display: flex; flex-direction: column; gap: 4px;
|
|
3056
3780
|
}
|
|
@@ -3083,23 +3807,24 @@ function renderNoPermission(needsReauth, t) {
|
|
|
3083
3807
|
</div>`;
|
|
3084
3808
|
}
|
|
3085
3809
|
function renderAdvancedEditor(state, t) {
|
|
3086
|
-
const { e2eTree, selectedFile, selectedFileContent, testItBlock, interceptorsBlock, saveButtonEnabled, isCreatingFile, collapsedDirs, sidebarWidth } = state;
|
|
3810
|
+
const { e2eTree, selectedFile, selectedFileContent, testItBlock, interceptorsBlock, testNotes, saveButtonEnabled, isCreatingFile, isCreatingFolder, collapsedDirs, sidebarWidth } = state;
|
|
3087
3811
|
const treeHtml = e2eTree.length ? renderTree(e2eTree, selectedFile, collapsedDirs) : `<div class="tree-item" style="color:#6c7a99">${t("ADVANCED_EDITOR.NO_FILES")}</div>`;
|
|
3088
3812
|
const contentHtml = selectedFileContent ? `<div class="file-name">\u{1F4C4} ${escHtml(selectedFile?.name ?? "")}</div>
|
|
3089
|
-
<pre>${
|
|
3813
|
+
<pre class="pre-file">${syntaxHighlight(selectedFileContent.slice(0, 6e3))}${selectedFileContent.length > 6e3 ? "\n..." : ""}</pre>` : `<div class="placeholder">${t("ADVANCED_EDITOR.SELECT_FILE")}</div>`;
|
|
3814
|
+
const notesHtml = testItBlock && testNotes ? `<p class="test-notes">${escHtml(testNotes)}</p>` : "";
|
|
3090
3815
|
const itHtml = testItBlock ? `<div style="margin-top:10px">
|
|
3091
3816
|
<div class="file-name block-header">
|
|
3092
3817
|
<span>${t("ADVANCED_EDITOR.IT_LABEL")}</span>
|
|
3093
3818
|
<button id="btn-copy-it" class="btn-copy">${t("ADVANCED_EDITOR.COPY_IT_BTN")}</button>
|
|
3094
3819
|
</div>
|
|
3095
|
-
<pre
|
|
3820
|
+
<pre class="pre-block">${syntaxHighlight(testItBlock.slice(0, 3e3))}</pre>
|
|
3096
3821
|
</div>` : "";
|
|
3097
3822
|
const interceptorsHtml = interceptorsBlock ? `<div style="margin-top:10px">
|
|
3098
3823
|
<div class="file-name block-header">
|
|
3099
3824
|
<span>${t("ADVANCED_EDITOR.BEFORE_EACH_LABEL")}</span>
|
|
3100
3825
|
<button id="btn-copy-interceptors" class="btn-copy">${t("ADVANCED_EDITOR.COPY_ICP_BTN")}</button>
|
|
3101
3826
|
</div>
|
|
3102
|
-
<pre
|
|
3827
|
+
<pre class="pre-block pre-icp">${syntaxHighlight(interceptorsBlock.slice(0, 3e3))}</pre>
|
|
3103
3828
|
</div>` : "";
|
|
3104
3829
|
const newFileForm = isCreatingFile ? `<div class="new-file-form">
|
|
3105
3830
|
<input id="input-new-file" type="text" placeholder="${escHtml(t("ADVANCED_EDITOR.NEW_FILE_PLACEHOLDER"))}" autocomplete="off" />
|
|
@@ -3109,21 +3834,30 @@ function renderAdvancedEditor(state, t) {
|
|
|
3109
3834
|
<button id="btn-new-file-cancel" class="btn-cancel-form">${t("ADVANCED_EDITOR.NEW_FILE_CANCEL")}</button>
|
|
3110
3835
|
</div>
|
|
3111
3836
|
</div>` : "";
|
|
3837
|
+
const newFolderForm = isCreatingFolder ? `<div class="new-file-form">
|
|
3838
|
+
<input id="input-new-folder" type="text" placeholder="${escHtml(t("ADVANCED_EDITOR.NEW_FOLDER_PLACEHOLDER"))}" autocomplete="off" />
|
|
3839
|
+
<div class="new-file-actions">
|
|
3840
|
+
<button id="btn-new-folder-confirm" class="btn-confirm">${t("ADVANCED_EDITOR.NEW_FILE_CONFIRM")}</button>
|
|
3841
|
+
<button id="btn-new-folder-cancel" class="btn-cancel-form">${t("ADVANCED_EDITOR.NEW_FILE_CANCEL")}</button>
|
|
3842
|
+
</div>
|
|
3843
|
+
</div>` : "";
|
|
3112
3844
|
const toolbar = `
|
|
3113
3845
|
<div class="sidebar-toolbar">
|
|
3114
3846
|
<button id="btn-new-file" class="btn-toolbar btn-new">${t("ADVANCED_EDITOR.NEW_FILE_BTN")}</button>
|
|
3115
|
-
<button id="btn-
|
|
3847
|
+
<button id="btn-new-folder" class="btn-toolbar btn-new-folder">${t("ADVANCED_EDITOR.NEW_FOLDER_BTN")}</button>
|
|
3848
|
+
<button id="btn-refresh" class="btn-toolbar btn-full">${t("ADVANCED_EDITOR.REFRESH_BTN")}</button>
|
|
3116
3849
|
</div>`;
|
|
3117
3850
|
return `
|
|
3118
3851
|
<div class="layout">
|
|
3119
3852
|
<div class="sidebar" style="width:${sidebarWidth}px">
|
|
3120
3853
|
${toolbar}
|
|
3121
3854
|
${newFileForm}
|
|
3855
|
+
${newFolderForm}
|
|
3122
3856
|
<div class="tree-scroll">${treeHtml}</div>
|
|
3123
3857
|
</div>
|
|
3124
3858
|
<div id="resize-handle" class="resize-handle"></div>
|
|
3125
3859
|
<div class="main">
|
|
3126
|
-
<div class="content-area">${contentHtml}${itHtml}${interceptorsHtml}</div>
|
|
3860
|
+
<div class="content-area">${contentHtml}${notesHtml}${itHtml}${interceptorsHtml}</div>
|
|
3127
3861
|
<div class="footer">
|
|
3128
3862
|
<button id="btn-save" class="btn-save"
|
|
3129
3863
|
${!saveButtonEnabled || !testItBlock ? "disabled" : ""}>
|
|
@@ -3184,10 +3918,12 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3184
3918
|
selectedFileContent = null;
|
|
3185
3919
|
testItBlock = "";
|
|
3186
3920
|
interceptorsBlock = "";
|
|
3921
|
+
testNotes = "";
|
|
3187
3922
|
isPreviewMode = false;
|
|
3188
3923
|
previewFileName = null;
|
|
3189
3924
|
previewFileContent = null;
|
|
3190
3925
|
isCreatingFile = false;
|
|
3926
|
+
isCreatingFolder = false;
|
|
3191
3927
|
collapsedDirs = /* @__PURE__ */ new Set();
|
|
3192
3928
|
sidebarWidth = 220;
|
|
3193
3929
|
hasPermission = false;
|
|
@@ -3250,7 +3986,7 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3250
3986
|
const name = rawName.trim().replace(/\.cy\.ts$/, "");
|
|
3251
3987
|
if (!name) return;
|
|
3252
3988
|
const fileName = `${name}.cy.ts`;
|
|
3253
|
-
const template = `describe('${name}', () => {
|
|
3989
|
+
const template = `describe('${escapeSingleQuotes(name)}', () => {
|
|
3254
3990
|
|
|
3255
3991
|
it('should ', () => {
|
|
3256
3992
|
|
|
@@ -3268,6 +4004,17 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3268
4004
|
this.isCreatingFile = false;
|
|
3269
4005
|
await this.getFoldersData();
|
|
3270
4006
|
}
|
|
4007
|
+
async createNewFolder(rawName) {
|
|
4008
|
+
if (!this._e2eHandle) return;
|
|
4009
|
+
const name = rawName.trim().replace(/[/\\]/g, "");
|
|
4010
|
+
if (!name) return;
|
|
4011
|
+
try {
|
|
4012
|
+
await this._e2eHandle.getDirectoryHandle(name, { create: true });
|
|
4013
|
+
} catch {
|
|
4014
|
+
}
|
|
4015
|
+
this.isCreatingFolder = false;
|
|
4016
|
+
await this.getFoldersData();
|
|
4017
|
+
}
|
|
3271
4018
|
async refreshTree() {
|
|
3272
4019
|
await this.getFoldersData();
|
|
3273
4020
|
}
|
|
@@ -3277,7 +4024,8 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3277
4024
|
if (this.interceptorsBlock) {
|
|
3278
4025
|
content = this.transformationService.insertBeforeEach(content, this.interceptorsBlock);
|
|
3279
4026
|
}
|
|
3280
|
-
|
|
4027
|
+
const comment = this.testNotes ? this.transformationService.buildBlockComment(this.testNotes) + "\n" : "";
|
|
4028
|
+
content = this.transformationService.insertItBlock(content, comment + this.testItBlock);
|
|
3281
4029
|
if (!content) return;
|
|
3282
4030
|
const writable = await this.selectedFileHandle.createWritable();
|
|
3283
4031
|
await writable.write(content);
|
|
@@ -3304,9 +4052,11 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3304
4052
|
if (test) {
|
|
3305
4053
|
this.testItBlock = test.itBlock ?? "";
|
|
3306
4054
|
this.interceptorsBlock = test.interceptorsBlock ?? "";
|
|
4055
|
+
this.testNotes = test.notes ?? "";
|
|
3307
4056
|
} else {
|
|
3308
4057
|
this.testItBlock = "";
|
|
3309
4058
|
this.interceptorsBlock = "";
|
|
4059
|
+
this.testNotes = "";
|
|
3310
4060
|
}
|
|
3311
4061
|
this.render();
|
|
3312
4062
|
}
|
|
@@ -3322,7 +4072,8 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3322
4072
|
fileName: this.selectedFile?.name ?? "",
|
|
3323
4073
|
testId: this.testId,
|
|
3324
4074
|
itBlock: this.testItBlock,
|
|
3325
|
-
interceptorsBlock: this.interceptorsBlock
|
|
4075
|
+
interceptorsBlock: this.interceptorsBlock,
|
|
4076
|
+
notes: this.testNotes
|
|
3326
4077
|
},
|
|
3327
4078
|
bubbles: true,
|
|
3328
4079
|
composed: true
|
|
@@ -3396,8 +4147,10 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3396
4147
|
selectedFileContent: this.selectedFileContent,
|
|
3397
4148
|
testItBlock: this.testItBlock,
|
|
3398
4149
|
interceptorsBlock: this.interceptorsBlock,
|
|
4150
|
+
testNotes: this.testNotes,
|
|
3399
4151
|
saveButtonEnabled: this.saveButtonEnabled,
|
|
3400
4152
|
isCreatingFile: this.isCreatingFile,
|
|
4153
|
+
isCreatingFolder: this.isCreatingFolder,
|
|
3401
4154
|
collapsedDirs: this.collapsedDirs,
|
|
3402
4155
|
sidebarWidth: this.sidebarWidth
|
|
3403
4156
|
}, this.t.bind(this))}`;
|
|
@@ -3421,9 +4174,16 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3421
4174
|
this.shadow.getElementById("btn-close")?.addEventListener("click", () => this.closePreview());
|
|
3422
4175
|
this.shadow.getElementById("btn-new-file")?.addEventListener("click", () => {
|
|
3423
4176
|
this.isCreatingFile = !this.isCreatingFile;
|
|
4177
|
+
this.isCreatingFolder = false;
|
|
3424
4178
|
this.render();
|
|
3425
4179
|
if (this.isCreatingFile) this.shadow.getElementById("input-new-file")?.focus();
|
|
3426
4180
|
});
|
|
4181
|
+
this.shadow.getElementById("btn-new-folder")?.addEventListener("click", () => {
|
|
4182
|
+
this.isCreatingFolder = !this.isCreatingFolder;
|
|
4183
|
+
this.isCreatingFile = false;
|
|
4184
|
+
this.render();
|
|
4185
|
+
if (this.isCreatingFolder) this.shadow.getElementById("input-new-folder")?.focus();
|
|
4186
|
+
});
|
|
3427
4187
|
this.shadow.getElementById("btn-refresh")?.addEventListener("click", () => this.refreshTree());
|
|
3428
4188
|
this.shadow.getElementById("btn-new-file-confirm")?.addEventListener("click", () => {
|
|
3429
4189
|
const input2 = this.shadow.getElementById("input-new-file");
|
|
@@ -3442,6 +4202,23 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
3442
4202
|
this.render();
|
|
3443
4203
|
}
|
|
3444
4204
|
});
|
|
4205
|
+
this.shadow.getElementById("btn-new-folder-confirm")?.addEventListener("click", () => {
|
|
4206
|
+
const folderInput2 = this.shadow.getElementById("input-new-folder");
|
|
4207
|
+
this.createNewFolder(folderInput2?.value ?? "");
|
|
4208
|
+
});
|
|
4209
|
+
this.shadow.getElementById("btn-new-folder-cancel")?.addEventListener("click", () => {
|
|
4210
|
+
this.isCreatingFolder = false;
|
|
4211
|
+
this.render();
|
|
4212
|
+
});
|
|
4213
|
+
const folderInput = this.shadow.getElementById("input-new-folder");
|
|
4214
|
+
folderInput?.addEventListener("keydown", (e) => {
|
|
4215
|
+
if (e.key === "Enter") {
|
|
4216
|
+
this.createNewFolder(folderInput.value);
|
|
4217
|
+
} else if (e.key === "Escape") {
|
|
4218
|
+
this.isCreatingFolder = false;
|
|
4219
|
+
this.render();
|
|
4220
|
+
}
|
|
4221
|
+
});
|
|
3445
4222
|
}
|
|
3446
4223
|
};
|
|
3447
4224
|
if (!customElements.get("advanced-test-editor")) {
|
|
@@ -3527,7 +4304,30 @@ var FILE_PREVIEW_STYLES = `
|
|
|
3527
4304
|
.btn-save:hover { background: #2ea043; border-color: #2ea043; }
|
|
3528
4305
|
.btn-launch { background: transparent; border-color: #e3b341; color: #e3b341; }
|
|
3529
4306
|
.btn-launch:hover { background: rgba(227,179,65,0.1); }
|
|
4307
|
+
.btn-launch:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
4308
|
+
.btn-launch:disabled:hover { background: transparent; }
|
|
4309
|
+
.launch-hint { font-size: 10px; color: #8b6d3b; align-self: center; }
|
|
4310
|
+
.btn-insert { background: transparent; border-color: #a371f7; color: #a371f7; }
|
|
4311
|
+
.btn-insert:hover { background: rgba(163,113,247,0.12); color: #c8a8ff; }
|
|
3530
4312
|
.btn-diff-active { background: rgba(47,129,247,0.15); border-color: rgba(47,129,247,0.4); color: #2f81f7; }
|
|
4313
|
+
|
|
4314
|
+
/* \u2500\u2500 Run result panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
4315
|
+
.run-result {
|
|
4316
|
+
border-top: 1px solid #21262d; padding: 8px 12px; background: #0d1117;
|
|
4317
|
+
max-height: 220px; overflow-y: auto; flex-shrink: 0;
|
|
4318
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
4319
|
+
}
|
|
4320
|
+
.run-status { font-size: 12px; font-weight: 600; }
|
|
4321
|
+
.run-passed .run-status { color: #3fb950; }
|
|
4322
|
+
.run-failed .run-status { color: #f85149; }
|
|
4323
|
+
.run-running .run-status { color: #e3b341; }
|
|
4324
|
+
.run-error .run-status { color: #f85149; }
|
|
4325
|
+
.run-output {
|
|
4326
|
+
margin: 6px 0 0; padding: 8px 10px; background: #161b22; border: 1px solid #21262d;
|
|
4327
|
+
border-radius: 5px; font-size: 10px; color: #c9d1d9; line-height: 1.5;
|
|
4328
|
+
font-family: 'Cascadia Code','Fira Code','Consolas',monospace;
|
|
4329
|
+
white-space: pre-wrap; word-break: break-word; max-height: 160px; overflow-y: auto;
|
|
4330
|
+
}
|
|
3531
4331
|
`;
|
|
3532
4332
|
|
|
3533
4333
|
// src/components/file-preview/file-preview.template.ts
|
|
@@ -3584,8 +4384,14 @@ function buildDiffHtml(original, current, t) {
|
|
|
3584
4384
|
return `<div class="diff-line ${type}"><span class="diff-sign">${sign}</span><span>${escHtml(line)}</span></div>`;
|
|
3585
4385
|
}).join("");
|
|
3586
4386
|
}
|
|
4387
|
+
var RUN_STATUS_KEY = {
|
|
4388
|
+
running: "FILE_PREVIEW.LAUNCH_RUNNING",
|
|
4389
|
+
passed: "FILE_PREVIEW.LAUNCH_PASSED",
|
|
4390
|
+
failed: "FILE_PREVIEW.LAUNCH_FAILED",
|
|
4391
|
+
error: "FILE_PREVIEW.LAUNCH_NO_RUNNER"
|
|
4392
|
+
};
|
|
3587
4393
|
function renderFilePreview(state, t) {
|
|
3588
|
-
const { fileName, showDiff, fileContent, originalContent, currentContent, itBlock, interceptorsBlock, closeLabel } = state;
|
|
4394
|
+
const { fileName, showDiff, fileContent, originalContent, currentContent, itBlock, interceptorsBlock, closeLabel, isLocal, runState, runOutput } = state;
|
|
3589
4395
|
const blocksPanelHtml = itBlock || interceptorsBlock ? `
|
|
3590
4396
|
<div class="blocks-panel">
|
|
3591
4397
|
${itBlock ? `
|
|
@@ -3606,6 +4412,13 @@ function renderFilePreview(state, t) {
|
|
|
3606
4412
|
</div>` : ""}
|
|
3607
4413
|
</div>` : "";
|
|
3608
4414
|
const hasChanges = originalContent !== null && originalContent !== (fileContent ?? "");
|
|
4415
|
+
const hasBlocks = !!(itBlock || interceptorsBlock);
|
|
4416
|
+
const launchBtnHtml = isLocal ? `<button id="btn-launch" class="btn-launch" ${runState === "running" ? "disabled" : ""}>${runState === "running" ? t("FILE_PREVIEW.LAUNCH_RUNNING") : t("FILE_PREVIEW.LAUNCH_BTN")}</button>` : `<button class="btn-launch" disabled title="${t("FILE_PREVIEW.LAUNCH_LOCAL_ONLY")}">${t("FILE_PREVIEW.LAUNCH_BTN")}</button>
|
|
4417
|
+
<span class="launch-hint">${t("FILE_PREVIEW.LAUNCH_LOCAL_ONLY")}</span>`;
|
|
4418
|
+
const runResultHtml = runState !== "idle" ? `<div class="run-result run-${runState}">
|
|
4419
|
+
<span class="run-status">${t(RUN_STATUS_KEY[runState])}</span>
|
|
4420
|
+
${runOutput ? `<pre class="run-output">${escHtml(runOutput.slice(0, 8e3))}</pre>` : ""}
|
|
4421
|
+
</div>` : "";
|
|
3609
4422
|
return `
|
|
3610
4423
|
<div class="container">
|
|
3611
4424
|
<div class="header">
|
|
@@ -3616,9 +4429,11 @@ function renderFilePreview(state, t) {
|
|
|
3616
4429
|
${showDiff ? `<div class="diff-panel" id="diff-panel">${buildDiffHtml(originalContent ?? "", currentContent, t)}</div>` : `<textarea class="editor" id="editor" spellcheck="false">${escHtml(fileContent ?? "")}</textarea>`}
|
|
3617
4430
|
${blocksPanelHtml}
|
|
3618
4431
|
</div>
|
|
4432
|
+
${runResultHtml}
|
|
3619
4433
|
<div class="footer">
|
|
3620
|
-
|
|
4434
|
+
${launchBtnHtml}
|
|
3621
4435
|
${hasChanges ? `<button id="btn-diff" class="${showDiff ? "btn-diff-active" : ""}">${t("FILE_PREVIEW.DIFF_BTN")}</button>` : ""}
|
|
4436
|
+
${hasBlocks && !showDiff ? `<button id="btn-insert" class="btn-insert" title="${t("FILE_PREVIEW.INSERT_TITLE")}">${t("FILE_PREVIEW.INSERT_BTN")}</button>` : ""}
|
|
3622
4437
|
<button id="btn-save" class="btn-save">${t("FILE_PREVIEW.SAVE_BTN")}</button>
|
|
3623
4438
|
<button id="btn-close">${escHtml(closeLabel || t("FILE_PREVIEW.CLOSE_BTN"))}</button>
|
|
3624
4439
|
</div>
|
|
@@ -3634,11 +4449,18 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
3634
4449
|
translation = translationService;
|
|
3635
4450
|
itBlock = "";
|
|
3636
4451
|
interceptorsBlock = "";
|
|
4452
|
+
notes = "";
|
|
3637
4453
|
commands = [];
|
|
3638
4454
|
interceptors = [];
|
|
4455
|
+
/** Endpoint of the local Cypress runner. Configurable; defaults to the bundled runner's port. */
|
|
4456
|
+
runnerUrl = "http://localhost:8123/run-test";
|
|
4457
|
+
/** Whether the app is served locally — gates the launch button. Overridable for tests. */
|
|
4458
|
+
isLocal = isLocalHost(window.location.hostname);
|
|
3639
4459
|
_fileContent = null;
|
|
3640
4460
|
_originalContent = null;
|
|
3641
4461
|
_showDiff = false;
|
|
4462
|
+
_runState = "idle";
|
|
4463
|
+
_runOutput = "";
|
|
3642
4464
|
constructor() {
|
|
3643
4465
|
super();
|
|
3644
4466
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
@@ -3666,15 +4488,34 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
3666
4488
|
onClose() {
|
|
3667
4489
|
this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
|
|
3668
4490
|
}
|
|
4491
|
+
/**
|
|
4492
|
+
* Runs the current spec headless via the local runner and shows the result.
|
|
4493
|
+
* No-op off localhost. Never throws — connection failures surface as an
|
|
4494
|
+
* "error" state + toast instead of an unhandled rejection.
|
|
4495
|
+
*/
|
|
3669
4496
|
async launchTest(specPath) {
|
|
3670
|
-
|
|
4497
|
+
if (!this.isLocal) return;
|
|
4498
|
+
const path = specPath ?? this.fileName ?? "";
|
|
3671
4499
|
if (!path) return;
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
4500
|
+
this._runState = "running";
|
|
4501
|
+
this._runOutput = "";
|
|
4502
|
+
this.render();
|
|
4503
|
+
try {
|
|
4504
|
+
const response = await fetch(this.runnerUrl, {
|
|
4505
|
+
method: "POST",
|
|
4506
|
+
headers: { "Content-Type": "application/json" },
|
|
4507
|
+
body: JSON.stringify({ specPath: path })
|
|
4508
|
+
});
|
|
4509
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
4510
|
+
const result = await response.json();
|
|
4511
|
+
this._runOutput = result.output ?? "";
|
|
4512
|
+
this._runState = result.success ? "passed" : "failed";
|
|
4513
|
+
} catch {
|
|
4514
|
+
this._runState = "error";
|
|
4515
|
+
this._runOutput = "";
|
|
4516
|
+
showToast(this.t("FILE_PREVIEW.LAUNCH_NO_RUNNER"), false);
|
|
4517
|
+
}
|
|
4518
|
+
this.render();
|
|
3678
4519
|
}
|
|
3679
4520
|
copyToClipboard(text) {
|
|
3680
4521
|
navigator.clipboard?.writeText(text);
|
|
@@ -3683,6 +4524,32 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
3683
4524
|
this._showDiff = !this._showDiff;
|
|
3684
4525
|
this.render();
|
|
3685
4526
|
}
|
|
4527
|
+
/**
|
|
4528
|
+
* Merges the it() and beforeEach() blocks into the current editor content
|
|
4529
|
+
* using the same logic as the automatic "Insert into file" action, so the
|
|
4530
|
+
* user does not have to copy/paste manually. Leaves the content untouched
|
|
4531
|
+
* (and warns) when the file has no valid describe() block to insert into.
|
|
4532
|
+
*/
|
|
4533
|
+
insertBlocks() {
|
|
4534
|
+
const base = this.textarea?.value ?? this._fileContent ?? "";
|
|
4535
|
+
let content = base;
|
|
4536
|
+
if (this.interceptorsBlock) {
|
|
4537
|
+
const merged = advancedTestTransformationService.insertBeforeEach(content, this.interceptorsBlock);
|
|
4538
|
+
if (merged) content = merged;
|
|
4539
|
+
}
|
|
4540
|
+
if (this.itBlock) {
|
|
4541
|
+
const comment = this.notes ? advancedTestTransformationService.buildBlockComment(this.notes) + "\n" : "";
|
|
4542
|
+
const merged = advancedTestTransformationService.insertItBlock(content, comment + this.itBlock);
|
|
4543
|
+
if (merged) content = merged;
|
|
4544
|
+
}
|
|
4545
|
+
if (content === base) {
|
|
4546
|
+
showToast(this.t("FILE_PREVIEW.INSERT_ERROR"), false);
|
|
4547
|
+
return;
|
|
4548
|
+
}
|
|
4549
|
+
this._fileContent = content;
|
|
4550
|
+
this.render();
|
|
4551
|
+
showToast(this.t("FILE_PREVIEW.INSERT_DONE"));
|
|
4552
|
+
}
|
|
3686
4553
|
render() {
|
|
3687
4554
|
this.shadow.innerHTML = `<style>${FILE_PREVIEW_STYLES}</style>${renderFilePreview({
|
|
3688
4555
|
fileName: this.fileName,
|
|
@@ -3692,7 +4559,10 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
3692
4559
|
currentContent: this.textarea?.value ?? this._fileContent ?? "",
|
|
3693
4560
|
itBlock: this.itBlock,
|
|
3694
4561
|
interceptorsBlock: this.interceptorsBlock,
|
|
3695
|
-
closeLabel: this.closeLabel
|
|
4562
|
+
closeLabel: this.closeLabel,
|
|
4563
|
+
isLocal: this.isLocal,
|
|
4564
|
+
runState: this._runState,
|
|
4565
|
+
runOutput: this._runOutput
|
|
3696
4566
|
}, this.t.bind(this))}`;
|
|
3697
4567
|
this.textarea = this.shadow.getElementById("editor");
|
|
3698
4568
|
if (this.textarea) {
|
|
@@ -3708,6 +4578,7 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
3708
4578
|
this.copyToClipboard(this.textarea?.value ?? this._fileContent ?? "");
|
|
3709
4579
|
});
|
|
3710
4580
|
this.shadow.getElementById("btn-diff")?.addEventListener("click", () => this.toggleDiff());
|
|
4581
|
+
this.shadow.getElementById("btn-insert")?.addEventListener("click", () => this.insertBlocks());
|
|
3711
4582
|
this.shadow.getElementById("btn-copy-it")?.addEventListener("click", () => {
|
|
3712
4583
|
this.copyToClipboard(this.itBlock);
|
|
3713
4584
|
});
|
|
@@ -3955,6 +4826,9 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
3955
4826
|
interceptorsUnsub;
|
|
3956
4827
|
pauseUnsub;
|
|
3957
4828
|
selectorNotFoundUnsub;
|
|
4829
|
+
langUnsub;
|
|
4830
|
+
sessionUnsub;
|
|
4831
|
+
sessionSaveTimer;
|
|
3958
4832
|
controlFirstTimeData = true;
|
|
3959
4833
|
_previsualizerRef = null;
|
|
3960
4834
|
httpMonitor;
|
|
@@ -3962,6 +4836,7 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
3962
4836
|
recording;
|
|
3963
4837
|
persistence;
|
|
3964
4838
|
translation;
|
|
4839
|
+
isVisible = false;
|
|
3965
4840
|
isRecording = false;
|
|
3966
4841
|
isPaused = false;
|
|
3967
4842
|
cypressCommands = [];
|
|
@@ -3996,17 +4871,22 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
3996
4871
|
this.initSelectorStrategy();
|
|
3997
4872
|
this.initSubscriptions();
|
|
3998
4873
|
this.render();
|
|
4874
|
+
this.initVisibility();
|
|
4875
|
+
this.initSessionContinuity();
|
|
3999
4876
|
this.keydownHandler = (e) => this.handleKeyboardEvent(e);
|
|
4000
4877
|
window.addEventListener("keydown", this.keydownHandler);
|
|
4001
4878
|
}
|
|
4002
4879
|
disconnectedCallback() {
|
|
4003
4880
|
if (this._isDisabled) return;
|
|
4004
4881
|
window.removeEventListener("keydown", this.keydownHandler);
|
|
4882
|
+
this.flushActiveSessionOnDisconnect();
|
|
4005
4883
|
this.recordingUnsub?.();
|
|
4006
4884
|
this.commandsUnsub?.();
|
|
4007
4885
|
this.interceptorsUnsub?.();
|
|
4008
4886
|
this.pauseUnsub?.();
|
|
4009
4887
|
this.selectorNotFoundUnsub?.();
|
|
4888
|
+
this.langUnsub?.();
|
|
4889
|
+
this.sessionUnsub?.();
|
|
4010
4890
|
this.httpMonitor?.uninstall();
|
|
4011
4891
|
this.recording.destroy();
|
|
4012
4892
|
}
|
|
@@ -4029,11 +4909,13 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
4029
4909
|
this.isRecording = val;
|
|
4030
4910
|
if (!val && !this.controlFirstTimeData) {
|
|
4031
4911
|
this.saveRecordingHistory();
|
|
4912
|
+
this.clearSessionPersistence();
|
|
4032
4913
|
this.showSaveTestDialog();
|
|
4033
4914
|
}
|
|
4034
4915
|
this.controlFirstTimeData = false;
|
|
4035
4916
|
this.render();
|
|
4036
4917
|
});
|
|
4918
|
+
this.sessionUnsub = this.recording.onSessionChange((state) => this.persistActiveSession(state));
|
|
4037
4919
|
this.commandsUnsub = this.recording.onCommandsChange((cmds) => {
|
|
4038
4920
|
this.cypressCommands = cmds;
|
|
4039
4921
|
if (this._previsualizerRef) this._previsualizerRef.commands = cmds;
|
|
@@ -4049,9 +4931,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
4049
4931
|
this.selectorNotFoundUnsub = this.recording.onSelectorNotFound((target) => {
|
|
4050
4932
|
if (this.smartSelectorEnabled) this.showSelectorPicker(target);
|
|
4051
4933
|
});
|
|
4934
|
+
this.langUnsub = this.translation.onLangChange(() => this.render());
|
|
4052
4935
|
}
|
|
4053
4936
|
showSelectorPicker(target) {
|
|
4054
|
-
import("./selector-picker-
|
|
4937
|
+
import("./selector-picker-NKYFFFGK.js").then(() => {
|
|
4055
4938
|
const existing = document.querySelector("selector-picker");
|
|
4056
4939
|
if (existing) existing.remove();
|
|
4057
4940
|
const picker = document.createElement("selector-picker");
|
|
@@ -4061,12 +4944,135 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
4061
4944
|
document.body.appendChild(picker);
|
|
4062
4945
|
});
|
|
4063
4946
|
}
|
|
4947
|
+
async initVisibility() {
|
|
4948
|
+
if (this.hasAttribute("start-hidden")) {
|
|
4949
|
+
this.isVisible = this.getAttribute("start-hidden") === "false";
|
|
4950
|
+
this.style.display = this.isVisible ? "" : "none";
|
|
4951
|
+
return;
|
|
4952
|
+
}
|
|
4953
|
+
const config = await this.persistence.getGeneralConfig();
|
|
4954
|
+
this.isVisible = config?.["startHidden"] !== "true";
|
|
4955
|
+
this.style.display = this.isVisible ? "" : "none";
|
|
4956
|
+
}
|
|
4064
4957
|
async initSelectorStrategy() {
|
|
4065
4958
|
const config = await this.persistence.getGeneralConfig();
|
|
4066
4959
|
const strategy = config?.["selectorStrategy"];
|
|
4067
4960
|
if (strategy) this.recording.selectorStrategy = strategy;
|
|
4068
4961
|
this.smartSelectorEnabled = config?.["smartSelectorEnabled"] !== "false";
|
|
4069
4962
|
}
|
|
4963
|
+
// ── cross-app session continuity (spec 006) ───────────────────────────────
|
|
4964
|
+
/**
|
|
4965
|
+
* On mount, detect a persisted live recording session and either resume it
|
|
4966
|
+
* silently (recent) or prompt continue/discard (stale). No-op when there is
|
|
4967
|
+
* no actively-recording session.
|
|
4968
|
+
*/
|
|
4969
|
+
async initSessionContinuity() {
|
|
4970
|
+
if (this._isDisabled) return;
|
|
4971
|
+
const session = await this.persistence.getActiveSession();
|
|
4972
|
+
if (!session || !session.isRecording) return;
|
|
4973
|
+
const ttlMin = await this.getResumeTtlMinutes();
|
|
4974
|
+
const ageMs = Date.now() - session.updatedAt;
|
|
4975
|
+
if (ageMs <= ttlMin * 6e4) {
|
|
4976
|
+
this.resumeSessionState(session);
|
|
4977
|
+
} else {
|
|
4978
|
+
this.promptResumeOrDiscard(session);
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
async getResumeTtlMinutes() {
|
|
4982
|
+
const config = await this.persistence.getConfig(RESUME_TTL_CONFIG_KEY);
|
|
4983
|
+
const raw = config?.[RESUME_TTL_CONFIG_KEY];
|
|
4984
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
4985
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_RESUME_TTL_MINUTES;
|
|
4986
|
+
}
|
|
4987
|
+
/** Rehydrates the recorder from a persisted session (no bootstrap re-emitted). */
|
|
4988
|
+
resumeSessionState(session) {
|
|
4989
|
+
this.recording.restoreSession(session);
|
|
4990
|
+
this.cypressCommands = session.commands;
|
|
4991
|
+
this.interceptors = session.interceptors;
|
|
4992
|
+
this.controlFirstTimeData = false;
|
|
4993
|
+
this.render();
|
|
4994
|
+
}
|
|
4995
|
+
async promptResumeOrDiscard(session) {
|
|
4996
|
+
const t = this.translation.translate.bind(this.translation);
|
|
4997
|
+
const result = await Swal.fire({
|
|
4998
|
+
title: t("RECORDER.SESSION_RESUME_TITLE"),
|
|
4999
|
+
html: `<div style="padding:8px 4px;color:#8b949e;font-size:13px;line-height:1.6"><p>${t("RECORDER.SESSION_RESUME_TEXT")}</p><p style="margin-top:6px;color:#c9d1d9"><b>${session.commands.length}</b> ${t("RECORDER.SESSION_RESUME_COUNT")}</p></div>`,
|
|
5000
|
+
showCancelButton: true,
|
|
5001
|
+
confirmButtonText: t("RECORDER.SESSION_CONTINUE_BTN"),
|
|
5002
|
+
cancelButtonText: t("RECORDER.SESSION_DISCARD_BTN"),
|
|
5003
|
+
color: "#e6edf3"
|
|
5004
|
+
});
|
|
5005
|
+
if (result && result.isConfirmed) {
|
|
5006
|
+
this.resumeSessionState(session);
|
|
5007
|
+
} else {
|
|
5008
|
+
this.discardSession();
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
/** Debounced persistence of the live session; clears it the moment recording stops. */
|
|
5012
|
+
persistActiveSession(state) {
|
|
5013
|
+
if (this._isDisabled) return;
|
|
5014
|
+
if (!state.isRecording) {
|
|
5015
|
+
this.clearSessionPersistence();
|
|
5016
|
+
return;
|
|
5017
|
+
}
|
|
5018
|
+
this.writeSessionBreadcrumb(state);
|
|
5019
|
+
if (this.sessionSaveTimer) clearTimeout(this.sessionSaveTimer);
|
|
5020
|
+
this.sessionSaveTimer = setTimeout(() => {
|
|
5021
|
+
const snap = this.recording.getSessionSnapshot();
|
|
5022
|
+
if (snap.isRecording) this.persistence.saveActiveSession(snap).catch(() => {
|
|
5023
|
+
});
|
|
5024
|
+
}, 300);
|
|
5025
|
+
}
|
|
5026
|
+
writeSessionBreadcrumb(state) {
|
|
5027
|
+
try {
|
|
5028
|
+
localStorage.setItem(ACTIVE_SESSION_BREADCRUMB_KEY, JSON.stringify({
|
|
5029
|
+
sessionId: state.sessionId,
|
|
5030
|
+
isRecording: state.isRecording,
|
|
5031
|
+
updatedAt: state.updatedAt
|
|
5032
|
+
}));
|
|
5033
|
+
} catch {
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
flushActiveSessionOnDisconnect() {
|
|
5037
|
+
const snap = this.recording.getSessionSnapshot();
|
|
5038
|
+
if (!snap.isRecording) return;
|
|
5039
|
+
if (this.sessionSaveTimer) clearTimeout(this.sessionSaveTimer);
|
|
5040
|
+
this.writeSessionBreadcrumb(snap);
|
|
5041
|
+
this.persistence.saveActiveSession(snap).catch(() => {
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
clearSessionPersistence() {
|
|
5045
|
+
if (this.sessionSaveTimer) {
|
|
5046
|
+
clearTimeout(this.sessionSaveTimer);
|
|
5047
|
+
this.sessionSaveTimer = void 0;
|
|
5048
|
+
}
|
|
5049
|
+
try {
|
|
5050
|
+
localStorage.removeItem(ACTIVE_SESSION_BREADCRUMB_KEY);
|
|
5051
|
+
} catch {
|
|
5052
|
+
}
|
|
5053
|
+
this.persistence.clearActiveSession().catch(() => {
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
5056
|
+
/** True when a synchronous breadcrumb marks an active recording session. */
|
|
5057
|
+
hasActiveSession() {
|
|
5058
|
+
try {
|
|
5059
|
+
const raw = localStorage.getItem(ACTIVE_SESSION_BREADCRUMB_KEY);
|
|
5060
|
+
if (!raw) return false;
|
|
5061
|
+
return !!JSON.parse(raw).isRecording;
|
|
5062
|
+
} catch {
|
|
5063
|
+
return false;
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
/** Programmatically resume the persisted session, if any. */
|
|
5067
|
+
resumeSession() {
|
|
5068
|
+
this.persistence.getActiveSession().then((s) => {
|
|
5069
|
+
if (s) this.resumeSessionState(s);
|
|
5070
|
+
});
|
|
5071
|
+
}
|
|
5072
|
+
/** Discard the persisted live session (breadcrumb + DB record). */
|
|
5073
|
+
discardSession() {
|
|
5074
|
+
this.clearSessionPersistence();
|
|
5075
|
+
}
|
|
4070
5076
|
toggle() {
|
|
4071
5077
|
this.recording.toggleRecording();
|
|
4072
5078
|
}
|
|
@@ -4080,6 +5086,12 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
4080
5086
|
handleKeyboardEvent(event) {
|
|
4081
5087
|
if (!event.ctrlKey) return;
|
|
4082
5088
|
const key = event.key.toLowerCase();
|
|
5089
|
+
if (key === "e" && event.shiftKey) {
|
|
5090
|
+
event.preventDefault();
|
|
5091
|
+
this.toggleVisibility();
|
|
5092
|
+
return;
|
|
5093
|
+
}
|
|
5094
|
+
if (!this.isVisible) return;
|
|
4083
5095
|
if (key === "r") {
|
|
4084
5096
|
event.preventDefault();
|
|
4085
5097
|
this.toggle();
|
|
@@ -4097,6 +5109,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
4097
5109
|
this.showSettingsDialog();
|
|
4098
5110
|
}
|
|
4099
5111
|
}
|
|
5112
|
+
toggleVisibility() {
|
|
5113
|
+
this.isVisible = !this.isVisible;
|
|
5114
|
+
this.style.display = this.isVisible ? "" : "none";
|
|
5115
|
+
}
|
|
4100
5116
|
// ── recording history (task 5) ────────────────────────────────────────────
|
|
4101
5117
|
saveRecordingHistory() {
|
|
4102
5118
|
if (!this.cypressCommands.length) return;
|
|
@@ -4332,13 +5348,13 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4332
5348
|
child.translation = this.translation;
|
|
4333
5349
|
container.appendChild(child);
|
|
4334
5350
|
child.addEventListener("savetest", (e) => {
|
|
4335
|
-
const { description, tags } = e.detail ?? {};
|
|
4336
|
-
this.onSaveTest(description ?? null, tags ?? []);
|
|
5351
|
+
const { description, notes, tags } = e.detail ?? {};
|
|
5352
|
+
this.onSaveTest(description ?? null, tags ?? [], notes ?? "");
|
|
4337
5353
|
Swal.close();
|
|
4338
5354
|
});
|
|
4339
5355
|
child.addEventListener("saveandexport", (e) => {
|
|
4340
|
-
const { description, tags } = e.detail ?? {};
|
|
4341
|
-
this.onSaveAndExportTest(description ?? null, tags ?? []);
|
|
5356
|
+
const { description, notes, tags } = e.detail ?? {};
|
|
5357
|
+
this.onSaveAndExportTest(description ?? null, tags ?? [], notes ?? "");
|
|
4342
5358
|
Swal.close();
|
|
4343
5359
|
});
|
|
4344
5360
|
},
|
|
@@ -4369,6 +5385,10 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4369
5385
|
child.addEventListener("smartselectorchange", (e) => {
|
|
4370
5386
|
this.smartSelectorEnabled = e.detail;
|
|
4371
5387
|
});
|
|
5388
|
+
child.addEventListener("starthiddenchange", (e) => {
|
|
5389
|
+
this.isVisible = !e.detail;
|
|
5390
|
+
this.style.display = this.isVisible ? "" : "none";
|
|
5391
|
+
});
|
|
4372
5392
|
},
|
|
4373
5393
|
willClose: () => {
|
|
4374
5394
|
this.isSettingsDialogOpen = false;
|
|
@@ -4423,7 +5443,8 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4423
5443
|
e.detail.fileName,
|
|
4424
5444
|
e.detail.testId,
|
|
4425
5445
|
e.detail.itBlock,
|
|
4426
|
-
e.detail.interceptorsBlock
|
|
5446
|
+
e.detail.interceptorsBlock,
|
|
5447
|
+
e.detail.notes
|
|
4427
5448
|
), 150);
|
|
4428
5449
|
});
|
|
4429
5450
|
},
|
|
@@ -4437,7 +5458,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4437
5458
|
}, 0);
|
|
4438
5459
|
});
|
|
4439
5460
|
}
|
|
4440
|
-
showFileEditorDialog(handle, content, fileName, testId, itBlock = "", interceptorsBlock = "") {
|
|
5461
|
+
showFileEditorDialog(handle, content, fileName, testId, itBlock = "", interceptorsBlock = "", notes = "") {
|
|
4441
5462
|
Swal.fire({
|
|
4442
5463
|
title: this.translation.translate("RECORDER.FILE_EDITOR_TITLE"),
|
|
4443
5464
|
html: '<div id="file-editor-modal-content" style="padding:0;height:540px"></div>',
|
|
@@ -4458,6 +5479,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4458
5479
|
child.closeLabel = this.translation.translate("FILE_PREVIEW.BACK_TO_EDITOR");
|
|
4459
5480
|
child.itBlock = itBlock;
|
|
4460
5481
|
child.interceptorsBlock = interceptorsBlock;
|
|
5482
|
+
child.notes = notes;
|
|
4461
5483
|
container.appendChild(child);
|
|
4462
5484
|
child.addEventListener("close", () => {
|
|
4463
5485
|
Swal.close();
|
|
@@ -4480,18 +5502,18 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4480
5502
|
this.resizePopup();
|
|
4481
5503
|
}
|
|
4482
5504
|
// ── save handlers ─────────────────────────────────────────────────────────
|
|
4483
|
-
async onSaveTest(description, tags = []) {
|
|
5505
|
+
async onSaveTest(description, tags = [], notes = "") {
|
|
4484
5506
|
if (description) {
|
|
4485
|
-
await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags);
|
|
5507
|
+
await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes);
|
|
4486
5508
|
this.recording.clearCommands();
|
|
4487
5509
|
this.clearRecordingHistory();
|
|
4488
5510
|
this.cypressCommands = [];
|
|
4489
5511
|
this.interceptors = [];
|
|
4490
5512
|
}
|
|
4491
5513
|
}
|
|
4492
|
-
async onSaveAndExportTest(description, tags = []) {
|
|
5514
|
+
async onSaveAndExportTest(description, tags = [], notes = "") {
|
|
4493
5515
|
if (description) {
|
|
4494
|
-
const id = await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags);
|
|
5516
|
+
const id = await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes);
|
|
4495
5517
|
this.recording.clearCommands();
|
|
4496
5518
|
this.clearRecordingHistory();
|
|
4497
5519
|
this.cypressCommands = [];
|
|
@@ -4518,6 +5540,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
4518
5540
|
render() {
|
|
4519
5541
|
const rec = this.isRecording;
|
|
4520
5542
|
const paused = this.isPaused;
|
|
5543
|
+
this.style.display = this.isVisible ? "" : "none";
|
|
4521
5544
|
this.shadow.innerHTML = `<style>${getRecorderStyles(rec, paused)}</style>${renderRecorderWidget(rec, paused, this.translation.translate.bind(this.translation))}`;
|
|
4522
5545
|
this.shadow.querySelector('[data-action="toggle"]')?.addEventListener("click", () => this.toggle());
|
|
4523
5546
|
this.shadow.querySelector('[data-action="pause"]')?.addEventListener("click", () => this.togglePause());
|
|
@@ -4532,23 +5555,28 @@ if (!customElements.get("lib-e2e-recorder")) {
|
|
|
4532
5555
|
}
|
|
4533
5556
|
|
|
4534
5557
|
// src/index.ts
|
|
4535
|
-
var VERSION = "0.
|
|
5558
|
+
var VERSION = "0.5.0";
|
|
4536
5559
|
export {
|
|
5560
|
+
ACTIVE_SESSION_BREADCRUMB_KEY,
|
|
4537
5561
|
AdvancedTestEditorElement,
|
|
4538
5562
|
AdvancedTestTransformationService,
|
|
4539
5563
|
ConfigurationElement,
|
|
4540
5564
|
DB_SCHEMA,
|
|
4541
5565
|
DB_STORE_NAMES,
|
|
5566
|
+
DEFAULT_RESUME_TTL_MINUTES,
|
|
4542
5567
|
FilePreviewElement,
|
|
4543
5568
|
HttpMonitor,
|
|
4544
5569
|
INPUT_TYPES,
|
|
4545
5570
|
LIB_E2E_CYPRESS_FOR_DUMMYS_SWAL2_STYLES,
|
|
5571
|
+
LOCALE_BY_LANG,
|
|
4546
5572
|
LibE2eRecorderElement,
|
|
4547
5573
|
PersistenceService,
|
|
5574
|
+
RESUME_TTL_CONFIG_KEY,
|
|
4548
5575
|
RecordingService,
|
|
4549
5576
|
SCROLLBAR_STYLES,
|
|
4550
5577
|
SUPPORTED_LANGS,
|
|
4551
5578
|
SaveTestElement,
|
|
5579
|
+
SelectorPickerElement,
|
|
4552
5580
|
Subject,
|
|
4553
5581
|
TestEditorElement,
|
|
4554
5582
|
TestPrevisualizerElement,
|
|
@@ -4559,10 +5587,13 @@ export {
|
|
|
4559
5587
|
generateAlias,
|
|
4560
5588
|
injectStyles,
|
|
4561
5589
|
isLang,
|
|
5590
|
+
isLocalHost,
|
|
5591
|
+
localeForLang,
|
|
4562
5592
|
makeModalResizable,
|
|
4563
5593
|
makeSwalDraggable,
|
|
4564
5594
|
makeSwalDraggableByContentId,
|
|
4565
5595
|
persistenceService,
|
|
5596
|
+
selectTestsForExport,
|
|
4566
5597
|
setSwal2DataCyAttribute,
|
|
4567
5598
|
showToast,
|
|
4568
5599
|
transformationService,
|