@m4l/testing 0.0.24 → 1.0.1

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 (235) hide show
  1. package/README.md +679 -0
  2. package/chunks/reactLeaflet-Cffkauof.js +1846 -0
  3. package/chunks/render-CsuV8xqo.js +17 -0
  4. package/chunks/userPreferences-BmOgRaIX.js +493 -0
  5. package/index.js +76 -25
  6. package/mocks/index.js +41 -0
  7. package/package.json +84 -69
  8. package/playwright/index.js +2041 -0
  9. package/rtl/index.js +180 -0
  10. package/types/libs/observability/mf/src/browserRuntimeHandles.d.ts +112 -0
  11. package/types/libs/observability/mf/src/diagnosticLogging.d.ts +2 -0
  12. package/types/libs/observability/mf/src/diagnosticPolicies/catalog.d.ts +267 -0
  13. package/types/libs/observability/mf/src/diagnosticPolicies/contracts.d.ts +64 -0
  14. package/types/libs/observability/mf/src/diagnosticPolicies/host.d.ts +139 -0
  15. package/types/libs/observability/mf/src/diagnosticPolicies/index.d.ts +6 -0
  16. package/types/libs/observability/mf/src/diagnosticPolicies/operativeMyGpsDevices.d.ts +104 -0
  17. package/types/libs/observability/mf/src/diagnosticPolicies/sharedReportsList.d.ts +44 -0
  18. package/types/libs/observability/mf/src/diagnosticPolicyKeys.d.ts +34 -0
  19. package/types/libs/observability/mf/src/index.d.ts +14 -0
  20. package/types/libs/observability/mf/src/runtimeSmoke.d.ts +13 -0
  21. package/types/libs/observability/mf/src/telemetry.d.ts +2 -0
  22. package/types/libs/observability/shared/src/diagnosticLogging.d.ts +97 -0
  23. package/types/libs/observability/shared/src/index.d.ts +6 -0
  24. package/types/libs/observability/shared/src/runtimeHandles.d.ts +27 -0
  25. package/types/libs/observability/shared/src/telemetry.d.ts +179 -0
  26. package/types/packages/testing/mocks/src/constants.d.ts +10 -0
  27. package/types/packages/testing/mocks/src/fixtures.d.ts +258 -0
  28. package/types/packages/testing/mocks/src/helpers/environment.d.ts +44 -0
  29. package/types/packages/testing/mocks/src/helpers/hostTools.d.ts +10 -0
  30. package/types/packages/testing/mocks/src/helpers/index.d.ts +5 -0
  31. package/types/packages/testing/mocks/src/helpers/moduleDictionary.d.ts +9 -0
  32. package/types/packages/testing/mocks/src/helpers/toaster.d.ts +9 -0
  33. package/types/packages/testing/mocks/src/helpers/userPreferences.d.ts +17 -0
  34. package/types/packages/testing/mocks/src/index.d.ts +4 -0
  35. package/types/packages/testing/mocks/src/types.d.ts +233 -0
  36. package/types/packages/testing/playwright/src/fixtures/createHostAreaFixture.d.ts +5 -0
  37. package/types/packages/testing/playwright/src/fixtures/createHostAuthenticatedMfMenuFixture.d.ts +5 -0
  38. package/types/packages/testing/playwright/src/fixtures/createHostAuthenticatedMfModuleDictionaryFixture.d.ts +5 -0
  39. package/types/packages/testing/playwright/src/fixtures/createHostAuthenticatedSessionFixture.d.ts +6 -0
  40. package/types/packages/testing/playwright/src/fixtures/createHostCookieFixture.d.ts +5 -0
  41. package/types/packages/testing/playwright/src/fixtures/createHostGuestSessionFixture.d.ts +5 -0
  42. package/types/packages/testing/playwright/src/fixtures/createHostLocaleFixture.d.ts +5 -0
  43. package/types/packages/testing/playwright/src/fixtures/createHostMfPreviewDynamicParamsFixture.d.ts +5 -0
  44. package/types/packages/testing/playwright/src/fixtures/createHostModuleDictionaryFixture.d.ts +11 -0
  45. package/types/packages/testing/playwright/src/fixtures/createHostUiConfigFixture.d.ts +7 -0
  46. package/types/packages/testing/playwright/src/fixtures/createHostUserPreferencesFixture.d.ts +6 -0
  47. package/types/packages/testing/playwright/src/fixtures/createListResponseFixture.d.ts +6 -0
  48. package/types/packages/testing/playwright/src/fixtures/index.d.ts +12 -0
  49. package/types/packages/testing/playwright/src/helpers/attachJson.d.ts +9 -0
  50. package/types/packages/testing/playwright/src/helpers/formAccess.d.ts +53 -0
  51. package/types/packages/testing/playwright/src/helpers/formSelect.d.ts +51 -0
  52. package/types/packages/testing/playwright/src/helpers/formatFailedChecks.d.ts +18 -0
  53. package/types/packages/testing/playwright/src/helpers/index.d.ts +16 -0
  54. package/types/packages/testing/playwright/src/helpers/listDatagrid.d.ts +219 -0
  55. package/types/packages/testing/playwright/src/helpers/listDetail.d.ts +65 -0
  56. package/types/packages/testing/playwright/src/helpers/listFilter.d.ts +27 -0
  57. package/types/packages/testing/playwright/src/helpers/listSort.d.ts +19 -0
  58. package/types/packages/testing/playwright/src/helpers/listStatePersistence.d.ts +15 -0
  59. package/types/packages/testing/playwright/src/helpers/openHostAccountMenuModule.d.ts +10 -0
  60. package/types/packages/testing/playwright/src/helpers/openHostAuthenticatedMfPreviewModule.d.ts +5 -0
  61. package/types/packages/testing/playwright/src/helpers/openHostMobileSidebarModule.d.ts +15 -0
  62. package/types/packages/testing/playwright/src/helpers/openHostSidebarModule.d.ts +5 -0
  63. package/types/packages/testing/playwright/src/helpers/realtimePerformanceBrowser.d.ts +144 -0
  64. package/types/packages/testing/playwright/src/helpers/viewModeDataGrid.d.ts +67 -0
  65. package/types/packages/testing/playwright/src/helpers/waitForBrowserCondition.d.ts +38 -0
  66. package/types/packages/testing/playwright/src/index.d.ts +11 -0
  67. package/types/packages/testing/playwright/src/installers/index.d.ts +20 -0
  68. package/types/packages/testing/playwright/src/installers/installHostAlarmsCountRoute.d.ts +5 -0
  69. package/types/packages/testing/playwright/src/installers/installHostAreasRoute.d.ts +5 -0
  70. package/types/packages/testing/playwright/src/installers/installHostAuthenticatedBootstrapRoutes.d.ts +6 -0
  71. package/types/packages/testing/playwright/src/installers/installHostAuthenticatedMfModuleDictionaryRoute.d.ts +5 -0
  72. package/types/packages/testing/playwright/src/installers/installHostAuthenticatedMfPreviewBootstrap.d.ts +11 -0
  73. package/types/packages/testing/playwright/src/installers/installHostAuthenticatedSessionRoute.d.ts +6 -0
  74. package/types/packages/testing/playwright/src/installers/installHostCookiesRoute.d.ts +8 -0
  75. package/types/packages/testing/playwright/src/installers/installHostGuestBootstrapRoutes.d.ts +7 -0
  76. package/types/packages/testing/playwright/src/installers/installHostGuestSessionRoute.d.ts +7 -0
  77. package/types/packages/testing/playwright/src/installers/installHostLocaleRoute.d.ts +5 -0
  78. package/types/packages/testing/playwright/src/installers/installHostMenusRoute.d.ts +11 -0
  79. package/types/packages/testing/playwright/src/installers/installHostModuleDictionaryRoute.d.ts +6 -0
  80. package/types/packages/testing/playwright/src/installers/installHostPasswordNewRoute.d.ts +6 -0
  81. package/types/packages/testing/playwright/src/installers/installHostPasswordResetRoute.d.ts +6 -0
  82. package/types/packages/testing/playwright/src/installers/installHostUiConfigAssetRoutes.d.ts +9 -0
  83. package/types/packages/testing/playwright/src/installers/installHostUiConfigRoute.d.ts +7 -0
  84. package/types/packages/testing/playwright/src/installers/installHostUserPreferencesRoute.d.ts +6 -0
  85. package/types/packages/testing/playwright/src/installers/installListEndpointRoute.d.ts +7 -0
  86. package/types/packages/testing/playwright/src/installers/installMicrofrontendRenderStub.d.ts +9 -0
  87. package/types/packages/testing/playwright/src/installers/installModulePrivilegesRoute.d.ts +11 -0
  88. package/types/packages/testing/playwright/src/types.d.ts +598 -0
  89. package/types/packages/testing/rtl/src/index.d.ts +4 -0
  90. package/types/packages/testing/rtl/src/matchMedia.d.ts +10 -0
  91. package/types/packages/testing/rtl/src/render.d.ts +17 -0
  92. package/types/packages/testing/rtl/src/resizeObserver.d.ts +33 -0
  93. package/types/packages/testing/rtl/src/storage.d.ts +10 -0
  94. package/types/packages/testing/src/index.d.ts +6 -0
  95. package/types/packages/testing/vitest/src/constants.d.ts +24 -0
  96. package/types/packages/testing/vitest/src/factories/app/index.d.ts +5 -0
  97. package/types/packages/testing/vitest/src/factories/app/m4lAppBase.d.ts +9 -0
  98. package/types/packages/testing/vitest/src/factories/app/m4lAppComponents.d.ts +20 -0
  99. package/types/packages/testing/vitest/src/factories/app/m4lAppCore.d.ts +12 -0
  100. package/types/packages/testing/vitest/src/factories/app/m4lAppGraphics.d.ts +10 -0
  101. package/types/packages/testing/vitest/src/factories/app/m4lAppLayouts.d.ts +18 -0
  102. package/types/packages/testing/vitest/src/factories/base/index.d.ts +1 -0
  103. package/types/packages/testing/vitest/src/factories/base/m4lBaseComponents.d.ts +13 -0
  104. package/types/packages/testing/vitest/src/factories/index.d.ts +7 -0
  105. package/types/packages/testing/vitest/src/factories/leaflet.d.ts +10 -0
  106. package/types/packages/testing/vitest/src/factories/packages/index.d.ts +7 -0
  107. package/types/packages/testing/vitest/src/factories/packages/m4lPackageBase.d.ts +11 -0
  108. package/types/packages/testing/vitest/src/factories/packages/m4lPackageComponents.d.ts +11 -0
  109. package/types/packages/testing/vitest/src/factories/packages/m4lPackageCore.d.ts +11 -0
  110. package/types/packages/testing/vitest/src/factories/packages/m4lPackageGclick.d.ts +10 -0
  111. package/types/packages/testing/vitest/src/factories/packages/m4lPackageGclickRealtime.d.ts +6 -0
  112. package/types/packages/testing/vitest/src/factories/packages/m4lPackageGraphics.d.ts +9 -0
  113. package/types/packages/testing/vitest/src/factories/packages/m4lPackageLayouts.d.ts +10 -0
  114. package/types/packages/testing/vitest/src/factories/reResizable.d.ts +6 -0
  115. package/types/packages/testing/vitest/src/factories/reactHookForm.d.ts +10 -0
  116. package/types/packages/testing/vitest/src/factories/reactLeaflet.d.ts +10 -0
  117. package/types/packages/testing/vitest/src/fixtures.d.ts +71 -0
  118. package/types/packages/testing/vitest/src/helpers/columnConfigs.d.ts +184 -0
  119. package/types/packages/testing/vitest/src/helpers/elementMocks.d.ts +166 -0
  120. package/types/packages/testing/vitest/src/helpers/filterFields.d.ts +37 -0
  121. package/types/packages/testing/vitest/src/helpers/index.d.ts +9 -0
  122. package/types/packages/testing/vitest/src/helpers/mapStore.d.ts +9 -0
  123. package/types/packages/testing/vitest/src/helpers/moduleContract.d.ts +15 -0
  124. package/types/packages/testing/vitest/src/helpers/rhf.d.ts +8 -0
  125. package/types/packages/testing/vitest/src/helpers/serialization.d.ts +7 -0
  126. package/types/packages/testing/vitest/src/helpers/sortFields.d.ts +28 -0
  127. package/types/packages/testing/vitest/src/helpers/windowSizes.d.ts +1 -0
  128. package/types/packages/testing/vitest/src/index.d.ts +6 -0
  129. package/types/packages/testing/vitest/src/installers/consoleError.d.ts +9 -0
  130. package/types/packages/testing/vitest/src/installers/index.d.ts +2 -0
  131. package/types/packages/testing/vitest/src/installers/rhfSubmit.d.ts +9 -0
  132. package/types/packages/testing/vitest/src/types.d.ts +116 -0
  133. package/vitest/index.js +55 -0
  134. package/e2e/commands/components/Accordions/commands.d.ts +0 -4
  135. package/e2e/commands/components/Accordions/commands.js +0 -19
  136. package/e2e/commands/components/Accordions/types.d.ts +0 -63
  137. package/e2e/commands/components/CommonActions/commands.d.ts +0 -4
  138. package/e2e/commands/components/CommonActions/commands.js +0 -11
  139. package/e2e/commands/components/CommonActions/types.d.ts +0 -20
  140. package/e2e/commands/components/DataGrid/commands.d.ts +0 -4
  141. package/e2e/commands/components/DataGrid/commands.js +0 -42
  142. package/e2e/commands/components/DataGrid/types.d.ts +0 -140
  143. package/e2e/commands/components/DynamicFilter/commands.d.ts +0 -4
  144. package/e2e/commands/components/DynamicFilter/commands.js +0 -12
  145. package/e2e/commands/components/DynamicFilter/types.d.ts +0 -27
  146. package/e2e/commands/components/DynamicSort/commands.d.ts +0 -4
  147. package/e2e/commands/components/DynamicSort/commands.js +0 -10
  148. package/e2e/commands/components/DynamicSort/types.d.ts +0 -26
  149. package/e2e/commands/components/Filters/commands.d.ts +0 -4
  150. package/e2e/commands/components/Filters/commands.js +0 -24
  151. package/e2e/commands/components/Filters/types.d.ts +0 -91
  152. package/e2e/commands/components/Form/commands.d.ts +0 -4
  153. package/e2e/commands/components/Form/commands.js +0 -11
  154. package/e2e/commands/components/Form/cypress/assertInitialFormState/assertInitialFormState.d.ts +0 -5
  155. package/e2e/commands/components/Form/cypress/assertInitialFormState/assertInitialFormState.js +0 -20
  156. package/e2e/commands/components/Form/cypress/assertInitialFormState/type.d.ts +0 -105
  157. package/e2e/commands/components/Form/cypress/assertInitialFormState/type.js +0 -1
  158. package/e2e/commands/components/Form/cypress/assertMandatoryFields/assertMandatoryFields.d.ts +0 -17
  159. package/e2e/commands/components/Form/cypress/assertMandatoryFields/assertMandatoryFields.js +0 -14
  160. package/e2e/commands/components/Form/cypress/assertMandatoryFields/type.d.ts +0 -28
  161. package/e2e/commands/components/Form/cypress/assertMandatoryFields/type.js +0 -1
  162. package/e2e/commands/components/Form/cypress/constants.d.ts +0 -3
  163. package/e2e/commands/components/Form/cypress/constants.js +0 -6
  164. package/e2e/commands/components/Form/cypress/fillAndAssertFormFields/fillAndAssertFormFields.d.ts +0 -8
  165. package/e2e/commands/components/Form/cypress/fillAndAssertFormFields/fillAndAssertFormFields.js +0 -29
  166. package/e2e/commands/components/Form/cypress/fillAndAssertFormFields/type.d.ts +0 -5
  167. package/e2e/commands/components/Form/cypress/fillAndAssertFormFields/type.js +0 -1
  168. package/e2e/commands/components/Form/cypress/getFormScope/getFormScope.d.ts +0 -20
  169. package/e2e/commands/components/Form/cypress/getFormScope/getFormScope.js +0 -8
  170. package/e2e/commands/components/Form/cypress/proceedFormStep/proceedFormStep.d.ts +0 -10
  171. package/e2e/commands/components/Form/cypress/proceedFormStep/proceedFormStep.js +0 -6
  172. package/e2e/commands/components/Form/cypress/proceedFormStep/type.d.ts +0 -13
  173. package/e2e/commands/components/Form/helpers/getFieldNameDomElement.d.ts +0 -7
  174. package/e2e/commands/components/Form/helpers/getFieldNameDomElement.js +0 -13
  175. package/e2e/commands/components/Form/types.d.ts +0 -88
  176. package/e2e/commands/components/MenuActions/commands.d.ts +0 -4
  177. package/e2e/commands/components/MenuActions/commands.js +0 -8
  178. package/e2e/commands/components/MenuActions/types.d.ts +0 -24
  179. package/e2e/commands/components/Popover/commands.d.ts +0 -4
  180. package/e2e/commands/components/Popover/commands.js +0 -8
  181. package/e2e/commands/components/Popover/types.d.ts +0 -15
  182. package/e2e/commands/components/PropertyValue/commands.d.ts +0 -4
  183. package/e2e/commands/components/PropertyValue/commands.js +0 -8
  184. package/e2e/commands/components/PropertyValue/types.d.ts +0 -19
  185. package/e2e/commands/components/Tab/commands.d.ts +0 -4
  186. package/e2e/commands/components/Tab/commands.js +0 -9
  187. package/e2e/commands/components/Tab/types.d.ts +0 -29
  188. package/e2e/commands/components/WindowBase/commands.d.ts +0 -4
  189. package/e2e/commands/components/WindowBase/commands.js +0 -24
  190. package/e2e/commands/components/WindowBase/types.d.ts +0 -100
  191. package/e2e/commands/components/index.d.ts +0 -12
  192. package/e2e/commands/components/index.js +0 -1
  193. package/e2e/commands/index.d.ts +0 -7
  194. package/e2e/commands/index.js +0 -1
  195. package/e2e/commands/intercepts/index.d.ts +0 -2
  196. package/e2e/commands/intercepts/index.js +0 -1
  197. package/e2e/commands/intercepts/noPrivileges/interceptCommands.d.ts +0 -5
  198. package/e2e/commands/intercepts/noPrivileges/interceptCommands.js +0 -15
  199. package/e2e/commands/intercepts/noPrivileges/types.d.ts +0 -8
  200. package/e2e/commands/intercepts/noPrivileges/types.js +0 -1
  201. package/e2e/commands/register.d.ts +0 -4
  202. package/e2e/commands/register.js +0 -19
  203. package/e2e/cypress.d.js +0 -1
  204. package/e2e/cypress.d.ts +0 -24
  205. package/e2e/index.d.ts +0 -1
  206. package/e2e/types.d.ts +0 -4
  207. package/index.d.ts +0 -4
  208. package/index.test.d.ts +0 -1
  209. package/testComponents/DataGrid/helpers.d.ts +0 -5
  210. package/testComponents/DataGrid/helpers.js +0 -95
  211. package/testComponents/DataGrid/index.d.ts +0 -2
  212. package/testComponents/DataGrid/index.js +0 -1
  213. package/testComponents/DataGrid/types.d.ts +0 -54
  214. package/testComponents/DataGrid/types.js +0 -1
  215. package/testComponents/DynamicFilter/helpers.d.ts +0 -5
  216. package/testComponents/DynamicFilter/helpers.js +0 -18
  217. package/testComponents/DynamicFilter/index.d.ts +0 -1
  218. package/testComponents/DynamicFilter/index.js +0 -1
  219. package/testComponents/DynamicFilter/types.d.ts +0 -0
  220. package/testComponents/DynamicSort/helpers.d.ts +0 -5
  221. package/testComponents/DynamicSort/helpers.js +0 -16
  222. package/testComponents/DynamicSort/index.d.ts +0 -1
  223. package/testComponents/DynamicSort/index.js +0 -1
  224. package/testComponents/index.d.ts +0 -3
  225. package/testComponents/index.js +0 -1
  226. package/testTools/constants.d.ts +0 -0
  227. package/testTools/index.d.ts +0 -1
  228. package/testTools/index.js +0 -1
  229. package/testTools/mocks.d.ts +0 -0
  230. package/testTools/setup.d.ts +0 -0
  231. package/testTools/setup.js +0 -1
  232. package/testTools/types.d.ts +0 -0
  233. package/testTools/utils.d.ts +0 -11
  234. package/testTools/utils.js +0 -23
  235. package/vite-env.d.ts +0 -5
