@open-mercato/core 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3287.1.450f4ffb56

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 (266) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/domain_mapping/index.js +31 -0
  3. package/dist/generated/entities/domain_mapping/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +16 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/auth.js +1 -1
  9. package/dist/helpers/integration/auth.js.map +2 -2
  10. package/dist/helpers/integration/authUi.js +1 -1
  11. package/dist/helpers/integration/authUi.js.map +2 -2
  12. package/dist/helpers/integration/salesUi.js +18 -17
  13. package/dist/helpers/integration/salesUi.js.map +2 -2
  14. package/dist/modules/auth/frontend/login.js +4 -4
  15. package/dist/modules/auth/frontend/login.js.map +2 -2
  16. package/dist/modules/auth/frontend/reset/[token]/page.js +5 -5
  17. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  18. package/dist/modules/auth/frontend/reset.js +2 -2
  19. package/dist/modules/auth/frontend/reset.js.map +2 -2
  20. package/dist/modules/customer_accounts/acl.js +2 -1
  21. package/dist/modules/customer_accounts/acl.js.map +2 -2
  22. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
  23. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
  24. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
  25. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
  26. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
  27. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
  28. package/dist/modules/customer_accounts/api/domain-check.js +70 -0
  29. package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
  30. package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
  31. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
  32. package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
  33. package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
  34. package/dist/modules/customer_accounts/api/login.js +14 -3
  35. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  36. package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
  37. package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
  38. package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
  39. package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
  40. package/dist/modules/customer_accounts/api/signup.js +32 -5
  41. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  42. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
  43. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
  44. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
  45. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
  46. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
  47. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
  48. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
  49. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
  50. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
  51. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
  52. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
  53. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
  55. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
  56. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
  57. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
  58. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
  59. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
  60. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
  61. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
  62. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
  63. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
  64. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
  65. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
  66. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +4 -3
  67. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  68. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +6 -5
  69. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  70. package/dist/modules/customer_accounts/data/enrichers.js +78 -12
  71. package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
  72. package/dist/modules/customer_accounts/data/entities.js +85 -2
  73. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  74. package/dist/modules/customer_accounts/data/guards.js +130 -0
  75. package/dist/modules/customer_accounts/data/guards.js.map +7 -0
  76. package/dist/modules/customer_accounts/data/validators.js +19 -0
  77. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  78. package/dist/modules/customer_accounts/di.js +23 -1
  79. package/dist/modules/customer_accounts/di.js.map +2 -2
  80. package/dist/modules/customer_accounts/events.js +9 -2
  81. package/dist/modules/customer_accounts/events.js.map +2 -2
  82. package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
  83. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  84. package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
  85. package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
  86. package/dist/modules/customer_accounts/lib/hostname.js +55 -0
  87. package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
  88. package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
  89. package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
  90. package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
  91. package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
  92. package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
  93. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
  94. package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
  95. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
  96. package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
  97. package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
  98. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
  99. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
  100. package/dist/modules/customer_accounts/notifications.client.js +76 -0
  101. package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
  102. package/dist/modules/customer_accounts/notifications.js +76 -0
  103. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  104. package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
  105. package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
  106. package/dist/modules/customer_accounts/setup.js +48 -1
  107. package/dist/modules/customer_accounts/setup.js.map +2 -2
  108. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
  109. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
  110. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
  111. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
  112. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
  113. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
  114. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
  115. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
  116. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
  117. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
  118. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
  119. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  120. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
  121. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
  122. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
  123. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
  124. package/dist/modules/customers/api/companies/route.js +4 -9
  125. package/dist/modules/customers/api/companies/route.js.map +2 -2
  126. package/dist/modules/customers/api/deals/route.js +4 -9
  127. package/dist/modules/customers/api/deals/route.js.map +2 -2
  128. package/dist/modules/customers/api/people/route.js +4 -9
  129. package/dist/modules/customers/api/people/route.js.map +2 -2
  130. package/dist/modules/customers/api/utils.js +0 -12
  131. package/dist/modules/customers/api/utils.js.map +2 -2
  132. package/dist/modules/customers/backend/customers/companies/page.js +361 -284
  133. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/deals/page.js +378 -473
  135. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  136. package/dist/modules/customers/backend/customers/people/page.js +368 -286
  137. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  138. package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
  139. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  140. package/dist/modules/customers/components/formConfig.js +2 -2
  141. package/dist/modules/customers/components/formConfig.js.map +2 -2
  142. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
  143. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  144. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
  145. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
  146. package/dist/modules/customers/widgets/injection-table.js +6 -0
  147. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  148. package/dist/modules/integrations/backend/integrations/[id]/page.js +2 -3
  149. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  150. package/dist/modules/perspectives/services/perspectiveService.js +16 -2
  151. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  152. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +5 -3
  153. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  154. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +3 -3
  155. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
  156. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +4 -2
  157. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
  158. package/dist/modules/query_index/lib/engine.js +31 -8
  159. package/dist/modules/query_index/lib/engine.js.map +2 -2
  160. package/dist/modules/sales/backend/sales/documents/[id]/page.js +3 -5
  161. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  162. package/dist/modules/sales/components/ProviderFieldInput.js +2 -2
  163. package/dist/modules/sales/components/ProviderFieldInput.js.map +2 -2
  164. package/dist/modules/sales/components/documents/SalesDocumentForm.js +3 -4
  165. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  166. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js +3 -4
  167. package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js.map +2 -2
  168. package/dist/modules/workflows/backend/tasks/[id]/page.js +2 -2
  169. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  170. package/dist/modules/workflows/frontend/checkout-demo/page.js +2 -2
  171. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  172. package/generated/entities/domain_mapping/index.ts +14 -0
  173. package/generated/entities.ids.generated.ts +2 -1
  174. package/generated/entity-fields-registry.ts +16 -0
  175. package/package.json +5 -5
  176. package/src/helpers/integration/auth.ts +1 -1
  177. package/src/helpers/integration/authUi.ts +1 -1
  178. package/src/helpers/integration/salesUi.ts +27 -23
  179. package/src/modules/auth/frontend/login.tsx +4 -3
  180. package/src/modules/auth/frontend/reset/[token]/page.tsx +4 -4
  181. package/src/modules/auth/frontend/reset.tsx +2 -2
  182. package/src/modules/customer_accounts/acl.ts +1 -0
  183. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
  184. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
  185. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
  186. package/src/modules/customer_accounts/api/domain-check.ts +83 -0
  187. package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
  188. package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
  189. package/src/modules/customer_accounts/api/login.ts +14 -3
  190. package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
  191. package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
  192. package/src/modules/customer_accounts/api/signup.ts +36 -5
  193. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
  194. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
  195. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
  196. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
  197. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
  198. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
  199. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
  200. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
  201. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
  202. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
  203. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
  204. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
  205. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +3 -2
  206. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +5 -4
  207. package/src/modules/customer_accounts/data/enrichers.ts +95 -12
  208. package/src/modules/customer_accounts/data/entities.ts +80 -1
  209. package/src/modules/customer_accounts/data/guards.ts +155 -0
  210. package/src/modules/customer_accounts/data/validators.ts +26 -0
  211. package/src/modules/customer_accounts/di.ts +35 -1
  212. package/src/modules/customer_accounts/events.ts +8 -1
  213. package/src/modules/customer_accounts/i18n/de.json +78 -0
  214. package/src/modules/customer_accounts/i18n/en.json +78 -0
  215. package/src/modules/customer_accounts/i18n/es.json +78 -0
  216. package/src/modules/customer_accounts/i18n/pl.json +78 -0
  217. package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
  218. package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
  219. package/src/modules/customer_accounts/lib/hostname.ts +76 -0
  220. package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
  221. package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
  222. package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
  223. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
  224. package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
  225. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
  226. package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
  227. package/src/modules/customer_accounts/notifications.client.ts +76 -0
  228. package/src/modules/customer_accounts/notifications.ts +76 -0
  229. package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
  230. package/src/modules/customer_accounts/setup.ts +81 -1
  231. package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
  232. package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
  233. package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
  234. package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
  235. package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
  236. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
  237. package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
  238. package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
  239. package/src/modules/customers/api/companies/route.ts +4 -9
  240. package/src/modules/customers/api/deals/route.ts +4 -9
  241. package/src/modules/customers/api/people/route.ts +4 -9
  242. package/src/modules/customers/api/utils.ts +0 -14
  243. package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
  244. package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
  245. package/src/modules/customers/backend/customers/people/page.tsx +331 -240
  246. package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
  247. package/src/modules/customers/components/formConfig.tsx +2 -2
  248. package/src/modules/customers/i18n/de.json +31 -0
  249. package/src/modules/customers/i18n/en.json +31 -0
  250. package/src/modules/customers/i18n/es.json +31 -0
  251. package/src/modules/customers/i18n/pl.json +31 -0
  252. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
  253. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
  254. package/src/modules/customers/widgets/injection-table.ts +6 -0
  255. package/src/modules/integrations/backend/integrations/[id]/page.tsx +2 -2
  256. package/src/modules/perspectives/services/perspectiveService.ts +30 -2
  257. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +5 -3
  258. package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +3 -3
  259. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +4 -2
  260. package/src/modules/query_index/lib/engine.ts +46 -8
  261. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +3 -5
  262. package/src/modules/sales/components/ProviderFieldInput.tsx +2 -2
  263. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +2 -3
  264. package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.tsx +3 -4
  265. package/src/modules/workflows/backend/tasks/[id]/page.tsx +2 -2
  266. package/src/modules/workflows/frontend/checkout-demo/page.tsx +2 -2
