@acutis/cli 0.1.2 → 0.2.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.
Files changed (200) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/AC3CibKK.js +1 -0
  3. package/.output/public/_nuxt/BGc2VNce.js +1 -0
  4. package/.output/public/_nuxt/BN4XOpVt.js +1 -0
  5. package/.output/public/_nuxt/BNJ2GN0A.js +1 -0
  6. package/.output/public/_nuxt/BP52ABGx.js +1 -0
  7. package/.output/public/_nuxt/BWcsrofD.js +1 -0
  8. package/.output/public/_nuxt/{b5tj-z25.js → Bqp63OdK.js} +1 -1
  9. package/.output/public/_nuxt/CF8y5KTS.js +3 -0
  10. package/.output/public/_nuxt/CUTaKHid.js +1 -0
  11. package/.output/public/_nuxt/CZEhodn6.js +1 -0
  12. package/.output/public/_nuxt/Cym5RBDq.js +1 -0
  13. package/.output/public/_nuxt/D1ge6Bwo.js +1 -0
  14. package/.output/public/_nuxt/D2UpbqeN.js +28 -0
  15. package/.output/public/_nuxt/DDKd6Dxq.js +1 -0
  16. package/.output/public/_nuxt/D_FJunXs.js +64 -0
  17. package/.output/public/_nuxt/Dc2A-6xl.js +6 -0
  18. package/.output/public/_nuxt/DoQkJV4U.js +1 -0
  19. package/.output/public/_nuxt/JwPm-b3T.js +1 -0
  20. package/.output/public/_nuxt/builds/latest.json +1 -1
  21. package/.output/public/_nuxt/builds/meta/45a88f3e-8555-4d4c-bdac-4d0788b1fa61.json +1 -0
  22. package/.output/public/_nuxt/cnFSOQCu.js +1 -0
  23. package/.output/public/_nuxt/entry.BmTN_nlL.css +2 -0
  24. package/.output/public/_nuxt/error-404.CkhL7nxg.css +1 -0
  25. package/.output/public/_nuxt/error-500.DNftkU4A.css +1 -0
  26. package/.output/public/_nuxt/tags.C6gBUbEB.css +1 -0
  27. package/.output/server/chunks/_/auth2.mjs +3 -3
  28. package/.output/server/chunks/_/environment.mjs +3 -3
  29. package/.output/server/chunks/_/error-500.mjs.map +1 -1
  30. package/.output/server/chunks/_/generation.mjs +3 -3
  31. package/.output/server/chunks/_/project.mjs +2 -2
  32. package/.output/server/chunks/_/project.service.mjs +3 -31
  33. package/.output/server/chunks/_/project.service.mjs.map +1 -1
  34. package/.output/server/chunks/_/recording.mjs +5 -2
  35. package/.output/server/chunks/_/recording.mjs.map +1 -1
  36. package/.output/server/chunks/_/scenario.mjs +4 -2
  37. package/.output/server/chunks/_/scenario.mjs.map +1 -1
  38. package/.output/server/chunks/_/scenario2.mjs +22 -5
  39. package/.output/server/chunks/_/scenario2.mjs.map +1 -1
  40. package/.output/server/chunks/_/spec-rules.mjs +29 -232
  41. package/.output/server/chunks/_/spec-rules.mjs.map +1 -1
  42. package/.output/server/chunks/build/{Alert-Q5hBYuxt.mjs → Alert-WVcujYrW.mjs} +14 -14
  43. package/.output/server/chunks/build/Alert-WVcujYrW.mjs.map +1 -0
  44. package/.output/server/chunks/build/{Container-HW0bT_Wn.mjs → Container-C0k092cW.mjs} +4 -4
  45. package/.output/server/chunks/build/Container-C0k092cW.mjs.map +1 -0
  46. package/.output/server/chunks/build/{Pagination-FFamqWKD.mjs → Pagination-BXryRfUN.mjs} +4 -4
  47. package/.output/server/chunks/build/{Pagination-FFamqWKD.mjs.map → Pagination-BXryRfUN.mjs.map} +1 -1
  48. package/.output/server/chunks/build/{_...scenario_-DNqQ06NM.mjs → _...scenario_-qp56INdQ.mjs} +687 -149
  49. package/.output/server/chunks/build/_...scenario_-qp56INdQ.mjs.map +1 -0
  50. package/.output/server/chunks/build/{_slug_-BlLRYU9d.mjs → _slug_-dNDiCabc.mjs} +788 -433
  51. package/.output/server/chunks/build/_slug_-dNDiCabc.mjs.map +1 -0
  52. package/.output/server/chunks/build/{error-404-Dzfp7Wik.mjs → error-404-B5fzVPdQ.mjs} +5 -5
  53. package/.output/server/chunks/build/error-404-B5fzVPdQ.mjs.map +1 -0
  54. package/.output/server/chunks/build/error-404-styles.C6Lq0aSH.mjs +9 -0
  55. package/.output/server/chunks/build/error-404-styles.C6Lq0aSH.mjs.map +1 -0
  56. package/.output/server/chunks/build/{error-500-Df60mzLr.mjs → error-500-Be62wrWs.mjs} +5 -5
  57. package/.output/server/chunks/build/error-500-Be62wrWs.mjs.map +1 -0
  58. package/.output/server/chunks/build/error-500-styles.D3b6pwL1.mjs +9 -0
  59. package/.output/server/chunks/build/error-500-styles.D3b6pwL1.mjs.map +1 -0
  60. package/.output/server/chunks/build/{origin-_Ab6zkKk.mjs → origin-DFFT7pGA.mjs} +546 -21
  61. package/.output/server/chunks/build/{origin-_Ab6zkKk.mjs.map → origin-DFFT7pGA.mjs.map} +1 -1
  62. package/.output/server/chunks/build/{pages-BuW8VoW3.mjs → pages-NkoKH95O.mjs} +35 -37
  63. package/.output/server/chunks/build/pages-NkoKH95O.mjs.map +1 -0
  64. package/.output/server/chunks/build/tags-DXumXgr9.mjs +1653 -0
  65. package/.output/server/chunks/build/tags-DXumXgr9.mjs.map +1 -0
  66. package/.output/server/chunks/build/telemetria-BaMsUnJ8.mjs +131 -0
  67. package/.output/server/chunks/build/telemetria-BaMsUnJ8.mjs.map +1 -0
  68. package/.output/server/chunks/build/timeline-styles.CzOXKr8R.mjs +9 -0
  69. package/.output/server/chunks/build/timeline-styles.CzOXKr8R.mjs.map +1 -0
  70. package/.output/server/chunks/nitro/nitro.mjs +618 -377
  71. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  72. package/.output/server/chunks/raw/driver-entry.mjs +1 -1
  73. package/.output/server/chunks/routes/api/projects/_slug/auth/record.post.mjs +1 -1
  74. package/.output/server/chunks/routes/api/projects/_slug/auth/skip.post.mjs +1 -1
  75. package/.output/server/chunks/routes/api/projects/_slug/auth.get.mjs +1 -1
  76. package/.output/server/chunks/routes/api/projects/_slug/auth.put.mjs +1 -1
  77. package/.output/server/chunks/routes/api/telemetry/report.post.mjs +133 -0
  78. package/.output/server/chunks/routes/api/telemetry/report.post.mjs.map +1 -0
  79. package/.output/server/chunks/routes/debug/close-page.post.mjs +40 -0
  80. package/.output/server/chunks/routes/debug/close-page.post.mjs.map +1 -0
  81. package/.output/server/chunks/routes/debug/erro-de-telemetria.get.mjs +49 -0
  82. package/.output/server/chunks/routes/debug/erro-de-telemetria.get.mjs.map +1 -0
  83. package/.output/server/chunks/routes/debug/replay-decision.post.mjs +43 -0
  84. package/.output/server/chunks/routes/debug/replay-decision.post.mjs.map +1 -0
  85. package/.output/server/chunks/routes/renderer.mjs +2 -2
  86. package/.output/server/chunks/routes/renderer.mjs.map +1 -1
  87. package/.output/server/chunks/routes/runner/video.get.mjs +1 -1
  88. package/.output/server/chunks/routes/ws.mjs +19 -2
  89. package/.output/server/chunks/routes/ws.mjs.map +1 -1
  90. package/.output/server/chunks/virtual/entry.mjs +11755 -11725
  91. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  92. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  93. package/.output/server/chunks/virtual/styles.mjs +4 -3
  94. package/.output/server/index.mjs +1 -1
  95. package/.output/server/package.json +1 -1
  96. package/bin/splash.mjs +2 -2
  97. package/dist-ui/driver-entry.js +3 -3
  98. package/package.json +5 -1
  99. package/reporters/stream-reporter.cjs +3 -3
  100. package/.output/public/_nuxt/5GgR1mbX.js +0 -1
  101. package/.output/public/_nuxt/ANcp5QFz.js +0 -1
  102. package/.output/public/_nuxt/BEFJRHhA2.js +0 -1
  103. package/.output/public/_nuxt/BEQgcp5R.js +0 -1
  104. package/.output/public/_nuxt/BEk00u4_.js +0 -1
  105. package/.output/public/_nuxt/BGdmRvCK2.js +0 -8
  106. package/.output/public/_nuxt/BMLYuU_e.js +0 -1
  107. package/.output/public/_nuxt/BWJpSLJL.js +0 -1
  108. package/.output/public/_nuxt/BXpPYfMx.js +0 -1
  109. package/.output/public/_nuxt/BbjTtCyI.js +0 -3
  110. package/.output/public/_nuxt/Bp3A7I5W2.js +0 -1
  111. package/.output/public/_nuxt/BuOb9NX0.js +0 -1
  112. package/.output/public/_nuxt/C1tNhfTr.js +0 -1
  113. package/.output/public/_nuxt/C2KIeuhG.js +0 -1
  114. package/.output/public/_nuxt/C5uJN_MU.js +0 -1
  115. package/.output/public/_nuxt/CBVnImq2.js +0 -1
  116. package/.output/public/_nuxt/CC0OsuIz.js +0 -28
  117. package/.output/public/_nuxt/CIFC2HNk.js +0 -1
  118. package/.output/public/_nuxt/CKps1Vc12.js +0 -3
  119. package/.output/public/_nuxt/CRH0iqnQ.js +0 -1
  120. package/.output/public/_nuxt/CTLrtTtn2.js +0 -64
  121. package/.output/public/_nuxt/CTqLwyQP.js +0 -1
  122. package/.output/public/_nuxt/CU7sPrmY.js +0 -3
  123. package/.output/public/_nuxt/CfOqJVtU.js +0 -1
  124. package/.output/public/_nuxt/Ch1EeKHP.js +0 -1
  125. package/.output/public/_nuxt/Clv7nJ_T2.js +0 -1
  126. package/.output/public/_nuxt/D-7v7DZs.js +0 -1
  127. package/.output/public/_nuxt/D4ykC_lY2.js +0 -1
  128. package/.output/public/_nuxt/D5HF1Z0f2.js +0 -1
  129. package/.output/public/_nuxt/DDHUGtv32.js +0 -4
  130. package/.output/public/_nuxt/DEJ3sRrh2.js +0 -1
  131. package/.output/public/_nuxt/DPM_BYFz2.js +0 -1
  132. package/.output/public/_nuxt/DXLPf3t6.js +0 -1
  133. package/.output/public/_nuxt/Dpnaldxf2.js +0 -1
  134. package/.output/public/_nuxt/DtuQPKVG.js +0 -1
  135. package/.output/public/_nuxt/FvWrDx_z.js +0 -1
  136. package/.output/public/_nuxt/Gcazu-lR.js +0 -1
  137. package/.output/public/_nuxt/QcKfgokC.js +0 -1
  138. package/.output/public/_nuxt/XDsaI4JL.js +0 -1
  139. package/.output/public/_nuxt/builds/meta/bf2cb2c6-2ddb-4f64-9777-e314571fd19d.json +0 -1
  140. package/.output/public/_nuxt/entry.m-nAFUcz.css +0 -2
  141. package/.output/public/_nuxt/error-404.DopINOT1.css +0 -1
  142. package/.output/public/_nuxt/error-500.D7goJC9g.css +0 -1
  143. package/.output/public/_nuxt/f0o80Gcm.js +0 -1
  144. package/.output/public/_nuxt/k5q7GKnv.js +0 -1
  145. package/.output/public/_nuxt/modal.3A_G7d4A.css +0 -1
  146. package/.output/public/_nuxt/tLr-SJaf.js +0 -1
  147. package/.output/public/dev/sample-run.webm +0 -0
  148. package/.output/server/chunks/build/Alert-Q5hBYuxt.mjs.map +0 -1
  149. package/.output/server/chunks/build/Badge-BgyIJKeU.mjs +0 -394
  150. package/.output/server/chunks/build/Badge-BgyIJKeU.mjs.map +0 -1
  151. package/.output/server/chunks/build/Card-DLzZ816u.mjs +0 -144
  152. package/.output/server/chunks/build/Card-DLzZ816u.mjs.map +0 -1
  153. package/.output/server/chunks/build/Container-HW0bT_Wn.mjs.map +0 -1
  154. package/.output/server/chunks/build/_...scenario_-DNqQ06NM.mjs.map +0 -1
  155. package/.output/server/chunks/build/_slug_-BlLRYU9d.mjs.map +0 -1
  156. package/.output/server/chunks/build/auth-status-5sNoLTPk.mjs +0 -128
  157. package/.output/server/chunks/build/auth-status-5sNoLTPk.mjs.map +0 -1
  158. package/.output/server/chunks/build/auth-status-Bo2gxZ-H.mjs +0 -53
  159. package/.output/server/chunks/build/auth-status-Bo2gxZ-H.mjs.map +0 -1
  160. package/.output/server/chunks/build/empty-xO5aD2Ip.mjs +0 -44
  161. package/.output/server/chunks/build/empty-xO5aD2Ip.mjs.map +0 -1
  162. package/.output/server/chunks/build/error-404-Dzfp7Wik.mjs.map +0 -1
  163. package/.output/server/chunks/build/error-404-styles.ByPbpRGS.mjs +0 -9
  164. package/.output/server/chunks/build/error-404-styles.ByPbpRGS.mjs.map +0 -1
  165. package/.output/server/chunks/build/error-500-Df60mzLr.mjs.map +0 -1
  166. package/.output/server/chunks/build/error-500-styles.Dz8enkTW.mjs +0 -9
  167. package/.output/server/chunks/build/error-500-styles.Dz8enkTW.mjs.map +0 -1
  168. package/.output/server/chunks/build/generation-warnings-RCB9uLi8.mjs +0 -123
  169. package/.output/server/chunks/build/generation-warnings-RCB9uLi8.mjs.map +0 -1
  170. package/.output/server/chunks/build/history-D-mei_D6.mjs +0 -163
  171. package/.output/server/chunks/build/history-D-mei_D6.mjs.map +0 -1
  172. package/.output/server/chunks/build/modal-81h_kbj9.mjs +0 -321
  173. package/.output/server/chunks/build/modal-81h_kbj9.mjs.map +0 -1
  174. package/.output/server/chunks/build/modal-Cpj-bQgu.mjs +0 -342
  175. package/.output/server/chunks/build/modal-Cpj-bQgu.mjs.map +0 -1
  176. package/.output/server/chunks/build/modal-DV_DQ0hO.mjs +0 -659
  177. package/.output/server/chunks/build/modal-DV_DQ0hO.mjs.map +0 -1
  178. package/.output/server/chunks/build/pages-BuW8VoW3.mjs.map +0 -1
  179. package/.output/server/chunks/build/phrases-CSDEYSbZ.mjs +0 -38
  180. package/.output/server/chunks/build/phrases-CSDEYSbZ.mjs.map +0 -1
  181. package/.output/server/chunks/build/project-run-filtered-CplHFc5x.mjs +0 -455
  182. package/.output/server/chunks/build/project-run-filtered-CplHFc5x.mjs.map +0 -1
  183. package/.output/server/chunks/build/run-stream-CNVDpZ_S.mjs +0 -157
  184. package/.output/server/chunks/build/run-stream-CNVDpZ_S.mjs.map +0 -1
  185. package/.output/server/chunks/build/scenario-runs-C7TDC5iA.mjs +0 -135
  186. package/.output/server/chunks/build/scenario-runs-C7TDC5iA.mjs.map +0 -1
  187. package/.output/server/chunks/build/scenario-suggestions-CsQg875h.mjs +0 -210
  188. package/.output/server/chunks/build/scenario-suggestions-CsQg875h.mjs.map +0 -1
  189. package/.output/server/chunks/build/scenario-test-run-Deg4Qlme.mjs +0 -505
  190. package/.output/server/chunks/build/scenario-test-run-Deg4Qlme.mjs.map +0 -1
  191. package/.output/server/chunks/build/steps-CcAjZqHX.mjs +0 -54
  192. package/.output/server/chunks/build/steps-CcAjZqHX.mjs.map +0 -1
  193. package/.output/server/chunks/build/tags-DrKY6Ah4.mjs +0 -594
  194. package/.output/server/chunks/build/tags-DrKY6Ah4.mjs.map +0 -1
  195. package/.output/server/chunks/build/warnings-D8SWbHw0.mjs +0 -76
  196. package/.output/server/chunks/build/warnings-D8SWbHw0.mjs.map +0 -1
  197. package/.output/server/chunks/raw/sample-run.mjs +0 -7
  198. package/.output/server/chunks/raw/sample-run.mjs.map +0 -1
  199. package/dist-ui/dev/sample-run.webm +0 -0
  200. /package/.output/public/_nuxt/{BDNMzG2s2.js → BDNMzG2s.js} +0 -0
