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
@@ -153,15 +153,19 @@ function createAddressFixture(testInfo: TestInfo): IAddressFixture {
153
153
  };
154
154
  }
155
155
 
156
+ function getAddressTextbox(page: Page, name: string): Locator {
157
+ return page.getByRole('group', { name, exact: true }).getByRole('textbox');
158
+ }
159
+
156
160
  async function fillAddressForm(page: Page, fixture: IAddressFixture) {
157
- await page.getByPlaceholder('Recipient name').fill(fixture.recipientName);
158
- await page.getByPlaceholder('Phone').fill(fixture.phone);
159
- await page.getByPlaceholder('Country code').fill(fixture.countryCode);
160
- await page.getByPlaceholder('Region').fill(fixture.region);
161
- await page.getByPlaceholder('City').fill(fixture.city);
162
- await page.getByPlaceholder('Postal code').fill(fixture.postalCode);
163
- await page.getByPlaceholder('Address line 1').fill(fixture.addressLine1);
164
- await page.getByPlaceholder('Address line 2').fill(fixture.addressLine2);
161
+ await getAddressTextbox(page, 'Recipient Name *').fill(fixture.recipientName);
162
+ await getAddressTextbox(page, 'Phone *').fill(fixture.phone);
163
+ await getAddressTextbox(page, 'Country Code *').fill(fixture.countryCode);
164
+ await getAddressTextbox(page, 'Region *').fill(fixture.region);
165
+ await getAddressTextbox(page, 'City *').fill(fixture.city);
166
+ await getAddressTextbox(page, 'Postal Code *').fill(fixture.postalCode);
167
+ await getAddressTextbox(page, 'Address Line 1 *').fill(fixture.addressLine1);
168
+ await getAddressTextbox(page, 'Address Line 2').fill(fixture.addressLine2);
165
169
  }
166
170
 