@@ -186,7 +186,7 @@ async function login(page, role = "admin") {
186
186
  }
187
187
  }
188
188
  await page.getByLabel("Email").fill(creds.email);
189
- const passwordInput = page.getByLabel("Password").first();
189
+ const passwordInput = page.getByLabel("Password", { exact: true }).first();
190
190
  if (await passwordInput.isVisible().catch(() => false)) {
191
191
  await passwordInput.fill(creds.password);
192
192
  await passwordInput.press("Enter");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/helpers/integration/auth.ts"],
4
- "sourcesContent": ["import { type Page } from '@playwright/test';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\n\nfunction loadEnvFileContent(): string | null {\n const candidatePaths = [\n resolve(process.cwd(), 'apps/mercato/.env'),\n resolve(process.cwd(), '.env'),\n ];\n\n for (const envPath of candidatePaths) {\n try {\n const content = readFileSync(envPath, 'utf-8');\n if (content.trim().length > 0) {\n return content;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\nfunction loadEnvValue(key: string): string | undefined {\n if (process.env[key]) return process.env[key];\n const content = loadEnvFileContent();\n if (!content) return undefined;\n const match = content.match(new RegExp(`^${key}=(.+)$`, 'm'));\n return match?.[1]?.trim();\n}\n\nexport const DEFAULT_CREDENTIALS: Record<string, { email: string; password: string }> = {\n superadmin: {\n email: loadEnvValue('OM_INIT_SUPERADMIN_EMAIL') || 'superadmin@acme.com',\n password: loadEnvValue('OM_INIT_SUPERADMIN_PASSWORD') || 'secret',\n },\n admin: { email: 'admin@acme.com', password: 'secret' },\n employee: { email: 'employee@acme.com', password: 'secret' },\n};\n\nexport type Role = 'superadmin' | 'admin' | 'employee';\n\nfunction decodeJwtClaims(token: string): { tenantId?: string; orgId?: string | null } | null {\n const parts = token.split('.');\n if (parts.length < 2) return null;\n try {\n const normalized = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');\n const payload = JSON.parse(Buffer.from(padded, 'base64').toString('utf8')) as {\n tenantId?: string;\n orgId?: string | null;\n };\n return payload;\n } catch {\n return null;\n }\n}\n\nasync function acknowledgeGlobalNotices(page: Page): Promise<void> {\n const baseUrl = process.env.BASE_URL || 'http://localhost:3000';\n await page.context().addCookies([\n {\n name: 'om_demo_notice_ack',\n value: 'ack',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_cookie_notice_ack',\n value: 'ack',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_feedback_suppress',\n value: '1',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_feedback_shown',\n value: new Date().toISOString().slice(0, 10),\n url: baseUrl,\n sameSite: 'Lax',\n },\n ]);\n}\n\nasync function dismissGlobalNoticesIfPresent(page: Page): Promise<void> {\n const cookieAcceptButton = page.getByRole('button', { name: /accept cookies/i }).first();\n if (await cookieAcceptButton.isVisible().catch(() => false)) {\n await cookieAcceptButton.click();\n }\n\n const demoNotice = page.getByText(/this instance is provided for demo purposes only/i).first();\n if (await demoNotice.isVisible().catch(() => false)) {\n const noticeContainer = demoNotice.locator('xpath=ancestor::div[contains(@class,\"pointer-events-auto\")]').first();\n const dismissButton = noticeContainer.locator('button').first();\n if (await dismissButton.isVisible().catch(() => false)) {\n await dismissButton.click();\n }\n }\n\n const feedbackDialog = page.getByRole('dialog', { name: /Talk to Open Mercato team/i }).first();\n if (await feedbackDialog.isVisible().catch(() => false)) {\n const closeButton = feedbackDialog.getByRole('button', { name: /close/i }).first();\n if (await closeButton.isVisible().catch(() => false)) {\n await closeButton.click().catch(() => {});\n } else {\n await page.keyboard.press('Escape').catch(() => {});\n }\n }\n}\n\nasync function recoverClientSideErrorPageIfPresent(page: Page): Promise<void> {\n const clientErrorHeading = page\n .getByRole('heading', { name: /Application error: a client-side exception has occurred/i })\n .first();\n if (!(await clientErrorHeading.isVisible().catch(() => false))) return;\n await page.reload({ waitUntil: 'domcontentloaded' });\n await dismissGlobalNoticesIfPresent(page);\n}\n\nasync function recoverGenericErrorPageIfPresent(page: Page): Promise<void> {\n const errorHeading = page.getByRole('heading', { name: /^Something went wrong$/i }).first();\n if (!(await errorHeading.isVisible().catch(() => false))) return;\n const retryButton = page.getByRole('button', { name: /Try again/i }).first();\n if (await retryButton.isVisible().catch(() => false)) {\n await retryButton.click().catch(() => {});\n await page.waitForLoadState('domcontentloaded').catch(() => {});\n await page.waitForTimeout(500).catch(() => {});\n } else {\n await page.reload({ waitUntil: 'domcontentloaded' });\n }\n await dismissGlobalNoticesIfPresent(page);\n}\n\nexport async function login(page: Page, role: Role = 'admin'): Promise<void> {\n const creds = DEFAULT_CREDENTIALS[role];\n const loginReadySelector = 'form[data-auth-ready=\"1\"]';\n const hasBackendUrl = (): boolean => /\\/backend(?:\\/.*)?$/.test(page.url());\n const waitForBackend = async (timeout: number): Promise<boolean> => {\n try {\n await page.waitForURL(/\\/backend(?:\\/.*)?$/, { timeout });\n return true;\n } catch {\n return hasBackendUrl();\n }\n };\n\n await acknowledgeGlobalNotices(page);\n const apiLoginForm = new URLSearchParams();\n apiLoginForm.set('email', creds.email);\n apiLoginForm.set('password', creds.password);\n // Retry-on-429 against the auth rate limit (5/60s per email). Capped\n // exponential backoff: 1s, 2s, 4s \u2014 worst-case ~7s.\n let apiLoginResponse: Awaited<ReturnType<typeof page.request.post>> | null = null;\n for (let retry = 0; retry < 4; retry += 1) {\n apiLoginResponse = await page.request.post('/api/auth/login', {\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n },\n data: apiLoginForm.toString(),\n }).catch(() => null);\n if (!apiLoginResponse || apiLoginResponse.status() !== 429) break;\n await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** retry));\n }\n if (apiLoginResponse?.ok()) {\n const apiLoginBody = (await apiLoginResponse.json().catch(() => null)) as { token?: string } | null;\n const claims = typeof apiLoginBody?.token === 'string' ? decodeJwtClaims(apiLoginBody.token) : null;\n const baseUrl = process.env.BASE_URL || 'http://localhost:3000';\n const cookies = [];\n if (claims?.tenantId) {\n cookies.push({\n name: 'om_selected_tenant',\n value: claims.tenantId,\n url: baseUrl,\n sameSite: 'Lax' as const,\n });\n }\n if (claims?.orgId) {\n cookies.push({\n name: 'om_selected_org',\n value: claims.orgId,\n url: baseUrl,\n sameSite: 'Lax' as const,\n });\n }\n if (cookies.length > 0) {\n await page.context().addCookies(cookies);\n }\n await page.goto('/backend', { waitUntil: 'domcontentloaded' });\n if (await waitForBackend(8_000)) return;\n }\n\n for (let attempt = 0; attempt < 4; attempt += 1) {\n await page.goto('/login', { waitUntil: 'domcontentloaded' });\n await dismissGlobalNoticesIfPresent(page);\n await recoverClientSideErrorPageIfPresent(page);\n await recoverGenericErrorPageIfPresent(page);\n await page.waitForSelector(loginReadySelector, { state: 'visible', timeout: 3_000 }).catch(() => null);\n if (await page.getByLabel('Email').isVisible().catch(() => false)) break;\n if (attempt === 3) {\n throw new Error(`Login form is unavailable for role: ${role}; current URL: ${page.url()}`);\n }\n }\n await page.getByLabel('Email').fill(creds.email);\n\n const passwordInput = page.getByLabel('Password').first();\n if (await passwordInput.isVisible().catch(() => false)) {\n await passwordInput.fill(creds.password);\n await passwordInput.press('Enter');\n } else {\n const submitButton = page.getByRole('button', { name: /login|sign in|continue with sso/i }).first();\n await submitButton.click();\n }\n\n if (await waitForBackend(7_000)) return;\n\n const loginForm = page.locator('form').first();\n if (await loginForm.isVisible().catch(() => false)) {\n await loginForm.evaluate((element) => {\n const form = element as HTMLFormElement\n form.requestSubmit()\n }).catch(() => {})\n }\n if (await waitForBackend(5_000)) return;\n\n const loginButton = page.getByRole('button', { name: /login|sign in|continue with sso/i }).first();\n if (await loginButton.isVisible().catch(() => false)) {\n await loginButton.click({ force: true });\n }\n if (await waitForBackend(8_000)) return;\n\n throw new Error(`Login did not reach backend for role: ${role}; current URL: ${page.url()}`);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type Page } from '@playwright/test';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\n\nfunction loadEnvFileContent(): string | null {\n const candidatePaths = [\n resolve(process.cwd(), 'apps/mercato/.env'),\n resolve(process.cwd(), '.env'),\n ];\n\n for (const envPath of candidatePaths) {\n try {\n const content = readFileSync(envPath, 'utf-8');\n if (content.trim().length > 0) {\n return content;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\nfunction loadEnvValue(key: string): string | undefined {\n if (process.env[key]) return process.env[key];\n const content = loadEnvFileContent();\n if (!content) return undefined;\n const match = content.match(new RegExp(`^${key}=(.+)$`, 'm'));\n return match?.[1]?.trim();\n}\n\nexport const DEFAULT_CREDENTIALS: Record<string, { email: string; password: string }> = {\n superadmin: {\n email: loadEnvValue('OM_INIT_SUPERADMIN_EMAIL') || 'superadmin@acme.com',\n password: loadEnvValue('OM_INIT_SUPERADMIN_PASSWORD') || 'secret',\n },\n admin: { email: 'admin@acme.com', password: 'secret' },\n employee: { email: 'employee@acme.com', password: 'secret' },\n};\n\nexport type Role = 'superadmin' | 'admin' | 'employee';\n\nfunction decodeJwtClaims(token: string): { tenantId?: string; orgId?: string | null } | null {\n const parts = token.split('.');\n if (parts.length < 2) return null;\n try {\n const normalized = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');\n const payload = JSON.parse(Buffer.from(padded, 'base64').toString('utf8')) as {\n tenantId?: string;\n orgId?: string | null;\n };\n return payload;\n } catch {\n return null;\n }\n}\n\nasync function acknowledgeGlobalNotices(page: Page): Promise<void> {\n const baseUrl = process.env.BASE_URL || 'http://localhost:3000';\n await page.context().addCookies([\n {\n name: 'om_demo_notice_ack',\n value: 'ack',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_cookie_notice_ack',\n value: 'ack',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_feedback_suppress',\n value: '1',\n url: baseUrl,\n sameSite: 'Lax',\n },\n {\n name: 'om_feedback_shown',\n value: new Date().toISOString().slice(0, 10),\n url: baseUrl,\n sameSite: 'Lax',\n },\n ]);\n}\n\nasync function dismissGlobalNoticesIfPresent(page: Page): Promise<void> {\n const cookieAcceptButton = page.getByRole('button', { name: /accept cookies/i }).first();\n if (await cookieAcceptButton.isVisible().catch(() => false)) {\n await cookieAcceptButton.click();\n }\n\n const demoNotice = page.getByText(/this instance is provided for demo purposes only/i).first();\n if (await demoNotice.isVisible().catch(() => false)) {\n const noticeContainer = demoNotice.locator('xpath=ancestor::div[contains(@class,\"pointer-events-auto\")]').first();\n const dismissButton = noticeContainer.locator('button').first();\n if (await dismissButton.isVisible().catch(() => false)) {\n await dismissButton.click();\n }\n }\n\n const feedbackDialog = page.getByRole('dialog', { name: /Talk to Open Mercato team/i }).first();\n if (await feedbackDialog.isVisible().catch(() => false)) {\n const closeButton = feedbackDialog.getByRole('button', { name: /close/i }).first();\n if (await closeButton.isVisible().catch(() => false)) {\n await closeButton.click().catch(() => {});\n } else {\n await page.keyboard.press('Escape').catch(() => {});\n }\n }\n}\n\nasync function recoverClientSideErrorPageIfPresent(page: Page): Promise<void> {\n const clientErrorHeading = page\n .getByRole('heading', { name: /Application error: a client-side exception has occurred/i })\n .first();\n if (!(await clientErrorHeading.isVisible().catch(() => false))) return;\n await page.reload({ waitUntil: 'domcontentloaded' });\n await dismissGlobalNoticesIfPresent(page);\n}\n\nasync function recoverGenericErrorPageIfPresent(page: Page): Promise<void> {\n const errorHeading = page.getByRole('heading', { name: /^Something went wrong$/i }).first();\n if (!(await errorHeading.isVisible().catch(() => false))) return;\n const retryButton = page.getByRole('button', { name: /Try again/i }).first();\n if (await retryButton.isVisible().catch(() => false)) {\n await retryButton.click().catch(() => {});\n await page.waitForLoadState('domcontentloaded').catch(() => {});\n await page.waitForTimeout(500).catch(() => {});\n } else {\n await page.reload({ waitUntil: 'domcontentloaded' });\n }\n await dismissGlobalNoticesIfPresent(page);\n}\n\nexport async function login(page: Page, role: Role = 'admin'): Promise<void> {\n const creds = DEFAULT_CREDENTIALS[role];\n const loginReadySelector = 'form[data-auth-ready=\"1\"]';\n const hasBackendUrl = (): boolean => /\\/backend(?:\\/.*)?$/.test(page.url());\n const waitForBackend = async (timeout: number): Promise<boolean> => {\n try {\n await page.waitForURL(/\\/backend(?:\\/.*)?$/, { timeout });\n return true;\n } catch {\n return hasBackendUrl();\n }\n };\n\n await acknowledgeGlobalNotices(page);\n const apiLoginForm = new URLSearchParams();\n apiLoginForm.set('email', creds.email);\n apiLoginForm.set('password', creds.password);\n // Retry-on-429 against the auth rate limit (5/60s per email). Capped\n // exponential backoff: 1s, 2s, 4s \u2014 worst-case ~7s.\n let apiLoginResponse: Awaited<ReturnType<typeof page.request.post>> | null = null;\n for (let retry = 0; retry < 4; retry += 1) {\n apiLoginResponse = await page.request.post('/api/auth/login', {\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n },\n data: apiLoginForm.toString(),\n }).catch(() => null);\n if (!apiLoginResponse || apiLoginResponse.status() !== 429) break;\n await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** retry));\n }\n if (apiLoginResponse?.ok()) {\n const apiLoginBody = (await apiLoginResponse.json().catch(() => null)) as { token?: string } | null;\n const claims = typeof apiLoginBody?.token === 'string' ? decodeJwtClaims(apiLoginBody.token) : null;\n const baseUrl = process.env.BASE_URL || 'http://localhost:3000';\n const cookies = [];\n if (claims?.tenantId) {\n cookies.push({\n name: 'om_selected_tenant',\n value: claims.tenantId,\n url: baseUrl,\n sameSite: 'Lax' as const,\n });\n }\n if (claims?.orgId) {\n cookies.push({\n name: 'om_selected_org',\n value: claims.orgId,\n url: baseUrl,\n sameSite: 'Lax' as const,\n });\n }\n if (cookies.length > 0) {\n await page.context().addCookies(cookies);\n }\n await page.goto('/backend', { waitUntil: 'domcontentloaded' });\n if (await waitForBackend(8_000)) return;\n }\n\n for (let attempt = 0; attempt < 4; attempt += 1) {\n await page.goto('/login', { waitUntil: 'domcontentloaded' });\n await dismissGlobalNoticesIfPresent(page);\n await recoverClientSideErrorPageIfPresent(page);\n await recoverGenericErrorPageIfPresent(page);\n await page.waitForSelector(loginReadySelector, { state: 'visible', timeout: 3_000 }).catch(() => null);\n if (await page.getByLabel('Email').isVisible().catch(() => false)) break;\n if (attempt === 3) {\n throw new Error(`Login form is unavailable for role: ${role}; current URL: ${page.url()}`);\n }\n }\n await page.getByLabel('Email').fill(creds.email);\n\n const passwordInput = page.getByLabel('Password', { exact: true }).first();\n if (await passwordInput.isVisible().catch(() => false)) {\n await passwordInput.fill(creds.password);\n await passwordInput.press('Enter');\n } else {\n const submitButton = page.getByRole('button', { name: /login|sign in|continue with sso/i }).first();\n await submitButton.click();\n }\n\n if (await waitForBackend(7_000)) return;\n\n const loginForm = page.locator('form').first();\n if (await loginForm.isVisible().catch(() => false)) {\n await loginForm.evaluate((element) => {\n const form = element as HTMLFormElement\n form.requestSubmit()\n }).catch(() => {})\n }\n if (await waitForBackend(5_000)) return;\n\n const loginButton = page.getByRole('button', { name: /login|sign in|continue with sso/i }).first();\n if (await loginButton.isVisible().catch(() => false)) {\n await loginButton.click({ force: true });\n }\n if (await waitForBackend(8_000)) return;\n\n throw new Error(`Login did not reach backend for role: ${role}; current URL: ${page.url()}`);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["resolve"]
7
7
  }
@@ -30,7 +30,7 @@ async function createUserViaUi(page, input) {
30
30
  await rolesInput.press("Enter");
31
31
  await page.getByRole("button", { name: "Create" }).first().click();
32
32
  await expect(page).toHaveURL(/\/backend\/users(?:\?.*)?$/);
33
- await page.getByRole("textbox", { name: "Search", exact: true }).fill(input.email);
33
+ await page.getByRole("searchbox", { name: "Search", exact: true }).fill(input.email);
34
34
  await expect(page.getByRole("row", { name: new RegExp(input.email, "i") })).toBeVisible();
35
35
  }
36
36
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/helpers/integration/authUi.ts"],
4
- "sourcesContent": ["import { expect, type Page } from '@playwright/test';\n\nexport async function createUserViaUi(page: Page, input: { email: string; password: string; role?: string }) {\n const role = input.role ?? 'employee';\n\n await page.goto('/backend/users/create');\n await expect(page.getByText('Create User')).toBeVisible();\n\n const emailInput = page.locator('[data-crud-field-id=\"email\"] input').first();\n const nameInput = page.locator('[data-crud-field-id=\"name\"] input').first();\n const passwordInput = page.locator('[data-crud-field-id=\"password\"] input').first();\n\n await expect(emailInput).toBeVisible();\n await emailInput.fill(input.email);\n if (await nameInput.count()) {\n await nameInput.fill('');\n }\n await expect(passwordInput).toBeVisible();\n await passwordInput.fill(input.password);\n\n const orgSelect = page.locator('main').locator('select').first();\n await expect(orgSelect).toBeEnabled();\n const orgValue = await orgSelect.evaluate((element) => {\n const select = element as HTMLSelectElement;\n for (const option of Array.from(select.options)) {\n if (option.value && option.value.trim().length > 0) return option.value;\n }\n return '';\n });\n if (orgValue) {\n await orgSelect.selectOption(orgValue);\n }\n\n const rolesInput = page.getByRole('textbox', { name: /add tag and press enter/i });\n await rolesInput.fill(role);\n await rolesInput.press('Enter');\n\n await page.getByRole('button', { name: 'Create' }).first().click();\n await expect(page).toHaveURL(/\\/backend\\/users(?:\\?.*)?$/);\n await page.getByRole('textbox', { name: 'Search', exact: true }).fill(input.email);\n await expect(page.getByRole('row', { name: new RegExp(input.email, 'i') })).toBeVisible();\n}\n"],
5
- "mappings": "AAAA,SAAS,cAAyB;AAElC,eAAsB,gBAAgB,MAAY,OAA2D;AAC3G,QAAM,OAAO,MAAM,QAAQ;AAE3B,QAAM,KAAK,KAAK,uBAAuB;AACvC,QAAM,OAAO,KAAK,UAAU,aAAa,CAAC,EAAE,YAAY;AAExD,QAAM,aAAa,KAAK,QAAQ,oCAAoC,EAAE,MAAM;AAC5E,QAAM,YAAY,KAAK,QAAQ,mCAAmC,EAAE,MAAM;AAC1E,QAAM,gBAAgB,KAAK,QAAQ,uCAAuC,EAAE,MAAM;AAElF,QAAM,OAAO,UAAU,EAAE,YAAY;AACrC,QAAM,WAAW,KAAK,MAAM,KAAK;AACjC,MAAI,MAAM,UAAU,MAAM,GAAG;AAC3B,UAAM,UAAU,KAAK,EAAE;AAAA,EACzB;AACA,QAAM,OAAO,aAAa,EAAE,YAAY;AACxC,QAAM,cAAc,KAAK,MAAM,QAAQ;AAEvC,QAAM,YAAY,KAAK,QAAQ,MAAM,EAAE,QAAQ,QAAQ,EAAE,MAAM;AAC/D,QAAM,OAAO,SAAS,EAAE,YAAY;AACpC,QAAM,WAAW,MAAM,UAAU,SAAS,CAAC,YAAY;AACrD,UAAM,SAAS;AACf,eAAW,UAAU,MAAM,KAAK,OAAO,OAAO,GAAG;AAC/C,UAAI,OAAO,SAAS,OAAO,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,OAAO;AAAA,IACpE;AACA,WAAO;AAAA,EACT,CAAC;AACD,MAAI,UAAU;AACZ,UAAM,UAAU,aAAa,QAAQ;AAAA,EACvC;AAEA,QAAM,aAAa,KAAK,UAAU,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACjF,QAAM,WAAW,KAAK,IAAI;AAC1B,QAAM,WAAW,MAAM,OAAO;AAE9B,QAAM,KAAK,UAAU,UAAU,EAAE,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM;AACjE,QAAM,OAAO,IAAI,EAAE,UAAU,4BAA4B;AACzD,QAAM,KAAK,UAAU,WAAW,EAAE,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,KAAK,MAAM,KAAK;AACjF,QAAM,OAAO,KAAK,UAAU,OAAO,EAAE,MAAM,IAAI,OAAO,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY;AAC1F;",
4
+ "sourcesContent": ["import { expect, type Page } from '@playwright/test';\n\nexport async function createUserViaUi(page: Page, input: { email: string; password: string; role?: string }) {\n const role = input.role ?? 'employee';\n\n await page.goto('/backend/users/create');\n await expect(page.getByText('Create User')).toBeVisible();\n\n const emailInput = page.locator('[data-crud-field-id=\"email\"] input').first();\n const nameInput = page.locator('[data-crud-field-id=\"name\"] input').first();\n const passwordInput = page.locator('[data-crud-field-id=\"password\"] input').first();\n\n await expect(emailInput).toBeVisible();\n await emailInput.fill(input.email);\n if (await nameInput.count()) {\n await nameInput.fill('');\n }\n await expect(passwordInput).toBeVisible();\n await passwordInput.fill(input.password);\n\n const orgSelect = page.locator('main').locator('select').first();\n await expect(orgSelect).toBeEnabled();\n const orgValue = await orgSelect.evaluate((element) => {\n const select = element as HTMLSelectElement;\n for (const option of Array.from(select.options)) {\n if (option.value && option.value.trim().length > 0) return option.value;\n }\n return '';\n });\n if (orgValue) {\n await orgSelect.selectOption(orgValue);\n }\n\n const rolesInput = page.getByRole('textbox', { name: /add tag and press enter/i });\n await rolesInput.fill(role);\n await rolesInput.press('Enter');\n\n await page.getByRole('button', { name: 'Create' }).first().click();\n await expect(page).toHaveURL(/\\/backend\\/users(?:\\?.*)?$/);\n await page.getByRole('searchbox', { name: 'Search', exact: true }).fill(input.email);\n await expect(page.getByRole('row', { name: new RegExp(input.email, 'i') })).toBeVisible();\n}\n"],
5
+ "mappings": "AAAA,SAAS,cAAyB;AAElC,eAAsB,gBAAgB,MAAY,OAA2D;AAC3G,QAAM,OAAO,MAAM,QAAQ;AAE3B,QAAM,KAAK,KAAK,uBAAuB;AACvC,QAAM,OAAO,KAAK,UAAU,aAAa,CAAC,EAAE,YAAY;AAExD,QAAM,aAAa,KAAK,QAAQ,oCAAoC,EAAE,MAAM;AAC5E,QAAM,YAAY,KAAK,QAAQ,mCAAmC,EAAE,MAAM;AAC1E,QAAM,gBAAgB,KAAK,QAAQ,uCAAuC,EAAE,MAAM;AAElF,QAAM,OAAO,UAAU,EAAE,YAAY;AACrC,QAAM,WAAW,KAAK,MAAM,KAAK;AACjC,MAAI,MAAM,UAAU,MAAM,GAAG;AAC3B,UAAM,UAAU,KAAK,EAAE;AAAA,EACzB;AACA,QAAM,OAAO,aAAa,EAAE,YAAY;AACxC,QAAM,cAAc,KAAK,MAAM,QAAQ;AAEvC,QAAM,YAAY,KAAK,QAAQ,MAAM,EAAE,QAAQ,QAAQ,EAAE,MAAM;AAC/D,QAAM,OAAO,SAAS,EAAE,YAAY;AACpC,QAAM,WAAW,MAAM,UAAU,SAAS,CAAC,YAAY;AACrD,UAAM,SAAS;AACf,eAAW,UAAU,MAAM,KAAK,OAAO,OAAO,GAAG;AAC/C,UAAI,OAAO,SAAS,OAAO,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,OAAO;AAAA,IACpE;AACA,WAAO;AAAA,EACT,CAAC;AACD,MAAI,UAAU;AACZ,UAAM,UAAU,aAAa,QAAQ;AAAA,EACvC;AAEA,QAAM,aAAa,KAAK,UAAU,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACjF,QAAM,WAAW,KAAK,IAAI;AAC1B,QAAM,WAAW,MAAM,OAAO;AAE9B,QAAM,KAAK,UAAU,UAAU,EAAE,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM;AACjE,QAAM,OAAO,IAAI,EAAE,UAAU,4BAA4B;AACzD,QAAM,KAAK,UAAU,aAAa,EAAE,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,KAAK,MAAM,KAAK;AACnF,QAAM,OAAO,KAAK,UAAU,OAAO,EAAE,MAAM,IAAI,OAAO,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY;AAC1F;",
6
6
  "names": []
7
7
  }
