cabloy 5.1.122 → 5.1.124

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 (244) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +13 -3
  3. package/.claude/skills/cabloy-workflow/SKILL.md +6 -0
  4. package/.claude/skills/cabloy-workflow/evals/evals.json +6 -0
  5. package/CHANGELOG.md +24 -0
  6. package/CLAUDE.md +1 -1
  7. package/cabloy-docs/.vitepress/config.mjs +4 -0
  8. package/cabloy-docs/frontend/form-layout-guide.md +29 -7
  9. package/cabloy-docs/fullstack/parallel-worktree-environment.md +87 -0
  10. package/e2e/scripts/e2e.ts +13 -1
  11. package/e2e/scripts/startE2eVona.ts +4 -1
  12. package/e2e/specs/a-commerce/commerce.spec.ts +129 -32
  13. package/package.json +2 -1
  14. package/playwright-report/data/24ac790ec112339c76cfb0c8e2c2539c2f953a0b.zip +0 -0
  15. package/playwright-report/data/2fddcabcdbd0b695061a565acfff243cb3a4f5e3.zip +0 -0
  16. package/playwright-report/data/4738038d9e861d63fd25937c9a3570934786a62f.zip +0 -0
  17. package/playwright-report/data/72f99c7da67c7f6ea53cab7d8681798b70d79a10.md +222 -0
  18. package/playwright-report/data/7b9684a7c793e9798b898f8ff50177b660a7a733.md +222 -0
  19. package/playwright-report/data/8b31595f5f1cc9d23863af51a592e6b90dcbc4ce.zip +0 -0
  20. package/playwright-report/data/95b578407a68806202dda917a61c50c750bd285b.md +222 -0
  21. package/playwright-report/data/ad960b3a58f224f5b4cad04a24067600064ffb7b.md +222 -0
  22. package/playwright-report/index.html +90 -0
  23. package/playwright-report/trace/assets/codeMirrorModule-LEHpjmcn.js +32 -0
  24. package/playwright-report/trace/assets/defaultSettingsView-BNmKHKpQ.js +264 -0
  25. package/playwright-report/trace/assets/urlMatch-BYQrIQwR.js +1 -0
  26. package/playwright-report/trace/codeMirrorModule.DYBRYzYX.css +1 -0
  27. package/playwright-report/trace/codicon.DCmgc-ay.ttf +0 -0
  28. package/playwright-report/trace/defaultSettingsView.CjdS-WJx.css +1 -0
  29. package/playwright-report/trace/index.CzXZzn5A.css +1 -0
  30. package/playwright-report/trace/index.DMMX1gXU.js +2 -0
  31. package/playwright-report/trace/index.html +44 -0
  32. package/playwright-report/trace/manifest.webmanifest +16 -0
  33. package/playwright-report/trace/playwright-logo.svg +9 -0
  34. package/playwright-report/trace/snapshot.html +10 -0
  35. package/playwright-report/trace/snapshot.v8KI4P3m.js +2 -0
  36. package/playwright-report/trace/sw.bundle.js +5 -0
  37. package/playwright-report/trace/uiMode.BZQ54Kgt.css +1 -0
  38. package/playwright-report/trace/uiMode.Ut8wwJNp.js +6 -0
  39. package/playwright-report/trace/uiMode.html +18 -0
  40. package/playwright-report/trace/xtermModule.DYP7pi_n.css +32 -0
  41. package/vona/env/.env +12 -0
  42. package/vona/env/.env.test +7 -0
  43. package/vona/packages-cli/cli/package.json +1 -1
  44. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}Create.tsx_ +19 -1
  45. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}Update.tsx_ +19 -1
  46. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}View.tsx_ +19 -1
  47. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +6 -1
  48. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +6 -1
  49. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}Create.tsx_ +19 -1
  50. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}Update.tsx_ +19 -1
  51. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}View.tsx_ +19 -1
  52. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/3-en-us.ts +6 -1
  53. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +6 -1
  54. package/vona/packages-cli/cli-set-api/package.json +1 -1
  55. package/vona/pnpm-lock.yaml +255 -12
  56. package/vona/pnpm-workspace.yaml +1 -1
  57. package/vona/src/suite/a-commerce/modules/commerce-payment/package.json +4 -1
  58. package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +45 -3
  59. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.index.ts +1 -0
  60. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.version.ts +25 -1
  61. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +20 -0
  62. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAttempt.tsx +9 -0
  63. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAudit.tsx +5 -2
  64. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/refundAttempt.tsx +6 -0
  65. package/vona/src/suite/a-commerce/modules/commerce-payment/src/index.ts +2 -0
  66. package/vona/src/suite/a-commerce/modules/commerce-payment/src/lib/index.ts +1 -0
  67. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +54 -0
  68. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/paymentAttempt.ts +7 -1
  69. package/vona/src/suite/a-commerce/modules/commerce-payment/src/types/index.ts +1 -0
  70. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +21 -0
  71. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/index.ts +30 -0
  72. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/en-us.ts +4 -0
  73. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/zh-cn.ts +4 -0
  74. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateCreate.tsx +88 -1
  75. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateUpdate.tsx +44 -1
  76. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateView.tsx +72 -1
  77. package/vona/src/suite/a-commerce/modules/commerce-promotion/test/couponTemplateFormLayout.test.ts +121 -0
  78. package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/bean/ssrMenu.home.ts +4 -1
  79. package/vona/src/suite/a-commerce/modules/commerce-trade/package.json +4 -1
  80. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +0 -21
  81. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutResult.tsx +3 -0
  82. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +79 -0
  83. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +227 -0
  84. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +121 -13
  85. package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +312 -0
  86. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +76 -0
  87. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +842 -0
  88. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/this.ts +2 -0
  89. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payProvider.ts +92 -0
  90. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +105 -0
  91. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +28 -0
  92. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.redlock.ts +11 -0
  93. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +113 -0
  94. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/queue.outboxDispatch.ts +31 -0
  95. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/schedule.outboxDispatch.ts +11 -0
  96. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentSession.ts +55 -0
  97. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/webhook.ts +47 -0
  98. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/paymentSessionStart.tsx +8 -0
  99. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/paymentSessionView.tsx +69 -0
  100. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/webhookReceipt.tsx +12 -0
  101. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/outboxEvent.tsx +46 -0
  102. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentAudit.tsx +36 -0
  103. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentSession.tsx +72 -0
  104. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +54 -0
  105. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/refundOperation.tsx +37 -0
  106. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/webhookInbox.tsx +59 -0
  107. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/index.ts +3 -0
  108. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/index.ts +2 -0
  109. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/payProvider.ts +7 -0
  110. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/payScene.ts +7 -0
  111. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/outboxEvent.ts +10 -0
  112. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentAudit.ts +10 -0
  113. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +10 -0
  114. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/providerOperation.ts +10 -0
  115. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/refundOperation.ts +10 -0
  116. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/webhookInbox.ts +10 -0
  117. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/outbox.ts +156 -0
  118. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +278 -0
  119. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +5 -0
  120. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/webhook.ts +127 -0
  121. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/index.ts +3 -0
  122. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts +74 -0
  123. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payScene.ts +63 -0
  124. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +151 -0
  125. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/outbox.test.ts +209 -0
  126. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/paymentSession.test.ts +153 -0
  127. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/webhook.test.ts +278 -0
  128. package/vona/src/suite-vendor/a-pay/modules/a-pay/tsconfig.build.json +11 -0
  129. package/vona/src/suite-vendor/a-pay/modules/a-pay/tsconfig.json +7 -0
  130. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +55 -0
  131. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/index.ts +182 -0
  132. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/this.ts +2 -0
  133. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +149 -0
  134. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/controller/mockPayment.ts +25 -0
  135. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockPaymentComplete.tsx +13 -0
  136. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockPaymentReceipt.tsx +16 -0
  137. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/index.ts +3 -0
  138. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/lib/index.ts +1 -0
  139. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/service/payMock.ts +78 -0
  140. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/types/index.ts +10 -0
  141. package/vona/src/suite-vendor/a-pay/modules/pay-mock/test/payMock.test.ts +141 -0
  142. package/vona/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.build.json +11 -0
  143. package/vona/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.json +7 -0
  144. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +55 -0
  145. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/index.ts +62 -0
  146. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/this.ts +2 -0
  147. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +91 -0
  148. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/index.ts +3 -0
  149. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/index.ts +1 -0
  150. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/types/index.ts +9 -0
  151. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.build.json +11 -0
  152. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.json +7 -0
  153. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +55 -0
  154. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/index.ts +62 -0
  155. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/this.ts +2 -0
  156. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/bean/payProvider.stripe.ts +88 -0
  157. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/index.ts +2 -0
  158. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/types/index.ts +8 -0
  159. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.build.json +11 -0
  160. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.json +7 -0
  161. package/vona/src/suite-vendor/a-pay/package.json +15 -0
  162. package/vona/src/suite-vendor/a-pay/tsconfig.base.json +4 -0
  163. package/vona/src/suite-vendor/a-pay/tsconfig.json +19 -0
  164. package/zova/env/.env +1 -0
  165. package/zova/openapi.config.ts +5 -1
  166. package/zova/package.original.json +1 -1
  167. package/zova/packages-cli/cli/package.json +2 -2
  168. package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/project/openapi.config.ts +5 -1
  169. package/zova/packages-cli/cli-set-front/package.json +1 -1
  170. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +8 -0
  171. package/zova/packages-cli/cli-set-front/src/lib/common/cliCreatePage.ts +7 -24
  172. package/zova/packages-cli/cli-set-front/src/lib/common/utils.ts +10 -1
  173. package/zova/packages-utils/zova-jsx/package.json +2 -2
  174. package/zova/packages-zova/zova/package.json +3 -3
  175. package/zova/packages-zova/zova-core/package.json +1 -1
  176. package/zova/packages-zova/zova-core/src/types/interface/component.ts +5 -0
  177. package/zova/pnpm-lock.yaml +96 -26
  178. package/zova/src/front/config/config/config.cabloyCommerce.ts +1 -1
  179. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/schemas.ts +110 -12
  180. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/types.ts +1292 -160
  181. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +0 -1
  182. package/zova/src/suite/a-commerce/modules/commerce-trade/package.json +4 -1
  183. package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +1 -47
  184. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +23 -8
  185. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +249 -62
  186. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +5 -1
  187. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +152 -39
  188. package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +1 -1
  189. package/zova/src/suite-vendor/a-pay/modules/a-pay/cli/openapi.config.ts +9 -0
  190. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +53 -0
  191. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/component/paymentNextAction.ts +31 -0
  192. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +217 -0
  193. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/this.ts +2 -0
  194. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/baseURL.ts +5 -0
  195. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/index.ts +3 -0
  196. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/schemas.ts +584 -0
  197. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/types.ts +9324 -0
  198. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/paymentSession.ts +52 -0
  199. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/apiSchema/paymentSession.ts +20 -0
  200. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +76 -0
  201. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/index.ts +3 -0
  202. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/lib/index.ts +1 -0
  203. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +32 -0
  204. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentCoordinator.ts +13 -0
  205. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/types/index.ts +1 -0
  206. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +25 -0
  207. package/zova/src/suite-vendor/a-pay/modules/a-pay/tsconfig.build.json +13 -0
  208. package/zova/src/suite-vendor/a-pay/modules/a-pay/tsconfig.json +5 -0
  209. package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +9 -0
  210. package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +53 -0
  211. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/index.ts +140 -0
  212. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/this.ts +2 -0
  213. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/baseURL.ts +5 -0
  214. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/index.ts +3 -0
  215. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/schemas.ts +584 -0
  216. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/types.ts +9324 -0
  217. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockPayment.ts +34 -0
  218. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockPayment.ts +13 -0
  219. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/index.ts +1 -0
  220. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +20 -0
  221. package/zova/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.build.json +13 -0
  222. package/zova/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.json +5 -0
  223. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/package.json +52 -0
  224. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/index.ts +26 -0
  225. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/this.ts +2 -0
  226. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/index.ts +1 -0
  227. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.build.json +13 -0
  228. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.json +5 -0
  229. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/package.json +52 -0
  230. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/index.ts +26 -0
  231. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/this.ts +2 -0
  232. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/index.ts +1 -0
  233. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.build.json +13 -0
  234. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.json +5 -0
  235. package/zova/src/suite-vendor/a-pay/package.json +15 -0
  236. package/zova/src/suite-vendor/a-pay/tsconfig.base.json +4 -0
  237. package/zova/src/suite-vendor/a-pay/tsconfig.json +4 -0
  238. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  239. package/zova/src/suite-vendor/a-zova/package.json +2 -2
  240. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/payment.ts +0 -23
  241. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradePayment.ts +0 -36
  242. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradePayment.ts +0 -13
  243. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/payment.ts +0 -25
  244. /package/vona/patches/{zova-core@5.1.79.patch → zova-core@5.1.81.patch} +0 -0