@@ -0,0 +1,1653 @@
1
+ import { M as useState, _ as _sfc_main$1, f as useRoute, k as icon_default, e as _sfc_main$5, p as modal_default$2, i as useAi, l as _sfc_main$7, A as AI_OFF_HINT, N as useRequestEvent, $ as $fetch$1, j as extractServerError, q as _sfc_main$1$2, m as _sfc_main$3 } from '../virtual/entry.mjs';
2
+ import { a as _sfc_main$1$1, _ as _sfc_main$2 } from './origin-DFFT7pGA.mjs';
3
+ import { _ as _sfc_main } from './Alert-WVcujYrW.mjs';
4
+ import { _ as _plugin_vue_export_helper_default } from './_plugin-vue_export-helper-BOaGB7Aw.mjs';
5
+ import { ref, computed, defineComponent, mergeProps, withCtx, unref, createVNode, openBlock, createBlock, Fragment, renderList, toDisplayString, createCommentVNode, isRef, useModel, h, watch, nextTick, mergeModels, renderSlot, createTextVNode, createSlots, useSSRContext } from 'vue';
6
+ import { m as getRequestURL } from '../nitro/nitro.mjs';
7
+ import { ssrRenderComponent, ssrRenderList, ssrInterpolate, ssrRenderAttrs, ssrRenderAttr, ssrRenderClass, ssrIncludeBooleanAttr, ssrRenderSlot } from 'vue/server-renderer';
8
+ import Prism from 'prismjs';
9
+
10
+ //#region app/components/scenario/warnings.vue?vue&type=script&setup=true&lang.ts
11
+ var EMPTY_VAR = "env-sem-valor";
12
+ /** O texto da ressalva, sem o slug da regra que o backend prefixa. */
13
+ var warnings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
14
+ __name: "warnings",
15
+ __ssrInlineRender: true,
16
+ props: {
17
+ warnings: { default: () => [] },
18
+ slug: {}
19
+ },
20
+ setup(__props) {
21
+ const reasons = computed(() => __props.warnings.map((warning) => warning.replace(/^[a-z-]+: /, "")));
22
+ const fillable = computed(() => __props.warnings.some((warning) => warning.startsWith(EMPTY_VAR)));
23
+ return (_ctx, _push, _parent, _attrs) => {
24
+ const _component_UAlert = _sfc_main;
25
+ const _component_UButton = _sfc_main$1;
26
+ if (__props.warnings.length) _push(ssrRenderComponent(_component_UAlert, mergeProps({
27
+ color: "warning",
28
+ variant: "soft",
29
+ icon: "i-ic-round-warning",
30
+ title: "A geração terminou com ressalvas",
31
+ "data-testid": "geracao-ressalvas"
32
+ }, _attrs), {
33
+ description: withCtx((_, _push, _parent, _scopeId) => {
34
+ if (_push) {
35
+ _push(`<ul class="list-disc pl-4"${_scopeId}><!--[-->`);
36
+ ssrRenderList(unref(reasons), (reason) => {
37
+ _push(`<li${_scopeId}>${ssrInterpolate(reason)}</li>`);
38
+ });
39
+ _push(`<!--]--></ul>`);
40
+ if (unref(fillable)) _push(ssrRenderComponent(_component_UButton, {
41
+ label: "Preencher ambiente",
42
+ "trailing-icon": "i-ic-round-arrow-forward",
43
+ color: "warning",
44
+ variant: "link",
45
+ class: "mt-1 px-0",
46
+ to: `/projects/${__props.slug}?environment`,
47
+ "data-testid": "ressalvas-ambiente"
48
+ }, null, _parent, _scopeId));
49
+ else _push(`<!---->`);
50
+ } else return [createVNode("ul", { class: "list-disc pl-4" }, [(openBlock(true), createBlock(Fragment, null, renderList(unref(reasons), (reason) => {
51
+ return openBlock(), createBlock("li", { key: reason }, toDisplayString(reason), 1);
52
+ }), 128))]), unref(fillable) ? (openBlock(), createBlock(_component_UButton, {
53
+ key: 0,
54
+ label: "Preencher ambiente",
55
+ "trailing-icon": "i-ic-round-arrow-forward",
56
+ color: "warning",
57
+ variant: "link",
58
+ class: "mt-1 px-0",
59
+ to: `/projects/${__props.slug}?environment`,
60
+ "data-testid": "ressalvas-ambiente"
61
+ }, null, 8, ["to"])) : createCommentVNode("", true)];
62
+ }),
63
+ _: 1
64
+ }, _parent));
65
+ else _push(`<!---->`);
66
+ };
67
+ }
68
+ });
69
+ //#endregion
70
+ //#region app/components/scenario/warnings.vue
71
+ var _sfc_setup$11 = warnings_vue_vue_type_script_setup_true_lang_default.setup;
72
+ warnings_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
73
+ const ssrContext = useSSRContext();
74
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/warnings.vue");
75
+ return _sfc_setup$11 ? _sfc_setup$11(props, ctx) : void 0;
76
+ };
77
+ var warnings_default = Object.assign(warnings_vue_vue_type_script_setup_true_lang_default, { __name: "ScenarioWarnings" });
78
+ //#endregion
79
+ //#region node_modules/.pnpm/nuxt@4.5.2_@babel+plugin-syntax-jsx@7.29.7_@babel+core@7.29.7__@babel+plugin-syntax-typ_45e57be3aa7485e3075b5014322cbc65/node_modules/nuxt/dist/app/composables/url.js
80
+ /** @since 3.5.0 */
81
+ function useRequestURL(opts) {
82
+ return getRequestURL(useRequestEvent(), opts);
83
+ }
84
+ //#endregion
85
+ //#region app/components/webdriver/setup.vue?vue&type=script&setup=true&lang.ts
86
+ var setup_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
87
+ __name: "setup",
88
+ __ssrInlineRender: true,
89
+ setup(__props) {
90
+ const os = ref("macos");
91
+ const tabs = [
92
+ {
93
+ label: "macOS",
94
+ value: "macos"
95
+ },
96
+ {
97
+ label: "Linux",
98
+ value: "linux"
99
+ },
100
+ {
101
+ label: "Windows",
102
+ value: "windows"
103
+ }
104
+ ];
105
+ const route = useRoute();
106
+ const origin = useRequestURL().origin;
107
+ const currentUrl = computed(() => `${origin}${route.fullPath}`);
108
+ const commands = computed(() => ({
109
+ macos: `open -na "Google Chrome" --args --remote-debugging-port=9222 --user-data-dir="$HOME/.acutis/chrome" "${currentUrl.value}"`,
110
+ linux: `google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.acutis/chrome" "${currentUrl.value}" &`,
111
+ windows: `Start-Process "chrome" -ArgumentList "--remote-debugging-port=9222","--user-data-dir=$env:USERPROFILE\\.acutis\\chrome","${currentUrl.value}"`
112
+ }));
113
+ const copied = ref(false);
114
+ let copiedTimer;
115
+ async function copy() {
116
+ await (void 0).clipboard.writeText(commands.value[os.value]);
117
+ copied.value = true;
118
+ clearTimeout(copiedTimer);
119
+ copiedTimer = setTimeout(() => {
120
+ copied.value = false;
121
+ }, 2e3);
122
+ }
123
+ return (_ctx, _push, _parent, _attrs) => {
124
+ const _component_UTabs = _sfc_main$1$1;
125
+ const _component_BaseButtonIcon = icon_default;
126
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "flex flex-col gap-3 rounded-lg bg-elevated p-4" }, _attrs))}><p class="text-sm text-muted"> Abra o Chrome com a porta de depuração. Ele já abre nesta página, é só clicar em <span class="font-medium text-default">Novo cenário</span> de novo: </p>`);
127
+ _push(ssrRenderComponent(_component_UTabs, {
128
+ modelValue: unref(os),
129
+ "onUpdate:modelValue": ($event) => isRef(os) ? os.value = $event : null,
130
+ items: tabs,
131
+ content: false,
132
+ class: "w-full"
133
+ }, null, _parent));
134
+ _push(`<div class="flex items-center gap-2"><code data-testid="webdriver-comando" class="grow overflow-x-auto whitespace-nowrap rounded-md bg-default p-3 text-xs">${ssrInterpolate(unref(commands)[unref(os)])}</code>`);
135
+ _push(ssrRenderComponent(_component_BaseButtonIcon, {
136
+ icon: unref(copied) ? "i-ic-round-check" : "i-ic-round-content-copy",
137
+ label: unref(copied) ? "Copiado!" : "Copiar comando",
138
+ color: "neutral",
139
+ variant: "soft",
140
+ "data-testid": "webdriver-copiar",
141
+ onClick: copy
142
+ }, null, _parent));
143
+ _push(`</div></div>`);
144
+ };
145
+ }
146
+ });
147
+ //#endregion
148
+ //#region app/components/webdriver/setup.vue
149
+ var _sfc_setup$10 = setup_vue_vue_type_script_setup_true_lang_default.setup;
150
+ setup_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
151
+ const ssrContext = useSSRContext();
152
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/webdriver/setup.vue");
153
+ return _sfc_setup$10 ? _sfc_setup$10(props, ctx) : void 0;
154
+ };
155
+ var setup_default = Object.assign(setup_vue_vue_type_script_setup_true_lang_default, { __name: "WebdriverSetup" });
156
+ //#endregion
157
+ //#region app/components/base/empty.vue?vue&type=script&setup=true&lang.ts
158
+ var empty_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
159
+ __name: "empty",
160
+ __ssrInlineRender: true,
161
+ props: {
162
+ icon: {},
163
+ title: {},
164
+ description: { default: "" },
165
+ testid: { default: "" }
166
+ },
167
+ setup(__props) {
168
+ return (_ctx, _push, _parent, _attrs) => {
169
+ const _component_UIcon = _sfc_main$5;
170
+ _push(`<div${ssrRenderAttrs(mergeProps({
171
+ class: "flex flex-col items-center gap-3 py-10 text-center",
172
+ "data-testid": __props.testid
173
+ }, _attrs))}><div class="flex size-12 items-center justify-center rounded-lg bg-elevated">`);
174
+ _push(ssrRenderComponent(_component_UIcon, {
175
+ name: __props.icon,
176
+ class: "size-7 text-dimmed"
177
+ }, null, _parent));
178
+ _push(`</div><p class="font-semibold">${ssrInterpolate(__props.title)}</p>`);
179
+ if (__props.description) _push(`<p class="max-w-sm text-sm text-muted">${ssrInterpolate(__props.description)}</p>`);
180
+ else _push(`<!---->`);
181
+ _push(`</div>`);
182
+ };
183
+ }
184
+ });
185
+ //#endregion
186
+ //#region app/components/base/empty.vue
187
+ var _sfc_setup$9 = empty_vue_vue_type_script_setup_true_lang_default.setup;
188
+ empty_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
189
+ const ssrContext = useSSRContext();
190
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/base/empty.vue");
191
+ return _sfc_setup$9 ? _sfc_setup$9(props, ctx) : void 0;
192
+ };
193
+ var empty_default = Object.assign(empty_vue_vue_type_script_setup_true_lang_default, { __name: "BaseEmpty" });
194
+ //#endregion
195
+ //#region app/utils/recorder-event.ts
196
+ /** A gravação como o backend a guarda: os campos do evento, sem o que é do transporte. */
197
+ function toRecordedEvents(events) {
198
+ return events.map((event) => ({
199
+ type: event.type,
200
+ timestamp: event.timestamp,
201
+ url: event.url ?? null,
202
+ selectors: event.selectors ?? null,
203
+ label: event.label ?? null,
204
+ innerText: event.innerText ?? null,
205
+ tagName: event.tagName ?? null,
206
+ inputType: event.inputType ?? null,
207
+ value: event.value ?? null,
208
+ sensitive: event.sensitive ?? false,
209
+ checked: event.checked ?? null,
210
+ html: event.html ?? null,
211
+ assert: event.assert
212
+ }));
213
+ }
214
+ /** Como o evento chama o elemento, na mesma ordem de fontes do `SpecEmitter` do backend. */
215
+ function target(event) {
216
+ const selectors = event.selectors;
217
+ const clean = (event.label || event.innerText || selectors?.placeholder || selectors?.text || "").replace(/\s+/g, " ").trim();
218
+ return clean === "" ? null : clean;
219
+ }
220
+ /** O alvo entre aspas, separando o que veio da tela do verbo que o descreve. */
221
+ function quoted(what) {
222
+ return `"${what}"`;
223
+ }
224
+ /** O caminho da URL, que é como a execução chama a tela: "/login", ou a página inicial na raiz. */
225
+ function page(event) {
226
+ const url = event.url ?? "";
227
+ try {
228
+ const path = new URL(url, "http://base.test").pathname;
229
+ return path === "/" ? "a página inicial" : quoted(path);
230
+ } catch {
231
+ return url ? quoted(url) : "a página inicial";
232
+ }
233
+ }
234
+ /**
235
+ * A frase que descreve o evento gravado, no mesmo vocabulário dos passos da execução
236
+ * ("Clica em Entrar", "Preenche Senha"). A navegação é dita pelo caminho da URL.
237
+ */
238
+ function describeRecorderEvent(event) {
239
+ const what = target(event);
240
+ switch (event.type) {
241
+ case "navigate": return `Navega para ${page(event)}`;
242
+ case "click": return what ? `Clica em ${quoted(what)}` : "Clica no elemento";
243
+ case "hover": return what ? `Passa o mouse em ${quoted(what)}` : "Passa o mouse no elemento";
244
+ case "fill": {
245
+ const field = what ? quoted(what) : "o campo";
246
+ if (!event.value) return `Preenche ${field}`;
247
+ return `Preenche ${field} com ${event.sensitive ? "••••" : quoted(event.value)}`;
248
+ }
249
+ case "submit": return "Envia o formulário";
250
+ case "assert":
251
+ if (event.assert?.assertType === "url") return `Confere que a tela é ${page(event)}`;
252
+ return `Confere ${what ? quoted(what) : "o elemento"}`;
253
+ default: return what ?? event.url ?? event.type ?? "";
254
+ }
255
+ }
256
+ //#endregion
257
+ //#region app/components/scenario/review/timeline.vue?vue&type=script&setup=true&lang.ts
258
+ var timeline_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
259
+ __name: "timeline",
260
+ __ssrInlineRender: true,
261
+ props: {
262
+ events: {},
263
+ videoSrc: { default: null },
264
+ recordingStartedAt: { default: null },
265
+ resumable: {
266
+ type: Boolean,
267
+ default: false
268
+ }
269
+ },
270
+ emits: ["resume"],
271
+ setup(__props, { emit: __emit }) {
272
+ ref(null);
273
+ const currentTime = ref(0);
274
+ const armed = ref(null);
275
+ const ready = ref(false);
276
+ const eventIcons = {
277
+ navigate: "i-ic-round-public",
278
+ click: "i-ic-round-ads-click",
279
+ fill: "i-ic-round-edit",
280
+ submit: "i-ic-round-send",
281
+ hover: "i-ic-round-mouse",
282
+ assert: "i-ic-round-check-circle"
283
+ };
284
+ function offsetSeconds(event) {
285
+ if (!__props.recordingStartedAt || !event.timestamp) return 0;
286
+ return Math.max(0, (event.timestamp - __props.recordingStartedAt) / 1e3);
287
+ }
288
+ const currentIndex = computed(() => {
289
+ let index = -1;
290
+ __props.events.forEach((event, i) => {
291
+ if (offsetSeconds(event) <= currentTime.value) index = i;
292
+ });
293
+ return index;
294
+ });
295
+ /** Quantos passos saem da gravação quando o corte armado for confirmado. */
296
+ const dropped = computed(() => armed.value === null ? 0 : __props.events.length - armed.value);
297
+ return (_ctx, _push, _parent, _attrs) => {
298
+ const _component_UIcon = _sfc_main$5;
299
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "grid grid-cols-1 gap-6 lg:grid-cols-5" }, _attrs))} data-v-46efcf33>`);
300
+ if (__props.videoSrc) _push(`<video data-testid="revisao-video"${ssrRenderAttr("src", __props.videoSrc)} controls class="w-full self-start rounded-lg lg:sticky lg:top-0 lg:col-span-3" data-v-46efcf33></video>`);
301
+ else _push(`<!---->`);
302
+ _push(`<div class="${ssrRenderClass(__props.videoSrc ? "lg:col-span-2" : "lg:col-span-5")}" data-v-46efcf33><p class="mb-3 font-semibold" data-v-46efcf33> Timeline dos eventos </p><ol class="flex w-max max-w-full flex-col" data-v-46efcf33><!--[-->`);
303
+ ssrRenderList(__props.events, (event, i) => {
304
+ _push(`<li data-testid="revisao-evento"${ssrRenderAttr("data-current", i === unref(currentIndex) ? "true" : "false")}${ssrRenderAttr("data-dropped", unref(armed) !== null && i >= unref(armed) ? "true" : "false")} class="${ssrRenderClass([unref(armed) !== null && i >= unref(armed) ? "opacity-50" : "", "group relative flex cursor-pointer items-stretch gap-3 transition-opacity"])}" data-v-46efcf33>`);
305
+ if (__props.resumable && i > 0) {
306
+ _push(`<button type="button" data-testid="revisao-retomar"${ssrRenderAttr("data-armed", unref(armed) === i ? "true" : "false")} class="${ssrRenderClass([unref(armed) === i ? "opacity-100!" : "", "absolute -top-5 -right-1.5 -left-1.5 z-10 flex h-4 items-center gap-2 opacity-0 transition-opacity focus-visible:opacity-100 group-hover:opacity-100"])}"${ssrRenderAttr("aria-label", unref(armed) === i ? `Confirmar a retomada deste passo, descartando ${unref(dropped)}` : "Retomar a gravação deste passo")} data-v-46efcf33><span class="${ssrRenderClass([unref(armed) === i ? "bg-error" : "bg-primary", "h-px flex-1 rounded-full"])}" data-v-46efcf33></span><span class="${ssrRenderClass([unref(armed) === i ? "bg-error text-inverted" : "bg-default text-primary", "rounded-full px-2 py-0.5 text-[10px] leading-none font-medium tracking-wide uppercase"])}" data-v-46efcf33>`);
307
+ if (unref(armed) !== i) _push(`<!--[-->retomar daqui<!--]-->`);
308
+ else if (unref(ready)) _push(`<!--[-->clique de novo · descarta ${ssrInterpolate(unref(dropped))}<!--]-->`);
309
+ else _push(`<!--[-->aguarde…<!--]-->`);
310
+ _push(`</span><span class="relative h-px flex-1 overflow-visible rounded-full bg-accented" data-v-46efcf33>`);
311
+ if (unref(armed) === i) _push(`<span class="arming absolute inset-y-0 left-0 rounded-full bg-error" data-v-46efcf33></span>`);
312
+ else _push(`<!---->`);
313
+ _push(`</span></button>`);
314
+ } else _push(`<!---->`);
315
+ _push(`<div class="flex flex-col items-center" data-v-46efcf33>`);
316
+ _push(ssrRenderComponent(_component_UIcon, {
317
+ name: eventIcons[event.type] ?? "i-ic-round-help-outline",
318
+ class: ["size-5 shrink-0 transition-colors", i <= unref(currentIndex) ? "text-primary" : "text-muted"]
319
+ }, null, _parent));
320
+ if (i < __props.events.length - 1) _push(`<span class="${ssrRenderClass([i < unref(currentIndex) ? "bg-primary" : "bg-accented", "w-px grow transition-colors"])}" data-v-46efcf33></span>`);
321
+ else _push(`<!---->`);
322
+ _push(`</div><div class="pb-6 min-w-0 flex-1" data-v-46efcf33><p class="${ssrRenderClass([[i === unref(currentIndex) ? "font-semibold text-primary" : "text-default", unref(armed) !== null && i >= unref(armed) ? "line-through decoration-dimmed" : ""], "truncate text-sm transition-colors group-hover:text-highlighted"])}" data-v-46efcf33>${ssrInterpolate(unref(describeRecorderEvent)(event))}</p>`);
323
+ if (__props.recordingStartedAt) _push(`<p class="text-xs text-dimmed" data-v-46efcf33>${ssrInterpolate(offsetSeconds(event).toFixed(1))}s </p>`);
324
+ else _push(`<!---->`);
325
+ _push(`</div></li>`);
326
+ });
327
+ _push(`<!--]--></ol></div></div>`);
328
+ };
329
+ }
330
+ });
331
+ //#endregion
332
+ //#region app/components/scenario/review/timeline.vue
333
+ var _sfc_setup$8 = timeline_vue_vue_type_script_setup_true_lang_default.setup;
334
+ timeline_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
335
+ const ssrContext = useSSRContext();
336
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/review/timeline.vue");
337
+ return _sfc_setup$8 ? _sfc_setup$8(props, ctx) : void 0;
338
+ };
339
+ var timeline_default = /*#__PURE__*/ Object.assign(_plugin_vue_export_helper_default(timeline_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-46efcf33"]]), { __name: "ScenarioReviewTimeline" });
340
+ //#endregion
341
+ //#region app/components/base/codefield.vue?vue&type=script&setup=true&lang.ts
342
+ var codefield_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
343
+ __name: "codefield",
344
+ __ssrInlineRender: true,
345
+ props: /*@__PURE__*/ mergeModels({
346
+ language: {},
347
+ testid: {},
348
+ readonly: {
349
+ type: Boolean,
350
+ default: false
351
+ }
352
+ }, {
353
+ "modelValue": { default: "" },
354
+ "modelModifiers": {}
355
+ }),
356
+ emits: ["update:modelValue"],
357
+ setup(__props) {
358
+ const model = useModel(__props, "modelValue");
359
+ const textarea = ref(null);
360
+ function renderTokens(stream) {
361
+ if (typeof stream === "string") return [stream];
362
+ if (Array.isArray(stream)) return stream.flatMap(renderTokens);
363
+ const aliases = Array.isArray(stream.alias) ? stream.alias : [stream.alias].filter(Boolean);
364
+ return [h("span", { class: [
365
+ "token",
366
+ stream.type,
367
+ ...aliases
368
+ ] }, renderTokens(stream.content))];
369
+ }
370
+ const highlighted = computed(() => [...Prism.tokenize(model.value, Prism.languages[__props.language]), "\n"]);
371
+ const HighlightedCode = defineComponent(() => () => h("code", null, renderTokens(highlighted.value)));
372
+ function resize() {
373
+ const el = textarea.value;
374
+ if (!el) return;
375
+ el.style.height = "auto";
376
+ el.style.height = `${el.scrollHeight}px`;
377
+ }
378
+ watch(model, () => nextTick(resize));
379
+ return (_ctx, _push, _parent, _attrs) => {
380
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "codefield relative overflow-hidden rounded-md bg-elevated font-mono text-sm/6" }, _attrs))} data-v-3e187072><pre aria-hidden="true" class="codefield-box pointer-events-none absolute inset-0 m-0 overflow-hidden" data-v-3e187072>`);
381
+ _push(ssrRenderComponent(unref(HighlightedCode), null, null, _parent));
382
+ _push(`</pre><textarea${ssrRenderAttr("data-testid", __props.testid)}${ssrIncludeBooleanAttr(__props.readonly) ? " readonly" : ""} spellcheck="false" class="codefield-box relative block w-full resize-none bg-transparent text-transparent caret-[var(--ui-text-highlighted)] outline-none" data-v-3e187072>${ssrInterpolate(model.value)}</textarea></div>`);
383
+ };
384
+ }
385
+ });
386
+ //#endregion
387
+ //#region app/components/base/codefield.vue
388
+ var _sfc_setup$7 = codefield_vue_vue_type_script_setup_true_lang_default.setup;
389
+ codefield_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
390
+ const ssrContext = useSSRContext();
391
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/base/codefield.vue");
392
+ return _sfc_setup$7 ? _sfc_setup$7(props, ctx) : void 0;
393
+ };
394
+ var codefield_default = /*#__PURE__*/ Object.assign(_plugin_vue_export_helper_default(codefield_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3e187072"]]), { __name: "BaseCodefield" });
395
+ //#endregion
396
+ //#region app/components/base/confirm.vue?vue&type=script&setup=true&lang.ts
397
+ var confirm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
398
+ __name: "confirm",
399
+ __ssrInlineRender: true,
400
+ props: /*@__PURE__*/ mergeModels({
401
+ title: {},
402
+ description: { default: "" },
403
+ confirmLabel: { default: "Confirmar" },
404
+ confirmColor: { default: "primary" },
405
+ confirmTestid: { default: "confirmar" },
406
+ loading: {
407
+ type: Boolean,
408
+ default: false
409
+ }
410
+ }, {
411
+ "open": {
412
+ type: Boolean,
413
+ default: false
414
+ },
415
+ "openModifiers": {}
416
+ }),
417
+ emits: /*@__PURE__*/ mergeModels(["confirm"], ["update:open"]),
418
+ setup(__props, { emit: __emit }) {
419
+ const open = useModel(__props, "open");
420
+ const emit = __emit;
421
+ return (_ctx, _push, _parent, _attrs) => {
422
+ const _component_BaseModal = modal_default$2;
423
+ const _component_UButton = _sfc_main$1;
424
+ _push(ssrRenderComponent(_component_BaseModal, mergeProps({
425
+ open: open.value,
426
+ "onUpdate:open": ($event) => open.value = $event,
427
+ title: __props.title
428
+ }, _attrs), {
429
+ body: withCtx((_, _push, _parent, _scopeId) => {
430
+ if (_push) {
431
+ _push(`<div class="flex min-h-24 items-center justify-center text-center"${_scopeId}><p class="w-full text-lg text-muted break-words"${_scopeId}>`);
432
+ ssrRenderSlot(_ctx.$slots, "description", {}, () => {
433
+ _push(`${ssrInterpolate(__props.description)}`);
434
+ }, _push, _parent, _scopeId);
435
+ _push(`</p></div>`);
436
+ } else return [createVNode("div", { class: "flex min-h-24 items-center justify-center text-center" }, [createVNode("p", { class: "w-full text-lg text-muted break-words" }, [renderSlot(_ctx.$slots, "description", {}, () => [createTextVNode(toDisplayString(__props.description), 1)])])])];
437
+ }),
438
+ footer: withCtx((_, _push, _parent, _scopeId) => {
439
+ if (_push) {
440
+ _push(ssrRenderComponent(_component_UButton, {
441
+ label: "Cancelar",
442
+ color: "neutral",
443
+ variant: "ghost",
444
+ "data-testid": "confirmar-cancelar",
445
+ onClick: ($event) => open.value = false
446
+ }, null, _parent, _scopeId));
447
+ _push(ssrRenderComponent(_component_UButton, {
448
+ label: __props.confirmLabel,
449
+ color: __props.confirmColor,
450
+ loading: __props.loading,
451
+ "data-testid": __props.confirmTestid,
452
+ onClick: ($event) => emit("confirm")
453
+ }, null, _parent, _scopeId));
454
+ } else return [createVNode(_component_UButton, {
455
+ label: "Cancelar",
456
+ color: "neutral",
457
+ variant: "ghost",
458
+ "data-testid": "confirmar-cancelar",
459
+ onClick: ($event) => open.value = false
460
+ }, null, 8, ["onClick"]), createVNode(_component_UButton, {
461
+ label: __props.confirmLabel,
462
+ color: __props.confirmColor,
463
+ loading: __props.loading,
464
+ "data-testid": __props.confirmTestid,
465
+ onClick: ($event) => emit("confirm")
466
+ }, null, 8, [
467
+ "label",
468
+ "color",
469
+ "loading",
470
+ "data-testid",
471
+ "onClick"
472
+ ])];
473
+ }),
474
+ _: 3
475
+ }, _parent));
476
+ };
477
+ }
478
+ });
479
+ //#endregion
480
+ //#region app/components/base/confirm.vue
481
+ var _sfc_setup$6 = confirm_vue_vue_type_script_setup_true_lang_default.setup;
482
+ confirm_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
483
+ const ssrContext = useSSRContext();
484
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/base/confirm.vue");
485
+ return _sfc_setup$6 ? _sfc_setup$6(props, ctx) : void 0;
486
+ };
487
+ var confirm_default = Object.assign(confirm_vue_vue_type_script_setup_true_lang_default, { __name: "BaseConfirm" });
488
+ /** O nome de arquivo que um título vira. Acompanha o `Str::slug` que o backend aplica ao gravar. */
489
+ function slugify(value) {
490
+ return value.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 80).replace(/^-+|-+$/g, "");
491
+ }
492
+ function draftFromScenario(scenario) {
493
+ const parts = scenario.spec.replace(/^tests\//, "").replace(/\.spec\.ts$/, "").split("/");
494
+ return {
495
+ title: scenario.title,
496
+ path: parts.at(-1),
497
+ domain: parts.length > 1 ? parts.slice(0, -1).join("/") : "",
498
+ tags: scenario.tags,
499
+ gherkin: scenario.gherkin ?? "",
500
+ playwright: scenario.playwright
501
+ };
502
+ }
503
+ //#endregion
504
+ //#region app/components/scenario/review/contexts.vue?vue&type=script&setup=true&lang.ts
505
+ var contexts_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
506
+ __name: "contexts",
507
+ __ssrInlineRender: true,
508
+ props: /*@__PURE__*/ mergeModels({
509
+ isAuth: {
510
+ type: Boolean,
511
+ default: false
512
+ },
513
+ novo: {
514
+ type: Boolean,
515
+ default: false
516
+ }
517
+ }, {
518
+ "draft": { required: true },
519
+ "draftModifiers": {}
520
+ }),
521
+ emits: ["update:draft"],
522
+ setup(__props) {
523
+ const draft = useModel(__props, "draft");
524
+ function tagsFromLine(line) {
525
+ return line.match(/@[\w-]+/g) ?? [];
526
+ }
527
+ function tagsFromGherkin(gherkin) {
528
+ const firstLine = gherkin.split("\n")[0] ?? "";
529
+ return firstLine.trim().startsWith("@") ? tagsFromLine(firstLine) : [];
530
+ }
531
+ function tagsFromPlaywright(playwright) {
532
+ const match = playwright.match(/tag:\s*\[([^\]]*)\]/);
533
+ return match ? tagsFromLine(match[1]) : [];
534
+ }
535
+ function stampGherkinTags(gherkin, tags) {
536
+ if (!gherkin.trim()) return gherkin;
537
+ const lines = gherkin.split("\n");
538
+ if (lines[0]?.trim().startsWith("@")) lines.shift();
539
+ const body = lines.join("\n");
540
+ return tags.length ? `${tags.join(" ")}\n${body}` : body;
541
+ }
542
+ function stampPlaywrightTags(playwright, tags) {
543
+ if (!tags.length) return playwright;
544
+ const list = `tag: [${tags.map((t) => `'${t}'`).join(", ")}]`;
545
+ if (/tag:\s*\[[^\]]*\]/.test(playwright)) return playwright.replace(/tag:\s*\[[^\]]*\]/, list);
546
+ return playwright.replace(/test\.describe\(\s*((["']).+?\2)\s*,\s*(?=\(|async)/, `test.describe($1, { ${list} }, `);
547
+ }
548
+ let syncing = false;
549
+ function applyTags(tags) {
550
+ syncing = true;
551
+ draft.value = {
552
+ ...draft.value,
553
+ tags,
554
+ gherkin: stampGherkinTags(draft.value.gherkin, tags),
555
+ playwright: stampPlaywrightTags(draft.value.playwright, tags)
556
+ };
557
+ nextTick(() => {
558
+ syncing = false;
559
+ });
560
+ }
561
+ const tagsText = computed({
562
+ get: () => draft.value.tags.join(" "),
563
+ set: (value) => applyTags(tagsFromLine(value))
564
+ });
565
+ watch(() => draft.value.gherkin, (gherkin) => {
566
+ if (syncing) return;
567
+ const tags = tagsFromGherkin(gherkin);
568
+ if (tags.length && tags.join(" ") !== draft.value.tags.join(" ")) applyTags(tags);
569
+ });
570
+ watch(() => draft.value.playwright, (playwright) => {
571
+ if (syncing) return;
572
+ const tags = tagsFromPlaywright(playwright);
573
+ if (tags.length && tags.join(" ") !== draft.value.tags.join(" ")) applyTags(tags);
574
+ });
575
+ /** No cenário novo o arquivo espelha o título, enquanto ninguém o tiver editado à mão. */
576
+ watch(() => draft.value.title, (title, previous) => {
577
+ if (!__props.novo || draft.value.path !== slugify(previous)) return;
578
+ draft.value = {
579
+ ...draft.value,
580
+ path: slugify(title)
581
+ };
582
+ });
583
+ return (_ctx, _push, _parent, _attrs) => {
584
+ const _component_UFormField = _sfc_main$1$2;
585
+ const _component_UInput = _sfc_main$3;
586
+ const _component_BaseCodefield = codefield_default;
587
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "flex flex-col gap-5" }, _attrs))}><div class="grid grid-cols-1 gap-4 sm:grid-cols-2">`);
588
+ _push(ssrRenderComponent(_component_UFormField, { label: "Título do cenário" }, {
589
+ default: withCtx((_, _push, _parent, _scopeId) => {
590
+ if (_push) _push(ssrRenderComponent(_component_UInput, {
591
+ modelValue: draft.value.title,
592
+ "onUpdate:modelValue": ($event) => draft.value.title = $event,
593
+ maxlength: unref(120),
594
+ "data-testid": "contexto-titulo",
595
+ class: "w-full"
596
+ }, null, _parent, _scopeId));
597
+ else return [createVNode(_component_UInput, {
598
+ modelValue: draft.value.title,
599
+ "onUpdate:modelValue": ($event) => draft.value.title = $event,
600
+ maxlength: unref(120),
601
+ "data-testid": "contexto-titulo",
602
+ class: "w-full"
603
+ }, null, 8, [
604
+ "modelValue",
605
+ "onUpdate:modelValue",
606
+ "maxlength"
607
+ ])];
608
+ }),
609
+ _: 1
610
+ }, _parent));
611
+ if (!__props.isAuth) _push(ssrRenderComponent(_component_UFormField, {
612
+ label: "Arquivo",
613
+ help: "Nome do .spec.ts / .feature"
614
+ }, {
615
+ default: withCtx((_, _push, _parent, _scopeId) => {
616
+ if (_push) _push(ssrRenderComponent(_component_UInput, {
617
+ modelValue: draft.value.path,
618
+ "onUpdate:modelValue": ($event) => draft.value.path = $event,
619
+ maxlength: unref(80),
620
+ "data-testid": "contexto-path",
621
+ class: "w-full"
622
+ }, null, _parent, _scopeId));
623
+ else return [createVNode(_component_UInput, {
624
+ modelValue: draft.value.path,
625
+ "onUpdate:modelValue": ($event) => draft.value.path = $event,
626
+ maxlength: unref(80),
627
+ "data-testid": "contexto-path",
628
+ class: "w-full"
629
+ }, null, 8, [
630
+ "modelValue",
631
+ "onUpdate:modelValue",
632
+ "maxlength"
633
+ ])];
634
+ }),
635
+ _: 1
636
+ }, _parent));
637
+ else _push(`<!---->`);
638
+ _push(`</div>`);
639
+ if (!__props.isAuth) _push(ssrRenderComponent(_component_UFormField, {
640
+ label: "Domínio",
641
+ help: "Pasta onde o cenário será salvo (ex.: login, checkout)"
642
+ }, {
643
+ default: withCtx((_, _push, _parent, _scopeId) => {
644
+ if (_push) _push(ssrRenderComponent(_component_UInput, {
645
+ modelValue: draft.value.domain,
646
+ "onUpdate:modelValue": ($event) => draft.value.domain = $event,
647
+ maxlength: unref(80),
648
+ "data-testid": "contexto-dominio",
649
+ class: "w-full"
650
+ }, null, _parent, _scopeId));
651
+ else return [createVNode(_component_UInput, {
652
+ modelValue: draft.value.domain,
653
+ "onUpdate:modelValue": ($event) => draft.value.domain = $event,
654
+ maxlength: unref(80),
655
+ "data-testid": "contexto-dominio",
656
+ class: "w-full"
657
+ }, null, 8, [
658
+ "modelValue",
659
+ "onUpdate:modelValue",
660
+ "maxlength"
661
+ ])];
662
+ }),
663
+ _: 1
664
+ }, _parent));
665
+ else _push(`<!---->`);
666
+ if (!__props.isAuth) _push(ssrRenderComponent(_component_UFormField, {
667
+ label: "Tags",
668
+ help: "Separadas por espaço (ex.: @read @criando)"
669
+ }, {
670
+ default: withCtx((_, _push, _parent, _scopeId) => {
671
+ if (_push) _push(ssrRenderComponent(_component_UInput, {
672
+ modelValue: unref(tagsText),
673
+ "onUpdate:modelValue": ($event) => isRef(tagsText) ? tagsText.value = $event : null,
674
+ "data-testid": "contexto-tags",
675
+ class: "w-full"
676
+ }, null, _parent, _scopeId));
677
+ else return [createVNode(_component_UInput, {
678
+ modelValue: unref(tagsText),
679
+ "onUpdate:modelValue": ($event) => isRef(tagsText) ? tagsText.value = $event : null,
680
+ "data-testid": "contexto-tags",
681
+ class: "w-full"
682
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])];
683
+ }),
684
+ _: 1
685
+ }, _parent));
686
+ else _push(`<!---->`);
687
+ _push(ssrRenderComponent(_component_UFormField, { label: "Cenário (Gherkin)" }, {
688
+ default: withCtx((_, _push, _parent, _scopeId) => {
689
+ if (_push) _push(ssrRenderComponent(_component_BaseCodefield, {
690
+ modelValue: draft.value.gherkin,
691
+ "onUpdate:modelValue": ($event) => draft.value.gherkin = $event,
692
+ language: "gherkin",
693
+ testid: "contexto-cenario"
694
+ }, null, _parent, _scopeId));
695
+ else return [createVNode(_component_BaseCodefield, {
696
+ modelValue: draft.value.gherkin,
697
+ "onUpdate:modelValue": ($event) => draft.value.gherkin = $event,
698
+ language: "gherkin",
699
+ testid: "contexto-cenario"
700
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])];
701
+ }),
702
+ _: 1
703
+ }, _parent));
704
+ _push(ssrRenderComponent(_component_UFormField, { label: "Teste (Playwright)" }, {
705
+ default: withCtx((_, _push, _parent, _scopeId) => {
706
+ if (_push) _push(ssrRenderComponent(_component_BaseCodefield, {
707
+ modelValue: draft.value.playwright,
708
+ "onUpdate:modelValue": ($event) => draft.value.playwright = $event,
709
+ language: "typescript",
710
+ testid: "contexto-teste"
711
+ }, null, _parent, _scopeId));
712
+ else return [createVNode(_component_BaseCodefield, {
713
+ modelValue: draft.value.playwright,
714
+ "onUpdate:modelValue": ($event) => draft.value.playwright = $event,
715
+ language: "typescript",
716
+ testid: "contexto-teste"
717
+ }, null, 8, ["modelValue", "onUpdate:modelValue"])];
718
+ }),
719
+ _: 1
720
+ }, _parent));
721
+ _push(`</div>`);
722
+ };
723
+ }
724
+ });
725
+ //#endregion
726
+ //#region app/components/scenario/review/contexts.vue
727
+ var _sfc_setup$5 = contexts_vue_vue_type_script_setup_true_lang_default.setup;
728
+ contexts_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
729
+ const ssrContext = useSSRContext();
730
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/review/contexts.vue");
731
+ return _sfc_setup$5 ? _sfc_setup$5(props, ctx) : void 0;
732
+ };
733
+ var contexts_default = Object.assign(contexts_vue_vue_type_script_setup_true_lang_default, { __name: "ScenarioReviewContexts" });
734
+ //#endregion
735
+ //#region app/components/base/loading/phrases.vue?vue&type=script&setup=true&lang.ts
736
+ var phrases_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
737
+ __name: "phrases",
738
+ __ssrInlineRender: true,
739
+ props: {
740
+ phrases: {},
741
+ icon: { default: "i-line-md-coffee-half-empty-twotone-loop" },
742
+ hint: { default: "Isso pode levar alguns segundos." }
743
+ },
744
+ setup(__props) {
745
+ const display = ref(__props.phrases[0]);
746
+ return (_ctx, _push, _parent, _attrs) => {
747
+ const _component_UIcon = _sfc_main$5;
748
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "flex flex-col items-center gap-6 py-10 text-center" }, _attrs))}>`);
749
+ _push(ssrRenderComponent(_component_UIcon, {
750
+ name: __props.icon,
751
+ class: "size-20 text-highlighted/75"
752
+ }, null, _parent));
753
+ _push(`<p class="text-lg font-mono font-medium text-highlighted">${ssrInterpolate(unref(display))}</p><p class="text-xs text-dimmed">${ssrInterpolate(__props.hint)}</p></div>`);
754
+ };
755
+ }
756
+ });
757
+ //#endregion
758
+ //#region app/components/base/loading/phrases.vue
759
+ var _sfc_setup$4 = phrases_vue_vue_type_script_setup_true_lang_default.setup;
760
+ phrases_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
761
+ const ssrContext = useSSRContext();
762
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/base/loading/phrases.vue");
763
+ return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
764
+ };
765
+ var phrases_default = Object.assign(phrases_vue_vue_type_script_setup_true_lang_default, { __name: "BaseLoadingPhrases" });
766
+ //#endregion
767
+ //#region app/components/scenario/review/loading.vue?vue&type=script&setup=true&lang.ts
768
+ var loading_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
769
+ __name: "loading",
770
+ __ssrInlineRender: true,
771
+ props: { auth: {
772
+ type: Boolean,
773
+ default: false
774
+ } },
775
+ setup(__props) {
776
+ const SCENARIO_PHRASES = [
777
+ "Analisando os elementos da tela",
778
+ "Identificando os fluxos de navegação",
779
+ "Interpretando suas interações",
780
+ "Gerando os contextos do cenário",
781
+ "Escrevendo os passos em Gherkin",
782
+ "Traduzindo para o teste Playwright",
783
+ "Dando os retoques finais"
784
+ ];
785
+ const AUTH_PHRASES = [
786
+ "Analisando os eventos gravados",
787
+ "Identificando os campos de usuário e senha",
788
+ "Escrevendo o fluxo de autenticação"
789
+ ];
790
+ return (_ctx, _push, _parent, _attrs) => {
791
+ _push(ssrRenderComponent(phrases_default, mergeProps({
792
+ phrases: __props.auth ? AUTH_PHRASES : SCENARIO_PHRASES,
793
+ "data-testid": __props.auth ? "auth-carregando" : "contexto-carregando"
794
+ }, _attrs), null, _parent));
795
+ };
796
+ }
797
+ });
798
+ //#endregion
799
+ //#region app/components/scenario/review/loading.vue
800
+ var _sfc_setup$3 = loading_vue_vue_type_script_setup_true_lang_default.setup;
801
+ loading_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
802
+ const ssrContext = useSSRContext();
803
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/review/loading.vue");
804
+ return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
805
+ };
806
+ var loading_default = Object.assign(loading_vue_vue_type_script_setup_true_lang_default, { __name: "ScenarioReviewLoading" });
807
+ //#endregion
808
+ //#region app/composables/webdriver.ts
809
+ var socket = null;
810
+ function useWebdriver() {
811
+ const state = useState("webdriver", () => ({
812
+ connected: false,
813
+ extensionReady: false,
814
+ recording: false,
815
+ replaying: false,
816
+ replayFailedStep: null,
817
+ error: null,
818
+ events: [],
819
+ videoSessionId: null,
820
+ recordingStartedAt: null,
821
+ storageState: null
822
+ }));
823
+ const url = "";
824
+ function send(type, payload = {}) {
825
+ if (void 0 === WebSocket.OPEN) socket.send(JSON.stringify({
826
+ type,
827
+ ...payload
828
+ }));
829
+ }
830
+ function ensureConnected() {}
831
+ function startRecording(mode = "scenario", options = {}) {
832
+ state.value.error = null;
833
+ state.value.events = options.replay ?? [];
834
+ state.value.videoSessionId = null;
835
+ state.value.storageState = null;
836
+ state.value.recording = true;
837
+ state.value.replaying = (options.replay?.length ?? 0) > 0;
838
+ state.value.replayFailedStep = null;
839
+ send("START_RECORDING", {
840
+ mode,
841
+ ...options
842
+ });
843
+ }
844
+ function stopRecording() {
845
+ send("STOP_RECORDING");
846
+ state.value.recording = false;
847
+ }
848
+ return {
849
+ state,
850
+ url,
851
+ ensureConnected,
852
+ startRecording,
853
+ stopRecording
854
+ };
855
+ }
856
+ //#endregion
857
+ //#region app/components/scenario/review/modal.vue?vue&type=script&setup=true&lang.ts
858
+ var modal_vue_vue_type_script_setup_true_lang_default$1 = /*@__PURE__*/ defineComponent({
859
+ __name: "modal",
860
+ __ssrInlineRender: true,
861
+ props: /*@__PURE__*/ mergeModels({
862
+ slug: {},
863
+ isPublic: {
864
+ type: Boolean,
865
+ default: false
866
+ },
867
+ scenario: { default: null }
868
+ }, {
869
+ "open": {
870
+ type: Boolean,
871
+ default: false
872
+ },
873
+ "openModifiers": {}
874
+ }),
875
+ emits: /*@__PURE__*/ mergeModels([
876
+ "generated",
877
+ "rerecord",
878
+ "resume"
879
+ ], ["update:open"]),
880
+ setup(__props, { emit: __emit }) {
881
+ const open = useModel(__props, "open");
882
+ const emit = __emit;
883
+ const isAuth = computed(() => __props.scenario?.is_auth === true);
884
+ const scenarioId = computed(() => __props.scenario.spec.replace(/^tests\//, "").replace(/\.(spec|setup)\.ts$/, ""));
885
+ const { state, url } = useWebdriver();
886
+ function emptyDraft() {
887
+ return {
888
+ title: "",
889
+ tags: [],
890
+ domain: "",
891
+ path: "",
892
+ gherkin: "",
893
+ playwright: ""
894
+ };
895
+ }
896
+ const step = ref("review");
897
+ const draft = ref(emptyDraft());
898
+ const error = ref(null);
899
+ watch(open, (isOpen) => {
900
+ if (isOpen) {
901
+ step.value = "review";
902
+ draft.value = emptyDraft();
903
+ error.value = null;
904
+ }
905
+ });
906
+ const modalTitle = computed(() => {
907
+ if (step.value === "edit") return "Revise os contextos";
908
+ if (isAuth.value) return "Revise a gravação do login";
909
+ return __props.scenario ? "Revise a gravação retomada" : "Revise seus eventos";
910
+ });
911
+ const generateLabel = computed(() => isAuth.value ? "Gerar autenticação" : "Gerar cenário");
912
+ const timeline = computed(() => state.value.events.filter((event) => event.type && event.timestamp).sort((a, b) => a.timestamp - b.timestamp));
913
+ const submitting = ref(false);
914
+ const baseUrl = computed(() => {
915
+ const first = timeline.value.find((event) => event.url);
916
+ if (!first?.url) return null;
917
+ try {
918
+ return new URL(first.url).origin;
919
+ } catch {
920
+ return null;
921
+ }
922
+ });
923
+ const mappedEvents = computed(() => toRecordedEvents(timeline.value));
924
+ /** A autenticação não tem contexto para revisar: sai da revisão direto para o auth.setup.ts. */
925
+ async function recordAuth() {
926
+ try {
927
+ const generated = await $fetch$1(`/api/projects/${__props.slug}/auth/record`, {
928
+ method: "POST",
929
+ body: {
930
+ baseUrl: baseUrl.value,
931
+ events: mappedEvents.value
932
+ }
933
+ });
934
+ open.value = false;
935
+ emit("generated", generated);
936
+ } catch (err) {
937
+ console.error("Falha ao gravar a autenticação:", err);
938
+ error.value = extractServerError(err, "Não foi possível gerar a autenticação a partir da gravação.");
939
+ step.value = "review";
940
+ }
941
+ }
942
+ async function generate() {
943
+ if (!baseUrl.value) {
944
+ error.value = "Nenhuma navegação registrada na gravação.";
945
+ return;
946
+ }
947
+ error.value = null;
948
+ step.value = "loading";
949
+ if (isAuth.value) return recordAuth();
950
+ try {
951
+ const generated = await $fetch$1(`/api/projects/${__props.slug}/tests/draft`, {
952
+ method: "POST",
953
+ body: {
954
+ baseUrl: baseUrl.value,
955
+ events: mappedEvents.value,
956
+ isPublic: __props.isPublic
957
+ }
958
+ });
959
+ draft.value = __props.scenario ? {
960
+ ...generated,
961
+ title: __props.scenario.title,
962
+ path: scenarioId.value,
963
+ domain: __props.scenario.domain ?? ""
964
+ } : generated;
965
+ step.value = "edit";
966
+ } catch (err) {
967
+ error.value = extractServerError(err, "Não foi possível gerar o cenário. Tente novamente.");
968
+ step.value = "review";
969
+ }
970
+ }
971
+ async function commit() {
972
+ if (!draft.value) return;
973
+ submitting.value = true;
974
+ error.value = null;
975
+ try {
976
+ const saved = await $fetch$1(__props.scenario ? `/api/projects/${__props.slug}/scenarios/${scenarioId.value}` : `/api/projects/${__props.slug}/tests`, {
977
+ method: __props.scenario ? "PATCH" : "POST",
978
+ body: {
979
+ ...draft.value,
980
+ events: mappedEvents.value
981
+ }
982
+ });
983
+ open.value = false;
984
+ emit("generated", saved);
985
+ } catch (err) {
986
+ error.value = extractServerError(err, "Não foi possível salvar o cenário. Tente novamente.");
987
+ } finally {
988
+ submitting.value = false;
989
+ }
990
+ }
991
+ function rerecord() {
992
+ open.value = false;
993
+ emit("rerecord");
994
+ }
995
+ /** Retomar do passo escolhido mantém o que veio antes dele e descarta ele e os seguintes. */
996
+ function resume(index) {
997
+ open.value = false;
998
+ emit("resume", timeline.value.slice(0, index));
999
+ }
1000
+ return (_ctx, _push, _parent, _attrs) => {
1001
+ const _component_BaseModal = modal_default$2;
1002
+ const _component_ScenarioReviewLoading = loading_default;
1003
+ const _component_ScenarioWarnings = warnings_default;
1004
+ const _component_ScenarioReviewContexts = contexts_default;
1005
+ const _component_ScenarioReviewTimeline = timeline_default;
1006
+ const _component_UAlert = _sfc_main;
1007
+ const _component_UButton = _sfc_main$1;
1008
+ _push(ssrRenderComponent(_component_BaseModal, mergeProps({
1009
+ open: open.value,
1010
+ "onUpdate:open": ($event) => open.value = $event,
1011
+ title: unref(modalTitle),
1012
+ dismissable: false,
1013
+ loading: unref(step) === "loading",
1014
+ wide: ""
1015
+ }, _attrs), {
1016
+ body: withCtx((_, _push, _parent, _scopeId) => {
1017
+ if (_push) {
1018
+ if (unref(step) === "loading") _push(ssrRenderComponent(_component_ScenarioReviewLoading, { auth: unref(isAuth) }, null, _parent, _scopeId));
1019
+ else if (unref(step) === "edit") {
1020
+ _push(`<!--[-->`);
1021
+ _push(ssrRenderComponent(_component_ScenarioWarnings, {
1022
+ warnings: unref(draft).warnings,
1023
+ slug: __props.slug,
1024
+ class: "mb-4"
1025
+ }, null, _parent, _scopeId));
1026
+ _push(ssrRenderComponent(_component_ScenarioReviewContexts, {
1027
+ draft: unref(draft),
1028
+ "onUpdate:draft": ($event) => isRef(draft) ? draft.value = $event : null,
1029
+ novo: ""
1030
+ }, null, _parent, _scopeId));
1031
+ _push(`<!--]-->`);
1032
+ } else _push(ssrRenderComponent(_component_ScenarioReviewTimeline, {
1033
+ events: unref(timeline),
1034
+ "video-src": unref(state).videoSessionId ? `${unref(url)}/recording/${unref(state).videoSessionId}` : null,
1035
+ "recording-started-at": unref(state).recordingStartedAt,
1036
+ resumable: "",
1037
+ onResume: resume
1038
+ }, null, _parent, _scopeId));
1039
+ if (unref(error)) _push(ssrRenderComponent(_component_UAlert, {
1040
+ color: "error",
1041
+ variant: "soft",
1042
+ description: unref(error),
1043
+ class: "mt-4",
1044
+ "data-testid": "revisao-erro"
1045
+ }, null, _parent, _scopeId));
1046
+ else _push(`<!---->`);
1047
+ } else return [unref(step) === "loading" ? (openBlock(), createBlock(_component_ScenarioReviewLoading, {
1048
+ key: 0,
1049
+ auth: unref(isAuth)
1050
+ }, null, 8, ["auth"])) : unref(step) === "edit" ? (openBlock(), createBlock(Fragment, { key: 1 }, [createVNode(_component_ScenarioWarnings, {
1051
+ warnings: unref(draft).warnings,
1052
+ slug: __props.slug,
1053
+ class: "mb-4"
1054
+ }, null, 8, ["warnings", "slug"]), createVNode(_component_ScenarioReviewContexts, {
1055
+ draft: unref(draft),
1056
+ "onUpdate:draft": ($event) => isRef(draft) ? draft.value = $event : null,
1057
+ novo: ""
1058
+ }, null, 8, ["draft", "onUpdate:draft"])], 64)) : (openBlock(), createBlock(_component_ScenarioReviewTimeline, {
1059
+ key: 2,
1060
+ events: unref(timeline),
1061
+ "video-src": unref(state).videoSessionId ? `${unref(url)}/recording/${unref(state).videoSessionId}` : null,
1062
+ "recording-started-at": unref(state).recordingStartedAt,
1063
+ resumable: "",
1064
+ onResume: resume
1065
+ }, null, 8, [
1066
+ "events",
1067
+ "video-src",
1068
+ "recording-started-at"
1069
+ ])), unref(error) ? (openBlock(), createBlock(_component_UAlert, {
1070
+ key: 3,
1071
+ color: "error",
1072
+ variant: "soft",
1073
+ description: unref(error),
1074
+ class: "mt-4",
1075
+ "data-testid": "revisao-erro"
1076
+ }, null, 8, ["description"])) : createCommentVNode("", true)];
1077
+ }),
1078
+ footer: withCtx((_, _push, _parent, _scopeId) => {
1079
+ if (_push) {
1080
+ if (unref(step) === "edit") {
1081
+ _push(`<!--[-->`);
1082
+ _push(ssrRenderComponent(_component_UButton, {
1083
+ label: "Cancelar",
1084
+ color: "neutral",
1085
+ variant: "ghost",
1086
+ "data-testid": "contexto-cancelar",
1087
+ onClick: ($event) => open.value = false
1088
+ }, null, _parent, _scopeId));
1089
+ _push(ssrRenderComponent(_component_UButton, {
1090
+ label: "Enviar",
1091
+ loading: unref(submitting),
1092
+ "data-testid": "contexto-enviar",
1093
+ onClick: commit
1094
+ }, null, _parent, _scopeId));
1095
+ _push(`<!--]-->`);
1096
+ } else {
1097
+ _push(`<!--[-->`);
1098
+ _push(ssrRenderComponent(_component_UButton, {
1099
+ label: "Gravar novamente",
1100
+ color: "neutral",
1101
+ variant: "soft",
1102
+ class: "mr-auto",
1103
+ "data-testid": "revisao-regravar",
1104
+ onClick: rerecord
1105
+ }, null, _parent, _scopeId));
1106
+ _push(ssrRenderComponent(_component_UButton, {
1107
+ label: "Cancelar",
1108
+ color: "neutral",
1109
+ variant: "ghost",
1110
+ "data-testid": "revisao-cancelar",
1111
+ onClick: ($event) => open.value = false
1112
+ }, null, _parent, _scopeId));
1113
+ _push(ssrRenderComponent(_component_UButton, {
1114
+ label: unref(generateLabel),
1115
+ "data-testid": "revisao-gerar",
1116
+ onClick: generate
1117
+ }, null, _parent, _scopeId));
1118
+ _push(`<!--]-->`);
1119
+ }
1120
+ } else return [unref(step) === "edit" ? (openBlock(), createBlock(Fragment, { key: 0 }, [createVNode(_component_UButton, {
1121
+ label: "Cancelar",
1122
+ color: "neutral",
1123
+ variant: "ghost",
1124
+ "data-testid": "contexto-cancelar",
1125
+ onClick: ($event) => open.value = false
1126
+ }, null, 8, ["onClick"]), createVNode(_component_UButton, {
1127
+ label: "Enviar",
1128
+ loading: unref(submitting),
1129
+ "data-testid": "contexto-enviar",
1130
+ onClick: commit
1131
+ }, null, 8, ["loading"])], 64)) : (openBlock(), createBlock(Fragment, { key: 1 }, [
1132
+ createVNode(_component_UButton, {
1133
+ label: "Gravar novamente",
1134
+ color: "neutral",
1135
+ variant: "soft",
1136
+ class: "mr-auto",
1137
+ "data-testid": "revisao-regravar",
1138
+ onClick: rerecord
1139
+ }),
1140
+ createVNode(_component_UButton, {
1141
+ label: "Cancelar",
1142
+ color: "neutral",
1143
+ variant: "ghost",
1144
+ "data-testid": "revisao-cancelar",
1145
+ onClick: ($event) => open.value = false
1146
+ }, null, 8, ["onClick"]),
1147
+ createVNode(_component_UButton, {
1148
+ label: unref(generateLabel),
1149
+ "data-testid": "revisao-gerar",
1150
+ onClick: generate
1151
+ }, null, 8, ["label"])
1152
+ ], 64))];
1153
+ }),
1154
+ _: 1
1155
+ }, _parent));
1156
+ };
1157
+ }
1158
+ });
1159
+ //#endregion
1160
+ //#region app/components/scenario/review/modal.vue
1161
+ var _sfc_setup$2 = modal_vue_vue_type_script_setup_true_lang_default$1.setup;
1162
+ modal_vue_vue_type_script_setup_true_lang_default$1.setup = (props, ctx) => {
1163
+ const ssrContext = useSSRContext();
1164
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/review/modal.vue");
1165
+ return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
1166
+ };
1167
+ var modal_default$1 = Object.assign(modal_vue_vue_type_script_setup_true_lang_default$1, { __name: "ScenarioReviewModal" });
1168
+ //#endregion
1169
+ //#region app/components/scenario/test-run/steps.vue?vue&type=script&setup=true&lang.ts
1170
+ var steps_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
1171
+ __name: "steps",
1172
+ __ssrInlineRender: true,
1173
+ props: { steps: { default: () => [] } },
1174
+ setup(__props) {
1175
+ const stepIcons = {
1176
+ waiting: "i-ic-round-radio-button-unchecked",
1177
+ running: "line-md:loading-twotone-loop",
1178
+ success: "i-ic-round-check-circle",
1179
+ failed: "i-ic-round-error"
1180
+ };
1181
+ const stepColors = {
1182
+ waiting: "text-dimmed",
1183
+ running: "text-neutral",
1184
+ success: "text-success",
1185
+ failed: "text-error"
1186
+ };
1187
+ return (_ctx, _push, _parent, _attrs) => {
1188
+ const _component_UIcon = _sfc_main$5;
1189
+ _push(`<ol${ssrRenderAttrs(mergeProps({ class: "flex flex-col" }, _attrs))}><!--[-->`);
1190
+ ssrRenderList(__props.steps, (step, i) => {
1191
+ _push(`<li data-testid="execucao-step"${ssrRenderAttr("data-status", step.status)} class="flex items-stretch gap-3 pb-4 last:pb-0"><div class="flex flex-col items-center">`);
1192
+ _push(ssrRenderComponent(_component_UIcon, {
1193
+ name: stepIcons[step.status],
1194
+ class: ["size-6 shrink-0 transition-colors", stepColors[step.status]]
1195
+ }, null, _parent));
1196
+ if (i < __props.steps.length - 1) _push(`<span class="w-px grow min-h-4 -mb-4 bg-inverted/30"></span>`);
1197
+ else _push(`<!---->`);
1198
+ _push(`</div><div class="min-w-0"><p class="text-base" data-testid="execucao-step-titulo">${ssrInterpolate(step.title)}</p>`);
1199
+ if (step.status === "failed" && step.error) _push(`<p class="text-sm text-dimmed mt-1" data-testid="execucao-step-erro"><span class="font-semibold text-error">Erro:</span> ${ssrInterpolate(step.error)}</p>`);
1200
+ else _push(`<!---->`);
1201
+ _push(`</div></li>`);
1202
+ });
1203
+ _push(`<!--]--></ol>`);
1204
+ };
1205
+ }
1206
+ });
1207
+ //#endregion
1208
+ //#region app/components/scenario/test-run/steps.vue
1209
+ var _sfc_setup$1 = steps_vue_vue_type_script_setup_true_lang_default.setup;
1210
+ steps_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
1211
+ const ssrContext = useSSRContext();
1212
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/test-run/steps.vue");
1213
+ return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
1214
+ };
1215
+ var steps_default = Object.assign(steps_vue_vue_type_script_setup_true_lang_default, { __name: "ScenarioTestRunSteps" });
1216
+ //#endregion
1217
+ //#region app/components/scenario/test-run/modal.vue?vue&type=script&setup=true&lang.ts
1218
+ var modal_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
1219
+ __name: "modal",
1220
+ __ssrInlineRender: true,
1221
+ props: /*@__PURE__*/ mergeModels({
1222
+ running: {
1223
+ type: Boolean,
1224
+ default: false
1225
+ },
1226
+ passed: {
1227
+ type: Boolean,
1228
+ default: false
1229
+ },
1230
+ steps: { default: () => [] },
1231
+ videoUrl: { default: null },
1232
+ projectName: {},
1233
+ scenarioName: {},
1234
+ branch: { default: null },
1235
+ testedAt: { default: null },
1236
+ playwright: { default: null },
1237
+ output: { default: null },
1238
+ kind: { default: "cenario" }
1239
+ }, {
1240
+ "open": {
1241
+ type: Boolean,
1242
+ default: false
1243
+ },
1244
+ "openModifiers": {}
1245
+ }),
1246
+ emits: /*@__PURE__*/ mergeModels(["fix"], ["update:open"]),
1247
+ setup(__props, { emit: __emit }) {
1248
+ const emit = __emit;
1249
+ const open = useModel(__props, "open");
1250
+ const { configured: aiConfigured } = useAi();
1251
+ const isAuth = computed(() => __props.kind === "autenticacao");
1252
+ const label = computed(() => isAuth.value ? "Autenticação" : "Cenário");
1253
+ const runningTitle = computed(() => isAuth.value ? "Testando autenticação..." : "Testando cenário...");
1254
+ const resultTitle = computed(() => isAuth.value ? "Resultado da autenticação" : "Resultado do teste");
1255
+ const failedStep = computed(() => __props.steps.findIndex((step) => step.status === "failed"));
1256
+ function seekToPreviewFrame(event) {
1257
+ const video = event.target;
1258
+ video.currentTime = video.duration * .25;
1259
+ }
1260
+ return (_ctx, _push, _parent, _attrs) => {
1261
+ const _component_BaseModal = modal_default$2;
1262
+ const _component_UBadge = _sfc_main$2;
1263
+ const _component_UTooltip = _sfc_main$7;
1264
+ const _component_UButton = _sfc_main$1;
1265
+ const _component_BaseLoadingPhrases = phrases_default;
1266
+ const _component_UAlert = _sfc_main;
1267
+ const _component_ScenarioTestRunSteps = steps_default;
1268
+ const _component_BaseCodefield = codefield_default;
1269
+ _push(ssrRenderComponent(_component_BaseModal, mergeProps({
1270
+ open: open.value,
1271
+ "onUpdate:open": ($event) => open.value = $event,
1272
+ dismissable: !__props.running,
1273
+ wide: ""
1274
+ }, _attrs), createSlots({
1275
+ header: withCtx((_, _push, _parent, _scopeId) => {
1276
+ if (_push) {
1277
+ _push(`<div class="flex w-full items-start justify-between gap-4"${_scopeId}><div class="flex flex-col pt-6 gap-2"${_scopeId}>`);
1278
+ if (__props.running) _push(`<p class="text-xl font-bold"${_scopeId}>${ssrInterpolate(unref(runningTitle))}</p>`);
1279
+ else {
1280
+ _push(`<!--[-->`);
1281
+ _push(ssrRenderComponent(_component_UBadge, {
1282
+ class: "self-start",
1283
+ color: __props.passed ? "success" : "error",
1284
+ icon: __props.passed ? "i-ic-round-check-circle" : "i-ic-round-error",
1285
+ label: __props.passed ? "Sucesso" : "Falha",
1286
+ "data-testid": "execucao-status"
1287
+ }, null, _parent, _scopeId));
1288
+ _push(`<p class="mt-2 text-xl font-bold"${_scopeId}>${ssrInterpolate(unref(resultTitle))}</p><!--]-->`);
1289
+ }
1290
+ _push(`</div>`);
1291
+ if (!__props.running && !__props.passed && unref(failedStep) !== -1) _push(ssrRenderComponent(_component_UTooltip, {
1292
+ text: unref(AI_OFF_HINT),
1293
+ disabled: unref(aiConfigured),
1294
+ "delay-duration": 0,
1295
+ arrow: ""
1296
+ }, {
1297
+ default: withCtx((_, _push, _parent, _scopeId) => {
1298
+ if (_push) {
1299
+ _push(`<span class="mt-6 shrink-0"${_scopeId}>`);
1300
+ _push(ssrRenderComponent(_component_UButton, {
1301
+ label: "Corrigir",
1302
+ "trailing-icon": "i-ic-round-auto-awesome",
1303
+ disabled: !unref(aiConfigured),
1304
+ class: unref(aiConfigured) ? "" : "pointer-events-none",
1305
+ "data-testid": "execucao-corrigir",
1306
+ onClick: ($event) => emit("fix")
1307
+ }, null, _parent, _scopeId));
1308
+ _push(`</span>`);
1309
+ } else return [createVNode("span", { class: "mt-6 shrink-0" }, [createVNode(_component_UButton, {
1310
+ label: "Corrigir",
1311
+ "trailing-icon": "i-ic-round-auto-awesome",
1312
+ disabled: !unref(aiConfigured),
1313
+ class: unref(aiConfigured) ? "" : "pointer-events-none",
1314
+ "data-testid": "execucao-corrigir",
1315
+ onClick: ($event) => emit("fix")
1316
+ }, null, 8, [
1317
+ "disabled",
1318
+ "class",
1319
+ "onClick"
1320
+ ])])];
1321
+ }),
1322
+ _: 1
1323
+ }, _parent, _scopeId));
1324
+ else _push(`<!---->`);
1325
+ _push(`</div>`);
1326
+ } else return [createVNode("div", { class: "flex w-full items-start justify-between gap-4" }, [createVNode("div", { class: "flex flex-col pt-6 gap-2" }, [__props.running ? (openBlock(), createBlock("p", {
1327
+ key: 0,
1328
+ class: "text-xl font-bold"
1329
+ }, toDisplayString(unref(runningTitle)), 1)) : (openBlock(), createBlock(Fragment, { key: 1 }, [createVNode(_component_UBadge, {
1330
+ class: "self-start",
1331
+ color: __props.passed ? "success" : "error",
1332
+ icon: __props.passed ? "i-ic-round-check-circle" : "i-ic-round-error",
1333
+ label: __props.passed ? "Sucesso" : "Falha",
1334
+ "data-testid": "execucao-status"
1335
+ }, null, 8, [
1336
+ "color",
1337
+ "icon",
1338
+ "label"
1339
+ ]), createVNode("p", { class: "mt-2 text-xl font-bold" }, toDisplayString(unref(resultTitle)), 1)], 64))]), !__props.running && !__props.passed && unref(failedStep) !== -1 ? (openBlock(), createBlock(_component_UTooltip, {
1340
+ key: 0,
1341
+ text: unref(AI_OFF_HINT),
1342
+ disabled: unref(aiConfigured),
1343
+ "delay-duration": 0,
1344
+ arrow: ""
1345
+ }, {
1346
+ default: withCtx(() => [createVNode("span", { class: "mt-6 shrink-0" }, [createVNode(_component_UButton, {
1347
+ label: "Corrigir",
1348
+ "trailing-icon": "i-ic-round-auto-awesome",
1349
+ disabled: !unref(aiConfigured),
1350
+ class: unref(aiConfigured) ? "" : "pointer-events-none",
1351
+ "data-testid": "execucao-corrigir",
1352
+ onClick: ($event) => emit("fix")
1353
+ }, null, 8, [
1354
+ "disabled",
1355
+ "class",
1356
+ "onClick"
1357
+ ])])]),
1358
+ _: 1
1359
+ }, 8, ["text", "disabled"])) : createCommentVNode("", true)])];
1360
+ }),
1361
+ body: withCtx((_, _push, _parent, _scopeId) => {
1362
+ if (_push) {
1363
+ if (__props.running && __props.steps.length === 0) _push(ssrRenderComponent(_component_BaseLoadingPhrases, {
1364
+ phrases: ["Iniciando a execução"],
1365
+ "data-testid": "execucao-iniciando"
1366
+ }, null, _parent, _scopeId));
1367
+ else {
1368
+ _push(`<!--[-->`);
1369
+ if (!__props.running) {
1370
+ _push(`<div class="mb-6 space-y-0.5 text-sm" data-testid="execucao-detalhes"${_scopeId}><p${_scopeId}><span class="font-semibold"${_scopeId}>Projeto:</span> ${ssrInterpolate(__props.projectName)}</p><p${_scopeId}><span class="font-semibold"${_scopeId}>${ssrInterpolate(unref(label))}:</span> ${ssrInterpolate(__props.scenarioName)}</p>`);
1371
+ if (__props.branch) _push(`<p${_scopeId}><span class="font-semibold"${_scopeId}>Branch:</span> ${ssrInterpolate(__props.branch)}</p>`);
1372
+ else _push(`<!---->`);
1373
+ if (__props.testedAt) _push(`<p${_scopeId}><span class="font-semibold"${_scopeId}>Testado em:</span> ${ssrInterpolate(__props.testedAt)}</p>`);
1374
+ else _push(`<!---->`);
1375
+ _push(`</div>`);
1376
+ } else _push(`<!---->`);
1377
+ if (__props.videoUrl) _push(`<video${ssrRenderAttr("src", __props.videoUrl)} preload="metadata" controls class="mx-auto mb-6 w-3/4 rounded-lg bg-elevated" data-testid="execucao-video"${_scopeId}></video>`);
1378
+ else _push(`<!---->`);
1379
+ if (__props.output) _push(ssrRenderComponent(_component_UAlert, {
1380
+ color: "error",
1381
+ variant: "soft",
1382
+ icon: "i-ic-round-error",
1383
+ title: "A execução não chegou a começar",
1384
+ class: "mb-6",
1385
+ "data-testid": "execucao-saida"
1386
+ }, {
1387
+ description: withCtx((_, _push, _parent, _scopeId) => {
1388
+ if (_push) _push(`<p class="mb-2"${_scopeId}> O Playwright encerrou antes de rodar qualquer passo. Saída do runner: </p><code class="block whitespace-pre-wrap font-mono text-xs"${_scopeId}>${ssrInterpolate(__props.output)}</code>`);
1389
+ else return [createVNode("p", { class: "mb-2" }, " O Playwright encerrou antes de rodar qualquer passo. Saída do runner: "), createVNode("code", { class: "block whitespace-pre-wrap font-mono text-xs" }, toDisplayString(__props.output), 1)];
1390
+ }),
1391
+ _: 1
1392
+ }, _parent, _scopeId));
1393
+ else _push(`<!---->`);
1394
+ if (__props.steps.length) _push(`<p class="mb-3 text-lg font-semibold"${_scopeId}> Timeline de eventos </p>`);
1395
+ else _push(`<!---->`);
1396
+ _push(ssrRenderComponent(_component_ScenarioTestRunSteps, { steps: __props.steps }, null, _parent, _scopeId));
1397
+ if (__props.playwright) {
1398
+ _push(`<!--[--><p class="mt-6 mb-3 text-lg font-semibold"${_scopeId}> Código executado </p>`);
1399
+ _push(ssrRenderComponent(_component_BaseCodefield, {
1400
+ "model-value": __props.playwright,
1401
+ language: "typescript",
1402
+ readonly: "",
1403
+ testid: "execucao-playwright"
1404
+ }, null, _parent, _scopeId));
1405
+ _push(`<!--]-->`);
1406
+ } else _push(`<!---->`);
1407
+ _push(`<!--]-->`);
1408
+ }
1409
+ } else return [__props.running && __props.steps.length === 0 ? (openBlock(), createBlock(_component_BaseLoadingPhrases, {
1410
+ key: 0,
1411
+ phrases: ["Iniciando a execução"],
1412
+ "data-testid": "execucao-iniciando"
1413
+ })) : (openBlock(), createBlock(Fragment, { key: 1 }, [
1414
+ !__props.running ? (openBlock(), createBlock("div", {
1415
+ key: 0,
1416
+ class: "mb-6 space-y-0.5 text-sm",
1417
+ "data-testid": "execucao-detalhes"
1418
+ }, [
1419
+ createVNode("p", null, [createVNode("span", { class: "font-semibold" }, "Projeto:"), createTextVNode(" " + toDisplayString(__props.projectName), 1)]),
1420
+ createVNode("p", null, [createVNode("span", { class: "font-semibold" }, toDisplayString(unref(label)) + ":", 1), createTextVNode(" " + toDisplayString(__props.scenarioName), 1)]),
1421
+ __props.branch ? (openBlock(), createBlock("p", { key: 0 }, [createVNode("span", { class: "font-semibold" }, "Branch:"), createTextVNode(" " + toDisplayString(__props.branch), 1)])) : createCommentVNode("", true),
1422
+ __props.testedAt ? (openBlock(), createBlock("p", { key: 1 }, [createVNode("span", { class: "font-semibold" }, "Testado em:"), createTextVNode(" " + toDisplayString(__props.testedAt), 1)])) : createCommentVNode("", true)
1423
+ ])) : createCommentVNode("", true),
1424
+ __props.videoUrl ? (openBlock(), createBlock("video", {
1425
+ key: 1,
1426
+ src: __props.videoUrl,
1427
+ preload: "metadata",
1428
+ controls: "",
1429
+ class: "mx-auto mb-6 w-3/4 rounded-lg bg-elevated",
1430
+ "data-testid": "execucao-video",
1431
+ onLoadedmetadata: seekToPreviewFrame
1432
+ }, null, 40, ["src"])) : createCommentVNode("", true),
1433
+ __props.output ? (openBlock(), createBlock(_component_UAlert, {
1434
+ key: 2,
1435
+ color: "error",
1436
+ variant: "soft",
1437
+ icon: "i-ic-round-error",
1438
+ title: "A execução não chegou a começar",
1439
+ class: "mb-6",
1440
+ "data-testid": "execucao-saida"
1441
+ }, {
1442
+ description: withCtx(() => [createVNode("p", { class: "mb-2" }, " O Playwright encerrou antes de rodar qualquer passo. Saída do runner: "), createVNode("code", { class: "block whitespace-pre-wrap font-mono text-xs" }, toDisplayString(__props.output), 1)]),
1443
+ _: 1
1444
+ })) : createCommentVNode("", true),
1445
+ __props.steps.length ? (openBlock(), createBlock("p", {
1446
+ key: 3,
1447
+ class: "mb-3 text-lg font-semibold"
1448
+ }, " Timeline de eventos ")) : createCommentVNode("", true),
1449
+ createVNode(_component_ScenarioTestRunSteps, { steps: __props.steps }, null, 8, ["steps"]),
1450
+ __props.playwright ? (openBlock(), createBlock(Fragment, { key: 4 }, [createVNode("p", { class: "mt-6 mb-3 text-lg font-semibold" }, " Código executado "), createVNode(_component_BaseCodefield, {
1451
+ "model-value": __props.playwright,
1452
+ language: "typescript",
1453
+ readonly: "",
1454
+ testid: "execucao-playwright"
1455
+ }, null, 8, ["model-value"])], 64)) : createCommentVNode("", true)
1456
+ ], 64))];
1457
+ }),
1458
+ _: 2
1459
+ }, [!__props.running ? {
1460
+ name: "footer",
1461
+ fn: withCtx((_, _push, _parent, _scopeId) => {
1462
+ if (_push) _push(ssrRenderComponent(_component_UButton, {
1463
+ label: "Fechar",
1464
+ color: "neutral",
1465
+ variant: "ghost",
1466
+ "data-testid": "execucao-fechar",
1467
+ onClick: ($event) => open.value = false
1468
+ }, null, _parent, _scopeId));
1469
+ else return [createVNode(_component_UButton, {
1470
+ label: "Fechar",
1471
+ color: "neutral",
1472
+ variant: "ghost",
1473
+ "data-testid": "execucao-fechar",
1474
+ onClick: ($event) => open.value = false
1475
+ }, null, 8, ["onClick"])];
1476
+ }),
1477
+ key: "0"
1478
+ } : void 0]), _parent));
1479
+ };
1480
+ }
1481
+ });
1482
+ //#endregion
1483
+ //#region app/components/scenario/test-run/modal.vue
1484
+ var _sfc_setup = modal_vue_vue_type_script_setup_true_lang_default.setup;
1485
+ modal_vue_vue_type_script_setup_true_lang_default.setup = (props, ctx) => {
1486
+ const ssrContext = useSSRContext();
1487
+ (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/scenario/test-run/modal.vue");
1488
+ return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
1489
+ };
1490
+ var modal_default = Object.assign(modal_vue_vue_type_script_setup_true_lang_default, { __name: "ScenarioTestRunModal" });
1491
+ //#endregion
1492
+ //#region app/composables/run-stream.ts
1493
+ /** O que o Playwright reporta por passo/teste, no vocabulário da timeline. */
1494
+ var STATUS = {
1495
+ pending: "running",
1496
+ success: "success",
1497
+ failed: "failed",
1498
+ skipped: "waiting"
1499
+ };
1500
+ function videoUrlFor(path) {
1501
+ return `/runner/video?${new URLSearchParams({ path })}`;
1502
+ }
1503
+ /** O relatório HTML da última execução do projeto, servido pelo backend com vídeo e trace dentro. */
1504
+ function reportUrlFor(slug) {
1505
+ return `/api/projects/${slug}/report/`;
1506
+ }
1507
+ function formatTestedAt(date) {
1508
+ return date.toLocaleString("pt-BR", {
1509
+ dateStyle: "short",
1510
+ timeStyle: "short"
1511
+ });
1512
+ }
1513
+ /** Execução de um spec pelo backend, com a timeline chegando passo a passo por SSE. */
1514
+ function useRunStream(slug) {
1515
+ const steps = ref([]);
1516
+ const ran = ref([]);
1517
+ const running = ref(false);
1518
+ const passed = ref(false);
1519
+ const videoUrl = ref(null);
1520
+ const testedAt = ref(null);
1521
+ const output = ref(null);
1522
+ /** A ordem é a que o teste declarou; o passo que ele nunca anunciou entra no fim, na chegada. */
1523
+ const tests = computed(() => ran.value.map((test) => ({
1524
+ ...test,
1525
+ steps: steps.value.map((step, arrival) => ({
1526
+ step,
1527
+ arrival
1528
+ })).filter(({ step }) => step.testId === test.id).sort((a, b) => (a.step.order ?? Infinity) - (b.step.order ?? Infinity) || a.arrival - b.arrival).map(({ step }) => step)
1529
+ })));
1530
+ function reset() {
1531
+ steps.value = [];
1532
+ ran.value = [];
1533
+ videoUrl.value = null;
1534
+ testedAt.value = null;
1535
+ output.value = null;
1536
+ passed.value = false;
1537
+ }
1538
+ /** O passo pertence ao teste do evento; a semente da timeline ainda não tem dono e serve a qualquer um. */
1539
+ function sameTest(step, event) {
1540
+ return !event.testId || !step.testId || step.testId === event.testId;
1541
+ }
1542
+ /** A timeline semeada pelo run inteiro não tem dono: o teste adota a que é dele antes de criar a sua. */
1543
+ function seed(testId, titles) {
1544
+ const seeded = [...steps.value];
1545
+ titles.forEach((title, order) => {
1546
+ const orphan = seeded.findIndex((step) => step.title === title && step.status === "waiting" && !step.testId);
1547
+ const step = {
1548
+ testId,
1549
+ order,
1550
+ title,
1551
+ status: "waiting"
1552
+ };
1553
+ if (orphan === -1) seeded.push(step);
1554
+ else seeded[orphan] = step;
1555
+ });
1556
+ steps.value = seeded;
1557
+ }
1558
+ function start({ spec, grep }, onFinish) {
1559
+ reset();
1560
+ running.value = true;
1561
+ const query = new URLSearchParams({
1562
+ ...spec ? { spec } : {},
1563
+ ...grep ? { grep } : {}
1564
+ });
1565
+ const source = new EventSource(`/api/projects/${slug()}/run-stream?${query}`);
1566
+ source.onmessage = (message) => {
1567
+ const data = JSON.parse(message.data);
1568
+ if (data.event === "run:started") steps.value = (data.steps ?? []).map((title) => ({
1569
+ title,
1570
+ status: "waiting"
1571
+ }));
1572
+ if (data.event === "step") {
1573
+ if (data.status === "pending") {
1574
+ const own = steps.value.findIndex((step) => step.title === data.title && step.status === "waiting" && step.testId === data.testId);
1575
+ const waiting = own !== -1 ? own : steps.value.findIndex((step) => step.title === data.title && step.status === "waiting" && sameTest(step, data));
1576
+ const seeded = waiting === -1 ? void 0 : steps.value[waiting];
1577
+ const step = {
1578
+ testId: data.testId,
1579
+ order: seeded?.order,
1580
+ title: data.title,
1581
+ status: "running"
1582
+ };
1583
+ if (waiting === -1) steps.value = [...steps.value, step];
1584
+ else steps.value[waiting] = step;
1585
+ return;
1586
+ }
1587
+ const running = steps.value.findLastIndex((step) => step.title === data.title && step.status === "running" && sameTest(step, data));
1588
+ const index = running !== -1 || data.status !== "failed" ? running : steps.value.findLastIndex((step) => step.title === data.title && step.status === "success" && sameTest(step, data));
1589
+ if (index !== -1) steps.value[index] = {
1590
+ testId: steps.value[index].testId ?? data.testId,
1591
+ order: steps.value[index].order,
1592
+ title: data.title,
1593
+ status: STATUS[data.status],
1594
+ error: data.error ?? null
1595
+ };
1596
+ }
1597
+ if (data.event === "test") {
1598
+ if (data.id) {
1599
+ if (!ran.value.some((previous) => previous.id === data.id) && data.steps?.length) seed(data.id, data.steps);
1600
+ const test = {
1601
+ id: data.id,
1602
+ title: data.title,
1603
+ status: STATUS[data.status],
1604
+ error: data.error ?? null
1605
+ };
1606
+ const known = ran.value.findIndex((previous) => previous.id === data.id);
1607
+ if (known === -1) ran.value = [...ran.value, test];
1608
+ else ran.value[known] = test;
1609
+ }
1610
+ if (data.status !== "pending" && data.videoPath) videoUrl.value = videoUrlFor(data.videoPath);
1611
+ }
1612
+ if (data.event === "run:finished") {
1613
+ source.close();
1614
+ running.value = false;
1615
+ passed.value = data.passed;
1616
+ output.value = data.output ?? null;
1617
+ testedAt.value = formatTestedAt(/* @__PURE__ */ new Date());
1618
+ onFinish?.();
1619
+ }
1620
+ };
1621
+ source.onerror = () => {
1622
+ source.close();
1623
+ running.value = false;
1624
+ testedAt.value = formatTestedAt(/* @__PURE__ */ new Date());
1625
+ if (steps.value.length === 0) steps.value = [{
1626
+ title: "Não foi possível executar o teste.",
1627
+ status: "failed"
1628
+ }];
1629
+ };
1630
+ }
1631
+ return {
1632
+ steps,
1633
+ tests,
1634
+ running,
1635
+ passed,
1636
+ videoUrl,
1637
+ testedAt,
1638
+ output,
1639
+ failedStep: computed(() => steps.value.find((step) => step.status === "failed")),
1640
+ start,
1641
+ reset
1642
+ };
1643
+ }
1644
+ //#endregion
1645
+ //#region app/utils/tags.ts
1646
+ function tagColor(tag) {
1647
+ if (tag === "@read") return "success";
1648
+ if (tag === "@write") return "warning";
1649
+ return "primary";
1650
+ }
1651
+
1652
+ export { useWebdriver as a, timeline_default as b, codefield_default as c, confirm_default as d, empty_default as e, modal_default as f, formatTestedAt as g, draftFromScenario as h, contexts_default as i, steps_default as j, modal_default$1 as m, phrases_default as p, reportUrlFor as r, setup_default as s, tagColor as t, useRunStream as u, videoUrlFor as v, warnings_default as w };
1653
+ //# sourceMappingURL=tags-DXumXgr9.mjs.map