cabloy 5.1.140 → 5.1.142

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 (142) hide show
  1. package/.cabloy-version +1 -1
  2. package/.github/workflows/playwright-e2e.yml +2 -2
  3. package/CHANGELOG.md +32 -0
  4. package/cabloy-docs/frontend/form-guide.md +47 -0
  5. package/cabloy-docs/frontend/markdown-guide.md +50 -17
  6. package/cabloy-docs/frontend/route-alias-guide.md +20 -0
  7. package/cabloy-docs/frontend/scripts.md +9 -11
  8. package/cabloy-docs/frontend/zova-form-under-the-hood.md +25 -0
  9. package/cabloy-docs/fullstack/framework-performance.md +3 -3
  10. package/cabloy-docs/fullstack/parallel-worktree-environment.md +1 -1
  11. package/cabloy-docs/fullstack/quickstart.md +5 -4
  12. package/cabloy-docs/reference/repo-scripts.md +29 -42
  13. package/e2e/config/playwright.config.ts +34 -0
  14. package/e2e/scripts/e2e.ts +2 -27
  15. package/e2e/scripts/runE2e.ts +62 -0
  16. package/e2e/scripts/runE2eArgs.test.ts +75 -0
  17. package/e2e/scripts/runE2eArgs.ts +158 -0
  18. package/e2e/specs/{a-commerce/commerce.spec.ts → a-commerce.spec.ts} +364 -33
  19. package/e2e/specs/{cabloy-basic/account.spec.ts → account.spec.ts} +1 -2
  20. package/e2e/specs/{cabloy-basic/basic.spec.ts → cabloy-basic.spec.ts} +6 -8
  21. package/oxlint.config.ts +3 -0
  22. package/package.json +3 -9
  23. package/scripts/upgrade.ts +63 -68
  24. package/vona/packages-utils/lint/package.json +1 -1
  25. package/vona/packages-utils/lint/src/oxc/lint.ts +1 -1
  26. package/vona/packages-utils/lint/src/oxc/lintVue.ts +1 -1
  27. package/vona/pnpm-lock.yaml +100 -657
  28. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +7 -2
  29. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +31 -15
  30. package/vona/src/suite/a-home/modules/home-user/package.json +1 -0
  31. package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +15 -2
  32. package/vona/src/suite/a-home/modules/home-user/test/accountActivation.test.ts +34 -4
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/package.json +2 -1
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/index.ts +26 -0
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/bean/imageScene.markdown.ts +13 -0
  36. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  37. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  38. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +23 -0
  39. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +7 -0
  40. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  41. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  42. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  43. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  44. package/zova/packages-utils/zova-jsx/package.json +2 -2
  45. package/zova/packages-zova/zova/package.json +3 -3
  46. package/zova/packages-zova/zova-core/package.json +1 -1
  47. package/zova/packages-zova/zova-core/src/bean/resource/locale/type.ts +1 -0
  48. package/zova/pnpm-lock.yaml +63 -18
  49. package/zova/src/front/config/config/config.cabloyBasicWeb.ts +3 -1
  50. package/zova/src/front/config/config/config.cabloyCommerce.ts +4 -2
  51. package/zova/src/suite/a-commerce/modules/commerce-catalog/src/api/openapi/schemas.ts +0 -11
  52. package/zova/src/suite/a-commerce/modules/commerce-catalog/src/api/openapi/types.ts +23 -170
  53. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +0 -11
  54. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +23 -170
  55. package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +64 -63
  56. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/schemas.ts +0 -11
  57. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/types.ts +23 -170
  58. package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/api/openapi/schemas.ts +0 -11
  59. package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/api/openapi/types.ts +23 -170
  60. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +0 -11
  61. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +29 -171
  62. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +413 -134
  63. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +76 -0
  64. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +67 -0
  65. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +1 -0
  66. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +1 -1
  67. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +7 -7
  68. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +46 -9
  69. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +50 -30
  70. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/schemas.ts +0 -11
  71. package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +23 -170
  72. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +0 -11
  73. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +23 -170
  74. package/zova/src/suite/cabloy-basic/modules/basic-captcha/src/component/formFieldCaptcha/controller.tsx +6 -2
  75. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +0 -11
  76. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +23 -170
  77. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +0 -11
  78. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +23 -170
  79. package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +9 -15
  80. package/zova/src/suite/cabloy-basic/modules/basic-markdown/package.json +10 -2
  81. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/index.ts +11 -2
  82. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/locales.ts +7 -0
  83. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/controller.tsx +723 -5
  84. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/render.tsx +461 -2
  85. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/en-us.ts +59 -0
  86. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/zh-cn.ts +59 -0
  87. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/index.ts +1 -0
  88. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/codeBlockLanguages.ts +42 -0
  89. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +20 -0
  90. package/zova/src/suite/cabloy-basic/modules/basic-markdown/test/lib/codeBlockLanguages.test.ts +25 -0
  91. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  92. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -0
  93. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  94. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  95. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/schemas.ts +0 -11
  96. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/types.ts +23 -170
  97. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +26 -12
  98. package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +1 -1
  99. package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  100. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/schemas.ts +0 -11
  101. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/types.ts +23 -170
  102. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockMockPayment.ts +26 -0
  103. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockMockPayment.ts +8 -1
  104. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +13 -1
  105. package/zova/src/suite-vendor/a-pay/package.json +3 -3
  106. package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
  107. package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +2 -0
  108. package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +7 -6
  109. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
  110. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/src/service/ssrHandler.ts +1 -1
  111. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  112. package/zova/src/suite-vendor/a-zova/package.json +4 -4
  113. package/e2e/config/playwright.basic.config.ts +0 -3
  114. package/e2e/config/playwright.commerce.config.ts +0 -3
  115. package/e2e/config/playwright.shared.config.ts +0 -38
  116. package/e2e/scripts/testE2eClean.ts +0 -56
  117. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/package.json +0 -56
  118. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/.metadata/index.ts +0 -404
  119. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/.metadata/locales.ts +0 -18
  120. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/.metadata/this.ts +0 -2
  121. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/bean/bean.paypal.ts +0 -99
  122. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/bean/event.paypalCancelOrder.ts +0 -15
  123. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/bean/event.paypalCaptureOrder.ts +0 -22
  124. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/bean/meta.version.ts +0 -24
  125. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/config/config.ts +0 -14
  126. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/config/errors.ts +0 -3
  127. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/config/locale/en-us.ts +0 -3
  128. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/config/locale/zh-cn.ts +0 -3
  129. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/controller/paypal.ts +0 -39
  130. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/dto/paypalOrderRecordOptions.tsx +0 -31
  131. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/dto/paypalOrderRecordPayload.tsx +0 -18
  132. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/entity/paypalRecord.tsx +0 -30
  133. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/index.ts +0 -3
  134. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/model/paypalRecord.ts +0 -10
  135. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/service/paypal.ts +0 -83
  136. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/types/index.ts +0 -1
  137. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/src/types/paypal.ts +0 -18
  138. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/tsconfig.build.json +0 -11
  139. package/vona/src/suite-vendor/a-paypal/modules/a-paypal/tsconfig.json +0 -7
  140. package/vona/src/suite-vendor/a-paypal/package.json +0 -13
  141. package/vona/src/suite-vendor/a-paypal/tsconfig.base.json +0 -4
  142. package/vona/src/suite-vendor/a-paypal/tsconfig.json +0 -10
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.140
1
+ 5.1.142
@@ -27,7 +27,7 @@ jobs:
27
27
  - name: install Chromium
