lib-e2e-cypress-for-dummys-ts 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/{chunk-CQEDHL7A.js → chunk-VT2GLZ62.js} +40 -7
- package/dist/chunk-VT2GLZ62.js.map +1 -0
- package/dist/index.cjs +1404 -545
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -19
- package/dist/index.d.ts +49 -19
- package/dist/index.js +1352 -540
- package/dist/index.js.map +1 -1
- package/dist/runner.js +18 -2
- package/dist/selector-picker-ZEZ2G5K6.js +7 -0
- package/package.json +1 -1
- 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-ZEZ2G5K6.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,41 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// src/utils/code-format.utils.ts
|
|
34
|
+
function gcd(a, b) {
|
|
35
|
+
while (b) {
|
|
36
|
+
const t = b;
|
|
37
|
+
b = a % b;
|
|
38
|
+
a = t;
|
|
39
|
+
}
|
|
40
|
+
return a;
|
|
41
|
+
}
|
|
42
|
+
function escapeSingleQuotes(value) {
|
|
43
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
44
|
+
}
|
|
45
|
+
function escapeCssAttrValue(value) {
|
|
46
|
+
return value.replace(/"/g, '\\"');
|
|
47
|
+
}
|
|
48
|
+
function normalizeBlock(code, baseIndent) {
|
|
49
|
+
const lines = code.split("\n").map((l) => l.trimEnd());
|
|
50
|
+
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
51
|
+
if (!nonEmpty.length) return "";
|
|
52
|
+
const indents = nonEmpty.map((l) => l.length - l.trimStart().length);
|
|
53
|
+
const minIndent = Math.min(...indents);
|
|
54
|
+
const relIndents = [...new Set(indents.map((n) => n - minIndent).filter((n) => n > 0))];
|
|
55
|
+
const step = relIndents.length > 0 ? relIndents.reduce(gcd) : 2;
|
|
56
|
+
return lines.map((l) => {
|
|
57
|
+
if (!l.trim()) return "";
|
|
58
|
+
const level = Math.round((l.length - l.trimStart().length - minIndent) / step);
|
|
59
|
+
return baseIndent + " ".repeat(level) + l.trimStart();
|
|
60
|
+
}).join("\n");
|
|
61
|
+
}
|
|
62
|
+
var init_code_format_utils = __esm({
|
|
63
|
+
"src/utils/code-format.utils.ts"() {
|
|
64
|
+
"use strict";
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
33
68
|
// src/utils/selector-quality.utils.ts
|
|
34
69
|
function getSelectorQuality(el) {
|
|
35
70
|
if (el.hasAttribute("data-cy") || el.hasAttribute("data-testid") || el.hasAttribute("aria-label")) {
|
|
@@ -46,11 +81,11 @@ function getSelectorQuality(el) {
|
|
|
46
81
|
}
|
|
47
82
|
function buildPickerSelector(el) {
|
|
48
83
|
const dataCy = el.getAttribute("data-cy");
|
|
49
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
84
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
50
85
|
const dataTestid = el.getAttribute("data-testid");
|
|
51
|
-
if (dataTestid) return `[data-testid="${dataTestid}"]`;
|
|
86
|
+
if (dataTestid) return `[data-testid="${escapeCssAttrValue(dataTestid)}"]`;
|
|
52
87
|
const ariaLabel = el.getAttribute("aria-label");
|
|
53
|
-
if (ariaLabel) return `[aria-label="${ariaLabel}"]`;
|
|
88
|
+
if (ariaLabel) return `[aria-label="${escapeCssAttrValue(ariaLabel)}"]`;
|
|
54
89
|
const rawId = el.id;
|
|
55
90
|
if (rawId && rawId.length < 25 && /^[a-zA-Z][\w-]*$/.test(rawId) && !FORBIDDEN_ID_PREFIXES.some((p) => rawId.startsWith(p)) && !/^\d+$/.test(rawId)) {
|
|
56
91
|
return `#${rawId}`;
|
|
@@ -84,6 +119,7 @@ var FORBIDDEN_ID_PREFIXES;
|
|
|
84
119
|
var init_selector_quality_utils = __esm({
|
|
85
120
|
"src/utils/selector-quality.utils.ts"() {
|
|
86
121
|
"use strict";
|
|
122
|
+
init_code_format_utils();
|
|
87
123
|
FORBIDDEN_ID_PREFIXES = [
|
|
88
124
|
"cdk-",
|
|
89
125
|
"mat-",
|
|
@@ -314,6 +350,7 @@ var init_selector_picker = __esm({
|
|
|
314
350
|
"src/components/selector-picker/selector-picker.ts"() {
|
|
315
351
|
"use strict";
|
|
316
352
|
init_selector_quality_utils();
|
|
353
|
+
init_code_format_utils();
|
|
317
354
|
init_selector_picker_styles();
|
|
318
355
|
init_selector_picker_template();
|
|
319
356
|
OWN_DATA_CY = "lib-e2e-cypress-for-dummys";
|
|
@@ -433,7 +470,7 @@ var init_selector_picker = __esm({
|
|
|
433
470
|
confirm(index) {
|
|
434
471
|
const row = this.rows[index];
|
|
435
472
|
if (!row) return;
|
|
436
|
-
const command = `cy.get('${row.selector}').click()`;
|
|
473
|
+
const command = `cy.get('${escapeSingleQuotes(row.selector)}').click()`;
|
|
437
474
|
this.recording.appendCommand(command);
|
|
438
475
|
this.dispatchEvent(new CustomEvent("selectorchosen", { detail: command, bubbles: true, composed: true }));
|
|
439
476
|
this.closeSilently();
|
|
@@ -451,8 +488,8 @@ var init_selector_picker = __esm({
|
|
|
451
488
|
this.remove();
|
|
452
489
|
}
|
|
453
490
|
};
|
|
454
|
-
if (!customElements.get("selector-picker")) {
|
|
455
|
-
customElements.define("selector-picker", SelectorPickerElement);
|
|
491
|
+
if (!customElements.get("lib-e2e-selector-picker")) {
|
|
492
|
+
customElements.define("lib-e2e-selector-picker", SelectorPickerElement);
|
|
456
493
|
}
|
|
457
494
|
}
|
|
458
495
|
});
|
|
@@ -482,18 +519,19 @@ __export(index_exports, {
|
|
|
482
519
|
SUPPORTED_LANGS: () => SUPPORTED_LANGS,
|
|
483
520
|
SaveTestElement: () => SaveTestElement,
|
|
484
521
|
SelectorPickerElement: () => SelectorPickerElement,
|
|
485
|
-
Subject: () => Subject,
|
|
486
522
|
TOGGLE_MARGIN: () => TOGGLE_MARGIN,
|
|
487
523
|
TestEditorElement: () => TestEditorElement,
|
|
488
524
|
TestPrevisualizerElement: () => TestPrevisualizerElement,
|
|
489
525
|
TransformationService: () => TransformationService,
|
|
490
526
|
TranslationService: () => TranslationService,
|
|
491
527
|
VERSION: () => VERSION,
|
|
528
|
+
_resetHttpMonitorState: () => _resetHttpMonitorState,
|
|
492
529
|
advancedTestTransformationService: () => advancedTestTransformationService,
|
|
493
530
|
boxOffsetForDirection: () => boxOffsetForDirection,
|
|
494
531
|
boxTopLeftFor: () => boxTopLeftFor,
|
|
495
532
|
clampTogglePosition: () => clampTogglePosition,
|
|
496
533
|
defaultTogglePosition: () => defaultTogglePosition,
|
|
534
|
+
ensurePopupDimensions: () => ensurePopupDimensions,
|
|
497
535
|
generateAlias: () => generateAlias,
|
|
498
536
|
inferAssertionCommand: () => inferAssertionCommand,
|
|
499
537
|
injectStyles: () => injectStyles,
|
|
@@ -503,7 +541,6 @@ __export(index_exports, {
|
|
|
503
541
|
makeModalResizable: () => makeModalResizable,
|
|
504
542
|
makeSwalDraggable: () => makeSwalDraggable,
|
|
505
543
|
makeSwalDraggableByContentId: () => makeSwalDraggableByContentId,
|
|
506
|
-
persistenceService: () => persistenceService,
|
|
507
544
|
resolveExpandDirection: () => resolveExpandDirection,
|
|
508
545
|
selectTestsForExport: () => selectTestsForExport,
|
|
509
546
|
setSwal2DataCyAttribute: () => setSwal2DataCyAttribute,
|
|
@@ -650,7 +687,10 @@ var I18N_ES = {
|
|
|
650
687
|
NOTES_PLACEHOLDER: "Qu\xE9 valida este test, condiciones previas, etc.",
|
|
651
688
|
TICKET_LABEL: "Ticket / Issue (opcional):",
|
|
652
689
|
TICKET_PLACEHOLDER: "Ej: PROJ-123, #42",
|
|
653
|
-
TICKET_WARN: "ID no v\xE1lido para el proveedor configurado (se guardar\xE1 igualmente)"
|
|
690
|
+
TICKET_WARN: "ID no v\xE1lido para el proveedor configurado (se guardar\xE1 igualmente)",
|
|
691
|
+
CONFIRM_DISCARD: "\xBFSeguro que quieres descartar el test grabado? Se perder\xE1n todos los pasos registrados.",
|
|
692
|
+
CONFIRM_DISCARD_BTN: "S\xED, descartar",
|
|
693
|
+
BACK_BTN: "Volver"
|
|
654
694
|
},
|
|
655
695
|
TEST_EDITOR: {
|
|
656
696
|
NO_TAGS: "Sin etiquetas",
|
|
@@ -732,7 +772,30 @@ var I18N_ES = {
|
|
|
732
772
|
ISSUE_TRACKER_SUB: "A\xF1ade un ID de ticket al guardar y genera un enlace clickable en el listado.",
|
|
733
773
|
ISSUE_TRACKER_PROVIDER: "Proveedor",
|
|
734
774
|
ISSUE_TRACKER_BASE_URL: "URL base",
|
|
735
|
-
ISSUE_TRACKER_BASE_URL_PH: "Ej: https://miproyecto.atlassian.net"
|
|
775
|
+
ISSUE_TRACKER_BASE_URL_PH: "Ej: https://miproyecto.atlassian.net",
|
|
776
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
777
|
+
LOGIN_SETUP_NOT_CONFIGURED: "No configurado",
|
|
778
|
+
LOGIN_SETUP_SET_UP_BTN: "Configurar",
|
|
779
|
+
LOGIN_SETUP_TITLE: "Configuraci\xF3n de login",
|
|
780
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Crear scaffold",
|
|
781
|
+
LOGIN_SETUP_SELECT_EXISTING: "Seleccionar fichero existente",
|
|
782
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nombre del fichero",
|
|
783
|
+
LOGIN_SETUP_FILE_NAME_PH: "Ej: cypress/common-services/login.service.ts",
|
|
784
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-escanear",
|
|
785
|
+
LOGIN_SETUP_NO_FUNCTIONS: "No se encontraron funciones exportadas. Rellena el scaffold y re-escanea.",
|
|
786
|
+
LOGIN_SETUP_BEFORE_LABEL: "Funci\xF3n before() (una vez por suite)",
|
|
787
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Funci\xF3n beforeEach() (antes de cada test)",
|
|
788
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Ninguna \u2014",
|
|
789
|
+
LOGIN_SETUP_SUMMARY: "Fichero: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
790
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Guardar",
|
|
791
|
+
LOGIN_SETUP_CLEAR_BTN: "Eliminar configuraci\xF3n",
|
|
792
|
+
LOGIN_SETUP_CANCEL_BTN: "Cancelar",
|
|
793
|
+
LOGIN_SETUP_SOURCE_LABEL: "Origen del fichero de login",
|
|
794
|
+
LOGIN_SETUP_CREATE_SUB: "Genera un scaffold con funciones de ejemplo",
|
|
795
|
+
LOGIN_SETUP_SELECT_SUB: "Apunta a un servicio de login existente",
|
|
796
|
+
LOGIN_SETUP_INJECT_TITLE: "\xBFA\xF1adir bloques de login?",
|
|
797
|
+
LOGIN_SETUP_INJECT_TEXT: "Este fichero no tiene los bloques de login configurados. \xBFQuieres a\xF1adir el import y los bloques before/beforeEach ahora?",
|
|
798
|
+
LOGIN_SETUP_INJECT_CONFIRM: "S\xED, a\xF1adir"
|
|
736
799
|
},
|
|
737
800
|
SELECTOR_PICKER: {
|
|
738
801
|
TITLE: "Selecciona un elemento del DOM",
|
|
@@ -823,6 +886,9 @@ var I18N_ES = {
|
|
|
823
886
|
FILE_SAVE_ERROR_TOAST: "Error al guardar el archivo",
|
|
824
887
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures escritos en cypress/fixtures",
|
|
825
888
|
FIXTURES_NO_FOLDER_TOAST: "No se pudieron escribir los fixtures (configura la carpeta Cypress)",
|
|
889
|
+
FILE_UPLOAD_MANUAL_TOAST: "A\xF1ade {files} a cypress/fixtures/ antes de ejecutar el test.",
|
|
890
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copiado/s a cypress/fixtures/.",
|
|
891
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "No se pudo copiar {files} a cypress/fixtures/. A\xF1\xE1delo manualmente.",
|
|
826
892
|
BTN_CONFIG: "Config",
|
|
827
893
|
BTN_FILES: "Ficheros",
|
|
828
894
|
BTN_COMMANDS: "Comandos",
|
|
@@ -870,7 +936,7 @@ var I18N_ES = {
|
|
|
870
936
|
SEC_PANELS: "\u{1F5C2} Paneles",
|
|
871
937
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 guardados: filtrar por tag, copiar, combinar, exportar.",
|
|
872
938
|
PANEL_COMMANDS: "\u2328\uFE0F Comandos \u2014 de la grabaci\xF3n actual: reordenar, borrar, a\xF1adir aserciones.",
|
|
873
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 idioma, selector, HTTP, carpeta Cypress, datos, visibilidad, posici\xF3n.",
|
|
939
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 idioma, selector, HTTP, carpeta Cypress, login setup, datos, visibilidad, posici\xF3n.",
|
|
874
940
|
PANEL_FILES: "\u{1F4C1} Ficheros \u2014 inserta tests en tus .cy.ts y lanza el runner.",
|
|
875
941
|
SEC_SELECTOR: "\u{1F50E} Selectores",
|
|
876
942
|
SELECTOR_1: "Elige el atributo preferido (data-cy, data-testid, aria-label, id) en Config.",
|
|
@@ -883,6 +949,7 @@ var I18N_ES = {
|
|
|
883
949
|
ADV_RUNNER: "Runner local: npx lib-e2e-cypress-runner para lanzar un spec headless desde el editor.",
|
|
884
950
|
ADV_CROSSAPP: "Cross-app: la grabaci\xF3n sobrevive al cambiar de micro-frontend (mismo origen).",
|
|
885
951
|
ADV_DRAG: "Arrastra el bot\xF3n \u23FA para mover el widget; recuerda su posici\xF3n.",
|
|
952
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
886
953
|
CLOSE: "Cerrar",
|
|
887
954
|
TAB_REFERENCE: "Referencia r\xE1pida",
|
|
888
955
|
TAB_GUIDE: "Gu\xEDa de uso",
|
|
@@ -900,13 +967,13 @@ var I18N_ES = {
|
|
|
900
967
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
901
968
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
902
969
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
970
|
+
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.",
|
|
903
971
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
904
972
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
905
973
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
906
974
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
907
975
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
908
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
909
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
976
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
910
977
|
}
|
|
911
978
|
};
|
|
912
979
|
|
|
@@ -937,7 +1004,10 @@ var I18N_EN = {
|
|
|
937
1004
|
NOTES_PLACEHOLDER: "What this test validates, preconditions, etc.",
|
|
938
1005
|
TICKET_LABEL: "Ticket / Issue (optional):",
|
|
939
1006
|
TICKET_PLACEHOLDER: "E.g.: PROJ-123, #42",
|
|
940
|
-
TICKET_WARN: "Invalid ID for the configured provider (will be saved anyway)"
|
|
1007
|
+
TICKET_WARN: "Invalid ID for the configured provider (will be saved anyway)",
|
|
1008
|
+
CONFIRM_DISCARD: "Are you sure you want to discard this recording? All recorded steps will be lost.",
|
|
1009
|
+
CONFIRM_DISCARD_BTN: "Yes, discard",
|
|
1010
|
+
BACK_BTN: "Go back"
|
|
941
1011
|
},
|
|
942
1012
|
TEST_EDITOR: {
|
|
943
1013
|
NO_TAGS: "No tags",
|
|
@@ -1019,7 +1089,30 @@ var I18N_EN = {
|
|
|
1019
1089
|
ISSUE_TRACKER_SUB: "Add a ticket ID when saving and generate a clickable link in the test list.",
|
|
1020
1090
|
ISSUE_TRACKER_PROVIDER: "Provider",
|
|
1021
1091
|
ISSUE_TRACKER_BASE_URL: "Base URL",
|
|
1022
|
-
ISSUE_TRACKER_BASE_URL_PH: "E.g.: https://myproject.atlassian.net"
|
|
1092
|
+
ISSUE_TRACKER_BASE_URL_PH: "E.g.: https://myproject.atlassian.net",
|
|
1093
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
1094
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Not configured",
|
|
1095
|
+
LOGIN_SETUP_SET_UP_BTN: "Set up",
|
|
1096
|
+
LOGIN_SETUP_TITLE: "Login setup",
|
|
1097
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Create scaffold",
|
|
1098
|
+
LOGIN_SETUP_SELECT_EXISTING: "Select existing file",
|
|
1099
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "File name",
|
|
1100
|
+
LOGIN_SETUP_FILE_NAME_PH: "E.g.: cypress/common-services/login.service.ts",
|
|
1101
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scan",
|
|
1102
|
+
LOGIN_SETUP_NO_FUNCTIONS: "No exported functions found. Fill in the scaffold and re-scan.",
|
|
1103
|
+
LOGIN_SETUP_BEFORE_LABEL: "before() function (once per suite)",
|
|
1104
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "beforeEach() function (before every test)",
|
|
1105
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 None \u2014",
|
|
1106
|
+
LOGIN_SETUP_SUMMARY: "File: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
1107
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Save",
|
|
1108
|
+
LOGIN_SETUP_CLEAR_BTN: "Remove configuration",
|
|
1109
|
+
LOGIN_SETUP_CANCEL_BTN: "Cancel",
|
|
1110
|
+
LOGIN_SETUP_SOURCE_LABEL: "Login file source",
|
|
1111
|
+
LOGIN_SETUP_CREATE_SUB: "Generate a scaffold with sample functions",
|
|
1112
|
+
LOGIN_SETUP_SELECT_SUB: "Point to an existing login service file",
|
|
1113
|
+
LOGIN_SETUP_INJECT_TITLE: "Add login blocks?",
|
|
1114
|
+
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?",
|
|
1115
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Yes, add them"
|
|
1023
1116
|
},
|
|
1024
1117
|
SELECTOR_PICKER: {
|
|
1025
1118
|
TITLE: "Select a DOM element",
|
|
@@ -1110,6 +1203,9 @@ var I18N_EN = {
|
|
|
1110
1203
|
FILE_SAVE_ERROR_TOAST: "Error saving the file",
|
|
1111
1204
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures written to cypress/fixtures",
|
|
1112
1205
|
FIXTURES_NO_FOLDER_TOAST: "Could not write fixtures (configure the Cypress folder)",
|
|
1206
|
+
FILE_UPLOAD_MANUAL_TOAST: "Add {files} to cypress/fixtures/ before running the test.",
|
|
1207
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copied to cypress/fixtures/.",
|
|
1208
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Could not copy {files} to cypress/fixtures/. Add it manually.",
|
|
1113
1209
|
BTN_CONFIG: "Config",
|
|
1114
1210
|
BTN_FILES: "Files",
|
|
1115
1211
|
BTN_COMMANDS: "Commands",
|
|
@@ -1157,7 +1253,7 @@ var I18N_EN = {
|
|
|
1157
1253
|
SEC_PANELS: "\u{1F5C2} Panels",
|
|
1158
1254
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 saved tests: filter by tag, copy, combine, export.",
|
|
1159
1255
|
PANEL_COMMANDS: "\u2328\uFE0F Commands \u2014 current recording: reorder, delete, add assertions.",
|
|
1160
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 language, selector, HTTP, Cypress folder, data, visibility, position.",
|
|
1256
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 language, selector, HTTP, Cypress folder, login setup, data, visibility, position.",
|
|
1161
1257
|
PANEL_FILES: "\u{1F4C1} Files \u2014 insert tests into your .cy.ts and launch the runner.",
|
|
1162
1258
|
SEC_SELECTOR: "\u{1F50E} Selectors",
|
|
1163
1259
|
SELECTOR_1: "Choose the preferred attribute (data-cy, data-testid, aria-label, id) in Config.",
|
|
@@ -1170,6 +1266,7 @@ var I18N_EN = {
|
|
|
1170
1266
|
ADV_RUNNER: "Local runner: npx lib-e2e-cypress-runner to run a spec headless from the editor.",
|
|
1171
1267
|
ADV_CROSSAPP: "Cross-app: the recording survives switching micro-frontend (same origin).",
|
|
1172
1268
|
ADV_DRAG: "Drag the \u23FA button to move the widget; its position is remembered.",
|
|
1269
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): point to your login service; the recorder automatically injects before()/beforeEach() blocks into each new test.",
|
|
1173
1270
|
CLOSE: "Close",
|
|
1174
1271
|
TAB_REFERENCE: "Quick reference",
|
|
1175
1272
|
TAB_GUIDE: "Usage guide",
|
|
@@ -1187,13 +1284,13 @@ var I18N_EN = {
|
|
|
1187
1284
|
G_COV_4: "Reliable selector (data-cy/testid/aria/id) and a picker when none is obvious.",
|
|
1188
1285
|
G_COV_5: "Automatic assertions (Alt+click) and the manual builder.",
|
|
1189
1286
|
G_COV_6: "Continuity when switching micro-frontend (same origin), invisible mode and i18n.",
|
|
1287
|
+
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.",
|
|
1190
1288
|
G_SEC_NOTCOVERED: "\u{1F6AB} What it does NOT cover (yet)",
|
|
1191
1289
|
G_NC_1: "Different origins / subdomains (same origin only).",
|
|
1192
1290
|
G_NC_2: "File upload (selectFile), drag & drop, and hover.",
|
|
1193
1291
|
G_NC_3: "The Tab key (Cypress can't .type() it).",
|
|
1194
1292
|
G_NC_4: "DELETE requests (intentionally ignored).",
|
|
1195
|
-
G_NC_5: "Third-party iframes and Shadow DOM may not be captured."
|
|
1196
|
-
G_NC_6: "Programmatic login generation (on the roadmap)."
|
|
1293
|
+
G_NC_5: "Third-party iframes and Shadow DOM may not be captured."
|
|
1197
1294
|
}
|
|
1198
1295
|
};
|
|
1199
1296
|
|
|
@@ -1224,8 +1321,11 @@ var I18N_FR = {
|
|
|
1224
1321
|
NOTES_PLACEHOLDER: "Ce que ce test valide, conditions pr\xE9alables, etc.",
|
|
1225
1322
|
TICKET_LABEL: "Ticket / Issue (optionnel) :",
|
|
1226
1323
|
TICKET_PLACEHOLDER: "Ex : PROJ-123, #42",
|
|
1227
|
-
TICKET_WARN: "ID non valide pour le fournisseur configur\xE9 (sera enregistr\xE9 quand m\xEAme)"
|
|
1324
|
+
TICKET_WARN: "ID non valide pour le fournisseur configur\xE9 (sera enregistr\xE9 quand m\xEAme)",
|
|
1228
1325
|
// TODO: translate
|
|
1326
|
+
CONFIRM_DISCARD: "\xCAtes-vous s\xFBr de vouloir abandonner cet enregistrement ? Toutes les \xE9tapes enregistr\xE9es seront perdues.",
|
|
1327
|
+
CONFIRM_DISCARD_BTN: "Oui, abandonner",
|
|
1328
|
+
BACK_BTN: "Retour"
|
|
1229
1329
|
},
|
|
1230
1330
|
TEST_EDITOR: {
|
|
1231
1331
|
NO_TAGS: "Sans \xE9tiquettes",
|
|
@@ -1307,7 +1407,30 @@ var I18N_FR = {
|
|
|
1307
1407
|
ISSUE_TRACKER_SUB: "Ajoutez un ID de ticket lors de l'enregistrement et g\xE9n\xE9rez un lien cliquable.",
|
|
1308
1408
|
ISSUE_TRACKER_PROVIDER: "Fournisseur",
|
|
1309
1409
|
ISSUE_TRACKER_BASE_URL: "URL de base",
|
|
1310
|
-
ISSUE_TRACKER_BASE_URL_PH: "Ex : https://monprojet.atlassian.net"
|
|
1410
|
+
ISSUE_TRACKER_BASE_URL_PH: "Ex : https://monprojet.atlassian.net",
|
|
1411
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
1412
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Non configur\xE9",
|
|
1413
|
+
LOGIN_SETUP_SET_UP_BTN: "Configurer",
|
|
1414
|
+
LOGIN_SETUP_TITLE: "Configuration du login",
|
|
1415
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Cr\xE9er un scaffold",
|
|
1416
|
+
LOGIN_SETUP_SELECT_EXISTING: "S\xE9lectionner un fichier existant",
|
|
1417
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nom du fichier",
|
|
1418
|
+
LOGIN_SETUP_FILE_NAME_PH: "Ex : cypress/common-services/login.service.ts",
|
|
1419
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scanner",
|
|
1420
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Aucune fonction export\xE9e trouv\xE9e. Remplissez le scaffold et re-scannez.",
|
|
1421
|
+
LOGIN_SETUP_BEFORE_LABEL: "Fonction before() (une fois par suite)",
|
|
1422
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Fonction beforeEach() (avant chaque test)",
|
|
1423
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Aucune \u2014",
|
|
1424
|
+
LOGIN_SETUP_SUMMARY: "Fichier : {file} \xB7 before : {before} \xB7 beforeEach : {beforeEach}",
|
|
1425
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Enregistrer",
|
|
1426
|
+
LOGIN_SETUP_CLEAR_BTN: "Supprimer la configuration",
|
|
1427
|
+
LOGIN_SETUP_CANCEL_BTN: "Annuler",
|
|
1428
|
+
LOGIN_SETUP_SOURCE_LABEL: "Source du fichier de login",
|
|
1429
|
+
LOGIN_SETUP_CREATE_SUB: "G\xE9n\xE9rer un scaffold avec des fonctions exemples",
|
|
1430
|
+
LOGIN_SETUP_SELECT_SUB: "Pointer vers un fichier de service de login existant",
|
|
1431
|
+
LOGIN_SETUP_INJECT_TITLE: "Ajouter les blocs de login ?",
|
|
1432
|
+
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 ?",
|
|
1433
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Oui, ajouter"
|
|
1311
1434
|
},
|
|
1312
1435
|
SELECTOR_PICKER: {
|
|
1313
1436
|
TITLE: "S\xE9lectionnez un \xE9l\xE9ment du DOM",
|
|
@@ -1398,6 +1521,12 @@ var I18N_FR = {
|
|
|
1398
1521
|
FILE_SAVE_ERROR_TOAST: "Erreur lors de l'enregistrement",
|
|
1399
1522
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures \xE9crites dans cypress/fixtures",
|
|
1400
1523
|
FIXTURES_NO_FOLDER_TOAST: "Impossible d'\xE9crire les fixtures (configure le dossier Cypress)",
|
|
1524
|
+
FILE_UPLOAD_MANUAL_TOAST: "Ajoutez {files} \xE0 cypress/fixtures/ avant d'ex\xE9cuter le test.",
|
|
1525
|
+
// TODO: translate
|
|
1526
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copi\xE9(s) dans cypress/fixtures/.",
|
|
1527
|
+
// TODO: translate
|
|
1528
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Impossible de copier {files} vers cypress/fixtures/. Ajoutez-le manuellement.",
|
|
1529
|
+
// TODO: translate
|
|
1401
1530
|
BTN_CONFIG: "Config",
|
|
1402
1531
|
BTN_FILES: "Fichiers",
|
|
1403
1532
|
BTN_COMMANDS: "Commandes",
|
|
@@ -1445,7 +1574,7 @@ var I18N_FR = {
|
|
|
1445
1574
|
SEC_PANELS: "\u{1F5C2} Panneaux",
|
|
1446
1575
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 enregistr\xE9s : filtrer par tag, copier, combiner, exporter.",
|
|
1447
1576
|
PANEL_COMMANDS: "\u2328\uFE0F Commandes \u2014 enregistrement en cours : r\xE9ordonner, supprimer, ajouter des assertions.",
|
|
1448
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 langue, s\xE9lecteur, HTTP, dossier Cypress, donn\xE9es, visibilit\xE9, position.",
|
|
1577
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 langue, s\xE9lecteur, HTTP, dossier Cypress, login setup, donn\xE9es, visibilit\xE9, position.",
|
|
1449
1578
|
PANEL_FILES: "\u{1F4C1} Fichiers \u2014 ins\xE8re des tests dans tes .cy.ts et lance le runner.",
|
|
1450
1579
|
SEC_SELECTOR: "\u{1F50E} S\xE9lecteurs",
|
|
1451
1580
|
SELECTOR_1: "Choisis l'attribut pr\xE9f\xE9r\xE9 (data-cy, data-testid, aria-label, id) dans Config.",
|
|
@@ -1458,6 +1587,8 @@ var I18N_FR = {
|
|
|
1458
1587
|
ADV_RUNNER: "Runner local : npx lib-e2e-cypress-runner pour lancer un spec headless depuis l'\xE9diteur.",
|
|
1459
1588
|
ADV_CROSSAPP: "Cross-app : l'enregistrement survit au changement de micro-frontend (m\xEAme origine).",
|
|
1460
1589
|
ADV_DRAG: "Fais glisser le bouton \u23FA pour d\xE9placer le widget ; sa position est m\xE9moris\xE9e.",
|
|
1590
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
1591
|
+
// TODO: translate (fr)
|
|
1461
1592
|
CLOSE: "Fermer",
|
|
1462
1593
|
TAB_REFERENCE: "R\xE9f\xE9rence rapide",
|
|
1463
1594
|
TAB_GUIDE: "Guide d'utilisation",
|
|
@@ -1476,13 +1607,14 @@ var I18N_FR = {
|
|
|
1476
1607
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
1477
1608
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
1478
1609
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
1610
|
+
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.",
|
|
1611
|
+
// TODO: translate (fr)
|
|
1479
1612
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
1480
1613
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
1481
1614
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
1482
1615
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
1483
1616
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
1484
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1485
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
1617
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1486
1618
|
}
|
|
1487
1619
|
};
|
|
1488
1620
|
|
|
@@ -1513,8 +1645,11 @@ var I18N_IT = {
|
|
|
1513
1645
|
NOTES_PLACEHOLDER: "Cosa valida questo test, condizioni preliminari, ecc.",
|
|
1514
1646
|
TICKET_LABEL: "Ticket / Issue (opzionale):",
|
|
1515
1647
|
TICKET_PLACEHOLDER: "Es: PROJ-123, #42",
|
|
1516
|
-
TICKET_WARN: "ID non valido per il provider configurato (verr\xE0 salvato comunque)"
|
|
1648
|
+
TICKET_WARN: "ID non valido per il provider configurato (verr\xE0 salvato comunque)",
|
|
1517
1649
|
// TODO: translate
|
|
1650
|
+
CONFIRM_DISCARD: "Sei sicuro di voler scartare questa registrazione? Tutti i passi registrati andranno persi.",
|
|
1651
|
+
CONFIRM_DISCARD_BTN: "S\xEC, scarta",
|
|
1652
|
+
BACK_BTN: "Indietro"
|
|
1518
1653
|
},
|
|
1519
1654
|
TEST_EDITOR: {
|
|
1520
1655
|
NO_TAGS: "Nessuna etichetta",
|
|
@@ -1596,7 +1731,30 @@ var I18N_IT = {
|
|
|
1596
1731
|
ISSUE_TRACKER_SUB: "Aggiungi un ID ticket al salvataggio e genera un link cliccabile nell'elenco.",
|
|
1597
1732
|
ISSUE_TRACKER_PROVIDER: "Provider",
|
|
1598
1733
|
ISSUE_TRACKER_BASE_URL: "URL base",
|
|
1599
|
-
ISSUE_TRACKER_BASE_URL_PH: "Es: https://mioprogetto.atlassian.net"
|
|
1734
|
+
ISSUE_TRACKER_BASE_URL_PH: "Es: https://mioprogetto.atlassian.net",
|
|
1735
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
1736
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Non configurato",
|
|
1737
|
+
LOGIN_SETUP_SET_UP_BTN: "Configura",
|
|
1738
|
+
LOGIN_SETUP_TITLE: "Configurazione del login",
|
|
1739
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Crea scaffold",
|
|
1740
|
+
LOGIN_SETUP_SELECT_EXISTING: "Seleziona file esistente",
|
|
1741
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Nome del file",
|
|
1742
|
+
LOGIN_SETUP_FILE_NAME_PH: "Es: cypress/common-services/login.service.ts",
|
|
1743
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Re-scansiona",
|
|
1744
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Nessuna funzione esportata trovata. Compila lo scaffold e ri-scansiona.",
|
|
1745
|
+
LOGIN_SETUP_BEFORE_LABEL: "Funzione before() (una volta per suite)",
|
|
1746
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "Funzione beforeEach() (prima di ogni test)",
|
|
1747
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Nessuna \u2014",
|
|
1748
|
+
LOGIN_SETUP_SUMMARY: "File: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
1749
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Salva",
|
|
1750
|
+
LOGIN_SETUP_CLEAR_BTN: "Rimuovi configurazione",
|
|
1751
|
+
LOGIN_SETUP_CANCEL_BTN: "Annulla",
|
|
1752
|
+
LOGIN_SETUP_SOURCE_LABEL: "Sorgente del file di login",
|
|
1753
|
+
LOGIN_SETUP_CREATE_SUB: "Genera uno scaffold con funzioni di esempio",
|
|
1754
|
+
LOGIN_SETUP_SELECT_SUB: "Punta a un file di servizio di login esistente",
|
|
1755
|
+
LOGIN_SETUP_INJECT_TITLE: "Aggiungere i blocchi di login?",
|
|
1756
|
+
LOGIN_SETUP_INJECT_TEXT: "Questo file non contiene i blocchi di login configurati. Vuoi aggiungere l'import e i blocchi before/beforeEach ora?",
|
|
1757
|
+
LOGIN_SETUP_INJECT_CONFIRM: "S\xEC, aggiungi"
|
|
1600
1758
|
},
|
|
1601
1759
|
SELECTOR_PICKER: {
|
|
1602
1760
|
TITLE: "Seleziona un elemento DOM",
|
|
@@ -1687,6 +1845,12 @@ var I18N_IT = {
|
|
|
1687
1845
|
FILE_SAVE_ERROR_TOAST: "Errore durante il salvataggio",
|
|
1688
1846
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixture scritte in cypress/fixtures",
|
|
1689
1847
|
FIXTURES_NO_FOLDER_TOAST: "Impossibile scrivere le fixture (configura la cartella Cypress)",
|
|
1848
|
+
FILE_UPLOAD_MANUAL_TOAST: "Aggiungi {files} a cypress/fixtures/ prima di eseguire il test.",
|
|
1849
|
+
// TODO: translate
|
|
1850
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} copiato/i in cypress/fixtures/.",
|
|
1851
|
+
// TODO: translate
|
|
1852
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "Impossibile copiare {files} in cypress/fixtures/. Aggiungilo manualmente.",
|
|
1853
|
+
// TODO: translate
|
|
1690
1854
|
BTN_CONFIG: "Config",
|
|
1691
1855
|
BTN_FILES: "File",
|
|
1692
1856
|
BTN_COMMANDS: "Comandi",
|
|
@@ -1734,7 +1898,7 @@ var I18N_IT = {
|
|
|
1734
1898
|
SEC_PANELS: "\u{1F5C2} Pannelli",
|
|
1735
1899
|
PANEL_TESTS: "\u{1F4CB} Test \u2014 salvati: filtra per tag, copia, combina, esporta.",
|
|
1736
1900
|
PANEL_COMMANDS: "\u2328\uFE0F Comandi \u2014 registrazione corrente: riordina, elimina, aggiungi asserzioni.",
|
|
1737
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 lingua, selettore, HTTP, cartella Cypress, dati, visibilit\xE0, posizione.",
|
|
1901
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 lingua, selettore, HTTP, cartella Cypress, login setup, dati, visibilit\xE0, posizione.",
|
|
1738
1902
|
PANEL_FILES: "\u{1F4C1} File \u2014 inserisci test nei tuoi .cy.ts e avvia il runner.",
|
|
1739
1903
|
SEC_SELECTOR: "\u{1F50E} Selettori",
|
|
1740
1904
|
SELECTOR_1: "Scegli l'attributo preferito (data-cy, data-testid, aria-label, id) in Config.",
|
|
@@ -1747,6 +1911,8 @@ var I18N_IT = {
|
|
|
1747
1911
|
ADV_RUNNER: "Runner locale: npx lib-e2e-cypress-runner per eseguire uno spec headless dall'editor.",
|
|
1748
1912
|
ADV_CROSSAPP: "Cross-app: la registrazione sopravvive al cambio di micro-frontend (stessa origine).",
|
|
1749
1913
|
ADV_DRAG: "Trascina il pulsante \u23FA per spostare il widget; la posizione viene ricordata.",
|
|
1914
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
1915
|
+
// TODO: translate (it)
|
|
1750
1916
|
CLOSE: "Chiudi",
|
|
1751
1917
|
TAB_REFERENCE: "Riferimento rapido",
|
|
1752
1918
|
TAB_GUIDE: "Guida all'uso",
|
|
@@ -1765,13 +1931,14 @@ var I18N_IT = {
|
|
|
1765
1931
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
1766
1932
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
1767
1933
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
1934
|
+
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.",
|
|
1935
|
+
// TODO: translate (it)
|
|
1768
1936
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
1769
1937
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
1770
1938
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
1771
1939
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
1772
1940
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
1773
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1774
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
1941
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
1775
1942
|
}
|
|
1776
1943
|
};
|
|
1777
1944
|
|
|
@@ -1802,8 +1969,11 @@ var I18N_DE = {
|
|
|
1802
1969
|
NOTES_PLACEHOLDER: "Was dieser Test validiert, Voraussetzungen usw.",
|
|
1803
1970
|
TICKET_LABEL: "Ticket / Issue (optional):",
|
|
1804
1971
|
TICKET_PLACEHOLDER: "Z. B.: PROJ-123, #42",
|
|
1805
|
-
TICKET_WARN: "Ung\xFCltige ID f\xFCr den konfigurierten Anbieter (wird trotzdem gespeichert)"
|
|
1972
|
+
TICKET_WARN: "Ung\xFCltige ID f\xFCr den konfigurierten Anbieter (wird trotzdem gespeichert)",
|
|
1806
1973
|
// TODO: translate
|
|
1974
|
+
CONFIRM_DISCARD: "Sind Sie sicher, dass Sie diese Aufzeichnung verwerfen m\xF6chten? Alle aufgezeichneten Schritte gehen verloren.",
|
|
1975
|
+
CONFIRM_DISCARD_BTN: "Ja, verwerfen",
|
|
1976
|
+
BACK_BTN: "Zur\xFCck"
|
|
1807
1977
|
},
|
|
1808
1978
|
TEST_EDITOR: {
|
|
1809
1979
|
NO_TAGS: "Keine Tags",
|
|
@@ -1885,7 +2055,30 @@ var I18N_DE = {
|
|
|
1885
2055
|
ISSUE_TRACKER_SUB: "F\xFCge beim Speichern eine Ticket-ID hinzu und erstelle einen klickbaren Link.",
|
|
1886
2056
|
ISSUE_TRACKER_PROVIDER: "Anbieter",
|
|
1887
2057
|
ISSUE_TRACKER_BASE_URL: "Basis-URL",
|
|
1888
|
-
ISSUE_TRACKER_BASE_URL_PH: "Z. B.: https://meinprojekt.atlassian.net"
|
|
2058
|
+
ISSUE_TRACKER_BASE_URL_PH: "Z. B.: https://meinprojekt.atlassian.net",
|
|
2059
|
+
LOGIN_SETUP_SECTION: "\u{1F510} Login setup",
|
|
2060
|
+
LOGIN_SETUP_NOT_CONFIGURED: "Nicht konfiguriert",
|
|
2061
|
+
LOGIN_SETUP_SET_UP_BTN: "Einrichten",
|
|
2062
|
+
LOGIN_SETUP_TITLE: "Login-Konfiguration",
|
|
2063
|
+
LOGIN_SETUP_CREATE_SCAFFOLD: "Scaffold erstellen",
|
|
2064
|
+
LOGIN_SETUP_SELECT_EXISTING: "Vorhandene Datei ausw\xE4hlen",
|
|
2065
|
+
LOGIN_SETUP_FILE_NAME_LABEL: "Dateiname",
|
|
2066
|
+
LOGIN_SETUP_FILE_NAME_PH: "Z. B.: cypress/common-services/login.service.ts",
|
|
2067
|
+
LOGIN_SETUP_RESCAN: "\u{1F504} Erneut scannen",
|
|
2068
|
+
LOGIN_SETUP_NO_FUNCTIONS: "Keine exportierten Funktionen gefunden. Scaffold ausf\xFCllen und erneut scannen.",
|
|
2069
|
+
LOGIN_SETUP_BEFORE_LABEL: "before()-Funktion (einmal pro Suite)",
|
|
2070
|
+
LOGIN_SETUP_BEFORE_EACH_LABEL: "beforeEach()-Funktion (vor jedem Test)",
|
|
2071
|
+
LOGIN_SETUP_NONE_OPTION: "\u2014 Keine \u2014",
|
|
2072
|
+
LOGIN_SETUP_SUMMARY: "Datei: {file} \xB7 before: {before} \xB7 beforeEach: {beforeEach}",
|
|
2073
|
+
LOGIN_SETUP_SAVE_BTN: "\u{1F4BE} Speichern",
|
|
2074
|
+
LOGIN_SETUP_CLEAR_BTN: "Konfiguration entfernen",
|
|
2075
|
+
LOGIN_SETUP_CANCEL_BTN: "Abbrechen",
|
|
2076
|
+
LOGIN_SETUP_SOURCE_LABEL: "Quelle der Login-Datei",
|
|
2077
|
+
LOGIN_SETUP_CREATE_SUB: "Scaffold mit Beispielfunktionen erstellen",
|
|
2078
|
+
LOGIN_SETUP_SELECT_SUB: "Auf eine vorhandene Login-Service-Datei verweisen",
|
|
2079
|
+
LOGIN_SETUP_INJECT_TITLE: "Login-Bl\xF6cke hinzuf\xFCgen?",
|
|
2080
|
+
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?",
|
|
2081
|
+
LOGIN_SETUP_INJECT_CONFIRM: "Ja, hinzuf\xFCgen"
|
|
1889
2082
|
},
|
|
1890
2083
|
SELECTOR_PICKER: {
|
|
1891
2084
|
TITLE: "DOM-Element ausw\xE4hlen",
|
|
@@ -1976,6 +2169,12 @@ var I18N_DE = {
|
|
|
1976
2169
|
FILE_SAVE_ERROR_TOAST: "Fehler beim Speichern",
|
|
1977
2170
|
FIXTURES_WRITTEN_TOAST: "\u2713 Fixtures in cypress/fixtures geschrieben",
|
|
1978
2171
|
FIXTURES_NO_FOLDER_TOAST: "Fixtures konnten nicht geschrieben werden (Cypress-Ordner konfigurieren)",
|
|
2172
|
+
FILE_UPLOAD_MANUAL_TOAST: "F\xFCge {files} zu cypress/fixtures/ hinzu, bevor du den Test ausf\xFChrst.",
|
|
2173
|
+
// TODO: translate
|
|
2174
|
+
FILE_UPLOAD_COPIED_TOAST: "{files} nach cypress/fixtures/ kopiert.",
|
|
2175
|
+
// TODO: translate
|
|
2176
|
+
FILE_UPLOAD_COPY_ERROR_TOAST: "{files} konnte nicht nach cypress/fixtures/ kopiert werden. F\xFCge es manuell hinzu.",
|
|
2177
|
+
// TODO: translate
|
|
1979
2178
|
BTN_CONFIG: "Config",
|
|
1980
2179
|
BTN_FILES: "Dateien",
|
|
1981
2180
|
BTN_COMMANDS: "Befehle",
|
|
@@ -2023,7 +2222,7 @@ var I18N_DE = {
|
|
|
2023
2222
|
SEC_PANELS: "\u{1F5C2} Panels",
|
|
2024
2223
|
PANEL_TESTS: "\u{1F4CB} Tests \u2014 gespeichert: nach Tag filtern, kopieren, kombinieren, exportieren.",
|
|
2025
2224
|
PANEL_COMMANDS: "\u2328\uFE0F Befehle \u2014 aktuelle Aufzeichnung: umsortieren, l\xF6schen, Assertions hinzuf\xFCgen.",
|
|
2026
|
-
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 Sprache, Selektor, HTTP, Cypress-Ordner, Daten, Sichtbarkeit, Position.",
|
|
2225
|
+
PANEL_CONFIG: "\u2699\uFE0F Config \u2014 Sprache, Selektor, HTTP, Cypress-Ordner, Login-Setup, Daten, Sichtbarkeit, Position.",
|
|
2027
2226
|
PANEL_FILES: "\u{1F4C1} Dateien \u2014 Tests in deine .cy.ts einf\xFCgen und den Runner starten.",
|
|
2028
2227
|
SEC_SELECTOR: "\u{1F50E} Selektoren",
|
|
2029
2228
|
SELECTOR_1: "W\xE4hle das bevorzugte Attribut (data-cy, data-testid, aria-label, id) unter Config.",
|
|
@@ -2036,6 +2235,8 @@ var I18N_DE = {
|
|
|
2036
2235
|
ADV_RUNNER: "Lokaler Runner: npx lib-e2e-cypress-runner, um einen Spec headless aus dem Editor zu starten.",
|
|
2037
2236
|
ADV_CROSSAPP: "Cross-App: die Aufzeichnung \xFCbersteht den Wechsel des Micro-Frontends (gleicher Origin).",
|
|
2038
2237
|
ADV_DRAG: "Ziehe den \u23FA-Button, um das Widget zu verschieben; die Position wird gemerkt.",
|
|
2238
|
+
ADV_LOGIN: "Login Setup (Ctrl+3): apunta a tu servicio de login; el grabador inyecta before()/beforeEach() autom\xE1ticamente en cada test nuevo.",
|
|
2239
|
+
// TODO: translate (de)
|
|
2039
2240
|
CLOSE: "Schlie\xDFen",
|
|
2040
2241
|
TAB_REFERENCE: "Kurzreferenz",
|
|
2041
2242
|
TAB_GUIDE: "Nutzungsleitfaden",
|
|
@@ -2054,13 +2255,14 @@ var I18N_DE = {
|
|
|
2054
2255
|
G_COV_4: "Selector fiable (data-cy/testid/aria/id) y picker cuando no hay uno claro.",
|
|
2055
2256
|
G_COV_5: "Aserciones autom\xE1ticas (Alt+click) y constructor manual.",
|
|
2056
2257
|
G_COV_6: "Continuidad al cambiar de micro-frontend (mismo origen), modo invisible e i18n.",
|
|
2258
|
+
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.",
|
|
2259
|
+
// TODO: translate (de)
|
|
2057
2260
|
G_SEC_NOTCOVERED: "\u{1F6AB} Qu\xE9 NO cubre (por ahora)",
|
|
2058
2261
|
G_NC_1: "Or\xEDgenes distintos / subdominios (solo mismo origen).",
|
|
2059
2262
|
G_NC_2: "Subida de ficheros (selectFile), arrastrar y soltar, y hover.",
|
|
2060
2263
|
G_NC_3: "La tecla Tab (Cypress no la soporta en .type()).",
|
|
2061
2264
|
G_NC_4: "Peticiones DELETE (se ignoran a prop\xF3sito).",
|
|
2062
|
-
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
2063
|
-
G_NC_6: "Generaci\xF3n de login program\xE1tico (en el roadmap)."
|
|
2265
|
+
G_NC_5: "iframes y Shadow DOM de terceros pueden no capturarse."
|
|
2064
2266
|
}
|
|
2065
2267
|
};
|
|
2066
2268
|
|
|
@@ -2103,34 +2305,8 @@ var TranslationService = class {
|
|
|
2103
2305
|
};
|
|
2104
2306
|
var translationService = new TranslationService();
|
|
2105
2307
|
|
|
2106
|
-
// src/utils/code-format.utils.ts
|
|
2107
|
-
function gcd(a, b) {
|
|
2108
|
-
while (b) {
|
|
2109
|
-
const t = b;
|
|
2110
|
-
b = a % b;
|
|
2111
|
-
a = t;
|
|
2112
|
-
}
|
|
2113
|
-
return a;
|
|
2114
|
-
}
|
|
2115
|
-
function escapeSingleQuotes(value) {
|
|
2116
|
-
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2117
|
-
}
|
|
2118
|
-
function normalizeBlock(code, baseIndent) {
|
|
2119
|
-
const lines = code.split("\n").map((l) => l.trimEnd());
|
|
2120
|
-
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
2121
|
-
if (!nonEmpty.length) return "";
|
|
2122
|
-
const indents = nonEmpty.map((l) => l.length - l.trimStart().length);
|
|
2123
|
-
const minIndent = Math.min(...indents);
|
|
2124
|
-
const relIndents = [...new Set(indents.map((n) => n - minIndent).filter((n) => n > 0))];
|
|
2125
|
-
const step = relIndents.length > 0 ? relIndents.reduce(gcd) : 2;
|
|
2126
|
-
return lines.map((l) => {
|
|
2127
|
-
if (!l.trim()) return "";
|
|
2128
|
-
const level = Math.round((l.length - l.trimStart().length - minIndent) / step);
|
|
2129
|
-
return baseIndent + " ".repeat(level) + l.trimStart();
|
|
2130
|
-
}).join("\n");
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
2308
|
// src/services/transformation.service.ts
|
|
2309
|
+
init_code_format_utils();
|
|
2134
2310
|
var TransformationService = class {
|
|
2135
2311
|
toLang(lang) {
|
|
2136
2312
|
return isLang(lang) ? lang : "en";
|
|
@@ -2195,6 +2371,7 @@ var advancedTestTransformationService = new AdvancedTestTransformationService();
|
|
|
2195
2371
|
init_selector_quality_utils();
|
|
2196
2372
|
|
|
2197
2373
|
// src/utils/assertion.utils.ts
|
|
2374
|
+
init_code_format_utils();
|
|
2198
2375
|
var MAX_TEXT_LENGTH = 60;
|
|
2199
2376
|
function inferAssertionCommand(el, selector) {
|
|
2200
2377
|
const should = (body) => `cy.get('${selector}').should(${body})`;
|
|
@@ -2219,6 +2396,7 @@ function inferAssertionCommand(el, selector) {
|
|
|
2219
2396
|
}
|
|
2220
2397
|
|
|
2221
2398
|
// src/services/recording.service.ts
|
|
2399
|
+
init_code_format_utils();
|
|
2222
2400
|
var OWN_SELECTOR = '[data-cy="lib-e2e-cypress-for-dummys"]';
|
|
2223
2401
|
function createSessionId() {
|
|
2224
2402
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
@@ -2234,6 +2412,7 @@ var RecordingService = class {
|
|
|
2234
2412
|
selectorNotFound$ = new Subject(null);
|
|
2235
2413
|
inputDebounceTimers = /* @__PURE__ */ new Map();
|
|
2236
2414
|
fixtures = /* @__PURE__ */ new Map();
|
|
2415
|
+
uploadedFiles = /* @__PURE__ */ new Map();
|
|
2237
2416
|
abort = new AbortController();
|
|
2238
2417
|
selectorStrategy = "data-cy";
|
|
2239
2418
|
/** Stable id of the current live session (null when none has started). */
|
|
@@ -2260,7 +2439,7 @@ var RecordingService = class {
|
|
|
2260
2439
|
this.isPaused$.next(false);
|
|
2261
2440
|
this.isRecording$.next(true);
|
|
2262
2441
|
this.addCommand(`cy.viewport(1900, 1200)`);
|
|
2263
|
-
this.addCommand(`cy.visit('${window.location.pathname}')`);
|
|
2442
|
+
this.addCommand(`cy.visit('${escapeSingleQuotes(window.location.pathname)}')`);
|
|
2264
2443
|
this.addCommand(`cy.get('[data-cy="lib-e2e-cypress-for-dummys"]').invoke('hide');`);
|
|
2265
2444
|
}
|
|
2266
2445
|
/**
|
|
@@ -2358,10 +2537,20 @@ var RecordingService = class {
|
|
|
2358
2537
|
getFixturesSnapshot() {
|
|
2359
2538
|
return [...this.fixtures.entries()].map(([name, content]) => ({ name, content }));
|
|
2360
2539
|
}
|
|
2540
|
+
addUploadedFile(filename, bytes) {
|
|
2541
|
+
this.uploadedFiles.set(filename, bytes);
|
|
2542
|
+
}
|
|
2543
|
+
getUploadedFilesSnapshot() {
|
|
2544
|
+
return [...this.uploadedFiles.entries()].map(([filename, bytes]) => ({ filename, bytes }));
|
|
2545
|
+
}
|
|
2546
|
+
clearUploadedFiles() {
|
|
2547
|
+
this.uploadedFiles.clear();
|
|
2548
|
+
}
|
|
2361
2549
|
clearCommands() {
|
|
2362
2550
|
this.commands$.next([]);
|
|
2363
2551
|
this.interceptors$.next([]);
|
|
2364
2552
|
this.fixtures.clear();
|
|
2553
|
+
this.uploadedFiles.clear();
|
|
2365
2554
|
}
|
|
2366
2555
|
clearInterceptors() {
|
|
2367
2556
|
this.interceptors$.next([]);
|
|
@@ -2481,7 +2670,8 @@ var RecordingService = class {
|
|
|
2481
2670
|
if (e.altKey) return;
|
|
2482
2671
|
const selector = this.resolveSelectorFor(e.target);
|
|
2483
2672
|
if (!selector) return;
|
|
2484
|
-
const
|
|
2673
|
+
const safeSelector = escapeSingleQuotes(selector);
|
|
2674
|
+
const clickCmd = `cy.get('${safeSelector}').click()`;
|
|
2485
2675
|
let cmds = this.commands$.getValue();
|
|
2486
2676
|
let removed = 0;
|
|
2487
2677
|
while (removed < 2 && cmds.length > 0 && cmds[cmds.length - 1] === clickCmd) {
|
|
@@ -2489,7 +2679,7 @@ var RecordingService = class {
|
|
|
2489
2679
|
removed++;
|
|
2490
2680
|
}
|
|
2491
2681
|
if (removed > 0) this.commands$.next(cmds);
|
|
2492
|
-
this.addCommand(`cy.get('${
|
|
2682
|
+
this.addCommand(`cy.get('${safeSelector}').dblclick()`);
|
|
2493
2683
|
},
|
|
2494
2684
|
{ signal: this.abort.signal }
|
|
2495
2685
|
);
|
|
@@ -2501,7 +2691,7 @@ var RecordingService = class {
|
|
|
2501
2691
|
if (!this.isRecording$.getValue() || this.isPaused$.getValue()) return;
|
|
2502
2692
|
const selector = this.resolveSelectorFor(e.target);
|
|
2503
2693
|
if (!selector) return;
|
|
2504
|
-
this.addCommand(`cy.get('${selector}').rightclick()`);
|
|
2694
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').rightclick()`);
|
|
2505
2695
|
},
|
|
2506
2696
|
{ signal: this.abort.signal }
|
|
2507
2697
|
);
|
|
@@ -2521,7 +2711,7 @@ var RecordingService = class {
|
|
|
2521
2711
|
if (!selector) return;
|
|
2522
2712
|
this.flushInputDebounce(target);
|
|
2523
2713
|
const token = key === "Enter" ? "{enter}" : "{esc}";
|
|
2524
|
-
this.addCommand(`cy.get('${selector}').type('${token}')`);
|
|
2714
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').type('${token}')`);
|
|
2525
2715
|
},
|
|
2526
2716
|
{ signal: this.abort.signal }
|
|
2527
2717
|
);
|
|
@@ -2545,19 +2735,52 @@ var RecordingService = class {
|
|
|
2545
2735
|
if (!this.isRecording$.getValue()) return;
|
|
2546
2736
|
const target = e.target;
|
|
2547
2737
|
if (!target || this.isOwnElement(target)) return;
|
|
2548
|
-
|
|
2738
|
+
const tag = target.tagName.toLowerCase();
|
|
2739
|
+
if (tag === "select") {
|
|
2549
2740
|
this.handleSelectEvent(target);
|
|
2741
|
+
} else if (tag === "input") {
|
|
2742
|
+
const type = (target.getAttribute("type") ?? "").toLowerCase();
|
|
2743
|
+
if (type === "file") {
|
|
2744
|
+
void this.handleFileInputChange(target);
|
|
2745
|
+
}
|
|
2550
2746
|
}
|
|
2551
2747
|
},
|
|
2552
2748
|
{ signal: this.abort.signal }
|
|
2553
2749
|
);
|
|
2554
2750
|
}
|
|
2751
|
+
async handleFileInputChange(target) {
|
|
2752
|
+
if (this.isPaused$.getValue()) return;
|
|
2753
|
+
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2754
|
+
if (!container) return;
|
|
2755
|
+
const selector = this.getReliableSelector(container);
|
|
2756
|
+
if (!selector || this.isOwnSelector(selector)) return;
|
|
2757
|
+
const files = Array.from(target.files ?? []);
|
|
2758
|
+
if (!files.length) return;
|
|
2759
|
+
const paths = files.map((f) => `'cypress/fixtures/${escapeSingleQuotes(f.name)}'`);
|
|
2760
|
+
const pathArg = paths.length === 1 ? paths[0] : `[${paths.join(", ")}]`;
|
|
2761
|
+
this.addCommand(`cy.get('${escapeSingleQuotes(selector)}').selectFile(${pathArg})`);
|
|
2762
|
+
for (const file of files) {
|
|
2763
|
+
const bytes = await this.readFileAsArrayBuffer(file);
|
|
2764
|
+
this.addUploadedFile(file.name, bytes);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
readFileAsArrayBuffer(file) {
|
|
2768
|
+
if (typeof file.arrayBuffer === "function") {
|
|
2769
|
+
return file.arrayBuffer();
|
|
2770
|
+
}
|
|
2771
|
+
return new Promise((resolve, reject) => {
|
|
2772
|
+
const reader = new FileReader();
|
|
2773
|
+
reader.onload = () => resolve(reader.result);
|
|
2774
|
+
reader.onerror = () => reject(reader.error);
|
|
2775
|
+
reader.readAsArrayBuffer(file);
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2555
2778
|
listenToRouteChanges() {
|
|
2556
2779
|
let lastUrl = window.location.pathname + window.location.search + window.location.hash;
|
|
2557
2780
|
const addUrlCommand = (newUrl) => {
|
|
2558
2781
|
if (!this.isRecording$.getValue()) return;
|
|
2559
2782
|
if (newUrl === lastUrl) return;
|
|
2560
|
-
this.addCommand(`cy.url().should('include', '${newUrl}')`);
|
|
2783
|
+
this.addCommand(`cy.url().should('include', '${escapeSingleQuotes(newUrl)}')`);
|
|
2561
2784
|
lastUrl = newUrl;
|
|
2562
2785
|
};
|
|
2563
2786
|
const wrapMethod = (type) => {
|
|
@@ -2658,7 +2881,8 @@ var RecordingService = class {
|
|
|
2658
2881
|
if (matSelect) dataCy = matSelect.closest("[data-cy]")?.getAttribute("data-cy") ?? null;
|
|
2659
2882
|
if (!dataCy) dataCy = target.closest("[data-cy]")?.getAttribute("data-cy") ?? null;
|
|
2660
2883
|
if (dataCy) {
|
|
2661
|
-
|
|
2884
|
+
const safeSel = escapeSingleQuotes(`[data-cy="${escapeCssAttrValue(dataCy)}"]`);
|
|
2885
|
+
this.addCommand(`cy.get('${safeSel}').click()`);
|
|
2662
2886
|
return;
|
|
2663
2887
|
}
|
|
2664
2888
|
if (selector) {
|
|
@@ -2690,7 +2914,7 @@ var RecordingService = class {
|
|
|
2690
2914
|
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2691
2915
|
if (!container) return;
|
|
2692
2916
|
const selector = this.getReliableSelector(container);
|
|
2693
|
-
const value = target.value
|
|
2917
|
+
const value = escapeSingleQuotes(target.value);
|
|
2694
2918
|
this.addGenericCommand({
|
|
2695
2919
|
selector,
|
|
2696
2920
|
action: (s) => `cy.get('${s}').clear().type('${value}')`
|
|
@@ -2709,7 +2933,7 @@ var RecordingService = class {
|
|
|
2709
2933
|
const container = target.closest("[data-cy], [data-testid], [aria-label], [id]");
|
|
2710
2934
|
if (!container) return;
|
|
2711
2935
|
const selector = this.getReliableSelector(container);
|
|
2712
|
-
const value = target.value
|
|
2936
|
+
const value = escapeSingleQuotes(target.value);
|
|
2713
2937
|
this.addGenericCommand({
|
|
2714
2938
|
selector,
|
|
2715
2939
|
action: (s) => `cy.get('${s}').select('${value}')`
|
|
@@ -2718,7 +2942,7 @@ var RecordingService = class {
|
|
|
2718
2942
|
// ── Shared helpers ────────────────────────────────────────────────────────
|
|
2719
2943
|
addGenericCommand(opts) {
|
|
2720
2944
|
if (!opts.selector || this.isOwnSelector(opts.selector)) return;
|
|
2721
|
-
this.addCommand(opts.action(opts.selector));
|
|
2945
|
+
this.addCommand(opts.action(escapeSingleQuotes(opts.selector)));
|
|
2722
2946
|
}
|
|
2723
2947
|
getReliableSelector(el) {
|
|
2724
2948
|
const strategy = this.selectorStrategy;
|
|
@@ -2730,20 +2954,20 @@ var RecordingService = class {
|
|
|
2730
2954
|
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;
|
|
2731
2955
|
switch (strategy) {
|
|
2732
2956
|
case "data-testid":
|
|
2733
|
-
if (dataTestid) return `[data-testid="${dataTestid}"]`;
|
|
2734
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2957
|
+
if (dataTestid) return `[data-testid="${escapeCssAttrValue(dataTestid)}"]`;
|
|
2958
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2735
2959
|
return validId ? `#${validId}` : null;
|
|
2736
2960
|
case "aria-label":
|
|
2737
|
-
if (ariaLabel) return `[aria-label="${ariaLabel}"]`;
|
|
2738
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2961
|
+
if (ariaLabel) return `[aria-label="${escapeCssAttrValue(ariaLabel)}"]`;
|
|
2962
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2739
2963
|
return validId ? `#${validId}` : null;
|
|
2740
2964
|
case "id":
|
|
2741
2965
|
if (validId) return `#${validId}`;
|
|
2742
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2743
|
-
return dataTestid ? `[data-testid="${dataTestid}"]` : null;
|
|
2966
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2967
|
+
return dataTestid ? `[data-testid="${escapeCssAttrValue(dataTestid)}"]` : null;
|
|
2744
2968
|
default:
|
|
2745
|
-
if (dataCy) return `[data-cy="${dataCy}"]`;
|
|
2746
|
-
if (dataDotCy) return `[data.cy="${dataDotCy}"]`;
|
|
2969
|
+
if (dataCy) return `[data-cy="${escapeCssAttrValue(dataCy)}"]`;
|
|
2970
|
+
if (dataDotCy) return `[data.cy="${escapeCssAttrValue(dataDotCy)}"]`;
|
|
2747
2971
|
return validId ? `#${validId}` : null;
|
|
2748
2972
|
}
|
|
2749
2973
|
}
|
|
@@ -2769,8 +2993,41 @@ var RecordingService = class {
|
|
|
2769
2993
|
|
|
2770
2994
|
// src/services/persistence.service.ts
|
|
2771
2995
|
var import_idb = require("idb");
|
|
2996
|
+
init_code_format_utils();
|
|
2997
|
+
|
|
2998
|
+
// src/models/issue-tracker.model.ts
|
|
2999
|
+
var ISSUE_TRACKER_PRESETS = {
|
|
3000
|
+
jira: { id: "jira", label: "Jira", urlPattern: "{baseUrl}/browse/{id}", idPattern: /^[A-Z][A-Z0-9]+-\d+$/ },
|
|
3001
|
+
github: { id: "github", label: "GitHub Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3002
|
+
gitlab: { id: "gitlab", label: "GitLab Issues", urlPattern: "{baseUrl}/-/issues/{id}", idPattern: /^\d+$/ },
|
|
3003
|
+
azure: { id: "azure", label: "Azure DevOps", urlPattern: "{baseUrl}/_workitems/edit/{id}", idPattern: /^\d+$/ },
|
|
3004
|
+
bitbucket: { id: "bitbucket", label: "Bitbucket Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3005
|
+
custom: { id: "custom", label: "Custom", urlPattern: "{baseUrl}/{id}", idPattern: /.+/ }
|
|
3006
|
+
};
|
|
3007
|
+
var DEFAULT_ISSUE_TRACKER_CONFIG = {
|
|
3008
|
+
enabled: false,
|
|
3009
|
+
provider: "jira",
|
|
3010
|
+
baseUrl: ""
|
|
3011
|
+
};
|
|
3012
|
+
|
|
3013
|
+
// src/utils/ticket.utils.ts
|
|
3014
|
+
function buildTicketUrl(ticketId, config) {
|
|
3015
|
+
if (!config.enabled || !ticketId.trim() || !config.baseUrl.trim()) return null;
|
|
3016
|
+
const preset = ISSUE_TRACKER_PRESETS[config.provider];
|
|
3017
|
+
const base = config.baseUrl.replace(/\/+$/, "");
|
|
3018
|
+
return preset.urlPattern.replace("{baseUrl}", base).replace("{id}", encodeURIComponent(ticketId.trim()));
|
|
3019
|
+
}
|
|
3020
|
+
function buildTicketComment(ticketId) {
|
|
3021
|
+
return `// Ticket: ${ticketId.trim()}`;
|
|
3022
|
+
}
|
|
3023
|
+
function isValidTicketId(ticketId, provider) {
|
|
3024
|
+
if (!ticketId.trim()) return false;
|
|
3025
|
+
return ISSUE_TRACKER_PRESETS[provider].idPattern.test(ticketId.trim());
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
// src/services/persistence.service.ts
|
|
2772
3029
|
var ACTIVE_SESSION_ID = 1;
|
|
2773
|
-
var PersistenceService = class {
|
|
3030
|
+
var PersistenceService = class _PersistenceService {
|
|
2774
3031
|
// dbName is overridable so tests can use unique names for isolation.
|
|
2775
3032
|
constructor(dbName = DB_SCHEMA.name) {
|
|
2776
3033
|
this.dbName = dbName;
|
|
@@ -2823,7 +3080,8 @@ var PersistenceService = class {
|
|
|
2823
3080
|
if (!record) return null;
|
|
2824
3081
|
const commands = await this.getCommandStrings(testId);
|
|
2825
3082
|
const interceptors = await this.getInterceptorStrings(testId);
|
|
2826
|
-
const
|
|
3083
|
+
const ticketComment = record.ticketId ? buildTicketComment(record.ticketId) + "\n" : "";
|
|
3084
|
+
const itBlock = `${ticketComment}it('${escapeSingleQuotes(record.name)}', () => {
|
|
2827
3085
|
${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
2828
3086
|
});`;
|
|
2829
3087
|
const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => normalizeBlock(i, " ")).join("\n") + "\n" : "";
|
|
@@ -2904,6 +3162,30 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2904
3162
|
const records = await db.getAll("configuration");
|
|
2905
3163
|
return records[0] ?? null;
|
|
2906
3164
|
}
|
|
3165
|
+
// ── Login setup config ───────────────────────────────────────────────────
|
|
3166
|
+
static LOGIN_SETUP_KEY = "loginSetupConfig";
|
|
3167
|
+
async saveLoginSetup(config) {
|
|
3168
|
+
await this.setConfigKey(_PersistenceService.LOGIN_SETUP_KEY, JSON.stringify(config));
|
|
3169
|
+
}
|
|
3170
|
+
async getLoginSetup() {
|
|
3171
|
+
const record = await this.getConfig(_PersistenceService.LOGIN_SETUP_KEY);
|
|
3172
|
+
if (!record) return null;
|
|
3173
|
+
const raw = record[_PersistenceService.LOGIN_SETUP_KEY];
|
|
3174
|
+
if (typeof raw !== "string") return null;
|
|
3175
|
+
try {
|
|
3176
|
+
return JSON.parse(raw);
|
|
3177
|
+
} catch {
|
|
3178
|
+
return null;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
async clearLoginSetup() {
|
|
3182
|
+
const db = await this.getDB();
|
|
3183
|
+
const records = await db.getAll("configuration");
|
|
3184
|
+
if (!records.length) return;
|
|
3185
|
+
const current = { ...records[0] };
|
|
3186
|
+
delete current[_PersistenceService.LOGIN_SETUP_KEY];
|
|
3187
|
+
await db.put("configuration", current);
|
|
3188
|
+
}
|
|
2907
3189
|
// ── Active recording session ──────────────────────────────────────────────
|
|
2908
3190
|
/** Upserts the live recording session (single record, fixed key). */
|
|
2909
3191
|
async saveActiveSession(state) {
|
|
@@ -2946,6 +3228,24 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2946
3228
|
}
|
|
2947
3229
|
await this.bulkInsertWithoutId("interceptors", interceptors);
|
|
2948
3230
|
}
|
|
3231
|
+
async hasDirectoryAccess() {
|
|
3232
|
+
const config = await this.getGeneralConfig();
|
|
3233
|
+
return !!config?.["cypressDirectoryHandle"];
|
|
3234
|
+
}
|
|
3235
|
+
async writeUploadedFile(filename, bytes) {
|
|
3236
|
+
const config = await this.getGeneralConfig();
|
|
3237
|
+
const dirHandle = config?.["cypressDirectoryHandle"];
|
|
3238
|
+
if (!dirHandle) throw new Error("No Cypress folder configured");
|
|
3239
|
+
const perm = dirHandle;
|
|
3240
|
+
let state = await perm.queryPermission({ mode: "readwrite" });
|
|
3241
|
+
if (state !== "granted") state = await perm.requestPermission({ mode: "readwrite" });
|
|
3242
|
+
if (state !== "granted") throw new Error("Write permission denied");
|
|
3243
|
+
const fixturesDir = await dirHandle.getDirectoryHandle("fixtures", { create: true });
|
|
3244
|
+
const fileHandle = await fixturesDir.getFileHandle(filename, { create: true });
|
|
3245
|
+
const writable = await fileHandle.createWritable();
|
|
3246
|
+
await writable.write(bytes);
|
|
3247
|
+
await writable.close();
|
|
3248
|
+
}
|
|
2949
3249
|
async requestDirectoryPermissions() {
|
|
2950
3250
|
if (!("showDirectoryPicker" in window)) {
|
|
2951
3251
|
throw new Error("File System Access API not supported");
|
|
@@ -2988,7 +3288,36 @@ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
2988
3288
|
}
|
|
2989
3289
|
}
|
|
2990
3290
|
};
|
|
2991
|
-
|
|
3291
|
+
|
|
3292
|
+
// src/services/http-monitor.ts
|
|
3293
|
+
init_code_format_utils();
|
|
3294
|
+
|
|
3295
|
+
// src/utils/redact.utils.ts
|
|
3296
|
+
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
3297
|
+
"password",
|
|
3298
|
+
"token",
|
|
3299
|
+
"secret",
|
|
3300
|
+
"authorization",
|
|
3301
|
+
"cookie",
|
|
3302
|
+
"access_token",
|
|
3303
|
+
"refresh_token"
|
|
3304
|
+
]);
|
|
3305
|
+
function isSensitiveKey(key) {
|
|
3306
|
+
return SENSITIVE_KEYS.has(key.toLowerCase());
|
|
3307
|
+
}
|
|
3308
|
+
function redactSensitiveFields(obj) {
|
|
3309
|
+
if (Array.isArray(obj)) {
|
|
3310
|
+
return obj.map(redactSensitiveFields);
|
|
3311
|
+
}
|
|
3312
|
+
if (obj !== null && typeof obj === "object") {
|
|
3313
|
+
const result = {};
|
|
3314
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3315
|
+
result[key] = isSensitiveKey(key) ? "[REDACTED]" : redactSensitiveFields(value);
|
|
3316
|
+
}
|
|
3317
|
+
return result;
|
|
3318
|
+
}
|
|
3319
|
+
return obj;
|
|
3320
|
+
}
|
|
2992
3321
|
|
|
2993
3322
|
// src/services/http-monitor.ts
|
|
2994
3323
|
var INTERCEPTED_METHODS = ["GET", "POST", "PUT"];
|
|
@@ -3001,8 +3330,12 @@ function generateAlias(method, url) {
|
|
|
3001
3330
|
return `${method.toLowerCase()}-intercepted-request`;
|
|
3002
3331
|
}
|
|
3003
3332
|
}
|
|
3333
|
+
var SAFE_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
3004
3334
|
function buildValidations(base, obj) {
|
|
3005
|
-
return Object.keys(obj).filter((key) => key !== "id" && key !== "uid").map((key) =>
|
|
3335
|
+
return Object.keys(obj).filter((key) => key !== "id" && key !== "uid").map((key) => {
|
|
3336
|
+
const accessor = SAFE_IDENTIFIER.test(key) ? `.${key}` : `['${escapeSingleQuotes(key)}']`;
|
|
3337
|
+
return `expect(${base}${accessor}).to.equal(${JSON.stringify(obj[key])});`;
|
|
3338
|
+
}).join("\n");
|
|
3006
3339
|
}
|
|
3007
3340
|
function buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody) {
|
|
3008
3341
|
if (extendedHttp && method === "GET" && responseBody) {
|
|
@@ -3054,20 +3387,149 @@ function resolveMethod(input, init) {
|
|
|
3054
3387
|
if (input instanceof Request) return input.method.toUpperCase();
|
|
3055
3388
|
return "GET";
|
|
3056
3389
|
}
|
|
3390
|
+
async function _handleFetchInterception(recording, input, init, responseClone) {
|
|
3391
|
+
const method = resolveMethod(input, init);
|
|
3392
|
+
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
3393
|
+
const url = resolveUrl(input);
|
|
3394
|
+
const alias = generateAlias(method, url);
|
|
3395
|
+
recording.registerInterceptor(method, url, alias);
|
|
3396
|
+
const extendedHttp = localStorage.getItem("extendedHttpCommands") === "true";
|
|
3397
|
+
const fixtureMode = localStorage.getItem("fixtureMode") === "true";
|
|
3398
|
+
const requestBody = extendedHttp ? parseRequestBody(init) : null;
|
|
3399
|
+
let responseText = null;
|
|
3400
|
+
let responseBody = null;
|
|
3401
|
+
if (extendedHttp || fixtureMode && method === "GET") {
|
|
3402
|
+
try {
|
|
3403
|
+
responseText = await responseClone.text();
|
|
3404
|
+
if (extendedHttp) responseBody = parseJsonObject(responseText);
|
|
3405
|
+
} catch {
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (fixtureMode && method === "GET" && responseText !== null) {
|
|
3409
|
+
const pretty = prettyJsonOrNull(responseText);
|
|
3410
|
+
if (pretty !== null) {
|
|
3411
|
+
const redacted = redactSensitiveFields(JSON.parse(pretty));
|
|
3412
|
+
recording.registerFixture(`${alias}.json`, JSON.stringify(redacted, null, 2));
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
const cmd = buildCyWaitCommand(
|
|
3416
|
+
method,
|
|
3417
|
+
alias,
|
|
3418
|
+
extendedHttp,
|
|
3419
|
+
requestBody ? redactSensitiveFields(requestBody) : null,
|
|
3420
|
+
responseBody ? redactSensitiveFields(responseBody) : null
|
|
3421
|
+
);
|
|
3422
|
+
recording.addCommand(cmd);
|
|
3423
|
+
}
|
|
3424
|
+
function _handleXhrInterception(recording, method, url, requestBody, responseText) {
|
|
3425
|
+
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
3426
|
+
const alias = generateAlias(method, url);
|
|
3427
|
+
recording.registerInterceptor(method, url, alias);
|
|
3428
|
+
if (localStorage.getItem("fixtureMode") === "true" && method === "GET") {
|
|
3429
|
+
const pretty = prettyJsonOrNull(responseText);
|
|
3430
|
+
if (pretty !== null) {
|
|
3431
|
+
const redacted = redactSensitiveFields(JSON.parse(pretty));
|
|
3432
|
+
recording.registerFixture(`${alias}.json`, JSON.stringify(redacted, null, 2));
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
const extendedHttp = localStorage.getItem("extendedHttpCommands") === "true";
|
|
3436
|
+
const responseBody = extendedHttp ? parseJsonObject(responseText) : null;
|
|
3437
|
+
const cmd = buildCyWaitCommand(
|
|
3438
|
+
method,
|
|
3439
|
+
alias,
|
|
3440
|
+
extendedHttp,
|
|
3441
|
+
requestBody ? redactSensitiveFields(requestBody) : null,
|
|
3442
|
+
responseBody ? redactSensitiveFields(responseBody) : null
|
|
3443
|
+
);
|
|
3444
|
+
recording.addCommand(cmd);
|
|
3445
|
+
}
|
|
3446
|
+
var _refCount = 0;
|
|
3447
|
+
var _originalFetch = null;
|
|
3448
|
+
var _originalXHR = null;
|
|
3449
|
+
var _recordings = /* @__PURE__ */ new Set();
|
|
3450
|
+
function _patchFetch() {
|
|
3451
|
+
const orig = window.fetch;
|
|
3452
|
+
_originalFetch = orig;
|
|
3453
|
+
window.fetch = async (input, init) => {
|
|
3454
|
+
const response = await orig(input, init);
|
|
3455
|
+
try {
|
|
3456
|
+
const active = [..._recordings];
|
|
3457
|
+
if (active.length > 0) {
|
|
3458
|
+
await Promise.all(
|
|
3459
|
+
active.map((rec) => _handleFetchInterception(rec, input, init, response.clone()).catch(() => {
|
|
3460
|
+
}))
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
} catch {
|
|
3464
|
+
}
|
|
3465
|
+
return response;
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
function _restoreFetch() {
|
|
3469
|
+
if (!_originalFetch) return;
|
|
3470
|
+
window.fetch = _originalFetch;
|
|
3471
|
+
_originalFetch = null;
|
|
3472
|
+
}
|
|
3473
|
+
function _patchXhr() {
|
|
3474
|
+
const OrigXHR = window.XMLHttpRequest;
|
|
3475
|
+
_originalXHR = OrigXHR;
|
|
3476
|
+
window.XMLHttpRequest = class extends OrigXHR {
|
|
3477
|
+
_xhrMethod = "GET";
|
|
3478
|
+
_xhrUrl = "";
|
|
3479
|
+
_xhrRequestBody = null;
|
|
3480
|
+
open(method, url, async = true, user, password) {
|
|
3481
|
+
this._xhrMethod = method.toUpperCase();
|
|
3482
|
+
this._xhrUrl = url instanceof URL ? url.href : url;
|
|
3483
|
+
super.open(method, url, async, user, password);
|
|
3484
|
+
}
|
|
3485
|
+
send(body) {
|
|
3486
|
+
if (typeof body === "string") {
|
|
3487
|
+
this._xhrRequestBody = parseJsonObject(body);
|
|
3488
|
+
}
|
|
3489
|
+
this.addEventListener("load", () => {
|
|
3490
|
+
try {
|
|
3491
|
+
for (const rec of _recordings) {
|
|
3492
|
+
_handleXhrInterception(rec, this._xhrMethod, this._xhrUrl, this._xhrRequestBody, this.responseText);
|
|
3493
|
+
}
|
|
3494
|
+
} catch {
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3497
|
+
super.send(body);
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
function _restoreXhr() {
|
|
3502
|
+
if (!_originalXHR) return;
|
|
3503
|
+
window.XMLHttpRequest = _originalXHR;
|
|
3504
|
+
_originalXHR = null;
|
|
3505
|
+
}
|
|
3506
|
+
function _resetHttpMonitorState() {
|
|
3507
|
+
_refCount = 0;
|
|
3508
|
+
_originalFetch = null;
|
|
3509
|
+
_originalXHR = null;
|
|
3510
|
+
_recordings.clear();
|
|
3511
|
+
}
|
|
3057
3512
|
var HttpMonitor = class {
|
|
3058
3513
|
constructor(recording) {
|
|
3059
3514
|
this.recording = recording;
|
|
3060
3515
|
}
|
|
3061
3516
|
recording;
|
|
3062
|
-
originalFetch = null;
|
|
3063
|
-
originalXHR = null;
|
|
3064
3517
|
install() {
|
|
3065
|
-
this.
|
|
3066
|
-
|
|
3518
|
+
_recordings.add(this.recording);
|
|
3519
|
+
_refCount++;
|
|
3520
|
+
if (_refCount === 1) {
|
|
3521
|
+
_patchFetch();
|
|
3522
|
+
_patchXhr();
|
|
3523
|
+
}
|
|
3067
3524
|
}
|
|
3068
3525
|
uninstall() {
|
|
3069
|
-
this.
|
|
3070
|
-
|
|
3526
|
+
_recordings.delete(this.recording);
|
|
3527
|
+
if (_refCount <= 0) return;
|
|
3528
|
+
_refCount--;
|
|
3529
|
+
if (_refCount === 0) {
|
|
3530
|
+
_restoreFetch();
|
|
3531
|
+
_restoreXhr();
|
|
3532
|
+
}
|
|
3071
3533
|
}
|
|
3072
3534
|
isExtendedHttpEnabled() {
|
|
3073
3535
|
return localStorage.getItem("extendedHttpCommands") === "true";
|
|
@@ -3075,100 +3537,6 @@ var HttpMonitor = class {
|
|
|
3075
3537
|
isFixtureModeEnabled() {
|
|
3076
3538
|
return localStorage.getItem("fixtureMode") === "true";
|
|
3077
3539
|
}
|
|
3078
|
-
installFetch() {
|
|
3079
|
-
if (this.originalFetch) return;
|
|
3080
|
-
const originalFetch = window.fetch;
|
|
3081
|
-
this.originalFetch = originalFetch;
|
|
3082
|
-
window.fetch = async (input, init) => {
|
|
3083
|
-
const response = await originalFetch(input, init);
|
|
3084
|
-
try {
|
|
3085
|
-
await this.handleFetchInterception(input, init, response.clone());
|
|
3086
|
-
} catch {
|
|
3087
|
-
}
|
|
3088
|
-
return response;
|
|
3089
|
-
};
|
|
3090
|
-
}
|
|
3091
|
-
uninstallFetch() {
|
|
3092
|
-
if (!this.originalFetch) return;
|
|
3093
|
-
window.fetch = this.originalFetch;
|
|
3094
|
-
this.originalFetch = null;
|
|
3095
|
-
}
|
|
3096
|
-
installXhr() {
|
|
3097
|
-
if (this.originalXHR) return;
|
|
3098
|
-
this.originalXHR = window.XMLHttpRequest;
|
|
3099
|
-
const OrigXHR = this.originalXHR;
|
|
3100
|
-
const handleXhrInterception = this.handleXhrInterception.bind(this);
|
|
3101
|
-
window.XMLHttpRequest = class extends OrigXHR {
|
|
3102
|
-
_xhrMethod = "GET";
|
|
3103
|
-
_xhrUrl = "";
|
|
3104
|
-
_xhrRequestBody = null;
|
|
3105
|
-
open(method, url, async = true, user, password) {
|
|
3106
|
-
this._xhrMethod = method.toUpperCase();
|
|
3107
|
-
this._xhrUrl = url instanceof URL ? url.href : url;
|
|
3108
|
-
super.open(method, url, async, user, password);
|
|
3109
|
-
}
|
|
3110
|
-
send(body) {
|
|
3111
|
-
if (typeof body === "string") {
|
|
3112
|
-
this._xhrRequestBody = parseJsonObject(body);
|
|
3113
|
-
}
|
|
3114
|
-
this.addEventListener("load", () => {
|
|
3115
|
-
try {
|
|
3116
|
-
handleXhrInterception(
|
|
3117
|
-
this._xhrMethod,
|
|
3118
|
-
this._xhrUrl,
|
|
3119
|
-
this._xhrRequestBody,
|
|
3120
|
-
this.responseText
|
|
3121
|
-
);
|
|
3122
|
-
} catch {
|
|
3123
|
-
}
|
|
3124
|
-
});
|
|
3125
|
-
super.send(body);
|
|
3126
|
-
}
|
|
3127
|
-
};
|
|
3128
|
-
}
|
|
3129
|
-
uninstallXhr() {
|
|
3130
|
-
if (!this.originalXHR) return;
|
|
3131
|
-
window.XMLHttpRequest = this.originalXHR;
|
|
3132
|
-
this.originalXHR = null;
|
|
3133
|
-
}
|
|
3134
|
-
async handleFetchInterception(input, init, responseClone) {
|
|
3135
|
-
const method = resolveMethod(input, init);
|
|
3136
|
-
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
3137
|
-
const url = resolveUrl(input);
|
|
3138
|
-
const alias = generateAlias(method, url);
|
|
3139
|
-
this.recording.registerInterceptor(method, url, alias);
|
|
3140
|
-
const extendedHttp = this.isExtendedHttpEnabled();
|
|
3141
|
-
const fixtureMode = this.isFixtureModeEnabled();
|
|
3142
|
-
const requestBody = extendedHttp ? parseRequestBody(init) : null;
|
|
3143
|
-
let responseText = null;
|
|
3144
|
-
let responseBody = null;
|
|
3145
|
-
if (extendedHttp || fixtureMode && method === "GET") {
|
|
3146
|
-
try {
|
|
3147
|
-
responseText = await responseClone.text();
|
|
3148
|
-
if (extendedHttp) responseBody = parseJsonObject(responseText);
|
|
3149
|
-
} catch {
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
if (fixtureMode && method === "GET" && responseText !== null) {
|
|
3153
|
-
const pretty = prettyJsonOrNull(responseText);
|
|
3154
|
-
if (pretty !== null) this.recording.registerFixture(`${alias}.json`, pretty);
|
|
3155
|
-
}
|
|
3156
|
-
const cmd = buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody);
|
|
3157
|
-
this.recording.addCommand(cmd);
|
|
3158
|
-
}
|
|
3159
|
-
handleXhrInterception(method, url, requestBody, responseText) {
|
|
3160
|
-
if (!INTERCEPTED_METHODS.includes(method)) return;
|
|
3161
|
-
const alias = generateAlias(method, url);
|
|
3162
|
-
this.recording.registerInterceptor(method, url, alias);
|
|
3163
|
-
if (this.isFixtureModeEnabled() && method === "GET") {
|
|
3164
|
-
const pretty = prettyJsonOrNull(responseText);
|
|
3165
|
-
if (pretty !== null) this.recording.registerFixture(`${alias}.json`, pretty);
|
|
3166
|
-
}
|
|
3167
|
-
const extendedHttp = this.isExtendedHttpEnabled();
|
|
3168
|
-
const responseBody = extendedHttp ? parseJsonObject(responseText) : null;
|
|
3169
|
-
const cmd = buildCyWaitCommand(method, alias, extendedHttp, requestBody, responseBody);
|
|
3170
|
-
this.recording.addCommand(cmd);
|
|
3171
|
-
}
|
|
3172
3540
|
};
|
|
3173
3541
|
|
|
3174
3542
|
// src/utils/styles.utils.ts
|
|
@@ -3417,6 +3785,19 @@ function setSwal2DataCyAttribute(dataCy = "lib-e2e-cypress-for-dummys") {
|
|
|
3417
3785
|
document.querySelector(".swal2-html-container")?.setAttribute("data-cy", dataCy);
|
|
3418
3786
|
document.querySelector(".swal2-title")?.setAttribute("data-cy", dataCy);
|
|
3419
3787
|
}
|
|
3788
|
+
function ensurePopupDimensions(popup, heightPx) {
|
|
3789
|
+
if (!popup) return;
|
|
3790
|
+
popup.style.height = `${heightPx}px`;
|
|
3791
|
+
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
3792
|
+
if (!htmlContainer) return;
|
|
3793
|
+
htmlContainer.style.flex = "1";
|
|
3794
|
+
htmlContainer.style.minHeight = "0";
|
|
3795
|
+
htmlContainer.style.overflow = "hidden";
|
|
3796
|
+
htmlContainer.style.padding = "0";
|
|
3797
|
+
htmlContainer.style.margin = "0";
|
|
3798
|
+
htmlContainer.style.display = "flex";
|
|
3799
|
+
htmlContainer.style.flexDirection = "column";
|
|
3800
|
+
}
|
|
3420
3801
|
|
|
3421
3802
|
// src/utils/toast.utils.ts
|
|
3422
3803
|
function showToast(message, isSuccess = true) {
|
|
@@ -3689,35 +4070,8 @@ var TestPrevisualizerElement = class extends HTMLElement {
|
|
|
3689
4070
|
}
|
|
3690
4071
|
}
|
|
3691
4072
|
};
|
|
3692
|
-
if (!customElements.get("test-previsualizer")) {
|
|
3693
|
-
customElements.define("test-previsualizer", TestPrevisualizerElement);
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
|
-
// src/models/issue-tracker.model.ts
|
|
3697
|
-
var ISSUE_TRACKER_PRESETS = {
|
|
3698
|
-
jira: { id: "jira", label: "Jira", urlPattern: "{baseUrl}/browse/{id}", idPattern: /^[A-Z][A-Z0-9]+-\d+$/ },
|
|
3699
|
-
github: { id: "github", label: "GitHub Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3700
|
-
gitlab: { id: "gitlab", label: "GitLab Issues", urlPattern: "{baseUrl}/-/issues/{id}", idPattern: /^\d+$/ },
|
|
3701
|
-
azure: { id: "azure", label: "Azure DevOps", urlPattern: "{baseUrl}/_workitems/edit/{id}", idPattern: /^\d+$/ },
|
|
3702
|
-
bitbucket: { id: "bitbucket", label: "Bitbucket Issues", urlPattern: "{baseUrl}/issues/{id}", idPattern: /^\d+$/ },
|
|
3703
|
-
custom: { id: "custom", label: "Custom", urlPattern: "{baseUrl}/{id}", idPattern: /.+/ }
|
|
3704
|
-
};
|
|
3705
|
-
var DEFAULT_ISSUE_TRACKER_CONFIG = {
|
|
3706
|
-
enabled: false,
|
|
3707
|
-
provider: "jira",
|
|
3708
|
-
baseUrl: ""
|
|
3709
|
-
};
|
|
3710
|
-
|
|
3711
|
-
// src/utils/ticket.utils.ts
|
|
3712
|
-
function buildTicketUrl(ticketId, config) {
|
|
3713
|
-
if (!config.enabled || !ticketId.trim() || !config.baseUrl.trim()) return null;
|
|
3714
|
-
const preset = ISSUE_TRACKER_PRESETS[config.provider];
|
|
3715
|
-
const base = config.baseUrl.replace(/\/+$/, "");
|
|
3716
|
-
return preset.urlPattern.replace("{baseUrl}", base).replace("{id}", encodeURIComponent(ticketId.trim()));
|
|
3717
|
-
}
|
|
3718
|
-
function isValidTicketId(ticketId, provider) {
|
|
3719
|
-
if (!ticketId.trim()) return false;
|
|
3720
|
-
return ISSUE_TRACKER_PRESETS[provider].idPattern.test(ticketId.trim());
|
|
4073
|
+
if (!customElements.get("lib-e2e-test-previsualizer")) {
|
|
4074
|
+
customElements.define("lib-e2e-test-previsualizer", TestPrevisualizerElement);
|
|
3721
4075
|
}
|
|
3722
4076
|
|
|
3723
4077
|
// src/components/save-test/save-test.styles.ts
|
|
@@ -3781,10 +4135,21 @@ var SAVE_TEST_STYLES = `
|
|
|
3781
4135
|
transition: color 0.12s; padding: 0; background: none; border: none;
|
|
3782
4136
|
}
|
|
3783
4137
|
.chip-del:hover { color: #f85149; filter: none; }
|
|
4138
|
+
.discard-warn { color: #e6edf3; font-size: 13px; margin-bottom: 20px; line-height: 1.5; }
|
|
3784
4139
|
`;
|
|
3785
4140
|
|
|
3786
4141
|
// src/components/save-test/save-test.template.ts
|
|
3787
4142
|
init_html_utils();
|
|
4143
|
+
function renderSaveTestConfirmDiscard(t) {
|
|
4144
|
+
return `
|
|
4145
|
+
<div class="container">
|
|
4146
|
+
<p class="discard-warn">\u26A0 ${t("SAVE_TEST.CONFIRM_DISCARD")}</p>
|
|
4147
|
+
<div class="btn-row">
|
|
4148
|
+
<button class="btn-danger" id="btn-confirm-discard">${t("SAVE_TEST.CONFIRM_DISCARD_BTN")}</button>
|
|
4149
|
+
<button id="btn-back-discard">${t("SAVE_TEST.BACK_BTN")}</button>
|
|
4150
|
+
</div>
|
|
4151
|
+
</div>`;
|
|
4152
|
+
}
|
|
3788
4153
|
function renderSaveTestAsk(t) {
|
|
3789
4154
|
return `
|
|
3790
4155
|
<div class="container">
|
|
@@ -3830,6 +4195,7 @@ function renderSaveTestDesc(description, notes, tags, ticketId, issueTrackerEnab
|
|
|
3830
4195
|
var SaveTestElement = class extends HTMLElement {
|
|
3831
4196
|
shadow;
|
|
3832
4197
|
_step = "ask";
|
|
4198
|
+
_stepBeforeDiscard = "ask";
|
|
3833
4199
|
description = "";
|
|
3834
4200
|
notes = "";
|
|
3835
4201
|
tags = [];
|
|
@@ -3857,8 +4223,17 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3857
4223
|
this.dispatch("saveandexport", { description: this.description.trim(), notes: this.notes, tags: [...this.tags], ticketId: this.ticketId.trim() });
|
|
3858
4224
|
}
|
|
3859
4225
|
cancel() {
|
|
4226
|
+
this._stepBeforeDiscard = this._step === "confirm-discard" ? "ask" : this._step;
|
|
4227
|
+
this._step = "confirm-discard";
|
|
4228
|
+
this.render();
|
|
4229
|
+
}
|
|
4230
|
+
confirmDiscard() {
|
|
3860
4231
|
this.dispatch("savetest", { description: null, notes: "", tags: [], ticketId: "" });
|
|
3861
4232
|
}
|
|
4233
|
+
backFromDiscard() {
|
|
4234
|
+
this._step = this._stepBeforeDiscard;
|
|
4235
|
+
this.render();
|
|
4236
|
+
}
|
|
3862
4237
|
restartComponent() {
|
|
3863
4238
|
this._step = "ask";
|
|
3864
4239
|
this.description = "";
|
|
@@ -3889,6 +4264,12 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3889
4264
|
this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
|
|
3890
4265
|
}
|
|
3891
4266
|
render() {
|
|
4267
|
+
if (this._step === "confirm-discard") {
|
|
4268
|
+
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestConfirmDiscard(this.t.bind(this))}`;
|
|
4269
|
+
this.shadow.getElementById("btn-confirm-discard")?.addEventListener("click", () => this.confirmDiscard());
|
|
4270
|
+
this.shadow.getElementById("btn-back-discard")?.addEventListener("click", () => this.backFromDiscard());
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
3892
4273
|
if (this._step === "ask") {
|
|
3893
4274
|
this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestAsk(this.t.bind(this))}`;
|
|
3894
4275
|
this.shadow.getElementById("btn-yes")?.addEventListener("click", () => this.askSave());
|
|
@@ -3930,10 +4311,13 @@ var SaveTestElement = class extends HTMLElement {
|
|
|
3930
4311
|
}
|
|
3931
4312
|
}
|
|
3932
4313
|
};
|
|
3933
|
-
if (!customElements.get("save-test")) {
|
|
3934
|
-
customElements.define("save-test", SaveTestElement);
|
|
4314
|
+
if (!customElements.get("lib-e2e-save-test")) {
|
|
4315
|
+
customElements.define("lib-e2e-save-test", SaveTestElement);
|
|
3935
4316
|
}
|
|
3936
4317
|
|
|
4318
|
+
// src/components/test-editor/test-editor.ts
|
|
4319
|
+
init_code_format_utils();
|
|
4320
|
+
|
|
3937
4321
|
// src/components/test-editor/test-editor.styles.ts
|
|
3938
4322
|
var TEST_EDITOR_STYLES = `
|
|
3939
4323
|
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; overflow: hidden; }
|
|
@@ -4156,6 +4540,7 @@ function highlightLine(line) {
|
|
|
4156
4540
|
}
|
|
4157
4541
|
|
|
4158
4542
|
// src/components/test-editor/test-editor.template.ts
|
|
4543
|
+
init_code_format_utils();
|
|
4159
4544
|
function renderTestRow(test, i, state, t) {
|
|
4160
4545
|
const { selectMode, selectedIds, expandedIndex, interceptorsByTest, locale, issueTrackerConfig } = state;
|
|
4161
4546
|
const expanded = expandedIndex === i;
|
|
@@ -4164,7 +4549,9 @@ function renderTestRow(test, i, state, t) {
|
|
|
4164
4549
|
const tagsHtml = (test.tags ?? []).length ? `<span class="test-tags">${(test.tags ?? []).map((tag) => `<span class="test-tag">${escHtml(tag)}</span>`).join("")}</span>` : "";
|
|
4165
4550
|
const checkbox = selectMode ? `<input type="checkbox" ${selectedIds.has(test.id) ? "checked" : ""} data-select="${test.id}" />` : "";
|
|
4166
4551
|
const hasIcps = (test.interceptors ?? []).length > 0;
|
|
4167
|
-
const
|
|
4552
|
+
const ticketId = test.ticketId;
|
|
4553
|
+
const ticketComment = ticketId ? buildTicketComment(ticketId) + "\n" : "";
|
|
4554
|
+
const itBlockCode = `${ticketComment}it('${escapeSingleQuotes(test.name)}', () => {
|
|
4168
4555
|
${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
4169
4556
|
});`;
|
|
4170
4557
|
const icpBlockCode = icps.length ? `beforeEach(() => {
|
|
@@ -4172,7 +4559,6 @@ ${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
|
4172
4559
|
${icps.map((c) => normalizeBlock(c, " ")).join("\n")}
|
|
4173
4560
|
});` : "";
|
|
4174
4561
|
const notesHtml = expanded && test.notes ? `<p class="test-notes">${escHtml(test.notes)}</p>` : "";
|
|
4175
|
-
const ticketId = test.ticketId;
|
|
4176
4562
|
const ticketUrl = ticketId ? buildTicketUrl(ticketId, issueTrackerConfig) : null;
|
|
4177
4563
|
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>` : "";
|
|
4178
4564
|
const body = expanded ? `
|
|
@@ -4421,8 +4807,85 @@ ${beforeEach}${itBlocks}
|
|
|
4421
4807
|
});
|
|
4422
4808
|
}
|
|
4423
4809
|
};
|
|
4424
|
-
if (!customElements.get("test-editor")) {
|
|
4425
|
-
customElements.define("test-editor", TestEditorElement);
|
|
4810
|
+
if (!customElements.get("lib-e2e-test-editor")) {
|
|
4811
|
+
customElements.define("lib-e2e-test-editor", TestEditorElement);
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
// src/utils/login-setup.utils.ts
|
|
4815
|
+
var EXPORT_FN_RE = /^(?!\/\/).*\bexport\s+(?:async\s+)?function\s+(\w+)|^(?!\/\/).*\bexport\s+const\s+(\w+)\s*=\s*(?:async\s*)?\(/gm;
|
|
4816
|
+
function extractExportedFunctions(fileContent) {
|
|
4817
|
+
const names = [];
|
|
4818
|
+
let match;
|
|
4819
|
+
const re = new RegExp(EXPORT_FN_RE.source, EXPORT_FN_RE.flags);
|
|
4820
|
+
while ((match = re.exec(fileContent)) !== null) {
|
|
4821
|
+
const name = match[1] ?? match[2];
|
|
4822
|
+
if (name) names.push(name);
|
|
4823
|
+
}
|
|
4824
|
+
return names;
|
|
4825
|
+
}
|
|
4826
|
+
function buildLoginScaffold() {
|
|
4827
|
+
return [
|
|
4828
|
+
"/**",
|
|
4829
|
+
" * Login service scaffold \u2014 fill in your own auth logic.",
|
|
4830
|
+
" * This file is not committed to source control.",
|
|
4831
|
+
" */",
|
|
4832
|
+
"",
|
|
4833
|
+
"export function fetchAuthToken() {",
|
|
4834
|
+
" // TODO: implement \u2014 retrieve and return your auth token",
|
|
4835
|
+
"}",
|
|
4836
|
+
"",
|
|
4837
|
+
"export function setupRequestInterceptors() {",
|
|
4838
|
+
" // TODO: implement \u2014 configure cy.intercept() or set localStorage/cookies",
|
|
4839
|
+
"}",
|
|
4840
|
+
""
|
|
4841
|
+
].join("\n");
|
|
4842
|
+
}
|
|
4843
|
+
function buildLoginImportPath(fromFilePath, toServiceFilePath) {
|
|
4844
|
+
const fromParts = fromFilePath.replace(/\\/g, "/").split("/");
|
|
4845
|
+
const toParts = toServiceFilePath.replace(/\\/g, "/").split("/");
|
|
4846
|
+
fromParts.pop();
|
|
4847
|
+
let common = 0;
|
|
4848
|
+
while (common < fromParts.length && common < toParts.length - 1 && fromParts[common] === toParts[common]) {
|
|
4849
|
+
common++;
|
|
4850
|
+
}
|
|
4851
|
+
const ups = fromParts.length - common;
|
|
4852
|
+
const downs = toParts.slice(common);
|
|
4853
|
+
const serviceFile = downs[downs.length - 1].replace(/\.ts$/, "");
|
|
4854
|
+
const prefix = ups === 0 ? "." : Array(ups).fill("..").join("/");
|
|
4855
|
+
const middle = downs.slice(0, -1);
|
|
4856
|
+
return [prefix, ...middle, serviceFile].join("/");
|
|
4857
|
+
}
|
|
4858
|
+
function hasLoginBlocks(content, fnNames) {
|
|
4859
|
+
return fnNames.some((fn) => new RegExp(`\\b${fn}\\s*\\(`).test(content));
|
|
4860
|
+
}
|
|
4861
|
+
function injectLoginBlocksIntoExisting(content, importLine, beforeBlock, beforeEachBlock) {
|
|
4862
|
+
let result = content;
|
|
4863
|
+
if (importLine && !content.includes(importLine)) {
|
|
4864
|
+
result = `${importLine}
|
|
4865
|
+
|
|
4866
|
+
${result}`;
|
|
4867
|
+
}
|
|
4868
|
+
const blocks = [beforeBlock, beforeEachBlock].filter(Boolean).join("");
|
|
4869
|
+
if (blocks) {
|
|
4870
|
+
const describeMatch = result.match(/(describe\s*\(.*?{)/s);
|
|
4871
|
+
if (describeMatch) {
|
|
4872
|
+
const insertPos = (describeMatch.index ?? 0) + describeMatch[0].length;
|
|
4873
|
+
result = result.slice(0, insertPos) + "\n" + blocks + result.slice(insertPos);
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
return result;
|
|
4877
|
+
}
|
|
4878
|
+
function buildLoginBlocks(importPath, beforeFn, beforeEachFn) {
|
|
4879
|
+
if (!beforeFn && !beforeEachFn) {
|
|
4880
|
+
return { importLine: "", beforeBlock: "", beforeEachBlock: "" };
|
|
4881
|
+
}
|
|
4882
|
+
const fns = [...new Set([beforeFn, beforeEachFn].filter(Boolean))];
|
|
4883
|
+
const importLine = `import { ${fns.join(", ")} } from '${importPath}';`;
|
|
4884
|
+
const beforeBlock = beforeFn ? ` before(() => { ${beforeFn}(); });
|
|
4885
|
+
` : "";
|
|
4886
|
+
const beforeEachBlock = beforeEachFn ? ` beforeEach(() => { ${beforeEachFn}(); });
|
|
4887
|
+
` : "";
|
|
4888
|
+
return { importLine, beforeBlock, beforeEachBlock };
|
|
4426
4889
|
}
|
|
4427
4890
|
|
|
4428
4891
|
// src/components/configuration/configuration.styles.ts
|
|
@@ -4583,6 +5046,99 @@ var CONFIGURATION_STYLES = `
|
|
|
4583
5046
|
.btn-export-confirm { background: #238636; border-color: #238636; color: #fff; }
|
|
4584
5047
|
.btn-export-confirm:hover:not(:disabled) { background: #2ea043; border-color: #2ea043; }
|
|
4585
5048
|
.btn-export-confirm:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
5049
|
+
|
|
5050
|
+
/* \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 */
|
|
5051
|
+
input, select, button, textarea { font-family: inherit; }
|
|
5052
|
+
|
|
5053
|
+
/* \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 */
|
|
5054
|
+
.btn-primary { background: #1f6feb; border-color: #1f6feb; color: #fff; }
|
|
5055
|
+
.btn-primary:hover { background: #388bfd; border-color: #388bfd; color: #fff; }
|
|
5056
|
+
.btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
5057
|
+
|
|
5058
|
+
/* \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 */
|
|
5059
|
+
.ls-modal { max-width: 500px; }
|
|
5060
|
+
.ls-hd { display: flex; align-items: center; gap: 8px; }
|
|
5061
|
+
.ls-hd-icon { font-size: 15px; }
|
|
5062
|
+
|
|
5063
|
+
.ls-body {
|
|
5064
|
+
padding: 20px; overflow-y: auto; flex: 1;
|
|
5065
|
+
display: flex; flex-direction: column; gap: 20px;
|
|
5066
|
+
scrollbar-width: thin; scrollbar-color: #30363d transparent;
|
|
5067
|
+
}
|
|
5068
|
+
|
|
5069
|
+
.ls-section { display: flex; flex-direction: column; gap: 8px; }
|
|
5070
|
+
.ls-section-label {
|
|
5071
|
+
font-size: 10px; font-weight: 700; text-transform: uppercase;
|
|
5072
|
+
letter-spacing: 0.8px; color: #484f58;
|
|
5073
|
+
}
|
|
5074
|
+
|
|
5075
|
+
.ls-actions { display: flex; gap: 10px; }
|
|
5076
|
+
.ls-action-btn {
|
|
5077
|
+
flex: 1; padding: 14px 12px; border-radius: 8px;
|
|
5078
|
+
background: #0d1117; border: 1px solid #21262d; color: #8b949e;
|
|
5079
|
+
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
|
5080
|
+
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
|
5081
|
+
}
|
|
5082
|
+
.ls-action-btn:hover { background: #161b22; color: #e6edf3; border-color: #388bfd; }
|
|
5083
|
+
.ls-action-icon { font-size: 22px; line-height: 1; }
|
|
5084
|
+
.ls-action-label { font-size: 12px; font-weight: 600; color: #c9d1d9; }
|
|
5085
|
+
.ls-action-btn:hover .ls-action-label { color: #e6edf3; }
|
|
5086
|
+
.ls-action-sub { font-size: 10px; color: #484f58; line-height: 1.4; text-align: center; }
|
|
5087
|
+
|
|
5088
|
+
.ls-file-row {
|
|
5089
|
+
display: flex; align-items: center; gap: 10px;
|
|
5090
|
+
padding: 10px 12px; background: #0d1117;
|
|
5091
|
+
border: 1px solid #21262d; border-radius: 8px;
|
|
5092
|
+
}
|
|
5093
|
+
.ls-file-icon { font-size: 14px; flex-shrink: 0; }
|
|
5094
|
+
.ls-file-name {
|
|
5095
|
+
flex: 1; font-size: 11px; color: #c9d1d9;
|
|
5096
|
+
font-family: 'Cascadia Code','Fira Code','Consolas',monospace;
|
|
5097
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
5098
|
+
min-width: 0;
|
|
5099
|
+
}
|
|
5100
|
+
.ls-btn-rescan {
|
|
5101
|
+
flex-shrink: 0; padding: 4px 10px; font-size: 11px;
|
|
5102
|
+
background: #161b22; border-color: #30363d; color: #8b949e;
|
|
5103
|
+
}
|
|
5104
|
+
.ls-btn-rescan:hover { background: #21262d; color: #e6edf3; }
|
|
5105
|
+
|
|
5106
|
+
.ls-input {
|
|
5107
|
+
width: 100%; padding: 8px 10px; background: #0d1117;
|
|
5108
|
+
border: 1px solid #30363d; border-radius: 6px;
|
|
5109
|
+
color: #c9d1d9; font-size: 12px; outline: none; font-family: inherit;
|
|
5110
|
+
}
|
|
5111
|
+
.ls-input:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.12); }
|
|
5112
|
+
|
|
5113
|
+
.ls-fns {
|
|
5114
|
+
display: flex; flex-wrap: wrap; gap: 6px;
|
|
5115
|
+
padding: 10px; background: #0d1117;
|
|
5116
|
+
border: 1px solid #21262d; border-radius: 6px; min-height: 38px;
|
|
5117
|
+
}
|
|
5118
|
+
.ls-fn-chip {
|
|
5119
|
+
padding: 3px 10px; font-size: 11px; color: #79c0ff;
|
|
5120
|
+
background: rgba(47,129,247,0.1); border: 1px solid rgba(47,129,247,0.2);
|
|
5121
|
+
border-radius: 12px; font-family: 'Cascadia Code','Fira Code','Consolas',monospace;
|
|
5122
|
+
}
|
|
5123
|
+
|
|
5124
|
+
.ls-field-block { display: flex; flex-direction: column; gap: 6px; }
|
|
5125
|
+
.ls-field-block + .ls-field-block { margin-top: 12px; }
|
|
5126
|
+
.ls-label { font-size: 11px; color: #8b949e; }
|
|
5127
|
+
.ls-select {
|
|
5128
|
+
width: 100%; padding: 8px 10px; background: #0d1117;
|
|
5129
|
+
border: 1px solid #30363d; border-radius: 6px;
|
|
5130
|
+
color: #c9d1d9; font-size: 12px; outline: none; cursor: pointer;
|
|
5131
|
+
}
|
|
5132
|
+
.ls-select:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.12); }
|
|
5133
|
+
|
|
5134
|
+
.ls-empty-state {
|
|
5135
|
+
display: flex; align-items: center; gap: 10px;
|
|
5136
|
+
padding: 14px; background: #0d1117; border: 1px solid #21262d;
|
|
5137
|
+
border-radius: 8px; font-size: 12px; color: #484f58;
|
|
5138
|
+
}
|
|
5139
|
+
.ls-empty-icon { font-size: 16px; }
|
|
5140
|
+
|
|
5141
|
+
.ls-ft { padding: 14px 20px; }
|
|
4586
5142
|
`;
|
|
4587
5143
|
|
|
4588
5144
|
// src/components/configuration/configuration.template.ts
|
|
@@ -4800,6 +5356,13 @@ function renderConfiguration(state, t) {
|
|
|
4800
5356
|
</div>` : ""}
|
|
4801
5357
|
</div>
|
|
4802
5358
|
|
|
5359
|
+
<!-- Login Setup -->
|
|
5360
|
+
<div class="card card-wide">
|
|
5361
|
+
<div class="card-hd">${t("CONFIG.LOGIN_SETUP_SECTION")}</div>
|
|
5362
|
+
${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>`}
|
|
5363
|
+
<button id="btn-open-login-setup" class="btn-sm">${t("CONFIG.LOGIN_SETUP_SET_UP_BTN")}</button>
|
|
5364
|
+
</div>
|
|
5365
|
+
|
|
4803
5366
|
<!-- Data -->
|
|
4804
5367
|
<div class="card card-wide">
|
|
4805
5368
|
<div class="card-hd">${t("CONFIG.DATA_SECTION")}</div>
|
|
@@ -4813,7 +5376,93 @@ function renderConfiguration(state, t) {
|
|
|
4813
5376
|
</div>
|
|
4814
5377
|
</div>
|
|
4815
5378
|
|
|
4816
|
-
</div>${renderExportOverlay(state, t)}`;
|
|
5379
|
+
</div>${renderExportOverlay(state, t)}${renderLoginSetupOverlay(state, t)}`;
|
|
5380
|
+
}
|
|
5381
|
+
function renderLoginSetupOverlay(state, t) {
|
|
5382
|
+
if (!state.isLoginSetupOpen) return "";
|
|
5383
|
+
const cfg = state.loginSetupConfig;
|
|
5384
|
+
const path = state.loginSetupDraftPath ?? cfg?.filePath ?? "";
|
|
5385
|
+
const fns = state.loginSetupDraftFunctions ?? cfg?.detectedFunctions ?? [];
|
|
5386
|
+
const beforeFn = state.loginSetupDraftBeforeFn !== void 0 ? state.loginSetupDraftBeforeFn : cfg?.beforeFn ?? null;
|
|
5387
|
+
const beforeEachFn = state.loginSetupDraftBeforeEachFn !== void 0 ? state.loginSetupDraftBeforeEachFn : cfg?.beforeEachFn ?? null;
|
|
5388
|
+
const hasContent = state.loginSetupDraftHasContent ?? !!cfg?.fileContent;
|
|
5389
|
+
const fnOption = (name, selected) => `<option value="${escAttr(name ?? "")}" ${selected === name ? "selected" : ""}>${name ? escHtml(name) : t("CONFIG.LOGIN_SETUP_NONE_OPTION")}</option>`;
|
|
5390
|
+
const fnDropdown = (id, label, selected) => `
|
|
5391
|
+
<div class="ls-field-block">
|
|
5392
|
+
<label class="ls-label" for="${id}">${escHtml(label)}</label>
|
|
5393
|
+
<select id="${id}" class="ls-select">
|
|
5394
|
+
${fnOption(null, selected)}
|
|
5395
|
+
${fns.map((fn) => fnOption(fn, selected)).join("")}
|
|
5396
|
+
</select>
|
|
5397
|
+
</div>`;
|
|
5398
|
+
const functionsSection = hasContent ? fns.length > 0 ? `<div class="ls-section">
|
|
5399
|
+
<span class="ls-section-label">Funciones detectadas</span>
|
|
5400
|
+
<div class="ls-fns">${fns.map((fn) => `<span class="ls-fn-chip">${escHtml(fn)}</span>`).join("")}</div>
|
|
5401
|
+
</div>
|
|
5402
|
+
<div class="ls-section">
|
|
5403
|
+
<span class="ls-section-label">Asignaci\xF3n de bloques</span>
|
|
5404
|
+
${fnDropdown("login-setup-before-fn", t("CONFIG.LOGIN_SETUP_BEFORE_LABEL"), beforeFn)}
|
|
5405
|
+
${fnDropdown("login-setup-before-each-fn", t("CONFIG.LOGIN_SETUP_BEFORE_EACH_LABEL"), beforeEachFn)}
|
|
5406
|
+
</div>` : `<div class="ls-empty-state">
|
|
5407
|
+
<span class="ls-empty-icon">\u{1F50D}</span>
|
|
5408
|
+
<span>${t("CONFIG.LOGIN_SETUP_NO_FUNCTIONS")}</span>
|
|
5409
|
+
</div>` : "";
|
|
5410
|
+
const fileSection = hasContent ? `<div class="ls-section">
|
|
5411
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_FILE_NAME_LABEL")}</span>
|
|
5412
|
+
<div class="ls-file-row">
|
|
5413
|
+
<span class="ls-file-icon">\u{1F4C4}</span>
|
|
5414
|
+
<span class="ls-file-name">${escHtml(path)}</span>
|
|
5415
|
+
<button id="btn-login-setup-rescan" class="ls-btn-rescan">${t("CONFIG.LOGIN_SETUP_RESCAN")}</button>
|
|
5416
|
+
</div>
|
|
5417
|
+
</div>` : `<div class="ls-section">
|
|
5418
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_FILE_NAME_LABEL")}</span>
|
|
5419
|
+
<input id="login-setup-filepath" type="text" class="ls-input"
|
|
5420
|
+
placeholder="${escAttr(t("CONFIG.LOGIN_SETUP_FILE_NAME_PH"))}"
|
|
5421
|
+
value="${escAttr(path)}" />
|
|
5422
|
+
</div>`;
|
|
5423
|
+
return `
|
|
5424
|
+
<div class="export-overlay" id="login-setup-overlay">
|
|
5425
|
+
<div class="export-modal ls-modal">
|
|
5426
|
+
|
|
5427
|
+
<div class="export-hd ls-hd">
|
|
5428
|
+
<span class="ls-hd-icon">\u{1F510}</span>
|
|
5429
|
+
${t("CONFIG.LOGIN_SETUP_TITLE")}
|
|
5430
|
+
</div>
|
|
5431
|
+
|
|
5432
|
+
<div class="ls-body">
|
|
5433
|
+
|
|
5434
|
+
<div class="ls-section">
|
|
5435
|
+
<span class="ls-section-label">${t("CONFIG.LOGIN_SETUP_SOURCE_LABEL")}</span>
|
|
5436
|
+
<div class="ls-actions">
|
|
5437
|
+
<button id="btn-login-setup-create-scaffold" class="ls-action-btn">
|
|
5438
|
+
<span class="ls-action-icon">\u2728</span>
|
|
5439
|
+
<span class="ls-action-label">${t("CONFIG.LOGIN_SETUP_CREATE_SCAFFOLD")}</span>
|
|
5440
|
+
<span class="ls-action-sub">${t("CONFIG.LOGIN_SETUP_CREATE_SUB")}</span>
|
|
5441
|
+
</button>
|
|
5442
|
+
<button id="btn-login-setup-pick-file" class="ls-action-btn">
|
|
5443
|
+
<span class="ls-action-icon">\u{1F4C2}</span>
|
|
5444
|
+
<span class="ls-action-label">${t("CONFIG.LOGIN_SETUP_SELECT_EXISTING")}</span>
|
|
5445
|
+
<span class="ls-action-sub">${t("CONFIG.LOGIN_SETUP_SELECT_SUB")}</span>
|
|
5446
|
+
</button>
|
|
5447
|
+
</div>
|
|
5448
|
+
</div>
|
|
5449
|
+
|
|
5450
|
+
${fileSection}
|
|
5451
|
+
|
|
5452
|
+
${functionsSection}
|
|
5453
|
+
|
|
5454
|
+
</div>
|
|
5455
|
+
|
|
5456
|
+
<div class="export-ft ls-ft">
|
|
5457
|
+
<button id="btn-login-setup-clear" class="btn-danger">${t("CONFIG.LOGIN_SETUP_CLEAR_BTN")}</button>
|
|
5458
|
+
<span class="export-ft-actions">
|
|
5459
|
+
<button id="btn-login-setup-cancel">${t("CONFIG.LOGIN_SETUP_CANCEL_BTN")}</button>
|
|
5460
|
+
<button id="btn-login-setup-save" class="btn-primary">${t("CONFIG.LOGIN_SETUP_SAVE_BTN")}</button>
|
|
5461
|
+
</span>
|
|
5462
|
+
</div>
|
|
5463
|
+
|
|
5464
|
+
</div>
|
|
5465
|
+
</div>`;
|
|
4817
5466
|
}
|
|
4818
5467
|
|
|
4819
5468
|
// src/components/configuration/configuration.ts
|
|
@@ -4834,6 +5483,15 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4834
5483
|
exportSelectedIds = /* @__PURE__ */ new Set();
|
|
4835
5484
|
exportSelectedTags = /* @__PURE__ */ new Set();
|
|
4836
5485
|
issueTrackerConfig = { ...DEFAULT_ISSUE_TRACKER_CONFIG };
|
|
5486
|
+
isLoginSetupOpen = false;
|
|
5487
|
+
loginSetupConfig = null;
|
|
5488
|
+
loginSetupDraftPath = "";
|
|
5489
|
+
loginSetupDraftFunctions = [];
|
|
5490
|
+
loginSetupDraftBeforeFn = null;
|
|
5491
|
+
loginSetupDraftBeforeEachFn = null;
|
|
5492
|
+
loginSetupDraftHasContent = false;
|
|
5493
|
+
loginSetupDraftHandle = null;
|
|
5494
|
+
loginSetupDraftContent = "";
|
|
4837
5495
|
filesystemGranted = false;
|
|
4838
5496
|
cypressFolderName = null;
|
|
4839
5497
|
constructor() {
|
|
@@ -4875,6 +5533,7 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4875
5533
|
provider: config?.["issueTrackerProvider"] ?? DEFAULT_ISSUE_TRACKER_CONFIG.provider,
|
|
4876
5534
|
baseUrl: config?.["issueTrackerBaseUrl"] ?? ""
|
|
4877
5535
|
};
|
|
5536
|
+
this.loginSetupConfig = await this.persistence.getLoginSetup();
|
|
4878
5537
|
this.render();
|
|
4879
5538
|
}
|
|
4880
5539
|
async onLanguageChange(lang) {
|
|
@@ -4940,6 +5599,85 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
4940
5599
|
await this.persistence.setConfig({ issueTrackerBaseUrl: baseUrl });
|
|
4941
5600
|
this.dispatchEvent(new CustomEvent("issuetrackerchange", { detail: { ...this.issueTrackerConfig }, bubbles: true, composed: true }));
|
|
4942
5601
|
}
|
|
5602
|
+
openLoginSetupPanel() {
|
|
5603
|
+
this.isLoginSetupOpen = true;
|
|
5604
|
+
const cfg = this.loginSetupConfig;
|
|
5605
|
+
this.loginSetupDraftHandle = null;
|
|
5606
|
+
this.loginSetupDraftContent = cfg?.fileContent ?? "";
|
|
5607
|
+
this.loginSetupDraftPath = cfg?.filePath ?? "";
|
|
5608
|
+
this.loginSetupDraftFunctions = cfg?.detectedFunctions ?? [];
|
|
5609
|
+
this.loginSetupDraftBeforeFn = cfg?.beforeFn ?? null;
|
|
5610
|
+
this.loginSetupDraftBeforeEachFn = cfg?.beforeEachFn ?? null;
|
|
5611
|
+
this.loginSetupDraftHasContent = !!cfg?.fileContent;
|
|
5612
|
+
this.render();
|
|
5613
|
+
}
|
|
5614
|
+
closeLoginSetupPanel() {
|
|
5615
|
+
this.isLoginSetupOpen = false;
|
|
5616
|
+
this.render();
|
|
5617
|
+
}
|
|
5618
|
+
async saveLoginSetupConfig(config) {
|
|
5619
|
+
await this.persistence.saveLoginSetup(config);
|
|
5620
|
+
this.loginSetupConfig = config;
|
|
5621
|
+
this.isLoginSetupOpen = false;
|
|
5622
|
+
this.render();
|
|
5623
|
+
}
|
|
5624
|
+
async clearLoginSetupConfig() {
|
|
5625
|
+
await this.persistence.clearLoginSetup();
|
|
5626
|
+
this.loginSetupConfig = null;
|
|
5627
|
+
this.render();
|
|
5628
|
+
}
|
|
5629
|
+
async loginSetupPickFile() {
|
|
5630
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim() ?? "";
|
|
5631
|
+
try {
|
|
5632
|
+
const [handle] = await window.showOpenFilePicker({
|
|
5633
|
+
types: [{ description: "TypeScript files", accept: { "text/plain": [".ts"] } }],
|
|
5634
|
+
multiple: false
|
|
5635
|
+
});
|
|
5636
|
+
this.loginSetupDraftHandle = handle;
|
|
5637
|
+
const file = await handle.getFile();
|
|
5638
|
+
this.loginSetupDraftContent = await file.text();
|
|
5639
|
+
this.loginSetupDraftHasContent = true;
|
|
5640
|
+
this.loginSetupDraftPath = currentPath || `cypress/common-services/${handle.name}`;
|
|
5641
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(this.loginSetupDraftContent);
|
|
5642
|
+
this.render();
|
|
5643
|
+
} catch (e) {
|
|
5644
|
+
if (e?.name !== "AbortError") showToast(this.t("CONFIG.FOLDER_ERROR_TOAST"), false);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
async loginSetupCreateScaffold() {
|
|
5648
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim() ?? "";
|
|
5649
|
+
try {
|
|
5650
|
+
const handle = await window.showSaveFilePicker({
|
|
5651
|
+
suggestedName: "login.service.ts",
|
|
5652
|
+
types: [{ description: "TypeScript files", accept: { "text/plain": [".ts"] } }]
|
|
5653
|
+
});
|
|
5654
|
+
const content = buildLoginScaffold();
|
|
5655
|
+
const writable = await handle.createWritable();
|
|
5656
|
+
await writable.write(content);
|
|
5657
|
+
await writable.close();
|
|
5658
|
+
this.loginSetupDraftHandle = handle;
|
|
5659
|
+
this.loginSetupDraftContent = content;
|
|
5660
|
+
this.loginSetupDraftHasContent = true;
|
|
5661
|
+
this.loginSetupDraftPath = currentPath || `cypress/common-services/${handle.name}`;
|
|
5662
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(content);
|
|
5663
|
+
this.render();
|
|
5664
|
+
} catch (e) {
|
|
5665
|
+
if (e?.name !== "AbortError") showToast(this.t("CONFIG.FOLDER_ERROR_TOAST"), false);
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
async loginSetupRescan() {
|
|
5669
|
+
const currentPath = this.shadow.getElementById("login-setup-filepath")?.value.trim();
|
|
5670
|
+
if (currentPath !== void 0) this.loginSetupDraftPath = currentPath;
|
|
5671
|
+
try {
|
|
5672
|
+
if (this.loginSetupDraftHandle) {
|
|
5673
|
+
const file = await this.loginSetupDraftHandle.getFile();
|
|
5674
|
+
this.loginSetupDraftContent = await file.text();
|
|
5675
|
+
}
|
|
5676
|
+
this.loginSetupDraftFunctions = extractExportedFunctions(this.loginSetupDraftContent);
|
|
5677
|
+
this.render();
|
|
5678
|
+
} catch {
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
4943
5681
|
async changeFolder() {
|
|
4944
5682
|
try {
|
|
4945
5683
|
await this.persistence.requestDirectoryPermissions();
|
|
@@ -5042,7 +5780,14 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
5042
5780
|
exportTests: this.exportTests,
|
|
5043
5781
|
exportSelectedIds: this.exportSelectedIds,
|
|
5044
5782
|
exportSelectedTags: this.exportSelectedTags,
|
|
5045
|
-
issueTrackerConfig: this.issueTrackerConfig
|
|
5783
|
+
issueTrackerConfig: this.issueTrackerConfig,
|
|
5784
|
+
isLoginSetupOpen: this.isLoginSetupOpen,
|
|
5785
|
+
loginSetupConfig: this.loginSetupConfig,
|
|
5786
|
+
loginSetupDraftPath: this.loginSetupDraftPath,
|
|
5787
|
+
loginSetupDraftFunctions: this.loginSetupDraftFunctions,
|
|
5788
|
+
loginSetupDraftBeforeFn: this.loginSetupDraftBeforeFn,
|
|
5789
|
+
loginSetupDraftBeforeEachFn: this.loginSetupDraftBeforeEachFn,
|
|
5790
|
+
loginSetupDraftHasContent: this.loginSetupDraftHasContent
|
|
5046
5791
|
}, this.t.bind(this))}`;
|
|
5047
5792
|
;
|
|
5048
5793
|
this.shadow.getElementById("lang-select").addEventListener(
|
|
@@ -5101,24 +5846,48 @@ var ConfigurationElement = class extends HTMLElement {
|
|
|
5101
5846
|
this.shadow.querySelectorAll("[data-export-tag]").forEach(
|
|
5102
5847
|
(el) => el.addEventListener("click", () => this.toggleExportTag(el.dataset["exportTag"] ?? ""))
|
|
5103
5848
|
);
|
|
5849
|
+
this.shadow.getElementById("btn-open-login-setup")?.addEventListener("click", () => this.openLoginSetupPanel());
|
|
5850
|
+
this.shadow.getElementById("btn-login-setup-cancel")?.addEventListener("click", () => this.closeLoginSetupPanel());
|
|
5851
|
+
this.shadow.getElementById("btn-login-setup-pick-file")?.addEventListener("click", () => this.loginSetupPickFile());
|
|
5852
|
+
this.shadow.getElementById("btn-login-setup-create-scaffold")?.addEventListener("click", () => this.loginSetupCreateScaffold());
|
|
5853
|
+
this.shadow.getElementById("btn-login-setup-rescan")?.addEventListener("click", () => this.loginSetupRescan());
|
|
5854
|
+
this.shadow.getElementById("btn-login-setup-clear")?.addEventListener("click", () => this.clearLoginSetupConfig());
|
|
5855
|
+
this.shadow.getElementById("btn-login-setup-save")?.addEventListener("click", async () => {
|
|
5856
|
+
const pathEl = this.shadow.getElementById("login-setup-filepath");
|
|
5857
|
+
const beforeEl = this.shadow.getElementById("login-setup-before-fn");
|
|
5858
|
+
const beforeEachEl = this.shadow.getElementById("login-setup-before-each-fn");
|
|
5859
|
+
await this.saveLoginSetupConfig({
|
|
5860
|
+
enabled: true,
|
|
5861
|
+
filePath: pathEl?.value.trim() ?? this.loginSetupDraftPath,
|
|
5862
|
+
fileContent: this.loginSetupDraftContent,
|
|
5863
|
+
detectedFunctions: this.loginSetupDraftFunctions,
|
|
5864
|
+
beforeFn: beforeEl?.value || null,
|
|
5865
|
+
beforeEachFn: beforeEachEl?.value || null
|
|
5866
|
+
});
|
|
5867
|
+
});
|
|
5868
|
+
;
|
|
5104
5869
|
this.shadow.getElementById("file-input").addEventListener("change", async (e) => {
|
|
5105
5870
|
const file = e.target.files?.[0];
|
|
5106
5871
|
if (!file) return;
|
|
5107
5872
|
try {
|
|
5108
5873
|
await this.importAllData(file);
|
|
5109
|
-
|
|
5874
|
+
showToast(this.t("CONFIG.IMPORT_SUCCESS"));
|
|
5110
5875
|
} catch (err) {
|
|
5111
|
-
|
|
5876
|
+
showToast(err.message ?? this.t("CONFIG.IMPORT_ERROR"), false);
|
|
5112
5877
|
}
|
|
5113
5878
|
const target = e.target;
|
|
5114
5879
|
if (target) target.value = "";
|
|
5115
5880
|
});
|
|
5116
5881
|
}
|
|
5117
5882
|
};
|
|
5118
|
-
if (!customElements.get("e2e-configuration")) {
|
|
5119
|
-
customElements.define("e2e-configuration", ConfigurationElement);
|
|
5883
|
+
if (!customElements.get("lib-e2e-configuration")) {
|
|
5884
|
+
customElements.define("lib-e2e-configuration", ConfigurationElement);
|
|
5120
5885
|
}
|
|
5121
5886
|
|
|
5887
|
+
// src/components/advanced-test-editor/advanced-test-editor.ts
|
|
5888
|
+
var import_sweetalert2 = __toESM(require("sweetalert2"), 1);
|
|
5889
|
+
init_code_format_utils();
|
|
5890
|
+
|
|
5122
5891
|
// src/components/advanced-test-editor/advanced-test-editor.styles.ts
|
|
5123
5892
|
var ADVANCED_TEST_EDITOR_STYLES = `
|
|
5124
5893
|
:host { display: flex; flex-direction: column; flex: 1; min-height: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; overflow: hidden; }
|
|
@@ -5377,6 +6146,7 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
5377
6146
|
isCreatingFolder = false;
|
|
5378
6147
|
collapsedDirs = /* @__PURE__ */ new Set();
|
|
5379
6148
|
sidebarWidth = 220;
|
|
6149
|
+
loginSetupConfig = null;
|
|
5380
6150
|
hasPermission = false;
|
|
5381
6151
|
needsReauth = false;
|
|
5382
6152
|
_dirHandle = null;
|
|
@@ -5437,20 +6207,51 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
5437
6207
|
const name = rawName.trim().replace(/\.cy\.ts$/, "");
|
|
5438
6208
|
if (!name) return;
|
|
5439
6209
|
const fileName = `${name}.cy.ts`;
|
|
5440
|
-
const
|
|
6210
|
+
const cfg = this.loginSetupConfig;
|
|
6211
|
+
if (cfg?.enabled && (cfg.beforeFn || cfg.beforeEachFn)) {
|
|
6212
|
+
const e2eName = this._e2eHandle.name ?? "e2e";
|
|
6213
|
+
const fromPath = `cypress/${e2eName}/${fileName}`;
|
|
6214
|
+
const importPath = buildLoginImportPath(fromPath, cfg.filePath);
|
|
6215
|
+
const { importLine, beforeBlock, beforeEachBlock } = buildLoginBlocks(
|
|
6216
|
+
importPath,
|
|
6217
|
+
cfg.beforeFn,
|
|
6218
|
+
cfg.beforeEachFn
|
|
6219
|
+
);
|
|
6220
|
+
const header = importLine ? `${importLine}
|
|
5441
6221
|
|
|
6222
|
+
` : "";
|
|
6223
|
+
const inner = [beforeBlock, beforeEachBlock].filter(Boolean).join("");
|
|
6224
|
+
const template = `${header}describe('${escapeSingleQuotes(name)}', () => {
|
|
6225
|
+
${inner}
|
|
5442
6226
|
it('should ', () => {
|
|
5443
6227
|
|
|
5444
6228
|
});
|
|
5445
6229
|
|
|
5446
6230
|
});
|
|
5447
6231
|
`;
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
6232
|
+
try {
|
|
6233
|
+
const fileHandle = await this._e2eHandle.getFileHandle(fileName, { create: true });
|
|
6234
|
+
const writable = await fileHandle.createWritable();
|
|
6235
|
+
await writable.write(template);
|
|
6236
|
+
await writable.close();
|
|
6237
|
+
} catch {
|
|
6238
|
+
}
|
|
6239
|
+
} else {
|
|
6240
|
+
const template = `describe('${escapeSingleQuotes(name)}', () => {
|
|
6241
|
+
|
|
6242
|
+
it('should ', () => {
|
|
6243
|
+
|
|
6244
|
+
});
|
|
6245
|
+
|
|
6246
|
+
});
|
|
6247
|
+
`;
|
|
6248
|
+
try {
|
|
6249
|
+
const fileHandle = await this._e2eHandle.getFileHandle(fileName, { create: true });
|
|
6250
|
+
const writable = await fileHandle.createWritable();
|
|
6251
|
+
await writable.write(template);
|
|
6252
|
+
await writable.close();
|
|
6253
|
+
} catch {
|
|
6254
|
+
}
|
|
5454
6255
|
}
|
|
5455
6256
|
this.isCreatingFile = false;
|
|
5456
6257
|
await this.getFoldersData();
|
|
@@ -5478,6 +6279,30 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
5478
6279
|
const comment = this.testNotes ? this.transformationService.buildBlockComment(this.testNotes) + "\n" : "";
|
|
5479
6280
|
content = this.transformationService.insertItBlock(content, comment + this.testItBlock);
|
|
5480
6281
|
if (!content) return;
|
|
6282
|
+
const cfg = this.loginSetupConfig;
|
|
6283
|
+
if (cfg?.enabled && (cfg.beforeFn || cfg.beforeEachFn)) {
|
|
6284
|
+
const fns = [cfg.beforeFn, cfg.beforeEachFn].filter(Boolean);
|
|
6285
|
+
if (!hasLoginBlocks(content, fns)) {
|
|
6286
|
+
const e2eName = this._e2eHandle?.name ?? "e2e";
|
|
6287
|
+
const fileName = this.selectedFile?.name ?? "";
|
|
6288
|
+
const fromPath = `cypress/${e2eName}/${fileName}`;
|
|
6289
|
+
const importPath = buildLoginImportPath(fromPath, cfg.filePath);
|
|
6290
|
+
const { importLine, beforeBlock, beforeEachBlock } = buildLoginBlocks(importPath, cfg.beforeFn, cfg.beforeEachFn);
|
|
6291
|
+
const result = await import_sweetalert2.default.fire({
|
|
6292
|
+
title: this.t("CONFIG.LOGIN_SETUP_INJECT_TITLE"),
|
|
6293
|
+
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>`,
|
|
6294
|
+
icon: "question",
|
|
6295
|
+
showCancelButton: true,
|
|
6296
|
+
confirmButtonText: this.t("CONFIG.LOGIN_SETUP_INJECT_CONFIRM"),
|
|
6297
|
+
cancelButtonText: this.t("CONFIG.LOGIN_SETUP_CANCEL_BTN"),
|
|
6298
|
+
confirmButtonColor: "#1f6feb",
|
|
6299
|
+
width: 460
|
|
6300
|
+
});
|
|
6301
|
+
if (result.isConfirmed) {
|
|
6302
|
+
content = injectLoginBlocksIntoExisting(content, importLine, beforeBlock, beforeEachBlock);
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
}
|
|
5481
6306
|
const writable = await this.selectedFileHandle.createWritable();
|
|
5482
6307
|
await writable.write(content);
|
|
5483
6308
|
await writable.close();
|
|
@@ -5672,8 +6497,8 @@ var AdvancedTestEditorElement = class extends HTMLElement {
|
|
|
5672
6497
|
});
|
|
5673
6498
|
}
|
|
5674
6499
|
};
|
|
5675
|
-
if (!customElements.get("advanced-test-editor")) {
|
|
5676
|
-
customElements.define("advanced-test-editor", AdvancedTestEditorElement);
|
|
6500
|
+
if (!customElements.get("lib-e2e-advanced-test-editor")) {
|
|
6501
|
+
customElements.define("lib-e2e-advanced-test-editor", AdvancedTestEditorElement);
|
|
5677
6502
|
}
|
|
5678
6503
|
|
|
5679
6504
|
// src/components/file-preview/file-preview.styles.ts
|
|
@@ -6039,8 +6864,8 @@ var FilePreviewElement = class extends HTMLElement {
|
|
|
6039
6864
|
});
|
|
6040
6865
|
}
|
|
6041
6866
|
};
|
|
6042
|
-
if (!customElements.get("file-preview")) {
|
|
6043
|
-
customElements.define("file-preview", FilePreviewElement);
|
|
6867
|
+
if (!customElements.get("lib-e2e-file-preview")) {
|
|
6868
|
+
customElements.define("lib-e2e-file-preview", FilePreviewElement);
|
|
6044
6869
|
}
|
|
6045
6870
|
|
|
6046
6871
|
// src/components/help-panel/help-panel.styles.ts
|
|
@@ -6136,12 +6961,12 @@ var REFERENCE_SECTIONS = [
|
|
|
6136
6961
|
{ title: "HELP.SEC_PANELS", items: ["HELP.PANEL_TESTS", "HELP.PANEL_COMMANDS", "HELP.PANEL_CONFIG", "HELP.PANEL_FILES"] },
|
|
6137
6962
|
{ title: "HELP.SEC_SELECTOR", items: ["HELP.SELECTOR_1", "HELP.SELECTOR_2"] },
|
|
6138
6963
|
{ title: "HELP.SEC_DATA", items: ["HELP.DATA_1", "HELP.DATA_2"] },
|
|
6139
|
-
{ title: "HELP.SEC_ADVANCED", items: ["HELP.ADV_INVISIBLE", "HELP.ADV_RUNNER", "HELP.ADV_CROSSAPP", "HELP.ADV_DRAG"] }
|
|
6964
|
+
{ title: "HELP.SEC_ADVANCED", items: ["HELP.ADV_INVISIBLE", "HELP.ADV_RUNNER", "HELP.ADV_CROSSAPP", "HELP.ADV_DRAG", "HELP.ADV_LOGIN"] }
|
|
6140
6965
|
];
|
|
6141
6966
|
var GUIDE_SECTIONS = [
|
|
6142
6967
|
{ 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"] },
|
|
6143
|
-
{ 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"] },
|
|
6144
|
-
{ 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"
|
|
6968
|
+
{ 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"] },
|
|
6969
|
+
{ 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"] }
|
|
6145
6970
|
];
|
|
6146
6971
|
function sectionsHtml(sections, t) {
|
|
6147
6972
|
return sections.map(
|
|
@@ -6188,12 +7013,12 @@ var HelpPanelElement = class extends HTMLElement {
|
|
|
6188
7013
|
});
|
|
6189
7014
|
}
|
|
6190
7015
|
};
|
|
6191
|
-
if (!customElements.get("help-panel")) {
|
|
6192
|
-
customElements.define("help-panel", HelpPanelElement);
|
|
7016
|
+
if (!customElements.get("lib-e2e-help-panel")) {
|
|
7017
|
+
customElements.define("lib-e2e-help-panel", HelpPanelElement);
|
|
6193
7018
|
}
|
|
6194
7019
|
|
|
6195
7020
|
// src/components/lib-e2e-recorder/lib-e2e-recorder.ts
|
|
6196
|
-
var
|
|
7021
|
+
var import_sweetalert22 = __toESM(require("sweetalert2"), 1);
|
|
6197
7022
|
|
|
6198
7023
|
// src/components/lib-e2e-recorder/lib-e2e-recorder.styles.ts
|
|
6199
7024
|
var DIRECTIONS = ["up-left", "up-right", "down-left", "down-right"];
|
|
@@ -6394,6 +7219,109 @@ function renderRecorderWidget(rec, paused, t) {
|
|
|
6394
7219
|
<div class="rec-badge">${paused ? t("RECORDER.BADGE_PAUSED") : t("RECORDER.BADGE_REC")}</div>`;
|
|
6395
7220
|
}
|
|
6396
7221
|
|
|
7222
|
+
// src/components/lib-e2e-recorder/assertion-builder.ts
|
|
7223
|
+
init_code_format_utils();
|
|
7224
|
+
var NO_VALUE_ASSERTIONS = /* @__PURE__ */ new Set(["be.visible", "not.exist", "be.disabled", "be.checked"]);
|
|
7225
|
+
function injectAssertionBuilder(container, t, onAddCommand) {
|
|
7226
|
+
container.insertAdjacentHTML("beforeend", `
|
|
7227
|
+
<div id="assertion-builder"
|
|
7228
|
+
style="padding:10px 12px;border-top:1px solid #21262d;background:#0d1117">
|
|
7229
|
+
<div style="font-size:10px;color:#484f58;text-transform:uppercase;letter-spacing:.8px;font-weight:600;margin-bottom:8px">
|
|
7230
|
+
${t("RECORDER.ASSERTION_SECTION")}
|
|
7231
|
+
</div>
|
|
7232
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:flex-end">
|
|
7233
|
+
<input id="assert-selector" type="text"
|
|
7234
|
+
placeholder="${t("RECORDER.ASSERT_SELECTOR_PH").replace(/"/g, """)}"
|
|
7235
|
+
style="flex:2;min-width:140px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
7236
|
+
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none;
|
|
7237
|
+
font-family:'Cascadia Code','Fira Code',monospace"/>
|
|
7238
|
+
<select id="assert-type"
|
|
7239
|
+
style="padding:5px 8px;background:#161b22;border:1px solid #30363d;border-radius:5px;
|
|
7240
|
+
color:#c9d1d9;font-size:11px;outline:none;cursor:pointer;flex-shrink:0">
|
|
7241
|
+
<option value="be.visible">be.visible</option>
|
|
7242
|
+
<option value="not.exist">not.exist</option>
|
|
7243
|
+
<option value="be.disabled">be.disabled</option>
|
|
7244
|
+
<option value="be.checked">be.checked</option>
|
|
7245
|
+
<option value="contain.text">contain.text</option>
|
|
7246
|
+
<option value="have.value">have.value</option>
|
|
7247
|
+
<option value="have.length">have.length</option>
|
|
7248
|
+
<option value="have.class">have.class</option>
|
|
7249
|
+
<option value="have.attr">have.attr</option>
|
|
7250
|
+
</select>
|
|
7251
|
+
<input id="assert-value" type="text" placeholder="${t("RECORDER.ASSERT_VALUE_PH")}"
|
|
7252
|
+
style="flex:2;min-width:110px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
7253
|
+
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none"/>
|
|
7254
|
+
<button id="btn-add-assertion"
|
|
7255
|
+
style="padding:5px 12px;border:none;border-radius:5px;cursor:pointer;
|
|
7256
|
+
font-size:11px;font-weight:500;background:#2f81f7;color:#fff;
|
|
7257
|
+
white-space:nowrap;flex-shrink:0">
|
|
7258
|
+
${t("RECORDER.ASSERT_ADD_BTN")}
|
|
7259
|
+
</button>
|
|
7260
|
+
</div>
|
|
7261
|
+
</div>`);
|
|
7262
|
+
document.getElementById("btn-add-assertion")?.addEventListener("click", () => {
|
|
7263
|
+
const sel = document.getElementById("assert-selector").value.trim();
|
|
7264
|
+
const type = document.getElementById("assert-type").value;
|
|
7265
|
+
const val = document.getElementById("assert-value").value.trim();
|
|
7266
|
+
if (!sel) return;
|
|
7267
|
+
const s = escapeSingleQuotes(sel);
|
|
7268
|
+
const v = escapeSingleQuotes(val);
|
|
7269
|
+
const cmd = NO_VALUE_ASSERTIONS.has(type) || !val ? `cy.get('${s}').should('${type}')` : `cy.get('${s}').should('${type}', '${v}')`;
|
|
7270
|
+
onAddCommand(cmd);
|
|
7271
|
+
document.getElementById("assert-selector").value = "";
|
|
7272
|
+
document.getElementById("assert-value").value = "";
|
|
7273
|
+
});
|
|
7274
|
+
}
|
|
7275
|
+
|
|
7276
|
+
// src/components/lib-e2e-recorder/filesystem-setup.ts
|
|
7277
|
+
function mountFilesystemSetupContent(container, t, onSkip, onSelect) {
|
|
7278
|
+
container.innerHTML = `
|
|
7279
|
+
<div style="padding:16px 20px 20px;color:#8b949e;font-size:13px;line-height:1.7">
|
|
7280
|
+
<p>${t("RECORDER.FS_INTRO_HTML")}</p>
|
|
7281
|
+
<p style="margin-top:10px;margin-bottom:6px;font-size:11px;color:#8b949e">
|
|
7282
|
+
${t("RECORDER.FS_STRUCTURE_HINT_HTML")}
|
|
7283
|
+
</p>
|
|
7284
|
+
<pre style="margin:0;padding:10px 14px;background:#0d1117;border:1px solid #21262d;
|
|
7285
|
+
border-radius:8px;font-size:11px;color:#c9d1d9;line-height:1.8;
|
|
7286
|
+
font-family:'Cascadia Code','Fira Code','Consolas',monospace">
|
|
7287
|
+
cypress/ <span style="color:#484f58">${t("RECORDER.FS_TREE_PICK_HINT")}</span>
|
|
7288
|
+
\u2514\u2500\u2500 e2e/ <span style="color:#484f58">${t("RECORDER.FS_TREE_READ_HINT")}</span>
|
|
7289
|
+
\u2514\u2500\u2500 *.cy.ts</pre>
|
|
7290
|
+
<p style="margin-top:8px;font-size:11px;color:#484f58">
|
|
7291
|
+
${t("RECORDER.FS_PERMISSION_NOTE")}
|
|
7292
|
+
</p>
|
|
7293
|
+
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:20px">
|
|
7294
|
+
<button id="fs-skip"
|
|
7295
|
+
style="padding:7px 16px;border:1px solid #30363d;border-radius:6px;cursor:pointer;
|
|
7296
|
+
font-size:12px;font-weight:500;background:transparent;color:#8b949e">
|
|
7297
|
+
${t("RECORDER.FS_LATER_BTN")}
|
|
7298
|
+
</button>
|
|
7299
|
+
<button id="fs-select"
|
|
7300
|
+
style="padding:7px 16px;border:none;border-radius:6px;cursor:pointer;
|
|
7301
|
+
font-size:12px;font-weight:500;background:#2f81f7;color:#fff">
|
|
7302
|
+
${t("RECORDER.FS_SELECT_BTN")}
|
|
7303
|
+
</button>
|
|
7304
|
+
</div>
|
|
7305
|
+
</div>`;
|
|
7306
|
+
document.getElementById("fs-skip")?.addEventListener("click", () => {
|
|
7307
|
+
void onSkip();
|
|
7308
|
+
});
|
|
7309
|
+
document.getElementById("fs-select")?.addEventListener("click", () => {
|
|
7310
|
+
void onSelect();
|
|
7311
|
+
});
|
|
7312
|
+
}
|
|
7313
|
+
|
|
7314
|
+
// src/utils/swal-mount.utils.ts
|
|
7315
|
+
function mountComponentInSwal(tagName, container, props, events) {
|
|
7316
|
+
const el = document.createElement(tagName);
|
|
7317
|
+
Object.assign(el, props);
|
|
7318
|
+
container.appendChild(el);
|
|
7319
|
+
for (const { name, handler } of events ?? []) {
|
|
7320
|
+
el.addEventListener(name, handler);
|
|
7321
|
+
}
|
|
7322
|
+
return el;
|
|
7323
|
+
}
|
|
7324
|
+
|
|
6397
7325
|
// src/utils/fixture-convert.utils.ts
|
|
6398
7326
|
function toFixtureInterceptors(interceptors, fixtures) {
|
|
6399
7327
|
const fixtureNames = new Set(fixtures.map((f) => f.name));
|
|
@@ -6513,6 +7441,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6513
7441
|
this.recording.destroy();
|
|
6514
7442
|
this._needsRecordingRebuild = true;
|
|
6515
7443
|
}
|
|
7444
|
+
// HttpMonitor reads extendedHttpCommands and fixtureMode synchronously inside
|
|
7445
|
+
// fetch/XHR interception handlers, so they must live in localStorage (sync).
|
|
7446
|
+
// IndexedDB is the persistent source of truth; this method mirrors it into
|
|
7447
|
+
// localStorage on every mount so the sync cache is always fresh.
|
|
6516
7448
|
async initHttpConfig() {
|
|
6517
7449
|
const config = await this.persistence.getConfig("extendedHttpCommands");
|
|
6518
7450
|
if (config === null) {
|
|
@@ -6560,9 +7492,9 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6560
7492
|
}
|
|
6561
7493
|
showSelectorPicker(target) {
|
|
6562
7494
|
Promise.resolve().then(() => (init_selector_picker(), selector_picker_exports)).then(() => {
|
|
6563
|
-
const existing = document.querySelector("selector-picker");
|
|
7495
|
+
const existing = document.querySelector("lib-e2e-selector-picker");
|
|
6564
7496
|
if (existing) existing.remove();
|
|
6565
|
-
const picker = document.createElement("selector-picker");
|
|
7497
|
+
const picker = document.createElement("lib-e2e-selector-picker");
|
|
6566
7498
|
picker.targetElement = target;
|
|
6567
7499
|
picker.recording = this.recording;
|
|
6568
7500
|
picker.translation = this.translation;
|
|
@@ -6624,7 +7556,7 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6624
7556
|
}
|
|
6625
7557
|
async promptResumeOrDiscard(session) {
|
|
6626
7558
|
const t = this.translation.translate.bind(this.translation);
|
|
6627
|
-
const result = await
|
|
7559
|
+
const result = await import_sweetalert22.default.fire({
|
|
6628
7560
|
title: t("RECORDER.SESSION_RESUME_TITLE"),
|
|
6629
7561
|
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>`,
|
|
6630
7562
|
showCancelButton: true,
|
|
@@ -6843,8 +7775,9 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6843
7775
|
this.showFilesystemSetupDialog();
|
|
6844
7776
|
}
|
|
6845
7777
|
showFilesystemSetupDialog() {
|
|
6846
|
-
|
|
6847
|
-
|
|
7778
|
+
const t = this.translation.translate.bind(this.translation);
|
|
7779
|
+
import_sweetalert22.default.fire({
|
|
7780
|
+
title: t("RECORDER.FS_TITLE"),
|
|
6848
7781
|
html: '<div id="fs-setup-content"></div>',
|
|
6849
7782
|
showCloseButton: true,
|
|
6850
7783
|
showConfirmButton: false,
|
|
@@ -6853,277 +7786,171 @@ var LibE2eRecorderElement = class extends HTMLElement {
|
|
|
6853
7786
|
setSwal2DataCyAttribute();
|
|
6854
7787
|
const container = document.getElementById("fs-setup-content");
|
|
6855
7788
|
if (!container) return;
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
<button id="fs-skip"
|
|
6873
|
-
style="padding:7px 16px;border:1px solid #30363d;border-radius:6px;cursor:pointer;
|
|
6874
|
-
font-size:12px;font-weight:500;background:transparent;color:#8b949e">
|
|
6875
|
-
${this.translation.translate("RECORDER.FS_LATER_BTN")}
|
|
6876
|
-
</button>
|
|
6877
|
-
<button id="fs-select"
|
|
6878
|
-
style="padding:7px 16px;border:none;border-radius:6px;cursor:pointer;
|
|
6879
|
-
font-size:12px;font-weight:500;background:#2f81f7;color:#fff">
|
|
6880
|
-
${this.translation.translate("RECORDER.FS_SELECT_BTN")}
|
|
6881
|
-
</button>
|
|
6882
|
-
</div>
|
|
6883
|
-
</div>`;
|
|
6884
|
-
document.getElementById("fs-skip")?.addEventListener("click", async () => {
|
|
6885
|
-
await this.persistence.setConfigKey("allowReadWriteFiles", "false");
|
|
6886
|
-
import_sweetalert2.default.close();
|
|
6887
|
-
});
|
|
6888
|
-
document.getElementById("fs-select")?.addEventListener("click", async () => {
|
|
6889
|
-
try {
|
|
6890
|
-
await this.persistence.requestDirectoryPermissions();
|
|
6891
|
-
import_sweetalert2.default.close();
|
|
6892
|
-
showToast(this.translation.translate("RECORDER.FS_SUCCESS_TOAST"));
|
|
6893
|
-
} catch (e) {
|
|
6894
|
-
if (e?.name !== "AbortError") {
|
|
6895
|
-
showToast(this.translation.translate("RECORDER.FS_ERROR_TOAST"), false);
|
|
7789
|
+
mountFilesystemSetupContent(
|
|
7790
|
+
container,
|
|
7791
|
+
t,
|
|
7792
|
+
async () => {
|
|
7793
|
+
await this.persistence.setConfigKey("allowReadWriteFiles", "false");
|
|
7794
|
+
import_sweetalert22.default.close();
|
|
7795
|
+
},
|
|
7796
|
+
async () => {
|
|
7797
|
+
try {
|
|
7798
|
+
await this.persistence.requestDirectoryPermissions();
|
|
7799
|
+
import_sweetalert22.default.close();
|
|
7800
|
+
showToast(t("RECORDER.FS_SUCCESS_TOAST"));
|
|
7801
|
+
} catch (e) {
|
|
7802
|
+
if (e?.name !== "AbortError") {
|
|
7803
|
+
showToast(t("RECORDER.FS_ERROR_TOAST"), false);
|
|
7804
|
+
}
|
|
6896
7805
|
}
|
|
6897
7806
|
}
|
|
6898
|
-
|
|
7807
|
+
);
|
|
6899
7808
|
}
|
|
6900
7809
|
});
|
|
6901
7810
|
}
|
|
6902
7811
|
// ── dialogs ──────────────────────────────────────────────────────────────
|
|
6903
7812
|
showCommandsDialog() {
|
|
6904
|
-
this.
|
|
6905
|
-
|
|
7813
|
+
this.openSwalDialog(
|
|
7814
|
+
"isCommandsDialogOpen",
|
|
7815
|
+
"commands-modal-content",
|
|
7816
|
+
{
|
|
6906
7817
|
title: this.translation.translate("MAIN_FRAME.DIALOG_COMMANDS"),
|
|
6907
7818
|
html: '<div id="commands-modal-content" style="min-height:250px;padding:0"></div>',
|
|
6908
7819
|
showCloseButton: true,
|
|
6909
|
-
showConfirmButton: false,
|
|
6910
7820
|
allowOutsideClick: false,
|
|
6911
7821
|
backdrop: false,
|
|
6912
|
-
width: 640
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
7822
|
+
width: 640
|
|
7823
|
+
},
|
|
7824
|
+
(container) => {
|
|
7825
|
+
const child = mountComponentInSwal("lib-e2e-test-previsualizer", container, {
|
|
7826
|
+
translation: this.translation,
|
|
7827
|
+
commands: this.cypressCommands,
|
|
7828
|
+
interceptors: this.interceptors,
|
|
7829
|
+
editable: true
|
|
7830
|
+
}, [
|
|
7831
|
+
{ name: "deletecommand", handler: (e) => this.recording.removeCommand(e.detail) },
|
|
7832
|
+
{ name: "movecommand", handler: (e) => this.recording.moveCommand(e.detail.from, e.detail.to) },
|
|
7833
|
+
{ name: "deleteinterceptor", handler: (e) => this.recording.removeInterceptor(e.detail) }
|
|
7834
|
+
]);
|
|
7835
|
+
this._previsualizerRef = child;
|
|
7836
|
+
injectAssertionBuilder(container, this.translation.translate.bind(this.translation), (cmd) => this.recording.appendCommand(cmd));
|
|
7837
|
+
},
|
|
7838
|
+
{
|
|
7839
|
+
// Make the container click-through so the page underneath remains interactive
|
|
7840
|
+
onDidOpenPreamble: () => {
|
|
6917
7841
|
const swalContainer = document.querySelector(".swal2-container");
|
|
6918
7842
|
if (swalContainer) {
|
|
6919
7843
|
swalContainer.style.pointerEvents = "none";
|
|
6920
7844
|
const popup = swalContainer.querySelector(".swal2-popup");
|
|
6921
7845
|
if (popup) popup.style.pointerEvents = "all";
|
|
6922
7846
|
}
|
|
6923
|
-
const container = document.getElementById("commands-modal-content");
|
|
6924
|
-
if (!container) return;
|
|
6925
|
-
const child = document.createElement("test-previsualizer");
|
|
6926
|
-
child.translation = this.translation;
|
|
6927
|
-
child.commands = this.cypressCommands;
|
|
6928
|
-
child.interceptors = this.interceptors;
|
|
6929
|
-
child.editable = true;
|
|
6930
|
-
container.appendChild(child);
|
|
6931
|
-
this._previsualizerRef = child;
|
|
6932
|
-
child.addEventListener("deletecommand", (e) => {
|
|
6933
|
-
this.recording.removeCommand(e.detail);
|
|
6934
|
-
});
|
|
6935
|
-
child.addEventListener("movecommand", (e) => {
|
|
6936
|
-
this.recording.moveCommand(e.detail.from, e.detail.to);
|
|
6937
|
-
});
|
|
6938
|
-
child.addEventListener("deleteinterceptor", (e) => {
|
|
6939
|
-
this.recording.removeInterceptor(e.detail);
|
|
6940
|
-
});
|
|
6941
|
-
const assertionHtml = `
|
|
6942
|
-
<div id="assertion-builder"
|
|
6943
|
-
style="padding:10px 12px;border-top:1px solid #21262d;background:#0d1117">
|
|
6944
|
-
<div style="font-size:10px;color:#484f58;text-transform:uppercase;letter-spacing:.8px;font-weight:600;margin-bottom:8px">
|
|
6945
|
-
${this.translation.translate("RECORDER.ASSERTION_SECTION")}
|
|
6946
|
-
</div>
|
|
6947
|
-
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:flex-end">
|
|
6948
|
-
<input id="assert-selector" type="text"
|
|
6949
|
-
placeholder="${this.translation.translate("RECORDER.ASSERT_SELECTOR_PH").replace(/"/g, """)}"
|
|
6950
|
-
style="flex:2;min-width:140px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6951
|
-
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none;
|
|
6952
|
-
font-family:'Cascadia Code','Fira Code',monospace"/>
|
|
6953
|
-
<select id="assert-type"
|
|
6954
|
-
style="padding:5px 8px;background:#161b22;border:1px solid #30363d;border-radius:5px;
|
|
6955
|
-
color:#c9d1d9;font-size:11px;outline:none;cursor:pointer;flex-shrink:0">
|
|
6956
|
-
<option value="be.visible">be.visible</option>
|
|
6957
|
-
<option value="not.exist">not.exist</option>
|
|
6958
|
-
<option value="be.disabled">be.disabled</option>
|
|
6959
|
-
<option value="be.checked">be.checked</option>
|
|
6960
|
-
<option value="contain.text">contain.text</option>
|
|
6961
|
-
<option value="have.value">have.value</option>
|
|
6962
|
-
<option value="have.length">have.length</option>
|
|
6963
|
-
<option value="have.class">have.class</option>
|
|
6964
|
-
<option value="have.attr">have.attr</option>
|
|
6965
|
-
</select>
|
|
6966
|
-
<input id="assert-value" type="text" placeholder="${this.translation.translate("RECORDER.ASSERT_VALUE_PH")}"
|
|
6967
|
-
style="flex:2;min-width:110px;padding:5px 8px;background:#161b22;border:1px solid #30363d;
|
|
6968
|
-
border-radius:5px;color:#c9d1d9;font-size:11px;outline:none"/>
|
|
6969
|
-
<button id="btn-add-assertion"
|
|
6970
|
-
style="padding:5px 12px;border:none;border-radius:5px;cursor:pointer;
|
|
6971
|
-
font-size:11px;font-weight:500;background:#2f81f7;color:#fff;
|
|
6972
|
-
white-space:nowrap;flex-shrink:0">
|
|
6973
|
-
${this.translation.translate("RECORDER.ASSERT_ADD_BTN")}
|
|
6974
|
-
</button>
|
|
6975
|
-
</div>
|
|
6976
|
-
</div>`;
|
|
6977
|
-
container.insertAdjacentHTML("beforeend", assertionHtml);
|
|
6978
|
-
const NO_VALUE_ASSERTIONS = /* @__PURE__ */ new Set(["be.visible", "not.exist", "be.disabled", "be.checked"]);
|
|
6979
|
-
document.getElementById("btn-add-assertion")?.addEventListener("click", () => {
|
|
6980
|
-
const sel = document.getElementById("assert-selector").value.trim();
|
|
6981
|
-
const type = document.getElementById("assert-type").value;
|
|
6982
|
-
const val = document.getElementById("assert-value").value.trim();
|
|
6983
|
-
if (!sel) return;
|
|
6984
|
-
const s = escapeSingleQuotes(sel);
|
|
6985
|
-
const v = escapeSingleQuotes(val);
|
|
6986
|
-
const cmd = NO_VALUE_ASSERTIONS.has(type) || !val ? `cy.get('${s}').should('${type}')` : `cy.get('${s}').should('${type}', '${v}')`;
|
|
6987
|
-
this.recording.appendCommand(cmd);
|
|
6988
|
-
document.getElementById("assert-selector").value = "";
|
|
6989
|
-
document.getElementById("assert-value").value = "";
|
|
6990
|
-
});
|
|
6991
7847
|
},
|
|
6992
|
-
|
|
6993
|
-
this.isCommandsDialogOpen = false;
|
|
7848
|
+
onWillClose: () => {
|
|
6994
7849
|
this._previsualizerRef = null;
|
|
6995
7850
|
}
|
|
6996
|
-
}
|
|
6997
|
-
|
|
6998
|
-
});
|
|
7851
|
+
}
|
|
7852
|
+
);
|
|
6999
7853
|
}
|
|
7000
7854
|
showSavedTestsDialog() {
|
|
7001
|
-
this.
|
|
7002
|
-
|
|
7855
|
+
this.openSwalDialog(
|
|
7856
|
+
"isSavedTestsDialogOpen",
|
|
7857
|
+
"saved-tests-modal-content",
|
|
7858
|
+
{
|
|
7003
7859
|
title: this.translation.translate("MAIN_FRAME.DIALOG_SAVED_TESTS"),
|
|
7004
7860
|
html: '<div id="saved-tests-modal-content" style="min-height:250px;padding:0"></div>',
|
|
7005
7861
|
showCloseButton: true,
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
child.translation = this.translation;
|
|
7017
|
-
child.issueTrackerConfig = this.issueTrackerConfig;
|
|
7018
|
-
container.appendChild(child);
|
|
7019
|
-
},
|
|
7020
|
-
willClose: () => {
|
|
7021
|
-
this.isSavedTestsDialogOpen = false;
|
|
7022
|
-
}
|
|
7023
|
-
});
|
|
7024
|
-
this.resizePopup();
|
|
7025
|
-
});
|
|
7862
|
+
width: 680
|
|
7863
|
+
},
|
|
7864
|
+
(container) => {
|
|
7865
|
+
mountComponentInSwal("lib-e2e-test-editor", container, {
|
|
7866
|
+
persistence: this.persistence,
|
|
7867
|
+
translation: this.translation,
|
|
7868
|
+
issueTrackerConfig: this.issueTrackerConfig
|
|
7869
|
+
});
|
|
7870
|
+
}
|
|
7871
|
+
);
|
|
7026
7872
|
}
|
|
7027
7873
|
showSaveTestDialog() {
|
|
7028
|
-
this.
|
|
7029
|
-
|
|
7874
|
+
this.openSwalDialog(
|
|
7875
|
+
"isSaveTestDialogOpen",
|
|
7876
|
+
"save-test-modal-content",
|
|
7877
|
+
{
|
|
7030
7878
|
title: this.translation.translate("MAIN_FRAME.DIALOG_SAVE"),
|
|
7031
7879
|
html: '<div id="save-test-modal-content" style="padding:0"></div>',
|
|
7032
|
-
showCloseButton:
|
|
7033
|
-
showConfirmButton: false,
|
|
7880
|
+
showCloseButton: false,
|
|
7034
7881
|
allowOutsideClick: false,
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
child.issueTrackerConfig = this.issueTrackerConfig;
|
|
7044
|
-
container.appendChild(child);
|
|
7045
|
-
child.addEventListener("savetest", (e) => {
|
|
7882
|
+
allowEscapeKey: false
|
|
7883
|
+
},
|
|
7884
|
+
(container) => {
|
|
7885
|
+
mountComponentInSwal("lib-e2e-save-test", container, {
|
|
7886
|
+
translation: this.translation,
|
|
7887
|
+
issueTrackerConfig: this.issueTrackerConfig
|
|
7888
|
+
}, [
|
|
7889
|
+
{ name: "savetest", handler: (e) => {
|
|
7046
7890
|
const { description, notes, tags, ticketId } = e.detail ?? {};
|
|
7047
7891
|
this.onSaveTest(description ?? null, tags ?? [], notes ?? "", ticketId ?? "");
|
|
7048
|
-
|
|
7049
|
-
}
|
|
7050
|
-
|
|
7892
|
+
import_sweetalert22.default.close();
|
|
7893
|
+
} },
|
|
7894
|
+
{ name: "saveandexport", handler: (e) => {
|
|
7051
7895
|
const { description, notes, tags, ticketId } = e.detail ?? {};
|
|
7052
7896
|
this.onSaveAndExportTest(description ?? null, tags ?? [], notes ?? "", ticketId ?? "");
|
|
7053
|
-
|
|
7054
|
-
}
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
});
|
|
7060
|
-
});
|
|
7897
|
+
import_sweetalert22.default.close();
|
|
7898
|
+
} }
|
|
7899
|
+
]);
|
|
7900
|
+
},
|
|
7901
|
+
{ skipResize: true }
|
|
7902
|
+
);
|
|
7061
7903
|
}
|
|
7062
7904
|
showSettingsDialog() {
|
|
7063
|
-
this.
|
|
7064
|
-
|
|
7905
|
+
this.openSwalDialog(
|
|
7906
|
+
"isSettingsDialogOpen",
|
|
7907
|
+
"settings-modal-content",
|
|
7908
|
+
{
|
|
7065
7909
|
title: this.translation.translate("MAIN_FRAME.SETTINGS"),
|
|
7066
7910
|
html: '<div id="settings-modal-content" style="padding:0"></div>',
|
|
7067
7911
|
showCloseButton: true,
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
const child = document.createElement("e2e-configuration");
|
|
7077
|
-
child.persistence = this.persistence;
|
|
7078
|
-
child.translation = this.translation;
|
|
7079
|
-
container.appendChild(child);
|
|
7080
|
-
child.addEventListener("smartselectorchange", (e) => {
|
|
7912
|
+
width: 520
|
|
7913
|
+
},
|
|
7914
|
+
(container) => {
|
|
7915
|
+
mountComponentInSwal("lib-e2e-configuration", container, {
|
|
7916
|
+
persistence: this.persistence,
|
|
7917
|
+
translation: this.translation
|
|
7918
|
+
}, [
|
|
7919
|
+
{ name: "smartselectorchange", handler: (e) => {
|
|
7081
7920
|
this.smartSelectorEnabled = e.detail;
|
|
7082
|
-
}
|
|
7083
|
-
|
|
7921
|
+
} },
|
|
7922
|
+
{ name: "starthiddenchange", handler: (e) => {
|
|
7084
7923
|
this.isVisible = !e.detail;
|
|
7085
7924
|
this.style.display = this.isVisible ? "" : "none";
|
|
7086
|
-
}
|
|
7087
|
-
|
|
7088
|
-
|
|
7925
|
+
} },
|
|
7926
|
+
{ name: "resetwidgetposition", handler: () => this.resetWidgetPosition() },
|
|
7927
|
+
{ name: "issuetrackerchange", handler: (e) => {
|
|
7089
7928
|
this.issueTrackerConfig = e.detail;
|
|
7090
|
-
}
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
}
|
|
7095
|
-
});
|
|
7096
|
-
this.resizePopup();
|
|
7097
|
-
});
|
|
7929
|
+
} }
|
|
7930
|
+
]);
|
|
7931
|
+
}
|
|
7932
|
+
);
|
|
7098
7933
|
}
|
|
7099
7934
|
showHelpDialog() {
|
|
7100
|
-
this.
|
|
7101
|
-
|
|
7935
|
+
this.openSwalDialog(
|
|
7936
|
+
"isHelpDialogOpen",
|
|
7937
|
+
"help-modal-content",
|
|
7938
|
+
{
|
|
7102
7939
|
title: this.translation.translate("HELP.TITLE"),
|
|
7103
7940
|
html: '<div id="help-modal-content" style="padding:0"></div>',
|
|
7104
7941
|
showCloseButton: true,
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
const child = document.createElement("help-panel");
|
|
7114
|
-
child.translation = this.translation;
|
|
7115
|
-
container.appendChild(child);
|
|
7116
|
-
},
|
|
7117
|
-
willClose: () => {
|
|
7118
|
-
this.isHelpDialogOpen = false;
|
|
7119
|
-
}
|
|
7120
|
-
});
|
|
7121
|
-
this.resizePopup();
|
|
7122
|
-
});
|
|
7942
|
+
width: 560
|
|
7943
|
+
},
|
|
7944
|
+
(container) => {
|
|
7945
|
+
mountComponentInSwal("lib-e2e-help-panel", container, {
|
|
7946
|
+
translation: this.translation
|
|
7947
|
+
});
|
|
7948
|
+
}
|
|
7949
|
+
);
|
|
7123
7950
|
}
|
|
7124
7951
|
showAdvancedEditorDialog(testId) {
|
|
7125
7952
|
this.toggleModal("isAdvancedEditorDialogOpen", () => {
|
|
7126
|
-
|
|
7953
|
+
import_sweetalert22.default.fire({
|
|
7127
7954
|
title: this.translation.translate("MAIN_FRAME.SHOW_ADVANCED_EDITOR"),
|
|
7128
7955
|
html: '<div id="advanced-editor-modal-content" style="flex:1;min-height:0;display:flex;flex-direction:column;padding:0"></div>',
|
|
7129
7956
|
showCloseButton: true,
|
|
@@ -7134,42 +7961,33 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7134
7961
|
didOpen: () => {
|
|
7135
7962
|
makeSwalDraggable();
|
|
7136
7963
|
setSwal2DataCyAttribute();
|
|
7137
|
-
|
|
7138
|
-
if (popup) {
|
|
7139
|
-
popup.style.height = "600px";
|
|
7140
|
-
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
7141
|
-
if (htmlContainer) {
|
|
7142
|
-
htmlContainer.style.flex = "1";
|
|
7143
|
-
htmlContainer.style.minHeight = "0";
|
|
7144
|
-
htmlContainer.style.overflow = "hidden";
|
|
7145
|
-
htmlContainer.style.padding = "0";
|
|
7146
|
-
htmlContainer.style.margin = "0";
|
|
7147
|
-
htmlContainer.style.display = "flex";
|
|
7148
|
-
htmlContainer.style.flexDirection = "column";
|
|
7149
|
-
}
|
|
7150
|
-
}
|
|
7964
|
+
ensurePopupDimensions(import_sweetalert22.default.getPopup(), 600);
|
|
7151
7965
|
const container = document.getElementById("advanced-editor-modal-content");
|
|
7152
7966
|
if (!container) return;
|
|
7153
|
-
const child =
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7967
|
+
const child = mountComponentInSwal("lib-e2e-advanced-test-editor", container, {
|
|
7968
|
+
persistence: this.persistence,
|
|
7969
|
+
translation: this.translation,
|
|
7970
|
+
...testId !== void 0 ? { testId } : {}
|
|
7971
|
+
}, [
|
|
7972
|
+
{ name: "selectorstrategychange", handler: (e) => {
|
|
7973
|
+
this.recording.selectorStrategy = e.detail;
|
|
7974
|
+
} },
|
|
7975
|
+
{ name: "closemodal", handler: () => import_sweetalert22.default.close() },
|
|
7976
|
+
{ name: "openfileeditor", handler: (e) => {
|
|
7977
|
+
import_sweetalert22.default.close();
|
|
7978
|
+
setTimeout(() => this.showFileEditorDialog(
|
|
7979
|
+
e.detail.handle,
|
|
7980
|
+
e.detail.content,
|
|
7981
|
+
e.detail.fileName,
|
|
7982
|
+
e.detail.testId,
|
|
7983
|
+
e.detail.itBlock,
|
|
7984
|
+
e.detail.interceptorsBlock,
|
|
7985
|
+
e.detail.notes
|
|
7986
|
+
), 150);
|
|
7987
|
+
} }
|
|
7988
|
+
]);
|
|
7989
|
+
void this.persistence.getLoginSetup().then((cfg) => {
|
|
7990
|
+
child.loginSetupConfig = cfg;
|
|
7173
7991
|
});
|
|
7174
7992
|
},
|
|
7175
7993
|
willClose: () => {
|
|
@@ -7177,13 +7995,13 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7177
7995
|
}
|
|
7178
7996
|
});
|
|
7179
7997
|
setTimeout(() => {
|
|
7180
|
-
const popup =
|
|
7998
|
+
const popup = import_sweetalert22.default.getPopup();
|
|
7181
7999
|
if (popup) makeModalResizable(popup, { minWidth: 400, minHeight: 0 });
|
|
7182
8000
|
}, 0);
|
|
7183
8001
|
});
|
|
7184
8002
|
}
|
|
7185
8003
|
showFileEditorDialog(handle, content, fileName, testId, itBlock = "", interceptorsBlock = "", notes = "") {
|
|
7186
|
-
|
|
8004
|
+
import_sweetalert22.default.fire({
|
|
7187
8005
|
title: this.translation.translate("RECORDER.FILE_EDITOR_TITLE"),
|
|
7188
8006
|
html: '<div id="file-editor-modal-content" style="flex:1;min-height:0;display:flex;flex-direction:column;padding:0"></div>',
|
|
7189
8007
|
showCloseButton: false,
|
|
@@ -7194,47 +8012,35 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7194
8012
|
didOpen: () => {
|
|
7195
8013
|
makeSwalDraggable();
|
|
7196
8014
|
setSwal2DataCyAttribute();
|
|
7197
|
-
|
|
7198
|
-
if (popup) {
|
|
7199
|
-
popup.style.height = "640px";
|
|
7200
|
-
const htmlContainer = popup.querySelector(".swal2-html-container");
|
|
7201
|
-
if (htmlContainer) {
|
|
7202
|
-
htmlContainer.style.flex = "1";
|
|
7203
|
-
htmlContainer.style.minHeight = "0";
|
|
7204
|
-
htmlContainer.style.overflow = "hidden";
|
|
7205
|
-
htmlContainer.style.padding = "0";
|
|
7206
|
-
htmlContainer.style.margin = "0";
|
|
7207
|
-
htmlContainer.style.display = "flex";
|
|
7208
|
-
htmlContainer.style.flexDirection = "column";
|
|
7209
|
-
}
|
|
7210
|
-
}
|
|
8015
|
+
ensurePopupDimensions(import_sweetalert22.default.getPopup(), 640);
|
|
7211
8016
|
const container = document.getElementById("file-editor-modal-content");
|
|
7212
8017
|
if (!container) return;
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
8018
|
+
mountComponentInSwal("lib-e2e-file-preview", container, {
|
|
8019
|
+
translation: this.translation,
|
|
8020
|
+
fileContent: content,
|
|
8021
|
+
fileName,
|
|
8022
|
+
closeLabel: this.translation.translate("FILE_PREVIEW.BACK_TO_EDITOR"),
|
|
8023
|
+
itBlock,
|
|
8024
|
+
interceptorsBlock,
|
|
8025
|
+
notes
|
|
8026
|
+
}, [
|
|
8027
|
+
{ name: "close", handler: () => {
|
|
8028
|
+
import_sweetalert22.default.close();
|
|
8029
|
+
setTimeout(() => this.showAdvancedEditorDialog(testId), 150);
|
|
8030
|
+
} },
|
|
8031
|
+
{ name: "save", handler: async (e) => {
|
|
8032
|
+
try {
|
|
8033
|
+
const writable = await handle.createWritable();
|
|
8034
|
+
await writable.write(e.detail);
|
|
8035
|
+
await writable.close();
|
|
8036
|
+
showToast(this.translation.translate("RECORDER.FILE_SAVED_TOAST"));
|
|
8037
|
+
} catch {
|
|
8038
|
+
showToast(this.translation.translate("RECORDER.FILE_SAVE_ERROR_TOAST"), false);
|
|
8039
|
+
}
|
|
8040
|
+
import_sweetalert22.default.close();
|
|
8041
|
+
setTimeout(() => this.showAdvancedEditorDialog(testId), 150);
|
|
8042
|
+
} }
|
|
8043
|
+
]);
|
|
7238
8044
|
}
|
|
7239
8045
|
});
|
|
7240
8046
|
this.resizePopup();
|
|
@@ -7243,6 +8049,14 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7243
8049
|
async onSaveTest(description, tags = [], notes = "", ticketId = "") {
|
|
7244
8050
|
if (!description) return;
|
|
7245
8051
|
await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes, ticketId || void 0);
|
|
8052
|
+
const uploadedFiles = this.recording.getUploadedFilesSnapshot();
|
|
8053
|
+
if (uploadedFiles.length > 0) {
|
|
8054
|
+
const filenames = uploadedFiles.map((f) => f.filename).join(", ");
|
|
8055
|
+
showToast(
|
|
8056
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_MANUAL_TOAST").replace("{files}", filenames),
|
|
8057
|
+
false
|
|
8058
|
+
);
|
|
8059
|
+
}
|
|
7246
8060
|
this.recording.clearCommands();
|
|
7247
8061
|
this.clearRecordingHistory();
|
|
7248
8062
|
this.cypressCommands = [];
|
|
@@ -7264,6 +8078,29 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7264
8078
|
showToast(this.translation.translate("RECORDER.FIXTURES_NO_FOLDER_TOAST"), false);
|
|
7265
8079
|
}
|
|
7266
8080
|
}
|
|
8081
|
+
const uploadedFiles = this.recording.getUploadedFilesSnapshot();
|
|
8082
|
+
if (uploadedFiles.length > 0) {
|
|
8083
|
+
const filenames = uploadedFiles.map((f) => f.filename).join(", ");
|
|
8084
|
+
const hasAccess = await this.persistence.hasDirectoryAccess();
|
|
8085
|
+
if (hasAccess) {
|
|
8086
|
+
try {
|
|
8087
|
+
for (const uf of uploadedFiles) {
|
|
8088
|
+
await this.persistence.writeUploadedFile(uf.filename, uf.bytes);
|
|
8089
|
+
}
|
|
8090
|
+
showToast(this.translation.translate("RECORDER.FILE_UPLOAD_COPIED_TOAST").replace("{files}", filenames));
|
|
8091
|
+
} catch {
|
|
8092
|
+
showToast(
|
|
8093
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_COPY_ERROR_TOAST").replace("{files}", filenames),
|
|
8094
|
+
false
|
|
8095
|
+
);
|
|
8096
|
+
}
|
|
8097
|
+
} else {
|
|
8098
|
+
showToast(
|
|
8099
|
+
this.translation.translate("RECORDER.FILE_UPLOAD_MANUAL_TOAST").replace("{files}", filenames),
|
|
8100
|
+
false
|
|
8101
|
+
);
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
7267
8104
|
const id = await this.persistence.insertTest(description, finalCommands, finalInterceptors, tags, notes, ticketId || void 0);
|
|
7268
8105
|
this.recording.clearCommands();
|
|
7269
8106
|
this.clearRecordingHistory();
|
|
@@ -7274,7 +8111,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7274
8111
|
// ── helpers ───────────────────────────────────────────────────────────────
|
|
7275
8112
|
toggleModal(flag, openFn) {
|
|
7276
8113
|
if (this[flag]) {
|
|
7277
|
-
|
|
8114
|
+
import_sweetalert22.default.close();
|
|
7278
8115
|
this[flag] = false;
|
|
7279
8116
|
return;
|
|
7280
8117
|
}
|
|
@@ -7283,10 +8120,32 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
|
|
|
7283
8120
|
}
|
|
7284
8121
|
resizePopup() {
|
|
7285
8122
|
setTimeout(() => {
|
|
7286
|
-
const popup =
|
|
8123
|
+
const popup = import_sweetalert22.default.getPopup();
|
|
7287
8124
|
if (popup) makeModalResizable(popup, { minWidth: 400, minHeight: 200 });
|
|
7288
8125
|
}, 0);
|
|
7289
8126
|
}
|
|
8127
|
+
openSwalDialog(flag, contentId, swalOptions, onMount, extra) {
|
|
8128
|
+
this.toggleModal(flag, () => {
|
|
8129
|
+
import_sweetalert22.default.fire({
|
|
8130
|
+
showConfirmButton: false,
|
|
8131
|
+
color: "#e6edf3",
|
|
8132
|
+
...swalOptions,
|
|
8133
|
+
didOpen: () => {
|
|
8134
|
+
makeSwalDraggable();
|
|
8135
|
+
setSwal2DataCyAttribute();
|
|
8136
|
+
extra?.onDidOpenPreamble?.();
|
|
8137
|
+
const container = document.getElementById(contentId);
|
|
8138
|
+
if (!container) return;
|
|
8139
|
+
onMount(container);
|
|
8140
|
+
},
|
|
8141
|
+
willClose: () => {
|
|
8142
|
+
this[flag] = false;
|
|
8143
|
+
extra?.onWillClose?.();
|
|
8144
|
+
}
|
|
8145
|
+
});
|
|
8146
|
+
if (!extra?.skipResize) this.resizePopup();
|
|
8147
|
+
});
|
|
8148
|
+
}
|
|
7290
8149
|
render() {
|
|
7291
8150
|
const rec = this.isRecording;
|
|
7292
8151
|
const paused = this.isPaused;
|
|
@@ -7315,7 +8174,7 @@ if (!customElements.get("lib-e2e-recorder")) {
|
|
|
7315
8174
|
}
|
|
7316
8175
|
|
|
7317
8176
|
// src/index.ts
|
|
7318
|
-
var VERSION = "0.
|
|
8177
|
+
var VERSION = "0.10.0";
|
|
7319
8178
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7320
8179
|
0 && (module.exports = {
|
|
7321
8180
|
ACTIVE_SESSION_BREADCRUMB_KEY,
|
|
@@ -7340,18 +8199,19 @@ var VERSION = "0.9.0";
|
|
|
7340
8199
|
SUPPORTED_LANGS,
|
|
7341
8200
|
SaveTestElement,
|
|
7342
8201
|
SelectorPickerElement,
|
|
7343
|
-
Subject,
|
|
7344
8202
|
TOGGLE_MARGIN,
|
|
7345
8203
|
TestEditorElement,
|
|
7346
8204
|
TestPrevisualizerElement,
|
|
7347
8205
|
TransformationService,
|
|
7348
8206
|
TranslationService,
|
|
7349
8207
|
VERSION,
|
|
8208
|
+
_resetHttpMonitorState,
|
|
7350
8209
|
advancedTestTransformationService,
|
|
7351
8210
|
boxOffsetForDirection,
|
|
7352
8211
|
boxTopLeftFor,
|
|
7353
8212
|
clampTogglePosition,
|
|
7354
8213
|
defaultTogglePosition,
|
|
8214
|
+
ensurePopupDimensions,
|
|
7355
8215
|
generateAlias,
|
|
7356
8216
|
inferAssertionCommand,
|
|
7357
8217
|
injectStyles,
|
|
@@ -7361,7 +8221,6 @@ var VERSION = "0.9.0";
|
|
|
7361
8221
|
makeModalResizable,
|
|
7362
8222
|
makeSwalDraggable,
|
|
7363
8223
|
makeSwalDraggableByContentId,
|
|
7364
|
-
persistenceService,
|
|
7365
8224
|
resolveExpandDirection,
|
|
7366
8225
|
selectTestsForExport,
|
|
7367
8226
|
setSwal2DataCyAttribute,
|