@@ -0,0 +1,222 @@
1
+ # Instructions
2
+
3
+ - Following Playwright test failed.
4
+ - Explain why, be concise, respect Playwright best practices.
5
+ - Provide a snippet of code with the fix, if possible.
6
+
7
+ # Test info
8
+
9
+ - Name: commerce.spec.ts >> Phase 50/60: authenticated customer completes payment and observes operator shipment
10
+ - Location: e2e/specs/a-commerce/commerce.spec.ts:348:1
11
+
12
+ # Error details
13
+
14
+ ```
15
+ Error: expect(received).toEqual(expected) // deep equality
16
+
17
+ Expected: Any<String>
18
+ Received: undefined
19
+ ```
20
+
21
+ # Test source
22
+
23
+ ```ts
24
+ 1 | import type { APIRequestContext, Browser, Locator, Page, TestInfo } from '@playwright/test';
25
+ 2 |
26
+ 3 | import { expect, test } from '@playwright/test';
27
+ 4 |
28
+ 5 | const privateMarkers = ['cart', 'address', 'order', 'coupon', 'payment'];
29
+ 6 | const addressResourceUrl =
30
+ 7 | /\/commerce-admin\/rest\/resource\/commerce-member(?:%3A|:|%253A)address(?:[/?#]|$)/;
31
+ 8 | const addressMinePath = '/api/commerce/member/address/mine';
32
+ 9 | const addressActionPath = '/api/commerce/member/address';
33
+ 10 | const passportTestActivateCurrentPath = '/api/home/user/passportTest/activateCurrent';
34
+ 11 |
35
+ 12 | interface IAddressFixture {
36
+ 13 | addressLine1: string;
37
+ 14 | city: string;
38
+ 15 | recipientName: string;
39
+ 16 | updatedCity: string;
40
+ 17 | }
41
+ 18 |
42
+ 19 | function collectPageErrors(page: Page) {
43
+ 20 | const errors: Error[] = [];
44
+ 21 | page.on('pageerror', error => {
45
+ 22 | errors.push(error);
46
+ 23 | });
47
+ 24 | return errors;
48
+ 25 | }
49
+ 26 |
50
+ 27 | function waitForApiResponse(page: Page, method: string, path: string | RegExp) {
51
+ 28 | return page.waitForResponse(response => {
52
+ 29 | const url = new URL(response.url());
53
+ 30 | return (
54
+ 31 | response.request().method() === method &&
55
+ 32 | (typeof path === 'string' ? url.pathname === path : path.test(url.pathname)) &&
56
+ 33 | !response.request().headers()['x-vona-openapi-schema']
57
+ 34 | );
58
+ 35 | });
59
+ 36 | }
60
+ 37 |
61
+ 38 | function waitForAddressResponse(page: Page, method: string, path: string | RegExp) {
62
+ 39 | return waitForApiResponse(page, method, path);
63
+ 40 | }
64
+ 41 |
65
+ 42 | function waitForAddressMine(page: Page) {
66
+ 43 | return waitForAddressResponse(page, 'GET', addressMinePath);
67
+ 44 | }
68
+ 45 |
69
+ 46 | async function getAdminOrderRow(page: Page, orderId: number): Promise<Locator> {
70
+ 47 | const row = page.locator(`tr:has(td:first-child:text-is("${orderId}"))`);
71
+ 48 | await expect(row).toHaveCount(1);
72
+ 49 | return row;
73
+ 50 | }
74
+ 51 |
75
+ 52 | async function login(
76
+ 53 | page: Page,
77
+ 54 | path: string,
78
+ 55 | username: string,
79
+ 56 | password: string,
80
+ 57 | hydratedSite: string,
81
+ 58 | ) {
82
+ 59 | await page.goto(path, { waitUntil: 'load' });
83
+ 60 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', hydratedSite);
84
+ 61 | if (!page.url().includes('/login')) {
85
+ 62 | await page.waitForURL(/\/login(?:\?|$)/);
86
+ 63 | }
87
+ 64 | if (page.url().includes('/login')) {
88
+ 65 | const usernameInput = page.getByPlaceholder('Your Username');
89
+ 66 | const passwordInput = page.getByPlaceholder('Your Password');
90
+ 67 | await usernameInput.fill(username);
91
+ 68 | await passwordInput.fill(password);
92
+ 69 | await expect(usernameInput).toHaveValue(username);
93
+ 70 | await expect(passwordInput).toHaveValue(password);
94
+ 71 | await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
95
+ 72 | const loginResponse = waitForApiResponse(page, 'POST', '/api/home/user/passport/login');
96
+ 73 | await page.getByRole('button', { name: 'Login', exact: true }).click();
97
+ 74 | const loginResponseValue = await loginResponse;
98
+ 75 | if (!loginResponseValue.ok()) {
99
+ 76 | throw new Error(
100
+ 77 | `login failed: ${loginResponseValue.status()} ${await loginResponseValue.text()}`,
101
+ 78 | );
102
+ 79 | }
103
+ 80 | await expect(page).not.toHaveURL(/\/login(?:\?|$)/);
104
+ 81 | }
105
+ 82 | }
106
+ 83 |
107
+ 84 | async function registerCustomer(
108
+ 85 | request: APIRequestContext,
109
+ 86 | testInfo: TestInfo,
110
+ 87 | ): Promise<{ password: string; username: string }> {
111
+ 88 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
112
+ 89 | const username = `e2e-address-${id}`;
113
+ 90 | const password = 'address-e2e-password';
114
+ 91 | const captchaResponse = await request.post('/api/captcha/create', {
115
+ 92 | data: { scene: 'captcha-simple:simple' },
116
+ 93 | });
117
+ 94 | expect(captchaResponse.ok()).toBeTruthy();
118
+ 95 | const captcha = (await captchaResponse.json()).data;
119
+ 96 | expect(captcha?.id).toEqual(expect.any(String));
120
+ > 97 | expect(captcha?.token).toEqual(expect.any(String));
121
+ | ^ Error: expect(received).toEqual(expected) // deep equality
122
+ 98 |
123
+ 99 | const registerResponse = await request.post('/api/home/user/passport/register', {
124
+ 100 | data: {
125
+ 101 | username,
126
+ 102 | email: `${username}@example.test`,
127
+ 103 | password,
128
+ 104 | passwordConfirm: password,
129
+ 105 | captcha: { id: captcha.id, token: captcha.token },
130
+ 106 | },
131
+ 107 | });
132
+ 108 | expect(registerResponse.ok()).toBeTruthy();
133
+ 109 | const registration = (await registerResponse.json()).data;
134
+ 110 | expect(registration?.jwt?.accessToken).toEqual(expect.any(String));
135
+ 111 | const activateResponse = await request.post(passportTestActivateCurrentPath, {
136
+ 112 | headers: { Authorization: `Bearer ${registration.jwt.accessToken}` },
137
+ 113 | });
138
+ 114 | expect(activateResponse.ok()).toBeTruthy();
139
+ 115 | return { username, password };
140
+ 116 | }
141
+ 117 |
142
+ 118 | function createAddressFixture(testInfo: TestInfo): IAddressFixture {
143
+ 119 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
144
+ 120 | return {
145
+ 121 | recipientName: `E2E Address ${id}`,
146
+ 122 | addressLine1: `1 Test Street ${id}`,
147
+ 123 | city: `Original City ${id}`,
148
+ 124 | updatedCity: `Updated City ${id}`,
149
+ 125 | };
150
+ 126 | }
151
+ 127 |
152
+ 128 | async function fillAddressForm(page: Page, fixture: IAddressFixture) {
153
+ 129 | await page.getByPlaceholder('Recipient name').fill(fixture.recipientName);
154
+ 130 | await page.getByPlaceholder('Phone').fill('15555550123');
155
+ 131 | await page.getByPlaceholder('Country code').fill('US');
156
+ 132 | await page.getByPlaceholder('Region').fill('California');
157
+ 133 | await page.getByPlaceholder('City').fill(fixture.city);
158
+ 134 | await page.getByPlaceholder('Postal code').fill('94105');
159
+ 135 | await page.getByPlaceholder('Address line 1').fill(fixture.addressLine1);
160
+ 136 | await page.getByPlaceholder('Address line 2').fill('Suite E2E');
161
+ 137 | }
162
+ 138 |
163
+ 139 | async function createAddressThroughCustomerPage(
164
+ 140 | browser: Browser,
165
+ 141 | request: APIRequestContext,
166
+ 142 | testInfo: TestInfo,
167
+ 143 | ) {
168
+ 144 | const customer = await registerCustomer(request, testInfo);
169
+ 145 | const fixture = createAddressFixture(testInfo);
170
+ 146 | const context = await browser.newContext();
171
+ 147 | const page = await context.newPage();
172
+ 148 | const pageErrors = collectPageErrors(page);
173
+ 149 |
174
+ 150 | await login(page, '/commerce/address', customer.username, customer.password, 'commerce');
175
+ 151 | await expect(page).toHaveURL(/\/commerce\/address(?:\?|$)/);
176
+ 152 | await expect(page.getByRole('heading', { name: 'Addresses' })).toBeVisible();
177
+ 153 | await expect(page.getByText('No addresses yet.')).toBeVisible();
178
+ 154 |
179
+ 155 | await fillAddressForm(page, fixture);
180
+ 156 | const createResponse = waitForAddressResponse(page, 'POST', `${addressActionPath}/createMine`);
181
+ 157 | const mineResponse = waitForAddressMine(page);
182
+ 158 | await page.getByRole('button', { name: 'Save address', exact: true }).click();
183
+ 159 | await createResponse;
184
+ 160 | await mineResponse;
185
+ 161 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toBeVisible();
186
+ 162 |
187
+ 163 | return { context, fixture, page, pageErrors };
188
+ 164 | }
189
+ 165 |
190
+ 166 | async function deleteAddressThroughCustomerPage(page: Page, fixture: IAddressFixture) {
191
+ 167 | const card = page
192
+ 168 | .locator('article')
193
+ 169 | .filter({ has: page.getByRole('heading', { name: fixture.recipientName }) });
194
+ 170 | const deleteResponse = waitForAddressResponse(page, 'DELETE', /\/deleteMine\/[^/]+$/);
195
+ 171 | const mineResponse = waitForAddressMine(page);
196
+ 172 | await card.getByRole('button', { name: 'Delete', exact: true }).click();
197
+ 173 | await deleteResponse;
198
+ 174 | await mineResponse;
199
+ 175 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toHaveCount(0);
200
+ 176 | await expect(page.getByText('No addresses yet.')).toBeVisible();
201
+ 177 | }
202
+ 178 |
203
+ 179 | test(
204
+ 180 | 'ATP-SSR-01: anonymous Commerce HTML is public and hydration completes',
205
+ 181 | { tag: ['@web', '@smoke'] },
206
+ 182 | async ({ page, request }) => {
207
+ 183 | const response = await request.get('/commerce');
208
+ 184 | expect(response.ok()).toBeTruthy();
209
+ 185 | const html = await response.text();
210
+ 186 | const normalizedHtml = html.toLowerCase();
211
+ 187 | expect(normalizedHtml).not.toContain('data-zova-hydrated');
212
+ 188 |
213
+ 189 | for (const marker of privateMarkers) {
214
+ 190 | expect(normalizedHtml).not.toContain(`"${marker}"`);
215
+ 191 | }
216
+ 192 |
217
+ 193 | const pageErrors = collectPageErrors(page);
218
+ 194 | const documentResponse = await page.goto('/commerce', { waitUntil: 'load' });
219
+ 195 | expect(documentResponse?.ok()).toBeTruthy();
220
+ 196 | await expect(page).toHaveURL(/\/commerce(?:\/|$)/);
221
+ 197 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
222
+ ```
@@ -0,0 +1,222 @@
1
+ # Instructions
2
+
3
+ - Following Playwright test failed.
4
+ - Explain why, be concise, respect Playwright best practices.
5
+ - Provide a snippet of code with the fix, if possible.
6
+
7
+ # Test info
8
+
9
+ - Name: commerce.spec.ts >> ATP-ADDR-01: systemAdmin inspects Address Resource without mutation controls
10
+ - Location: e2e/specs/a-commerce/commerce.spec.ts:652:1
11
+
12
+ # Error details
13
+
14
+ ```
15
+ Error: expect(received).toEqual(expected) // deep equality
16
+
17
+ Expected: Any<String>
18
+ Received: undefined
19
+ ```
20
+
21
+ # Test source
22
+
23
+ ```ts
24
+ 1 | import type { APIRequestContext, Browser, Locator, Page, TestInfo } from '@playwright/test';
25
+ 2 |
26
+ 3 | import { expect, test } from '@playwright/test';
27
+ 4 |
28
+ 5 | const privateMarkers = ['cart', 'address', 'order', 'coupon', 'payment'];
29
+ 6 | const addressResourceUrl =
30
+ 7 | /\/commerce-admin\/rest\/resource\/commerce-member(?:%3A|:|%253A)address(?:[/?#]|$)/;
31
+ 8 | const addressMinePath = '/api/commerce/member/address/mine';
32
+ 9 | const addressActionPath = '/api/commerce/member/address';
33
+ 10 | const passportTestActivateCurrentPath = '/api/home/user/passportTest/activateCurrent';
34
+ 11 |
35
+ 12 | interface IAddressFixture {
36
+ 13 | addressLine1: string;
37
+ 14 | city: string;
38
+ 15 | recipientName: string;
39
+ 16 | updatedCity: string;
40
+ 17 | }
41
+ 18 |
42
+ 19 | function collectPageErrors(page: Page) {
43
+ 20 | const errors: Error[] = [];
44
+ 21 | page.on('pageerror', error => {
45
+ 22 | errors.push(error);
46
+ 23 | });
47
+ 24 | return errors;
48
+ 25 | }
49
+ 26 |
50
+ 27 | function waitForApiResponse(page: Page, method: string, path: string | RegExp) {
51
+ 28 | return page.waitForResponse(response => {
52
+ 29 | const url = new URL(response.url());
53
+ 30 | return (
54
+ 31 | response.request().method() === method &&
55
+ 32 | (typeof path === 'string' ? url.pathname === path : path.test(url.pathname)) &&
56
+ 33 | !response.request().headers()['x-vona-openapi-schema']
57
+ 34 | );
58
+ 35 | });
59
+ 36 | }
60
+ 37 |
61
+ 38 | function waitForAddressResponse(page: Page, method: string, path: string | RegExp) {
62
+ 39 | return waitForApiResponse(page, method, path);
63
+ 40 | }
64
+ 41 |
65
+ 42 | function waitForAddressMine(page: Page) {
66
+ 43 | return waitForAddressResponse(page, 'GET', addressMinePath);
67
+ 44 | }
68
+ 45 |
69
+ 46 | async function getAdminOrderRow(page: Page, orderId: number): Promise<Locator> {
70
+ 47 | const row = page.locator(`tr:has(td:first-child:text-is("${orderId}"))`);
71
+ 48 | await expect(row).toHaveCount(1);
72
+ 49 | return row;
73
+ 50 | }
74
+ 51 |
75
+ 52 | async function login(
76
+ 53 | page: Page,
77
+ 54 | path: string,
78
+ 55 | username: string,
79
+ 56 | password: string,
80
+ 57 | hydratedSite: string,
81
+ 58 | ) {
82
+ 59 | await page.goto(path, { waitUntil: 'load' });
83
+ 60 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', hydratedSite);
84
+ 61 | if (!page.url().includes('/login')) {
85
+ 62 | await page.waitForURL(/\/login(?:\?|$)/);
86
+ 63 | }
87
+ 64 | if (page.url().includes('/login')) {
88
+ 65 | const usernameInput = page.getByPlaceholder('Your Username');
89
+ 66 | const passwordInput = page.getByPlaceholder('Your Password');
90
+ 67 | await usernameInput.fill(username);
91
+ 68 | await passwordInput.fill(password);
92
+ 69 | await expect(usernameInput).toHaveValue(username);
93
+ 70 | await expect(passwordInput).toHaveValue(password);
94
+ 71 | await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
95
+ 72 | const loginResponse = waitForApiResponse(page, 'POST', '/api/home/user/passport/login');
96
+ 73 | await page.getByRole('button', { name: 'Login', exact: true }).click();
97
+ 74 | const loginResponseValue = await loginResponse;
98
+ 75 | if (!loginResponseValue.ok()) {
99
+ 76 | throw new Error(
100
+ 77 | `login failed: ${loginResponseValue.status()} ${await loginResponseValue.text()}`,
101
+ 78 | );
102
+ 79 | }
103
+ 80 | await expect(page).not.toHaveURL(/\/login(?:\?|$)/);
104
+ 81 | }
105
+ 82 | }
106
+ 83 |
107
+ 84 | async function registerCustomer(
108
+ 85 | request: APIRequestContext,
109
+ 86 | testInfo: TestInfo,
110
+ 87 | ): Promise<{ password: string; username: string }> {
111
+ 88 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
112
+ 89 | const username = `e2e-address-${id}`;
113
+ 90 | const password = 'address-e2e-password';
114
+ 91 | const captchaResponse = await request.post('/api/captcha/create', {
115
+ 92 | data: { scene: 'captcha-simple:simple' },
116
+ 93 | });
117
+ 94 | expect(captchaResponse.ok()).toBeTruthy();
118
+ 95 | const captcha = (await captchaResponse.json()).data;
119
+ 96 | expect(captcha?.id).toEqual(expect.any(String));
120
+ > 97 | expect(captcha?.token).toEqual(expect.any(String));
121
+ | ^ Error: expect(received).toEqual(expected) // deep equality
122
+ 98 |
123
+ 99 | const registerResponse = await request.post('/api/home/user/passport/register', {
124
+ 100 | data: {
125
+ 101 | username,
126
+ 102 | email: `${username}@example.test`,
127
+ 103 | password,
128
+ 104 | passwordConfirm: password,
129
+ 105 | captcha: { id: captcha.id, token: captcha.token },
130
+ 106 | },
131
+ 107 | });
132
+ 108 | expect(registerResponse.ok()).toBeTruthy();
133
+ 109 | const registration = (await registerResponse.json()).data;
134
+ 110 | expect(registration?.jwt?.accessToken).toEqual(expect.any(String));
135
+ 111 | const activateResponse = await request.post(passportTestActivateCurrentPath, {
136
+ 112 | headers: { Authorization: `Bearer ${registration.jwt.accessToken}` },
137
+ 113 | });
138
+ 114 | expect(activateResponse.ok()).toBeTruthy();
139
+ 115 | return { username, password };
140
+ 116 | }
141
+ 117 |
142
+ 118 | function createAddressFixture(testInfo: TestInfo): IAddressFixture {
143
+ 119 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
144
+ 120 | return {
145
+ 121 | recipientName: `E2E Address ${id}`,
146
+ 122 | addressLine1: `1 Test Street ${id}`,
147
+ 123 | city: `Original City ${id}`,
148
+ 124 | updatedCity: `Updated City ${id}`,
149
+ 125 | };
150
+ 126 | }
151
+ 127 |
152
+ 128 | async function fillAddressForm(page: Page, fixture: IAddressFixture) {
153
+ 129 | await page.getByPlaceholder('Recipient name').fill(fixture.recipientName);
154
+ 130 | await page.getByPlaceholder('Phone').fill('15555550123');
155
+ 131 | await page.getByPlaceholder('Country code').fill('US');
156
+ 132 | await page.getByPlaceholder('Region').fill('California');
157
+ 133 | await page.getByPlaceholder('City').fill(fixture.city);
158
+ 134 | await page.getByPlaceholder('Postal code').fill('94105');
159
+ 135 | await page.getByPlaceholder('Address line 1').fill(fixture.addressLine1);
160
+ 136 | await page.getByPlaceholder('Address line 2').fill('Suite E2E');
161
+ 137 | }
162
+ 138 |
163
+ 139 | async function createAddressThroughCustomerPage(
164
+ 140 | browser: Browser,
165
+ 141 | request: APIRequestContext,
166
+ 142 | testInfo: TestInfo,
167
+ 143 | ) {
168
+ 144 | const customer = await registerCustomer(request, testInfo);
169
+ 145 | const fixture = createAddressFixture(testInfo);
170
+ 146 | const context = await browser.newContext();
171
+ 147 | const page = await context.newPage();
172
+ 148 | const pageErrors = collectPageErrors(page);
173
+ 149 |
174
+ 150 | await login(page, '/commerce/address', customer.username, customer.password, 'commerce');
175
+ 151 | await expect(page).toHaveURL(/\/commerce\/address(?:\?|$)/);
176
+ 152 | await expect(page.getByRole('heading', { name: 'Addresses' })).toBeVisible();
177
+ 153 | await expect(page.getByText('No addresses yet.')).toBeVisible();
178
+ 154 |
179
+ 155 | await fillAddressForm(page, fixture);
180
+ 156 | const createResponse = waitForAddressResponse(page, 'POST', `${addressActionPath}/createMine`);
181
+ 157 | const mineResponse = waitForAddressMine(page);
182
+ 158 | await page.getByRole('button', { name: 'Save address', exact: true }).click();
183
+ 159 | await createResponse;
184
+ 160 | await mineResponse;
185
+ 161 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toBeVisible();
186
+ 162 |
187
+ 163 | return { context, fixture, page, pageErrors };
188
+ 164 | }
189
+ 165 |
190
+ 166 | async function deleteAddressThroughCustomerPage(page: Page, fixture: IAddressFixture) {
191
+ 167 | const card = page
192
+ 168 | .locator('article')
193
+ 169 | .filter({ has: page.getByRole('heading', { name: fixture.recipientName }) });
194
+ 170 | const deleteResponse = waitForAddressResponse(page, 'DELETE', /\/deleteMine\/[^/]+$/);
195
+ 171 | const mineResponse = waitForAddressMine(page);
196
+ 172 | await card.getByRole('button', { name: 'Delete', exact: true }).click();
197
+ 173 | await deleteResponse;
198
+ 174 | await mineResponse;
199
+ 175 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toHaveCount(0);
200
+ 176 | await expect(page.getByText('No addresses yet.')).toBeVisible();
201
+ 177 | }
202
+ 178 |
203
+ 179 | test(
204
+ 180 | 'ATP-SSR-01: anonymous Commerce HTML is public and hydration completes',
205
+ 181 | { tag: ['@web', '@smoke'] },
206
+ 182 | async ({ page, request }) => {
207
+ 183 | const response = await request.get('/commerce');
208
+ 184 | expect(response.ok()).toBeTruthy();
209
+ 185 | const html = await response.text();
210
+ 186 | const normalizedHtml = html.toLowerCase();
211
+ 187 | expect(normalizedHtml).not.toContain('data-zova-hydrated');
212
+ 188 |
213
+ 189 | for (const marker of privateMarkers) {
214
+ 190 | expect(normalizedHtml).not.toContain(`"${marker}"`);
215
+ 191 | }
216
+ 192 |
217
+ 193 | const pageErrors = collectPageErrors(page);
218
+ 194 | const documentResponse = await page.goto('/commerce', { waitUntil: 'load' });
219
+ 195 | expect(documentResponse?.ok()).toBeTruthy();
220
+ 196 | await expect(page).toHaveURL(/\/commerce(?:\/|$)/);
221
+ 197 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
222
+ ```
@@ -0,0 +1,222 @@
1
+ # Instructions
2
+
3
+ - Following Playwright test failed.
4
+ - Explain why, be concise, respect Playwright best practices.
5
+ - Provide a snippet of code with the fix, if possible.
6
+
7
+ # Test info
8
+
9
+ - Name: commerce.spec.ts >> Phase 60: customer requests and operator executes a whole-order refund
10
+ - Location: e2e/specs/a-commerce/commerce.spec.ts:539:1
11
+
12
+ # Error details
13
+
14
+ ```
15
+ Error: expect(received).toEqual(expected) // deep equality
16
+
17
+ Expected: Any<String>
18
+ Received: undefined
19
+ ```
20
+
21
+ # Test source
22
+
23
+ ```ts
24
+ 1 | import type { APIRequestContext, Browser, Locator, Page, TestInfo } from '@playwright/test';
25
+ 2 |
26
+ 3 | import { expect, test } from '@playwright/test';
27
+ 4 |
28
+ 5 | const privateMarkers = ['cart', 'address', 'order', 'coupon', 'payment'];
29
+ 6 | const addressResourceUrl =
30
+ 7 | /\/commerce-admin\/rest\/resource\/commerce-member(?:%3A|:|%253A)address(?:[/?#]|$)/;
31
+ 8 | const addressMinePath = '/api/commerce/member/address/mine';
32
+ 9 | const addressActionPath = '/api/commerce/member/address';
33
+ 10 | const passportTestActivateCurrentPath = '/api/home/user/passportTest/activateCurrent';
34
+ 11 |
35
+ 12 | interface IAddressFixture {
36
+ 13 | addressLine1: string;
37
+ 14 | city: string;
38
+ 15 | recipientName: string;
39
+ 16 | updatedCity: string;
40
+ 17 | }
41
+ 18 |
42
+ 19 | function collectPageErrors(page: Page) {
43
+ 20 | const errors: Error[] = [];
44
+ 21 | page.on('pageerror', error => {
45
+ 22 | errors.push(error);
46
+ 23 | });
47
+ 24 | return errors;
48
+ 25 | }
49
+ 26 |
50
+ 27 | function waitForApiResponse(page: Page, method: string, path: string | RegExp) {
51
+ 28 | return page.waitForResponse(response => {
52
+ 29 | const url = new URL(response.url());
53
+ 30 | return (
54
+ 31 | response.request().method() === method &&
55
+ 32 | (typeof path === 'string' ? url.pathname === path : path.test(url.pathname)) &&
56
+ 33 | !response.request().headers()['x-vona-openapi-schema']
57
+ 34 | );
58
+ 35 | });
59
+ 36 | }
60
+ 37 |
61
+ 38 | function waitForAddressResponse(page: Page, method: string, path: string | RegExp) {
62
+ 39 | return waitForApiResponse(page, method, path);
63
+ 40 | }
64
+ 41 |
65
+ 42 | function waitForAddressMine(page: Page) {
66
+ 43 | return waitForAddressResponse(page, 'GET', addressMinePath);
67
+ 44 | }
68
+ 45 |
69
+ 46 | async function getAdminOrderRow(page: Page, orderId: number): Promise<Locator> {
70
+ 47 | const row = page.locator(`tr:has(td:first-child:text-is("${orderId}"))`);
71
+ 48 | await expect(row).toHaveCount(1);
72
+ 49 | return row;
73
+ 50 | }
74
+ 51 |
75
+ 52 | async function login(
76
+ 53 | page: Page,
77
+ 54 | path: string,
78
+ 55 | username: string,
79
+ 56 | password: string,
80
+ 57 | hydratedSite: string,
81
+ 58 | ) {
82
+ 59 | await page.goto(path, { waitUntil: 'load' });
83
+ 60 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', hydratedSite);
84
+ 61 | if (!page.url().includes('/login')) {
85
+ 62 | await page.waitForURL(/\/login(?:\?|$)/);
86
+ 63 | }
87
+ 64 | if (page.url().includes('/login')) {
88
+ 65 | const usernameInput = page.getByPlaceholder('Your Username');
89
+ 66 | const passwordInput = page.getByPlaceholder('Your Password');
90
+ 67 | await usernameInput.fill(username);
91
+ 68 | await passwordInput.fill(password);
92
+ 69 | await expect(usernameInput).toHaveValue(username);
93
+ 70 | await expect(passwordInput).toHaveValue(password);
94
+ 71 | await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
95
+ 72 | const loginResponse = waitForApiResponse(page, 'POST', '/api/home/user/passport/login');
96
+ 73 | await page.getByRole('button', { name: 'Login', exact: true }).click();
97
+ 74 | const loginResponseValue = await loginResponse;
98
+ 75 | if (!loginResponseValue.ok()) {
99
+ 76 | throw new Error(
100
+ 77 | `login failed: ${loginResponseValue.status()} ${await loginResponseValue.text()}`,
101
+ 78 | );
102
+ 79 | }
103
+ 80 | await expect(page).not.toHaveURL(/\/login(?:\?|$)/);
104
+ 81 | }
105
+ 82 | }
106
+ 83 |
107
+ 84 | async function registerCustomer(
108
+ 85 | request: APIRequestContext,
109
+ 86 | testInfo: TestInfo,
110
+ 87 | ): Promise<{ password: string; username: string }> {
111
+ 88 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
112
+ 89 | const username = `e2e-address-${id}`;
113
+ 90 | const password = 'address-e2e-password';
114
+ 91 | const captchaResponse = await request.post('/api/captcha/create', {
115
+ 92 | data: { scene: 'captcha-simple:simple' },
116
+ 93 | });
117
+ 94 | expect(captchaResponse.ok()).toBeTruthy();
118
+ 95 | const captcha = (await captchaResponse.json()).data;
119
+ 96 | expect(captcha?.id).toEqual(expect.any(String));
120
+ > 97 | expect(captcha?.token).toEqual(expect.any(String));
121
+ | ^ Error: expect(received).toEqual(expected) // deep equality
122
+ 98 |
123
+ 99 | const registerResponse = await request.post('/api/home/user/passport/register', {
124
+ 100 | data: {
125
+ 101 | username,
126
+ 102 | email: `${username}@example.test`,
127
+ 103 | password,
128
+ 104 | passwordConfirm: password,
129
+ 105 | captcha: { id: captcha.id, token: captcha.token },
130
+ 106 | },
131
+ 107 | });
132
+ 108 | expect(registerResponse.ok()).toBeTruthy();
133
+ 109 | const registration = (await registerResponse.json()).data;
134
+ 110 | expect(registration?.jwt?.accessToken).toEqual(expect.any(String));
135
+ 111 | const activateResponse = await request.post(passportTestActivateCurrentPath, {
136
+ 112 | headers: { Authorization: `Bearer ${registration.jwt.accessToken}` },
137
+ 113 | });
138
+ 114 | expect(activateResponse.ok()).toBeTruthy();
139
+ 115 | return { username, password };
140
+ 116 | }
141
+ 117 |
142
+ 118 | function createAddressFixture(testInfo: TestInfo): IAddressFixture {
143
+ 119 | const id = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
144
+ 120 | return {
145
+ 121 | recipientName: `E2E Address ${id}`,
146
+ 122 | addressLine1: `1 Test Street ${id}`,
147
+ 123 | city: `Original City ${id}`,
148
+ 124 | updatedCity: `Updated City ${id}`,
149
+ 125 | };
150
+ 126 | }
151
+ 127 |
152
+ 128 | async function fillAddressForm(page: Page, fixture: IAddressFixture) {
153
+ 129 | await page.getByPlaceholder('Recipient name').fill(fixture.recipientName);
154
+ 130 | await page.getByPlaceholder('Phone').fill('15555550123');
155
+ 131 | await page.getByPlaceholder('Country code').fill('US');
156
+ 132 | await page.getByPlaceholder('Region').fill('California');
157
+ 133 | await page.getByPlaceholder('City').fill(fixture.city);
158
+ 134 | await page.getByPlaceholder('Postal code').fill('94105');
159
+ 135 | await page.getByPlaceholder('Address line 1').fill(fixture.addressLine1);
160
+ 136 | await page.getByPlaceholder('Address line 2').fill('Suite E2E');
161
+ 137 | }
162
+ 138 |
163
+ 139 | async function createAddressThroughCustomerPage(
164
+ 140 | browser: Browser,
165
+ 141 | request: APIRequestContext,
166
+ 142 | testInfo: TestInfo,
167
+ 143 | ) {
168
+ 144 | const customer = await registerCustomer(request, testInfo);
169
+ 145 | const fixture = createAddressFixture(testInfo);
170
+ 146 | const context = await browser.newContext();
171
+ 147 | const page = await context.newPage();
172
+ 148 | const pageErrors = collectPageErrors(page);
173
+ 149 |
174
+ 150 | await login(page, '/commerce/address', customer.username, customer.password, 'commerce');
175
+ 151 | await expect(page).toHaveURL(/\/commerce\/address(?:\?|$)/);
176
+ 152 | await expect(page.getByRole('heading', { name: 'Addresses' })).toBeVisible();
177
+ 153 | await expect(page.getByText('No addresses yet.')).toBeVisible();
178
+ 154 |
179
+ 155 | await fillAddressForm(page, fixture);
180
+ 156 | const createResponse = waitForAddressResponse(page, 'POST', `${addressActionPath}/createMine`);
181
+ 157 | const mineResponse = waitForAddressMine(page);
182
+ 158 | await page.getByRole('button', { name: 'Save address', exact: true }).click();
183
+ 159 | await createResponse;
184
+ 160 | await mineResponse;
185
+ 161 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toBeVisible();
186
+ 162 |
187
+ 163 | return { context, fixture, page, pageErrors };
188
+ 164 | }
189
+ 165 |
190
+ 166 | async function deleteAddressThroughCustomerPage(page: Page, fixture: IAddressFixture) {
191
+ 167 | const card = page
192
+ 168 | .locator('article')
193
+ 169 | .filter({ has: page.getByRole('heading', { name: fixture.recipientName }) });
194
+ 170 | const deleteResponse = waitForAddressResponse(page, 'DELETE', /\/deleteMine\/[^/]+$/);
195
+ 171 | const mineResponse = waitForAddressMine(page);
196
+ 172 | await card.getByRole('button', { name: 'Delete', exact: true }).click();
197
+ 173 | await deleteResponse;
198
+ 174 | await mineResponse;
199
+ 175 | await expect(page.getByRole('heading', { name: fixture.recipientName })).toHaveCount(0);
200
+ 176 | await expect(page.getByText('No addresses yet.')).toBeVisible();
201
+ 177 | }
202
+ 178 |
203
+ 179 | test(
204
+ 180 | 'ATP-SSR-01: anonymous Commerce HTML is public and hydration completes',
205
+ 181 | { tag: ['@web', '@smoke'] },
206
+ 182 | async ({ page, request }) => {
207
+ 183 | const response = await request.get('/commerce');
208
+ 184 | expect(response.ok()).toBeTruthy();
209
+ 185 | const html = await response.text();
210
+ 186 | const normalizedHtml = html.toLowerCase();
211
+ 187 | expect(normalizedHtml).not.toContain('data-zova-hydrated');
212
+ 188 |
213
+ 189 | for (const marker of privateMarkers) {
214
+ 190 | expect(normalizedHtml).not.toContain(`"${marker}"`);
215
+ 191 | }
216
+ 192 |
217
+ 193 | const pageErrors = collectPageErrors(page);
218
+ 194 | const documentResponse = await page.goto('/commerce', { waitUntil: 'load' });
219
+ 195 | expect(documentResponse?.ok()).toBeTruthy();
220
+ 196 | await expect(page).toHaveURL(/\/commerce(?:\/|$)/);
221
+ 197 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
222
+ ```