28
28
  run: npx playwright install --with-deps chromium
29
29
  - name: run Basic E2E baseline
30
- run: npm run test:e2e:basic:clean
30
+ run: npm run test:e2e -- cabloy-basic account
31
31
  - name: upload Playwright artifacts
32
32
  if: failure()
33
33
  uses: actions/upload-artifact@v7
@@ -65,7 +65,7 @@ jobs:
65
65
  - name: synchronize Vona dependencies
66
66
  run: npm run deps:vona
67
67
  - name: run Commerce E2E baseline
68
- run: npm run test:e2e:commerce:clean
68
+ run: npm run test:e2e a-commerce
69
69
  - name: upload Playwright artifacts
70
70
  if: failure()
71
71
  uses: actions/upload-artifact@v7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.142
4
+
5
+ ### Features
6
+
7
+ - Add commerce specifications and refund support.
8
+ - Add address form and consumer URL support.
9
+ - Add captcha support.
10
+ - Add `LocalePattern` support.
11
+ - Add Markdown code blocks, images, links, basic formatting, and tables.
12
+ - Add `onFieldStateReady` documentation.
13
+
14
+ ### Bug Fixes
15
+
16
+ - Fix JSX linting issues.
17
+
18
+ ### Improvements
19
+
20
+ - Refactor end-to-end tests.
21
+ - Regenerate the Vona lockfile.
22
+ - Update account and type definitions.
23
+
24
+ ## 5.1.141
25
+
26
+ ### Features
27
+
28
+ - Update framework functionality and SSR handling.
29
+
30
+ ### Improvements
31
+
32
+ - Update framework performance documentation.
33
+ - Refactor payment integration code by removing PayPal support.
34
+
3
35
  ## 5.1.140
4
36
 
5
37
  ### Improvements
@@ -200,6 +200,53 @@ Use this when you need to:
200
200
  - intercept input and blur behavior yourself
201
201
  - mix Zova form state with a custom render contract
202
202
 
203
+ ### Initialize external state with `onFieldStateReady`
204
+
205
+ A custom field sometimes has two state surfaces:
206
+
207
+ - the `ControllerFormField` state owned by `ZForm`
208
+ - state owned by the custom renderer or an external resource, such as a widget value or a challenge response
209
+
210
+ Use `onFieldStateReady` when the custom field needs to synchronize those surfaces after Zova has prepared or reset the field:
211
+
212
+ ```tsx
213
+ <ZFormField
214
+ name="externalValue"
215
+ onFieldStateReady={formField => {
216
+ if (this.externalValue !== undefined) {
217
+ formField.setValue(this.externalValue);
218
+ }
219
+ }}
220
+ slotDefault={({ props }, formField) => {
221
+ return (
222
+ <input
223
+ {...props}
224
+ value={formField.field.state.value ?? ''}
225
+ onInput={e => {
226
+ const value = (e.target as HTMLInputElement).value;
227
+ this.externalValue = value;
228
+ formField.setValue(value);
229
+ }}
230
+ onBlur={() => formField.handleBlur()}
231
+ ></input>
232
+ );
233
+ }}
234
+ ></ZFormField>
235
+ ```
236
+
237
+ The callback receives the current `ControllerFormField` instance. It is a field-state lifecycle notification, not a DOM or VNode mounted hook, an asynchronous API-completion callback, or a general replacement for `nextTick`.
238
+
239
+ Zova invokes it in two important situations:
240
+
241
+ 1. after the field controller has created its field binding and finished its initialization;
242
+ 2. after a schema-property change updates the field options and calls `resetField(this.name)`.
243
+
244
+ The second timing is important: write the external value in the callback **after** the reset, because a write made before `resetField(...)` can be overwritten. If an external API later creates or refreshes the value, synchronize that API result explicitly as well; `onFieldStateReady` does not fetch, retry, or observe external resources for you.
245
+
246
+ Use input handlers or `formField.setValue(...)` for ordinary value changes, `formField.handleBlur()` for blur state, and `onEffect` when the field needs a value-change effect. `onFieldStateReady` is a replay point for external state after field initialization or reset, not a per-value-change listener.
247
+
248
+ For the source-level ordering and the relationship between field readiness and rendering, see [Zova Form Under the Hood](/frontend/zova-form-under-the-hood).
249
+
203
250
  ### `ZFormFieldBlank`
