lib-e2e-cypress-for-dummys-ts 0.9.0 → 1.0.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/LICENSE +21 -0
- package/README.md +767 -707
- package/dist/{chunk-CQEDHL7A.js → chunk-24WFAT4W.js} +40 -7
- package/dist/chunk-24WFAT4W.js.map +1 -0
- package/dist/index.cjs +1467 -628
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -62
- package/dist/index.d.ts +46 -62
- package/dist/index.js +1416 -607
- package/dist/index.js.map +1 -1
- package/dist/runner.js +18 -2
- package/dist/selector-picker-KJUXPXHH.js +7 -0
- package/package.json +74 -74
- package/dist/chunk-CQEDHL7A.js.map +0 -1
- package/dist/selector-picker-JM5HID3Z.js +0 -7
- /package/dist/{selector-picker-JM5HID3Z.js.map → selector-picker-KJUXPXHH.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,8 +2,11 @@ import {
|
|
|
2
2
|
FORBIDDEN_ID_PREFIXES,
|
|
3
3
|
SelectorPickerElement,
|
|
4
4
|
escAttr,
|
|
5
|
-
escHtml
|
|
6
|
-
|
|
5
|
+
escHtml,
|
|
6
|
+
escapeCssAttrValue,
|
|
7
|
+
escapeSingleQuotes,
|
|
8
|
+
normalizeBlock
|
|
9
|
+
} from "./chunk-24WFAT4W.js";
|
|
7
10
|
|
|
8
11
|
// src/models/lang.model.ts
|
|
9
12
|
var SUPPORTED_LANGS = ["es", "en", "fr", "it", "de"];
|
|
@@ -90,11 +93,6 @@ var DB_SCHEMA = {
|
|
|
90
93
|
};
|
|
91
94
|
var DB_STORE_NAMES = DB_SCHEMA.stores.map((s) => s.name);
|
|
92
95
|
|
|
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
96
|
// src/utils/subject.ts
|
|
99
97
|
var Subject = class {
|
|
100
98
|
_value;
|
|
@@ -142,7 +140,10 @@ var I18N_ES = {
|
|
|
142
140
|
NOTES_PLACEHOLDER: "Qu\xE9 valida este test, condiciones previas, etc.",
|
|
143
141
|
TICKET_LABEL: "Ticket / Issue (opcional):",
|
|
144
142
|
TICKET_PLACEHOLDER: "Ej: PROJ-123, #42",
|
|
145
|
-
TICKET_WARN: "ID no v\xE1lido para el proveedor configurado (se guardar\xE1 igualmente)"
|
|
143
|
+
TICKET_WARN: "ID no v\xE1lido para el proveedor configurado (se guardar\xE1 igualmente)",
|
|
144
|
+
CONFIRM_DISCARD: "\xBFSeguro que quieres descartar el test grabado? Se perder\xE1n todos los pasos registrados.",
|
|
145
|
+
CONFIRM_DISCARD_BTN: "S\xED, descartar",
|
|
146
|
+
BACK_BTN: "Volver"
|
|
146
147
|
},
|
|
147
148
|
TEST_EDITOR: {
|
|
148
149
|
NO_TAGS: "Sin etiquetas",
|
|
@@ -224,7 +225,32 @@ var I18N_ES = {
|
|
|
224
225
|
ISSUE_TRACKER_SUB: "A\xF1ade un ID de ticket al guardar y genera un enlace clickable en el listado.",
|
|
225
226
|
ISSUE_TRACKER_PROVIDER: "Proveedor",
|
|
226
227
|
ISSUE_TRACKER_BASE_URL: "URL base",
|
|
227
|
-
ISSUE_TRACKER_BASE_URL_PH: "Ej: https://miproyecto.atlassian.net"
|
|
228
|
+
ISSUE_TRACKER_BASE_URL_PH: "Ej: https://miproyecto.atlassian.net",
|
|
229
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
230
|
+
LOGIN_SETUP_NOT_CONFIGURED: "No configurado",
|
|
231
|
+
LOGIN_SETUP_SET_UP_BTN: "Configurar",
|
|
232
|
+
LOGIN_SETUP_TITLE: "Configuraci\xF3n de login",
|
|
233
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Crear scaffold",
|
|
234
|
+
LOGIN_SETUP_SELECT_EXISTING: "Seleccionar fichero existente",
|
|
235
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nombre del fichero",
|
|
236
|
+
LOGIN_SETUP_FILE_NAME_PH: "Ej: cypress/common-services/login.service.ts",
|
|
237
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-escanear",
|
|
238
|
+
LOGIN_SETUP_FUNCTIONS_DETECTED: "Funciones detectadas",
|
|
239
|
+
LOGIN_SETUP_BLOCK_ASSIGN: "Asignaci\xF3n de bloques",
|
|
240
|
+
LOGIN_SETUP_NO_FUNCTIONS: "No se encontraron funciones exportadas. Rellena el scaffold y re-escanea.",
|
|
241
|
+
LOGIN_SETUP_BEFORE_LABEL: "Funci\xF3n before() (una vez por suite)",
|
|
242
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Funci\xF3n beforeEach() (antes de cada test)",
|
|
243
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Ninguna \u2014",
|
|
244
|
+
LOGIN_SETUP_SUMMARY: "Fichero: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
245
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Guardar",
|
|
246
|
+
LOGIN_SETUP_CLEAR_BTN: "Eliminar configuraci\xF3n",
|
|
247
|
+
LOGIN_SETUP_CANCEL_BTN: "Cancelar",
|
|
248
|
+
LOGIN_SETUP_SOURCE_LABEL: "Origen del fichero de login",
|
|
249
|
+
LOGIN_SETUP_CREATE_SUB: "Genera un scaffold con funciones de ejemplo",
|
|
250
|
+
LOGIN_SETUP_SELECT_SUB: "Apunta a un servicio de login existente",
|
|
251
|
+
LOGIN_SETUP_INJECT_TITLE: "\xBFA\xF1adir bloques de login?",
|
|
252
|
+
LOGIN_SETUP_INJECT_TEXT: "Este fichero no tiene los bloques de login configurados. \xBFQuieres a\xF1adir el import y los bloques before/beforeEach ahora?",
|
|
253
|
+
LOGIN_SETUP_INJECT_CONFIRM: "S\xED, a\xF1adir"
|
|
228
254
|
},
|
|
229
255
|
SELECTOR_PICKER: {
|
|
230
256
|
TITLE: "Selecciona un elemento del DOM",
|
|
@@ -315,6 +341,9 @@ var I18N_ES = {
|
|
|
315
341
|
FILE_SAVE_ERROR_TOAST: "Error al guardar el archivo",
|
|
316
342
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures escritos en cypress/fixtures",
|
|
317
343
|
FIXTURES_NO_FOLDER_TOAST: "No se pudieron escribir los fixtures (configura la carpeta Cypress)",
|
|
344
|
+
FILE_UPLOAD_MANUAL_TOAST: "A\xF1ade {files} a cypress/fixtures/ antes de ejecutar el test.",
|
|
345
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copiado/s a cypress/fixtures/.",
|
|
346
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "No se pudo copiar {files} a cypress/fixtures/. A\xF1\xE1delo manualmente.",
|
|
318
347
|
BTN_CONFIG: "Config",
|
|
319
348
|
BTN_FILES: "Ficheros",
|
|
320
349
|
BTN_COMMANDS: "Comandos",
|
|
@@ -362,7 +391,7 @@ var I18N_ES = {
|
|
|
362
391
|
SEC_PANELS: "\u{1F5C2} Paneles",
|
|
363
392
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 guardados: filtrar por tag, copiar, combinar, exportar.",
|
|
364
393
|
PANEL_COMMANDS: "\u2328\uFE0F Comandos \u2014 de la grabaci\xF3n actual: reordenar, borrar, a\xF1adir aserciones.",
|
|
365
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 idioma, selector, HTTP, carpeta Cypress, datos, visibilidad, posici\xF3n.",
|
|
394
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 idioma, selector, HTTP, carpeta Cypress, login setup, datos, visibilidad, posici\xF3n.",
|
|
366
395
|
PANEL_FILES: "\u{1F4C1} Ficheros \u2014 inserta tests en tus .cy.ts y lanza el runner.",
|
|
367
396
|
SEC_SELECTOR: "\u{1F50E} Selectores",
|
|
368
397
|
SELECTOR_1: "Elige el atributo preferido (data-cy, data-testid, aria-label, id) en Config.",
|
|
@@ -375,6 +404,7 @@ var I18N_ES = {
|
|
|
375
404
|
ADV_RUNNER: "Runner local: npx lib-e2e-cypress-runner para lanzar un spec headless desde el editor.",
|
|
376
405
|
ADV_CROSSAPP: "Cross-app: la grabaci\xF3n sobrevive al cambiar de micro-frontend (mismo origen).",
|
|
377
406
|
ADV_DRAG: "Arrastra el bot\xF3n \u23FA para mover el widget; recuerda su posici\xF3n.",
|
|
407
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
378
408
|
CLOSE: "Cerrar",
|
|
379
409
|
TAB_REFERENCE: "Referencia r\xE1pida",
|
|
380
410
|
TAB_GUIDE: "Gu\xEDa de uso",
|
|
@@ -392,13 +422,13 @@ var I18N_ES = {
|
|
|
392
422
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
393
423
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
394
424
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
425
|
+
G_COV_7: "Login Setup: configura tu funci\xF3n de login en Ctrl+3 \u2192 Login Setup y el grabador a\xF1ade los bloques before()/beforeEach() a cada test nuevo.",
|
|
395
426
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
396
427
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
397
428
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
398
429
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
399
430
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
400
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
401
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
431
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
402
432
|
}
|
|
403
433
|
};
|
|
404
434
|
|
|
@@ -429,7 +459,10 @@ var I18N_EN = {
|
|
|
429
459
|
NOTES_PLACEHOLDER: "What this test validates, preconditions, etc.",
|
|
430
460
|
TICKET_LABEL: "Ticket / Issue (optional):",
|
|
431
461
|
TICKET_PLACEHOLDER: "E.g.: PROJ-123, #42",
|
|
432
|
-
TICKET_WARN: "Invalid ID for the configured provider (will be saved anyway)"
|
|
462
|
+
TICKET_WARN: "Invalid ID for the configured provider (will be saved anyway)",
|
|
463
|
+
CONFIRM_DISCARD: "Are you sure you want to discard this recording? All recorded steps will be lost.",
|
|
464
|
+
CONFIRM_DISCARD_BTN: "Yes, discard",
|
|
465
|
+
BACK_BTN: "Go back"
|
|
433
466
|
},
|
|
434
467
|
TEST_EDITOR: {
|
|
435
468
|
NO_TAGS: "No tags",
|
|
@@ -511,7 +544,32 @@ var I18N_EN = {
|
|
|
511
544
|
ISSUE_TRACKER_SUB: "Add a ticket ID when saving and generate a clickable link in the test list.",
|
|
512
545
|
ISSUE_TRACKER_PROVIDER: "Provider",
|
|
513
546
|
ISSUE_TRACKER_BASE_URL: "Base URL",
|
|
514
|
-
ISSUE_TRACKER_BASE_URL_PH: "E.g.: https://myproject.atlassian.net"
|
|
547
|
+
ISSUE_TRACKER_BASE_URL_PH: "E.g.: https://myproject.atlassian.net",
|
|
548
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
549
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Not configured",
|
|
550
|
+
LOGIN_SETUP_SET_UP_BTN: "Set up",
|
|
551
|
+
LOGIN_SETUP_TITLE: "Login setup",
|
|
552
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Create scaffold",
|
|
553
|
+
LOGIN_SETUP_SELECT_EXISTING: "Select existing file",
|
|
554
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "File name",
|
|
555
|
+
LOGIN_SETUP_FILE_NAME_PH: "E.g.: cypress/common-services/login.service.ts",
|
|
556
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scan",
|
|
557
|
+
LOGIN_SETUP_FUNCTIONS_DETECTED: "Detected functions",
|
|
558
|
+
LOGIN_SETUP_BLOCK_ASSIGN: "Block assignment",
|
|
559
|
+
LOGIN_SETUP_NO_FUNCTIONS: "No exported functions found. Fill in the scaffold and re-scan.",
|
|
560
|
+
LOGIN_SETUP_BEFORE_LABEL: "before() function (once per suite)",
|
|
561
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "beforeEach() function (before every test)",
|
|
562
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 None \u2014",
|
|
563
|
+
LOGIN_SETUP_SUMMARY: "File: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
564
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Save",
|
|
565
|
+
LOGIN_SETUP_CLEAR_BTN: "Remove configuration",
|
|
566
|
+
LOGIN_SETUP_CANCEL_BTN: "Cancel",
|
|
567
|
+
LOGIN_SETUP_SOURCE_LABEL: "Login file source",
|
|
568
|
+
LOGIN_SETUP_CREATE_SUB: "Generate a scaffold with sample functions",
|
|
569
|
+
LOGIN_SETUP_SELECT_SUB: "Point to an existing login service file",
|
|
570
|
+
LOGIN_SETUP_INJECT_TITLE: "Add login blocks?",
|
|
571
|
+
LOGIN_SETUP_INJECT_TEXT: "This file does not have the configured login blocks. Do you want to add the import and before/beforeEach blocks now?",
|
|
572
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Yes, add them"
|
|
515
573
|
},
|
|
516
574
|
SELECTOR_PICKER: {
|
|
517
575
|
TITLE: "Select a DOM element",
|
|
@@ -602,6 +660,9 @@ var I18N_EN = {
|
|
|
602
660
|
FILE_SAVE_ERROR_TOAST: "Error saving the file",
|
|
603
661
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures written to cypress/fixtures",
|
|
604
662
|
FIXTURES_NO_FOLDER_TOAST: "Could not write fixtures (configure the Cypress folder)",
|
|
663
|
+
FILE_UPLOAD_MANUAL_TOAST: "Add {files} to cypress/fixtures/ before running the test.",
|
|
664
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copied to cypress/fixtures/.",
|
|
665
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Could not copy {files} to cypress/fixtures/. Add it manually.",
|
|
605
666
|
BTN_CONFIG: "Config",
|
|
606
667
|
BTN_FILES: "Files",
|
|
607
668
|
BTN_COMMANDS: "Commands",
|
|
@@ -649,7 +710,7 @@ var I18N_EN = {
|
|
|
649
710
|
SEC_PANELS: "\u{1F5C2} Panels",
|
|
650
711
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 saved tests: filter by tag, copy, combine, export.",
|
|
651
712
|
PANEL_COMMANDS: "\u2328\uFE0F Commands \u2014 current recording: reorder, delete, add assertions.",
|
|
652
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 language, selector, HTTP, Cypress folder, data, visibility, position.",
|
|
713
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 language, selector, HTTP, Cypress folder, login setup, data, visibility, position.",
|
|
653
714
|
PANEL_FILES: "\u{1F4C1} Files \u2014 insert tests into your .cy.ts and launch the runner.",
|
|
654
715
|
SEC_SELECTOR: "\u{1F50E} Selectors",
|
|
655
716
|
SELECTOR_1: "Choose the preferred attribute (data-cy, data-testid, aria-label, id) in Config.",
|
|
@@ -662,6 +723,7 @@ var I18N_EN = {
|
|
|
662
723
|
ADV_RUNNER: "Local runner: npx lib-e2e-cypress-runner to run a spec headless from the editor.",
|
|
663
724
|
ADV_CROSSAPP: "Cross-app: the recording survives switching micro-frontend (same origin).",
|
|
664
725
|
ADV_DRAG: "Drag the \u23FA button to move the widget; its position is remembered.",
|
|
726
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): point to your login service; the recorder automatically injects before()/beforeEach() blocks into each new test.",
|
|
665
727
|
CLOSE: "Close",
|
|
666
728
|
TAB_REFERENCE: "Quick reference",
|
|
667
729
|
TAB_GUIDE: "Usage guide",
|
|
@@ -679,13 +741,13 @@ var I18N_EN = {
|
|
|
679
741
|
G_COV_4: "Reliable selector (data-cy/testid/aria/id) and a picker when none is obvious.",
|
|
680
742
|
G_COV_5: "Automatic assertions (Alt+click) and the manual builder.",
|
|
681
743
|
G_COV_6: "Continuity when switching micro-frontend (same origin), invisible mode and i18n.",
|
|
744
|
+
G_COV_7: "Login Setup: configure your login function under Ctrl+3 \u2192 Login Setup; the recorder adds before()/beforeEach() blocks to every new test.",
|
|
682
745
|
G_SEC_NOTCOVERED: "\u{1F6AB} What it does NOT cover (yet)",
|
|
683
746
|
G_NC_1: "Different origins / subdomains (same origin only).",
|
|
684
747
|
G_NC_2: "File upload (selectFile), drag & drop, and hover.",
|
|
685
748
|
G_NC_3: "The Tab key (Cypress can't .type() it).",
|
|
686
749
|
G_NC_4: "DELETE requests (intentionally ignored).",
|
|
687
|
-
G_NC_5: "Third-party iframes and Shadow DOM may not be captured."
|
|
688
|
-
G_NC_6: "Programmatic login generation (on the roadmap)."
|
|
750
|
+
G_NC_5: "Third-party iframes and Shadow DOM may not be captured."
|
|
689
751
|
}
|
|
690
752
|
};
|
|
691
753
|
|
|
@@ -716,8 +778,11 @@ var I18N_FR = {
|
|
|
716
778
|
NOTES_PLACEHOLDER: "Ce que ce test valide, conditions pr\xE9alables, etc.",
|
|
717
779
|
TICKET_LABEL: "Ticket / Issue (optionnel) :",
|
|
718
780
|
TICKET_PLACEHOLDER: "Ex : PROJ-123, #42",
|
|
719
|
-
TICKET_WARN: "ID non valide pour le fournisseur configur\xE9 (sera enregistr\xE9 quand m\xEAme)"
|
|
781
|
+
TICKET_WARN: "ID non valide pour le fournisseur configur\xE9 (sera enregistr\xE9 quand m\xEAme)",
|
|
720
782
|
// TODO: translate
|
|
783
|
+
CONFIRM_DISCARD: "\xCAtes-vous s\xFBr de vouloir abandonner cet enregistrement ? Toutes les \xE9tapes enregistr\xE9es seront perdues.",
|
|
784
|
+
CONFIRM_DISCARD_BTN: "Oui, abandonner",
|
|
785
|
+
BACK_BTN: "Retour"
|
|
721
786
|
},
|
|
722
787
|
TEST_EDITOR: {
|
|
723
788
|
NO_TAGS: "Sans \xE9tiquettes",
|
|
@@ -799,7 +864,32 @@ var I18N_FR = {
|
|
|
799
864
|
ISSUE_TRACKER_SUB: "Ajoutez un ID de ticket lors de l'enregistrement et g\xE9n\xE9rez un lien cliquable.",
|
|
800
865
|
ISSUE_TRACKER_PROVIDER: "Fournisseur",
|
|
801
866
|
ISSUE_TRACKER_BASE_URL: "URL de base",
|
|
802
|
-
ISSUE_TRACKER_BASE_URL_PH: "Ex : https://monprojet.atlassian.net"
|
|
867
|
+
ISSUE_TRACKER_BASE_URL_PH: "Ex : https://monprojet.atlassian.net",
|
|
868
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
869
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Non configur\xE9",
|
|
870
|
+
LOGIN_SETUP_SET_UP_BTN: "Configurer",
|
|
871
|
+
LOGIN_SETUP_TITLE: "Configuration du login",
|
|
872
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Cr\xE9er un scaffold",
|
|
873
|
+
LOGIN_SETUP_SELECT_EXISTING: "S\xE9lectionner un fichier existant",
|
|
874
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nom du fichier",
|
|
875
|
+
LOGIN_SETUP_FILE_NAME_PH: "Ex : cypress/common-services/login.service.ts",
|
|
876
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scanner",
|
|
877
|
+
LOGIN_SETUP_FUNCTIONS_DETECTED: "Fonctions d\xE9tect\xE9es",
|
|
878
|
+
LOGIN_SETUP_BLOCK_ASSIGN: "Affectation des blocs",
|
|
879
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Aucune fonction export\xE9e trouv\xE9e. Remplissez le scaffold et re-scannez.",
|
|
880
|
+
LOGIN_SETUP_BEFORE_LABEL: "Fonction before() (une fois par suite)",
|
|
881
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Fonction beforeEach() (avant chaque test)",
|
|
882
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Aucune \u2014",
|
|
883
|
+
LOGIN_SETUP_SUMMARY: "Fichier : {file} \xB7 before : {before} \xB7 beforeEach : {beforeEach}",
|
|
884
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Enregistrer",
|
|
885
|
+
LOGIN_SETUP_CLEAR_BTN: "Supprimer la configuration",
|
|
886
|
+
LOGIN_SETUP_CANCEL_BTN: "Annuler",
|
|
887
|
+
LOGIN_SETUP_SOURCE_LABEL: "Source du fichier de login",
|
|
888
|
+
LOGIN_SETUP_CREATE_SUB: "G\xE9n\xE9rer un scaffold avec des fonctions exemples",
|
|
889
|
+
LOGIN_SETUP_SELECT_SUB: "Pointer vers un fichier de service de login existant",
|
|
890
|
+
LOGIN_SETUP_INJECT_TITLE: "Ajouter les blocs de login ?",
|
|
891
|
+
LOGIN_SETUP_INJECT_TEXT: "Ce fichier ne contient pas les blocs de login configur\xE9s. Voulez-vous ajouter l'import et les blocs before/beforeEach maintenant ?",
|
|
892
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Oui, ajouter"
|
|
803
893
|
},
|
|
804
894
|
SELECTOR_PICKER: {
|
|
805
895
|
TITLE: "S\xE9lectionnez un \xE9l\xE9ment du DOM",
|
|
@@ -890,6 +980,12 @@ var I18N_FR = {
|
|
|
890
980
|
FILE_SAVE_ERROR_TOAST: "Erreur lors de l'enregistrement",
|
|
891
981
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures \xE9crites dans cypress/fixtures",
|
|
892
982
|
FIXTURES_NO_FOLDER_TOAST: "Impossible d'\xE9crire les fixtures (configure le dossier Cypress)",
|
|
983
|
+
FILE_UPLOAD_MANUAL_TOAST: "Ajoutez {files} \xE0 cypress/fixtures/ avant d'ex\xE9cuter le test.",
|
|
984
|
+
// TODO: translate
|
|
985
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copi\xE9(s) dans cypress/fixtures/.",
|
|
986
|
+
// TODO: translate
|
|
987
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Impossible de copier {files} vers cypress/fixtures/. Ajoutez-le manuellement.",
|
|
988
|
+
// TODO: translate
|
|
893
989
|
BTN_CONFIG: "Config",
|
|
894
990
|
BTN_FILES: "Fichiers",
|
|
895
991
|
BTN_COMMANDS: "Commandes",
|
|
@@ -937,7 +1033,7 @@ var I18N_FR = {
|
|
|
937
1033
|
SEC_PANELS: "\u{1F5C2} Panneaux",
|
|
938
1034
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 enregistr\xE9s : filtrer par tag, copier, combiner, exporter.",
|
|
939
1035
|
PANEL_COMMANDS: "\u2328\uFE0F Commandes \u2014 enregistrement en cours : r\xE9ordonner, supprimer, ajouter des assertions.",
|
|
940
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 langue, s\xE9lecteur, HTTP, dossier Cypress, donn\xE9es, visibilit\xE9, position.",
|
|
1036
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 langue, s\xE9lecteur, HTTP, dossier Cypress, login setup, donn\xE9es, visibilit\xE9, position.",
|
|
941
1037
|
PANEL_FILES: "\u{1F4C1} Fichiers \u2014 ins\xE8re des tests dans tes .cy.ts et lance le runner.",
|
|
942
1038
|
SEC_SELECTOR: "\u{1F50E} S\xE9lecteurs",
|
|
943
1039
|
SELECTOR_1: "Choisis l'attribut pr\xE9f\xE9r\xE9 (data-cy, data-testid, aria-label, id) dans Config.",
|
|
@@ -950,6 +1046,8 @@ var I18N_FR = {
|
|
|
950
1046
|
ADV_RUNNER: "Runner local : npx lib-e2e-cypress-runner pour lancer un spec headless depuis l'\xE9diteur.",
|
|
951
1047
|
ADV_CROSSAPP: "Cross-app : l'enregistrement survit au changement de micro-frontend (m\xEAme origine).",
|
|
952
1048
|
ADV_DRAG: "Fais glisser le bouton \u23FA pour d\xE9placer le widget ; sa position est m\xE9moris\xE9e.",
|
|
1049
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
1050
|
+
// TODO: translate (fr)
|
|
953
1051
|
CLOSE: "Fermer",
|
|
954
1052
|
TAB_REFERENCE: "R\xE9f\xE9rence rapide",
|
|
955
1053
|
TAB_GUIDE: "Guide d'utilisation",
|
|
@@ -968,13 +1066,14 @@ var I18N_FR = {
|
|
|
968
1066
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
969
1067
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
970
1068
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
1069
|
+
G_COV_7: "Login Setup: configura tu funci\xF3n de login en Ctrl+3 \u2192 Login Setup y el grabador a\xF1ade los bloques before()/beforeEach() a cada test nuevo.",
|
|
1070
|
+
// TODO: translate (fr)
|
|
971
1071
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
972
1072
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
973
1073
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
974
1074
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
975
1075
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
976
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
977
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
1076
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
978
1077
|
}
|
|
979
1078
|
};
|
|
980
1079
|
|
|
@@ -1005,8 +1104,11 @@ var I18N_IT = {
|
|
|
1005
1104
|
NOTES_PLACEHOLDER: "Cosa valida questo test, condizioni preliminari, ecc.",
|
|
1006
1105
|
TICKET_LABEL: "Ticket / Issue (opzionale):",
|
|
1007
1106
|
TICKET_PLACEHOLDER: "Es: PROJ-123, #42",
|
|
1008
|
-
TICKET_WARN: "ID non valido per il provider configurato (verr\xE0 salvato comunque)"
|
|
1107
|
+
TICKET_WARN: "ID non valido per il provider configurato (verr\xE0 salvato comunque)",
|
|
1009
1108
|
// TODO: translate
|
|
1109
|
+
CONFIRM_DISCARD: "Sei sicuro di voler scartare questa registrazione? Tutti i passi registrati andranno persi.",
|
|
1110
|
+
CONFIRM_DISCARD_BTN: "S\xEC, scarta",
|
|
1111
|
+
BACK_BTN: "Indietro"
|
|
1010
1112
|
},
|
|
1011
1113
|
TEST_EDITOR: {
|
|
1012
1114
|
NO_TAGS: "Nessuna etichetta",
|
|
@@ -1088,7 +1190,32 @@ var I18N_IT = {
|
|
|
1088
1190
|
ISSUE_TRACKER_SUB: "Aggiungi un ID ticket al salvataggio e genera un link cliccabile nell'elenco.",
|
|
1089
1191
|
ISSUE_TRACKER_PROVIDER: "Provider",
|
|
1090
1192
|
ISSUE_TRACKER_BASE_URL: "URL base",
|
|
1091
|
-
ISSUE_TRACKER_BASE_URL_PH: "Es: https://mioprogetto.atlassian.net"
|
|
1193
|
+
ISSUE_TRACKER_BASE_URL_PH: "Es: https://mioprogetto.atlassian.net",
|
|
1194
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
1195
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Non configurato",
|
|
1196
|
+
LOGIN_SETUP_SET_UP_BTN: "Configura",
|
|
1197
|
+
LOGIN_SETUP_TITLE: "Configurazione del login",
|
|
1198
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Crea scaffold",
|
|
1199
|
+
LOGIN_SETUP_SELECT_EXISTING: "Seleziona file esistente",
|
|
1200
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nome del file",
|
|
1201
|
+
LOGIN_SETUP_FILE_NAME_PH: "Es: cypress/common-services/login.service.ts",
|
|
1202
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scansiona",
|
|
1203
|
+
LOGIN_SETUP_FUNCTIONS_DETECTED: "Funzioni rilevate",
|
|
1204
|
+
LOGIN_SETUP_BLOCK_ASSIGN: "Assegnazione dei blocchi",
|
|
1205
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Nessuna funzione esportata trovata. Compila lo scaffold e ri-scansiona.",
|
|
1206
|
+
LOGIN_SETUP_BEFORE_LABEL: "Funzione before() (una volta per suite)",
|
|
1207
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Funzione beforeEach() (prima di ogni test)",
|
|
1208
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Nessuna \u2014",
|
|
1209
|
+
LOGIN_SETUP_SUMMARY: "File: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
1210
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Salva",
|
|
1211
|
+
LOGIN_SETUP_CLEAR_BTN: "Rimuovi configurazione",
|
|
1212
|
+
LOGIN_SETUP_CANCEL_BTN: "Annulla",
|
|
1213
|
+
LOGIN_SETUP_SOURCE_LABEL: "Sorgente del file di login",
|
|
1214
|
+
LOGIN_SETUP_CREATE_SUB: "Genera uno scaffold con funzioni di esempio",
|
|
1215
|
+
LOGIN_SETUP_SELECT_SUB: "Punta a un file di servizio di login esistente",
|
|
1216
|
+
LOGIN_SETUP_INJECT_TITLE: "Aggiungere i blocchi di login?",
|
|
1217
|
+
LOGIN_SETUP_INJECT_TEXT: "Questo file non contiene i blocchi di login configurati. Vuoi aggiungere l'import e i blocchi before/beforeEach ora?",
|
|
1218
|
+
LOGIN_SETUP_INJECT_CONFIRM: "S\xEC, aggiungi"
|
|
1092
1219
|
},
|
|
1093
1220
|
SELECTOR_PICKER: {
|
|
1094
1221
|
TITLE: "Seleziona un elemento DOM",
|
|
@@ -1179,6 +1306,12 @@ var I18N_IT = {
|
|
|
1179
1306
|
FILE_SAVE_ERROR_TOAST: "Errore durante il salvataggio",
|
|
1180
1307
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixture scritte in cypress/fixtures",
|
|
1181
1308
|
FIXTURES_NO_FOLDER_TOAST: "Impossibile scrivere le fixture (configura la cartella Cypress)",
|
|
1309
|
+
FILE_UPLOAD_MANUAL_TOAST: "Aggiungi {files} a cypress/fixtures/ prima di eseguire il test.",
|
|
1310
|
+
// TODO: translate
|
|
1311
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copiato/i in cypress/fixtures/.",
|
|
1312
|
+
// TODO: translate
|
|
1313
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Impossibile copiare {files} in cypress/fixtures/. Aggiungilo manualmente.",
|
|
1314
|
+
// TODO: translate
|
|
1182
1315
|
BTN_CONFIG: "Config",
|
|
1183
1316
|
BTN_FILES: "File",
|
|
1184
1317
|
BTN_COMMANDS: "Comandi",
|
|
@@ -1226,7 +1359,7 @@ var I18N_IT = {
|
|
|
1226
1359
|
SEC_PANELS: "\u{1F5C2} Pannelli",
|
|
1227
1360
|
PANEL_TESTS: "\u{1F4CB} Test \u2014 salvati: filtra per tag, copia, combina, esporta.",
|
|
1228
1361
|
PANEL_COMMANDS: "\u2328\uFE0F Comandi \u2014 registrazione corrente: riordina, elimina, aggiungi asserzioni.",
|
|
1229
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 lingua, selettore, HTTP, cartella Cypress, dati, visibilit\xE0, posizione.",
|
|
1362
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 lingua, selettore, HTTP, cartella Cypress, login setup, dati, visibilit\xE0, posizione.",
|
|
1230
1363
|
PANEL_FILES: "\u{1F4C1} File \u2014 inserisci test nei tuoi .cy.ts e avvia il runner.",
|
|
1231
1364
|
SEC_SELECTOR: "\u{1F50E} Selettori",
|
|
1232
1365
|
SELECTOR_1: "Scegli l'attributo preferito (data-cy, data-testid, aria-label, id) in Config.",
|
|
@@ -1239,6 +1372,8 @@ var I18N_IT = {
|
|
|
1239
1372
|
ADV_RUNNER: "Runner locale: npx lib-e2e-cypress-runner per eseguire uno spec headless dall'editor.",
|
|
1240
1373
|
ADV_CROSSAPP: "Cross-app: la registrazione sopravvive al cambio di micro-frontend (stessa origine).",
|
|
1241
1374
|
ADV_DRAG: "Trascina il pulsante \u23FA per spostare il widget; la posizione viene ricordata.",
|
|
1375
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
1376
|
+
// TODO: translate (it)
|
|
1242
1377
|
CLOSE: "Chiudi",
|
|
1243
1378
|
TAB_REFERENCE: "Riferimento rapido",
|
|
1244
1379
|
TAB_GUIDE: "Guida all'uso",
|
|
@@ -1257,13 +1392,14 @@ var I18N_IT = {
|
|
|
1257
1392
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
1258
1393
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
1259
1394
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
1395
|
+
G_COV_7: "Login Setup: configura tu funci\xF3n de login en Ctrl+3 \u2192 Login Setup y el grabador a\xF1ade los bloques before()/beforeEach() a cada test nuevo.",
|
|
1396
|
+
// TODO: translate (it)
|
|
1260
1397
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
1261
1398
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
1262
1399
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
1263
1400
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
1264
1401
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
1265
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1266
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
1402
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1267
1403
|
}
|
|
1268
1404
|
};
|
|
1269
1405
|
|
|
@@ -1294,8 +1430,11 @@ var I18N_DE = {
|
|
|
1294
1430
|
NOTES_PLACEHOLDER: "Was dieser Test validiert, Voraussetzungen usw.",
|
|
1295
1431
|
TICKET_LABEL: "Ticket / Issue (optional):",
|
|
1296
1432
|
TICKET_PLACEHOLDER: "Z. B.: PROJ-123, #42",
|
|
1297
|
-
TICKET_WARN: "Ung\xFCltige ID f\xFCr den konfigurierten Anbieter (wird trotzdem gespeichert)"
|
|
1433
|
+
TICKET_WARN: "Ung\xFCltige ID f\xFCr den konfigurierten Anbieter (wird trotzdem gespeichert)",
|
|
1298
1434
|
// TODO: translate
|
|
1435
|
+
CONFIRM_DISCARD: "Sind Sie sicher, dass Sie diese Aufzeichnung verwerfen m\xF6chten? Alle aufgezeichneten Schritte gehen verloren.",
|
|
1436
|
+
CONFIRM_DISCARD_BTN: "Ja, verwerfen",
|
|
1437
|
+
BACK_BTN: "Zur\xFCck"
|
|
1299
1438
|
},
|
|
1300
1439
|
TEST_EDITOR: {
|
|
1301
1440
|
NO_TAGS: "Keine Tags",
|
|
@@ -1377,7 +1516,32 @@ var I18N_DE = {
|
|
|
1377
1516
|
ISSUE_TRACKER_SUB: "F\xFCge beim Speichern eine Ticket-ID hinzu und erstelle einen klickbaren Link.",
|
|
1378
1517
|
ISSUE_TRACKER_PROVIDER: "Anbieter",
|
|
1379
1518
|
ISSUE_TRACKER_BASE_URL: "Basis-URL",
|
|
1380
|
-
ISSUE_TRACKER_BASE_URL_PH: "Z. B.: https://meinprojekt.atlassian.net"
|
|
1519
|
+
ISSUE_TRACKER_BASE_URL_PH: "Z. B.: https://meinprojekt.atlassian.net",
|
|
1520
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
1521
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Nicht konfiguriert",
|
|
1522
|
+
LOGIN_SETUP_SET_UP_BTN: "Einrichten",
|
|
1523
|
+
LOGIN_SETUP_TITLE: "Login-Konfiguration",
|
|
1524
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Scaffold erstellen",
|
|
1525
|
+
LOGIN_SETUP_SELECT_EXISTING: "Vorhandene Datei ausw\xE4hlen",
|
|
1526
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Dateiname",
|
|
1527
|
+
LOGIN_SETUP_FILE_NAME_PH: "Z. B.: cypress/common-services/login.service.ts",
|
|
1528
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Erneut scannen",
|
|
1529
|
+
LOGIN_SETUP_FUNCTIONS_DETECTED: "Erkannte Funktionen",
|
|
1530
|
+
LOGIN_SETUP_BLOCK_ASSIGN: "Blockzuweisung",
|
|
1531
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Keine exportierten Funktionen gefunden. Scaffold ausf\xFCllen und erneut scannen.",
|
|
1532
|
+
LOGIN_SETUP_BEFORE_LABEL: "before()-Funktion (einmal pro Suite)",
|
|
1533
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "beforeEach()-Funktion (vor jedem Test)",
|
|
1534
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Keine \u2014",
|
|
1535
|
+
LOGIN_SETUP_SUMMARY: "Datei: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
1536
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Speichern",
|
|
1537
|
+
LOGIN_SETUP_CLEAR_BTN: "Konfiguration entfernen",
|
|
1538
|
+
LOGIN_SETUP_CANCEL_BTN: "Abbrechen",
|
|
1539
|
+
LOGIN_SETUP_SOURCE_LABEL: "Quelle der Login-Datei",
|
|
1540
|
+
LOGIN_SETUP_CREATE_SUB: "Scaffold mit Beispielfunktionen erstellen",
|
|
1541
|
+
LOGIN_SETUP_SELECT_SUB: "Auf eine vorhandene Login-Service-Datei verweisen",
|
|
1542
|
+
LOGIN_SETUP_INJECT_TITLE: "Login-Bl\xF6cke hinzuf\xFCgen?",
|
|
1543
|
+
LOGIN_SETUP_INJECT_TEXT: "Diese Datei enth\xE4lt die konfigurierten Login-Bl\xF6cke nicht. M\xF6chten Sie jetzt den Import und die before/beforeEach-Bl\xF6cke hinzuf\xFCgen?",
|
|
1544
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Ja, hinzuf\xFCgen"
|
|
1381
1545
|
},
|
|
1382
1546
|
SELECTOR_PICKER: {
|
|
1383
1547
|
TITLE: "DOM-Element ausw\xE4hlen",
|
|
@@ -1468,6 +1632,12 @@ var I18N_DE = {
|
|
|
1468
1632
|
FILE_SAVE_ERROR_TOAST: "Fehler beim Speichern",
|
|
1469
1633
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures in cypress/fixtures geschrieben",
|
|
1470
1634
|
FIXTURES_NO_FOLDER_TOAST: "Fixtures konnten nicht geschrieben werden (Cypress-Ordner konfigurieren)",
|
|
1635
|
+
FILE_UPLOAD_MANUAL_TOAST: "F\xFCge {files} zu cypress/fixtures/ hinzu, bevor du den Test ausf\xFChrst.",
|
|
1636
|
+
// TODO: translate
|
|
1637
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} nach cypress/fixtures/ kopiert.",
|
|
1638
|
+
// TODO: translate
|
|
1639
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "{files} konnte nicht nach cypress/fixtures/ kopiert werden. F\xFCge es manuell hinzu.",
|
|
1640
|
+
// TODO: translate
|
|
1471
1641
|
BTN_CONFIG: "Config",
|
|
1472
1642
|
BTN_FILES: "Dateien",
|
|
1473
1643
|
BTN_COMMANDS: "Befehle",
|
|
@@ -1515,7 +1685,7 @@ var I18N_DE = {
|
|
|
1515
1685
|
SEC_PANELS: "\u{1F5C2} Panels",
|
|
1516
1686
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 gespeichert: nach Tag filtern, kopieren, kombinieren, exportieren.",
|
|
1517
1687
|
PANEL_COMMANDS: "\u2328\uFE0F Befehle \u2014 aktuelle Aufzeichnung: umsortieren, l\xF6schen, Assertions hinzuf\xFCgen.",
|
|
1518
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 Sprache, Selektor, HTTP, Cypress-Ordner, Daten, Sichtbarkeit, Position.",
|
|
1688
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 Sprache, Selektor, HTTP, Cypress-Ordner, Login-Setup, Daten, Sichtbarkeit, Position.",
|
|
1519
1689
|
PANEL_FILES: "\u{1F4C1} Dateien \u2014 Tests in deine .cy.ts einf\xFCgen und den Runner starten.",
|
|
1520
1690
|
SEC_SELECTOR: "\u{1F50E} Selektoren",
|
|
1521
1691
|
SELECTOR_1: "W\xE4hle das bevorzugte Attribut (data-cy, data-testid, aria-label, id) unter Config.",
|
|
@@ -1528,6 +1698,8 @@ var I18N_DE = {
|
|
|
1528
1698
|
ADV_RUNNER: "Lokaler Runner: npx lib-e2e-cypress-runner, um einen Spec headless aus dem Editor zu starten.",
|
|
1529
1699
|
ADV_CROSSAPP: "Cross-App: die Aufzeichnung \xFCbersteht den Wechsel des Micro-Frontends (gleicher Origin).",
|
|
1530
1700
|
ADV_DRAG: "Ziehe den \u23FA-Button, um das Widget zu verschieben; die Position wird gemerkt.",
|
|
1701
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
1702
|
+
// TODO: translate (de)
|
|
1531
1703
|
CLOSE: "Schlie\xDFen",
|
|
1532
1704
|
TAB_REFERENCE: "Kurzreferenz",
|
|
1533
1705
|
TAB_GUIDE: "Nutzungsleitfaden",
|
|
@@ -1546,13 +1718,14 @@ var I18N_DE = {
|
|
|
1546
1718
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
1547
1719
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
1548
1720
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
1721
|
+
G_COV_7: "Login Setup: configura tu funci\xF3n de login en Ctrl+3 \u2192 Login Setup y el grabador a\xF1ade los bloques before()/beforeEach() a cada test nuevo.",
|
|
1722
|
+
// TODO: translate (de)
|
|
1549
1723
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
1550
1724
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
1551
1725
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
1552
1726
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
1553
1727
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
1554
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1555
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
1728
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1556
1729
|
}
|
|
1557
1730
|
};
|
|
1558
1731
|
|
|
@@ -1595,33 +1768,6 @@ var TranslationService = class {
|
|
|
1595
1768
|
};
|
|
1596
1769
|
var translationService = new TranslationService();
|
|
1597
1770
|
|
|
1598
|
-
// src/utils/code-format.utils.ts
|
|
1599
|
-
function gcd(a, b) {
|
|
1600
|
-
while (b) {
|
|
1601
|
-
const t = b;
|
|
1602
|
-
b = a % b;
|
|
1603
|
-
a = t;
|
|
1604
|
-
}
|
|
1605
|
-
return a;
|
|
1606
|
-
}
|
|
1607
|
-
function escapeSingleQuotes(value) {
|
|
1608
|
-
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1609
|
-
}
|
|
1610
|
-
function normalizeBlock(code, baseIndent) {
|
|
1611
|
-
const lines = code.split("\n").map((l) => l.trimEnd());
|
|
1612
|
-
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
1613
|
-
if (!nonEmpty.length) return "";
|
|
1614
|
-
const indents = nonEmpty.map((l) => l.length - l.trimStart().length);
|
|
1615
|
-
const minIndent = Math.min(...indents);
|
|
1616
|
-
const relIndents = [...new Set(indents.map((n) => n - minIndent).filter((n) => n > 0))];
|
|
1617
|
-
const step = relIndents.length > 0 ? relIndents.reduce(gcd) : 2;
|
|
1618
|
-
return lines.map((l) => {
|
|
1619
|
-
if (!l.trim()) return "";
|
|
1620
|
-
const level = Math.round((l.length - l.trimStart().length - minIndent) / step);
|
|
1621
|
-
return baseIndent + " ".repeat(level) + l.trimStart();
|
|
1622
|
-
}).join("\n");
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
1771
|
// src/services/transformation.service.ts
|
|
1626
1772
|
var TransformationService = class {
|
|
1627
1773
|
toLang(lang) {
|
|
@@ -1686,7 +1832,8 @@ var advancedTestTransformationService = new AdvancedTestTransformationService();
|
|
|
1686
1832
|
// src/utils/assertion.utils.ts
|
|
1687
1833
|
var MAX_TEXT_LENGTH = 60;
|
|
1688
1834
|
function inferAssertionCommand(el, selector) {
|
|
1689
|
-
const
|
|
1835
|
+
const safeSelector = escapeSingleQuotes(selector);
|
|
1836
|
+
const should = (body) => `cy.get('${safeSelector}').should(${body})`;
|
|
1690
1837
|
const tag = el.tagName.toLowerCase();
|
|
1691
1838
|
if (tag === "input") {
|
|
1692
1839
|
const input = el;
|
|
@@ -1723,6 +1870,7 @@ var RecordingService = class {
|
|
|
1723
1870
|
selectorNotFound$ = new Subject(null);
|
|
1724
1871
|
inputDebounceTimers = /* @__PURE__ */ new Map();
|
|
1725
1872
|
fixtures = /* @__PURE__ */ new Map();
|
|
1873
|
+
uploadedFiles = /* @__PURE__ */ new Map();
|
|
1726
1874
|
abort = new AbortController();
|
|
1727
1875
|
selectorStrategy = "data-cy";
|
|
1728
1876
|
/** Stable id of the current live session (null when none has started). */
|
|
@@ -1749,7 +1897,7 @@ var RecordingService = class {
|
|
|
1749
1897
|
this.isPaused$.next(false);
|
|
1750
1898
|
this.isRecording$.next(true);
|
|
1751
1899
|
this.addCommand(`cy.viewport(1900, 1200)`);
|
|
1752
|
-
this.addCommand(`cy.visit('${window.location.pathname}')`);
|
|
1900
|
+
this.addCommand(`cy.visit('${escapeSingleQuotes(window.location.pathname)}')`);
|
|
1753
1901
|
this.addCommand(`cy.get('[data-cy="lib-e2e-cypress-for-dummys"]').invoke('hide');`);
|
|
1754
1902
|
}
|
|
1755
1903
|
/**
|
|
@@ -1832,8 +1980,8 @@ var RecordingService = class {
|
|
|
1832
1980
|
}
|
|
1833
1981
|
registerInterceptor(method, url, alias, fixtureFile) {
|
|
1834
1982
|
if (!this.isRecording$.getValue() || this.isPaused$.getValue()) return;
|
|
1835
|
-
const wildcard = this.urlToWildcard(url, method);
|
|
1836
|
-
const command = fixtureFile ? `cy.intercept('${method}', '${wildcard}', { fixture: '${fixtureFile}' }).as('${alias}')` : `cy.intercept('${method}', '${wildcard}').as('${alias}')`;
|
|
1983
|
+
const wildcard = escapeSingleQuotes(this.urlToWildcard(url, method));
|
|
1984
|
+
const command = fixtureFile ? `cy.intercept('${method}', '${wildcard}', { fixture: '${escapeSingleQuotes(fixtureFile)}' }).as('${alias}')` : `cy.intercept('${method}', '${wildcard}').as('${alias}')`;
|
|
1837
1985
|
const current = this.interceptors$.getValue();
|
|
1838
1986
|
if (!current.includes(command)) {
|
|
1839
1987
|
this.interceptors$.next([...current, command]);
|
|
@@ -1847,10 +1995,20 @@ var RecordingService = class {
|
|
|
1847
1995
|
getFixturesSnapshot() {
|
|
1848
1996
|
return [...this.fixtures.entries()].map(([name, content]) => ({ name, content }));
|
|
1849
1997
|
}
|
|
1998
|
+
addUploadedFile(filename, bytes) {
|
|
1999
|
+
this.uploadedFiles.set(filename, bytes);
|
|
2000
|
+
}
|
|
2001
|
+
getUploadedFilesSnapshot() {
|
|
2002
|
+
return [...this.uploadedFiles.entries()].map(([filename, bytes]) => ({ filename, bytes }));
|
|
2003
|
+
}
|
|
2004
|
+
clearUploadedFiles() {
|
|
2005
|
+
this.uploadedFiles.clear();
|
|
2006
|
+
}
|
|
1850
2007
|
clearCommands() {
|
|
1851
2008
|
this.commands$.next([]);
|
|
1852
2009
|
this.interceptors$.next([]);
|
|
1853
2010
|
this.fixtures.clear();
|
|
2011
|
+
this.uploadedFiles.clear();
|
|
1854
2012
|
}
|
|
1855
2013
|
clearInterceptors() {
|
|
1856
2014
|
this.interceptors$.next([]);
|
|
@@ -1970,7 +2128,8 @@ var RecordingService = class {
|
|
|
1970
2128
|
if (e.altKey) return;
|
|
1971
2129
|
const selector = this.resolveSelectorFor(e.target);
|
|
1972
2130
|
if (!selector) return;
|
|
1973
|
-
const
|
|
2131
|
+
const safeSelector = escapeSingleQuotes(selector);
|
|
2132
|
+
const clickCmd = `cy.get('${safeSelector}').click()`;
|
|
1974
2133
|
let cmds = this.commands$.getValue();
|
|
1975
2134
|
let removed = 0;
|
|
1976
2135
|
while (removed < 2 && cmds.length > 0 && cmds[cmds.length - 1] === clickCmd) {
|
|
@@ -1978,7 +2137,7 @@ var RecordingService = class {
|
|
|
1978
2137
|
removed++;
|
|
1979
2138
|
}
|
|
1980
2139
|
if (removed > 0) this.commands$.next(cmds);
|
|
1981
|
-
this.addCommand(`cy.get('${
|
|
2140
|
+
this.addCommand(`cy.get('${safeSelector}').dblclick()`);
|
|
1982
2141
|
},
|
|
1983
2142
|
{ signal: this.abort.signal }
|
|
1984
2143
|
);
|
|
@@ -1990,7 +2149,7 @@ var RecordingService = class {
|
|
|
1990
2149
|
if (!this.isRecording$.getValue() || this.isPaused$.getValue()) return;
|
|
1991
2150
|
const selector = this.resolveSelectorFor(e.target);
|
|
1992
2151
|
if (!selector) return;
|
|
1993
|
-
this.addCommand(`cy.get('${selector}').rightclick()`);
|
|
2152
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').rightclick()`);
|
|
1994
2153
|
},
|
|
1995
2154
|
{ signal: this.abort.signal }
|
|
1996
2155
|
);
|
|
@@ -2010,7 +2169,7 @@ var RecordingService = class {
|
|
|
2010
2169
|
if (!selector) return;
|
|
2011
2170
|
this.flushInputDebounce(target);
|
|
2012
2171
|
const token = key === "Enter" ? "{enter}" : "{esc}";
|
|
2013
|
-
this.addCommand(`cy.get('${selector}').type('${token}')`);
|
|
2172
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').type('${token}')`);
|
|
2014
2173
|
},
|
|
2015
2174
|
{ signal: this.abort.signal }
|
|
2016
2175
|
);
|
|
@@ -2034,19 +2193,52 @@ var RecordingService = class {
|
|
|
2034
2193
|
if (!this.isRecording$.getValue()) return;
|
|
2035
2194
|
const target = e.target;
|
|
2036
2195
|
if (!target || this.isOwnElement(target)) return;
|
|
2037
|
-
|
|
2196
|
+
const tag = target.tagName.toLowerCase();
|
|
2197
|
+
if (tag === "select") {
|
|
2038
2198
|
this.handleSelectEvent(target);
|
|
2199
|
+
} else if (tag === "input") {
|
|
2200
|
+
const type = (target.getAttribute("type") ?? "").toLowerCase();
|
|
2201
|
+
if (type === "file") {
|
|
2202
|
+
void this.handleFileInputChange(target);
|
|
2203
|
+
}
|
|
2039
2204
|
}
|
|
2040
2205
|
},
|
|
2041
2206
|
{ signal: this.abort.signal }
|
|
2042
2207
|
);
|
|
2043
2208
|
}
|
|
2209
|
+
async handleFileInputChange(target) {
|
|
2210
|
+
if (this.isPaused$.getValue()) return;
|
|
2211
|
+
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2212
|
+
if (!container) return;
|
|
2213
|
+
const selector = this.getReliableSelector(container);
|
|
2214
|
+
if (!selector || this.isOwnSelector(selector)) return;
|
|
2215
|
+
const files = Array.from(target.files ?? []);
|
|
2216
|
+
if (!files.length) return;
|
|
2217
|
+
const paths = files.map((f) => `'cypress/fixtures/${escapeSingleQuotes(f.name)}'`);
|
|
2218
|
+
const pathArg = paths.length === 1 ? paths[0] : `[${paths.join(", ")}]`;
|
|
2219
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').selectFile(${pathArg})`);
|
|
2220
|
+
for (const file of files) {
|
|
2221
|
+
const bytes = await this.readFileAsArrayBuffer(file);
|
|
2222
|
+
this.addUploadedFile(file.name, bytes);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
readFileAsArrayBuffer(file) {
|
|
2226
|
+
if (typeof file.arrayBuffer === "function") {
|
|
2227
|
+
return file.arrayBuffer();
|
|
2228
|
+
}
|
|
2229
|
+
return new Promise((resolve, reject) => {
|
|
2230
|
+
const reader = new FileReader();
|
|
2231
|
+
reader.onload = () => resolve(reader.result);
|
|
2232
|
+
reader.onerror = () => reject(reader.error);
|
|
2233
|
+
reader.readAsArrayBuffer(file);
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2044
2236
|
listenToRouteChanges() {
|
|
2045
2237
|
let lastUrl = window.location.pathname + window.location.search + window.location.hash;
|
|
2046
2238
|
const addUrlCommand = (newUrl) => {
|
|
2047
2239
|
if (!this.isRecording$.getValue()) return;
|
|
2048
2240
|
if (newUrl === lastUrl) return;
|
|
2049
|
-
this.addCommand(`cy.url().should('include', '${newUrl}')`);
|
|
2241
|
+
this.addCommand(`cy.url().should('include', '${escapeSingleQuotes(newUrl)}')`);
|
|
2050
2242
|
lastUrl = newUrl;
|
|
2051
2243
|
};
|
|
2052
2244
|
const wrapMethod = (type) => {
|
|
@@ -2147,7 +2339,8 @@ var RecordingService = class {
|
|
|
2147
2339
|
if (matSelect) dataCy = matSelect.closest("[data-cy]")?.getAttribute("data-cy") ?? null;
|
|
2148
2340
|
if (!dataCy) dataCy = target.closest("[data-cy]")?.getAttribute("data-cy") ?? null;
|
|
2149
2341
|
if (dataCy) {
|
|
2150
|
-
|
|
2342
|
+
const safeSel = escapeSingleQuotes(`[data-cy="${escapeCssAttrValue(dataCy)}"]`);
|
|
2343
|
+
this.addCommand(`cy.get('${safeSel}').click()`);
|
|
2151
2344
|
return;
|
|
2152
2345
|
}
|
|
2153
2346
|
if (selector) {
|
|
@@ -2179,7 +2372,7 @@ var RecordingService = class {
|
|
|
2179
2372
|
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2180
2373
|
if (!container) return;
|
|
2181
2374
|
const selector = this.getReliableSelector(container);
|
|
2182
|
-
const value = target.value
|
|
2375
|
+
const value = escapeSingleQuotes(target.value);
|
|
2183
2376
|
this.addGenericCommand({
|
|
2184
2377
|
selector,
|
|
2185
2378
|
action: (s) => `cy.get('${s}').clear().type('${value}')`
|
|
@@ -2198,7 +2391,7 @@ var RecordingService = class {
|
|
|
2198
2391
|
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2199
2392
|
if (!container) return;
|
|
2200
2393
|
const selector = this.getReliableSelector(container);
|
|
2201
|
-
const value = target.value
|
|
2394
|
+
const value = escapeSingleQuotes(target.value);
|
|
2202
2395
|
this.addGenericCommand({
|
|
2203
2396
|
selector,
|
|
2204
2397
|
action: (s) => `cy.get('${s}').select('${value}')`
|
|
@@ -2207,7 +2400,7 @@ var RecordingService = class {
|
|
|
2207
2400
|
// ── Shared helpers ────────────────────────────────────────────────────────
|
|
2208
2401
|
addGenericCommand(opts) {
|
|
2209
2402
|
if (!opts.selector || this.isOwnSelector(opts.selector)) return;
|
|
2210
|
-
this.addCommand(opts.action(opts.selector));
|
|
2403
|
+
this.addCommand(opts.action(escapeSingleQuotes(opts.selector)));
|
|
2211
2404
|
}
|
|
2212
2405
|
getReliableSelector(el) {
|
|
2213
2406
|
const strategy = this.selectorStrategy;
|
|
@@ -2219,20 +2412,20 @@ var RecordingService = class {
|
|
|
2219
2412
|
const validId = rawId && rawId.length < 25 && /^[a-zA-Z][\w-]*$/.test(rawId) && !FORBIDDEN_ID_PREFIXES.some((p) => rawId.startsWith(p)) && !/^\d+$/.test(rawId) ? rawId : null;
|
|
2220
2413
|
switch (strategy) {
|
|
2221
2414
|
case "data-testid":
|
|
2222
|
-
if (dataTestid) return `[data-testid="${dataTestid}"]`;
|
|
2223
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2415
|
+
if (dataTestid) return `[data-testid="${escapeCssAttrValue(dataTestid)}"]`;
|
|
2416
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2224
2417
|
return validId ? `#${validId}` : null;
|
|
2225
2418
|
case "aria-label":
|
|
2226
|
-
if (ariaLabel) return `[aria-label="${ariaLabel}"]`;
|
|
2227
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2419
|
+
if (ariaLabel) return `[aria-label="${escapeCssAttrValue(ariaLabel)}"]`;
|
|
2420
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2228
2421
|
return validId ? `#${validId}` : null;
|
|
2229
2422
|
case "id":
|
|
2230
2423
|
if (validId) return `#${validId}`;
|
|
2231
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2232
|
-
return dataTestid ? `[data-testid="${dataTestid}"]` : null;
|
|
2424
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2425
|
+
return dataTestid ? `[data-testid="${escapeCssAttrValue(dataTestid)}"]` : null;
|
|
2233
2426
|
default:
|
|
2234
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2235
|
-
if (dataDotCy) return `[data.cy="${dataDotCy}"]`;
|
|
2427
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2428
|
+
if (dataDotCy) return `[data.cy="${escapeCssAttrValue(dataDotCy)}"]`;
|
|
2236
2429
|
return validId ? `#${validId}` : null;
|
|
2237
2430
|
}
|
|
2238
2431
|
}
|
|
@@ -2258,8 +2451,40 @@ var RecordingService = class {
|
|
|
2258
2451
|
|
|
2259
2452
|
// src/services/persistence.service.ts
|
|
2260
2453
|
import { openDB } from "idb";
|
|
2454
|
+
|
|
2455
|
+
// src/models/issue-tracker.model.ts
|
|
2456
|
+
var ISSUE_TRACKER_PRESETS = {
|
|
2457
|
+
jira: { id: "jira", label: "Jira", urlPattern: "{baseUrl}/browse/{id}", idPattern: /^[A-Z][A-Z0-9]+-\d+$/ },
|
|
2458
|
+
github: { id: "github", label: "GitHub Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
2459
|
+
gitlab: { id: "gitlab", label: "GitLab Issues", urlPattern: "{baseUrl}/-/issues/{id}", idPattern: /^\d+$/ },
|
|
2460
|
+
azure: { id: "azure", label: "Azure DevOps", urlPattern: "{baseUrl}/_workitems/edit/{id}", idPattern: /^\d+$/ },
|
|
2461
|
+
bitbucket: { id: "bitbucket", label: "Bitbucket Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
2462
|
+
custom: { id: "custom", label: "Custom", urlPattern: "{baseUrl}/{id}", idPattern: /.+/ }
|
|
2463
|
+
};
|
|
2464
|
+
var DEFAULT_ISSUE_TRACKER_CONFIG = {
|
|
2465
|
+
enabled: false,
|
|
2466
|
+
provider: "jira",
|
|
2467
|
+
baseUrl: ""
|
|
2468
|
+
};
|
|
2469
|
+
|
|
2470
|
+
// src/utils/ticket.utils.ts
|
|
2471
|
+
function buildTicketUrl(ticketId, config) {
|
|
2472
|
+
if (!config.enabled || !ticketId.trim() || !config.baseUrl.trim()) return null;
|
|
2473
|
+
const preset = ISSUE_TRACKER_PRESETS[config.provider];
|
|
2474
|
+
const base = config.baseUrl.replace(/\/+$/, "");
|
|
2475
|
+
return preset.urlPattern.replace("{baseUrl}", base).replace("{id}", encodeURIComponent(ticketId.trim()));
|
|
2476
|
+
}
|
|
2477
|
+
function buildTicketComment(ticketId) {
|
|
2478
|
+
return `// Ticket: ${ticketId.trim()}`;
|
|
2479
|
+
}
|
|
2480
|
+
function isValidTicketId(ticketId, provider) {
|
|
2481
|
+
if (!ticketId.trim()) return false;
|
|
2482
|
+
return ISSUE_TRACKER_PRESETS[provider].idPattern.test(ticketId.trim());
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// src/services/persistence.service.ts
|
|
2261
2486
|
var ACTIVE_SESSION_ID = 1;
|
|
2262
|
-
var PersistenceService = class {
|
|
2487
|
+
var PersistenceService = class _PersistenceService {
|
|
2263
2488
|
// dbName is overridable so tests can use unique names for isolation.
|
|
2264
2489
|
constructor(dbName = DB_SCHEMA.name) {
|
|
2265
2490
|
this.dbName = dbName;
|
|
@@ -2312,7 +2537,8 @@ var PersistenceService = class {
|
|
|
2312
2537
|
if (!record) return null;
|
|
2313
2538
|
const commands = await this.getCommandStrings(testId);
|
|
2314
2539
|
const interceptors = await this.getInterceptorStrings(testId);
|
|
2315
|
-
const
|
|
2540
|
+
const ticketComment = record.ticketId ? buildTicketComment(record.ticketId) + "\n" : "";
|
|
2541
|
+
const itBlock = `${ticketComment}it('${escapeSingleQuotes(record.name)}', () => {
|
|
2316
2542
|
${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
2317
2543
|
});`;
|
|
2318
2544
|
const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => normalizeBlock(i, " ")).join("\n") + "\n" : "";
|
|
@@ -2393,6 +2619,30 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2393
2619
|
const records = await db.getAll("configuration");
|
|
2394
2620
|
return records[0] ?? null;
|
|
2395
2621
|
}
|
|
2622
|
+
// ── Login setup config ───────────────────────────────────────────────────
|
|
2623
|
+
static LOGIN_SETUP_KEY = "loginSetupConfig";
|
|
2624
|
+
async saveLoginSetup(config) {
|
|
2625
|
+
await this.setConfigKey(_PersistenceService.LOGIN_SETUP_KEY, JSON.stringify(config));
|
|
2626
|
+
}
|
|
2627
|
+
async getLoginSetup() {
|
|
2628
|
+
const record = await this.getConfig(_PersistenceService.LOGIN_SETUP_KEY);
|
|
2629
|
+
if (!record) return null;
|
|
2630
|
+
const raw = record[_PersistenceService.LOGIN_SETUP_KEY];
|
|
2631
|
+
if (typeof raw !== "string") return null;
|
|
2632
|
+
try {
|
|
2633
|
+
return JSON.parse(raw);
|
|
2634
|
+
} catch {
|
|
2635
|
+
return null;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
async clearLoginSetup() {
|
|
2639
|
+
const db = await this.getDB();
|
|
2640
|
+
const records = await db.getAll("configuration");
|
|
2641
|
+
if (!records.length) return;
|
|
2642
|
+
const current = { ...records[0] };
|
|
2643
|
+
delete current[_PersistenceService.LOGIN_SETUP_KEY];
|
|
2644
|
+
await db.put("configuration", current);
|
|
2645
|
+
}
|
|
2396
2646
|
// ── Active recording session ──────────────────────────────────────────────
|
|
2397
2647
|
/** Upserts the live recording session (single record, fixed key). */
|
|
2398
2648
|
async saveActiveSession(state) {
|
|
@@ -2435,6 +2685,24 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2435
2685
|
}
|
|
2436
2686
|
await this.bulkInsertWithoutId("interceptors", interceptors);
|
|
2437
2687
|
}
|
|
2688
|
+
async hasDirectoryAccess() {
|
|
2689
|
+
const config = await this.getGeneralConfig();
|
|
2690
|
+
return !!config?.["cypressDirectoryHandle"];
|
|
2691
|
+
}
|
|
2692
|
+
async writeUploadedFile(filename, bytes) {
|
|
2693
|
+
const config = await this.getGeneralConfig();
|
|
2694
|
+
const dirHandle = config?.["cypressDirectoryHandle"];
|
|
2695
|
+
if (!dirHandle) throw new Error("No Cypress folder configured");
|
|
2696
|
+
const perm = dirHandle;
|
|
2697
|
+
let state = await perm.queryPermission({ mode: "readwrite" });
|
|
2698
|
+
if (state !== "granted") state = await perm.requestPermission({ mode: "readwrite" });
|
|
2699
|
+
if (state !== "granted") throw new Error("Write permission denied");
|
|
2700
|
+
const fixturesDir = await dirHandle.getDirectoryHandle("fixtures", { create: true });
|
|
2701
|
+
const fileHandle = await fixturesDir.getFileHandle(filename, { create: true });
|
|
2702
|
+
const writable = await fileHandle.createWritable();
|
|
2703
|
+
await writable.write(bytes);
|
|
2704
|
+
await writable.close();
|
|
2705
|
+
}
|
|
2438
2706
|
async requestDirectoryPermissions() {
|
|
2439
2707
|
if (!("showDirectoryPicker" in window)) {
|
|
2440
2708
|
throw new Error("File System Access API not supported");
|
|
@@ -2477,7 +2745,54 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2477
2745
|
}
|
|
2478
2746
|
}
|
|
2479
2747
|
};
|
|
2480
|
-
|
|
2748
|
+
|
|
2749
|
+
// src/utils/redact.utils.ts
|
|
2750
|
+
var SENSITIVE_SUBSTRINGS = [
|
|
2751
|
+
"password",
|
|
2752
|
+
"passwd",
|
|
2753
|
+
"passphrase",
|
|
2754
|
+
"secret",
|
|
2755
|
+
"token",
|
|
2756
|
+
// access_token, refresh_token, id_token, csrf_token…
|
|
2757
|
+
"apikey",
|
|
2758
|
+
// api_key, x-api-key — NOT bare "key" (avoids "normalKey")
|
|
2759
|
+
"authorization",
|
|
2760
|
+
"cookie",
|
|
2761
|
+
"credential",
|
|
2762
|
+
"privatekey",
|
|
2763
|
+
"sessionid",
|
|
2764
|
+
"creditcard",
|
|
2765
|
+
"cardnumber",
|
|
2766
|
+
"cvv"
|
|
2767
|
+
];
|
|
2768
|
+
var SENSITIVE_EXACT = /* @__PURE__ */ new Set([
|
|
2769
|
+
"auth",
|
|
2770
|
+
"pwd",
|
|
2771
|
+
"pin",
|
|
2772
|
+
"otp",
|
|
2773
|
+
"ssn"
|
|
2774
|
+
]);
|
|
2775
|
+
function normalizeKey(key) {
|
|
2776
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2777
|
+
}
|
|
2778
|
+
function isSensitiveKey(key) {
|
|
2779
|
+
const normalized = normalizeKey(key);
|
|
2780
|
+
if (SENSITIVE_EXACT.has(normalized)) return true;
|
|
2781
|
+
return SENSITIVE_SUBSTRINGS.some((s) => normalized.includes(s));
|
|
2782
|
+
}
|
|
2783
|
+
function redactSensitiveFields(obj) {
|
|
2784
|
+
if (Array.isArray(obj)) {
|
|
2785
|
+
return obj.map(redactSensitiveFields);
|
|
2786
|
+
}
|
|
2787
|
+
if (obj !== null && typeof obj === "object") {
|
|
2788
|
+
const result = {};
|
|
2789
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2790
|
+
result[key] = isSensitiveKey(key) ? "[REDACTED]" : redactSensitiveFields(value);
|
|
2791
|
+
}
|
|
2792
|
+
return result;
|
|
2793
|
+
}
|
|
2794
|
+
return obj;
|
|
2795
|
+
}
|
|
2481
2796
|
|
|
2482
2797
|
// src/services/http-monitor.ts
|
|
2483
2798
|
var INTERCEPTED_METHODS = ["GET", "POST", "PUT"];
|
|
@@ -2490,8 +2805,12 @@ function generateAlias(method, url) {
|
|
|
2490
2805
|
return `${method.toLowerCase()}-intercepted-request`;
|
|
2491
2806
|
}
|
|
2492
2807
|
}
|
|
2808
|
+
var SAFE_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
2493
2809
|
function buildValidations(base, obj) {
|
|
2494
|
-
return Object.keys(obj).filter((key) => key !== "id" && key !== "uid").map((key) =>
|
|
2810
|
+
return Object.keys(obj).filter((key) => key !== "id" && key !== "uid").map((key) => {
|
|
2811
|
+
const accessor = SAFE_IDENTIFIER.test(key) ? `.${key}` : `['${escapeSingleQuotes(key)}']`;
|
|
2812
|
+
return `expect(${base}${accessor}).to.equal(${JSON.stringify(obj[key])});`;
|
|
2813
|
+
}).join("\n");
|
|
2495
2814
|
}
|
|
2496
2815
|
function buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody) {
|
|
2497
2816
|
if (extendedHttp && method === "GET" && responseBody) {
|
|
@@ -2543,20 +2862,143 @@ function resolveMethod(input, init) {
|
|
|
2543
2862
|
if (input instanceof Request) return input.method.toUpperCase();
|
|
2544
2863
|
return "GET";
|
|
2545
2864
|
}
|
|
2865
|
+
async function _handleFetchInterception(recording, input, init, responseClone) {
|
|
2866
|
+
const method = resolveMethod(input, init);
|
|
2867
|
+
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
2868
|
+
const url = resolveUrl(input);
|
|
2869
|
+
const alias = generateAlias(method, url);
|
|
2870
|
+
recording.registerInterceptor(method, url, alias);
|
|
2871
|
+
const extendedHttp = localStorage.getItem("extendedHttpCommands") === "true";
|
|
2872
|
+
const fixtureMode = localStorage.getItem("fixtureMode") === "true";
|
|
2873
|
+
const requestBody = extendedHttp ? parseRequestBody(init) : null;
|
|
2874
|
+
let responseText = null;
|
|
2875
|
+
let responseBody = null;
|
|
2876
|
+
if (extendedHttp || fixtureMode && method === "GET") {
|
|
2877
|
+
try {
|
|
2878
|
+
responseText = await responseClone.text();
|
|
2879
|
+
if (extendedHttp) responseBody = parseJsonObject(responseText);
|
|
2880
|
+
} catch {
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
if (fixtureMode && method === "GET" && responseText !== null) {
|
|
2884
|
+
const pretty = prettyJsonOrNull(responseText);
|
|
2885
|
+
if (pretty !== null) {
|
|
2886
|
+
const redacted = redactSensitiveFields(JSON.parse(pretty));
|
|
2887
|
+
recording.registerFixture(`${alias}.json`, JSON.stringify(redacted, null, 2));
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
const cmd = buildCyWaitCommand(
|
|
2891
|
+
method,
|
|
2892
|
+
alias,
|
|
2893
|
+
extendedHttp,
|
|
2894
|
+
requestBody ? redactSensitiveFields(requestBody) : null,
|
|
2895
|
+
responseBody ? redactSensitiveFields(responseBody) : null
|
|
2896
|
+
);
|
|
2897
|
+
recording.addCommand(cmd);
|
|
2898
|
+
}
|
|
2899
|
+
function _handleXhrInterception(recording, method, url, requestBody, responseText) {
|
|
2900
|
+
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
2901
|
+
const alias = generateAlias(method, url);
|
|
2902
|
+
recording.registerInterceptor(method, url, alias);
|
|
2903
|
+
if (localStorage.getItem("fixtureMode") === "true" && method === "GET") {
|
|
2904
|
+
const pretty = prettyJsonOrNull(responseText);
|
|
2905
|
+
if (pretty !== null) {
|
|
2906
|
+
const redacted = redactSensitiveFields(JSON.parse(pretty));
|
|
2907
|
+
recording.registerFixture(`${alias}.json`, JSON.stringify(redacted, null, 2));
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
const extendedHttp = localStorage.getItem("extendedHttpCommands") === "true";
|
|
2911
|
+
const responseBody = extendedHttp ? parseJsonObject(responseText) : null;
|
|
2912
|
+
const cmd = buildCyWaitCommand(
|
|
2913
|
+
method,
|
|
2914
|
+
alias,
|
|
2915
|
+
extendedHttp,
|
|
2916
|
+
requestBody ? redactSensitiveFields(requestBody) : null,
|
|
2917
|
+
responseBody ? redactSensitiveFields(responseBody) : null
|
|
2918
|
+
);
|
|
2919
|
+
recording.addCommand(cmd);
|
|
2920
|
+
}
|
|
2921
|
+
var _refCount = 0;
|
|
2922
|
+
var _originalFetch = null;
|
|
2923
|
+
var _originalXHR = null;
|
|
2924
|
+
var _recordings = /* @__PURE__ */ new Set();
|
|
2925
|
+
function _patchFetch() {
|
|
2926
|
+
const orig = window.fetch;
|
|
2927
|
+
_originalFetch = orig;
|
|
2928
|
+
window.fetch = async (input, init) => {
|
|
2929
|
+
const response = await orig(input, init);
|
|
2930
|
+
try {
|
|
2931
|
+
const active = [..._recordings];
|
|
2932
|
+
if (active.length > 0) {
|
|
2933
|
+
await Promise.all(
|
|
2934
|
+
active.map((rec) => _handleFetchInterception(rec, input, init, response.clone()).catch(() => {
|
|
2935
|
+
}))
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
} catch {
|
|
2939
|
+
}
|
|
2940
|
+
return response;
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
function _restoreFetch() {
|
|
2944
|
+
if (!_originalFetch) return;
|
|
2945
|
+
window.fetch = _originalFetch;
|
|
2946
|
+
_originalFetch = null;
|
|
2947
|
+
}
|
|
2948
|
+
function _patchXhr() {
|
|
2949
|
+
const OrigXHR = window.XMLHttpRequest;
|
|
2950
|
+
_originalXHR = OrigXHR;
|
|
2951
|
+
window.XMLHttpRequest = class extends OrigXHR {
|
|
2952
|
+
_xhrMethod = "GET";
|
|
2953
|
+
_xhrUrl = "";
|
|
2954
|
+
_xhrRequestBody = null;
|
|
2955
|
+
open(method, url, async = true, user, password) {
|
|
2956
|
+
this._xhrMethod = method.toUpperCase();
|
|
2957
|
+
this._xhrUrl = url instanceof URL ? url.href : url;
|
|
2958
|
+
super.open(method, url, async, user, password);
|
|
2959
|
+
}
|
|
2960
|
+
send(body) {
|
|
2961
|
+
if (typeof body === "string") {
|
|
2962
|
+
this._xhrRequestBody = parseJsonObject(body);
|
|
2963
|
+
}
|
|
2964
|
+
this.addEventListener("load", () => {
|
|
2965
|
+
try {
|
|
2966
|
+
for (const rec of _recordings) {
|
|
2967
|
+
_handleXhrInterception(rec, this._xhrMethod, this._xhrUrl, this._xhrRequestBody, this.responseText);
|
|
2968
|
+
}
|
|
2969
|
+
} catch {
|
|
2970
|
+
}
|
|
2971
|
+
});
|
|
2972
|
+
super.send(body);
|
|
2973
|
+
}
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
function _restoreXhr() {
|
|
2977
|
+
if (!_originalXHR) return;
|
|
2978
|
+
window.XMLHttpRequest = _originalXHR;
|
|
2979
|
+
_originalXHR = null;
|
|
2980
|
+
}
|
|
2546
2981
|
var HttpMonitor = class {
|
|
2547
2982
|
constructor(recording) {
|
|
2548
2983
|
this.recording = recording;
|
|
2549
2984
|
}
|
|
2550
2985
|
recording;
|
|
2551
|
-
originalFetch = null;
|
|
2552
|
-
originalXHR = null;
|
|
2553
2986
|
install() {
|
|
2554
|
-
this.
|
|
2555
|
-
|
|
2987
|
+
_recordings.add(this.recording);
|
|
2988
|
+
_refCount++;
|
|
2989
|
+
if (_refCount === 1) {
|
|
2990
|
+
_patchFetch();
|
|
2991
|
+
_patchXhr();
|
|
2992
|
+
}
|
|
2556
2993
|
}
|
|
2557
2994
|
uninstall() {
|
|
2558
|
-
this.
|
|
2559
|
-
|
|
2995
|
+
_recordings.delete(this.recording);
|
|
2996
|
+
if (_refCount <= 0) return;
|
|
2997
|
+
_refCount--;
|
|
2998
|
+
if (_refCount === 0) {
|
|
2999
|
+
_restoreFetch();
|
|
3000
|
+
_restoreXhr();
|
|
3001
|
+
}
|
|
2560
3002
|
}
|
|
2561
3003
|
isExtendedHttpEnabled() {
|
|
2562
3004
|
return localStorage.getItem("extendedHttpCommands") === "true";
|
|
@@ -2564,100 +3006,6 @@ var HttpMonitor = class {
|
|
|
2564
3006
|
isFixtureModeEnabled() {
|
|
2565
3007
|
return localStorage.getItem("fixtureMode") === "true";
|
|
2566
3008
|
}
|
|
2567
|
-
installFetch() {
|
|
2568
|
-
if (this.originalFetch) return;
|
|
2569
|
-
const originalFetch = window.fetch;
|
|
2570
|
-
this.originalFetch = originalFetch;
|
|
2571
|
-
window.fetch = async (input, init) => {
|
|
2572
|
-
const response = await originalFetch(input, init);
|
|
2573
|
-
try {
|
|
2574
|
-
await this.handleFetchInterception(input, init, response.clone());
|
|
2575
|
-
} catch {
|
|
2576
|
-
}
|
|
2577
|
-
return response;
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
uninstallFetch() {
|
|
2581
|
-
if (!this.originalFetch) return;
|
|
2582
|
-
window.fetch = this.originalFetch;
|
|
2583
|
-
this.originalFetch = null;
|
|
2584
|
-
}
|
|
2585
|
-
installXhr() {
|
|
2586
|
-
if (this.originalXHR) return;
|
|
2587
|
-
this.originalXHR = window.XMLHttpRequest;
|
|
2588
|
-
const OrigXHR = this.originalXHR;
|
|
2589
|
-
const handleXhrInterception = this.handleXhrInterception.bind(this);
|
|
2590
|
-
window.XMLHttpRequest = class extends OrigXHR {
|
|
2591
|
-
_xhrMethod = "GET";
|
|
2592
|
-
_xhrUrl = "";
|
|
2593
|
-
_xhrRequestBody = null;
|
|
2594
|
-
open(method, url, async = true, user, password) {
|
|
2595
|
-
this._xhrMethod = method.toUpperCase();
|
|
2596
|
-
this._xhrUrl = url instanceof URL ? url.href : url;
|
|
2597
|
-
super.open(method, url, async, user, password);
|
|
2598
|
-
}
|
|
2599
|
-
send(body) {
|
|
2600
|
-
if (typeof body === "string") {
|
|
2601
|
-
this._xhrRequestBody = parseJsonObject(body);
|
|
2602
|
-
}
|
|
2603
|
-
this.addEventListener("load", () => {
|
|
2604
|
-
try {
|
|
2605
|
-
handleXhrInterception(
|
|
2606
|
-
this._xhrMethod,
|
|
2607
|
-
this._xhrUrl,
|
|
2608
|
-
this._xhrRequestBody,
|
|
2609
|
-
this.responseText
|
|
2610
|
-
);
|
|
2611
|
-
} catch {
|
|
2612
|
-
}
|
|
2613
|
-
});
|
|
2614
|
-
super.send(body);
|
|
2615
|
-
}
|
|
2616
|
-
};
|
|
2617
|
-
}
|
|
2618
|
-
uninstallXhr() {
|
|
2619
|
-
if (!this.originalXHR) return;
|
|
2620
|
-
window.XMLHttpRequest = this.originalXHR;
|
|
2621
|
-
this.originalXHR = null;
|
|
2622
|
-
}
|
|
2623
|
-
async handleFetchInterception(input, init, responseClone) {
|
|
2624
|
-
const method = resolveMethod(input, init);
|
|
2625
|
-
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
2626
|
-
const url = resolveUrl(input);
|
|
2627
|
-
const alias = generateAlias(method, url);
|
|
2628
|
-
this.recording.registerInterceptor(method, url, alias);
|
|
2629
|
-
const extendedHttp = this.isExtendedHttpEnabled();
|
|
2630
|
-
const fixtureMode = this.isFixtureModeEnabled();
|
|
2631
|
-
const requestBody = extendedHttp ? parseRequestBody(init) : null;
|
|
2632
|
-
let responseText = null;
|
|
2633
|
-
let responseBody = null;
|
|
2634
|
-
if (extendedHttp || fixtureMode && method === "GET") {
|
|
2635
|
-
try {
|
|
2636
|
-
responseText = await responseClone.text();
|
|
2637
|
-
if (extendedHttp) responseBody = parseJsonObject(responseText);
|
|
2638
|
-
} catch {
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
if (fixtureMode && method === "GET" && responseText !== null) {
|
|
2642
|
-
const pretty = prettyJsonOrNull(responseText);
|
|
2643
|
-
if (pretty !== null) this.recording.registerFixture(`${alias}.json`, pretty);
|
|
2644
|
-
}
|
|
2645
|
-
const cmd = buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody);
|
|
2646
|
-
this.recording.addCommand(cmd);
|
|
2647
|
-
}
|
|
2648
|
-
handleXhrInterception(method, url, requestBody, responseText) {
|
|
2649
|
-
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
2650
|
-
const alias = generateAlias(method, url);
|
|
2651
|
-
this.recording.registerInterceptor(method, url, alias);
|
|
2652
|
-
if (this.isFixtureModeEnabled() && method === "GET") {
|
|
2653
|
-
const pretty = prettyJsonOrNull(responseText);
|
|
2654
|
-
if (pretty !== null) this.recording.registerFixture(`${alias}.json`, pretty);
|
|
2655
|
-
}
|
|
2656
|
-
const extendedHttp = this.isExtendedHttpEnabled();
|
|
2657
|
-
const responseBody = extendedHttp ? parseJsonObject(responseText) : null;
|
|
2658
|
-
const cmd = buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody);
|
|
2659
|
-
this.recording.addCommand(cmd);
|
|
2660
|
-
}
|
|
2661
3009
|
};
|
|
2662
3010
|
|
|
2663
3011
|
// src/utils/styles.utils.ts
|
|
@@ -2889,23 +3237,24 @@ function makeSwalDraggable() {
|
|
|
2889
3237
|
if (!dragArea) return;
|
|
2890
3238
|
applyDraggable(swal, dragArea);
|
|
2891
3239
|
}
|
|
2892
|
-
function makeSwalDraggableByContentId(contentId) {
|
|
2893
|
-
const content = document.getElementById(contentId);
|
|
2894
|
-
if (!content) {
|
|
2895
|
-
makeSwalDraggable();
|
|
2896
|
-
return;
|
|
2897
|
-
}
|
|
2898
|
-
const swal = content.closest(".swal2-popup") ?? document.querySelector(".swal2-popup");
|
|
2899
|
-
if (!swal) return;
|
|
2900
|
-
const dragArea = resolveDragArea(swal);
|
|
2901
|
-
if (!dragArea) return;
|
|
2902
|
-
applyDraggable(swal, dragArea);
|
|
2903
|
-
}
|
|
2904
3240
|
function setSwal2DataCyAttribute(dataCy = "lib-e2e-cypress-for-dummys") {
|
|
2905
3241
|
document.querySelector(".swal2-container")?.setAttribute("data-cy", dataCy);
|
|
2906
3242
|
document.querySelector(".swal2-html-container")?.setAttribute("data-cy", dataCy);
|
|
2907
3243
|
document.querySelector(".swal2-title")?.setAttribute("data-cy", dataCy);
|
|
2908
3244
|
}
|
|
3245
|
+
function ensurePopupDimensions(popup, heightPx) {
|
|
3246
|
+
if (!popup) return;
|
|
3247
|
+
popup.style.height = `${heightPx}px`;
|
|
3248
|
+
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
3249
|
+
if (!htmlContainer) return;
|
|
3250
|
+
htmlContainer.style.flex = "1";
|
|
3251
|
+
htmlContainer.style.minHeight = "0";
|
|
3252
|
+
htmlContainer.style.overflow = "hidden";
|
|
3253
|
+
htmlContainer.style.padding = "0";
|
|
3254
|
+
htmlContainer.style.margin = "0";
|
|
3255
|
+
htmlContainer.style.display = "flex";
|
|
3256
|
+
htmlContainer.style.flexDirection = "column";
|
|
3257
|
+
}
|
|
2909
3258
|
|
|
2910
3259
|
// src/utils/toast.utils.ts
|
|
2911
3260
|
function showToast(message, isSuccess = true) {
|
|
@@ -2946,37 +3295,6 @@ function isLocalHost(hostname) {
|
|
|
2946
3295
|
return h === "localhost" || h.endsWith(".localhost") || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0";
|
|
2947
3296
|
}
|
|
2948
3297
|
|
|
2949
|
-
// src/utils/widget-position.utils.ts
|
|
2950
|
-
var TOGGLE_MARGIN = 30;
|
|
2951
|
-
var DRAG_THRESHOLD = 5;
|
|
2952
|
-
var DEFAULT_EDGE_OFFSET = 46;
|
|
2953
|
-
function boxOffsetForDirection(dir) {
|
|
2954
|
-
return {
|
|
2955
|
-
x: dir.endsWith("left") ? 144 : 46,
|
|
2956
|
-
y: dir.startsWith("up") ? 144 : 46
|
|
2957
|
-
};
|
|
2958
|
-
}
|
|
2959
|
-
function clampTogglePosition(x, y, vw, vh, margin = TOGGLE_MARGIN) {
|
|
2960
|
-
const maxX = Math.max(margin, vw - margin);
|
|
2961
|
-
const maxY = Math.max(margin, vh - margin);
|
|
2962
|
-
return {
|
|
2963
|
-
x: Math.min(Math.max(x, margin), maxX),
|
|
2964
|
-
y: Math.min(Math.max(y, margin), maxY)
|
|
2965
|
-
};
|
|
2966
|
-
}
|
|
2967
|
-
function resolveExpandDirection(x, y, vw, vh) {
|
|
2968
|
-
const vertical = y < vh / 2 ? "down" : "up";
|
|
2969
|
-
const horizontal = x < vw / 2 ? "right" : "left";
|
|
2970
|
-
return `${vertical}-${horizontal}`;
|
|
2971
|
-
}
|
|
2972
|
-
function defaultTogglePosition(vw, vh) {
|
|
2973
|
-
return { x: vw - DEFAULT_EDGE_OFFSET, y: vh - DEFAULT_EDGE_OFFSET };
|
|
2974
|
-
}
|
|
2975
|
-
function boxTopLeftFor(toggleCentre, dir) {
|
|
2976
|
-
const off = boxOffsetForDirection(dir);
|
|
2977
|
-
return { x: toggleCentre.x - off.x, y: toggleCentre.y - off.y };
|
|
2978
|
-
}
|
|
2979
|
-
|
|
2980
3298
|
// src/components/test-previsualizer/test-previsualizer.styles.ts
|
|
2981
3299
|
var TEST_PREVISUALIZER_STYLES = `
|
|
2982
3300
|
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; overflow: hidden; }
|
|
@@ -3174,35 +3492,8 @@ var TestPrevisualizerElement = class extends HTMLElement {
|
|
|
3174
3492
|
}
|
|
3175
3493
|
}
|
|
3176
3494
|
};
|
|
3177
|
-
if (!customElements.get("test-previsualizer")) {
|
|
3178
|
-
customElements.define("test-previsualizer", TestPrevisualizerElement);
|
|
3179
|
-
}
|
|
3180
|
-
|
|
3181
|
-
// src/models/issue-tracker.model.ts
|
|
3182
|
-
var ISSUE_TRACKER_PRESETS = {
|
|
3183
|
-
jira: { id: "jira", label: "Jira", urlPattern: "{baseUrl}/browse/{id}", idPattern: /^[A-Z][A-Z0-9]+-\d+$/ },
|
|
3184
|
-
github: { id: "github", label: "GitHub Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3185
|
-
gitlab: { id: "gitlab", label: "GitLab Issues", urlPattern: "{baseUrl}/-/issues/{id}", idPattern: /^\d+$/ },
|
|
3186
|
-
azure: { id: "azure", label: "Azure DevOps", urlPattern: "{baseUrl}/_workitems/edit/{id}", idPattern: /^\d+$/ },
|
|
3187
|
-
bitbucket: { id: "bitbucket", label: "Bitbucket Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3188
|
-
custom: { id: "custom", label: "Custom", urlPattern: "{baseUrl}/{id}", idPattern: /.+/ }
|
|
3189
|
-
};
|
|
3190
|
-
var DEFAULT_ISSUE_TRACKER_CONFIG = {
|
|
3191
|
-
enabled: false,
|
|
3192
|
-
provider: "jira",
|
|
3193
|
-
baseUrl: ""
|
|
3194
|
-
};
|
|
3195
|
-
|
|
3196
|
-
// src/utils/ticket.utils.ts
|
|
3197
|
-
function buildTicketUrl(ticketId, config) {
|
|
3198
|
-
if (!config.enabled || !ticketId.trim() || !config.baseUrl.trim()) return null;
|
|
3199
|
-
const preset = ISSUE_TRACKER_PRESETS[config.provider];
|
|
3200
|
-
const base = config.baseUrl.replace(/\/+$/, "");
|
|
3201
|
-
return preset.urlPattern.replace("{baseUrl}", base).replace("{id}", encodeURIComponent(ticketId.trim()));
|
|
3202
|
-
}
|
|
3203
|
-
function isValidTicketId(ticketId, provider) {
|
|
3204
|
-
if (!ticketId.trim()) return false;
|
|
3205
|
-
return ISSUE_TRACKER_PRESETS[provider].idPattern.test(ticketId.trim());
|
|
3495
|
+
if (!customElements.get("lib-e2e-test-previsualizer")) {
|
|
3496
|
+
customElements.define("lib-e2e-test-previsualizer", TestPrevisualizerElement);
|
|
3206
3497
|
}
|
|
3207
3498
|
|
|
3208
3499
|
// src/components/save-test/save-test.styles.ts
|
|
@@ -3266,9 +3557,20 @@ var SAVE_TEST_STYLES = `
|
|
|
3266
3557
|
transition: color 0.12s; padding: 0; background: none; border: none;
|
|
3267
3558
|
}
|
|
3268
3559
|
.chip-del:hover { color: #f85149; filter: none; }
|
|
3560
|
+
.discard-warn { color: #e6edf3; font-size: 13px; margin-bottom: 20px; line-height: 1.5; }
|
|
3269
3561
|
`;
|
|
3270
3562
|
|
|
3271
3563
|
// src/components/save-test/save-test.template.ts
|
|
3564
|
+
function renderSaveTestConfirmDiscard(t) {
|
|
3565
|
+
return `
|
|
3566
|
+
<div class="container">
|
|
3567
|
+
<p class="discard-warn">\u26A0 ${t("SAVE_TEST.CONFIRM_DISCARD")}</p>
|
|
3568
|
+
<div class="btn-row">
|
|
3569
|
+
<button class="btn-danger" id="btn-confirm-discard">${t("SAVE_TEST.CONFIRM_DISCARD_BTN")}</button>
|
|
3570
|
+
<button id="btn-back-discard">${t("SAVE_TEST.BACK_BTN")}</button>
|
|
3571
|
+
</div>
|
|
3572
|
+
</div>`;
|
|
3573
|
+
}
|
|
3272
3574
|
function renderSaveTestAsk(t) {
|
|
3273
3575
|
return `
|
|
3274
3576
|
<div class="container">
|
|
@@ -3314,6 +3616,7 @@ function renderSaveTestDesc(description, notes, tags, ticketId, issueTrackerEnab
|
|
|
3314
3616
|
var SaveTestElement = class extends HTMLElement {
|
|
3315
3617
|
shadow;
|
|
3316
3618
|
_step = "ask";
|
|
3619
|
+
_stepBeforeDiscard = "ask";
|
|
3317
3620
|
description = "";
|
|
3318
3621
|
notes = "";
|
|
3319
3622
|
tags = [];
|
|
@@ -3341,8 +3644,17 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3341
3644
|
this.dispatch("saveandexport", { description: this.description.trim(), notes: this.notes, tags: [...this.tags], ticketId: this.ticketId.trim() });
|
|
3342
3645
|
}
|
|
3343
3646
|
cancel() {
|
|
3647
|
+
this._stepBeforeDiscard = this._step === "confirm-discard" ? "ask" : this._step;
|
|
3648
|
+
this._step = "confirm-discard";
|
|
3649
|
+
this.render();
|
|
3650
|
+
}
|
|
3651
|
+
confirmDiscard() {
|
|
3344
3652
|
this.dispatch("savetest", { description: null, notes: "", tags: [], ticketId: "" });
|
|
3345
3653
|
}
|
|
3654
|
+
backFromDiscard() {
|
|
3655
|
+
this._step = this._stepBeforeDiscard;
|
|
3656
|
+
this.render();
|
|
3657
|
+
}
|
|
3346
3658
|
restartComponent() {
|
|
3347
3659
|
this._step = "ask";
|
|
3348
3660
|
this.description = "";
|
|
@@ -3373,6 +3685,12 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3373
3685
|
this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
|
|
3374
3686
|
}
|
|
3375
3687
|
render() {
|
|
3688
|
+
if (this._step === "confirm-discard") {
|
|
3689
|
+
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestConfirmDiscard(this.t.bind(this))}`;
|
|
3690
|
+
this.shadow.getElementById("btn-confirm-discard")?.addEventListener("click", () => this.confirmDiscard());
|
|
3691
|
+
this.shadow.getElementById("btn-back-discard")?.addEventListener("click", () => this.backFromDiscard());
|
|
3692
|
+
return;
|
|
3693
|
+
}
|
|
3376
3694
|
if (this._step === "ask") {
|
|
3377
3695
|
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestAsk(this.t.bind(this))}`;
|
|
3378
3696
|
this.shadow.getElementById("btn-yes")?.addEventListener("click", () => this.askSave());
|
|
@@ -3414,8 +3732,8 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3414
3732
|
}
|
|
3415
3733
|
}
|
|
3416
3734
|
};
|
|
3417
|
-
if (!customElements.get("save-test")) {
|
|
3418
|
-
customElements.define("save-test", SaveTestElement);
|
|
3735
|
+
if (!customElements.get("lib-e2e-save-test")) {
|
|
3736
|
+
customElements.define("lib-e2e-save-test", SaveTestElement);
|
|
3419
3737
|
}
|
|
3420
3738
|
|
|
3421
3739
|
// src/components/test-editor/test-editor.styles.ts
|
|
@@ -3644,7 +3962,9 @@ function renderTestRow(test, i, state, t) {
|
|
|
3644
3962
|
const tagsHtml = (test.tags ?? []).length ? `<span class="test-tags">${(test.tags ?? []).map((tag) => `<span class="test-tag">${escHtml(tag)}</span>`).join("")}</span>` : "";
|
|
3645
3963
|
const checkbox = selectMode ? `<input type="checkbox" ${selectedIds.has(test.id) ? "checked" : ""} data-select="${test.id}" />` : "";
|
|
3646
3964
|
const hasIcps = (test.interceptors ?? []).length > 0;
|
|
3647
|
-
const
|
|
3965
|
+
const ticketId = test.ticketId;
|
|
3966
|
+
const ticketComment = ticketId ? buildTicketComment(ticketId) + "\n" : "";
|
|
3967
|
+
const itBlockCode = `${ticketComment}it('${escapeSingleQuotes(test.name)}', () => {
|
|
3648
3968
|
${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
3649
3969
|
});`;
|
|
3650
3970
|
const icpBlockCode = icps.length ? `beforeEach(() => {
|
|
@@ -3652,7 +3972,6 @@ ${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
3652
3972
|
${icps.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
3653
3973
|
});` : "";
|
|
3654
3974
|
const notesHtml = expanded && test.notes ? `<p class="test-notes">${escHtml(test.notes)}</p>` : "";
|
|
3655
|
-
const ticketId = test.ticketId;
|
|
3656
3975
|
const ticketUrl = ticketId ? buildTicketUrl(ticketId, issueTrackerConfig) : null;
|
|
3657
3976
|
const ticketHtml = ticketId ? ticketUrl ? `<a class="ticket-link" href="${escAttr(ticketUrl)}" target="_blank" rel="noopener noreferrer" title="${t("TEST_EDITOR.TICKET_LINK_TITLE")}">${escHtml(ticketId)}</a>` : `<span class="ticket-badge">${escHtml(ticketId)}</span>` : "";
|
|
3658
3977
|
const body = expanded ? `
|
|
@@ -3901,8 +4220,90 @@ ${beforeEach}${itBlocks}
|
|
|
3901
4220
|
});
|
|
3902
4221
|
}
|
|
3903
4222
|
};
|
|
3904
|
-
if (!customElements.get("test-editor")) {
|
|
3905
|
-
customElements.define("test-editor", TestEditorElement);
|
|
4223
|
+
if (!customElements.get("lib-e2e-test-editor")) {
|
|
4224
|
+
customElements.define("lib-e2e-test-editor", TestEditorElement);
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
// src/models/active-session.model.ts
|
|
4228
|
+
var ACTIVE_SESSION_BREADCRUMB_KEY = "e2e-active-session";
|
|
4229
|
+
var RESUME_TTL_CONFIG_KEY = "resumeRecencyTtlMinutes";
|
|
4230
|
+
var DEFAULT_RESUME_TTL_MINUTES = 30;
|
|
4231
|
+
|
|
4232
|
+
// src/utils/login-setup.utils.ts
|
|
4233
|
+
var EXPORT_FN_RE = /^(?!\/\/).*\bexport\s+(?:async\s+)?function\s+(\w+)|^(?!\/\/).*\bexport\s+const\s+(\w+)\s*=\s*(?:async\s*)?\(/gm;
|
|
4234
|
+
function extractExportedFunctions(fileContent) {
|
|
4235
|
+
const names = [];
|
|
4236
|
+
let match;
|
|
4237
|
+
const re = new RegExp(EXPORT_FN_RE.source, EXPORT_FN_RE.flags);
|
|
4238
|
+
while ((match = re.exec(fileContent)) !== null) {
|
|
4239
|
+
const name = match[1] ?? match[2];
|
|
4240
|
+
if (name) names.push(name);
|
|
4241
|
+
}
|
|
4242
|
+
return names;
|
|
4243
|
+
}
|
|
4244
|
+
function buildLoginScaffold() {
|
|
4245
|
+
return [
|
|
4246
|
+
"/**",
|
|
4247
|
+
" * Login service scaffold \u2014 fill in your own auth logic.",
|
|
4248
|
+
" * This file is not committed to source control.",
|
|
4249
|
+
" */",
|
|
4250
|
+
"",
|
|
4251
|
+
"export function fetchAuthToken() {",
|
|
4252
|
+
" // TODO: implement \u2014 retrieve and return your auth token",
|
|
4253
|
+
"}",
|
|
4254
|
+
"",
|
|
4255
|
+
"export function setupRequestInterceptors() {",
|
|
4256
|
+
" // TODO: implement \u2014 configure cy.intercept() or set localStorage/cookies",
|
|
4257
|
+
"}",
|
|
4258
|
+
""
|
|
4259
|
+
].join("\n");
|
|
4260
|
+
}
|
|
4261
|
+
function buildLoginImportPath(fromFilePath, toServiceFilePath) {
|
|
4262
|
+
const fromParts = fromFilePath.replace(/\\/g, "/").split("/");
|
|
4263
|
+
const toParts = toServiceFilePath.replace(/\\/g, "/").split("/");
|
|
4264
|
+
fromParts.pop();
|
|
4265
|
+
let common = 0;
|
|
4266
|
+
while (common < fromParts.length && common < toParts.length - 1 && fromParts[common] === toParts[common]) {
|
|
4267
|
+
common++;
|
|
4268
|
+
}
|
|
4269
|
+
const ups = fromParts.length - common;
|
|
4270
|
+
const downs = toParts.slice(common);
|
|
4271
|
+
const serviceFile = downs[downs.length - 1].replace(/\.ts$/, "");
|
|
4272
|
+
const prefix = ups === 0 ? "." : Array(ups).fill("..").join("/");
|
|
4273
|
+
const middle = downs.slice(0, -1);
|
|
4274
|
+
return [prefix, ...middle, serviceFile].join("/");
|
|
4275
|
+
}
|
|
4276
|
+
function hasLoginBlocks(content, fnNames) {
|
|
4277
|
+
return fnNames.some((fn) => new RegExp(`\\b${fn}\\s*\\(`).test(content));
|
|
4278
|
+
}
|
|
4279
|
+
function injectLoginBlocksIntoExisting(content, importLine, beforeBlock, beforeEachBlock) {
|
|
4280
|
+
let result = content;
|
|
4281
|
+
if (importLine && !content.includes(importLine)) {
|
|
4282
|
+
result = `${importLine}
|
|
4283
|
+
|
|
4284
|
+
${result}`;
|
|
4285
|
+
}
|
|
4286
|
+
const blocks = [beforeBlock, beforeEachBlock].filter(Boolean).join("");
|
|
4287
|
+
if (blocks) {
|
|
4288
|
+
const describeMatch = result.match(/(describe\s*\(.*?{)/s);
|
|
4289
|
+
if (describeMatch) {
|
|
4290
|
+
const insertPos = (describeMatch.index ?? 0) + describeMatch[0].length;
|
|
4291
|
+
result = result.slice(0, insertPos) + "\n" + blocks + result.slice(insertPos);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
return result;
|
|
4295
|
+
}
|
|
4296
|
+
function buildLoginBlocks(importPath, beforeFn, beforeEachFn) {
|
|
4297
|
+
if (!beforeFn && !beforeEachFn) {
|
|
4298
|
+
return { importLine: "", beforeBlock: "", beforeEachBlock: "" };
|
|
4299
|
+
}
|
|
4300
|
+
const fns = [...new Set([beforeFn, beforeEachFn].filter(Boolean))];
|
|
4301
|
+
const importLine = `import { ${fns.join(", ")} } from '${importPath}';`;
|
|
4302
|
+
const beforeBlock = beforeFn ? ` before(() => { ${beforeFn}(); });
|
|
4303
|
+
` : "";
|
|
4304
|
+
const beforeEachBlock = beforeEachFn ? ` beforeEach(() => { ${beforeEachFn}(); });
|
|
4305
|
+
` : "";
|
|
4306
|
+
return { importLine, beforeBlock, beforeEachBlock };
|
|
3906
4307
|
}
|
|
3907
4308
|
|
|
3908
4309
|
// src/components/configuration/configuration.styles.ts
|
|
@@ -4063,6 +4464,99 @@ var CONFIGURATION_STYLES = `
|
|
|
4063
4464
|
.btn-export-confirm { background: #238636; border-color: #238636; color: #fff; }
|
|
4064
4465
|
.btn-export-confirm:hover:not(:disabled) { background: #2ea043; border-color: #2ea043; }
|
|
4065
4466
|
.btn-export-confirm:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
4467
|
+
|
|
4468
|
+
/* \u2500\u2500 Form-element font reset \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 */
|
|
4469
|
+
input, select, button, textarea { font-family: inherit; }
|
|
4470
|
+
|
|
4471
|
+
/* \u2500\u2500 Primary action button \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 */
|
|
4472
|
+
.btn-primary { background: #1f6feb; border-color: #1f6feb; color: #fff; }
|
|
4473
|
+
.btn-primary:hover { background: #388bfd; border-color: #388bfd; color: #fff; }
|
|
4474
|
+
.btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
4475
|
+
|
|
4476
|
+
/* \u2500\u2500 Login Setup overlay \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\u2500 */
|
|
4477
|
+
.ls-modal { max-width: 500px; }
|
|
4478
|
+
.ls-hd { display: flex; align-items: center; gap: 8px; }
|
|
4479
|
+
.ls-hd-icon { font-size: 15px; }
|
|
4480
|
+
|
|
4481
|
+
.ls-body {
|
|
4482
|
+
padding: 20px; overflow-y: auto; flex: 1;
|
|
4483
|
+
display: flex; flex-direction: column; gap: 20px;
|
|
4484
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
.ls-section { display: flex; flex-direction: column; gap: 8px; }
|
|
4488
|
+
.ls-section-label {
|
|
4489
|
+
font-size: 10px; font-weight: 700; text-transform: uppercase;
|
|
4490
|
+
letter-spacing: 0.8px; color: #484f58;
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
.ls-actions { display: flex; gap: 10px; }
|
|
4494
|
+
.ls-action-btn {
|
|
4495
|
+
flex: 1; padding: 14px 12px; border-radius: 8px;
|
|
4496
|
+
background: #0d1117; border: 1px solid #21262d; color: #8b949e;
|
|
4497
|
+
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
|
4498
|
+
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
|
4499
|
+
}
|
|
4500
|
+
.ls-action-btn:hover { background: #161b22; color: #e6edf3; border-color: #388bfd; }
|
|
4501
|
+
.ls-action-icon { font-size: 22px; line-height: 1; }
|
|
4502
|
+
.ls-action-label { font-size: 12px; font-weight: 600; color: #c9d1d9; }
|
|
4503
|
+
.ls-action-btn:hover .ls-action-label { color: #e6edf3; }
|
|
4504
|
+
.ls-action-sub { font-size: 10px; color: #484f58; line-height: 1.4; text-align: center; }
|
|
4505
|
+
|
|
4506
|
+
.ls-file-row {
|
|
4507
|
+
display: flex; align-items: center; gap: 10px;
|
|
4508
|
+
padding: 10px 12px; background: #0d1117;
|
|
4509
|
+
border: 1px solid #21262d; border-radius: 8px;
|
|
4510
|
+
}
|
|
4511
|
+
.ls-file-icon { font-size: 14px; flex-shrink: 0; }
|
|
4512
|
+
.ls-file-name {
|
|
4513
|
+
flex: 1; font-size: 11px; color: #c9d1d9;
|
|
4514
|
+
font-family: 'Cascadia Code','Fira Code','Consolas',monospace;
|
|
4515
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
4516
|
+
min-width: 0;
|
|
4517
|
+
}
|
|
4518
|
+
.ls-btn-rescan {
|
|
4519
|
+
flex-shrink: 0; padding: 4px 10px; font-size: 11px;
|
|
4520
|
+
background: #161b22; border-color: #30363d; color: #8b949e;
|
|
4521
|
+
}
|
|
4522
|
+
.ls-btn-rescan:hover { background: #21262d; color: #e6edf3; }
|
|
4523
|
+
|
|
4524
|
+
.ls-input {
|
|
4525
|
+
width: 100%; padding: 8px 10px; background: #0d1117;
|
|
4526
|
+
border: 1px solid #30363d; border-radius: 6px;
|
|
4527
|
+
color: #c9d1d9; font-size: 12px; outline: none; font-family: inherit;
|
|
4528
|
+
}
|
|
4529
|
+
.ls-input:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.12); }
|
|
4530
|
+
|
|
4531
|
+
.ls-fns {
|
|
4532
|
+
display: flex; flex-wrap: wrap; gap: 6px;
|
|
4533
|
+
padding: 10px; background: #0d1117;
|
|
4534
|
+
border: 1px solid #21262d; border-radius: 6px; min-height: 38px;
|
|
4535
|
+
}
|
|
4536
|
+
.ls-fn-chip {
|
|
4537
|
+
padding: 3px 10px; font-size: 11px; color: #79c0ff;
|
|
4538
|
+
background: rgba(47,129,247,0.1); border: 1px solid rgba(47,129,247,0.2);
|
|
4539
|
+
border-radius: 12px; font-family: 'Cascadia Code','Fira Code','Consolas',monospace;
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
.ls-field-block { display: flex; flex-direction: column; gap: 6px; }
|
|
4543
|
+
.ls-field-block + .ls-field-block { margin-top: 12px; }
|
|
4544
|
+
.ls-label { font-size: 11px; color: #8b949e; }
|
|
4545
|
+
.ls-select {
|
|
4546
|
+
width: 100%; padding: 8px 10px; background: #0d1117;
|
|
4547
|
+
border: 1px solid #30363d; border-radius: 6px;
|
|
4548
|
+
color: #c9d1d9; font-size: 12px; outline: none; cursor: pointer;
|
|
4549
|
+
}
|
|
4550
|
+
.ls-select:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.12); }
|
|
4551
|
+
|
|
4552
|
+
.ls-empty-state {
|
|
4553
|
+
display: flex; align-items: center; gap: 10px;
|
|
4554
|
+
padding: 14px; background: #0d1117; border: 1px solid #21262d;
|
|
4555
|
+
border-radius: 8px; font-size: 12px; color: #484f58;
|
|
4556
|
+
}
|
|
4557
|
+
.ls-empty-icon { font-size: 16px; }
|
|
4558
|
+
|
|
4559
|
+
.ls-ft { padding: 14px 20px; }
|
|
4066
4560
|
`;
|
|
4067
4561
|
|
|
4068
4562
|
// src/components/configuration/configuration.template.ts
|
|
@@ -4279,6 +4773,13 @@ function renderConfiguration(state, t) {
|
|
|
4279
4773
|
</div>` : ""}
|
|
4280
4774
|
</div>
|
|
4281
4775
|
|
|
4776
|
+
<!-- Login Setup -->
|
|
4777
|
+
<div class="card card-wide">
|
|
4778
|
+
<div class="card-hd">${t("CONFIG.LOGIN_SETUP_SECTION")}</div>
|
|
4779
|
+
${state.loginSetupConfig ? `<p style="font-size:12px;color:#8b949e;margin:0 0 8px">${escHtml(state.loginSetupConfig.filePath)}</p>` : `<p style="font-size:12px;color:#8b949e;margin:0 0 8px">${t("CONFIG.LOGIN_SETUP_NOT_CONFIGURED")}</p>`}
|
|
4780
|
+
<button id="btn-open-login-setup" class="btn-sm">${t("CONFIG.LOGIN_SETUP_SET_UP_BTN")}</button>
|
|
4781
|
+
</div>
|
|
4782
|
+
|
|
4282
4783
|
<!-- Data -->
|
|
4283
4784
|
<div class="card card-wide">
|
|
4284
4785
|
<div class="card-hd">${t("CONFIG.DATA_SECTION")}</div>
|
|
@@ -4292,7 +4793,93 @@ function renderConfiguration(state, t) {
|
|
|
4292
4793
|
</div>
|
|
4293
4794
|
</div>
|
|
4294
4795
|
|
|
4295
|
-
</div>${renderExportOverlay(state, t)}`;
|
|
4796
|
+
</div>${renderExportOverlay(state, t)}${renderLoginSetupOverlay(state, t)}`;
|
|
4797
|
+
}
|
|
4798
|
+
function renderLoginSetupOverlay(state, t) {
|
|
4799
|
+
if (!state.isLoginSetupOpen) return "";
|
|
4800
|
+
const cfg = state.loginSetupConfig;
|
|
4801
|
+
const path = state.loginSetupDraftPath ?? cfg?.filePath ?? "";
|
|
4802
|
+
const fns = state.loginSetupDraftFunctions ?? cfg?.detectedFunctions ?? [];
|
|
4803
|
+
const beforeFn = state.loginSetupDraftBeforeFn !== void 0 ? state.loginSetupDraftBeforeFn : cfg?.beforeFn ?? null;
|
|
4804
|
+
const beforeEachFn = state.loginSetupDraftBeforeEachFn !== void 0 ? state.loginSetupDraftBeforeEachFn : cfg?.beforeEachFn ?? null;
|
|
4805
|
+
const hasContent = state.loginSetupDraftHasContent ?? !!cfg?.fileContent;
|
|
4806
|
+
const fnOption = (name, selected) => `<option value="${escAttr(name ?? "")}" ${selected === name ? "selected" : ""}>${name ? escHtml(name) : t("CONFIG.LOGIN_SETUP_NONE_OPTION")}</option>`;
|
|
4807
|
+
const fnDropdown = (id, label, selected) => `
|
|
4808
|
+
<div class="ls-field-block">
|
|
4809
|
+
<label class="ls-label" for="${id}">${escHtml(label)}</label>
|
|
4810
|
+
<select id="${id}" class="ls-select">
|
|
4811
|
+
${fnOption(null, selected)}
|
|
4812
|
+
${fns.map((fn) => fnOption(fn, selected)).join("")}
|
|
4813
|
+
</select>
|
|
4814
|
+
</div>`;
|
|
4815
|
+
const functionsSection = hasContent ? fns.length > 0 ? `<div class="ls-section">
|
|
4816
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_FUNCTIONS_DETECTED")}</span>
|
|
4817
|
+
<div class="ls-fns">${fns.map((fn) => `<span class="ls-fn-chip">${escHtml(fn)}</span>`).join("")}</div>
|
|
4818
|
+
</div>
|
|
4819
|
+
<div class="ls-section">
|
|
4820
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_BLOCK_ASSIGN")}</span>
|
|
4821
|
+
${fnDropdown("login-setup-before-fn", t("CONFIG.LOGIN_SETUP_BEFORE_LABEL"), beforeFn)}
|
|
4822
|
+
${fnDropdown("login-setup-before-each-fn", t("CONFIG.LOGIN_SETUP_BEFORE_EACH_LABEL"), beforeEachFn)}
|
|
4823
|
+
</div>` : `<div class="ls-empty-state">
|
|
4824
|
+
<span class="ls-empty-icon">\u{1F50D}</span>
|
|
4825
|
+
<span>${t("CONFIG.LOGIN_SETUP_NO_FUNCTIONS")}</span>
|
|
4826
|
+
</div>` : "";
|
|
4827
|
+
const fileSection = hasContent ? `<div class="ls-section">
|
|
4828
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_FILE_NAME_LABEL")}</span>
|
|
4829
|
+
<div class="ls-file-row">
|
|
4830
|
+
<span class="ls-file-icon">\u{1F4C4}</span>
|
|
4831
|
+
<span class="ls-file-name">${escHtml(path)}</span>
|
|
4832
|
+
<button id="btn-login-setup-rescan" class="ls-btn-rescan">${t("CONFIG.LOGIN_SETUP_RESCAN")}</button>
|
|
4833
|
+
</div>
|
|
4834
|
+
</div>` : `<div class="ls-section">
|
|
4835
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_FILE_NAME_LABEL")}</span>
|
|
4836
|
+
<input id="login-setup-filepath" type="text" class="ls-input"
|
|
4837
|
+
placeholder="${escAttr(t("CONFIG.LOGIN_SETUP_FILE_NAME_PH"))}"
|
|
4838
|
+
value="${escAttr(path)}" />
|
|
4839
|
+
</div>`;
|
|
4840
|
+
return `
|
|
4841
|
+
<div class="export-overlay" id="login-setup-overlay">
|
|
4842
|
+
<div class="export-modal ls-modal">
|
|
4843
|
+
|
|
4844
|
+
<div class="export-hd ls-hd">
|
|
4845
|
+
<span class="ls-hd-icon">\u{1F510}</span>
|
|
4846
|
+
${t("CONFIG.LOGIN_SETUP_TITLE")}
|
|
4847
|
+
</div>
|
|
4848
|
+
|
|
4849
|
+
<div class="ls-body">
|
|
4850
|
+
|
|
4851
|
+
<div class="ls-section">
|
|
4852
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_SOURCE_LABEL")}</span>
|
|
4853
|
+
<div class="ls-actions">
|
|
4854
|
+
<button id="btn-login-setup-create-scaffold" class="ls-action-btn">
|
|
4855
|
+
<span class="ls-action-icon">\u2728</span>
|
|
4856
|
+
<span class="ls-action-label">${t("CONFIG.LOGIN_SETUP_CREATE_SCAFFOLD")}</span>
|
|
4857
|
+
<span class="ls-action-sub">${t("CONFIG.LOGIN_SETUP_CREATE_SUB")}</span>
|
|
4858
|
+
</button>
|
|
4859
|
+
<button id="btn-login-setup-pick-file" class="ls-action-btn">
|
|
4860
|
+
<span class="ls-action-icon">\u{1F4C2}</span>
|
|
4861
|
+
<span class="ls-action-label">${t("CONFIG.LOGIN_SETUP_SELECT_EXISTING")}</span>
|
|
4862
|
+
<span class="ls-action-sub">${t("CONFIG.LOGIN_SETUP_SELECT_SUB")}</span>
|
|
4863
|
+
</button>
|
|
4864
|
+
</div>
|
|
4865
|
+
</div>
|
|
4866
|
+
|
|
4867
|
+
${fileSection}
|
|
4868
|
+
|
|
4869
|
+
${functionsSection}
|
|
4870
|
+
|
|
4871
|
+
</div>
|
|
4872
|
+
|
|
4873
|
+
<div class="export-ft ls-ft">
|
|
4874
|
+
<button id="btn-login-setup-clear" class="btn-danger">${t("CONFIG.LOGIN_SETUP_CLEAR_BTN")}</button>
|
|
4875
|
+
<span class="export-ft-actions">
|
|
4876
|
+
<button id="btn-login-setup-cancel">${t("CONFIG.LOGIN_SETUP_CANCEL_BTN")}</button>
|
|
4877
|
+
<button id="btn-login-setup-save" class="btn-primary">${t("CONFIG.LOGIN_SETUP_SAVE_BTN")}</button>
|
|
4878
|
+
</span>
|
|
4879
|
+
</div>
|
|
4880
|
+
|
|
4881
|
+
</div>
|
|
4882
|
+
</div>`;
|
|
4296
4883
|
}
|
|
4297
4884
|
|
|
4298
4885
|
// src/components/configuration/configuration.ts
|
|
@@ -4313,6 +4900,15 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4313
4900
|
exportSelectedIds = /* @__PURE__ */ new Set();
|
|
4314
4901
|
exportSelectedTags = /* @__PURE__ */ new Set();
|
|
4315
4902
|
issueTrackerConfig = { ...DEFAULT_ISSUE_TRACKER_CONFIG };
|
|
4903
|
+
isLoginSetupOpen = false;
|
|
4904
|
+
loginSetupConfig = null;
|
|
4905
|
+
loginSetupDraftPath = "";
|
|
4906
|
+
loginSetupDraftFunctions = [];
|
|
4907
|
+
loginSetupDraftBeforeFn = null;
|
|
4908
|
+
loginSetupDraftBeforeEachFn = null;
|
|
4909
|
+
loginSetupDraftHasContent = false;
|
|
4910
|
+
loginSetupDraftHandle = null;
|
|
4911
|
+
loginSetupDraftContent = "";
|
|
4316
4912
|
filesystemGranted = false;
|
|
4317
4913
|
cypressFolderName = null;
|
|
4318
4914
|
constructor() {
|
|
@@ -4354,6 +4950,7 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4354
4950
|
provider: config?.["issueTrackerProvider"] ?? DEFAULT_ISSUE_TRACKER_CONFIG.provider,
|
|
4355
4951
|
baseUrl: config?.["issueTrackerBaseUrl"] ?? ""
|
|
4356
4952
|
};
|
|
4953
|
+
this.loginSetupConfig = await this.persistence.getLoginSetup();
|
|
4357
4954
|
this.render();
|
|
4358
4955
|
}
|
|
4359
4956
|
async onLanguageChange(lang) {
|
|
@@ -4419,6 +5016,85 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4419
5016
|
await this.persistence.setConfig({ issueTrackerBaseUrl: baseUrl });
|
|
4420
5017
|
this.dispatchEvent(new CustomEvent("issuetrackerchange", { detail: { ...this.issueTrackerConfig }, bubbles: true, composed: true }));
|
|
4421
5018
|
}
|
|
5019
|
+
openLoginSetupPanel() {
|
|
5020
|
+
this.isLoginSetupOpen = true;
|
|
5021
|
+
const cfg = this.loginSetupConfig;
|
|
5022
|
+
this.loginSetupDraftHandle = null;
|
|
5023
|
+
this.loginSetupDraftContent = cfg?.fileContent ?? "";
|
|
5024
|
+
this.loginSetupDraftPath = cfg?.filePath ?? "";
|
|
5025
|
+
this.loginSetupDraftFunctions = cfg?.detectedFunctions ?? [];
|
|
5026
|
+
this.loginSetupDraftBeforeFn = cfg?.beforeFn ?? null;
|
|
5027
|
+
this.loginSetupDraftBeforeEachFn = cfg?.beforeEachFn ?? null;
|
|
5028
|
+
this.loginSetupDraftHasContent = !!cfg?.fileContent;
|
|
5029
|
+
this.render();
|
|
5030
|
+
}
|
|
5031
|
+
closeLoginSetupPanel() {
|
|
5032
|
+
this.isLoginSetupOpen = false;
|
|
5033
|
+
this.render();
|
|
5034
|
+
}
|
|
5035
|
+
async saveLoginSetupConfig(config) {
|
|
5036
|
+
await this.persistence.saveLoginSetup(config);
|
|
5037
|
+
this.loginSetupConfig = config;
|
|
5038
|
+
this.isLoginSetupOpen = false;
|
|
5039
|
+
this.render();
|
|
5040
|
+
}
|
|
5041
|
+
async clearLoginSetupConfig() {
|
|
5042
|
+
await this.persistence.clearLoginSetup();
|
|
5043
|
+
this.loginSetupConfig = null;
|
|
5044
|
+
this.render();
|
|
5045
|
+
}
|
|
5046
|
+
async loginSetupPickFile() {
|
|
5047
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim() ?? "";
|
|
5048
|
+
try {
|
|
5049
|
+
const [handle] = await window.showOpenFilePicker({
|
|
5050
|
+
types: [{ description: "TypeScript files", accept: { "text/plain": [".ts"] } }],
|
|
5051
|
+
multiple: false
|
|
5052
|
+
});
|
|
5053
|
+
this.loginSetupDraftHandle = handle;
|
|
5054
|
+
const file = await handle.getFile();
|
|
5055
|
+
this.loginSetupDraftContent = await file.text();
|
|
5056
|
+
this.loginSetupDraftHasContent = true;
|
|
5057
|
+
this.loginSetupDraftPath = currentPath || `cypress/common-services/${handle.name}`;
|
|
5058
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(this.loginSetupDraftContent);
|
|
5059
|
+
this.render();
|
|
5060
|
+
} catch (e) {
|
|
5061
|
+
if (e?.name !== "AbortError") showToast(this.t("CONFIG.FOLDER_ERROR_TOAST"), false);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
async loginSetupCreateScaffold() {
|
|
5065
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim() ?? "";
|
|
5066
|
+
try {
|
|
5067
|
+
const handle = await window.showSaveFilePicker({
|
|
5068
|
+
suggestedName: "login.service.ts",
|
|
5069
|
+
types: [{ description: "TypeScript files", accept: { "text/plain": [".ts"] } }]
|
|
5070
|
+
});
|
|
5071
|
+
const content = buildLoginScaffold();
|
|
5072
|
+
const writable = await handle.createWritable();
|
|
5073
|
+
await writable.write(content);
|
|
5074
|
+
await writable.close();
|
|
5075
|
+
this.loginSetupDraftHandle = handle;
|
|
5076
|
+
this.loginSetupDraftContent = content;
|
|
5077
|
+
this.loginSetupDraftHasContent = true;
|
|
5078
|
+
this.loginSetupDraftPath = currentPath || `cypress/common-services/${handle.name}`;
|
|
5079
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(content);
|
|
5080
|
+
this.render();
|
|
5081
|
+
} catch (e) {
|
|
5082
|
+
if (e?.name !== "AbortError") showToast(this.t("CONFIG.FOLDER_ERROR_TOAST"), false);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
async loginSetupRescan() {
|
|
5086
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim();
|
|
5087
|
+
if (currentPath !== void 0) this.loginSetupDraftPath = currentPath;
|
|
5088
|
+
try {
|
|
5089
|
+
if (this.loginSetupDraftHandle) {
|
|
5090
|
+
const file = await this.loginSetupDraftHandle.getFile();
|
|
5091
|
+
this.loginSetupDraftContent = await file.text();
|
|
5092
|
+
}
|
|
5093
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(this.loginSetupDraftContent);
|
|
5094
|
+
this.render();
|
|
5095
|
+
} catch {
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
4422
5098
|
async changeFolder() {
|
|
4423
5099
|
try {
|
|
4424
5100
|
await this.persistence.requestDirectoryPermissions();
|
|
@@ -4521,7 +5197,14 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4521
5197
|
exportTests: this.exportTests,
|
|
4522
5198
|
exportSelectedIds: this.exportSelectedIds,
|
|
4523
5199
|
exportSelectedTags: this.exportSelectedTags,
|
|
4524
|
-
issueTrackerConfig: this.issueTrackerConfig
|
|
5200
|
+
issueTrackerConfig: this.issueTrackerConfig,
|
|
5201
|
+
isLoginSetupOpen: this.isLoginSetupOpen,
|
|
5202
|
+
loginSetupConfig: this.loginSetupConfig,
|
|
5203
|
+
loginSetupDraftPath: this.loginSetupDraftPath,
|
|
5204
|
+
loginSetupDraftFunctions: this.loginSetupDraftFunctions,
|
|
5205
|
+
loginSetupDraftBeforeFn: this.loginSetupDraftBeforeFn,
|
|
5206
|
+
loginSetupDraftBeforeEachFn: this.loginSetupDraftBeforeEachFn,
|
|
5207
|
+
loginSetupDraftHasContent: this.loginSetupDraftHasContent
|
|
4525
5208
|
}, this.t.bind(this))}`;
|
|
4526
5209
|
;
|
|
4527
5210
|
this.shadow.getElementById("lang-select").addEventListener(
|
|
@@ -4580,24 +5263,47 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4580
5263
|
this.shadow.querySelectorAll("[data-export-tag]").forEach(
|
|
4581
5264
|
(el) => el.addEventListener("click", () => this.toggleExportTag(el.dataset["exportTag"] ?? ""))
|
|
4582
5265
|
);
|
|
5266
|
+
this.shadow.getElementById("btn-open-login-setup")?.addEventListener("click", () => this.openLoginSetupPanel());
|
|
5267
|
+
this.shadow.getElementById("btn-login-setup-cancel")?.addEventListener("click", () => this.closeLoginSetupPanel());
|
|
5268
|
+
this.shadow.getElementById("btn-login-setup-pick-file")?.addEventListener("click", () => this.loginSetupPickFile());
|
|
5269
|
+
this.shadow.getElementById("btn-login-setup-create-scaffold")?.addEventListener("click", () => this.loginSetupCreateScaffold());
|
|
5270
|
+
this.shadow.getElementById("btn-login-setup-rescan")?.addEventListener("click", () => this.loginSetupRescan());
|
|
5271
|
+
this.shadow.getElementById("btn-login-setup-clear")?.addEventListener("click", () => this.clearLoginSetupConfig());
|
|
5272
|
+
this.shadow.getElementById("btn-login-setup-save")?.addEventListener("click", async () => {
|
|
5273
|
+
const pathEl = this.shadow.getElementById("login-setup-filepath");
|
|
5274
|
+
const beforeEl = this.shadow.getElementById("login-setup-before-fn");
|
|
5275
|
+
const beforeEachEl = this.shadow.getElementById("login-setup-before-each-fn");
|
|
5276
|
+
await this.saveLoginSetupConfig({
|
|
5277
|
+
enabled: true,
|
|
5278
|
+
filePath: pathEl?.value.trim() ?? this.loginSetupDraftPath,
|
|
5279
|
+
fileContent: this.loginSetupDraftContent,
|
|
5280
|
+
detectedFunctions: this.loginSetupDraftFunctions,
|
|
5281
|
+
beforeFn: beforeEl?.value || null,
|
|
5282
|
+
beforeEachFn: beforeEachEl?.value || null
|
|
5283
|
+
});
|
|
5284
|
+
});
|
|
5285
|
+
;
|
|
4583
5286
|
this.shadow.getElementById("file-input").addEventListener("change", async (e) => {
|
|
4584
5287
|
const file = e.target.files?.[0];
|
|
4585
5288
|
if (!file) return;
|
|
4586
5289
|
try {
|
|
4587
5290
|
await this.importAllData(file);
|
|
4588
|
-
|
|
5291
|
+
showToast(this.t("CONFIG.IMPORT_SUCCESS"));
|
|
4589
5292
|
} catch (err) {
|
|
4590
|
-
|
|
5293
|
+
showToast(err.message ?? this.t("CONFIG.IMPORT_ERROR"), false);
|
|
4591
5294
|
}
|
|
4592
5295
|
const target = e.target;
|
|
4593
5296
|
if (target) target.value = "";
|
|
4594
5297
|
});
|
|
4595
5298
|
}
|
|
4596
5299
|
};
|
|
4597
|
-
if (!customElements.get("e2e-configuration")) {
|
|
4598
|
-
customElements.define("e2e-configuration", ConfigurationElement);
|
|
5300
|
+
if (!customElements.get("lib-e2e-configuration")) {
|
|
5301
|
+
customElements.define("lib-e2e-configuration", ConfigurationElement);
|
|
4599
5302
|
}
|
|
4600
5303
|
|
|
5304
|
+
// src/components/advanced-test-editor/advanced-test-editor.ts
|
|
5305
|
+
import Swal from "sweetalert2";
|
|
5306
|
+
|
|
4601
5307
|
// src/components/advanced-test-editor/advanced-test-editor.styles.ts
|
|
4602
5308
|
var ADVANCED_TEST_EDITOR_STYLES = `
|
|
4603
5309
|
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; overflow: hidden; }
|
|
@@ -4855,6 +5561,7 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
4855
5561
|
isCreatingFolder = false;
|
|
4856
5562
|
collapsedDirs = /* @__PURE__ */ new Set();
|
|
4857
5563
|
sidebarWidth = 220;
|
|
5564
|
+
loginSetupConfig = null;
|
|
4858
5565
|
hasPermission = false;
|
|
4859
5566
|
needsReauth = false;
|
|
4860
5567
|
_dirHandle = null;
|
|
@@ -4915,20 +5622,51 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
4915
5622
|
const name = rawName.trim().replace(/\.cy\.ts$/, "");
|
|
4916
5623
|
if (!name) return;
|
|
4917
5624
|
const fileName = `${name}.cy.ts`;
|
|
4918
|
-
const
|
|
5625
|
+
const cfg = this.loginSetupConfig;
|
|
5626
|
+
if (cfg?.enabled && (cfg.beforeFn || cfg.beforeEachFn)) {
|
|
5627
|
+
const e2eName = this._e2eHandle.name ?? "e2e";
|
|
5628
|
+
const fromPath = `cypress/${e2eName}/${fileName}`;
|
|
5629
|
+
const importPath = buildLoginImportPath(fromPath, cfg.filePath);
|
|
5630
|
+
const { importLine, beforeBlock, beforeEachBlock } = buildLoginBlocks(
|
|
5631
|
+
importPath,
|
|
5632
|
+
cfg.beforeFn,
|
|
5633
|
+
cfg.beforeEachFn
|
|
5634
|
+
);
|
|
5635
|
+
const header = importLine ? `${importLine}
|
|
4919
5636
|
|
|
5637
|
+
` : "";
|
|
5638
|
+
const inner = [beforeBlock, beforeEachBlock].filter(Boolean).join("");
|
|
5639
|
+
const template = `${header}describe('${escapeSingleQuotes(name)}', () => {
|
|
5640
|
+
${inner}
|
|
4920
5641
|
it('should ', () => {
|
|
4921
5642
|
|
|
4922
5643
|
});
|
|
4923
5644
|
|
|
4924
5645
|
});
|
|
4925
5646
|
`;
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
5647
|
+
try {
|
|
5648
|
+
const fileHandle = await this._e2eHandle.getFileHandle(fileName, { create: true });
|
|
5649
|
+
const writable = await fileHandle.createWritable();
|
|
5650
|
+
await writable.write(template);
|
|
5651
|
+
await writable.close();
|
|
5652
|
+
} catch {
|
|
5653
|
+
}
|
|
5654
|
+
} else {
|
|
5655
|
+
const template = `describe('${escapeSingleQuotes(name)}', () => {
|
|
5656
|
+
|
|
5657
|
+
it('should ', () => {
|
|
5658
|
+
|
|
5659
|
+
});
|
|
5660
|
+
|
|
5661
|
+
});
|
|
5662
|
+
`;
|
|
5663
|
+
try {
|
|
5664
|
+
const fileHandle = await this._e2eHandle.getFileHandle(fileName, { create: true });
|
|
5665
|
+
const writable = await fileHandle.createWritable();
|
|
5666
|
+
await writable.write(template);
|
|
5667
|
+
await writable.close();
|
|
5668
|
+
} catch {
|
|
5669
|
+
}
|
|
4932
5670
|
}
|
|
4933
5671
|
this.isCreatingFile = false;
|
|
4934
5672
|
await this.getFoldersData();
|
|
@@ -4956,6 +5694,30 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
4956
5694
|
const comment = this.testNotes ? this.transformationService.buildBlockComment(this.testNotes) + "\n" : "";
|
|
4957
5695
|
content = this.transformationService.insertItBlock(content, comment + this.testItBlock);
|
|
4958
5696
|
if (!content) return;
|
|
5697
|
+
const cfg = this.loginSetupConfig;
|
|
5698
|
+
if (cfg?.enabled && (cfg.beforeFn || cfg.beforeEachFn)) {
|
|
5699
|
+
const fns = [cfg.beforeFn, cfg.beforeEachFn].filter(Boolean);
|
|
5700
|
+
if (!hasLoginBlocks(content, fns)) {
|
|
5701
|
+
const e2eName = this._e2eHandle?.name ?? "e2e";
|
|
5702
|
+
const fileName = this.selectedFile?.name ?? "";
|
|
5703
|
+
const fromPath = `cypress/${e2eName}/${fileName}`;
|
|
5704
|
+
const importPath = buildLoginImportPath(fromPath, cfg.filePath);
|
|
5705
|
+
const { importLine, beforeBlock, beforeEachBlock } = buildLoginBlocks(importPath, cfg.beforeFn, cfg.beforeEachFn);
|
|
5706
|
+
const result = await Swal.fire({
|
|
5707
|
+
title: this.t("CONFIG.LOGIN_SETUP_INJECT_TITLE"),
|
|
5708
|
+
html: `<div style="padding:10px 18px 14px;color:#8b949e;font-size:13px;line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">${this.t("CONFIG.LOGIN_SETUP_INJECT_TEXT")}</div>`,
|
|
5709
|
+
icon: "question",
|
|
5710
|
+
showCancelButton: true,
|
|
5711
|
+
confirmButtonText: this.t("CONFIG.LOGIN_SETUP_INJECT_CONFIRM"),
|
|
5712
|
+
cancelButtonText: this.t("CONFIG.LOGIN_SETUP_CANCEL_BTN"),
|
|
5713
|
+
confirmButtonColor: "#1f6feb",
|
|
5714
|
+
width: 460
|
|
5715
|
+
});
|
|
5716
|
+
if (result.isConfirmed) {
|
|
5717
|
+
content = injectLoginBlocksIntoExisting(content, importLine, beforeBlock, beforeEachBlock);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
4959
5721
|
const writable = await this.selectedFileHandle.createWritable();
|
|
4960
5722
|
await writable.write(content);
|
|
4961
5723
|
await writable.close();
|
|
@@ -5150,8 +5912,8 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
5150
5912
|
});
|
|
5151
5913
|
}
|
|
5152
5914
|
};
|
|
5153
|
-
if (!customElements.get("advanced-test-editor")) {
|
|
5154
|
-
customElements.define("advanced-test-editor", AdvancedTestEditorElement);
|
|
5915
|
+
if (!customElements.get("lib-e2e-advanced-test-editor")) {
|
|
5916
|
+
customElements.define("lib-e2e-advanced-test-editor", AdvancedTestEditorElement);
|
|
5155
5917
|
}
|
|
5156
5918
|
|
|
5157
5919
|
// src/components/file-preview/file-preview.styles.ts
|
|
@@ -5516,8 +6278,8 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
5516
6278
|
});
|
|
5517
6279
|
}
|
|
5518
6280
|
};
|
|
5519
|
-
if (!customElements.get("file-preview")) {
|
|
5520
|
-
customElements.define("file-preview", FilePreviewElement);
|
|
6281
|
+
if (!customElements.get("lib-e2e-file-preview")) {
|
|
6282
|
+
customElements.define("lib-e2e-file-preview", FilePreviewElement);
|
|
5521
6283
|
}
|
|
5522
6284
|
|
|
5523
6285
|
// src/components/help-panel/help-panel.styles.ts
|
|
@@ -5612,12 +6374,12 @@ var REFERENCE_SECTIONS = [
|
|
|
5612
6374
|
{ title: "HELP.SEC_PANELS", items: ["HELP.PANEL_TESTS", "HELP.PANEL_COMMANDS", "HELP.PANEL_CONFIG", "HELP.PANEL_FILES"] },
|
|
5613
6375
|
{ title: "HELP.SEC_SELECTOR", items: ["HELP.SELECTOR_1", "HELP.SELECTOR_2"] },
|
|
5614
6376
|
{ title: "HELP.SEC_DATA", items: ["HELP.DATA_1", "HELP.DATA_2"] },
|
|
5615
|
-
{ title: "HELP.SEC_ADVANCED", items: ["HELP.ADV_INVISIBLE", "HELP.ADV_RUNNER", "HELP.ADV_CROSSAPP", "HELP.ADV_DRAG"] }
|
|
6377
|
+
{ title: "HELP.SEC_ADVANCED", items: ["HELP.ADV_INVISIBLE", "HELP.ADV_RUNNER", "HELP.ADV_CROSSAPP", "HELP.ADV_DRAG", "HELP.ADV_LOGIN"] }
|
|
5616
6378
|
];
|
|
5617
6379
|
var GUIDE_SECTIONS = [
|
|
5618
6380
|
{ title: "HELP.G_SEC_WORKFLOW", items: ["HELP.G_WF_1", "HELP.G_WF_2", "HELP.G_WF_3", "HELP.G_WF_4", "HELP.G_WF_5"] },
|
|
5619
|
-
{ title: "HELP.G_SEC_COVERED", items: ["HELP.G_COV_1", "HELP.G_COV_2", "HELP.G_COV_3", "HELP.G_COV_4", "HELP.G_COV_5", "HELP.G_COV_6"] },
|
|
5620
|
-
{ title: "HELP.G_SEC_NOTCOVERED", items: ["HELP.G_NC_1", "HELP.G_NC_2", "HELP.G_NC_3", "HELP.G_NC_4", "HELP.G_NC_5"
|
|
6381
|
+
{ title: "HELP.G_SEC_COVERED", items: ["HELP.G_COV_1", "HELP.G_COV_2", "HELP.G_COV_3", "HELP.G_COV_4", "HELP.G_COV_5", "HELP.G_COV_6", "HELP.G_COV_7"] },
|
|
6382
|
+
{ title: "HELP.G_SEC_NOTCOVERED", items: ["HELP.G_NC_1", "HELP.G_NC_2", "HELP.G_NC_3", "HELP.G_NC_4", "HELP.G_NC_5"] }
|
|
5621
6383
|
];
|
|
5622
6384
|
function sectionsHtml(sections, t) {
|
|
5623
6385
|
return sections.map(
|
|
@@ -5664,12 +6426,12 @@ var HelpPanelElement = class extends HTMLElement {
|
|
|
5664
6426
|
});
|
|
5665
6427
|
}
|
|
5666
6428
|
};
|
|
5667
|
-
if (!customElements.get("help-panel")) {
|
|
5668
|
-
customElements.define("help-panel", HelpPanelElement);
|
|
6429
|
+
if (!customElements.get("lib-e2e-help-panel")) {
|
|
6430
|
+
customElements.define("lib-e2e-help-panel", HelpPanelElement);
|
|
5669
6431
|
}
|
|
5670
6432
|
|
|
5671
6433
|
// src/components/lib-e2e-recorder/lib-e2e-recorder.ts
|
|
5672
|
-
import
|
|
6434
|
+
import Swal2 from "sweetalert2";
|
|
5673
6435
|
|
|
5674
6436
|
// src/components/lib-e2e-recorder/lib-e2e-recorder.styles.ts
|
|
5675
6437
|
var DIRECTIONS = ["up-left", "up-right", "down-left", "down-right"];
|
|
@@ -5870,6 +6632,139 @@ function renderRecorderWidget(rec, paused, t) {
|
|
|
5870
6632
|
<div class="rec-badge">${paused ? t("RECORDER.BADGE_PAUSED") : t("RECORDER.BADGE_REC")}</div>`;
|
|
5871
6633
|
}
|
|
5872
6634
|
|
|
6635
|
+
// src/components/lib-e2e-recorder/assertion-builder.ts
|
|
6636
|
+
var NO_VALUE_ASSERTIONS = /* @__PURE__ */ new Set(["be.visible", "not.exist", "be.disabled", "be.checked"]);
|
|
6637
|
+
function injectAssertionBuilder(container, t, onAddCommand) {
|
|
6638
|
+
container.insertAdjacentHTML("beforeend", `
|
|
6639
|
+
<div id="assertion-builder"
|
|
6640
|
+
style="padding:10px 12px;border-top:1px solid #21262d;background:#0d1117">
|
|
6641
|
+
<div style="font-size:10px;color:#484f58;text-transform:uppercase;letter-spacing:.8px;font-weight:600;margin-bottom:8px">
|
|
6642
|
+
${t("RECORDER.ASSERTION_SECTION")}
|
|
6643
|
+
</div>
|
|
6644
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:flex-end">
|
|
6645
|
+
<input id="assert-selector" type="text"
|
|
6646
|
+
placeholder="${t("RECORDER.ASSERT_SELECTOR_PH").replace(/"/g, """)}"
|
|
6647
|
+
style="flex:2;min-width:140px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6648
|
+
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none;
|
|
6649
|
+
font-family:'Cascadia Code','Fira Code',monospace"/>
|
|
6650
|
+
<select id="assert-type"
|
|
6651
|
+
style="padding:5px 8px;background:#161b22;border:1px solid #30363d;border-radius:5px;
|
|
6652
|
+
color:#c9d1d9;font-size:11px;outline:none;cursor:pointer;flex-shrink:0">
|
|
6653
|
+
<option value="be.visible">be.visible</option>
|
|
6654
|
+
<option value="not.exist">not.exist</option>
|
|
6655
|
+
<option value="be.disabled">be.disabled</option>
|
|
6656
|
+
<option value="be.checked">be.checked</option>
|
|
6657
|
+
<option value="contain.text">contain.text</option>
|
|
6658
|
+
<option value="have.value">have.value</option>
|
|
6659
|
+
<option value="have.length">have.length</option>
|
|
6660
|
+
<option value="have.class">have.class</option>
|
|
6661
|
+
<option value="have.attr">have.attr</option>
|
|
6662
|
+
</select>
|
|
6663
|
+
<input id="assert-value" type="text" placeholder="${t("RECORDER.ASSERT_VALUE_PH")}"
|
|
6664
|
+
style="flex:2;min-width:110px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6665
|
+
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none"/>
|
|
6666
|
+
<button id="btn-add-assertion"
|
|
6667
|
+
style="padding:5px 12px;border:none;border-radius:5px;cursor:pointer;
|
|
6668
|
+
font-size:11px;font-weight:500;background:#2f81f7;color:#fff;
|
|
6669
|
+
white-space:nowrap;flex-shrink:0">
|
|
6670
|
+
${t("RECORDER.ASSERT_ADD_BTN")}
|
|
6671
|
+
</button>
|
|
6672
|
+
</div>
|
|
6673
|
+
</div>`);
|
|
6674
|
+
document.getElementById("btn-add-assertion")?.addEventListener("click", () => {
|
|
6675
|
+
const sel = document.getElementById("assert-selector").value.trim();
|
|
6676
|
+
const type = document.getElementById("assert-type").value;
|
|
6677
|
+
const val = document.getElementById("assert-value").value.trim();
|
|
6678
|
+
if (!sel) return;
|
|
6679
|
+
const s = escapeSingleQuotes(sel);
|
|
6680
|
+
const v = escapeSingleQuotes(val);
|
|
6681
|
+
const cmd = NO_VALUE_ASSERTIONS.has(type) || !val ? `cy.get('${s}').should('${type}')` : `cy.get('${s}').should('${type}', '${v}')`;
|
|
6682
|
+
onAddCommand(cmd);
|
|
6683
|
+
document.getElementById("assert-selector").value = "";
|
|
6684
|
+
document.getElementById("assert-value").value = "";
|
|
6685
|
+
});
|
|
6686
|
+
}
|
|
6687
|
+
|
|
6688
|
+
// src/components/lib-e2e-recorder/filesystem-setup.ts
|
|
6689
|
+
function mountFilesystemSetupContent(container, t, onSkip, onSelect) {
|
|
6690
|
+
container.innerHTML = `
|
|
6691
|
+
<div style="padding:16px 20px 20px;color:#8b949e;font-size:13px;line-height:1.7">
|
|
6692
|
+
<p>${t("RECORDER.FS_INTRO_HTML")}</p>
|
|
6693
|
+
<p style="margin-top:10px;margin-bottom:6px;font-size:11px;color:#8b949e">
|
|
6694
|
+
${t("RECORDER.FS_STRUCTURE_HINT_HTML")}
|
|
6695
|
+
</p>
|
|
6696
|
+
<pre style="margin:0;padding:10px 14px;background:#0d1117;border:1px solid #21262d;
|
|
6697
|
+
border-radius:8px;font-size:11px;color:#c9d1d9;line-height:1.8;
|
|
6698
|
+
font-family:'Cascadia Code','Fira Code','Consolas',monospace">
|
|
6699
|
+
cypress/ <span style="color:#484f58">${t("RECORDER.FS_TREE_PICK_HINT")}</span>
|
|
6700
|
+
\u2514\u2500\u2500 e2e/ <span style="color:#484f58">${t("RECORDER.FS_TREE_READ_HINT")}</span>
|
|
6701
|
+
\u2514\u2500\u2500 *.cy.ts</pre>
|
|
6702
|
+
<p style="margin-top:8px;font-size:11px;color:#484f58">
|
|
6703
|
+
${t("RECORDER.FS_PERMISSION_NOTE")}
|
|
6704
|
+
</p>
|
|
6705
|
+
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:20px">
|
|
6706
|
+
<button id="fs-skip"
|
|
6707
|
+
style="padding:7px 16px;border:1px solid #30363d;border-radius:6px;cursor:pointer;
|
|
6708
|
+
font-size:12px;font-weight:500;background:transparent;color:#8b949e">
|
|
6709
|
+
${t("RECORDER.FS_LATER_BTN")}
|
|
6710
|
+
</button>
|
|
6711
|
+
<button id="fs-select"
|
|
6712
|
+
style="padding:7px 16px;border:none;border-radius:6px;cursor:pointer;
|
|
6713
|
+
font-size:12px;font-weight:500;background:#2f81f7;color:#fff">
|
|
6714
|
+
${t("RECORDER.FS_SELECT_BTN")}
|
|
6715
|
+
</button>
|
|
6716
|
+
</div>
|
|
6717
|
+
</div>`;
|
|
6718
|
+
document.getElementById("fs-skip")?.addEventListener("click", () => {
|
|
6719
|
+
void onSkip();
|
|
6720
|
+
});
|
|
6721
|
+
document.getElementById("fs-select")?.addEventListener("click", () => {
|
|
6722
|
+
void onSelect();
|
|
6723
|
+
});
|
|
6724
|
+
}
|
|
6725
|
+
|
|
6726
|
+
// src/utils/swal-mount.utils.ts
|
|
6727
|
+
function mountComponentInSwal(tagName, container, props, events) {
|
|
6728
|
+
const el = document.createElement(tagName);
|
|
6729
|
+
Object.assign(el, props);
|
|
6730
|
+
container.appendChild(el);
|
|
6731
|
+
for (const { name, handler } of events ?? []) {
|
|
6732
|
+
el.addEventListener(name, handler);
|
|
6733
|
+
}
|
|
6734
|
+
return el;
|
|
6735
|
+
}
|
|
6736
|
+
|
|
6737
|
+
// src/utils/widget-position.utils.ts
|
|
6738
|
+
var TOGGLE_MARGIN = 30;
|
|
6739
|
+
var DRAG_THRESHOLD = 5;
|
|
6740
|
+
var DEFAULT_EDGE_OFFSET = 46;
|
|
6741
|
+
function boxOffsetForDirection(dir) {
|
|
6742
|
+
return {
|
|
6743
|
+
x: dir.endsWith("left") ? 144 : 46,
|
|
6744
|
+
y: dir.startsWith("up") ? 144 : 46
|
|
6745
|
+
};
|
|
6746
|
+
}
|
|
6747
|
+
function clampTogglePosition(x, y, vw, vh, margin = TOGGLE_MARGIN) {
|
|
6748
|
+
const maxX = Math.max(margin, vw - margin);
|
|
6749
|
+
const maxY = Math.max(margin, vh - margin);
|
|
6750
|
+
return {
|
|
6751
|
+
x: Math.min(Math.max(x, margin), maxX),
|
|
6752
|
+
y: Math.min(Math.max(y, margin), maxY)
|
|
6753
|
+
};
|
|
6754
|
+
}
|
|
6755
|
+
function resolveExpandDirection(x, y, vw, vh) {
|
|
6756
|
+
const vertical = y < vh / 2 ? "down" : "up";
|
|
6757
|
+
const horizontal = x < vw / 2 ? "right" : "left";
|
|
6758
|
+
return `${vertical}-${horizontal}`;
|
|
6759
|
+
}
|
|
6760
|
+
function defaultTogglePosition(vw, vh) {
|
|
6761
|
+
return { x: vw - DEFAULT_EDGE_OFFSET, y: vh - DEFAULT_EDGE_OFFSET };
|
|
6762
|
+
}
|
|
6763
|
+
function boxTopLeftFor(toggleCentre, dir) {
|
|
6764
|
+
const off = boxOffsetForDirection(dir);
|
|
6765
|
+
return { x: toggleCentre.x - off.x, y: toggleCentre.y - off.y };
|
|
6766
|
+
}
|
|
6767
|
+
|
|
5873
6768
|
// src/utils/fixture-convert.utils.ts
|
|
5874
6769
|
function toFixtureInterceptors(interceptors, fixtures) {
|
|
5875
6770
|
const fixtureNames = new Set(fixtures.map((f) => f.name));
|
|
@@ -5989,6 +6884,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
5989
6884
|
this.recording.destroy();
|
|
5990
6885
|
this._needsRecordingRebuild = true;
|
|
5991
6886
|
}
|
|
6887
|
+
// HttpMonitor reads extendedHttpCommands and fixtureMode synchronously inside
|
|
6888
|
+
// fetch/XHR interception handlers, so they must live in localStorage (sync).
|
|
6889
|
+
// IndexedDB is the persistent source of truth; this method mirrors it into
|
|
6890
|
+
// localStorage on every mount so the sync cache is always fresh.
|
|
5992
6891
|
async initHttpConfig() {
|
|
5993
6892
|
const config = await this.persistence.getConfig("extendedHttpCommands");
|
|
5994
6893
|
if (config === null) {
|
|
@@ -6035,10 +6934,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6035
6934
|
this.langUnsub = this.translation.onLangChange(() => this.render());
|
|
6036
6935
|
}
|
|
6037
6936
|
showSelectorPicker(target) {
|
|
6038
|
-
import("./selector-picker-
|
|
6039
|
-
const existing = document.querySelector("selector-picker");
|
|
6937
|
+
import("./selector-picker-KJUXPXHH.js").then(() => {
|
|
6938
|
+
const existing = document.querySelector("lib-e2e-selector-picker");
|
|
6040
6939
|
if (existing) existing.remove();
|
|
6041
|
-
const picker = document.createElement("selector-picker");
|
|
6940
|
+
const picker = document.createElement("lib-e2e-selector-picker");
|
|
6042
6941
|
picker.targetElement = target;
|
|
6043
6942
|
picker.recording = this.recording;
|
|
6044
6943
|
picker.translation = this.translation;
|
|
@@ -6100,7 +6999,7 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6100
6999
|
}
|
|
6101
7000
|
async promptResumeOrDiscard(session) {
|
|
6102
7001
|
const t = this.translation.translate.bind(this.translation);
|
|
6103
|
-
const result = await
|
|
7002
|
+
const result = await Swal2.fire({
|
|
6104
7003
|
title: t("RECORDER.SESSION_RESUME_TITLE"),
|
|
6105
7004
|
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>`,
|
|
6106
7005
|
showCancelButton: true,
|
|
@@ -6319,8 +7218,9 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6319
7218
|
this.showFilesystemSetupDialog();
|
|
6320
7219
|
}
|
|
6321
7220
|
showFilesystemSetupDialog() {
|
|
6322
|
-
|
|
6323
|
-
|
|
7221
|
+
const t = this.translation.translate.bind(this.translation);
|
|
7222
|
+
Swal2.fire({
|
|
7223
|
+
title: t("RECORDER.FS_TITLE"),
|
|
6324
7224
|
html: '<div id="fs-setup-content"></div>',
|
|
6325
7225
|
showCloseButton: true,
|
|
6326
7226
|
showConfirmButton: false,
|
|
@@ -6329,277 +7229,171 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6329
7229
|
setSwal2DataCyAttribute();
|
|
6330
7230
|
const container = document.getElementById("fs-setup-content");
|
|
6331
7231
|
if (!container) return;
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
<button id="fs-skip"
|
|
6349
|
-
style="padding:7px 16px;border:1px solid #30363d;border-radius:6px;cursor:pointer;
|
|
6350
|
-
font-size:12px;font-weight:500;background:transparent;color:#8b949e">
|
|
6351
|
-
${this.translation.translate("RECORDER.FS_LATER_BTN")}
|
|
6352
|
-
</button>
|
|
6353
|
-
<button id="fs-select"
|
|
6354
|
-
style="padding:7px 16px;border:none;border-radius:6px;cursor:pointer;
|
|
6355
|
-
font-size:12px;font-weight:500;background:#2f81f7;color:#fff">
|
|
6356
|
-
${this.translation.translate("RECORDER.FS_SELECT_BTN")}
|
|
6357
|
-
</button>
|
|
6358
|
-
</div>
|
|
6359
|
-
</div>`;
|
|
6360
|
-
document.getElementById("fs-skip")?.addEventListener("click", async () => {
|
|
6361
|
-
await this.persistence.setConfigKey("allowReadWriteFiles", "false");
|
|
6362
|
-
Swal.close();
|
|
6363
|
-
});
|
|
6364
|
-
document.getElementById("fs-select")?.addEventListener("click", async () => {
|
|
6365
|
-
try {
|
|
6366
|
-
await this.persistence.requestDirectoryPermissions();
|
|
6367
|
-
Swal.close();
|
|
6368
|
-
showToast(this.translation.translate("RECORDER.FS_SUCCESS_TOAST"));
|
|
6369
|
-
} catch (e) {
|
|
6370
|
-
if (e?.name !== "AbortError") {
|
|
6371
|
-
showToast(this.translation.translate("RECORDER.FS_ERROR_TOAST"), false);
|
|
7232
|
+
mountFilesystemSetupContent(
|
|
7233
|
+
container,
|
|
7234
|
+
t,
|
|
7235
|
+
async () => {
|
|
7236
|
+
await this.persistence.setConfigKey("allowReadWriteFiles", "false");
|
|
7237
|
+
Swal2.close();
|
|
7238
|
+
},
|
|
7239
|
+
async () => {
|
|
7240
|
+
try {
|
|
7241
|
+
await this.persistence.requestDirectoryPermissions();
|
|
7242
|
+
Swal2.close();
|
|
7243
|
+
showToast(t("RECORDER.FS_SUCCESS_TOAST"));
|
|
7244
|
+
} catch (e) {
|
|
7245
|
+
if (e?.name !== "AbortError") {
|
|
7246
|
+
showToast(t("RECORDER.FS_ERROR_TOAST"), false);
|
|
7247
|
+
}
|
|
6372
7248
|
}
|
|
6373
7249
|
}
|
|
6374
|
-
|
|
7250
|
+
);
|
|
6375
7251
|
}
|
|
6376
7252
|
});
|
|
6377
7253
|
}
|
|
6378
7254
|
// ── dialogs ──────────────────────────────────────────────────────────────
|
|
6379
7255
|
showCommandsDialog() {
|
|
6380
|
-
this.
|
|
6381
|
-
|
|
7256
|
+
this.openSwalDialog(
|
|
7257
|
+
"isCommandsDialogOpen",
|
|
7258
|
+
"commands-modal-content",
|
|
7259
|
+
{
|
|
6382
7260
|
title: this.translation.translate("MAIN_FRAME.DIALOG_COMMANDS"),
|
|
6383
7261
|
html: '<div id="commands-modal-content" style="min-height:250px;padding:0"></div>',
|
|
6384
7262
|
showCloseButton: true,
|
|
6385
|
-
showConfirmButton: false,
|
|
6386
7263
|
allowOutsideClick: false,
|
|
6387
7264
|
backdrop: false,
|
|
6388
|
-
width: 640
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
7265
|
+
width: 640
|
|
7266
|
+
},
|
|
7267
|
+
(container) => {
|
|
7268
|
+
const child = mountComponentInSwal("lib-e2e-test-previsualizer", container, {
|
|
7269
|
+
translation: this.translation,
|
|
7270
|
+
commands: this.cypressCommands,
|
|
7271
|
+
interceptors: this.interceptors,
|
|
7272
|
+
editable: true
|
|
7273
|
+
}, [
|
|
7274
|
+
{ name: "deletecommand", handler: (e) => this.recording.removeCommand(e.detail) },
|
|
7275
|
+
{ name: "movecommand", handler: (e) => this.recording.moveCommand(e.detail.from, e.detail.to) },
|
|
7276
|
+
{ name: "deleteinterceptor", handler: (e) => this.recording.removeInterceptor(e.detail) }
|
|
7277
|
+
]);
|
|
7278
|
+
this._previsualizerRef = child;
|
|
7279
|
+
injectAssertionBuilder(container, this.translation.translate.bind(this.translation), (cmd) => this.recording.appendCommand(cmd));
|
|
7280
|
+
},
|
|
7281
|
+
{
|
|
7282
|
+
// Make the container click-through so the page underneath remains interactive
|
|
7283
|
+
onDidOpenPreamble: () => {
|
|
6393
7284
|
const swalContainer = document.querySelector(".swal2-container");
|
|
6394
7285
|
if (swalContainer) {
|
|
6395
7286
|
swalContainer.style.pointerEvents = "none";
|
|
6396
7287
|
const popup = swalContainer.querySelector(".swal2-popup");
|
|
6397
7288
|
if (popup) popup.style.pointerEvents = "all";
|
|
6398
7289
|
}
|
|
6399
|
-
const container = document.getElementById("commands-modal-content");
|
|
6400
|
-
if (!container) return;
|
|
6401
|
-
const child = document.createElement("test-previsualizer");
|
|
6402
|
-
child.translation = this.translation;
|
|
6403
|
-
child.commands = this.cypressCommands;
|
|
6404
|
-
child.interceptors = this.interceptors;
|
|
6405
|
-
child.editable = true;
|
|
6406
|
-
container.appendChild(child);
|
|
6407
|
-
this._previsualizerRef = child;
|
|
6408
|
-
child.addEventListener("deletecommand", (e) => {
|
|
6409
|
-
this.recording.removeCommand(e.detail);
|
|
6410
|
-
});
|
|
6411
|
-
child.addEventListener("movecommand", (e) => {
|
|
6412
|
-
this.recording.moveCommand(e.detail.from, e.detail.to);
|
|
6413
|
-
});
|
|
6414
|
-
child.addEventListener("deleteinterceptor", (e) => {
|
|
6415
|
-
this.recording.removeInterceptor(e.detail);
|
|
6416
|
-
});
|
|
6417
|
-
const assertionHtml = `
|
|
6418
|
-
<div id="assertion-builder"
|
|
6419
|
-
style="padding:10px 12px;border-top:1px solid #21262d;background:#0d1117">
|
|
6420
|
-
<div style="font-size:10px;color:#484f58;text-transform:uppercase;letter-spacing:.8px;font-weight:600;margin-bottom:8px">
|
|
6421
|
-
${this.translation.translate("RECORDER.ASSERTION_SECTION")}
|
|
6422
|
-
</div>
|
|
6423
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:flex-end">
|
|
6424
|
-
<input id="assert-selector" type="text"
|
|
6425
|
-
placeholder="${this.translation.translate("RECORDER.ASSERT_SELECTOR_PH").replace(/"/g, """)}"
|
|
6426
|
-
style="flex:2;min-width:140px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6427
|
-
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none;
|
|
6428
|
-
font-family:'Cascadia Code','Fira Code',monospace"/>
|
|
6429
|
-
<select id="assert-type"
|
|
6430
|
-
style="padding:5px 8px;background:#161b22;border:1px solid #30363d;border-radius:5px;
|
|
6431
|
-
color:#c9d1d9;font-size:11px;outline:none;cursor:pointer;flex-shrink:0">
|
|
6432
|
-
<option value="be.visible">be.visible</option>
|
|
6433
|
-
<option value="not.exist">not.exist</option>
|
|
6434
|
-
<option value="be.disabled">be.disabled</option>
|
|
6435
|
-
<option value="be.checked">be.checked</option>
|
|
6436
|
-
<option value="contain.text">contain.text</option>
|
|
6437
|
-
<option value="have.value">have.value</option>
|
|
6438
|
-
<option value="have.length">have.length</option>
|
|
6439
|
-
<option value="have.class">have.class</option>
|
|
6440
|
-
<option value="have.attr">have.attr</option>
|
|
6441
|
-
</select>
|
|
6442
|
-
<input id="assert-value" type="text" placeholder="${this.translation.translate("RECORDER.ASSERT_VALUE_PH")}"
|
|
6443
|
-
style="flex:2;min-width:110px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6444
|
-
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none"/>
|
|
6445
|
-
<button id="btn-add-assertion"
|
|
6446
|
-
style="padding:5px 12px;border:none;border-radius:5px;cursor:pointer;
|
|
6447
|
-
font-size:11px;font-weight:500;background:#2f81f7;color:#fff;
|
|
6448
|
-
white-space:nowrap;flex-shrink:0">
|
|
6449
|
-
${this.translation.translate("RECORDER.ASSERT_ADD_BTN")}
|
|
6450
|
-
</button>
|
|
6451
|
-
</div>
|
|
6452
|
-
</div>`;
|
|
6453
|
-
container.insertAdjacentHTML("beforeend", assertionHtml);
|
|
6454
|
-
const NO_VALUE_ASSERTIONS = /* @__PURE__ */ new Set(["be.visible", "not.exist", "be.disabled", "be.checked"]);
|
|
6455
|
-
document.getElementById("btn-add-assertion")?.addEventListener("click", () => {
|
|
6456
|
-
const sel = document.getElementById("assert-selector").value.trim();
|
|
6457
|
-
const type = document.getElementById("assert-type").value;
|
|
6458
|
-
const val = document.getElementById("assert-value").value.trim();
|
|
6459
|
-
if (!sel) return;
|
|
6460
|
-
const s = escapeSingleQuotes(sel);
|
|
6461
|
-
const v = escapeSingleQuotes(val);
|
|
6462
|
-
const cmd = NO_VALUE_ASSERTIONS.has(type) || !val ? `cy.get('${s}').should('${type}')` : `cy.get('${s}').should('${type}', '${v}')`;
|
|
6463
|
-
this.recording.appendCommand(cmd);
|
|
6464
|
-
document.getElementById("assert-selector").value = "";
|
|
6465
|
-
document.getElementById("assert-value").value = "";
|
|
6466
|
-
});
|
|
6467
7290
|
},
|
|
6468
|
-
|
|
6469
|
-
this.isCommandsDialogOpen = false;
|
|
7291
|
+
onWillClose: () => {
|
|
6470
7292
|
this._previsualizerRef = null;
|
|
6471
7293
|
}
|
|
6472
|
-
}
|
|
6473
|
-
|
|
6474
|
-
});
|
|
7294
|
+
}
|
|
7295
|
+
);
|
|
6475
7296
|
}
|
|
6476
7297
|
showSavedTestsDialog() {
|
|
6477
|
-
this.
|
|
6478
|
-
|
|
7298
|
+
this.openSwalDialog(
|
|
7299
|
+
"isSavedTestsDialogOpen",
|
|
7300
|
+
"saved-tests-modal-content",
|
|
7301
|
+
{
|
|
6479
7302
|
title: this.translation.translate("MAIN_FRAME.DIALOG_SAVED_TESTS"),
|
|
6480
7303
|
html: '<div id="saved-tests-modal-content" style="min-height:250px;padding:0"></div>',
|
|
6481
7304
|
showCloseButton: true,
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
child.translation = this.translation;
|
|
6493
|
-
child.issueTrackerConfig = this.issueTrackerConfig;
|
|
6494
|
-
container.appendChild(child);
|
|
6495
|
-
},
|
|
6496
|
-
willClose: () => {
|
|
6497
|
-
this.isSavedTestsDialogOpen = false;
|
|
6498
|
-
}
|
|
6499
|
-
});
|
|
6500
|
-
this.resizePopup();
|
|
6501
|
-
});
|
|
7305
|
+
width: 680
|
|
7306
|
+
},
|
|
7307
|
+
(container) => {
|
|
7308
|
+
mountComponentInSwal("lib-e2e-test-editor", container, {
|
|
7309
|
+
persistence: this.persistence,
|
|
7310
|
+
translation: this.translation,
|
|
7311
|
+
issueTrackerConfig: this.issueTrackerConfig
|
|
7312
|
+
});
|
|
7313
|
+
}
|
|
7314
|
+
);
|
|
6502
7315
|
}
|
|
6503
7316
|
showSaveTestDialog() {
|
|
6504
|
-
this.
|
|
6505
|
-
|
|
7317
|
+
this.openSwalDialog(
|
|
7318
|
+
"isSaveTestDialogOpen",
|
|
7319
|
+
"save-test-modal-content",
|
|
7320
|
+
{
|
|
6506
7321
|
title: this.translation.translate("MAIN_FRAME.DIALOG_SAVE"),
|
|
6507
7322
|
html: '<div id="save-test-modal-content" style="padding:0"></div>',
|
|
6508
|
-
showCloseButton:
|
|
6509
|
-
showConfirmButton: false,
|
|
7323
|
+
showCloseButton: false,
|
|
6510
7324
|
allowOutsideClick: false,
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
child.issueTrackerConfig = this.issueTrackerConfig;
|
|
6520
|
-
container.appendChild(child);
|
|
6521
|
-
child.addEventListener("savetest", (e) => {
|
|
7325
|
+
allowEscapeKey: false
|
|
7326
|
+
},
|
|
7327
|
+
(container) => {
|
|
7328
|
+
mountComponentInSwal("lib-e2e-save-test", container, {
|
|
7329
|
+
translation: this.translation,
|
|
7330
|
+
issueTrackerConfig: this.issueTrackerConfig
|
|
7331
|
+
}, [
|
|
7332
|
+
{ name: "savetest", handler: (e) => {
|
|
6522
7333
|
const { description, notes, tags, ticketId } = e.detail ?? {};
|
|
6523
7334
|
this.onSaveTest(description ?? null, tags ?? [], notes ?? "", ticketId ?? "");
|
|
6524
|
-
|
|
6525
|
-
}
|
|
6526
|
-
|
|
7335
|
+
Swal2.close();
|
|
7336
|
+
} },
|
|
7337
|
+
{ name: "saveandexport", handler: (e) => {
|
|
6527
7338
|
const { description, notes, tags, ticketId } = e.detail ?? {};
|
|
6528
7339
|
this.onSaveAndExportTest(description ?? null, tags ?? [], notes ?? "", ticketId ?? "");
|
|
6529
|
-
|
|
6530
|
-
}
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
});
|
|
6536
|
-
});
|
|
7340
|
+
Swal2.close();
|
|
7341
|
+
} }
|
|
7342
|
+
]);
|
|
7343
|
+
},
|
|
7344
|
+
{ skipResize: true }
|
|
7345
|
+
);
|
|
6537
7346
|
}
|
|
6538
7347
|
showSettingsDialog() {
|
|
6539
|
-
this.
|
|
6540
|
-
|
|
7348
|
+
this.openSwalDialog(
|
|
7349
|
+
"isSettingsDialogOpen",
|
|
7350
|
+
"settings-modal-content",
|
|
7351
|
+
{
|
|
6541
7352
|
title: this.translation.translate("MAIN_FRAME.SETTINGS"),
|
|
6542
7353
|
html: '<div id="settings-modal-content" style="padding:0"></div>',
|
|
6543
7354
|
showCloseButton: true,
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
const child = document.createElement("e2e-configuration");
|
|
6553
|
-
child.persistence = this.persistence;
|
|
6554
|
-
child.translation = this.translation;
|
|
6555
|
-
container.appendChild(child);
|
|
6556
|
-
child.addEventListener("smartselectorchange", (e) => {
|
|
7355
|
+
width: 520
|
|
7356
|
+
},
|
|
7357
|
+
(container) => {
|
|
7358
|
+
mountComponentInSwal("lib-e2e-configuration", container, {
|
|
7359
|
+
persistence: this.persistence,
|
|
7360
|
+
translation: this.translation
|
|
7361
|
+
}, [
|
|
7362
|
+
{ name: "smartselectorchange", handler: (e) => {
|
|
6557
7363
|
this.smartSelectorEnabled = e.detail;
|
|
6558
|
-
}
|
|
6559
|
-
|
|
7364
|
+
} },
|
|
7365
|
+
{ name: "starthiddenchange", handler: (e) => {
|
|
6560
7366
|
this.isVisible = !e.detail;
|
|
6561
7367
|
this.style.display = this.isVisible ? "" : "none";
|
|
6562
|
-
}
|
|
6563
|
-
|
|
6564
|
-
|
|
7368
|
+
} },
|
|
7369
|
+
{ name: "resetwidgetposition", handler: () => this.resetWidgetPosition() },
|
|
7370
|
+
{ name: "issuetrackerchange", handler: (e) => {
|
|
6565
7371
|
this.issueTrackerConfig = e.detail;
|
|
6566
|
-
}
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
}
|
|
6571
|
-
});
|
|
6572
|
-
this.resizePopup();
|
|
6573
|
-
});
|
|
7372
|
+
} }
|
|
7373
|
+
]);
|
|
7374
|
+
}
|
|
7375
|
+
);
|
|
6574
7376
|
}
|
|
6575
7377
|
showHelpDialog() {
|
|
6576
|
-
this.
|
|
6577
|
-
|
|
7378
|
+
this.openSwalDialog(
|
|
7379
|
+
"isHelpDialogOpen",
|
|
7380
|
+
"help-modal-content",
|
|
7381
|
+
{
|
|
6578
7382
|
title: this.translation.translate("HELP.TITLE"),
|
|
6579
7383
|
html: '<div id="help-modal-content" style="padding:0"></div>',
|
|
6580
7384
|
showCloseButton: true,
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
const child = document.createElement("help-panel");
|
|
6590
|
-
child.translation = this.translation;
|
|
6591
|
-
container.appendChild(child);
|
|
6592
|
-
},
|
|
6593
|
-
willClose: () => {
|
|
6594
|
-
this.isHelpDialogOpen = false;
|
|
6595
|
-
}
|
|
6596
|
-
});
|
|
6597
|
-
this.resizePopup();
|
|
6598
|
-
});
|
|
7385
|
+
width: 560
|
|
7386
|
+
},
|
|
7387
|
+
(container) => {
|
|
7388
|
+
mountComponentInSwal("lib-e2e-help-panel", container, {
|
|
7389
|
+
translation: this.translation
|
|
7390
|
+
});
|
|
7391
|
+
}
|
|
7392
|
+
);
|
|
6599
7393
|
}
|
|
6600
7394
|
showAdvancedEditorDialog(testId) {
|
|
6601
7395
|
this.toggleModal("isAdvancedEditorDialogOpen", () => {
|
|
6602
|
-
|
|
7396
|
+
Swal2.fire({
|
|
6603
7397
|
title: this.translation.translate("MAIN_FRAME.SHOW_ADVANCED_EDITOR"),
|
|
6604
7398
|
html: '<div id="advanced-editor-modal-content" style="flex:1;min-height:0;display:flex;flex-direction:column;padding:0"></div>',
|
|
6605
7399
|
showCloseButton: true,
|
|
@@ -6610,42 +7404,33 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6610
7404
|
didOpen: () => {
|
|
6611
7405
|
makeSwalDraggable();
|
|
6612
7406
|
setSwal2DataCyAttribute();
|
|
6613
|
-
|
|
6614
|
-
if (popup) {
|
|
6615
|
-
popup.style.height = "600px";
|
|
6616
|
-
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
6617
|
-
if (htmlContainer) {
|
|
6618
|
-
htmlContainer.style.flex = "1";
|
|
6619
|
-
htmlContainer.style.minHeight = "0";
|
|
6620
|
-
htmlContainer.style.overflow = "hidden";
|
|
6621
|
-
htmlContainer.style.padding = "0";
|
|
6622
|
-
htmlContainer.style.margin = "0";
|
|
6623
|
-
htmlContainer.style.display = "flex";
|
|
6624
|
-
htmlContainer.style.flexDirection = "column";
|
|
6625
|
-
}
|
|
6626
|
-
}
|
|
7407
|
+
ensurePopupDimensions(Swal2.getPopup(), 600);
|
|
6627
7408
|
const container = document.getElementById("advanced-editor-modal-content");
|
|
6628
7409
|
if (!container) return;
|
|
6629
|
-
const child =
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
7410
|
+
const child = mountComponentInSwal("lib-e2e-advanced-test-editor", container, {
|
|
7411
|
+
persistence: this.persistence,
|
|
7412
|
+
translation: this.translation,
|
|
7413
|
+
...testId !== void 0 ? { testId } : {}
|
|
7414
|
+
}, [
|
|
7415
|
+
{ name: "selectorstrategychange", handler: (e) => {
|
|
7416
|
+
this.recording.selectorStrategy = e.detail;
|
|
7417
|
+
} },
|
|
7418
|
+
{ name: "closemodal", handler: () => Swal2.close() },
|
|
7419
|
+
{ name: "openfileeditor", handler: (e) => {
|
|
7420
|
+
Swal2.close();
|
|
7421
|
+
setTimeout(() => this.showFileEditorDialog(
|
|
7422
|
+
e.detail.handle,
|
|
7423
|
+
e.detail.content,
|
|
7424
|
+
e.detail.fileName,
|
|
7425
|
+
e.detail.testId,
|
|
7426
|
+
e.detail.itBlock,
|
|
7427
|
+
e.detail.interceptorsBlock,
|
|
7428
|
+
e.detail.notes
|
|
7429
|
+
), 150);
|
|
7430
|
+
} }
|
|
7431
|
+
]);
|
|
7432
|
+
void this.persistence.getLoginSetup().then((cfg) => {
|
|
7433
|
+
child.loginSetupConfig = cfg;
|
|
6649
7434
|
});
|
|
6650
7435
|
},
|
|
6651
7436
|
willClose: () => {
|
|
@@ -6653,13 +7438,13 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6653
7438
|
}
|
|
6654
7439
|
});
|
|
6655
7440
|
setTimeout(() => {
|
|
6656
|
-
const popup =
|
|
7441
|
+
const popup = Swal2.getPopup();
|
|
6657
7442
|
if (popup) makeModalResizable(popup, { minWidth: 400, minHeight: 0 });
|
|
6658
7443
|
}, 0);
|
|
6659
7444
|
});
|
|
6660
7445
|
}
|
|
6661
7446
|
showFileEditorDialog(handle, content, fileName, testId, itBlock = "", interceptorsBlock = "", notes = "") {
|
|
6662
|
-
|
|
7447
|
+
Swal2.fire({
|
|
6663
7448
|
title: this.translation.translate("RECORDER.FILE_EDITOR_TITLE"),
|
|
6664
7449
|
html: '<div id="file-editor-modal-content" style="flex:1;min-height:0;display:flex;flex-direction:column;padding:0"></div>',
|
|
6665
7450
|
showCloseButton: false,
|
|
@@ -6670,47 +7455,35 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6670
7455
|
didOpen: () => {
|
|
6671
7456
|
makeSwalDraggable();
|
|
6672
7457
|
setSwal2DataCyAttribute();
|
|
6673
|
-
|
|
6674
|
-
if (popup) {
|
|
6675
|
-
popup.style.height = "640px";
|
|
6676
|
-
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
6677
|
-
if (htmlContainer) {
|
|
6678
|
-
htmlContainer.style.flex = "1";
|
|
6679
|
-
htmlContainer.style.minHeight = "0";
|
|
6680
|
-
htmlContainer.style.overflow = "hidden";
|
|
6681
|
-
htmlContainer.style.padding = "0";
|
|
6682
|
-
htmlContainer.style.margin = "0";
|
|
6683
|
-
htmlContainer.style.display = "flex";
|
|
6684
|
-
htmlContainer.style.flexDirection = "column";
|
|
6685
|
-
}
|
|
6686
|
-
}
|
|
7458
|
+
ensurePopupDimensions(Swal2.getPopup(), 640);
|
|
6687
7459
|
const container = document.getElementById("file-editor-modal-content");
|
|
6688
7460
|
if (!container) return;
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
7461
|
+
mountComponentInSwal("lib-e2e-file-preview", container, {
|
|
7462
|
+
translation: this.translation,
|
|
7463
|
+
fileContent: content,
|
|
7464
|
+
fileName,
|
|
7465
|
+
closeLabel: this.translation.translate("FILE_PREVIEW.BACK_TO_EDITOR"),
|
|
7466
|
+
itBlock,
|
|
7467
|
+
interceptorsBlock,
|
|
7468
|
+
notes
|
|
7469
|
+
}, [
|
|
7470
|
+
{ name: "close", handler: () => {
|
|
7471
|
+
Swal2.close();
|
|
7472
|
+
setTimeout(() => this.showAdvancedEditorDialog(testId), 150);
|
|
7473
|
+
} },
|
|
7474
|
+
{ name: "save", handler: async (e) => {
|
|
7475
|
+
try {
|
|
7476
|
+
const writable = await handle.createWritable();
|
|
7477
|
+
await writable.write(e.detail);
|
|
7478
|
+
await writable.close();
|
|
7479
|
+
showToast(this.translation.translate("RECORDER.FILE_SAVED_TOAST"));
|
|
7480
|
+
} catch {
|
|
7481
|
+
showToast(this.translation.translate("RECORDER.FILE_SAVE_ERROR_TOAST"), false);
|
|
7482
|
+
}
|
|
7483
|
+
Swal2.close();
|
|
7484
|
+
setTimeout(() => this.showAdvancedEditorDialog(testId), 150);
|
|
7485
|
+
} }
|
|
7486
|
+
]);
|
|
6714
7487
|
}
|
|
6715
7488
|
});
|
|
6716
7489
|
this.resizePopup();
|
|
@@ -6719,6 +7492,14 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6719
7492
|
async onSaveTest(description, tags = [], notes = "", ticketId = "") {
|
|
6720
7493
|
if (!description) return;
|
|
6721
7494
|
await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes, ticketId || void 0);
|
|
7495
|
+
const uploadedFiles = this.recording.getUploadedFilesSnapshot();
|
|
7496
|
+
if (uploadedFiles.length > 0) {
|
|
7497
|
+
const filenames = uploadedFiles.map((f) => f.filename).join(", ");
|
|
7498
|
+
showToast(
|
|
7499
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_MANUAL_TOAST").replace("{files}", filenames),
|
|
7500
|
+
false
|
|
7501
|
+
);
|
|
7502
|
+
}
|
|
6722
7503
|
this.recording.clearCommands();
|
|
6723
7504
|
this.clearRecordingHistory();
|
|
6724
7505
|
this.cypressCommands = [];
|
|
@@ -6740,6 +7521,29 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6740
7521
|
showToast(this.translation.translate("RECORDER.FIXTURES_NO_FOLDER_TOAST"), false);
|
|
6741
7522
|
}
|
|
6742
7523
|
}
|
|
7524
|
+
const uploadedFiles = this.recording.getUploadedFilesSnapshot();
|
|
7525
|
+
if (uploadedFiles.length > 0) {
|
|
7526
|
+
const filenames = uploadedFiles.map((f) => f.filename).join(", ");
|
|
7527
|
+
const hasAccess = await this.persistence.hasDirectoryAccess();
|
|
7528
|
+
if (hasAccess) {
|
|
7529
|
+
try {
|
|
7530
|
+
for (const uf of uploadedFiles) {
|
|
7531
|
+
await this.persistence.writeUploadedFile(uf.filename, uf.bytes);
|
|
7532
|
+
}
|
|
7533
|
+
showToast(this.translation.translate("RECORDER.FILE_UPLOAD_COPIED_TOAST").replace("{files}", filenames));
|
|
7534
|
+
} catch {
|
|
7535
|
+
showToast(
|
|
7536
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_COPY_ERROR_TOAST").replace("{files}", filenames),
|
|
7537
|
+
false
|
|
7538
|
+
);
|
|
7539
|
+
}
|
|
7540
|
+
} else {
|
|
7541
|
+
showToast(
|
|
7542
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_MANUAL_TOAST").replace("{files}", filenames),
|
|
7543
|
+
false
|
|
7544
|
+
);
|
|
7545
|
+
}
|
|
7546
|
+
}
|
|
6743
7547
|
const id = await this.persistence.insertTest(description, finalCommands, finalInterceptors, tags, notes, ticketId || void 0);
|
|
6744
7548
|
this.recording.clearCommands();
|
|
6745
7549
|
this.clearRecordingHistory();
|
|
@@ -6750,7 +7554,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6750
7554
|
// ── helpers ───────────────────────────────────────────────────────────────
|
|
6751
7555
|
toggleModal(flag, openFn) {
|
|
6752
7556
|
if (this[flag]) {
|
|
6753
|
-
|
|
7557
|
+
Swal2.close();
|
|
6754
7558
|
this[flag] = false;
|
|
6755
7559
|
return;
|
|
6756
7560
|
}
|
|
@@ -6759,10 +7563,32 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
6759
7563
|
}
|
|
6760
7564
|
resizePopup() {
|
|
6761
7565
|
setTimeout(() => {
|
|
6762
|
-
const popup =
|
|
7566
|
+
const popup = Swal2.getPopup();
|
|
6763
7567
|
if (popup) makeModalResizable(popup, { minWidth: 400, minHeight: 200 });
|
|
6764
7568
|
}, 0);
|
|
6765
7569
|
}
|
|
7570
|
+
openSwalDialog(flag, contentId, swalOptions, onMount, extra) {
|
|
7571
|
+
this.toggleModal(flag, () => {
|
|
7572
|
+
Swal2.fire({
|
|
7573
|
+
showConfirmButton: false,
|
|
7574
|
+
color: "#e6edf3",
|
|
7575
|
+
...swalOptions,
|
|
7576
|
+
didOpen: () => {
|
|
7577
|
+
makeSwalDraggable();
|
|
7578
|
+
setSwal2DataCyAttribute();
|
|
7579
|
+
extra?.onDidOpenPreamble?.();
|
|
7580
|
+
const container = document.getElementById(contentId);
|
|
7581
|
+
if (!container) return;
|
|
7582
|
+
onMount(container);
|
|
7583
|
+
},
|
|
7584
|
+
willClose: () => {
|
|
7585
|
+
this[flag] = false;
|
|
7586
|
+
extra?.onWillClose?.();
|
|
7587
|
+
}
|
|
7588
|
+
});
|
|
7589
|
+
if (!extra?.skipResize) this.resizePopup();
|
|
7590
|
+
});
|
|
7591
|
+
}
|
|
6766
7592
|
render() {
|
|
6767
7593
|
const rec = this.isRecording;
|
|
6768
7594
|
const paused = this.isPaused;
|
|
@@ -6791,16 +7617,13 @@ if (!customElements.get("lib-e2e-recorder")) {
|
|
|
6791
7617
|
}
|
|
6792
7618
|
|
|
6793
7619
|
// src/index.ts
|
|
6794
|
-
var VERSION = "0.
|
|
7620
|
+
var VERSION = "1.0.0";
|
|
6795
7621
|
export {
|
|
6796
|
-
ACTIVE_SESSION_BREADCRUMB_KEY,
|
|
6797
7622
|
AdvancedTestEditorElement,
|
|
6798
7623
|
AdvancedTestTransformationService,
|
|
6799
7624
|
ConfigurationElement,
|
|
6800
7625
|
DB_SCHEMA,
|
|
6801
7626
|
DB_STORE_NAMES,
|
|
6802
|
-
DEFAULT_RESUME_TTL_MINUTES,
|
|
6803
|
-
DRAG_THRESHOLD,
|
|
6804
7627
|
FilePreviewElement,
|
|
6805
7628
|
HelpPanelElement,
|
|
6806
7629
|
HttpMonitor,
|
|
@@ -6809,24 +7632,16 @@ export {
|
|
|
6809
7632
|
LOCALE_BY_LANG,
|
|
6810
7633
|
LibE2eRecorderElement,
|
|
6811
7634
|
PersistenceService,
|
|
6812
|
-
RESUME_TTL_CONFIG_KEY,
|
|
6813
7635
|
RecordingService,
|
|
6814
7636
|
SCROLLBAR_STYLES,
|
|
6815
7637
|
SUPPORTED_LANGS,
|
|
6816
7638
|
SaveTestElement,
|
|
6817
7639
|
SelectorPickerElement,
|
|
6818
|
-
Subject,
|
|
6819
|
-
TOGGLE_MARGIN,
|
|
6820
7640
|
TestEditorElement,
|
|
6821
7641
|
TestPrevisualizerElement,
|
|
6822
7642
|
TransformationService,
|
|
6823
7643
|
TranslationService,
|
|
6824
7644
|
VERSION,
|
|
6825
|
-
advancedTestTransformationService,
|
|
6826
|
-
boxOffsetForDirection,
|
|
6827
|
-
boxTopLeftFor,
|
|
6828
|
-
clampTogglePosition,
|
|
6829
|
-
defaultTogglePosition,
|
|
6830
7645
|
generateAlias,
|
|
6831
7646
|
inferAssertionCommand,
|
|
6832
7647
|
injectStyles,
|
|
@@ -6835,13 +7650,7 @@ export {
|
|
|
6835
7650
|
localeForLang,
|
|
6836
7651
|
makeModalResizable,
|
|
6837
7652
|
makeSwalDraggable,
|
|
6838
|
-
makeSwalDraggableByContentId,
|
|
6839
|
-
persistenceService,
|
|
6840
|
-
resolveExpandDirection,
|
|
6841
7653
|
selectTestsForExport,
|
|
6842
|
-
|
|
6843
|
-
showToast,
|
|
6844
|
-
transformationService,
|
|
6845
|
-
translationService
|
|
7654
|
+
showToast
|
|
6846
7655
|
};
|
|
6847
7656
|
//# sourceMappingURL=index.js.map
|