167
171
  async function createAddressThroughCustomerPage(
@@ -216,7 +220,7 @@ test(
216
220
  async ({ page, request }) => {
217
221
  const response = await request.get('/commerce');
218
222
  expect(response.ok()).toBeTruthy();
219
- expect(response.headers()['cache-control']).toBe('public, max-age=600');
223
+ expect(response.headers()['cache-control']).toBe('no-cache, no-store, must-revalidate');
220
224
  const html = await response.text();
221
225
  const normalizedHtml = html.toLowerCase();
222
226
  expect(normalizedHtml).not.toContain('data-zova-hydrated');
@@ -366,7 +370,7 @@ test(
366
370
  );
367
371
 
368
372
  test(
369
- 'Commerce public SSR: theme cookie does not change cacheable HTML',
373
+ 'Commerce public SSR: theme cookie does not change SSR HTML',
370
374
  { tag: ['@web', '@ssr', '@theme'] },
371
375
  async ({ request }) => {
372
376
  const responses = await Promise.all([
@@ -378,7 +382,7 @@ test(
378
382
  for (const [index, response] of responses.entries()) {
379
383
  expect(response.status(), `public response ${index}`).toBe(200);
380
384
  expect(response.headers()['cache-control'], `public response ${index}`).toBe(
381
- 'public, max-age=600',
385
+ 'no-cache, no-store, must-revalidate',
382
386
  );
383
387
  expect(html[index].toLowerCase()).not.toContain('data-zova-hydrated');
384
388
  expect(html[index]).toContain('data-ssr-theme-dark-false="light"');
@@ -593,8 +597,9 @@ test(
593
597
  .filter({ has: page.getByRole('heading', { name: fixture.recipientName }) });
594
598
  await card.getByRole('button', { name: 'Edit', exact: true }).click();
595
599
  await expect(page.getByRole('heading', { name: 'Add address' })).toHaveCount(0);
596
- await expect(page.getByPlaceholder('City')).toHaveValue(fixture.city);
597
- await page.getByPlaceholder('City').fill(fixture.updatedCity);
600
+ const cityTextbox = getAddressTextbox(page, 'City *');
601
+ await expect(cityTextbox).toHaveValue(fixture.city);
602
+ await cityTextbox.fill(fixture.updatedCity);
598
603
 
599
604
  const updateResponse = waitForAddressResponse(page, 'PATCH', /\/updateMine\/[^/]+$/);
600
605
  const mineResponse = waitForAddressMine(page);
@@ -707,7 +712,7 @@ test(
707
712
  const orderHtml = await orderResponse.text();
708
713
  expect(orderHtml.toLowerCase()).not.toContain('data-zova-hydrated');
709
714
  expect(orderHtml).toContain(`Order #${checkout.orderId}`);
710
- expect(orderHtml).toContain('paid · $45.99');
715
+ expect(orderHtml).toContain('Paid · $45.99');
711
716
  expect(orderHtml).toContain('USD');
712
717
  expect(orderHtml).toContain(fixture.recipientName);
713
718
  expect(orderHtml).toContain(fixture.addressLine1);
@@ -1194,11 +1199,12 @@ test(
1194
1199
  await page.getByPlaceholder('Reason for refund').fill('E2E refund request');
1195
1200
  await page.getByRole('button', { name: 'Request refund', exact: true }).click();
1196
1201
  expect((await requestResponse).ok()).toBeTruthy();
1197
- await expect(page.getByText('refund_requested · $45.99')).toBeVisible();
1202
+ await expect(page.getByText('Refund requested · $45.99')).toBeVisible();
1198
1203
 
1199
1204
  const adminContext = await browser.newContext();
1200
1205
  const adminPage = await adminContext.newPage();
1201
1206
  const adminPageErrors = collectPageErrors(adminPage);
1207
+ const adminConsoleErrors = collectConsoleErrors(adminPage);
1202
1208
  try {
1203
1209
  await adminPage.setViewportSize({ width: 1440, height: 900 });
1204
1210
  await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
@@ -1211,28 +1217,87 @@ test(
1211
1217
  );
1212
1218
  const orderRow = await getAdminOrderRow(adminPage, checkout.orderId);
1213
1219
  await expect(orderRow).toBeVisible();
1214
- await orderRow.getByPlaceholder('Decision reason').fill('E2E approval');
1215
- await orderRow.getByRole('checkbox').check();
1220
+ await expect(
1221
+ orderRow.getByRole('button', { name: 'Review refund', exact: true }),
1222
+ ).toBeVisible();
1223
+ await expect(
1224
+ orderRow.getByRole('button', { name: 'Approve refund', exact: true }),
1225
+ ).toHaveCount(0);
1226
+ await expect(
1227
+ orderRow.getByRole('button', { name: 'Reject refund', exact: true }),
1228
+ ).toHaveCount(0);
1229
+ await orderRow.getByRole('button', { name: 'Review refund', exact: true }).click();
1230
+ const refundDecisionTitle = adminPage.getByRole('heading', { name: 'Refund decision' });
1231
+ await expect(refundDecisionTitle).toBeVisible();
1232
+ await adminPage.getByPlaceholder('Decision reason').fill('E2E approval');
1233
+ await adminPage.locator('input[name="decisionConfirmed"]').check();
1216
1234
  const approveResponse = waitForApiResponse(
1217
1235
  adminPage,
1218
1236
  'POST',
1219
1237
  `/api/commerce/trade/order/${checkout.orderId}/approveRefund`,
1220
1238
  );
1221
- await orderRow.getByRole('button', { name: 'Approve refund', exact: true }).click();
1239
+ await adminPage.getByRole('button', { name: 'Approve refund', exact: true }).click();
1222
1240
  expect((await approveResponse).ok()).toBeTruthy();
1241
+ await expect(refundDecisionTitle).toHaveCount(0);
1223
1242
  await expect(orderRow).toContainText('Refund approved');
1243
+ await expect(
1244
+ orderRow.getByRole('button', { name: 'Execute refund', exact: true }),
1245
+ ).toHaveCount(1);
1246
+ await expect(
1247
+ orderRow.getByRole('button', { name: 'Inspect refund recovery', exact: true }),
1248
+ ).toHaveCount(0);
1249
+ await expect(orderRow.getByRole('checkbox')).toHaveCount(0);
1250
+ await orderRow.getByRole('button', { name: 'Execute refund', exact: true }).click();
1251
+ const refundExecutionTitle = adminPage.getByRole('heading', { name: 'Refund execution' });
1252
+ await expect(refundExecutionTitle).toBeVisible();
1253
+ const refundExecutionDialog = adminPage
1254
+ .locator('div.card')
1255
+ .filter({ has: refundExecutionTitle });
1256
+ const executionButton = refundExecutionDialog.getByRole('button', {
1257
+ name: 'Execute refund',
1258
+ exact: true,
1259
+ });
1260
+ await expect(executionButton).toBeDisabled();
1261
+ const unavailableRecoveryResponse = waitForApiResponse(
1262
+ adminPage,
1263
+ 'GET',
1264
+ `/api/commerce/trade/order/${checkout.orderId}/refundRecovery`,
1265
+ );
1266
+ await refundExecutionDialog
1267
+ .getByRole('button', { name: 'Inspect refund recovery', exact: true })
1268
+ .click();
1269
+ expect((await unavailableRecoveryResponse).status()).toBe(404);
1270
+ await expect(
1271
+ refundExecutionDialog.getByText(
1272
+ 'Refund recovery is not available yet. Execute the refund first to create a provider refund submission.',
1273
+ ),
1274
+ ).toBeVisible();
1275
+ await expect(refundExecutionDialog.getByText(/Provider operation:/)).toHaveCount(0);
1276
+ await expect(refundExecutionDialog.getByPlaceholder('Recovery reason')).toHaveCount(0);
1277
+ await expect(
1278
+ refundExecutionDialog.getByRole('button', {
1279
+ name: 'Reconcile provider refund',
1280
+ exact: true,
1281
+ }),
1282
+ ).toHaveCount(0);
1283
+ await expect(
1284
+ refundExecutionDialog.getByRole('button', { name: 'Retry refund', exact: true }),
1285
+ ).toHaveCount(0);
1286
+ expect(
1287
+ adminConsoleErrors.filter(error =>
1288
+ /refund recovery is not available|AxiosError/i.test(error),
1289
+ ),
1290
+ ).toEqual([]);
1291
+ await refundExecutionDialog.locator('input[name="executionConfirmed"]').check();
1224
1292
  const executeResponse = waitForApiResponse(
1225
1293
  adminPage,
1226
1294
  'POST',
1227
1295
  `/api/commerce/trade/order/${checkout.orderId}/executeRefund`,
1228
1296
  );
1229
- await orderRow.getByRole('button', { name: 'Execute refund', exact: true }).click();
1297
+ await executionButton.click();
1230
1298
  const executeResponseValue = await executeResponse;
1231
1299
  expect(executeResponseValue.ok()).toBeTruthy();
1232
1300
  const executeResult = (await executeResponseValue.json()).data;
1233
- const authorization = executeResponseValue.request().headers()['authorization'];
1234
- expect(authorization).toMatch(/^Bearer /);
1235
- const authHeaders = { Authorization: authorization };
1236
1301
  expectTableIdentity(executeResult.refundAttemptId);
1237
1302
  expectTableIdentity(executeResult.refundOperationId);
1238
1303
  expect([
@@ -1240,11 +1305,39 @@ test(
1240
1305
  executeResult.refundState,
1241
1306
  executeResult.refundAttemptState,
1242
1307
  ]).toEqual(['refund_approved', 'approved', 'created']);
1243
- const completeRefundResponse = await adminPage.request.post(
1308
+ await expect(refundExecutionTitle).toHaveCount(0);
1309
+ await orderRow.getByRole('button', { name: 'Execute refund', exact: true }).click();
1310
+ await expect(refundExecutionTitle).toBeVisible();
1311
+ const recoveryResponse = waitForApiResponse(
1312
+ adminPage,
1313
+ 'GET',
1314
+ `/api/commerce/trade/order/${checkout.orderId}/refundRecovery`,
1315
+ );
1316
+ await adminPage
1317
+ .getByRole('button', { name: 'Inspect refund recovery', exact: true })
1318
+ .click();
1319
+ expect((await recoveryResponse).ok()).toBeTruthy();
1320
+ await expect(adminPage.getByText(/Provider operation:/)).toBeVisible();
1321
+ await expect(
1322
+ adminPage.getByText(
1323
+ 'A provider refund identifier is available. Wait for the verified provider webhook; do not resubmit or reconcile by querying.',
1324
+ ),
1325
+ ).toBeVisible();
1326
+ await expect(
1327
+ refundExecutionDialog.getByRole('button', {
1328
+ name: 'Reconcile provider refund',
1329
+ exact: true,
1330
+ }),
1331
+ ).toHaveCount(0);
1332
+ const completeRefundResponse = waitForApiResponse(
1333
+ adminPage,
1334
+ 'POST',
1244
1335
  `/api/pay/mock/payment-session/refund-operation/${executeResult.refundOperationId}/complete`,
1245
- { headers: authHeaders, data: { outcome: 'succeeded' } },
1246
1336
  );
1247
- expect(completeRefundResponse.ok()).toBeTruthy();
1337
+ await refundExecutionDialog
1338
+ .getByRole('button', { name: 'Complete mock refund', exact: true })
1339
+ .click();
1340
+ expect((await completeRefundResponse).ok()).toBeTruthy();
1248
1341
  await expect
1249
1342
  .poll(
1250
1343
  async () => {
@@ -1787,8 +1880,9 @@ const product = '${suffix}';
1787
1880
 
1788
1881
  ==highlighted text==
1789
1882
 
1790
- | Feature | Value |
1791
- | --- | --- |
1883
+
1884
+ | Feature | Value |
1885
+ | -------- | ------- |
1792
1886
  | Material | Durable |`;
1793
1887
  const adminContext = await browser.newContext();
1794
1888
  const adminPage = await adminContext.newPage();
@@ -1839,19 +1933,67 @@ const product = '${suffix}';
1839
1933
  expect(adminConsoleErrors).toEqual([]);
1840
1934
  const editor = adminPage.locator('[contenteditable="true"]');
1841
1935
  await expect(editor).toBeVisible();
1936
+ const toolbar = adminPage.getByRole('toolbar', { name: 'Markdown toolbar' });
1937
+ await expect(toolbar).toBeVisible();
1938
+ const undoButton = toolbar.getByRole('button', { name: 'Undo' });
1939
+ await expect(toolbar.getByRole('combobox', { name: 'Code block language' })).toHaveCount(0);
1940
+ const codeBlockToolbar = adminPage.getByRole('toolbar', { name: 'Code block language' });
1941
+ await expect(codeBlockToolbar).toBeHidden();
1942
+ await expect(toolbar.getByRole('button', { name: 'Bold' })).toHaveAttribute('type', 'button');
1943
+ await expect(undoButton).toBeEnabled();
1944
+ const insertTableButton = toolbar.getByRole('button', { name: 'Insert table' });
1945
+ await expect(insertTableButton).toHaveAttribute('type', 'button');
1946
+ await expect(insertTableButton).toHaveAttribute('aria-haspopup', 'grid');
1947
+ await expect(insertTableButton).toHaveAttribute('aria-expanded', 'false');
1948
+ await insertTableButton.click();
1949
+ const tablePickerDialog = adminPage.locator('#markdown-table-size-picker');
1950
+ const tablePicker = tablePickerDialog.locator('[role="grid"]');
1951
+ await expect(tablePickerDialog).toBeVisible();
1952
+ await expect(tablePicker).toHaveCount(1);
1953
+ await expect(insertTableButton).toHaveAttribute('aria-expanded', 'true');
1954
+ await expect(editor.locator('table')).toHaveCount(1);
1955
+ const tablePickerCell = tablePicker.locator('[data-table-picker-cell="2-3"]');
1956
+ await tablePickerCell.hover();
1957
+ await expect(tablePickerDialog.locator('[aria-live="polite"]')).toHaveText(
1958
+ 'Selected table size: 2 × 3',
1959
+ );
1960
+ await expect(tablePicker.locator('[aria-selected="true"]')).toHaveCount(6);
1961
+ await expect(tablePicker.locator('[aria-selected="false"]').first()).toBeVisible();
1962
+ await tablePickerCell.click();
1963
+ await expect(tablePickerDialog).toHaveCount(0);
1964
+ await expect(insertTableButton).toHaveAttribute('aria-expanded', 'false');
1965
+ await expect(editor.locator('table')).toHaveCount(2);
1966
+ await expect(editor.locator('table').last().locator('th')).toHaveCount(3);
1967
+ await expect(editor.locator('table').last().locator('td')).toHaveCount(3);
1842
1968
  const editorSurface = editor.locator('..');
1969
+ await editor.locator('h1').click();
1843
1970
  await expect(editorSurface).not.toHaveClass(/\bprose\b/);
1844
1971
  const editorSurfaceClass = await editorSurface.getAttribute('class');
1845
1972
  expect(editorSurfaceClass?.trim()).toBeTruthy();
1846
- await editorSurface.click({ position: { x: 20, y: 280 } });
1847
1973
  await expect(editor).toBeFocused();
1848
1974
  await expect(editor.locator('h1')).toHaveText(`Product content ${suffix}`);
1849
1975
  await expect(editor.locator('ul:not([data-type="taskList"])')).toHaveCount(1);
1850
1976
  await expect(editor.locator('blockquote')).toHaveText('Product quote');
1851
- await expect(editor.locator('pre code')).toHaveText(`const product = '${suffix}';`);
1977
+ const codeBlock = editor.locator('pre code');
1978
+ const codeBlockLanguage = codeBlockToolbar.getByRole('combobox', {
1979
+ name: 'Code block language',
1980
+ });
1981
+ await expect(codeBlockToolbar).toBeHidden();
1982
+ await expect(codeBlock).toHaveClass(/\blanguage-ts\b/);
1983
+ await expect(codeBlock).toHaveText(`const product = '${suffix}';`);
1984
+ await expect(codeBlock.locator('span[class*="hljs-"]')).not.toHaveCount(0);
1985
+ await codeBlock.click();
1986
+ await expect(codeBlockToolbar).toBeVisible();
1987
+ await expect(codeBlockLanguage).toBeEnabled();
1988
+ await expect(codeBlockLanguage).toHaveValue('typescript');
1989
+ await codeBlockLanguage.selectOption('python');
1990
+ await expect(codeBlock).toHaveClass(/\blanguage-python\b/);
1991
+ await expect(codeBlock.locator('span[class*="hljs-"]')).not.toHaveCount(0);
1992
+ await editor.locator('h1').click();
1993
+ await expect(codeBlockToolbar).toBeHidden();
1852
1994
  await expect(editor.locator('mark')).toHaveText('highlighted text');
1853
- await expect(editor.locator('table th')).toHaveCount(2);
1854
- await expect(editor.locator('table td')).toHaveCount(2);
1995
+ await expect(editor.locator('table th')).toHaveCount(5);
1996
+ await expect(editor.locator('table td')).toHaveCount(5);
1855
1997
  await expect(editor.locator('h1')).toHaveCSS('font-weight', '700');
1856
1998
  await expect(editor.locator('ul:not([data-type="taskList"])')).toHaveCSS(
1857
1999
  'list-style-type',
@@ -1864,15 +2006,75 @@ const product = '${suffix}';
1864
2006
  await expect(editor.locator('blockquote')).toHaveCSS('border-left-width', '4px');
1865
2007
  await expect(editor.locator('pre')).toHaveCSS('overflow-x', 'auto');
1866
2008
  await expect(editor.locator('table th').first()).toHaveCSS('font-weight', '600');
1867
- await adminPage.getByRole('button', { name: 'Submit', exact: true }).click();
2009
+ await editor.locator('h1').selectText();
2010
+ const boldButton = toolbar.getByRole('button', { name: 'Bold' });
2011
+ await boldButton.click();
2012
+ await expect(boldButton).toHaveAttribute('aria-pressed', 'true');
2013
+ await undoButton.click();
2014
+ await expect(boldButton).toHaveAttribute('aria-pressed', 'false');
2015
+ await expect(toolbar.getByRole('button', { name: 'Redo' })).toBeEnabled();
2016
+
2017
+ const tableToolbar = adminPage.getByRole('toolbar', { name: 'Table toolbar' });
2018
+ const insertedTable = editor.locator('table').last();
2019
+ await editor.locator('h1').click();
2020
+ await expect(tableToolbar).toBeHidden();
2021
+ await insertedTable.locator('td').first().click();
2022
+ expect(adminPageErrors).toEqual([]);
2023
+ expect(adminConsoleErrors).toEqual([]);
2024
+ await expect(tableToolbar).toBeVisible();
2025
+ expect(adminPageErrors).toEqual([]);
2026
+ expect(adminConsoleErrors).toEqual([]);
2027
+ await expect(tableToolbar.getByRole('button', { name: 'Add row before' })).toHaveAttribute(
2028
+ 'type',
2029
+ 'button',
2030
+ );
2031
+ await tableToolbar.getByRole('button', { name: 'Add row before' }).click();
2032
+ await expect(insertedTable.locator('td')).toHaveCount(6);
2033
+ expect(adminPageErrors).toEqual([]);
2034
+ await tableToolbar.getByRole('button', { name: 'Delete row' }).click();
2035
+ await expect(insertedTable.locator('td')).toHaveCount(3);
2036
+ expect(adminPageErrors).toEqual([]);
2037
+ await tableToolbar.getByRole('button', { name: 'Add row after' }).click();
2038
+ await expect(insertedTable.locator('td')).toHaveCount(6);
2039
+ expect(adminPageErrors).toEqual([]);
2040
+ await tableToolbar.getByRole('button', { name: 'Delete row' }).click();
2041
+ await expect(insertedTable.locator('td')).toHaveCount(3);
2042
+ expect(adminPageErrors).toEqual([]);
2043
+ await tableToolbar.getByRole('button', { name: 'Add column before' }).click();
2044
+ await expect(insertedTable.locator('th')).toHaveCount(4);
2045
+ await expect(insertedTable.locator('td')).toHaveCount(4);
2046
+ expect(adminPageErrors).toEqual([]);
2047
+ await tableToolbar.getByRole('button', { name: 'Delete column' }).click();
2048
+ await expect(insertedTable.locator('th')).toHaveCount(3);
2049
+ await expect(insertedTable.locator('td')).toHaveCount(3);
2050
+ expect(adminPageErrors).toEqual([]);
2051
+ await tableToolbar.getByRole('button', { name: 'Add column after' }).click();
2052
+ await expect(insertedTable.locator('th')).toHaveCount(4);
2053
+ await expect(insertedTable.locator('td')).toHaveCount(4);
2054
+ expect(adminPageErrors).toEqual([]);
2055
+ await tableToolbar.getByRole('button', { name: 'Delete column' }).click();
2056
+ await expect(insertedTable.locator('th')).toHaveCount(3);
2057
+ await expect(insertedTable.locator('td')).toHaveCount(3);
2058
+ expect(adminPageErrors).toEqual([]);
2059
+ await tableToolbar.getByRole('button', { name: 'Delete table' }).click();
2060
+ await expect(editor.locator('table')).toHaveCount(1);
2061
+ await expect(tableToolbar).toBeHidden();
2062
+ expect(adminPageErrors).toEqual([]);
1868
2063
 
2064
+ const productUpdate = waitForApiResponse(
2065
+ adminPage,
2066
+ 'PATCH',
2067
+ `${productActionPath}/${productId}`,
2068
+ );
2069
+ await adminPage.getByRole('button', { name: 'Submit', exact: true }).click();
2070
+ await productUpdate;
1869
2071
  const productResponseAfterUpdate = await adminPage.request.get(
1870
2072
  `${productActionPath}/${productId}`,
1871
2073
  { headers },
1872
2074
  );
1873
2075
  expect(productResponseAfterUpdate.ok()).toBeTruthy();
1874
2076
  expect((await productResponseAfterUpdate.json()).productContentForm.descriptionMarkdown).toBe(
1875
- markdown,
2077
+ markdown.replace('```ts', '```python'),
1876
2078
  );
1877
2079
  expect(adminPageErrors).toEqual([]);
1878
2080
  } finally {
@@ -1893,6 +2095,135 @@ const product = '${suffix}';
1893
2095
  },
1894
2096
  );
1895
2097
 
2098
+ test(
2099
+ 'ATP-SPC-02: Product Content link toolbar edits Markdown',
2100
+ { tag: ['@admin', '@flow', '@product'] },
2101
+ async ({ browser }, testInfo) => {
2102
+ test.setTimeout(180_000);
2103
+ const suffix = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
2104
+ const categoryName = `E2E Product Link Category ${suffix}`;
2105
+ const productTitle = `E2E Product Link ${suffix}`;
2106
+ const markdown = `# Product content ${suffix}\n\nA product description.`;
2107
+ const adminContext = await browser.newContext();
2108
+ const adminPage = await adminContext.newPage();
2109
+ const adminPageErrors = collectPageErrors(adminPage);
2110
+ const adminConsoleErrors = collectConsoleErrors(adminPage);
2111
+ const productListPath = '/commerce-admin/rest/resource/commerce-catalog%3Aproduct';
2112
+ const categoryActionPath = '/api/commerce/catalog/category';
2113
+ const productActionPath = '/api/commerce/catalog/product';
2114
+ let categoryId: number | string | undefined;
2115
+ let productId: number | string | undefined;
2116
+ let headers: { Authorization: string } | undefined;
2117
+ try {
2118
+ await adminPage.setViewportSize({ width: 1440, height: 900 });
2119
+ await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
2120
+ const accessToken = (await adminContext.cookies()).find(
2121
+ cookie => cookie.name === 'token',
2122
+ )?.value;
2123
+ expect(accessToken).toBeTruthy();
2124
+ headers = { Authorization: `Bearer ${accessToken}` };
2125
+
2126
+ const categoryResponse = await adminPage.request.post(categoryActionPath, {
2127
+ data: { name: categoryName, published: false },
2128
+ headers,
2129
+ });
2130
+ expect(categoryResponse.ok()).toBeTruthy();
2131
+ categoryId = (await categoryResponse.json()).data;
2132
+ expectTableIdentity(categoryId);
2133
+
2134
+ const productResponse = await adminPage.request.post(productActionPath, {
2135
+ data: {
2136
+ categoryId,
2137
+ title: productTitle,
2138
+ published: false,
2139
+ productContentForm: { descriptionMarkdown: markdown },
2140
+ },
2141
+ headers,
2142
+ });
2143
+ expect(productResponse.ok()).toBeTruthy();
2144
+ productId = (await productResponse.json()).data;
2145
+ expectTableIdentity(productId);
2146
+
2147
+ await adminPage.goto(`${productListPath}/${productId}/edit`, { waitUntil: 'load' });
2148
+ await expect(adminPage.locator('html')).toHaveAttribute(
2149
+ 'data-zova-hydrated',
2150
+ 'commerceAdmin',
2151
+ );
2152
+ const editor = adminPage.locator('[contenteditable="true"]');
2153
+ await expect(editor).toBeVisible();
2154
+ const toolbar = adminPage.getByRole('toolbar', { name: 'Markdown toolbar' });
2155
+ await expect(toolbar).toBeVisible();
2156
+ const linkButton = toolbar.getByRole('button', { name: 'Link' });
2157
+ await expect(linkButton).toHaveAttribute('type', 'button');
2158
+ await expect(linkButton).toBeDisabled();
2159
+
2160
+ await editor.locator('h1').selectText();
2161
+ await expect(linkButton).toBeEnabled();
2162
+ await linkButton.click();
2163
+ const linkPrompt = adminPage.locator('.fixed.inset-0.z-50');
2164
+ await expect(linkPrompt).toBeVisible();
2165
+ const linkInput = linkPrompt.locator('input[type="text"]');
2166
+ await linkInput.fill('https://example.com/docs');
2167
+ await linkInput.press('Enter');
2168
+ await expect(editor.locator('h1 a[href="https://example.com/docs"]')).toHaveText(
2169
+ `Product content ${suffix}`,
2170
+ );
2171
+ await expect(linkButton).toHaveAttribute('aria-pressed', 'true');
2172
+ await editor.press('ArrowLeft');
2173
+ await expect(linkButton).toHaveAttribute('aria-pressed', 'true');
2174
+
2175
+ await linkButton.click();
2176
+ await expect(linkPrompt).toBeVisible();
2177
+ await expect(linkInput).toHaveValue('https://example.com/docs');
2178
+ await linkInput.fill('https://example.com/updated');
2179
+ await linkPrompt.getByRole('button', { name: 'OK' }).click();
2180
+ await expect(editor.locator('h1 a[href="https://example.com/updated"]')).toHaveText(
2181
+ `Product content ${suffix}`,
2182
+ );
2183
+
2184
+ await linkButton.click();
2185
+ await expect(linkPrompt).toBeVisible();
2186
+ await linkPrompt.getByRole('button', { name: 'Cancel' }).click();
2187
+ await expect(editor.locator('h1 a[href="https://example.com/updated"]')).toHaveText(
2188
+ `Product content ${suffix}`,
2189
+ );
2190
+
2191
+ await editor.locator('h1').selectText();
2192
+ await expect(linkButton).toHaveAttribute('aria-pressed', 'true');
2193
+ await linkButton.click();
2194
+ await expect(linkPrompt).toBeVisible();
2195
+ await linkInput.fill('');
2196
+ await linkInput.press('Enter');
2197
+ await expect(editor.locator('h1 a')).toHaveCount(0);
2198
+ await adminPage.getByRole('button', { name: 'Submit', exact: true }).click();
2199
+ const productResponseAfterLinkRemoval = await adminPage.request.get(
2200
+ `${productActionPath}/${productId}`,
2201
+ { headers },
2202
+ );
2203
+ expect(productResponseAfterLinkRemoval.ok()).toBeTruthy();
2204
+ expect(
2205
+ (await productResponseAfterLinkRemoval.json()).productContentForm.descriptionMarkdown,
2206
+ ).not.toContain('https://example.com/updated');
2207
+ expect(adminPageErrors).toEqual([]);
2208
+ expect(adminConsoleErrors).toEqual([]);
2209
+ } finally {
2210
+ if (productId && headers) {
2211
+ const response = await adminPage.request.delete(`${productActionPath}/${productId}`, {
2212
+ headers,
2213
+ });
2214
+ expect(response.ok()).toBeTruthy();
2215
+ }
2216
+ if (categoryId && headers) {
2217
+ const response = await adminPage.request.delete(`${categoryActionPath}/${categoryId}`, {
2218
+ headers,
2219
+ });
2220
+ expect(response.ok()).toBeTruthy();
2221
+ }
2222
+ await adminContext.close().catch(() => {});
2223
+ }
2224
+ },
2225
+ );
2226
+
1896
2227
  test(
1897
2228
  'ATP-SPC-02: SKU renders semantic Admin currency and lifecycle controls',
1898
2229
  { tag: ['@admin', '@flow', '@sku'] },
@@ -17,7 +17,7 @@ const accountProfileApiPath = '/api/home/user/account/profile';
17
17
  const imageUploadApiPath = '/api/image/upload';
18
18
  const avatarFixturePath = path.resolve(
19
19
  import.meta.dirname,
20
- '../../../zova/src/suite/a-home/modules/home-base/assets/img/avatar_user.png',
20
+ '../../zova/src/suite/a-home/modules/home-base/assets/img/avatar_user.png',
21
21
  );
22
22
 
23
23
  function waitForApiResponse(page: Page, method: string, path: string) {
@@ -168,7 +168,6 @@ test(
168
168
  await page.getByPlaceholder('Your email address').fill('unknown-reset@example.test');
169
169
  const captcha = page.getByPlaceholder('Please input captcha');
170
170
  await expect(captcha).toHaveValue(/\S+/);
171
- await captcha.fill(await captcha.inputValue());
172
171
  const requestResponse = waitForApiResponse(
173
172
  page,
174
173
  'POST',
@@ -115,7 +115,7 @@ test(
115
115
  async ({ page, request }) => {
116
116
  const response = await request.get('/');
117
117
  expect(response.ok()).toBeTruthy();
118
- expect(response.headers()['cache-control']).toBe('public, max-age=600');
118
+ expect(response.headers()['cache-control']).toBe('no-cache, no-store, must-revalidate');
119
119
  const html = await response.text();
120
120
  expect(html.toLowerCase()).not.toContain('data-zova-hydrated');
121
121
  expect(html).not.toContain('ssr-body-ready-observer');
@@ -123,7 +123,9 @@ test(
123
123
 
124
124
  const routeOverrideResponse = await request.get('/demo/basic/routeQueryB');
125
125
  expect(routeOverrideResponse.ok()).toBeTruthy();
126
- expect(routeOverrideResponse.headers()['cache-control']).toBe('public, max-age=300');
126
+ expect(routeOverrideResponse.headers()['cache-control']).toBe(
127
+ 'no-cache, no-store, must-revalidate',
128
+ );
127
129
 
128
130
  const unlocalizedResponse = await request.get('/demo/basic/state');
129
131
  expect(unlocalizedResponse.ok()).toBeTruthy();
@@ -145,7 +147,7 @@ test(
145
147
  );
146
148
 
147
149
  test(
148
- 'ATP-BASIC-SSR-03: concurrent public routes retain their own cache policy',
150
+ 'ATP-BASIC-SSR-03: concurrent public routes are non-cacheable in development',
149
151
  { tag: ['@web', '@smoke'] },
150
152
  async ({ request }) => {
151
153
  const responses = await Promise.all(
@@ -159,11 +161,7 @@ test(
159
161
  for (const [index, response] of responses.entries()) {
160
162
  expect(response.ok(), `response ${index}`).toBeTruthy();
161
163
  expect(response.headers()['cache-control'], `response ${index}`).toBe(
162
- index % 3 === 0
163
- ? 'public, max-age=600'
164
- : index % 3 === 1
165
- ? 'public, max-age=300'
166
- : 'no-cache, no-store, must-revalidate',
164
+ 'no-cache, no-store, must-revalidate',
167
165
  );
168
166
  }
169
167
  },
package/oxlint.config.ts CHANGED
@@ -3,6 +3,9 @@ import { defineConfig } from 'oxlint';
3
3
 
4
4
  export default defineConfig(
5
5
  oxcLintConfigVue({
6
+ rules: {
7
+ '@stylistic/jsx-curly-newline': 'off',
8
+ },
6
9
  ignorePatterns: [
7
10
  // root original
8
11
  '**/.claude',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.140",
3
+ "version": "5.1.142",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -43,14 +43,8 @@
43
43
  "build:zova:commerce": "pnpm --dir zova run build:ssr:cabloyCommerceBatch",
44
44
  "build:zova:commerce:web": "pnpm --dir zova run build:ssr:cabloyCommerce && pnpm --dir zova run build:rest:cabloyCommerce",
45
45
  "build:zova:commerce:admin": "pnpm --dir zova run build:ssr:cabloyCommerceAdmin && pnpm --dir zova run build:rest:cabloyCommerceAdmin",
46
- "test:e2e:basic": "playwright test --config e2e/config/playwright.basic.config.ts",
47
- "test:e2e:basic:web": "playwright test --config e2e/config/playwright.basic.config.ts --grep @web",
48
- "test:e2e:basic:admin": "playwright test --config e2e/config/playwright.basic.config.ts --grep @admin",
49
- "test:e2e:basic:clean": "node e2e/scripts/testE2eClean.ts basic",
50
- "test:e2e:commerce": "playwright test --config e2e/config/playwright.commerce.config.ts",
51
- "test:e2e:commerce:web": "playwright test --config e2e/config/playwright.commerce.config.ts --grep @web",
52
- "test:e2e:commerce:admin": "playwright test --config e2e/config/playwright.commerce.config.ts --grep @admin",
53
- "test:e2e:commerce:clean": "node e2e/scripts/testE2eClean.ts commerce",
46
+ "test:e2e": "node e2e/scripts/runE2e.ts --clean",
47
+ "test:e2e:fast": "node e2e/scripts/runE2e.ts --fast",
54
48
  "start": "cd vona && npm run start",
55
49
  "start:one": "cd vona && npm run start:one",
56
50
  "test": "cd vona && npm run test",