204
251
 
205
252
  Use `ZFormFieldBlank` when the row is **not** a real data field.
@@ -16,6 +16,7 @@ Use this page together with:
16
16
  - [Component Guide](/frontend/component-guide)
17
17
  - [SSR ClientOnly](/frontend/ssr-client-only)
18
18
  - [Backend Markdown Guide](/backend/markdown-guide)
19
+ - [Image Guide](/frontend/image-guide)
19
20
 
20
21
  > [!TIP]
21
22
  > **Choose the component by data contract**
@@ -63,12 +64,22 @@ export class EntityArticleContent {
63
64
  }
64
65
  ```
65
66
 
66
- The current resource options type does not add business-specific options. The renderer identifier is the important part of the contract:
67
+ The field accepts an optional `imageScene` option. When it is omitted, the editor uploads through the public `a-markdown:markdown` scene supplied by the `a-markdown` module:
67
68
 
68
69
  ```typescript
69
70
  ZovaRender.field('basic-markdown:formFieldMarkdown');
70
71
  ```
71
72
 
73
+ A business module can override that default when its images need resource-specific authorization, ownership metadata, delivery behavior, or upload limits:
74
+
75
+ ```typescript
76
+ ZovaRender.field('basic-markdown:formFieldMarkdown', {
77
+ imageScene: 'article:bodyImage',
78
+ });
79
+ ```
80
+
81
+ The configured scene must be registered by the backend and must return a durable `http` or `https` URL. Define scene policy, authentication, provider, and upload constraints through the image contract described in the [Image Guide](/frontend/image-guide); do not construct storage URLs in the editor.
82
+
72
83
  The field value remains Markdown. It is not the editor's HTML output, and it should not be replaced with `innerHTML` or a browser DOM serialization.
73
84
 
74
85
  A field can keep the derived HTML hidden from the editor-facing form while still exposing it in a separate public DTO. The Commerce Catalog entity follows this shape:
@@ -90,11 +101,11 @@ The exact persistence and DTO design belongs to the business module. Markdown do
90
101
 
91
102
  `ZFormFieldMarkdown` is a Zova component with the normal Controller/Render/Style split.
92
103
 
93
- | Zova role | Responsibility in this component |
94
- | ---------- | ------------------------------------------------------------------------------------------- |
95
- | Controller | Owns the TipTap `Editor`, Markdown `value`, `readonly` state, form callbacks, and lifecycle |
96
- | Render | Composes the standard `ZFormField`, field shell, `ClientOnly`, and `EditorContent` |
97
- | Style | Creates the shared rich-text CSS class used by the editor and HTML display component |
104
+ | Zova role | Responsibility in this component |
105
+ | ---------- | ------------------------------------------------------------------------------------------------------------------- |
106
+ | Controller | Owns the TipTap `Editor`, Markdown `value`, `readonly` state, toolbar commands/state, form callbacks, and lifecycle |
107
+ | Render | Composes the standard `ZFormField`, field shell, client-only toolbar, and `EditorContent` |
108
+ | Style | Creates the shared rich-text CSS class used by the editor and HTML display component |
98
109
 
99
110
  This is a Markdown editor integrated with `ZFormField`; it is not a generic HTML editor API.
100
111
 
@@ -103,7 +114,7 @@ This is a Markdown editor integrated with `ZFormField`; it is not a generic HTML
103
114
  The Controller creates the browser-dependent TipTap editor in `$controllerMounted`. Its extension set includes:
104
115
 
105
116
  - Markdown
106
- - StarterKit
117
+ - StarterKit, with its plain code-block node replaced by `CodeBlockLowlight`
107
118
  - nested `TaskList` and `TaskItem`
108
119
  - Image
109
120
  - TableKit
@@ -119,6 +130,27 @@ editable: !this.readonly,
119
130
 
120
131
  The editor DOM receives the current component style class and a minimum-height/padding class. The outer field shell uses Cabloy Basic's DaisyUI-oriented classes and adds an error border when the host form field is invalid.
121
132
 
133
+ ### Toolbar
134
+
135
+ When the field is editable, the same `ClientOnly` boundary renders a toolbar above the editor. It provides controls for:
136
+
137
+ - undo and redo;
138
+ - paragraph and heading levels 1–6;
139
+ - bold, italic, strike-through, inline code, and highlight;
140
+ - bullet, ordered, and task lists;
141
+ - blockquotes, code blocks, and horizontal rules;
142
+ - applying, updating, and removing links on the current text selection through a URL prompt;
143
+ - uploading one image through the configured image scene and inserting the finalized image at the preserved selection;
144
+ - inserting a table through an 8 × 8 floating size picker. Moving over a cell previews the rectangle from the top-left cell to that cell (for example, 2 × 3); clicking inserts that many rows and columns. Inserted tables retain a header row.
145
+
146
+ The toolbar uses native buttons and a labelled block-style select, preserves the current editor selection while buttons are clicked, exposes active and unavailable states accessibly, and runs commands on the existing TipTap editor. Image upload delegates policy loading, validation, ordinary/direct transfer, and finalization to `basic-image`. Only a finalized URL using `http` or `https` is inserted, with the source filename as image alt text when available. The image node serializes through `getMarkdown()` just like every other editor command; it does not introduce an HTML value path. To add a link, select text and enter a URL; when the cursor is in an existing link, the same action edits its URL. Submitting an empty URL removes an existing link, while cancelling leaves the document unchanged. TipTap validates the URL before applying it, but backend Markdown rendering and sanitization remain the authoritative display security boundary. The table picker also supports keyboard navigation, Enter/Space to insert, and Escape to cancel. In readonly mode, TipTap remains visible but the toolbar and picker are omitted.
147
+
148
+ When the selection is inside an editable table, a separate contextual toolbar appears above that table. It adds and deletes rows or columns around the current cell, and can delete the table. The menu shares the same `ClientOnly`, selection-preservation, accessibility, readonly, and Markdown-serialization guarantees as the persistent toolbar. Column resizing remains out of scope.
149
+
150
+ When the cursor is inside an editable code block, a floating code-block toolbar appears above the block's `<pre>` element. It contains the language selector and supports Bash, CSS, C++, C#, Go, HTML, Java, JavaScript, JSON, Markdown, PHP, Python, Rust, Shell, SQL, and TypeScript, plus Plain text. The selector remains usable while focused and disappears after leaving the code block or entering readonly mode. Changing a language updates the fenced-code information string through TipTap's document attribute and persists as Markdown; lowlight token spans are an editor-only presentation detail and are never submitted as HTML. Existing unknown fence languages remain unchanged until the author explicitly selects a supported language or Plain text.
151
+
152
+ The toolbars intentionally do not include remote image import, a color picker, or a configurable plugin registry. Use the configured image scene for uploads; remote-image import needs its own explicit authorization and persistence contract. Link editing uses the existing Markdown link and backend sanitization contracts.
153
+
122
154
  ### Editor-to-form flow
123
155
 
124
156
  When TipTap emits an update, the controller calls:
@@ -148,8 +180,8 @@ When the component is disposed, the controller calls `editor.destroy()` so TipTa
148
180
  TipTap's `EditorContent` is browser-dependent. The editor is therefore inside Zova's `ClientOnly` component:
149
181
 
150
182
  - SSR renders a minimum-height placeholder;
151
- - the browser creates the editor after mounting;
152
- - the editor should not be described as an SSR-rendered interactive control.
183
+ - the browser creates the toolbar and editor after mounting;
184
+ - the editor and toolbar should not be described as SSR-rendered interactive controls.
153
185
 
154
186
  The read-only HTML display path is different: it does not need TipTap or `ClientOnly` because it receives already-generated HTML.
155
187
 
@@ -223,7 +255,7 @@ const ready = true;
223
255
  ![Product image](https://example.com/product.png)
224
256
  ````
225
257
 
226
- The exact HTML output is governed by the backend renderer and sanitizer. Do not infer support for arbitrary raw HTML, iframe embeds, inline styles, `data:` URLs, or custom classes from the presence of a TipTap extension.
258
+ The interactive editor decorates supported code blocks with lowlight token spans for syntax highlighting. The backend projection continues to render semantic `<code class="language-*">` output from Markdown; it does not persist editor token spans. Do not infer support for arbitrary raw HTML, iframe embeds, inline styles, `data:` URLs, or custom classes from the presence of a TipTap extension.
227
259
 
228
260
  ## Common mistakes
229
261
 
@@ -239,8 +271,8 @@ The exact HTML output is governed by the backend renderer and sanitizer. Do not
239
271
  4. **Expecting task checkboxes to be interactive**
240
272
  - The backend transforms rendered task inputs into disabled checkboxes for display.
241
273
 
242
- 5. **Expecting a toolbar or upload workflow from this module**
243
- - The module supplies an editor surface, not a toolbar specification, image upload service, remote image importer, or configurable plugin registry.
274
+ 5. **Expecting remote image import or unrelated plugin workflows from the toolbar**
275
+ - The toolbar supports configured image-scene uploads and Markdown link editing through a URL prompt, but it does not supply remote image import, a color picker, or a configurable plugin registry.
244
276
 
245
277
  6. **Treating browser validation or presentation CSS as security**
246
278
  - The backend renderer/sanitizer is the relevant content boundary. External image availability, privacy, CSP, and application authorization remain separate concerns.
@@ -252,10 +284,11 @@ Before integrating Markdown into a Zova feature, verify:
252
284
  1. The editable field is a string containing Markdown.
253
285
  2. The field metadata uses `basic-markdown:formFieldMarkdown`.
254
286
  3. The form value is updated through the editor's Markdown API, not HTML.
255
- 4. Blur and readonly behavior still pass through the standard `ZFormField` contract.
256
- 5. The interactive editor is inside the expected `ClientOnly` boundary.
257
- 6. Public display uses `ZMarkdownHtml` only with server-derived sanitized HTML.
258
- 7. Public DTOs do not expose editable Markdown unless that is intentional.
259
- 8. The backend write path regenerates the HTML projection for every create, update, import, or backfill path that can change Markdown.
287
+ 4. Blur and readonly behavior still pass through the standard `ZFormField` contract, and readonly mode omits toolbar controls.
288
+ 5. Toolbar commands keep the value in Markdown through the editor API rather than creating an HTML submission path.
289
+ 6. The interactive toolbar and editor are inside the expected `ClientOnly` boundary.
290
+ 7. Public display uses `ZMarkdownHtml` only with server-derived sanitized HTML.
291
+ 8. Public DTOs do not expose editable Markdown unless that is intentional.
292
+ 9. The backend write path regenerates the HTML projection for every create, update, import, or backfill path that can change Markdown.
260
293
 
261
294
  For the backend API and security details, continue with [Backend Markdown Guide](/backend/markdown-guide).
@@ -54,6 +54,26 @@ The distinction matters after an alias has a documented reason:
54
54
 
55
55
  Do not introduce a static route name solely to use `routes.name`: a static exception can use `routes.path` instead.
56
56
 
57
+ ## Locale-aware aliases
58
+
59
+ For a public route whose URL includes an optional locale segment, use the shared `LocalePattern` exported by Zova instead of hard-coding one language or copying a regular expression:
60
+
61
+ ```typescript
62
+ import { LocalePattern } from 'zova';
63
+
64
+ config.routes = {
65
+ name: {
66
+ 'commerce-catalog:product': {
67
+ alias: `/:locale(${LocalePattern})?/product/:id`,
68
+ },
69
+ },
70
+ };
71
+ ```
72
+
73
+ The current pattern accepts the common `xx` and `xx-yy` forms, such as `en-us`, `zh-cn`, and `fr-fr`. It defines the URL shape; it is not a whitelist of installed languages. The languages that the application actually offers still come from `config.locale.items` and the corresponding locale resources. When adding a language, update those locale registrations and resources, but do not duplicate the language in each route alias.
74
+
75
+ When the route declares `meta.locale: true`, `$router.getAliasPath(..., { params: { locale: true } })` uses the current locale and omits the configured default locale from an optional locale segment. Thus, with `en-us` as the default, the same alias can produce `/product/42` for `en-us` and `/zh-cn/product/42` for `zh-cn`.
76
+
57
77
  ## Generate a configured alias path
58
78
 
59
79
  When application code needs a user-facing URL for a named route, use the canonical route name with `$router.getAliasPath(...)`:
@@ -61,28 +61,26 @@ npm run build:zova:admin
61
61
  # Both default Basic sites, then a managed local run
62
62
  npm run build:zova
63
63
  npm run deps:vona
64
- npm run test:e2e:basic:clean
64
+ npm run test:e2e cabloy-basic account
65
65
  ```