package/README.md ADDED
@@ -0,0 +1,679 @@
1
+ # Testing Libraries
2
+
3
+ `packages/testing` contains workspace-shared testing helpers that can be consumed from multiple Nx projects without copying the same mock data or helper code into every package.
4
+
5
+ Treat `packages/testing` as the **default home for any testing helper that can reasonably be reused or that prevents future reimplementation**. If a fixture, installer, override pattern, browser/runtime shim, mock builder, or E2E helper could serve another test area with only minor adaptation, promote it into `packages/testing` even when that extraction is more work than keeping the first implementation local.
6
+
7
+ This is the publishable `@m4l/testing` package, targeting version `1.0.1`. Its source-first workspace API and published stable API share the same four subpaths. The `0.x` registry line remains unchanged.
8
+
9
+ Private workspace-only libraries remain under `libs/*`; use their own conventions rather than copying this package's release configuration.
10
+
11
+ ## Build, release and adoption
12
+
13
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:typecheck`
14
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:test`
15
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:test-coverage`
16
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:lint`
17
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:build`
18
+ - `NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false yarn nx run @m4l/testing:verify-package`
19
+
20
+ `verify-package` packs the build without publishing, installs it in a new OS
21
+ temporary directory outside the checkout, checks strict types with the
22
+ `development` condition enabled, and runs Vitest/RTL and Playwright API smoke
23
+ checks using only the installed tarball. There are no workspace aliases or
24
+ links. This is package-consumption verification, not MF host E2E. A second mocks-only installation verifies that React and test-runner peers are
25
+ not pulled in and that the compiled mocks execute independently. The consumers
26
+ are retained for diagnosis; its path and tarball integrity are written to ignored
27
+ `tmp/poc-mf-versioned-consumers/testing-package-verification.json`.
28
+
29
+ The output is `dist/packages/@m4l/testing`. Vite builds the ESM entries and bundles
30
+ the private observability implementation needed by Playwright. The build clears its declaration incremental state after Vite cleans the output,
31
+ then TypeScript emits declarations under `types/`; published export maps contain no `development`
32
+ condition. React, Vitest, Playwright and RTL remain consumer-owned peers, marked
33
+ optional so a mocks-only consumer does not need to install unrelated runners.
34
+ Consumers must install the peers required by their chosen subpath. The library declares compatible peer ranges from the tested versions; the
35
+ workspace and versioned MF consumers keep exact install pins. The dist-manifest
36
+ helper opts into preserving these source peer contracts rather than replacing
37
+ them with exact workspace versions, so unrelated consumers can resolve their
38
+ own compatible types and runners.
39
+
40
+ The root entry provides common React/Vitest/RTL exports and the modern Vitest
41
+ helpers. Version `1.0.1` does not ship the old Cypress `registerCommands` API or
42
+ the old `testColumnConfigs` / `testFilterFields` / `testSortFields` runners from
43
+ `0.0.24`; migrate those usages to the explicit subpaths and `assertColumnConfigs`,
44
+ `assertFilterFields`, `assertSortFields`. Old pinned `0.x` consumers retain their
45
+ published artifacts. Consumers of `latest` or `*` require a compatibility review
46
+ before the registry tag changes.
47
+
48
+ The package lint policy permits only the catalog import in
49
+ `playwright/src/helpers/realtimePerformanceBrowser.ts` to cross the buildable/private
50
+ boundary, because that implementation is bundled. External-consumer fixtures
51
+ may use public self-imports because they run after copying outside the workspace.
52
+ All other imports retain the normal Nx boundary rules.
53
+
54
+ The repository release allowlist and publish target opt this package into the
55
+ existing release machinery. The target version `1.0.1` requires an explicit
56
+ bootstrap/version review against current registry state and reachable tags;
57
+ registration alone does not force Nx Release to choose that version. No package
58
+ or dist-tag is published by the local POC. Verify the packed artifact outside the
59
+ workspace before beta/stable release. Non-Golden MFs adopt a stable exact NPM
60
+ version; if they need a testing API change, develop against source as Golden in
61
+ one MR and return to versioned consumption in another MR after stable publication.
62
+
63
+ ### One-time 1.0.1 bootstrap without Nx Release
64
+
65
+ The initial migration may use direct build/npm/Git commands. This is a one-time
66
+ bootstrap, not a second versioning system. Subsequent versions use the existing
67
+ beta/main Nx Release workflow. Keep the source manifest at `1.0.1` and use the
68
+ configured tag format `release/@m4l/testing/1.0.1`; Nx resolves subsequent versions
69
+ from reachable Git tags, not from an unrelated custom tag.
70
+
71
+ Before a real publication, commit and push the reviewed changes, use a clean
72
+ checkout, and coordinate the initial `main` pipeline so it does not also publish
73
+ this change. The existing `ENABLE_RELEASE=false` CI variable disables automatic
74
+ release jobs for that pipeline; it is pipeline-wide, so do not use it for a merge
75
+ that also needs other package releases. Do not persist that setting for later
76
+ pipelines. The normal scoped `main` release deliberately publishes a new version
77
+ for qualifying branch changes even if a manual publication already exists.
78
+
79
+ From the workspace root, build and verify without invoking the Nx CLI or Nx
80
+ Release (the Vite configuration still uses the workspace Nx path plugin):
81
+
82
+ ```bash
83
+ rtk proxy env NX_NO_CLOUD=true NX_CLOUD_DISTRIBUTED_EXECUTION=false NX_DAEMON=false yarn vite build --config packages/testing/vite.config.ts
84
+ rtk proxy yarn exec rimraf out-tsc/packages/testing/build
85
+ rtk proxy yarn exec tsc -p packages/testing/tsconfig.build.json
86
+ rtk proxy node -e "require('node:fs').copyFileSync('packages/testing/README.md', 'dist/packages/@m4l/testing/README.md')"
87
+ rtk proxy node packages/testing/scripts/verify-package.mjs
88
+ rtk proxy npm publish ./tmp/poc-mf-versioned-consumers/m4l-testing-1.0.1.tgz --dry-run --access public --tag latest --ignore-scripts --registry https://registry.npmjs.org
89
+ ```
90
+
91
+ The verification report records the tarball integrity. Check that `1.0.1` is
92
+ unpublished, the local and remote release tag are absent, and the committed
93
+ manifest and tarball both say `1.0.1`. A dry-run does not establish publish or
94
+ Git push permission. After the beta/quality checks and the one-time publication
95
+ coordination are satisfied, the separate write commands are:
96
+
97
+ ```bash
98
+ rtk proxy npm publish ./tmp/poc-mf-versioned-consumers/m4l-testing-1.0.1.tgz --access public --tag latest --ignore-scripts --registry https://registry.npmjs.org
99
+ rtk proxy npm view @m4l/testing@1.0.1 version dist.integrity --json --registry https://registry.npmjs.org
100
+ rtk proxy git tag -a release/@m4l/testing/1.0.1 HEAD -m "chore(release): publish 1.0.1"
101
+ rtk proxy git push origin refs/tags/release/@m4l/testing/1.0.1
102
+ ```
103
+
104
+ Publish the exact verified tarball and compare the registry integrity with the
105
+ report **before** tagging the unchanged build commit. If publication succeeds
106
+ but tagging/pushing fails, recover only the missing tag step; do not republish,
107
+ overwrite a tag, or increment the version as a workaround. This creates a GitLab
108
+ repository tag; it does not create a separate GitLab Release notes page.
109
+
110
+ Before returning to normal release automation, verify that the tag commit is
111
+ an ancestor of `main` and fetch that tag into future release checkouts. Preserve
112
+ the source manifest/version alignment and the existing beta requirement. The
113
+ next patch from a reachable `1.0.1` baseline is `1.0.2`; feature/breaking commits
114
+ may produce a minor/major version under the normal release rules.
115
+
116
+ ## Terminology
117
+
118
+ - `M4L` is the broader workspace/runtime namespace used by the monorepo packages and applications.
119
+ - `Patronus` refers only to the visual design-system layer: tokens, theme colors, and styling conventions applied on top of components.
120
+ - `Patronus` does **not** imply business logic, runtime behavior, package ownership, or generic application functionality.
121
+ - In shared testing helpers, prefer `M4L` naming for generic runtime or app-level behavior. Reserve `Patronus` wording for documentation that is specifically about the visual design-system surface.
122
+
123
+ ## Exports
124
+
125
+ - `@m4l/testing/rtl`
126
+ Shared React Testing Library helpers for broad app- and MF-level rendering patterns, including reusable DOM runtime mocks such as `ResizeObserver`, `matchMedia`, and browser storage.
127
+ - `@m4l/testing/mocks`
128
+ Shared deterministic constants and helper factories for common testing data such as environment payloads, dictionary data, formatter values, and neutral host-tools scaffolding.
129
+ - `@m4l/testing/vitest`
130
+ Shared Vitest-specific installers and module-factory helpers for repeated setup patterns, such as console-error filters plus reusable M4L app, M4L package, and exact-package module mocks.
131
+ - `@m4l/testing/playwright`
132
+ Shared Playwright E2E helpers for reusable route installers, browser/runtime helpers, deterministic fixture builders, and MF-loading stubs that should not remain owned by a single app E2E project.
133
+
134
+ Current shared Playwright helpers include:
135
+
136
+ The authenticated-preview helpers below currently define the reusable
137
+ host-owned baseline only. They cover authenticated shell bootstrap, sidebar
138
+ launch, and the common authenticated module-dictionary seam that shows up
139
+ immediately after the host mounts a real MF preview bundle. They do not own
140
+ app-specific URL assertions about selected modules. Feature-specific post-mount
141
+ mocks and visible MF-owned assertions stay in the owning MF E2E project. That
142
+ ownership split is now exercised by both `operative_my_gps_devices-e2e` and
143
+ `operative_gps_history_list-e2e`, and the API still stays intentionally narrow
144
+ until a third authenticated MF proves another shared seam.
145
+
146
+ When those helpers touch shell runtime selection or bootstrap overrides, treat
147
+ `apps/gclick/HOST_RUNTIME_STANDARD.md` as the canonical runtime methodology
148
+ guide and update it alongside the helper docs.
149
+
150
+ - `createHostAuthenticatedMfMenuFixture(...)`
151
+ Builds the minimal authenticated sidebar menu tree for one MF entry, including the MF `moduleId`, visible title, preview `debugPort`, and optional `dynamicParams` that should reach the opened MF window.
152
+ - `createHostAuthenticatedMfModuleDictionaryFixture(...)`
153
+ Builds the standard authenticated MF module-dictionary payload used after the host opens a real preview bundle, including the common `module_name` and `module_description` data keys.
154
+ - `createHostMfPreviewDynamicParamsFixture(...)`
155
+ Builds a typed preview-scenario `dynamicParams` payload for authenticated MF launches. It documents the shared `dummyPresetId` and `testCondition` keys while still allowing MF-specific extension keys when a scenario needs more than the common baseline.
156
+ - `installHostAuthenticatedMfPreviewBootstrap(...)`
157
+ Composes the authenticated host-shell bootstrap baseline with one deterministic MF sidebar entry. The high-level installer accepts the MF identity fields directly (`moduleId`, `title`, `debugPort`, optional `iconUrl`, and optional `dynamicParams`) instead of exposing the lower-level menu-fixture shape. The shared baseline now also stubs `GET /users/me/preferences`, so authenticated host-preview suites inherit the same preferences bootstrap seam that the host runtime now requires without re-registering that endpoint in every MF-owned E2E project.
158
+ - `installHostAuthenticatedMfModuleDictionaryRoute(...)`
159
+ Installs the common authenticated MF module-dictionary route used after host-side launch so each MF pilot does not need its own one-line `isAuth: true` wrapper around the lower-level module-dictionary installer.
160
+ - `openHostAuthenticatedMfPreviewModule(...)`
161
+ Installs the authenticated preview bootstrap, optionally lets the caller install MF-local post-mount route mocks, mirrors `dynamicParams.dummyPresetId` into the shell-bootstrap queries `hostRuntimeMode=dummy` and `hostRuntimeDummyPresetId` when one deterministic host runtime is needed, and navigates through the real host sidebar flow. URL expectations about selected modules stay in the owning app or MF E2E contract.
162
+ Keep the default debug-port readiness wait for flows that mount a real MF
163
+ preview bundle. Only set `skipDebugPortReadyWait: true` when the suite
164
+ replaces that remote bundle with `installMicrofrontendRenderStub(...)` and no
165
+ preview origin needs to boot at all. When one MF needs the shared host-open
166
+ flow to prove that its shell-managed window actually emerged before the
167
+ app-local ready wait begins, pass a caller-owned `waitForModuleOpen(...)`
168
+ gate and let the shared helper re-attempt the sidebar click once if that
169
+ gate never materializes.
170
+ - `openHostSidebarModule(...)`
171
+ Opens one host-shell sidebar module by visible title and expands the sidebar first when the menu item is not yet visible. The helper retries with a non-exact accessible-name match when icon text or other leading accessible content makes the exact `menuitem` name longer than the visible label alone.
172
+ It can also accept one caller-owned `waitForModuleOpen(...)` gate plus a
173
+ small `maxClickAttempts` budget so host-owned window emergence is verified
174
+ immediately after the sidebar click instead of being deferred to a later
175
+ app-specific ready wait.
176
+ - `installMicrofrontendRenderStub(...)`
177
+ Installs a deterministic browser-side `render_v2_*` / `unmount_*` MF contract stub that matches the current host runtime shape used by `MFLoader`, so host-shell E2E suites can validate MF opening behavior without downloading a real remote bundle. The stub records render calls immediately, then waits on a deadline for the host-created container before mounting the visible DOM so CI does not depend on one exact animation-frame budget.
178
+ - MF-owned authenticated preview helper files should keep window lookup logic
179
+ anchored on visible shell semantics, not on dev-only selectors. In practice
180
+ that means combining user-visible title text, the host `close` action, and
181
+ one MF-owned control rendered inside the managed window instead of assuming
182
+ `data-testid` or readable CSS class names survive the compiled preview lane.
183
+ - `attachJson(...)`
184
+ Attaches one formatted JSON payload to the current Playwright report entry so
185
+ app E2E projects do not each reinvent the same `testInfo.attach(...)` shape.
186
+ - `formatFailedChecks(...)`
187
+ Converts failed check arrays into one readable multiline assertion message
188
+ without forcing each app E2E project to keep its own string formatter.
189
+ - `readRealtimeMarkerInterpolationState(...)`,
190
+ `hasRealtimeMarkerContexts(...)`, and
191
+ `isRealtimeDeviceSelectedOrDebugPanelVisible(...)`
192
+ Browser-executable helpers for the shared
193
+ `window.__M4L_REALTIME_PERFORMANCE__` marker-interpolation debug state. Use
194
+ them with `page.evaluate(...)` and `page.waitForFunction(...)` instead of
195
+ rewriting the same browser-global access in each app E2E project.
196
+ - `tryWaitForBrowserCondition(...)` and `waitForBrowserCondition(...)`
197
+ Generic Playwright polling helpers that wrap `page.waitForFunction(...)` with
198
+ a reusable boolean-returning path plus a caller-owned timeout error path.
199
+
200
+ ## Internal structure
201
+
202
+ - `package.json`
203
+ Publishable package metadata with source-first development conditions and compiled subpath exports for `./rtl`, `./mocks`, `./vitest`, and `./playwright`.
204
+ - `tsconfig.json`
205
+ Solution/editor config for the testing package.
206
+ - `tsconfig.rtl.json`
207
+ Standalone typecheck/declaration project for `rtl`. It stays isolated from the workspace-wide `@m4l/*` alias graph so the focused internal project does not surface editor-only `rootDir` diagnostics from unrelated packages.
208
+ - `tsconfig.mocks.json`
209
+ Standalone typecheck/declaration project for `mocks`, using the same local-alias isolation as `rtl`.
210
+ - `tsconfig.vitest.json`
211
+ Standalone typecheck/declaration project for `vitest`. It keeps the Vitest-only runtime surface isolated while still allowing the shared installers to consume `@m4l/testing/mocks`.
212
+ - `tsconfig.playwright.json`
213
+ Standalone typecheck/declaration project for `playwright`. It reserves a dedicated shared home for reusable E2E helpers without coupling them to any one app E2E project.
214
+
215
+ The root TypeScript paths resolve all testing subpaths and observability to source.
216
+ The focused configs therefore need no local paths overrides. The aggregate
217
+ `tsconfig.lib.json` checks the runtime helpers; `tsconfig.spec.json` includes tests.
218
+ `tsconfig.build.json` emits build-only declarations with workspace-relative layout
219
+ inside the tarball, including the private observability types required by the
220
+ Playwright helpers. These are compiled declarations, not checkout dependencies.
221
+
222
+ Inside each public subpath, keep `src/index.ts` as a pure barrel only.
223
+ Do not place implementation logic directly in `index.ts`.
224
+ Split implementation into responsibility files such as:
225
+
226
+ - `types.ts`
227
+ - `constants.ts`
228
+ - `fixtures.ts`
229
+ - explicit single-purpose files such as `render.ts` when the subpath exposes one clear resource
230
+ - `helpers/`
231
+ Keep one helper file per helper or helper family, plus a barrel `helpers/index.ts`.
232
+ - `installers/`
233
+ Keep one installer file per installer or installer family, plus a barrel `installers/index.ts`.
234
+ - `factories/`
235
+ Keep one factory file per mocked library or module family, plus a barrel `factories/index.ts`.
236
+ When a factory family has layered composition, prefer explicit subfolders such
237
+ as `factories/base/`, `factories/packages/`, and `factories/app/`, each with
238
+ its own barrel so the hierarchy stays visible in the tree.
239
+
240
+ Avoid catch-all implementation filenames such as `utils.ts`, `helpers.ts`, `factories.ts`, or `installers.ts` once the logic has a clear named responsibility. Reserve those generic names for folders or barrel files only.
241
+
242
+ Consumer test projects should continue to consume `@m4l/testing/*` from source in their test-facing TypeScript config (`tsconfig.spec.json`, Playwright `tsconfig.json`, or equivalent) by widening `rootDir` and including the shared source globs they need. This keeps test iteration fast and avoids forcing prebuilt declaration output every time a shared testing helper export changes.
243
+ When a shared testing subpath starts shipping `.tsx` implementation files, those source-based consumer includes should cover both `**/*.ts` and `**/*.tsx` for that subpath so renames like `m4lBaseComponents.ts` → `m4lBaseComponents.tsx` do not trigger `TS6307` in downstream composite spec programs.
244
+ If a Playwright E2E project includes `packages/testing/playwright/src/**/*`, it
245
+ must also include `libs/observability/mf/src/**/*` and
246
+ `libs/observability/shared/src/**/*` in the same `tsconfig.json` because the
247
+ shared browser/runtime helpers now depend on the source-owned
248
+ `@m4l/observability/mf` catalog and its neutral
249
+ `@m4l/observability/shared` contracts. Keep those sources together; do not
250
+ switch that consumer seam to project references or `dist` mappings just to
251
+ suppress `TS6307`.
252
+ The internal `packages/testing` `tsconfig.vitest.json` and
253
+ `tsconfig.playwright.json` follow that same rule by widening `rootDir` and
254
+ including `libs/observability/mf/src/**/*` plus
255
+ `libs/observability/shared/src/**/*` directly instead of referencing
256
+ `../observability/tsconfig.mf.json` or relying on `out-tsc`.
257
+
258
+ Current shared runtime helpers in `@m4l/testing/rtl` include:
259
+
260
+ - `render(...)`
261
+ Shared React Testing Library render helper with automatic `cleanup()` and `vi.clearAllMocks()` after each test plus a ready-to-use `userEvent` instance.
262
+ - `installMatchMediaMock(...)`
263
+ Shared `window.matchMedia` installer for package and app setup files that need a browser-like media-query API without duplicating the same global shim in every project.
264
+ - `installStorageMock(...)`
265
+ Installs reusable `localStorage` / `sessionStorage` shims plus a shared `Storage` constructor for package and app setup files that need deterministic browser storage without keeping one app-local memory-storage class per project.
266
+ - `installResizeObserverMock(...)`
267
+ Installs a reusable `ResizeObserver` test double on `globalThis` / `window`, with manual `trigger()` support and optional auto-trigger behavior on `observe(...)`.
268
+ - `createResizeObserverEntry(...)`
269
+ Builds deterministic `ResizeObserverEntry` payloads for width/height-driven test scenarios.
270
+
271
+ Current shared data helpers in `@m4l/testing/mocks` include:
272
+
273
+ - `environmentFixture`, `useEnvironmentValueFixture`, `formattersFixture`, and related `*Fixture` constants
274
+ Shared deterministic baselines for tests that can reuse the standard data shape without per-test construction.
275
+ - `createEnvironmentFixture(...)`
276
+ Returns a full `EnvironmentType`-compatible fixture by merging explicit overrides on top of the shared baseline fixture.
277
+ - `createUseEnvironmentValueFixture(...)`
278
+ Returns a lighter `useEnvironment()`-style fixture for tests that only need the commonly consumed environment fields.
279
+ - `createUseModuleDictionaryValueFixture(...)`
280
+ Returns a lightweight `useModuleDictionary()` value with predictable `getLabel(...)` and `getModuleLabel()` behavior, plus optional keyed-label overrides for tests that only need stable dictionary resolution without rebuilding inline hook payloads.
281
+ - `createUserPreferencesFixture(...)`, `createUserPreferencesApiDataFixture(...)`, `createUserPreferencesMagnitudePropertyFixture(...)`, and `createUserPreferencesContextValueFixture(...)`
282
+ Build reusable authenticated user-preferences fixtures for tests that now depend on locale, number delimiters, date/time formats, time zone, or magnitude conversions. `createUserPreferencesContextValueFixture(...)` mirrors the `useUserPreferences()` runtime contract closely enough to exercise `changeUserPreferences(...)`, `formatNumber(...)`, `getMagnitudeProperty(...)`, and `convertMagnitude(...)` without importing the real app context.
283
+ - `createHostToolsFixture(...)`
284
+ Builds a neutral host-tools fixture with a `BrowserHistory`-compatible in-memory history baseline plus no-op callbacks, and lets each test override only the members it cares about.
285
+ - `createToasterFixture(...)`
286
+ Builds a deterministic toast stub for tests that only need a valid toaster shape and stable return ids.
287
+
288
+ Current shared Vitest helpers in `@m4l/testing/vitest` include:
289
+
290
+ - `installConsoleErrorFilter(...)`
291
+ Reusable console-error filter for noisy test-runner warnings that should be muted consistently across packages.
292
+ - `installRhfOnSubmitRef(...)`
293
+ Shared global ref installer for app tests that need a stable `RHFormProvider` submit bridge.
294
+ - `createM4lAppBaseModuleMock(...)`
295
+ - `createM4lPackageBaseModuleMock(...)`
296
+ - `createM4lBaseComponentsModuleMock(...)`
297
+ - `createM4lAppComponentsModuleMock(...)`
298
+ - `createM4lAppFormComponentsModuleOverrides(...)`
299
+ - `createM4lAppFormLayoutsModuleOverrides(...)`
300
+ - `createM4lAppCoreModuleMock(...)`
301
+ - `createM4lPackageGclickModuleMock(...)`
302
+ - `createM4lPackageGclickRealtimeModuleMock(...)`
303
+ - `createM4lAppGraphicsModuleMock(...)`
304
+ - `createM4lAppLayoutsModuleMock(...)`
305
+ - `createM4lPackageComponentsModuleMock(...)`
306
+ - `createM4lPackageCoreModuleMock(...)`
307
+ - `createM4lPackageGraphicsModuleMock(...)`
308
+ - `createM4lPackageLayoutsModuleMock(...)`
309
+ - `createPaperFormComponentMock(...)`
310
+ - `createChildrenContainerComponentMock(...)`
311
+ - `createNamedFieldComponentMock(...)`
312
+ - `createPropertyValueComponentMock(...)`
313
+ - `createIconComponentMock(...)`
314
+ - `createDividerComponentMock(...)`
315
+ - `createNavLinkComponentMock(...)`
316
+ - `createWindowBaseComponentMock(...)`
317
+ - `createHelmetPageComponentMock(...)`
318
+ - `createActionButtonComponentMock(...)`
319
+ - `createSuspenseLoadableComponentMock(...)`
320
+ - `createLeafletModuleMock(...)`
321
+ - `createReactHookFormModuleMock(...)`
322
+ - `createReResizableModuleMock(...)`
323
+ - `createReactLeafletModuleMock(...)`
324
+ Reusable module factories for M4L component consumers plus app and package `test/setup.ts` files so packages, hosts, and MFs can keep thin local adapters instead of repeating the same large `vi.mock(...)` payloads. When a consumer needs untouched real exports too, pass `baseModule` populated from `importOriginal(...)` and let the shared factory override only the test-facing surface.
325
+ `createM4lAppFormComponentsModuleOverrides(...)` provides a reusable override payload for app/MF `@m4l/components` setups that need the dynamic-MF parameters store plus neutral form container/action placeholders (`FormContainer`, `ActionsContainer`, `ActionFormCancel`, `ActionFormIntro`, `RHFAutocompleteAsync`, and `RHFRadioGroup`) without copying the same local override object across app test setups.
326
+ `createM4lAppFormLayoutsModuleOverrides(...)` provides a reusable override payload for app/MF `@m4l/layouts` setups that need neutral paper-form seams (`useDynamicPaperForm`, `useFormAddEdit`) and a configurable `createComponentField(...)` identity baseline without repeating the same setup-local overrides.
327
+ `createReactHookFormModuleMock(...)` now accepts `useFormContextData` so tests can inject package-local `setValue`, `reset`, `formState`, and related RHF behavior without rebuilding the full `react-hook-form` contract in each file.
328
+ `createLeafletModuleMock(...)` now covers the repeated partial `leaflet` seams that showed up during the `GpsMap` normalization work: configurable `L.control.scale()` and `L.DomEvent.disableClickPropagation()` / `disableScrollPropagation()` behavior, while still preserving real exports through `baseModule` when the consumer needs the untouched runtime surface.
329
+ `createReactLeafletModuleMock(...)` now covers neutral `Circle`, `GeoJSON`, `Polygon`, `Polyline`, `LayerGroup`, `Marker`, `Popup`, and configurable `useMap()` behavior. Use `useMapData` when the test can keep one stable map object for the whole file, or `useMapImplementation` when the test needs `useMap()` to read mutable per-test state such as a late-bound fake map ref. When a test still needs real exports such as `MapContainer`, keep the mock partial by passing `baseModule` from `importOriginal(...)` and override only the shape components or `useMap()` contract that belong to the scenario.
330
+ `createSuspenseLoadableComponentMock(...)` complements that baseline for per-test overrides: use it with `vi.mocked(Loadable).mockImplementation(...)` when a test needs lazy-route rendering through the existing app setup mock instead of re-declaring `@m4l/components` locally.
331
+ `createM4lAppBaseModuleMock(...)` is the app-level baseline for direct `@m4l/base` consumers in host and MF setups. It shares the same neutral `Icon`, `Tooltip`, and `Typography` placeholder contract as the package-level baseline while giving app `test/setup.ts` files the same naming convention used by `createM4lAppCoreModuleMock(...)` and `createM4lAppComponentsModuleMock(...)`.
332
+ `createM4lPackageBaseModuleMock(...)` is the dedicated package-level baseline for direct `@m4l/base` consumers. It keeps `Icon`, `Tooltip`, and `Typography` on a neutral primitive placeholder surface while `baseModule` can still preserve the real constants, class keys, and helper exports when the scenario needs them.
333
+ `createM4lBaseComponentsModuleMock(...)` is the shared base layer for `@m4l/components`: it covers reusable component-system primitives, form placeholders, DataGrid-friendly stubs, and passthrough spies for utility helpers such as validation builders when the real implementation should stay observable by default. When this README mentions the design-system layer behind those primitives, that is the narrow context where the term `Patronus` applies.
334
+ That same base layer now preserves `className`, `disabled`, and click handling on the shared `IconButton` placeholder so package and app tests can assert stable button-state behavior without rebuilding a local `@m4l/components` partial mock.
335
+ The shared button-like placeholders (`Button`, `IconButton`, and `Tab`) now
336
+ use explicit button-prop interfaces instead of `UnknownRecord` plus unsafe
337
+ handler casts, so the shared mock surface stays closer to the real React
338
+ button contract and consumer typechecks can catch drift earlier.
339
+ RHF placeholder components exposed by that base layer use explicit `rhf-*`
340
+ test ids such as `rhf-field-name`, `rhf-select-countryId`,
341
+ `rhf-number-timeMinInSeconds`, `rhf-helper-geometry`,
342
+ `rhf-radio-group-assignmentType`, and `rhf-checkable-list-devices` so tests
343
+ can distinguish shared React Hook Form placeholders from other mock surfaces.
344
+ That base layer also exposes typography helpers such as `getTypographyStyles()` so app and package tests can execute style maps without rebuilding another local `@m4l/components` partial mock.
345
+ The shared M4L app factories now also cover neutral platform-shell and MF-loading collaborators that repeatedly show up in app tests, including provider passthrough components such as `ModuleDictionaryProvider`, `ModuleSkeletonProvider`, `DomainCountryProvider`, `FormatterProvider`, `ModalProvider`, `PopupsProvider`, `AreasProvider`, `FlagsProvider`, `EnvironmentProvider`, `HostToolsProvider`, `NetworkProvider`, `AuthProvider`, `LocalesProvider`, `ThemeSettingsProvider`, `DeviceTypeProvider`, and `HostThemeProvider`, plus shell primitives such as `SideBar`, `AppBar`, `HelmetPage`, `AreasAdmin`, `LanguagePopover`, `AccountPopover`, `MFNoAuthApp`, `MFLoader`, `createDynamicMFStore(...)`, `createToaster(...)`, `getMFLoaderComponentsDictionary()`, `getLoadingErrorComponentsDictionary()`, and locale/domain hooks such as `useLocales()`, `useDomainCountry()`, and `getLocaleFromNetwork()`.
346
+ The shared base `@m4l/components` layer now also owns reusable view primitives such as `Divider`, `NavLink`, and `WindowBase`, plus focused placeholder helpers such as `createDividerComponentMock(...)`, `createNavLinkComponentMock(...)`, and `createWindowBaseComponentMock(...)` so app tests can stay on top of the setup-owned baseline instead of rebuilding local workspace-library mocks.
347
+ `createM4lPackageGclickModuleMock(...)` remains intentionally specific to the `@m4l/gclick` package and covers map-store, indicator, and GNSS enum behavior that only makes sense when mocking that exact runtime package.
348
+ The shared app-components baseline now exposes a minimal but stable
349
+ `WINDOW_SIZES.square` contract with both `medium` and `large` entries so app
350
+ tests can open modal windows without each suite rebuilding the same shape
351
+ locally. Consumers that need additional sizes such as `small`, `vertical`,
352
+ or `horizontal` can still extend that baseline in their local `test/setup.ts`
353
+ overrides.
354
+ `createM4lPackageGclickRealtimeModuleMock(...)` is the matching package-safe baseline for direct `@m4l/gclick-realtime` consumers. Use `surface: 'payload'` when the test only imports `@m4l/gclick-realtime/payload`, `surface: 'contracts'` for `@m4l/gclick-realtime/contracts`, and the default root surface when a direct `@m4l/gclick-realtime` mock should expose both contracts and payload guards.
355
+ The package-safe M4L factories intentionally stay narrower: they cover direct `@m4l/base` primitive placeholders through `createM4lPackageBaseModuleMock(...)`, compose on top of `createM4lBaseComponentsModuleMock(...)` where appropriate, and add only reusable package-test collaborators such as `useEnvironment()`, `useModuleDictionary()`, `useHostTools()`, `useNetwork()`, `useModuleSkeleton()`, `useLocalStorageWithListener()`, `useWindowToolsMF()`, package-safe popup scaffolding through `PopupsProvider`, popup/modal hooks, lightweight DataGrid helpers, package-safe `@m4l/layouts` contracts such as `DynamicTabs`, `useModule()`, and `useFormAddEdit()`, plus package-safe `@m4l/graphics` contracts such as `usePaginate()`, `useRows()`, and `getContrastTextColor()` without pulling app-shell collaborators into every library test suite.
356
+ The option exports that do not add extra fields, such as `CreateM4lAppGraphicsModuleMockOptions` and `CreateM4lAppLayoutsModuleMockOptions`, intentionally stay as direct type aliases of `CreateVitestModuleMockOptions` so the public helper API preserves the same call shape without carrying lint-only empty interfaces.
357
+ Legacy `*Microfrontend*` alias exports were removed from `@m4l/testing/vitest`; use the `*M4lApp*` names for generic app baselines, the `*M4lPackage*` names for generic library-package baselines, and `*Gclick*` only inside names that point to the exact `@m4l/gclick` runtime package.
358
+ The layered factories now live under `vitest/src/factories/base/`,
359
+ `vitest/src/factories/packages/`, and `vitest/src/factories/app/`. Treat the
360
+ folder name as part of the documentation: `base` defines the reusable
361
+ foundation, `packages` defines package-test composition, and `app` defines
362
+ app and MF composition.
363
+ For MF apps, prefer one real local `test/setup.ts` that installs the shared
364
+ baseline from `@m4l/testing` plus one MF-owned adapter file such as
365
+ `test/testing.ts` for mutable scenario state. That split keeps the global
366
+ setup stable while feature tests only override the small seams they truly
367
+ own, instead of rebuilding full `vi.mock(...)` payloads in every file.
368
+ Factory implementation files import `vi` explicitly from `vitest` instead of
369
+ relying on runner globals so the shared helper source stays readable and
370
+ typecheck-friendly even outside direct test-file context.
371
+ React-rendering factory implementation files now use `.tsx` plus JSX instead
372
+ of dense `React.createElement(...)` payloads so the shared mock surface stays
373
+ easier to read and maintain as it grows.
374
+ Shared factory families that mock publishable packages should also ship local
375
+ contract tests next to the factory implementation. In `packages/testing/vitest`,
376
+ keep those checks as colocated `__tests__/*.contract.test.ts` files plus any
377
+ matching `*.types.contract.ts` probes that can compile inside the internal
378
+ library program.
379
+ When a contract test needs to compare a shared mock against the real package
380
+ export surface, prefer `await vi.importActual<Record<string, unknown>>()`
381
+ inside the test file runtime path, such as an `it(...)` block or a shared
382
+ `beforeAll(...)` preload, instead of a top-level static import. For heavier
383
+ packages, preload the real module once in `beforeAll(...)` so contract tests
384
+ stay deterministic under full-suite load. That keeps the runtime drift check
385
+ real while avoiding `packages/testing` typecheck cycles or source-program
386
+ expansion into the consumer package.
387
+ Use runtime contract tests for real package-boundary drift detection
388
+ (existence and coarse export kind), and keep `*.types.contract.ts` probes
389
+ focused on the shared mock prop interfaces that belong to `packages/testing`
390
+ itself. Do not force `packages/testing` TypeScript programs to import full
391
+ consumer-package source trees just to compare public types.
392
+
393
+ ### Column, filter, and sort assertion helpers
394
+
395
+ - `assertColumnConfigs(columns, configs)`
396
+ Verifies a DataGrid column array against a typed config spec and returns
397
+ `string[]` of violations. Use this when a hook or helper builds DataGrid
398
+ columns and the test should own the `describe` / `it` structure while still
399
+ getting readable drift messages for column props, formatter helpers, and
400
+ supported hook-call metadata.
401
+ - `assertFilterFields(actualFields, expectedFields)`
402
+ Verifies a DynamicFilter field array and returns `string[]` of violations.
403
+ It checks the standard field properties plus `selectAsyncOptions` when the
404
+ field type is `selectAsync`.
405
+ - `assertSortFields(actualFields, expectedFields)`
406
+ Verifies a DynamicSort field array and returns `string[]` of violations for
407
+ structural drift such as `name`, `dictionaryId`, `type`, `presence`, and
408
+ optional `urlIcon`.
409
+
410
+ Use these helpers instead of manually asserting each field property inline.
411
+
412
+ ### Path for consumers outside this Nx workspace
413
+
414
+ `@m4l/testing` is still a private internal workspace library. For MF repos or
415
+ other consumers that live outside this Nx monorepo, the migration path is
416
+ explicitly one of these two options:
417
+
418
+ 1. publish `@m4l/testing` (or a narrowed public subpackage) so the external
419
+ consumer can import `assertColumnConfigs(...)`,
420
+ `assertFilterFields(...)`, and `assertSortFields(...)` directly
421
+ 2. copy the shared helper files into the external consumer's own local testing
422
+ helpers and treat them as repo-owned utilities there
423
+
424
+ Do not point non-Nx consumers at `packages/testing/old` anymore. Inside this
425
+ monorepo, `@m4l/testing/vitest` is now the canonical assertion-helper path.
426
+
427
+ ## Nx configuration note
428
+
429
+ `packages/testing` owns its Vitest contract explicitly:
430
+
431
+ - `project.json` defines the `test`, `test-ci`, and `test-coverage` targets
432
+ - `vitest.config.ts` stays unconditional because those targets execute it
433
+ directly
434
+ - the workspace does not register the `@nx/vitest` inference plugin in
435
+ `nx.json`, so this library does not need extra graph-time exclude wiring
436
+
437
+ That is the repository-standard **project-owned** Vitest mode for a library
438
+ that already declares explicit test targets.
439
+
440
+ ### Direct validation for `@m4l/testing/vitest`
441
+
442
+ `packages/testing/vitest` now has its own direct Vitest config and Nx test target.
443
+ Use that direct target whenever you change shared Vitest factories, helpers, or
444
+ installers.
445
+
446
+ - Main gate:
447
+
448
+ ```sh
449
+ nx test @m4l/testing
450
+ ```
451
+
452
+ This runs `packages/testing/vitest.config.ts` without coverage so local iteration
453
+ stays fast and the terminal output stays quieter during normal development.
454
+
455
+ - CI-equivalent target:
456
+
457
+ ```sh
458
+ nx run @m4l/testing:test-ci
459
+ ```
460
+
461
+ This runs the same config with coverage enabled and enforces
462
+ `100/100/100/100` thresholds for statements, branches, functions, and lines.
463
+ The coverage gate intentionally includes invalid-input guards for helpers
464
+ such as `assertFilterFields(...)` and `assertSortFields(...)`, plus the
465
+ exhaustive unsupported-hook fallback path inside `assertColumnConfigs(...)`,
466
+ so regressions fail at the shared-helper boundary instead of slipping through
467
+ as "unreachable" branches.
468
+
469
+ - Explicit local coverage target:
470
+
471
+ ```sh
472
+ nx run @m4l/testing:test-coverage
473
+ ```
474
+
475
+ Use this when you want the same direct coverage gate locally without using
476
+ the CI-named target.
477
+
478
+ - Direct runner command when Nx indirection is not needed:
479
+
480
+ ```sh
481
+ yarn vitest --config packages/testing/vitest.config.ts --run --coverage
482
+ ```
483
+
484
+ Use the direct `@m4l/testing` gate before validating downstream consumers such
485
+ as `@m4l/gclick`, `@m4l/gclick_host`, or `operative_gps_history_list`, because
486
+ shared-helper regressions now fail at the source library boundary instead of
487
+ waiting for a consumer suite to surface them.
488
+
489
+ ## Baseline mock contract
490
+
491
+ When a package, app, or MF installs a shared module baseline in `test/setup.ts`, treat that baseline as the default testing contract for the suite.
492
+
493
+ - Use shared factories such as `createM4lPackageComponentsModuleMock(...)`, `createM4lPackageCoreModuleMock(...)`, or the generic app-level variants to install the reusable module baseline once in `test/setup.ts`.
494
+ - Keep those factories narrow and curated: they should override only the reusable test-facing surface, while untouched exports stay real through `baseModule: await importOriginal(...)`.
495
+ - In individual test files, prefer `vi.mocked(...)`, `mockReturnValue(...)`, and `mockImplementation(...)` to adjust the behavior of the already-installed baseline mocks for the scenario under test.
496
+ - Do not re-declare another full `vi.mock('@m4l/components'...)` or `vi.mock('@m4l/core'...)` factory in each test file when the setup baseline already covers that module.
497
+ - `vi.mocked(...)` changes the behavior of the installed mock. It does **not** restore the original implementation of an export that `test/setup.ts` already replaced.
498
+ - If a test truly needs the original implementation of one export that the setup baseline overrides, use a local partial mock with `importOriginal(...)` / `vi.importActual(...)`, or change the shared factory so that export stays as passthrough by default.
499
+ - Keep local `vi.mock(...)` blocks for feature-only collaborators, sibling modules, or external dependencies whose behavior is specific to one subject and does not belong in the shared baseline.
500
+ - When a local `react-leaflet` mock is still needed, it should usually start from `createReactLeafletModuleMock(...)` and keep only the feature-specific pieces local, such as a ref-aware `Marker`, a late-bound `useMapImplementation`, or plugin-specific Leaflet state.
501
+ - When a local `leaflet` mock is still needed, it should usually start from `createLeafletModuleMock(...)` and keep only plugin-specific behavior local, such as `geoJSON()`, Geoman state, icon factories, or feature-only Leaflet helpers that are not yet reused elsewhere.
502
+ - Document the residual local seams after each migration slice. A local mock is acceptable when it encodes feature-only runtime behavior, but the owning package or app README should record why that seam stays local so later cleanups do not re-open the same extraction debate without new evidence.
503
+
504
+ Current shared Playwright policy in `@m4l/testing/playwright` is:
505
+
506
+ - treat it as the canonical home for reusable Playwright E2E helpers across apps
507
+ - extract cross-app route installers, auth/bootstrap mocks, MF bundle stubs, popup/window helpers, and deterministic E2E fixture builders there
508
+ - keep the initial scaffold intentionally small until the first app migrations promote their first shared helpers
509
+ - keep Playwright artifact ownership standardized at the workspace root under
510
+ `out-e2e/{project-name}/`
511
+ - resolve HTML/blob/output report folders from the workspace root inside
512
+ `playwright.config.ts` itself instead of relying on project-relative report
513
+ paths that only work from one `cwd`
514
+ - when an app or MF exposes a `show-report` Nx target, point it at the
515
+ workspace-root `out-e2e/{project-name}/report` path and forward extra args
516
+ so alternate `--host` / `--port` values remain easy when the default
517
+ Playwright port is already occupied
518
+
519
+ ### Local-first ownership and promotion timing
520
+
521
+ Use this rule before extracting another Playwright helper into
522
+ `@m4l/testing/playwright`:
523
+
524
+ - keep the helper local while it still depends on one app's selectors,
525
+ thresholds, scenario semantics, or assertion vocabulary
526
+ - keep topology adapters local while they still describe one app's host-vs-
527
+ standalone browser choreography
528
+ - keep contract-depth concepts such as `minimum` and `deep` local to the owning
529
+ E2E project; they are test-contract categories, not shared library concerns
530
+ - promote the helper once it already has a second consumer or clearly models a
531
+ shared shell/runtime seam without carrying app-specific DOM assumptions
532
+ - do not extract a helper early if the only way to share it is to fill the API
533
+ with app-specific branching such as `if host`, `if standalone`, or
534
+ per-module selectors
535
+
536
+ The practical bar is:
537
+
538
+ - first consumer: local is the default
539
+ - second real consumer or clean shared shell seam: promote to shared
540
+
541
+ Current shared Playwright helpers in `@m4l/testing/playwright` include:
542
+
543
+ - `createHostAuthenticatedSessionFixture(...)`
544
+ - `createHostAreaFixture(...)`
545
+ - `createHostCookieFixture(...)`
546
+ - `createHostUiConfigFixture(...)`
547
+ - `createHostLocaleFixture(...)`
548
+ - `createHostGuestSessionFixture(...)`
549
+ - `createHostModuleDictionaryFixture(...)`
550
+ Deterministic host-bootstrap payload builders that let app E2E projects keep only feature-specific labels or overrides local.
551
+ - `installHostAuthenticatedSessionRoute(...)`
552
+ - `installHostAreasRoute(...)`
553
+ - `installHostCookiesRoute(...)`
554
+ - `installHostMenusRoute(...)`
555
+ - `installHostAuthenticatedBootstrapRoutes(...)`
556
+ - `installHostUiConfigRoute(...)`
557
+ - `installHostUiConfigAssetRoutes(...)`
558
+ - `installHostGuestSessionRoute(...)`
559
+ - `installHostLocaleRoute(...)`
560
+ - `installHostModuleDictionaryRoute(...)`
561
+ - `installHostGuestBootstrapRoutes(...)`
562
+ - `installHostPasswordResetRoute(...)`
563
+ - `installHostPasswordNewRoute(...)`
564
+ Reusable host guest-bootstrap, authenticated-shell, and password-submit route installers that virtualize shared host contracts without pulling app-specific selectors into the shared library. The password installers intercept the current `POST */api/v1/*/auth/password/reset` and `POST */api/v1/*/auth/password/new` backend contracts; these API paths are intentionally distinct from the canonical `/password/new` host browser route. The new-password fixture mirrors the backend's `201` response with `{ "data": "ok" }`. The retired `/auth/password_new` path follows the host wildcard fallback instead of rendering the password form. `installHostUiConfigRoute(...)` now also supports an explicit `responseGate` promise so E2E suites can keep the guest shell blocked deterministically until `ui-config` resolves instead of depending on fixed timing windows. Prefer that gate-first approach whenever the browser contract is "the shell stays blocked until bootstrap finishes", keep exact transient spinner-node assertions closer to the owning unit or integration tests unless the browser-visible node itself is the contract under test, and pair these installers with single-worker Playwright lanes plus unique per-project ports when multiple E2E apps share one machine.
565
+ - `installMicrofrontendRenderStub(...)`
566
+ Reusable browser-side MF render/unmount contract stub for host-shell E2E coverage that needs to validate menu-to-window ownership without downloading a real remote bundle. It tracks mount successes/failures in the browser-side stub state for post-failure diagnostics.
567
+
568
+ ## Decision guide
569
+
570
+ Use this guide before adding a new shared helper or choosing one of the existing subpaths.
571
+
572
+ ### Mandatory reuse policy
573
+
574
+ - Start by assuming a new testing helper belongs in `packages/testing`.
575
+ - Keep a helper local only when it is tightly coupled to one feature's DOM structure, one component family's business semantics, or one app-specific assertion vocabulary and would add noise to the shared library.
576
+ - "Could another test or app benefit from this without carrying feature-only assumptions?" is the deciding question. If the answer is yes, move it to `packages/testing`.
577
+ - Choose the shared-library path even when the first extraction is harder. The repository standard values future reuse over short-term local convenience.
578
+
579
+ ### Choose the subpath by context
580
+
581
+ - Need deterministic data, neutral object shapes, or pure fixture builders with no Vitest-only behavior:
582
+ Use `@m4l/testing/mocks`.
583
+ - Need React Testing Library lifecycle setup or browser-global runtime shims such as `ResizeObserver`:
584
+ Use `@m4l/testing/rtl`.
585
+ - Need a package-wide `matchMedia` shim in `test/setup.ts`:
586
+ Start with `installMatchMediaMock(...)` from `@m4l/testing/rtl` instead of defining another local `Object.defineProperty(window, 'matchMedia', ...)` block.
587
+ - Need `vi.fn()`, `vi.mock(...)` payloads, runner-global setup, or app `test/setup.ts` installers:
588
+ Use `@m4l/testing/vitest`.
589
+ - Need Playwright route interception installers, reusable auth/bootstrap E2E mocks, browser/runtime helpers, or MF-loader stubs that can serve more than one app:
590
+ Use `@m4l/testing/playwright`.
591
+ - Need something that only makes sense for one package, feature, or component area and would add noise to the shared testing library:
592
+ Keep it local in `src/test/`, `test/`, or `__testing__/`.
593
+
594
+ ### Choose the helper family by need
595
+
596
+ - Static `*Fixture` constants:
597
+ Use them when the shared baseline already matches the scenario and the test does not need custom construction.
598
+ - `create*Fixture(...)` builders:
599
+ Use them when most of the shape is shared but a few fields change per suite or per test.
600
+ - `install*...(...)` installers:
601
+ Use them from setup files when a helper must patch `globalThis`, `window`, or another process-wide testing hook.
602
+ - `create*ModuleMock(...)` factories:
603
+ Use them to build the body of a shared `vi.mock(...)` payload, while keeping the final `vi.mock(...)` registration local to the consumer setup file.
604
+
605
+ ### Common decisions
606
+
607
+ - Need a full provider-compatible environment object:
608
+ Start with `environmentFixture` or `createEnvironmentFixture(...)`.
609
+ - Need to mock `useEnvironment()` directly:
610
+ Start with `useEnvironmentValueFixture` or `createUseEnvironmentValueFixture(...)`.
611
+ - Need to override `useModuleDictionary()` in a focused test without re-declaring `@m4l/core`:
612
+ Start with `createUseModuleDictionaryValueFixture(...)`, then install it through `vi.mocked(useModuleDictionary).mockReturnValue(...)` on top of the setup-owned baseline.
613
+ - Need neutral host tools or toast methods:
614
+ Start with `createHostToolsFixture(...)` and `createToasterFixture(...)`.
615
+ - Need a shared M4L app `test/setup.ts` baseline for host or MF:
616
+ Build the shared module payload with `@m4l/testing/vitest`, keep only app-specific overrides in the local setup file, and use `baseModule` when the setup must preserve untouched real exports from `importOriginal(...)`. Use `createM4lAppBaseModuleMock(...)` for direct `@m4l/base` imports, `createM4lAppCoreModuleMock(...)` for `@m4l/core`, and the matching app-level factory for components, layouts, or graphics.
617
+ - Need a shared M4L package `test/setup.ts` baseline for library component tests:
618
+ Start with `createM4lPackageCoreModuleMock(...)` and `createM4lPackageComponentsModuleMock(...)`, which already compose the shared `createM4lBaseComponentsModuleMock(...)` layer. Use `createM4lPackageBaseModuleMock(...)` when package tests import `@m4l/base` primitives directly. Keep the local package setup thin, and override per-scenario behavior through `vi.mocked(...)` instead of re-declaring the whole module factory in each test file.
619
+ - Need a reusable local `vi.mock('@m4l/layouts'...)` payload for package tests without adopting the broader app-shell baseline:
620
+ Start with `createM4lPackageLayoutsModuleMock(...)`, then override only the exact collaborator needed for the scenario through `moduleOverrides`.
621
+ - Need a reusable local `vi.mock('@m4l/graphics'...)` payload for package tests without adopting the broader app-shell baseline:
622
+ Start with `createM4lPackageGraphicsModuleMock(...)`, then override only the exact collaborator needed for the scenario through `moduleOverrides`.
623
+ - Need a reusable local `vi.mock('react-hook-form'...)` payload with a custom `useFormContext()` shape:
624
+ Start with `createReactHookFormModuleMock(...)`, then inject the scenario-specific `setValue`, `reset`, `formState`, or other RHF members through `useFormContextData`.
625
+ - Need a reusable local `vi.mock('react-leaflet'...)` payload for package tests:
626
+ Start with `createReactLeafletModuleMock(...)`. Use `moduleOverrides` for shape placeholders such as `Circle`, `GeoJSON`, `Polygon`, or `Polyline`, and pass `baseModule` from `importOriginal(...)` when the test still needs untouched real exports such as `MapContainer`.
627
+ - Need the real implementation of one export that the shared setup baseline already overrides:
628
+ Do not expect `vi.mocked(...)` to restore it. Use a local partial mock with `importOriginal(...)` / `vi.importActual(...)`, or keep that export as passthrough in the shared factory by default.
629
+ - Need reusable DOM placeholders for library component tests:
630
+ Start with `createPaperFormComponentMock(...)`, `createChildrenContainerComponentMock(...)`, `createNamedFieldComponentMock(...)`, `createPropertyValueComponentMock(...)`, `createIconComponentMock(...)`, and `createActionButtonComponentMock(...)` from `@m4l/testing/vitest` before creating another feature-local JSX placeholder helper.
631
+ When the placeholder represents an RHF-owned control installed through `createM4lBaseComponentsModuleMock(...)`, assert the explicit `rhf-*` test ids emitted by the shared baseline instead of reintroducing legacy ids such as `field-*`, `select-*`, or `checkbox-*`.
632
+ - Need a test-specific override for a library that the app already mocks in `test/setup.ts`:
633
+ Prefer `vi.mocked(...)` on the existing mock first. If the override shape becomes reusable across files or apps, promote the override helper into `@m4l/testing/vitest` instead of adding another local `vi.mock(...importOriginal)` block.
634
+ - Need to decide whether a remaining local mock should stay local:
635
+ Ask whether it still encodes feature-specific orchestration or a shared contract gap. Keep it local when it owns mutable plugin/runtime behavior such as Geoman state, `leaflet.geoJSON()`, or a one-off integration seam under active review; otherwise promote the reusable portion into `@m4l/testing`.
636
+ - Need reusable Playwright route mocking for host-style bootstrap, auth, or MF loading:
637
+ Start in `@m4l/testing/playwright` when the helper can be consumed by more than one app; keep only app-specific selectors and flows local.
638
+ - Need a package-local wrapper, feature-specific provider composition, or assertions tied to one feature and not reusable without carrying that feature's semantics:
639
+ Do not move it into `packages/testing`.
640
+
641
+ ## Reuse-first expectation
642
+
643
+ - Before creating any new testing helper, inspect `@m4l/testing/rtl`, `@m4l/testing/mocks`, `@m4l/testing/vitest`, and `@m4l/testing/playwright` first.
644
+ - If the helper can reasonably enrich one of those shared subpaths, add it there instead of creating a new local implementation.
645
+ - During migrations, treat local fixtures, setup wrappers, Vitest module payloads, and assertion helpers as candidates for `packages/testing` first. Keep only feature-specific orchestration local, and promote reusable portions so packages and apps do not rebuild the same testing surface independently.
646
+ - Before creating a new DOM/runtime helper that patches browser globals, check `@m4l/testing/rtl` first.
647
+ - Before creating a new Playwright route installer, auth/bootstrap mock, popup/window helper, or MF bundle stub, check `@m4l/testing/playwright` first.
648
+ - If a test needs `ResizeObserver`, prefer `installResizeObserverMock(...)` and `createResizeObserverEntry(...)` instead of adding a new inline `ResizeObserverMock` class in the test file.
649
+ - If you add a new public helper to `@m4l/testing/*`, document its purpose, intended usage context, and local-vs-shared boundary in the implementation JSDoc and keep this README decision guide aligned.
650
+ - Only keep a local observer mock when the behavior is truly feature-specific and cannot be expressed cleanly with the shared helper API.
651
+ - Keep generated declaration files out of `packages/testing/**/src`. Shared testing helpers are consumed from source in package test configs, so stray `.d.ts` files next to the source only add editor noise.
652
+
653
+ ## Placement rules
654
+
655
+ - Put cross-package reusable testing data in `@m4l/testing/mocks`.
656
+ - Put cross-package reusable Vitest installers and module-mock factories in `@m4l/testing/vitest`.
657
+ - Put cross-app reusable Playwright route installers, fixtures, and browser/runtime helpers in `@m4l/testing/playwright`.
658
+ - Prefer the shared environment factories in `@m4l/testing/mocks` before creating another inline `mockEnvironmentContext` or `environmentDataForTesting` object in a test file.
659
+ - Keep the final `vi.mock(...)` registration in the consumer `test/setup.ts` file even when the factory body comes from `@m4l/testing/vitest`.
660
+ - Keep app-specific page objects, selectors, and flows local to the owning `apps/*-e2e` project even when shared Playwright helpers exist.
661
+ - Keep feature-specific fixtures, wrappers, and assertions next to the feature in `__testing__/` or the local `src/test/` area only when they are genuinely too feature-bound to enrich `packages/testing`.
662
+ - Keep unit test files co-located with the component, hook, or helper they validate.
663
+
664
+ ## Design guidelines
665
+
666
+ - Prefer pure factories and deterministic constants over framework-bound mocks.
667
+ - Keep every `packages/testing/*/src/index.ts` file as a barrel-only entrypoint. Put implementation in responsibility files and let `index.ts` re-export that public surface.
668
+ - Use `*Fixture` for static test data, `create*Fixture(...)` for test-data builders, and `create*ModuleMock(...)` for `vi.mock(...)` payload factories.
669
+ - Prefer explicit implementation filenames such as `render.ts`, `consoleError.ts`, or `environment.ts` over catch-all filenames such as `utils.ts`.
670
+ - When a subpath accumulates several helpers, move them under `helpers/` with one file per helper or helper family and keep `helpers/index.ts` as a barrel.
671
+ - When a subpath accumulates several installers, move them under `installers/` with one file per installer or installer family and keep `installers/index.ts` as a barrel.
672
+ - Put reusable DOM observer/runtime helpers that patch browser globals, such as `ResizeObserver`, in `@m4l/testing/rtl` instead of `@m4l/testing/mocks`.
673
+ - Put reusable `vi.fn()`, `vi.mock(...)` factory payloads, and runner-specific setup logic in `@m4l/testing/vitest` instead of `@m4l/testing/mocks`.
674
+ - Inside `@m4l/testing/vitest`, keep repeated module factories under `factories/` with one file per mocked library or module family instead of a single large `factories.ts`.
675
+ - Put reusable Playwright helpers under explicit `fixtures/`, `helpers/`, or `installers/` folders inside `@m4l/testing/playwright` rather than leaving cross-app E2E support in one app project.
676
+ - Type shared fixtures with local contract interfaces inside `packages/testing`, then let each consumer validate compatibility with package-owned types through local `satisfies` adapters. This keeps the shared testing library typed without coupling it back to one runtime package.
677
+ - Keep `@m4l/testing/vitest` free of direct `@m4l/core` runtime type dependencies so packages such as `core` can consume it without creating a circular dependency.
678
+ - Do not move feature-specific wrappers into `packages/testing` just because more than one test file uses them inside the same feature area.
679
+ - When a helper only exists to support one component family, keep it local and document it there.