lib-e2e-cypress-for-dummys-ts 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -92,7 +92,9 @@ var I18N_ES = {
92
92
  NO_TAGS: "Sin etiquetas",
93
93
  SAVE_BTN: "\u{1F4BE} Guardar",
94
94
  SAVE_AND_EDIT: "\u{1F4DD} Guardar y editar",
95
- REMOVE_TAG_TITLE: "Quitar"
95
+ REMOVE_TAG_TITLE: "Quitar",
96
+ NOTES_LABEL: "Descripci\xF3n / Notas (opcional):",
97
+ NOTES_PLACEHOLDER: "Qu\xE9 valida este test, condiciones previas, etc."
96
98
  },
97
99
  TEST_EDITOR: {
98
100
  NO_TAGS: "Sin etiquetas",
@@ -141,7 +143,10 @@ var I18N_ES = {
141
143
  JSON_BAD_FORMAT: "El archivo no tiene el formato esperado.",
142
144
  SMART_SELECTOR_SECTION: "\u{1F50D} Selector inteligente",
143
145
  SMART_SELECTOR_TITLE: "Selector inteligente",
144
- SMART_SELECTOR_SUB: "Muestra un picker al hacer click en elementos sin selector v\xE1lido."
146
+ SMART_SELECTOR_SUB: "Muestra un picker al hacer click en elementos sin selector v\xE1lido.",
147
+ START_HIDDEN_SECTION: "\u{1F441} Visibilidad del widget",
148
+ START_HIDDEN_TITLE: "Iniciar oculto",
149
+ START_HIDDEN_SUB: "El widget arranca invisible. Usa Ctrl+Shift+E para mostrarlo u ocultarlo."
145
150
  },
146
151
  SELECTOR_PICKER: {
147
152
  TITLE: "Selecciona un elemento del DOM",
@@ -180,7 +185,12 @@ var I18N_ES = {
180
185
  COPY_ICP_BTN: "\u{1F4CB} Copiar interceptores",
181
186
  INSERT_BTN: "\u{1F4BE} Insertar en archivo",
182
187
  EDIT_MANUAL_BTN: "\u270F\uFE0F Editar manualmente",
183
- CLOSE_BTN: "\u2715 Cerrar"
188
+ CLOSE_BTN: "\u2715 Cerrar",
189
+ NEW_FILE_BTN: "+ Nuevo archivo",
190
+ REFRESH_BTN: "\u21BB Actualizar",
191
+ NEW_FILE_PLACEHOLDER: "nombre-del-test",
192
+ NEW_FILE_CONFIRM: "Crear",
193
+ NEW_FILE_CANCEL: "Cancelar"
184
194
  },
185
195
  FILE_PREVIEW: {
186
196
  NO_FILE: "Sin archivo",
@@ -249,7 +259,9 @@ var I18N_EN = {
249
259
  NO_TAGS: "No tags",
250
260
  SAVE_BTN: "\u{1F4BE} Save",
251
261
  SAVE_AND_EDIT: "\u{1F4DD} Save and edit",
252
- REMOVE_TAG_TITLE: "Remove"
262
+ REMOVE_TAG_TITLE: "Remove",
263
+ NOTES_LABEL: "Description / Notes (optional):",
264
+ NOTES_PLACEHOLDER: "What this test validates, preconditions, etc."
253
265
  },
254
266
  TEST_EDITOR: {
255
267
  NO_TAGS: "No tags",
@@ -298,7 +310,10 @@ var I18N_EN = {
298
310
  JSON_BAD_FORMAT: "The file does not have the expected format.",
299
311
  SMART_SELECTOR_SECTION: "\u{1F50D} Smart selector",
300
312
  SMART_SELECTOR_TITLE: "Smart selector",
301
- SMART_SELECTOR_SUB: "Shows a picker when clicking elements with no valid selector."
313
+ SMART_SELECTOR_SUB: "Shows a picker when clicking elements with no valid selector.",
314
+ START_HIDDEN_SECTION: "\u{1F441} Widget visibility",
315
+ START_HIDDEN_TITLE: "Start hidden",
316
+ START_HIDDEN_SUB: "The widget starts invisible. Use Ctrl+Shift+E to show or hide it."
302
317
  },
303
318
  SELECTOR_PICKER: {
304
319
  TITLE: "Select a DOM element",
@@ -337,7 +352,12 @@ var I18N_EN = {
337
352
  COPY_ICP_BTN: "\u{1F4CB} Copy interceptors",
338
353
  INSERT_BTN: "\u{1F4BE} Insert into file",
339
354
  EDIT_MANUAL_BTN: "\u270F\uFE0F Edit manually",
340
- CLOSE_BTN: "\u2715 Close"
355
+ CLOSE_BTN: "\u2715 Close",
356
+ NEW_FILE_BTN: "+ New file",
357
+ REFRESH_BTN: "\u21BB Refresh",
358
+ NEW_FILE_PLACEHOLDER: "test-name",
359
+ NEW_FILE_CONFIRM: "Create",
360
+ NEW_FILE_CANCEL: "Cancel"
341
361
  },
342
362
  FILE_PREVIEW: {
343
363
  NO_FILE: "No file",
@@ -406,7 +426,9 @@ var I18N_FR = {
406
426
  NO_TAGS: "Sans \xE9tiquettes",
407
427
  SAVE_BTN: "\u{1F4BE} Enregistrer",
408
428
  SAVE_AND_EDIT: "\u{1F4DD} Enregistrer et \xE9diter",
409
- REMOVE_TAG_TITLE: "Retirer"
429
+ REMOVE_TAG_TITLE: "Retirer",
430
+ NOTES_LABEL: "Description / Notes (optionnel) :",
431
+ NOTES_PLACEHOLDER: "Ce que ce test valide, conditions pr\xE9alables, etc."
410
432
  },
411
433
  TEST_EDITOR: {
412
434
  NO_TAGS: "Sans \xE9tiquettes",
@@ -455,7 +477,10 @@ var I18N_FR = {
455
477
  JSON_BAD_FORMAT: "Le fichier n'a pas le format attendu.",
456
478
  SMART_SELECTOR_SECTION: "\u{1F50D} S\xE9lecteur intelligent",
457
479
  SMART_SELECTOR_TITLE: "S\xE9lecteur intelligent",
458
- SMART_SELECTOR_SUB: "Affiche un s\xE9lecteur lors du clic sur des \xE9l\xE9ments sans s\xE9lecteur valide."
480
+ SMART_SELECTOR_SUB: "Affiche un s\xE9lecteur lors du clic sur des \xE9l\xE9ments sans s\xE9lecteur valide.",
481
+ START_HIDDEN_SECTION: "\u{1F441} Visibilit\xE9 du widget",
482
+ START_HIDDEN_TITLE: "D\xE9marrer masqu\xE9",
483
+ START_HIDDEN_SUB: "Le widget d\xE9marre invisible. Utilisez Ctrl+Shift+E pour l'afficher ou le masquer."
459
484
  },
460
485
  SELECTOR_PICKER: {
461
486
  TITLE: "S\xE9lectionnez un \xE9l\xE9ment du DOM",
@@ -494,7 +519,12 @@ var I18N_FR = {
494
519
  COPY_ICP_BTN: "\u{1F4CB} Copier les intercepteurs",
495
520
  INSERT_BTN: "\u{1F4BE} Ins\xE9rer dans le fichier",
496
521
  EDIT_MANUAL_BTN: "\u270F\uFE0F \xC9diter manuellement",
497
- CLOSE_BTN: "\u2715 Fermer"
522
+ CLOSE_BTN: "\u2715 Fermer",
523
+ NEW_FILE_BTN: "+ Nouveau fichier",
524
+ REFRESH_BTN: "\u21BB Actualiser",
525
+ NEW_FILE_PLACEHOLDER: "nom-du-test",
526
+ NEW_FILE_CONFIRM: "Cr\xE9er",
527
+ NEW_FILE_CANCEL: "Annuler"
498
528
  },
499
529
  FILE_PREVIEW: {
500
530
  NO_FILE: "Sans fichier",
@@ -563,7 +593,9 @@ var I18N_IT = {
563
593
  NO_TAGS: "Nessuna etichetta",
564
594
  SAVE_BTN: "\u{1F4BE} Salva",
565
595
  SAVE_AND_EDIT: "\u{1F4DD} Salva e modifica",
566
- REMOVE_TAG_TITLE: "Rimuovi"
596
+ REMOVE_TAG_TITLE: "Rimuovi",
597
+ NOTES_LABEL: "Descrizione / Note (opzionale):",
598
+ NOTES_PLACEHOLDER: "Cosa valida questo test, condizioni preliminari, ecc."
567
599
  },
568
600
  TEST_EDITOR: {
569
601
  NO_TAGS: "Nessuna etichetta",
@@ -612,7 +644,10 @@ var I18N_IT = {
612
644
  JSON_BAD_FORMAT: "Il file non ha il formato previsto.",
613
645
  SMART_SELECTOR_SECTION: "\u{1F50D} Selettore intelligente",
614
646
  SMART_SELECTOR_TITLE: "Selettore intelligente",
615
- SMART_SELECTOR_SUB: "Mostra un selettore quando si fa clic su elementi senza selettore valido."
647
+ SMART_SELECTOR_SUB: "Mostra un selettore quando si fa clic su elementi senza selettore valido.",
648
+ START_HIDDEN_SECTION: "\u{1F441} Visibilit\xE0 del widget",
649
+ START_HIDDEN_TITLE: "Avvia nascosto",
650
+ START_HIDDEN_SUB: "Il widget si avvia invisibile. Usa Ctrl+Shift+E per mostrarlo o nasconderlo."
616
651
  },
617
652
  SELECTOR_PICKER: {
618
653
  TITLE: "Seleziona un elemento DOM",
@@ -651,7 +686,12 @@ var I18N_IT = {
651
686
  COPY_ICP_BTN: "\u{1F4CB} Copia interceptor",
652
687
  INSERT_BTN: "\u{1F4BE} Inserisci nel file",
653
688
  EDIT_MANUAL_BTN: "\u270F\uFE0F Modifica manualmente",
654
- CLOSE_BTN: "\u2715 Chiudi"
689
+ CLOSE_BTN: "\u2715 Chiudi",
690
+ NEW_FILE_BTN: "+ Nuovo file",
691
+ REFRESH_BTN: "\u21BB Aggiorna",
692
+ NEW_FILE_PLACEHOLDER: "nome-del-test",
693
+ NEW_FILE_CONFIRM: "Crea",
694
+ NEW_FILE_CANCEL: "Annulla"
655
695
  },
656
696
  FILE_PREVIEW: {
657
697
  NO_FILE: "Nessun file",
@@ -720,7 +760,9 @@ var I18N_DE = {
720
760
  NO_TAGS: "Keine Tags",
721
761
  SAVE_BTN: "\u{1F4BE} Speichern",
722
762
  SAVE_AND_EDIT: "\u{1F4DD} Speichern und bearbeiten",
723
- REMOVE_TAG_TITLE: "Entfernen"
763
+ REMOVE_TAG_TITLE: "Entfernen",
764
+ NOTES_LABEL: "Beschreibung / Notizen (optional):",
765
+ NOTES_PLACEHOLDER: "Was dieser Test validiert, Voraussetzungen usw."
724
766
  },
725
767
  TEST_EDITOR: {
726
768
  NO_TAGS: "Keine Tags",
@@ -769,7 +811,10 @@ var I18N_DE = {
769
811
  JSON_BAD_FORMAT: "Die Datei hat nicht das erwartete Format.",
770
812
  SMART_SELECTOR_SECTION: "\u{1F50D} Intelligenter Selektor",
771
813
  SMART_SELECTOR_TITLE: "Intelligenter Selektor",
772
- SMART_SELECTOR_SUB: "Zeigt ein Auswahlfeld, wenn auf Elemente ohne g\xFCltigen Selektor geklickt wird."
814
+ SMART_SELECTOR_SUB: "Zeigt ein Auswahlfeld, wenn auf Elemente ohne g\xFCltigen Selektor geklickt wird.",
815
+ START_HIDDEN_SECTION: "\u{1F441} Widget-Sichtbarkeit",
816
+ START_HIDDEN_TITLE: "Versteckt starten",
817
+ START_HIDDEN_SUB: "Das Widget startet unsichtbar. Verwenden Sie Ctrl+Shift+E zum Ein-/Ausblenden."
773
818
  },
774
819
  SELECTOR_PICKER: {
775
820
  TITLE: "DOM-Element ausw\xE4hlen",
@@ -808,7 +853,12 @@ var I18N_DE = {
808
853
  COPY_ICP_BTN: "\u{1F4CB} Interceptors kopieren",
809
854
  INSERT_BTN: "\u{1F4BE} In Datei einf\xFCgen",
810
855
  EDIT_MANUAL_BTN: "\u270F\uFE0F Manuell bearbeiten",
811
- CLOSE_BTN: "\u2715 Schlie\xDFen"
856
+ CLOSE_BTN: "\u2715 Schlie\xDFen",
857
+ NEW_FILE_BTN: "+ Neue Datei",
858
+ REFRESH_BTN: "\u21BB Aktualisieren",
859
+ NEW_FILE_PLACEHOLDER: "test-name",
860
+ NEW_FILE_CONFIRM: "Erstellen",
861
+ NEW_FILE_CANCEL: "Abbrechen"
812
862
  },
813
863
  FILE_PREVIEW: {
814
864
  NO_FILE: "Keine Datei",
@@ -890,13 +940,37 @@ var TranslationService = class {
890
940
  };
891
941
  var translationService = new TranslationService();
892
942
 
943
+ // src/utils/code-format.utils.ts
944
+ function gcd(a, b) {
945
+ while (b) {
946
+ const t = b;
947
+ b = a % b;
948
+ a = t;
949
+ }
950
+ return a;
951
+ }
952
+ function normalizeBlock(code, baseIndent) {
953
+ const lines = code.split("\n").map((l) => l.trimEnd());
954
+ const nonEmpty = lines.filter((l) => l.trim().length > 0);
955
+ if (!nonEmpty.length) return "";
956
+ const indents = nonEmpty.map((l) => l.length - l.trimStart().length);
957
+ const minIndent = Math.min(...indents);
958
+ const relIndents = [...new Set(indents.map((n) => n - minIndent).filter((n) => n > 0))];
959
+ const step = relIndents.length > 0 ? relIndents.reduce(gcd) : 2;
960
+ return lines.map((l) => {
961
+ if (!l.trim()) return "";
962
+ const level = Math.round((l.length - l.trimStart().length - minIndent) / step);
963
+ return baseIndent + " ".repeat(level) + l.trimStart();
964
+ }).join("\n");
965
+ }
966
+
893
967
  // src/services/transformation.service.ts
894
968
  var TransformationService = class {
895
969
  toLang(lang) {
896
970
  return isLang(lang) ? lang : "en";
897
971
  }
898
972
  generateItDescription(description, commands) {
899
- const commandsBlock = commands.map((cmd) => ` ${cmd}`).join("\n");
973
+ const commandsBlock = commands.map((cmd) => normalizeBlock(cmd, " ")).join("\n");
900
974
  return `it('${description}', () => {
901
975
  ${commandsBlock}
902
976
  });`;
@@ -927,6 +1001,13 @@ ${interceptors} })
927
1001
  }
928
1002
  return content.slice(0, idx) + "\n" + itBlock + "\n" + content.slice(idx);
929
1003
  }
1004
+ buildBlockComment(notes) {
1005
+ if (!notes.trim()) return "";
1006
+ const lines = notes.split("\n").map((l) => ` * ${l}`).join("\n");
1007
+ return `/**
1008
+ ${lines}
1009
+ */`;
1010
+ }
930
1011
  isFile(file) {
931
1012
  return !!file && file.kind === "file";
932
1013
  }
@@ -1337,9 +1418,9 @@ var PersistenceService = class {
1337
1418
  return this._db;
1338
1419
  }
1339
1420
  // ── Tests ─────────────────────────────────────────────────────────────────
1340
- async insertTest(name, commands = [], interceptors = [], tags = []) {
1421
+ async insertTest(name, commands = [], interceptors = [], tags = [], notes) {
1341
1422
  const db = await this.getDB();
1342
- const record = { name, createdAt: Date.now(), ...tags.length ? { tags } : {} };
1423
+ const record = { name, createdAt: Date.now(), ...tags.length ? { tags } : {}, ...notes ? { notes } : {} };
1343
1424
  const id = await db.add("tests", record);
1344
1425
  if (commands.length) await this.insertCommands(commands, id);
1345
1426
  if (interceptors.length) await this.insertInterceptors(interceptors, id);
@@ -1363,9 +1444,9 @@ var PersistenceService = class {
1363
1444
  const commands = await this.getCommandStrings(testId);
1364
1445
  const interceptors = await this.getInterceptorStrings(testId);
1365
1446
  const itBlock = `it('${record.name}', () => {
1366
- ${commands.join("\n ")}
1447
+ ${commands.map((c) => normalizeBlock(c, " ")).join("\n")}
1367
1448
  });`;
1368
- const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => " " + i).join("\n") + "\n" : "";
1449
+ const interceptorsBlock = interceptors.length ? " // Auto-generated Cypress interceptors\n" + interceptors.map((i) => normalizeBlock(i, " ")).join("\n") + "\n" : "";
1369
1450
  return { ...record, commands, interceptors, cypressCommands: commands, itBlock, interceptorsBlock };
1370
1451
  }
1371
1452
  async deleteTest(id) {
@@ -2104,7 +2185,8 @@ if (!customElements.get("test-previsualizer")) {
2104
2185
  var SAVE_TEST_STYLES = `
2105
2186
  :host { display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e6edf3; }
2106
2187
  * { box-sizing: border-box; }
2107
- .container { padding: 24px 28px; text-align: center; }
2188
+ .container { padding: 24px 28px; text-align: left; }
2189
+ .btn-row, p { text-align: center; }
2108
2190
  p { margin: 0 0 20px; font-size: 14px; color: #8b949e; line-height: 1.5; }
2109
2191
  .btn-row { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }
2110
2192
  button {
@@ -2126,6 +2208,15 @@ var SAVE_TEST_STYLES = `
2126
2208
  }
2127
2209
  input[type="text"]:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.15); }
2128
2210
  input[type="text"]::placeholder { color: #484f58; }
2211
+ textarea {
2212
+ width: 100%; padding: 10px 14px; border: 1px solid #30363d;
2213
+ border-radius: 8px; background: #0d1117; color: #e6edf3;
2214
+ font-size: 14px; outline: none; margin-bottom: 4px;
2215
+ font-family: inherit; resize: vertical; line-height: 1.5;
2216
+ transition: border-color 0.15s, box-shadow 0.15s;
2217
+ }
2218
+ textarea:focus { border-color: #2f81f7; box-shadow: 0 0 0 3px rgba(47,129,247,0.15); }
2219
+ textarea::placeholder { color: #484f58; }
2129
2220
  .tag-label {
2130
2221
  font-size: 11px; color: #484f58; text-align: left; margin-bottom: 5px; display: block;
2131
2222
  }
@@ -2164,7 +2255,7 @@ function renderSaveTestAsk(t) {
2164
2255
  </div>
2165
2256
  </div>`;
2166
2257
  }
2167
- function renderSaveTestDesc(description, tags, t) {
2258
+ function renderSaveTestDesc(description, notes, tags, t) {
2168
2259
  const chipsHtml = tags.map(
2169
2260
  (tag) => `<span class="chip">${escHtml(tag)}<button class="chip-del" data-tag="${escAttr(tag)}" title="${t("SAVE_TEST.REMOVE_TAG_TITLE")}">\u2715</button></span>`
2170
2261
  ).join("");
@@ -2173,6 +2264,8 @@ function renderSaveTestDesc(description, tags, t) {
2173
2264
  <p>${t("SAVE_TEST.DESC_LABEL")} (<code>it()</code>):</p>
2174
2265
  <input id="desc-input" type="text" placeholder="${t("SAVE_TEST.DESC_PLACEHOLDER")}"
2175
2266
  value="${escAttr(description)}" autocomplete="off" />
2267
+ <span class="tag-label">${t("SAVE_TEST.NOTES_LABEL")}</span>
2268
+ <textarea id="notes-input" rows="3" placeholder="${escAttr(t("SAVE_TEST.NOTES_PLACEHOLDER"))}">${escHtml(notes)}</textarea>
2176
2269
  <span class="tag-label">${t("SAVE_TEST.TAGS_LABEL")}</span>
2177
2270
  <div class="tag-input-row">
2178
2271
  <input id="tag-input" type="text" placeholder="${t("SAVE_TEST.TAGS_PLACEHOLDER")}" autocomplete="off" />
@@ -2192,6 +2285,7 @@ var SaveTestElement = class extends HTMLElement {
2192
2285
  shadow;
2193
2286
  _step = "ask";
2194
2287
  description = "";
2288
+ notes = "";
2195
2289
  tags = [];
2196
2290
  translation = translationService;
2197
2291
  constructor() {
@@ -2209,17 +2303,18 @@ var SaveTestElement = class extends HTMLElement {
2209
2303
  this.render();
2210
2304
  }
2211
2305
  confirmSave() {
2212
- this.dispatch("savetest", { description: this.description.trim(), tags: [...this.tags] });
2306
+ this.dispatch("savetest", { description: this.description.trim(), notes: this.notes, tags: [...this.tags] });
2213
2307
  }
2214
2308
  confirmSaveAndExport() {
2215
- this.dispatch("saveandexport", { description: this.description.trim(), tags: [...this.tags] });
2309
+ this.dispatch("saveandexport", { description: this.description.trim(), notes: this.notes, tags: [...this.tags] });
2216
2310
  }
2217
2311
  cancel() {
2218
- this.dispatch("savetest", { description: null, tags: [] });
2312
+ this.dispatch("savetest", { description: null, notes: "", tags: [] });
2219
2313
  }
2220
2314
  restartComponent() {
2221
2315
  this._step = "ask";
2222
2316
  this.description = "";
2317
+ this.notes = "";
2223
2318
  this.tags = [];
2224
2319
  this.render();
2225
2320
  }
@@ -2246,12 +2341,16 @@ var SaveTestElement = class extends HTMLElement {
2246
2341
  this.shadow.getElementById("btn-yes")?.addEventListener("click", () => this.askSave());
2247
2342
  this.shadow.getElementById("btn-no")?.addEventListener("click", () => this.cancel());
2248
2343
  } else {
2249
- this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestDesc(this.description, this.tags, this.t.bind(this))}`;
2344
+ this.shadow.innerHTML = `<style>${SAVE_TEST_STYLES}</style>${renderSaveTestDesc(this.description, this.notes, this.tags, this.t.bind(this))}`;
2250
2345
  const descInput = this.shadow.getElementById("desc-input");
2346
+ const notesInput = this.shadow.getElementById("notes-input");
2251
2347
  const tagInput = this.shadow.getElementById("tag-input");
2252
2348
  descInput.addEventListener("input", () => {
2253
2349
  this.description = descInput.value;
2254
2350
  });
2351
+ notesInput.addEventListener("input", () => {
2352
+ this.notes = notesInput.value;
2353
+ });
2255
2354
  const tryAddTag = () => {
2256
2355
  this.addTag(tagInput.value);
2257
2356
  tagInput.value = "";
@@ -2357,15 +2456,124 @@ var TEST_EDITOR_STYLES = `
2357
2456
  background: #0d1117; padding: 10px 14px;
2358
2457
  border-top: 1px solid #21262d;
2359
2458
  }
2360
- .section-title { font-size: 10px; color: #484f58; text-transform: uppercase;
2361
- letter-spacing: 0.8px; margin-bottom: 6px; font-weight: 600; }
2362
- .cmd-list { font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
2363
- font-size: 11px; color: #c9d1d9; line-height: 1.8; }
2364
- .icp-list { font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
2365
- font-size: 11px; color: #3fb950; line-height: 1.8; margin-top: 10px; }
2366
- .copy-row { display: flex; gap: 6px; margin-top: 10px; }
2459
+ .test-notes {
2460
+ margin: 0 0 10px; padding: 8px 12px;
2461
+ background: rgba(47,129,247,0.06); border-left: 3px solid rgba(47,129,247,0.4);
2462
+ border-radius: 0 6px 6px 0; font-size: 12px; color: #8b949e;
2463
+ line-height: 1.6; white-space: pre-wrap; word-break: break-word;
2464
+ }
2465
+ .code-preview {
2466
+ margin: 0; padding: 10px 12px; background: #161b22;
2467
+ border-radius: 6px; border: 1px solid #21262d;
2468
+ font-size: 11px; color: #c9d1d9; line-height: 1.7;
2469
+ font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
2470
+ overflow-x: auto; white-space: pre;
2471
+ scrollbar-width: thin; scrollbar-color: #30363d transparent;
2472
+ }
2473
+ .code-preview::-webkit-scrollbar { height: 4px; }
2474
+ .code-preview::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
2475
+ .code-preview-icp { margin-top: 8px; }
2476
+ .sh-kw { color: #ff7b72; }
2477
+ .sh-bi { color: #d2a8ff; }
2478
+ .sh-str { color: #a5d6ff; }
2479
+ .sh-cmt { color: #8b949e; font-style: italic; }
2480
+ .sh-num { color: #79c0ff; }
2367
2481
  `;
2368
2482
 
2483
+ // src/utils/syntax-highlight.utils.ts
2484
+ var KEYWORDS = /* @__PURE__ */ new Set([
2485
+ "describe",
2486
+ "it",
2487
+ "xit",
2488
+ "fit",
2489
+ "xdescribe",
2490
+ "fdescribe",
2491
+ "beforeEach",
2492
+ "afterEach",
2493
+ "before",
2494
+ "after",
2495
+ "context",
2496
+ "const",
2497
+ "let",
2498
+ "var",
2499
+ "function",
2500
+ "return",
2501
+ "if",
2502
+ "else",
2503
+ "import",
2504
+ "export",
2505
+ "from",
2506
+ "new",
2507
+ "this",
2508
+ "null",
2509
+ "undefined",
2510
+ "true",
2511
+ "false",
2512
+ "async",
2513
+ "await",
2514
+ "of",
2515
+ "in",
2516
+ "typeof",
2517
+ "void"
2518
+ ]);
2519
+ var BUILTINS = /* @__PURE__ */ new Set(["cy", "expect", "assert", "Cypress", "chai"]);
2520
+ function syntaxHighlight(code) {
2521
+ return code.split("\n").map(highlightLine).join("\n");
2522
+ }
2523
+ function highlightLine(line) {
2524
+ let result = "";
2525
+ let i = 0;
2526
+ const len = line.length;
2527
+ while (i < len) {
2528
+ if (line[i] === "/" && i + 1 < len && line[i + 1] === "/") {
2529
+ result += `<span class="sh-cmt">${escHtml(line.slice(i))}</span>`;
2530
+ return result;
2531
+ }
2532
+ const q = line[i];
2533
+ if (q === "'" || q === '"' || q === "`") {
2534
+ let j = i + 1;
2535
+ while (j < len) {
2536
+ if (line[j] === "\\") {
2537
+ j += 2;
2538
+ continue;
2539
+ }
2540
+ if (line[j] === q) {
2541
+ j++;
2542
+ break;
2543
+ }
2544
+ j++;
2545
+ }
2546
+ result += `<span class="sh-str">${escHtml(line.slice(i, j))}</span>`;
2547
+ i = j;
2548
+ continue;
2549
+ }
2550
+ if (/[0-9]/.test(line[i]) && (i === 0 || !/[a-zA-Z_$]/.test(line[i - 1]))) {
2551
+ let j = i;
2552
+ while (j < len && /[0-9.]/.test(line[j])) j++;
2553
+ result += `<span class="sh-num">${escHtml(line.slice(i, j))}</span>`;
2554
+ i = j;
2555
+ continue;
2556
+ }
2557
+ if (/[a-zA-Z_$]/.test(line[i])) {
2558
+ let j = i;
2559
+ while (j < len && /[a-zA-Z0-9_$]/.test(line[j])) j++;
2560
+ const word = line.slice(i, j);
2561
+ if (KEYWORDS.has(word)) {
2562
+ result += `<span class="sh-kw">${escHtml(word)}</span>`;
2563
+ } else if (BUILTINS.has(word)) {
2564
+ result += `<span class="sh-bi">${escHtml(word)}</span>`;
2565
+ } else {
2566
+ result += escHtml(word);
2567
+ }
2568
+ i = j;
2569
+ continue;
2570
+ }
2571
+ result += escHtml(line[i]);
2572
+ i++;
2573
+ }
2574
+ return result;
2575
+ }
2576
+
2369
2577
  // src/components/test-editor/test-editor.template.ts
2370
2578
  function renderTestEditor(state, t) {
2371
2579
  const { tags, visible, selectedVisible, activeTag, selectMode, selectedIds, describeName, expandedIndex, interceptorsByTest } = state;
@@ -2383,18 +2591,20 @@ function renderTestEditor(state, t) {
2383
2591
  const icps = interceptorsByTest[test.id] ?? test.interceptors ?? [];
2384
2592
  const tagsHtml = (test.tags ?? []).length ? `<span class="test-tags">${(test.tags ?? []).map((tag) => `<span class="test-tag">${escHtml(tag)}</span>`).join("")}</span>` : "";
2385
2593
  const checkbox = selectMode ? `<input type="checkbox" ${selectedIds.has(test.id) ? "checked" : ""} data-select="${test.id}" />` : "";
2594
+ const hasIcps = (test.interceptors ?? []).length > 0;
2595
+ const itBlockCode = `it('${test.name}', () => {
2596
+ ${(test.commands ?? []).map((c) => normalizeBlock(c, " ")).join("\n")}
2597
+ });`;
2598
+ const icpBlockCode = icps.length ? `beforeEach(() => {
2599
+ // Auto-generated Cypress interceptors
2600
+ ${icps.map((c) => normalizeBlock(c, " ")).join("\n")}
2601
+ });` : "";
2602
+ const notesHtml = expanded && test.notes ? `<p class="test-notes">${escHtml(test.notes)}</p>` : "";
2386
2603
  const body = expanded ? `
2387
2604
  <div class="row-body">
2388
- <div class="section-title">${t("TEST_EDITOR.SECTION_COMMANDS")} (${(test.commands ?? []).length})</div>
2389
- <div class="cmd-list">${(test.commands ?? []).map(escHtml).join("<br>")}</div>
2390
- ${icps.length ? `<div class="icp-list">
2391
- <div class="section-title" style="margin-top:8px">${t("TEST_EDITOR.SECTION_INTERCEPTORS")}</div>
2392
- ${icps.map(escHtml).join("<br>")}
2393
- </div>` : ""}
2394
- <div class="copy-row">
2395
- <button class="btn-icon" data-action="copy-cmds" data-idx="${i}">${t("TEST_EDITOR.COPY_CMDS_BTN")}</button>
2396
- ${icps.length ? `<button class="btn-icon" data-action="copy-icps" data-idx="${i}">${t("TEST_EDITOR.COPY_ICPS_BTN")}</button>` : ""}
2397
- </div>
2605
+ ${notesHtml}
2606
+ <pre class="code-preview">${syntaxHighlight(itBlockCode)}</pre>
2607
+ ${icpBlockCode ? `<pre class="code-preview code-preview-icp">${syntaxHighlight(icpBlockCode)}</pre>` : ""}
2398
2608
  </div>` : "";
2399
2609
  return `
2400
2610
  <div class="row${selectedIds.has(test.id) ? " selected-row" : ""}">
@@ -2403,6 +2613,8 @@ function renderTestEditor(state, t) {
2403
2613
  <span class="test-name">${escHtml(test.name)}</span>
2404
2614
  ${tagsHtml}
2405
2615
  <span class="test-date">${date}</span>
2616
+ <button class="btn-icon" data-action="copy-cmds" data-idx="${i}" title="${t("TEST_EDITOR.COPY_CMDS_BTN")}">${t("TEST_EDITOR.COPY_CMDS_BTN")}</button>
2617
+ ${hasIcps ? `<button class="btn-icon" data-action="copy-icps" data-idx="${i}" title="${t("TEST_EDITOR.COPY_ICPS_BTN")}">${t("TEST_EDITOR.COPY_ICPS_BTN")}</button>` : ""}
2406
2618
  <button class="btn-icon btn-del" data-action="delete" data-id="${test.id}" title="${t("TEST_EDITOR.DELETE_TITLE")}">\u{1F5D1}</button>
2407
2619
  </div>
2408
2620
  ${body}
@@ -2581,7 +2793,7 @@ ${beforeEach}${itBlocks}
2581
2793
  ev.stopPropagation();
2582
2794
  const idx = Number(el.dataset["idx"]);
2583
2795
  const t = visible[idx];
2584
- this.copyToClipboard((this.interceptorsByTest[t?.id] ?? []).join("\n"));
2796
+ this.copyToClipboard((this.interceptorsByTest[t?.id] ?? t?.interceptors ?? []).join("\n"));
2585
2797
  });
2586
2798
  });
2587
2799
  }
@@ -2694,7 +2906,7 @@ var LANGS = [
2694
2906
  { value: "de", label: "Deutsch" }
2695
2907
  ];
2696
2908
  function renderConfiguration(state, t) {
2697
- const { selectedLanguage, advancedHttpConfig, selectorStrategy, filesystemGranted, cypressFolderName, smartSelectorEnabled } = state;
2909
+ const { selectedLanguage, advancedHttpConfig, selectorStrategy, filesystemGranted, cypressFolderName, smartSelectorEnabled, startHidden } = state;
2698
2910
  const langOptions = LANGS.map(
2699
2911
  (l) => `<option value="${l.value}" ${selectedLanguage === l.value ? "selected" : ""}>${l.label}</option>`
2700
2912
  ).join("");
@@ -2734,6 +2946,18 @@ function renderConfiguration(state, t) {
2734
2946
  </label>
2735
2947
  </div>
2736
2948
 
2949
+ <!-- Start Hidden -->
2950
+ <div class="card">
2951
+ <div class="card-hd">${t("CONFIG.START_HIDDEN_SECTION")}</div>
2952
+ <label class="check-row">
2953
+ <input type="checkbox" id="start-hidden-toggle" ${startHidden ? "checked" : ""} />
2954
+ <div>
2955
+ <div class="check-title">${t("CONFIG.START_HIDDEN_TITLE")}</div>
2956
+ <div class="check-sub">${t("CONFIG.START_HIDDEN_SUB")}</div>
2957
+ </div>
2958
+ </label>
2959
+ </div>
2960
+
2737
2961
  <!-- Selector Strategy -->
2738
2962
  <div class="card card-wide">
2739
2963
  <div class="card-hd">${t("CONFIG.SELECTOR_SECTION")}</div>
@@ -2796,6 +3020,7 @@ var ConfigurationElement = class extends HTMLElement {
2796
3020
  advancedHttpConfig = false;
2797
3021
  selectorStrategy = "data-cy";
2798
3022
  smartSelectorEnabled = true;
3023
+ startHidden = false;
2799
3024
  filesystemGranted = false;
2800
3025
  cypressFolderName = null;
2801
3026
  constructor() {
@@ -2821,6 +3046,7 @@ var ConfigurationElement = class extends HTMLElement {
2821
3046
  this.advancedHttpConfig = localStorage.getItem("extendedHttpCommands") === "true";
2822
3047
  this.selectorStrategy = config?.["selectorStrategy"] ?? "data-cy";
2823
3048
  this.smartSelectorEnabled = config?.["smartSelectorEnabled"] !== "false";
3049
+ this.startHidden = config?.["startHidden"] === "true";
2824
3050
  this.filesystemGranted = config?.["allowReadWriteFiles"] === "true";
2825
3051
  const handle = config?.["cypressDirectoryHandle"];
2826
3052
  this.cypressFolderName = handle?.name ?? null;
@@ -2838,6 +3064,12 @@ var ConfigurationElement = class extends HTMLElement {
2838
3064
  this.persistence.setConfig({ extendedHttpCommands: checked ? "true" : "false" });
2839
3065
  this.render();
2840
3066
  }
3067
+ async onStartHiddenChange(checked) {
3068
+ this.startHidden = checked;
3069
+ await this.persistence.setConfig({ startHidden: checked ? "true" : "false" });
3070
+ this.dispatchEvent(new CustomEvent("starthiddenchange", { detail: checked, bubbles: true, composed: true }));
3071
+ this.render();
3072
+ }
2841
3073
  async onSmartSelectorChange(enabled) {
2842
3074
  this.smartSelectorEnabled = enabled;
2843
3075
  await this.persistence.setConfig({ smartSelectorEnabled: enabled ? "true" : "false" });
@@ -2898,7 +3130,8 @@ var ConfigurationElement = class extends HTMLElement {
2898
3130
  selectorStrategy: this.selectorStrategy,
2899
3131
  filesystemGranted: this.filesystemGranted,
2900
3132
  cypressFolderName: this.cypressFolderName,
2901
- smartSelectorEnabled: this.smartSelectorEnabled
3133
+ smartSelectorEnabled: this.smartSelectorEnabled,
3134
+ startHidden: this.startHidden
2902
3135
  }, this.t.bind(this))}`;
2903
3136
  ;
2904
3137
  this.shadow.getElementById("lang-select").addEventListener(
@@ -2913,6 +3146,10 @@ var ConfigurationElement = class extends HTMLElement {
2913
3146
  "change",
2914
3147
  (e) => this.onSmartSelectorChange(e.target.checked)
2915
3148
  );
3149
+ this.shadow.getElementById("start-hidden-toggle").addEventListener(
3150
+ "change",
3151
+ (e) => this.onStartHiddenChange(e.target.checked)
3152
+ );
2916
3153
  this.shadow.getElementById("selector-strategy").addEventListener(
2917
3154
  "change",
2918
3155
  (e) => this.onSelectorStrategyChange(e.target.value)
@@ -2944,26 +3181,38 @@ var ADVANCED_TEST_EDITOR_STYLES = `
2944
3181
  * { box-sizing: border-box; }
2945
3182
  .layout { display: flex; flex: 1; min-height: 0; }
2946
3183
  .sidebar {
2947
- width: 220px; border-right: 1px solid #21262d; overflow-y: auto;
2948
- padding: 8px; background: #0d1117; flex-shrink: 0;
3184
+ border-right: none; display: flex; flex-direction: column;
3185
+ background: #0d1117; flex-shrink: 0; min-width: 140px; max-width: 500px;
3186
+ }
3187
+ .tree-scroll {
3188
+ flex: 1; overflow-y: auto; padding: 6px 4px;
2949
3189
  scrollbar-width: thin; scrollbar-color: #30363d transparent;
2950
3190
  }
2951
- .sidebar::-webkit-scrollbar { width: 4px; }
2952
- .sidebar::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
2953
- .main { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #161b22; }
3191
+ .tree-scroll::-webkit-scrollbar { width: 4px; }
3192
+ .tree-scroll::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
3193
+ .resize-handle {
3194
+ width: 4px; background: #21262d; cursor: col-resize; flex-shrink: 0;
3195
+ transition: background 0.15s;
3196
+ }
3197
+ .resize-handle:hover, .resize-handle.dragging { background: #2f81f7; }
3198
+ .main { flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #161b22; min-width: 0; }
2954
3199
  .no-perm {
2955
3200
  padding: 28px; color: #484f58; font-size: 13px; text-align: center;
2956
3201
  display: flex; flex-direction: column; align-items: center; gap: 12px; line-height: 1.6;
2957
3202
  }
2958
3203
  .tree-item {
2959
- padding: 5px 8px; border-radius: 5px; cursor: pointer;
3204
+ padding: 4px 6px; border-radius: 4px; cursor: pointer;
2960
3205
  font-size: 12px; color: #8b949e; white-space: nowrap;
2961
3206
  overflow: hidden; text-overflow: ellipsis;
2962
3207
  transition: background 0.12s, color 0.12s;
3208
+ display: flex; align-items: center; gap: 4px; user-select: none;
2963
3209
  }
2964
3210
  .tree-item:hover { background: #161b22; color: #c9d1d9; }
2965
3211
  .tree-item.selected { background: rgba(47,129,247,0.12); color: #2f81f7; }
2966
3212
  .tree-item.dir { color: #e3b341; font-weight: 600; }
3213
+ .tree-item.dir:hover { background: rgba(227,179,65,0.08); color: #f0c040; }
3214
+ .dir-arrow { font-size: 8px; color: #484f58; width: 10px; flex-shrink: 0; line-height: 1; }
3215
+ .tree-connector { display: block; width: 1px; background: #21262d; margin-left: 13px; height: 100%; }
2967
3216
  .content-area {
2968
3217
  flex: 1; padding: 12px 14px; overflow-y: auto;
2969
3218
  scrollbar-width: thin; scrollbar-color: #30363d transparent;
@@ -2973,12 +3222,21 @@ var ADVANCED_TEST_EDITOR_STYLES = `
2973
3222
  .file-name { font-size: 11px; color: #484f58; margin-bottom: 8px; font-family: monospace; }
2974
3223
  pre {
2975
3224
  background: #0d1117; padding: 12px; border-radius: 8px;
2976
- font-size: 11px; color: #c9d1d9; overflow-x: auto;
2977
- white-space: pre-wrap; word-break: break-all; margin: 0;
3225
+ font-size: 11px; color: #c9d1d9; margin: 0;
2978
3226
  font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
2979
- line-height: 1.7; overflow-y: auto;
2980
- border: 1px solid #21262d;
3227
+ line-height: 1.7; border: 1px solid #21262d;
3228
+ white-space: pre; overflow-x: auto; overflow-y: auto;
3229
+ scrollbar-width: thin; scrollbar-color: #30363d transparent;
2981
3230
  }
3231
+ pre::-webkit-scrollbar { width: 4px; height: 4px; }
3232
+ pre::-webkit-scrollbar-thumb { background: #30363d; border-radius: 2px; }
3233
+ .pre-block { font-size: 10.5px; }
3234
+ .pre-icp .sh-str { color: #85e89d; }
3235
+ .sh-kw { color: #ff7b72; }
3236
+ .sh-bi { color: #d2a8ff; }
3237
+ .sh-str { color: #a5d6ff; }
3238
+ .sh-cmt { color: #8b949e; font-style: italic; }
3239
+ .sh-num { color: #79c0ff; }
2982
3240
  .footer {
2983
3241
  padding: 10px 14px; border-top: 1px solid #21262d;
2984
3242
  display: flex; gap: 8px; justify-content: flex-end; background: #161b22;
@@ -2994,6 +3252,12 @@ var ADVANCED_TEST_EDITOR_STYLES = `
2994
3252
  .btn-save:hover { background: #1f6feb; border-color: #1f6feb; color: #fff; }
2995
3253
  .btn-save:disabled { background: #21262d; border-color: #30363d; color: #8b949e; }
2996
3254
  .placeholder { color: #484f58; font-size: 13px; padding: 28px; text-align: center; }
3255
+ .test-notes {
3256
+ margin: 0 0 10px; padding: 8px 12px;
3257
+ background: rgba(47,129,247,0.06); border-left: 3px solid rgba(47,129,247,0.4);
3258
+ border-radius: 0 6px 6px 0; font-size: 12px; color: #8b949e;
3259
+ line-height: 1.6; white-space: pre-wrap; word-break: break-word;
3260
+ }
2997
3261
  .block-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
2998
3262
  .btn-copy {
2999
3263
  padding: 3px 10px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer;
@@ -3001,6 +3265,34 @@ var ADVANCED_TEST_EDITOR_STYLES = `
3001
3265
  transition: background 0.12s, color 0.12s; white-space: nowrap; flex-shrink: 0;
3002
3266
  }
3003
3267
  .btn-copy:hover { background: #30363d; color: #e6edf3; }
3268
+ .sidebar-toolbar {
3269
+ display: flex; gap: 4px; padding: 6px 6px 4px;
3270
+ border-bottom: 1px solid #21262d; flex-shrink: 0; background: #0d1117;
3271
+ }
3272
+ .btn-toolbar {
3273
+ flex: 1; padding: 4px 6px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer;
3274
+ font-size: 10px; font-weight: 500; background: #161b22; color: #8b949e;
3275
+ transition: background 0.12s, color 0.12s; white-space: nowrap; overflow: hidden;
3276
+ text-overflow: ellipsis;
3277
+ }
3278
+ .btn-toolbar:hover { background: #30363d; color: #e6edf3; }
3279
+ .btn-toolbar.btn-new { color: #3fb950; border-color: #238636; }
3280
+ .btn-toolbar.btn-new:hover { background: #238636; color: #fff; }
3281
+ .new-file-form {
3282
+ padding: 6px; border-bottom: 1px solid #21262d; display: flex; flex-direction: column; gap: 4px;
3283
+ }
3284
+ .new-file-form input {
3285
+ width: 100%; padding: 4px 7px; background: #0d1117; border: 1px solid #30363d;
3286
+ border-radius: 5px; color: #e6edf3; font-size: 11px; font-family: monospace;
3287
+ outline: none;
3288
+ }
3289
+ .new-file-form input:focus { border-color: #2f81f7; }
3290
+ .new-file-form .ext { font-size: 9px; color: #484f58; padding: 0 2px; }
3291
+ .new-file-actions { display: flex; gap: 4px; }
3292
+ .btn-confirm { flex: 1; padding: 3px 6px; border: 1px solid #238636; border-radius: 5px; cursor: pointer; font-size: 10px; font-weight: 500; background: #238636; color: #fff; transition: background 0.12s; }
3293
+ .btn-confirm:hover { background: #2ea043; }
3294
+ .btn-cancel-form { flex: 1; padding: 3px 6px; border: 1px solid #30363d; border-radius: 5px; cursor: pointer; font-size: 10px; font-weight: 500; background: #21262d; color: #8b949e; transition: background 0.12s; }
3295
+ .btn-cancel-form:hover { background: #30363d; color: #e6edf3; }
3004
3296
  `;
3005
3297
 
3006
3298
  // src/components/advanced-test-editor/advanced-test-editor.template.ts
@@ -3018,29 +3310,48 @@ function renderNoPermission(needsReauth, t) {
3018
3310
  </div>`;
3019
3311
  }
3020
3312
  function renderAdvancedEditor(state, t) {
3021
- const { e2eTree, selectedFile, selectedFileContent, testItBlock, interceptorsBlock, saveButtonEnabled } = state;
3022
- const treeHtml = e2eTree.length ? renderTree(e2eTree, selectedFile) : `<div class="tree-item" style="color:#6c7a99">${t("ADVANCED_EDITOR.NO_FILES")}</div>`;
3313
+ const { e2eTree, selectedFile, selectedFileContent, testItBlock, interceptorsBlock, testNotes, saveButtonEnabled, isCreatingFile, collapsedDirs, sidebarWidth } = state;
3314
+ const treeHtml = e2eTree.length ? renderTree(e2eTree, selectedFile, collapsedDirs) : `<div class="tree-item" style="color:#6c7a99">${t("ADVANCED_EDITOR.NO_FILES")}</div>`;
3023
3315
  const contentHtml = selectedFileContent ? `<div class="file-name">\u{1F4C4} ${escHtml(selectedFile?.name ?? "")}</div>
3024
- <pre>${escHtml(selectedFileContent.slice(0, 4e3))}${selectedFileContent.length > 4e3 ? "\n..." : ""}</pre>` : `<div class="placeholder">${t("ADVANCED_EDITOR.SELECT_FILE")}</div>`;
3316
+ <pre class="pre-file">${syntaxHighlight(selectedFileContent.slice(0, 6e3))}${selectedFileContent.length > 6e3 ? "\n..." : ""}</pre>` : `<div class="placeholder">${t("ADVANCED_EDITOR.SELECT_FILE")}</div>`;
3317
+ const notesHtml = testItBlock && testNotes ? `<p class="test-notes">${escHtml(testNotes)}</p>` : "";
3025
3318
  const itHtml = testItBlock ? `<div style="margin-top:10px">
3026
3319
  <div class="file-name block-header">
3027
3320
  <span>${t("ADVANCED_EDITOR.IT_LABEL")}</span>
3028
3321
  <button id="btn-copy-it" class="btn-copy">${t("ADVANCED_EDITOR.COPY_IT_BTN")}</button>
3029
3322
  </div>
3030
- <pre style="max-height:120px;font-size:10.5px">${escHtml(testItBlock.slice(0, 500))}</pre>
3323
+ <pre class="pre-block">${syntaxHighlight(testItBlock.slice(0, 3e3))}</pre>
3031
3324
  </div>` : "";
3032
3325
  const interceptorsHtml = interceptorsBlock ? `<div style="margin-top:10px">
3033
3326
  <div class="file-name block-header">
3034
3327
  <span>${t("ADVANCED_EDITOR.BEFORE_EACH_LABEL")}</span>
3035
3328
  <button id="btn-copy-interceptors" class="btn-copy">${t("ADVANCED_EDITOR.COPY_ICP_BTN")}</button>
3036
3329
  </div>
3037
- <pre style="max-height:120px;font-size:10.5px;color:#3fb950">${escHtml(interceptorsBlock.slice(0, 500))}</pre>
3330
+ <pre class="pre-block pre-icp">${syntaxHighlight(interceptorsBlock.slice(0, 3e3))}</pre>
3331
+ </div>` : "";
3332
+ const newFileForm = isCreatingFile ? `<div class="new-file-form">
3333
+ <input id="input-new-file" type="text" placeholder="${escHtml(t("ADVANCED_EDITOR.NEW_FILE_PLACEHOLDER"))}" autocomplete="off" />
3334
+ <span class="ext">.cy.ts</span>
3335
+ <div class="new-file-actions">
3336
+ <button id="btn-new-file-confirm" class="btn-confirm">${t("ADVANCED_EDITOR.NEW_FILE_CONFIRM")}</button>
3337
+ <button id="btn-new-file-cancel" class="btn-cancel-form">${t("ADVANCED_EDITOR.NEW_FILE_CANCEL")}</button>
3338
+ </div>
3038
3339
  </div>` : "";
3340
+ const toolbar = `
3341
+ <div class="sidebar-toolbar">
3342
+ <button id="btn-new-file" class="btn-toolbar btn-new">${t("ADVANCED_EDITOR.NEW_FILE_BTN")}</button>
3343
+ <button id="btn-refresh" class="btn-toolbar">${t("ADVANCED_EDITOR.REFRESH_BTN")}</button>
3344
+ </div>`;
3039
3345
  return `
3040
3346
  <div class="layout">
3041
- <div class="sidebar">${treeHtml}</div>
3347
+ <div class="sidebar" style="width:${sidebarWidth}px">
3348
+ ${toolbar}
3349
+ ${newFileForm}
3350
+ <div class="tree-scroll">${treeHtml}</div>
3351
+ </div>
3352
+ <div id="resize-handle" class="resize-handle"></div>
3042
3353
  <div class="main">
3043
- <div class="content-area">${contentHtml}${itHtml}${interceptorsHtml}</div>
3354
+ <div class="content-area">${contentHtml}${notesHtml}${itHtml}${interceptorsHtml}</div>
3044
3355
  <div class="footer">
3045
3356
  <button id="btn-save" class="btn-save"
3046
3357
  ${!saveButtonEnabled || !testItBlock ? "disabled" : ""}>
@@ -3054,16 +3365,24 @@ function renderAdvancedEditor(state, t) {
3054
3365
  </div>
3055
3366
  </div>`;
3056
3367
  }
3057
- function renderTree(nodes, selected, indent = 0) {
3368
+ function renderTree(nodes, selected, collapsedDirs, indent = 0, parentPath = "") {
3058
3369
  return nodes.map((n) => {
3370
+ const currentPath = parentPath ? `${parentPath}/${n.name}` : n.name;
3059
3371
  const isFile = n.kind === "file";
3060
- const isSel = selected === n || selected?.name === n.name;
3061
- const cls = `tree-item${isFile ? "" : " dir"}${isSel ? " selected" : ""}`;
3062
- const pad = `padding-left:${8 + indent * 14}px`;
3063
- if (!isFile && n.children?.length) {
3064
- return `<div class="${cls}" style="${pad}">\u{1F4C1} ${escHtml(n.name)}</div>
3065
- ${renderTree(n.children, selected, indent + 1)}`;
3372
+ const pad = `padding-left:${6 + indent * 14}px`;
3373
+ if (!isFile) {
3374
+ const dir = n;
3375
+ const hasChildren = (dir.children?.length ?? 0) > 0;
3376
+ const isCollapsed = collapsedDirs.has(currentPath);
3377
+ const arrow = hasChildren ? isCollapsed ? "\u25B6" : "\u25BC" : "\xB7";
3378
+ const dirHtml = `<div class="tree-item dir" style="${pad}" data-dir-path="${escAttr(currentPath)}">
3379
+ <span class="dir-arrow">${arrow}</span>\u{1F4C1} ${escHtml(n.name)}
3380
+ </div>`;
3381
+ if (!hasChildren || isCollapsed) return dirHtml;
3382
+ return dirHtml + renderTree(dir.children, selected, collapsedDirs, indent + 1, currentPath);
3066
3383
  }
3384
+ const isSel = selected?.name === n.name;
3385
+ const cls = `tree-item${isSel ? " selected" : ""}`;
3067
3386
  const data = JSON.stringify({ kind: n.kind, name: n.name }).replace(/"/g, "&quot;");
3068
3387
  return `<div class="${cls}" style="${pad}" data-file="${data}">\u{1F4C4} ${escHtml(n.name)}</div>`;
3069
3388
  }).join("");
@@ -3093,12 +3412,17 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3093
3412
  selectedFileContent = null;
3094
3413
  testItBlock = "";
3095
3414
  interceptorsBlock = "";
3415
+ testNotes = "";
3096
3416
  isPreviewMode = false;
3097
3417
  previewFileName = null;
3098
3418
  previewFileContent = null;
3419
+ isCreatingFile = false;
3420
+ collapsedDirs = /* @__PURE__ */ new Set();
3421
+ sidebarWidth = 220;
3099
3422
  hasPermission = false;
3100
3423
  needsReauth = false;
3101
3424
  _dirHandle = null;
3425
+ _e2eHandle = null;
3102
3426
  constructor() {
3103
3427
  super();
3104
3428
  this.shadow = this.attachShadow({ mode: "open" });
@@ -3140,7 +3464,8 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3140
3464
  try {
3141
3465
  for await (const entry of this._dirHandle.values()) {
3142
3466
  if (entry.kind === "directory" && entry.name === "e2e") {
3143
- const tree = await this.transformationService.scanDirectory(entry);
3467
+ this._e2eHandle = entry;
3468
+ const tree = await this.transformationService.scanDirectory(this._e2eHandle);
3144
3469
  this.e2eTree = tree.children ?? [];
3145
3470
  this.render();
3146
3471
  return;
@@ -3149,13 +3474,40 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3149
3474
  } catch {
3150
3475
  }
3151
3476
  }
3477
+ async createNewFile(rawName) {
3478
+ if (!this._e2eHandle) return;
3479
+ const name = rawName.trim().replace(/\.cy\.ts$/, "");
3480
+ if (!name) return;
3481
+ const fileName = `${name}.cy.ts`;
3482
+ const template = `describe('${name}', () => {
3483
+
3484
+ it('should ', () => {
3485
+
3486
+ });
3487
+
3488
+ });
3489
+ `;
3490
+ try {
3491
+ const fileHandle = await this._e2eHandle.getFileHandle(fileName, { create: true });
3492
+ const writable = await fileHandle.createWritable();
3493
+ await writable.write(template);
3494
+ await writable.close();
3495
+ } catch {
3496
+ }
3497
+ this.isCreatingFile = false;
3498
+ await this.getFoldersData();
3499
+ }
3500
+ async refreshTree() {
3501
+ await this.getFoldersData();
3502
+ }
3152
3503
  async saveCommandsToFile() {
3153
3504
  if (!this.selectedFileHandle || !this.selectedFileContent || !this.testItBlock) return;
3154
3505
  let content = this.selectedFileContent;
3155
3506
  if (this.interceptorsBlock) {
3156
3507
  content = this.transformationService.insertBeforeEach(content, this.interceptorsBlock);
3157
3508
  }
3158
- content = this.transformationService.insertItBlock(content, this.testItBlock);
3509
+ const comment = this.testNotes ? this.transformationService.buildBlockComment(this.testNotes) + "\n" : "";
3510
+ content = this.transformationService.insertItBlock(content, comment + this.testItBlock);
3159
3511
  if (!content) return;
3160
3512
  const writable = await this.selectedFileHandle.createWritable();
3161
3513
  await writable.write(content);
@@ -3182,9 +3534,11 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3182
3534
  if (test) {
3183
3535
  this.testItBlock = test.itBlock ?? "";
3184
3536
  this.interceptorsBlock = test.interceptorsBlock ?? "";
3537
+ this.testNotes = test.notes ?? "";
3185
3538
  } else {
3186
3539
  this.testItBlock = "";
3187
3540
  this.interceptorsBlock = "";
3541
+ this.testNotes = "";
3188
3542
  }
3189
3543
  this.render();
3190
3544
  }
@@ -3206,6 +3560,44 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3206
3560
  composed: true
3207
3561
  }));
3208
3562
  }
3563
+ toggleDir(path) {
3564
+ if (this.collapsedDirs.has(path)) {
3565
+ this.collapsedDirs.delete(path);
3566
+ } else {
3567
+ this.collapsedDirs.add(path);
3568
+ }
3569
+ this.render();
3570
+ }
3571
+ setupResizeHandle() {
3572
+ const handle = this.shadow.getElementById("resize-handle");
3573
+ const sidebar = this.shadow.querySelector(".sidebar");
3574
+ if (!handle || !sidebar) return;
3575
+ let startX = 0;
3576
+ let startWidth = 0;
3577
+ const onMouseMove = (e) => {
3578
+ const delta = e.clientX - startX;
3579
+ const newWidth = Math.max(140, Math.min(500, startWidth + delta));
3580
+ this.sidebarWidth = newWidth;
3581
+ sidebar.style.width = `${newWidth}px`;
3582
+ };
3583
+ const onMouseUp = () => {
3584
+ document.removeEventListener("mousemove", onMouseMove);
3585
+ document.removeEventListener("mouseup", onMouseUp);
3586
+ document.body.style.cursor = "";
3587
+ document.body.style.userSelect = "";
3588
+ handle.classList.remove("dragging");
3589
+ };
3590
+ handle.addEventListener("mousedown", (e) => {
3591
+ startX = e.clientX;
3592
+ startWidth = sidebar.offsetWidth;
3593
+ document.addEventListener("mousemove", onMouseMove);
3594
+ document.addEventListener("mouseup", onMouseUp);
3595
+ document.body.style.cursor = "col-resize";
3596
+ document.body.style.userSelect = "none";
3597
+ handle.classList.add("dragging");
3598
+ e.preventDefault();
3599
+ });
3600
+ }
3209
3601
  closePreview() {
3210
3602
  this.isPreviewMode = false;
3211
3603
  this.previewFileName = null;
@@ -3236,7 +3628,11 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3236
3628
  selectedFileContent: this.selectedFileContent,
3237
3629
  testItBlock: this.testItBlock,
3238
3630
  interceptorsBlock: this.interceptorsBlock,
3239
- saveButtonEnabled: this.saveButtonEnabled
3631
+ testNotes: this.testNotes,
3632
+ saveButtonEnabled: this.saveButtonEnabled,
3633
+ isCreatingFile: this.isCreatingFile,
3634
+ collapsedDirs: this.collapsedDirs,
3635
+ sidebarWidth: this.sidebarWidth
3240
3636
  }, this.t.bind(this))}`;
3241
3637
  this.shadow.querySelectorAll("[data-file]").forEach((el) => {
3242
3638
  el.addEventListener("click", () => {
@@ -3244,11 +3640,41 @@ var AdvancedTestEditorElement = class extends HTMLElement {
3244
3640
  if (data) this.onFileClick(JSON.parse(data));
3245
3641
  });
3246
3642
  });
3643
+ this.shadow.querySelectorAll("[data-dir-path]").forEach((el) => {
3644
+ el.addEventListener("click", () => {
3645
+ const path = el.dataset["dirPath"];
3646
+ if (path) this.toggleDir(path);
3647
+ });
3648
+ });
3649
+ this.setupResizeHandle();
3247
3650
  this.shadow.getElementById("btn-save")?.addEventListener("click", () => this.saveCommandsToFile());
3248
3651
  this.shadow.getElementById("btn-edit")?.addEventListener("click", () => this.openEditManually());
3249
3652
  this.shadow.getElementById("btn-copy-it")?.addEventListener("click", () => this.copyToClipboard(this.testItBlock));
3250
3653
  this.shadow.getElementById("btn-copy-interceptors")?.addEventListener("click", () => this.copyToClipboard(this.interceptorsBlock));
3251
3654
  this.shadow.getElementById("btn-close")?.addEventListener("click", () => this.closePreview());
3655
+ this.shadow.getElementById("btn-new-file")?.addEventListener("click", () => {
3656
+ this.isCreatingFile = !this.isCreatingFile;
3657
+ this.render();
3658
+ if (this.isCreatingFile) this.shadow.getElementById("input-new-file")?.focus();
3659
+ });
3660
+ this.shadow.getElementById("btn-refresh")?.addEventListener("click", () => this.refreshTree());
3661
+ this.shadow.getElementById("btn-new-file-confirm")?.addEventListener("click", () => {
3662
+ const input2 = this.shadow.getElementById("input-new-file");
3663
+ this.createNewFile(input2?.value ?? "");
3664
+ });
3665
+ this.shadow.getElementById("btn-new-file-cancel")?.addEventListener("click", () => {
3666
+ this.isCreatingFile = false;
3667
+ this.render();
3668
+ });
3669
+ const input = this.shadow.getElementById("input-new-file");
3670
+ input?.addEventListener("keydown", (e) => {
3671
+ if (e.key === "Enter") {
3672
+ this.createNewFile(input.value);
3673
+ } else if (e.key === "Escape") {
3674
+ this.isCreatingFile = false;
3675
+ this.render();
3676
+ }
3677
+ });
3252
3678
  }
3253
3679
  };
3254
3680
  if (!customElements.get("advanced-test-editor")) {
@@ -3755,6 +4181,7 @@ function renderRecorderWidget(rec, paused, t) {
3755
4181
  // src/components/lib-e2e-recorder/lib-e2e-recorder.ts
3756
4182
  var LibE2eRecorderElement = class extends HTMLElement {
3757
4183
  shadow;
4184
+ _isDisabled = false;
3758
4185
  keydownHandler;
3759
4186
  recordingUnsub;
3760
4187
  commandsUnsub;
@@ -3768,6 +4195,7 @@ var LibE2eRecorderElement = class extends HTMLElement {
3768
4195
  recording;
3769
4196
  persistence;
3770
4197
  translation;
4198
+ isVisible = false;
3771
4199
  isRecording = false;
3772
4200
  isPaused = false;
3773
4201
  cypressCommands = [];
@@ -3782,6 +4210,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
3782
4210
  this.shadow = this.attachShadow({ mode: "open" });
3783
4211
  }
3784
4212
  connectedCallback() {
4213
+ if ("Cypress" in window) {
4214
+ this._isDisabled = true;
4215
+ return;
4216
+ }
3785
4217
  if (!this.getAttribute("data-cy")) {
3786
4218
  this.setAttribute("data-cy", "lib-e2e-cypress-for-dummys");
3787
4219
  }
@@ -3798,10 +4230,12 @@ var LibE2eRecorderElement = class extends HTMLElement {
3798
4230
  this.initSelectorStrategy();
3799
4231
  this.initSubscriptions();
3800
4232
  this.render();
4233
+ this.initVisibility();
3801
4234
  this.keydownHandler = (e) => this.handleKeyboardEvent(e);
3802
4235
  window.addEventListener("keydown", this.keydownHandler);
3803
4236
  }
3804
4237
  disconnectedCallback() {
4238
+ if (this._isDisabled) return;
3805
4239
  window.removeEventListener("keydown", this.keydownHandler);
3806
4240
  this.recordingUnsub?.();
3807
4241
  this.commandsUnsub?.();
@@ -3862,6 +4296,16 @@ var LibE2eRecorderElement = class extends HTMLElement {
3862
4296
  document.body.appendChild(picker);
3863
4297
  });
3864
4298
  }
4299
+ async initVisibility() {
4300
+ if (this.hasAttribute("start-hidden")) {
4301
+ this.isVisible = this.getAttribute("start-hidden") === "false";
4302
+ this.style.display = this.isVisible ? "" : "none";
4303
+ return;
4304
+ }
4305
+ const config = await this.persistence.getGeneralConfig();
4306
+ this.isVisible = config?.["startHidden"] !== "true";
4307
+ this.style.display = this.isVisible ? "" : "none";
4308
+ }
3865
4309
  async initSelectorStrategy() {
3866
4310
  const config = await this.persistence.getGeneralConfig();
3867
4311
  const strategy = config?.["selectorStrategy"];
@@ -3881,6 +4325,12 @@ var LibE2eRecorderElement = class extends HTMLElement {
3881
4325
  handleKeyboardEvent(event) {
3882
4326
  if (!event.ctrlKey) return;
3883
4327
  const key = event.key.toLowerCase();
4328
+ if (key === "e" && event.shiftKey) {
4329
+ event.preventDefault();
4330
+ this.toggleVisibility();
4331
+ return;
4332
+ }
4333
+ if (!this.isVisible) return;
3884
4334
  if (key === "r") {
3885
4335
  event.preventDefault();
3886
4336
  this.toggle();
@@ -3898,6 +4348,10 @@ var LibE2eRecorderElement = class extends HTMLElement {
3898
4348
  this.showSettingsDialog();
3899
4349
  }
3900
4350
  }
4351
+ toggleVisibility() {
4352
+ this.isVisible = !this.isVisible;
4353
+ this.style.display = this.isVisible ? "" : "none";
4354
+ }
3901
4355
  // ── recording history (task 5) ────────────────────────────────────────────
3902
4356
  saveRecordingHistory() {
3903
4357
  if (!this.cypressCommands.length) return;
@@ -4133,13 +4587,13 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4133
4587
  child.translation = this.translation;
4134
4588
  container.appendChild(child);
4135
4589
  child.addEventListener("savetest", (e) => {
4136
- const { description, tags } = e.detail ?? {};
4137
- this.onSaveTest(description ?? null, tags ?? []);
4590
+ const { description, notes, tags } = e.detail ?? {};
4591
+ this.onSaveTest(description ?? null, tags ?? [], notes ?? "");
4138
4592
  Swal.close();
4139
4593
  });
4140
4594
  child.addEventListener("saveandexport", (e) => {
4141
- const { description, tags } = e.detail ?? {};
4142
- this.onSaveAndExportTest(description ?? null, tags ?? []);
4595
+ const { description, notes, tags } = e.detail ?? {};
4596
+ this.onSaveAndExportTest(description ?? null, tags ?? [], notes ?? "");
4143
4597
  Swal.close();
4144
4598
  });
4145
4599
  },
@@ -4170,6 +4624,10 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4170
4624
  child.addEventListener("smartselectorchange", (e) => {
4171
4625
  this.smartSelectorEnabled = e.detail;
4172
4626
  });
4627
+ child.addEventListener("starthiddenchange", (e) => {
4628
+ this.isVisible = !e.detail;
4629
+ this.style.display = this.isVisible ? "" : "none";
4630
+ });
4173
4631
  },
4174
4632
  willClose: () => {
4175
4633
  this.isSettingsDialogOpen = false;
@@ -4182,7 +4640,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4182
4640
  this.toggleModal("isAdvancedEditorDialogOpen", () => {
4183
4641
  Swal.fire({
4184
4642
  title: this.translation.translate("MAIN_FRAME.SHOW_ADVANCED_EDITOR"),
4185
- html: '<div id="advanced-editor-modal-content" style="min-height:300px;padding:0"></div>',
4643
+ html: '<div id="advanced-editor-modal-content" style="flex:1;min-height:0;display:flex;flex-direction:column;padding:0"></div>',
4186
4644
  showCloseButton: true,
4187
4645
  showConfirmButton: false,
4188
4646
  allowOutsideClick: false,
@@ -4191,6 +4649,20 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4191
4649
  didOpen: () => {
4192
4650
  makeSwalDraggable();
4193
4651
  setSwal2DataCyAttribute();
4652
+ const popup = Swal.getPopup();
4653
+ if (popup) {
4654
+ popup.style.height = "600px";
4655
+ const htmlContainer = popup.querySelector(".swal2-html-container");
4656
+ if (htmlContainer) {
4657
+ htmlContainer.style.flex = "1";
4658
+ htmlContainer.style.minHeight = "0";
4659
+ htmlContainer.style.overflow = "hidden";
4660
+ htmlContainer.style.padding = "0";
4661
+ htmlContainer.style.margin = "0";
4662
+ htmlContainer.style.display = "flex";
4663
+ htmlContainer.style.flexDirection = "column";
4664
+ }
4665
+ }
4194
4666
  const container = document.getElementById("advanced-editor-modal-content");
4195
4667
  if (!container) return;
4196
4668
  const child = document.createElement("advanced-test-editor");
@@ -4218,7 +4690,10 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4218
4690
  this.isAdvancedEditorDialogOpen = false;
4219
4691
  }
4220
4692
  });
4221
- this.resizePopup();
4693
+ setTimeout(() => {
4694
+ const popup = Swal.getPopup();
4695
+ if (popup) makeModalResizable(popup, { minWidth: 400, minHeight: 0 });
4696
+ }, 0);
4222
4697
  });
4223
4698
  }
4224
4699
  showFileEditorDialog(handle, content, fileName, testId, itBlock = "", interceptorsBlock = "") {
@@ -4264,18 +4739,18 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4264
4739
  this.resizePopup();
4265
4740
  }
4266
4741
  // ── save handlers ─────────────────────────────────────────────────────────
4267
- async onSaveTest(description, tags = []) {
4742
+ async onSaveTest(description, tags = [], notes = "") {
4268
4743
  if (description) {
4269
- await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags);
4744
+ await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes);
4270
4745
  this.recording.clearCommands();
4271
4746
  this.clearRecordingHistory();
4272
4747
  this.cypressCommands = [];
4273
4748
  this.interceptors = [];
4274
4749
  }
4275
4750
  }
4276
- async onSaveAndExportTest(description, tags = []) {
4751
+ async onSaveAndExportTest(description, tags = [], notes = "") {
4277
4752
  if (description) {
4278
- const id = await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags);
4753
+ const id = await this.persistence.insertTest(description, this.cypressCommands, this.interceptors, tags, notes);
4279
4754
  this.recording.clearCommands();
4280
4755
  this.clearRecordingHistory();
4281
4756
  this.cypressCommands = [];
@@ -4302,6 +4777,7 @@ cypress/ <span style="color:#484f58">${this.translation.translate("RECOR
4302
4777
  render() {
4303
4778
  const rec = this.isRecording;
4304
4779
  const paused = this.isPaused;
4780
+ this.style.display = this.isVisible ? "" : "none";
4305
4781
  this.shadow.innerHTML = `<style>${getRecorderStyles(rec, paused)}</style>${renderRecorderWidget(rec, paused, this.translation.translate.bind(this.translation))}`;
4306
4782
  this.shadow.querySelector('[data-action="toggle"]')?.addEventListener("click", () => this.toggle());
4307
4783
  this.shadow.querySelector('[data-action="pause"]')?.addEventListener("click", () => this.togglePause());