66
66
 
67
- The managed clean command resets Vona-managed test data and the local Redis namespace, starts one development Vona worker, and runs the complete suite by default. Browser commands consume already-built artifacts; they do not rebuild them.
67
+ The managed clean command resets Vona-managed test data and the local Redis namespace, starts one development Vona worker, and runs the selected specs. Browser commands consume already-built artifacts; they do not rebuild them.
68
68
 
69
- The Basic and Commerce suite families use the same command shape:
69
+ The unified runner uses two commands:
70
70
 
71
71
  ```text
72
- test:e2e:<suite> complete suite
73
- test:e2e:<suite>:web all @web surface scenarios
74
- test:e2e:<suite>:admin all @admin surface scenarios
75
- test:e2e:<suite>:clean managed clean local suite run
72
+ npm run test:e2e clean local run with database reset
73
+ npm run test:e2e:fast fast run without automatic reset
76
74
  ```
77
75
 
78
- Use Playwright tags after npm's argument delimiter for scenario selection instead of adding one root script per scenario:
76
+ Place flat spec basenames directly after the npm script name. Use npm's `--` delimiter before Playwright options. With no names, every spec in `e2e/specs` runs. Native `--grep` and `--grep-invert` remain available, and repeatable `--tag` values require all listed tags:
79
77
 