@@ -13,9 +13,6 @@ function parseCurrencyAmount(value) {
13
13
  }
14
14
  return Number.parseFloat(lastMatch.replace("$", ""));
15
15
  }
16
- function normalizeAdjustmentKindValue(kindLabel) {
17
- return kindLabel.trim().toLowerCase().replace(/\s+/g, "_");
18
- }
19
16
  function readId(payload, keys) {
20
17
  if (!payload || typeof payload !== "object") return null;
21
18
  const map = payload;
@@ -837,27 +834,31 @@ async function addAdjustment(page, options) {
837
834
  });
838
835
  const kindTrigger = dialog.locator('[data-crud-field-id="kind"] [role="combobox"]').first();
839
836
  await expect(kindTrigger).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
840
- const labelInput = dialog.getByPlaceholder(/e\.g\. Shipping fee/i).first();
841
- await expect(labelInput).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
842
- await fillControlledInput(labelInput, options.label, 5e3);
843
- if (await kindTrigger.count() > 0) {
844
- const expectedKindValue = normalizeAdjustmentKindValue(options.kindLabel ?? "Surcharge");
845
- const kindLabel = options.kindLabel ?? "Surcharge";
846
- await kindTrigger.click();
847
- const option = page.getByRole("option", { name: new RegExp(`^${escapeRegExp(kindLabel)}$`, "i") }).first();
848
- const optionVisible = await option.waitFor({ state: "visible", timeout: 2e3 }).then(
849
- () => true,
850
- () => false
837
+ const kindLabel = options.kindLabel ?? "Surcharge";
838
+ const selectKindOption = async () => {
839
+ if (await kindTrigger.count() === 0) return;
840
+ await waitForOptionalTextToDisappear(
841
+ dialog,
842
+ /Loading adjustment kinds…|Loading adjustment kinds\.\.\./i,
843
+ TEST_WAIT_TIMEOUT_MS
851
844
  );
845
+ await expect(kindTrigger).toBeEnabled({ timeout: TEST_WAIT_TIMEOUT_MS });
846
+ await kindTrigger.click();
847
+ const kindOption = page.getByRole("option", { name: new RegExp(`^${escapeRegExp(kindLabel)}$`, "i") }).first();
848
+ const optionVisible = await kindOption.waitFor({ state: "visible", timeout: TEST_WAIT_TIMEOUT_MS }).then(() => true, () => false);
852
849
  if (optionVisible) {
853
- await option.click({ force: true });
850
+ await kindOption.click({ force: true });
854
851
  } else {
855
852
  await page.keyboard.type(kindLabel.charAt(0));
856
853
  await page.waitForTimeout(150);
857
854
  await page.keyboard.press("Enter");
858
855
  }
859
- void expectedKindValue;
860
- }
856
+ await expect(kindTrigger).toContainText(kindLabel, { timeout: 2e3 });
857
+ };
858
+ await selectKindOption();
859
+ const labelInput = dialog.getByPlaceholder(/e\.g\. Shipping fee/i).first();
860
+ await expect(labelInput).toBeVisible({ timeout: TEST_WAIT_TIMEOUT_MS });
861
+ await fillControlledInput(labelInput, options.label);
861
862
  const fixedAmountButton = dialog.getByRole("button", { name: /^Fixed amount$/i }).first();
862
863
  if (await fixedAmountButton.count() > 0) {
863
864
  await fixedAmountButton.click().catch(() => {