80
78
  ```bash
81
- npm run test:e2e:basic:clean -- --grep @flow
82
- npm run test:e2e:basic -- --grep ATP-BASIC-FLOW-01
79
+ npm run test:e2e cabloy-basic account -- --grep @flow
80
+ npm run test:e2e:fast a-commerce -- --tag @web --tag @smoke
83
81
  ```
84
82
 
85
- For the complete tag vocabulary, managed-runner argument boundaries, and externally managed-target examples, see [Repo Scripts](/reference/repo-scripts#ssr-browser-checks). For a separately managed Basic target, set `BASIC_E2E_BASE_URL`; Commerce commands use `COMMERCE_E2E_BASE_URL`. The caller owns external-target data, cache, and artifact freshness.
83
+ For the complete tag vocabulary, managed-runner argument boundaries, and externally managed-target examples, see [Repo Scripts](/reference/repo-scripts#ssr-browser-checks). A separately managed target uses `E2E_BASE_URL` with `test:e2e:fast`; the caller owns external-target data, cache, and artifact freshness.
86
84
 
87
85
  ## Cabloy Start root wrappers
88
86
 
@@ -293,6 +293,31 @@ A practical reading takeaway is:
293
293
  - **the form owns the cross-field runtime**
294
294
  - **each field controller owns one field’s TanStack binding and render context**
295
295
 
296
+ ### Field-state readiness and custom synchronization
297
+
298
+ The field options contract declares `onFieldStateReady` in:
299
+
300
+ ```text
301
+ zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts
302
+ ```
303
+
304
+ Its callback receives the current `ControllerFormField<TParentData>` instance. The callback is owned by the field controller; it is not a DOM ref, a VNode mounted event, or a notification that an external API has completed.
305
+
306
+ `ControllerFormField.__init__()` invokes the callback at the end of field initialization, after the field binding, computed props, behavior holder, and optional client-side `onEffect` watcher have been prepared. The same controller also watches the field's schema property. When that property changes materially, the sequence is:
307
+
308
+ ```text
309
+ _getFormFieldOptions()
310
+ -> _formField.api.update(options)
311
+ -> form.resetField(name)
312
+ -> onFieldStateReady(formField)
313
+ ```
314
+
315
+ The callback must follow `resetField(...)`. A custom field may hold a value outside the form controller—for example, a widget value or an externally created challenge object—and a value written before the reset can be replaced by the reset's default value. The callback provides the replay point where the custom field can write its current external value back through `formField.setValue(...)` or the owning form setter.
316
+
317
+ This lifecycle is separate from external data changes. If an API creates or refreshes the external value later, synchronize that result in the API completion path too. Likewise, user input should continue to update the field through `formField.setValue(...)`, while value-change effects belong to the normal field effect/input surfaces. `onFieldStateReady` is not a per-value-change listener and does not fetch, retry, or wait for a renderer DOM node.
318
+
319
+ For the public authoring shape and a renderer-neutral example, see [Initialize external state with `onFieldStateReady`](/frontend/form-guide#initialize-external-state-with-onfieldstateready).
320
+
296
321
  ## 7. How one field is configured
297
322
 
298
323
  The field controller builds its runtime configuration mainly through:
@@ -42,17 +42,17 @@ For the current public explanation of this backend capability, see [Cache Guide]
42
42
 
43
43
  This is the kind of result Cabloy is designed to support: a framework that stays operationally calm even when the system keeps running for long periods.
44
44
 
45
- One internally generated project was kept running continuously for **35 days**. At one representative PM2 snapshot, the process looked like this:
45
+ One internally generated project was kept running continuously for **2 months**. At one representative PM2 snapshot, the process looked like this:
46
46
 
47
47
  ```text
48
48
  ┌────┬─────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
49
49
  │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
50
50
  ├────┼─────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
51
- │ 0 │ cabloy_*** │ default │ N/A │ cluster │ 226947 │ 21D │ 17 │ online │ 0% │ 427.8mb │ ubuntu │ disabled │
51
+ │ 0 │ cabloy_*** │ default │ N/A │ cluster │ 226947 │ 2M │ 17 │ online │ 0% │ 388.4mb │ ubuntu │ disabled │
52
52
  └────┴─────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
53
53
  ```
54
54
 
55
- For this 35-day continuous run, the observed result was **0 memory leak**.
55
+ For this 2-month continuous run, the observed result was **0 memory leak**.
56
56
 
57
57
  The value of this example is not that it is a synthetic micro-benchmark. The value is that it reflects a real long-running project process with a clear, inspectable runtime footprint.
58
58
 
@@ -77,7 +77,7 @@ Use commands from the active repository root.
77
77
 
78
78
  | Edition | Marker | Frontend command | Managed clean E2E |
79
79
  | ------------ | ------------------ | -------------------------------------------------- | ------------------------------ |
80
- | Cabloy Basic | `__CABLOY_BASIC__` | `npm run dev:zova:admin` or `npm run dev:zova:web` | `npm run test:e2e:basic:clean` |
80
+ | Cabloy Basic | `__CABLOY_BASIC__` | `npm run dev:zova:admin` or `npm run dev:zova:web` | `npm run test:e2e` |
81
81
  | Cabloy Start | `__CABLOY_START__` | `npm run dev:zova:admin` or `npm run dev:zova:web` | `npm run test:e2e:start:clean` |
82
82
 
83
83
  Run one frontend command, not both, in each worktree. Both managed clean E2E workflows read Vona's effective local `SERVER_LISTEN_PORT`, then reset, start, and target that local runtime.
@@ -109,16 +109,17 @@ npm run upgrade:dry-run
109
109
  npm run upgrade
110
110
  ```
111
111
 
112
- In Cabloy Basic, upgrade synchronizes the framework-owned SSR browser E2E baseline, its suite/surface root `test:e2e:*` commands, and the `@playwright/test` development dependency. It does not add a root script for each individual E2E scenario. The framework reserves these paths:
112
+ In Cabloy Basic, upgrade synchronizes the framework-owned SSR browser E2E baseline, the two root E2E scripts, and the `@playwright/test` development dependency. The framework owns these paths:
113
113
 
114
114
  ```text
115
115
  e2e/config/
116
116
  e2e/scripts/
117
- e2e/specs/cabloy-basic/
118
- e2e/specs/a-commerce/
117
+ e2e/specs/cabloy-basic.spec.ts
118
+ e2e/specs/account.spec.ts
119
+ e2e/specs/a-commerce.spec.ts
119
120
  ```
120
121
 
121
- Keep project-owned browser tests outside those reserved paths, for example under `e2e/specs/my-project/`; upgrade overlays framework files without deleting project test paths. Projects whose previous upgrader predates this E2E synchronization may need to run `npm run upgrade` once more: the updated upgrader recognizes an incomplete Basic E2E baseline even when the version marker is already current.
122
+ Keep additional project-owned browser specs under other filenames in `e2e/specs`. The current baseline is a fresh-project contract; unsupported legacy layouts are not repaired by the upgrader.
122
123
 
123
124
  ### Cabloy Start repository baseline
124
125
 
@@ -33,14 +33,8 @@ Cabloy Basic exposes these shared root scripts:
33
33
  - `npm run start:one`
34
34
  - `npm run test`
35
35
  - `npm run db:reset`
36
- - `npm run test:e2e:basic`
37
- - `npm run test:e2e:basic:web`
38
- - `npm run test:e2e:basic:admin`
39
- - `npm run test:e2e:basic:clean`
40
- - `npm run test:e2e:commerce`
41
- - `npm run test:e2e:commerce:web`
42
- - `npm run test:e2e:commerce:admin`
43
- - `npm run test:e2e:commerce:clean`
36
+ - `npm run test:e2e`
37
+ - `npm run test:e2e:fast`
44
38
  - `npm run tsc`
45
39
  - `npm run docs:dev`
46
40
  - `npm run docs:build`
@@ -88,11 +82,12 @@ Basic upgrade owns these browser baseline paths:
88
82
  ```text
89
83
  e2e/config/
90
84
  e2e/scripts/
91
- e2e/specs/cabloy-basic/
92
- e2e/specs/a-commerce/
85
+ e2e/specs/cabloy-basic.spec.ts
86
+ e2e/specs/account.spec.ts
87
+ e2e/specs/a-commerce.spec.ts
93
88
  ```
94
89
 
95
- It also reconciles the framework Basic and Commerce `test:e2e:*` scripts and `@playwright/test` development dependency. Keep project browser tests outside those reserved paths. The upgrader merges framework baseline directories without deleting project-owned paths and can repair an incomplete Basic E2E baseline even when the framework version marker is already current.
90
+ It also reconciles the two framework E2E scripts and the `@playwright/test` development dependency. Keep additional project browser specs under other filenames in `e2e/specs`; the upgrader updates only the listed framework files. The current fresh baseline is required and is not repaired for unsupported legacy project layouts.
96
91
 
97
92
  ### Cabloy Start private repository
98
93
 
@@ -108,44 +103,38 @@ The public-package upgrade flow does not source or reconcile the Start baseline,
108
103
 
109
104
  ## SSR browser checks
110
105
 
111
- The suites use this command family:
106
+ The unified runner has two modes:
112
107
 
113
- - `test:e2e:<suite>` runs every browser scenario in the suite.
114
- - `test:e2e:<suite>:web` and `test:e2e:<suite>:admin` select `@web` and `@admin` scenarios.
115
- - `test:e2e:<suite>:clean` resets managed local state, starts one development Vona worker, then runs the suite or a Playwright-filtered subset.
108
+ - `npm run test:e2e`: clean local run; checks the managed port, resets the database, starts one development Vona worker, and runs Playwright.
109
+ - `npm run test:e2e:fast`: skips the reset for quick reruns and may target either the local managed server or an externally managed `E2E_BASE_URL`.
116
110
 
117
- The managed `:clean` runner requires port `7102` to be available. It owns the suite config and local lifecycle: it resets the database, and Playwright starts and stops `npm run dev:one`. It accepts normal Playwright selection and reporting options, but rejects external base URLs, `--config`, and positional spec paths. Use `--grep` or `--grep-invert` to narrow the run.
118
-
119
- Pass Playwright options after npm's `--` delimiter. `@web` and `@admin` are stable surface tags. Purpose tags depend on the suite: current scenarios use `@smoke`, while the Basic suite also uses `@flow`. ATP IDs remain in titles for exact evidence and failure reruns.
120
-
121
- ### Cabloy Basic and Commerce
122
-
123
- The Basic suite exercises Web at `/` and Admin at `/admin` through Vona's SSR dispatcher. Prepare fresh SSR and REST artifacts explicitly when frontend output has changed:
111
+ Place spec basenames directly after the npm script name; use npm's `--` delimiter only before Playwright options. Multiple spec names are allowed. With no names, every spec in `e2e/specs` is discovered:
124
112
 
125
113
  ```bash
126
- npm run build:zova
127
- npm run deps:vona
128
- npm run test:e2e:basic:clean
114
+ npm run test:e2e cabloy-basic account
115
+ npm run test:e2e a-commerce
116
+ npm run test:e2e:fast account
117
+ npm run test:e2e:fast a-commerce -- --grep ATP-SSR
118
+ npm run test:e2e:fast a-commerce -- --grep-invert @admin
129
119
  ```
130
120
 
121
+ Tags remain independent from filenames. Repeat `--tag` to require all tags, while native `--grep` and `--grep-invert` remain available:
122
+
131
123
  ```bash
132
- # Exact acceptance scenario
133
- npm run test:e2e:basic -- --grep ATP-BASIC-FLOW-01
124
+ npm run test:e2e:fast a-commerce -- --tag @web --tag @smoke
125
+ npm run test:e2e:fast account -- --grep @flow --tag @web
126
+ ```
134
127
 
135
- # Category or surface selection
136
- npm run test:e2e:basic:clean -- --grep @flow
137
- npm run test:e2e:basic:clean -- --grep @admin
128
+ The existing tags include `@web`, `@admin`, `@smoke`, `@flow`, `@ssr`, `@theme`, and the business tags used by Commerce such as `@cart`, `@payment`, `@shipment`, and `@refund`. No suite tag is required. Clean runs are local-only and reject `E2E_BASE_URL`; fast runs against an external target do not reset, start, stop, or rebuild that target.
138
129
 
139
- # Compose tags with a Playwright regular expression
140
- npm run test:e2e:basic:clean -- --grep '(?=.*@admin)(?=.*@flow)'
141
- ```
130
+ ### Cabloy Basic and Commerce
142
131
 
143
- For an externally managed Basic target, set `BASIC_E2E_BASE_URL` and use aggregate, surface, or forwarded-tag commands. These commands do not reset, start, stop, or rebuild the target:
132
+ The Basic baseline exercises Web at `/` and Admin at `/admin` through Vona's SSR dispatcher. Prepare artifacts when frontend output has changed:
144
133
 
145
134
  ```bash
146
- BASIC_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:basic
147
- BASIC_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:basic:admin
148
- BASIC_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:basic -- --grep @flow
135
+ npm run build:zova
136
+ npm run deps:vona
137
+ npm run test:e2e cabloy-basic account
149
138
  ```
150
139
 
151
140
  Commerce browser acceptance exercises Customer Web at `/commerce` and Operator Admin routing at `/commerce-admin`. Prepare its paired artifacts explicitly:
@@ -153,15 +142,13 @@ Commerce browser acceptance exercises Customer Web at `/commerce` and Operator A
153
142
  ```bash
154
143
  npm run build:zova:commerce
155
144
  npm run deps:vona
156
- npm run test:e2e:commerce:clean
145
+ npm run test:e2e a-commerce
157
146
  ```
158
147
 
159
- For an externally managed Commerce target, set `COMMERCE_E2E_BASE_URL` and use the matching aggregate, surface, or forwarded-tag command. The target owner is responsible for data, cache, and artifact freshness:
148
+ For a separately managed target, use the fast command:
160
149
 
161
150
  ```bash
162
- COMMERCE_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:commerce
163
- COMMERCE_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:commerce:web
164
- COMMERCE_E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:commerce -- --grep @smoke
151
+ E2E_BASE_URL=http://127.0.0.1:7102 npm run test:e2e:fast a-commerce -- --tag @smoke
165
152
  ```
166
153
 
167
154
  ### Cabloy Start
@@ -0,0 +1,34 @@
1
+ import { defineConfig } from '@playwright/test';
2
+
3
+ import { E2E_LOCAL_BASE_URL, E2E_ROOT_DIR } from '../scripts/e2e.ts';
4
+
5
+ const externalBaseURL = process.env.E2E_BASE_URL;
6
+ const baseURL = externalBaseURL || E2E_LOCAL_BASE_URL;
7
+
8
+ export default defineConfig({
9
+ testDir: `${E2E_ROOT_DIR}/e2e/specs`,
10
+ fullyParallel: false,
11
+ workers: 1,
12
+ forbidOnly: !!process.env.CI,
13
+ retries: process.env.CI ? 2 : 0,
14
+ reporter: process.env.CI ? [['html', { open: 'never' }], ['list']] : 'list',
15
+ use: {
16
+ baseURL,
17
+ trace: 'on-first-retry',
18
+ },
19
+ webServer: externalBaseURL
20
+ ? undefined
21
+ : {
22
+ command: 'node e2e/scripts/startE2eVona.ts',
23
+ cwd: E2E_ROOT_DIR,
24
+ url: `${baseURL}/`,
25
+ timeout: 180_000,
26
+ reuseExistingServer: false,
27
+ stdout: 'pipe',
28
+ stderr: 'pipe',
29
+ gracefulShutdown: {
30
+ signal: 'SIGINT',
31
+ timeout: 10_000,
32
+ },
33
+ },
34
+ });
@@ -16,30 +16,5 @@ if (!Number.isInteger(e2ePort) || e2ePort <= 0 || e2ePort > 65535) {
16
16
 
17
17
  export const E2E_PORT = e2ePort;
18
18
  export const E2E_LOCAL_BASE_URL = `http://127.0.0.1:${E2E_PORT}`;
19
-
20
- const E2E_CONFIG_DIR = resolve(E2E_ROOT_DIR, 'e2e', 'config');
21
- const E2E_SPECS_DIR = resolve(E2E_ROOT_DIR, 'e2e', 'specs');
22
-
23
- const e2eSuites = {
24
- basic: {
25
- externalBaseUrlEnv: 'BASIC_E2E_BASE_URL',
26
- configFile: resolve(E2E_CONFIG_DIR, 'playwright.basic.config.ts'),
27
- testDir: resolve(E2E_SPECS_DIR, 'cabloy-basic'),
28
- readinessPath: '/',
29
- },
30
- commerce: {
31
- externalBaseUrlEnv: 'COMMERCE_E2E_BASE_URL',
32
- configFile: resolve(E2E_CONFIG_DIR, 'playwright.commerce.config.ts'),
33
- testDir: resolve(E2E_SPECS_DIR, 'a-commerce'),
34
- readinessPath: '/commerce',
35
- },
36
- } as const;
37
-
38
- export type E2eSuiteName = keyof typeof e2eSuites;
39
-
40
- export function getE2eSuite(name: string | undefined) {
41
- if (name && name in e2eSuites) {
42
- return e2eSuites[name as E2eSuiteName];
43
- }
44
- throw new Error(`Expected an E2E suite: ${Object.keys(e2eSuites).join(', ')}.`);
45
- }
19
+ export const E2E_CONFIG_FILE = resolve(E2E_ROOT_DIR, 'e2e', 'config', 'playwright.config.ts');
20
+ export const E2E_SPECS_DIR = resolve(E2E_ROOT_DIR, 'e2e', 'specs');