@qwickapps/react-framework 1.3.4 → 1.4.0

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 (325) hide show
  1. package/README.md +1688 -2
  2. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
  3. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
  4. package/dist/components/ErrorBoundary.d.ts +7 -0
  5. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  6. package/dist/components/Html.d.ts +28 -18
  7. package/dist/components/Html.d.ts.map +1 -1
  8. package/dist/components/Logo.d.ts +12 -35
  9. package/dist/components/Logo.d.ts.map +1 -1
  10. package/dist/components/Markdown.d.ts +18 -13
  11. package/dist/components/Markdown.d.ts.map +1 -1
  12. package/dist/components/QwickApp.d.ts +16 -3
  13. package/dist/components/QwickApp.d.ts.map +1 -1
  14. package/dist/components/QwickIcon.d.ts +23 -0
  15. package/dist/components/QwickIcon.d.ts.map +1 -0
  16. package/dist/components/SafeSpan.d.ts +12 -5
  17. package/dist/components/SafeSpan.d.ts.map +1 -1
  18. package/dist/components/Scaffold.d.ts.map +1 -1
  19. package/dist/components/base/ModelView.d.ts +101 -0
  20. package/dist/components/base/ModelView.d.ts.map +1 -0
  21. package/dist/components/base/index.d.ts +11 -0
  22. package/dist/components/base/index.d.ts.map +1 -0
  23. package/dist/components/blocks/Article.d.ts +12 -2
  24. package/dist/components/blocks/Article.d.ts.map +1 -1
  25. package/dist/components/blocks/Code.d.ts +13 -2
  26. package/dist/components/blocks/Code.d.ts.map +1 -1
  27. package/dist/components/blocks/Content.d.ts.map +1 -1
  28. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  30. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  31. package/dist/components/blocks/Footer.d.ts.map +1 -1
  32. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  33. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  34. package/dist/components/blocks/Image.d.ts +41 -0
  35. package/dist/components/blocks/Image.d.ts.map +1 -0
  36. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  37. package/dist/components/blocks/ProductCard.d.ts.map +1 -1
  38. package/dist/components/blocks/Section.d.ts +16 -2
  39. package/dist/components/blocks/Section.d.ts.map +1 -1
  40. package/dist/components/blocks/Text.d.ts +41 -0
  41. package/dist/components/blocks/Text.d.ts.map +1 -0
  42. package/dist/components/blocks/index.d.ts +4 -0
  43. package/dist/components/blocks/index.d.ts.map +1 -1
  44. package/dist/components/buttons/Button.d.ts +23 -7
  45. package/dist/components/buttons/Button.d.ts.map +1 -1
  46. package/dist/components/forms/FormBlock.d.ts +19 -13
  47. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  48. package/dist/components/index.d.ts +4 -0
  49. package/dist/components/index.d.ts.map +1 -1
  50. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  51. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  52. package/dist/components/input/HtmlInputField.d.ts +17 -11
  53. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  54. package/dist/components/input/SelectInputField.d.ts +16 -10
  55. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  56. package/dist/components/input/SwitchInputField.d.ts +16 -10
  57. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  58. package/dist/components/input/TextField.d.ts.map +1 -1
  59. package/dist/components/input/TextInputField.d.ts +16 -11
  60. package/dist/components/input/TextInputField.d.ts.map +1 -1
  61. package/dist/components/layout/GridCell.d.ts +23 -6
  62. package/dist/components/layout/GridCell.d.ts.map +1 -1
  63. package/dist/components/layout/GridLayout.d.ts +24 -23
  64. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  65. package/dist/components/pages/FormPage.d.ts.map +1 -1
  66. package/dist/components/pages/Page.d.ts +49 -87
  67. package/dist/components/pages/Page.d.ts.map +1 -1
  68. package/dist/components/pages/index.d.ts +2 -2
  69. package/dist/components/pages/index.d.ts.map +1 -1
  70. package/dist/config/AppConfig.d.ts +49 -0
  71. package/dist/config/AppConfig.d.ts.map +1 -0
  72. package/dist/config/AppConfigBuilder.d.ts +75 -0
  73. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  74. package/dist/config/index.d.ts +13 -0
  75. package/dist/config/index.d.ts.map +1 -0
  76. package/dist/config/types.d.ts +130 -0
  77. package/dist/config/types.d.ts.map +1 -0
  78. package/dist/config.d.ts +15 -0
  79. package/dist/config.d.ts.map +1 -0
  80. package/dist/config.esm.js +451 -0
  81. package/dist/config.js +455 -0
  82. package/dist/contexts/PrintModeContext.d.ts +27 -0
  83. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  84. package/dist/contexts/QwickAppContext.d.ts +2 -2
  85. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  86. package/dist/contexts/ThemeContext.d.ts.map +1 -1
  87. package/dist/contexts/index.d.ts +2 -0
  88. package/dist/contexts/index.d.ts.map +1 -1
  89. package/dist/hooks/index.d.ts +2 -0
  90. package/dist/hooks/index.d.ts.map +1 -1
  91. package/dist/hooks/usePrintMode.d.ts +39 -0
  92. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  93. package/dist/index.css +1 -1
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.esm.css +1 -1
  97. package/dist/index.esm.js +20722 -16021
  98. package/dist/index.js +20725 -16010
  99. package/dist/schemas/CodeSchema.d.ts +2 -1
  100. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  101. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  102. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  103. package/dist/schemas/ContentSchema.d.ts +2 -1
  104. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  105. package/dist/schemas/GridCellSchema.d.ts +25 -0
  106. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  107. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  108. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  109. package/dist/schemas/HtmlSchema.d.ts +14 -0
  110. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  111. package/dist/schemas/ImageSchema.d.ts +32 -0
  112. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  113. package/dist/schemas/LogoSchema.d.ts +35 -0
  114. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  115. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  116. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  117. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  118. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  119. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  120. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  121. package/dist/schemas/SectionSchema.d.ts +2 -1
  122. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  123. package/dist/schemas/TextSchema.d.ts +37 -0
  124. package/dist/schemas/TextSchema.d.ts.map +1 -0
  125. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  126. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  127. package/dist/schemas/index.d.ts +15 -1
  128. package/dist/schemas/index.d.ts.map +1 -1
  129. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  130. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  131. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  132. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  133. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  134. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  135. package/dist/schemas/transformers/registry.d.ts +15 -0
  136. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  137. package/dist/schemas/types/Serializable.d.ts +46 -0
  138. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  139. package/dist/utils/htmlTransform.d.ts.map +1 -1
  140. package/dist/utils/reactUtils.d.ts +12 -3
  141. package/dist/utils/reactUtils.d.ts.map +1 -1
  142. package/package.json +17 -3
  143. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  145. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  148. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  150. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  152. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  154. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  155. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  156. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  158. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  159. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  160. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  161. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  162. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  163. package/src/__tests__/components/Logo.test.js +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  165. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  166. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  167. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  168. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  169. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  172. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  173. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  174. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  175. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  176. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  177. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  178. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  179. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  180. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  181. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  182. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  183. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  184. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  185. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  186. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  187. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  188. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  189. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  190. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  191. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  192. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  193. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  194. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  195. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  196. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  197. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  198. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  199. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  200. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  201. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  202. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  203. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  204. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  205. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  206. package/src/components/ErrorBoundary.tsx +8 -8
  207. package/src/components/Html.tsx +147 -44
  208. package/src/components/Logo.tsx +198 -100
  209. package/src/components/Markdown.tsx +125 -16
  210. package/src/components/QwickApp.tsx +64 -31
  211. package/src/components/QwickIcon.tsx +59 -0
  212. package/src/components/SafeSpan.tsx +65 -10
  213. package/src/components/Scaffold.tsx +2 -8
  214. package/src/components/base/ModelView.tsx +199 -0
  215. package/src/components/base/index.ts +11 -0
  216. package/src/components/blocks/Article.tsx +57 -18
  217. package/src/components/blocks/Code.md +529 -0
  218. package/src/components/blocks/Code.tsx +102 -15
  219. package/src/components/blocks/Content.tsx +25 -77
  220. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  221. package/src/components/blocks/FeatureCard.tsx +1 -2
  222. package/src/components/blocks/FeatureGrid.tsx +19 -1
  223. package/src/components/blocks/Footer.tsx +13 -1
  224. package/src/components/blocks/HeroBlock.tsx +87 -20
  225. package/src/components/blocks/Image.tsx +395 -0
  226. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  227. package/src/components/blocks/ProductCard.tsx +51 -52
  228. package/src/components/blocks/Section.tsx +113 -8
  229. package/src/components/blocks/Text.tsx +285 -0
  230. package/src/components/blocks/index.ts +4 -0
  231. package/src/components/buttons/Button.tsx +184 -15
  232. package/src/components/forms/FormBlock.tsx +70 -17
  233. package/src/components/index.ts +5 -0
  234. package/src/components/input/ChoiceInputField.tsx +48 -18
  235. package/src/components/input/HtmlInputField.tsx +48 -18
  236. package/src/components/input/SelectInputField.tsx +48 -16
  237. package/src/components/input/SwitchInputField.tsx +48 -17
  238. package/src/components/input/TextField.tsx +41 -1
  239. package/src/components/input/TextInputField.tsx +52 -18
  240. package/src/components/layout/GridCell.tsx +118 -9
  241. package/src/components/layout/GridLayout.tsx +125 -24
  242. package/src/components/pages/FormPage.tsx +0 -1
  243. package/src/components/pages/Page.css +304 -332
  244. package/src/components/pages/Page.tsx +307 -255
  245. package/src/components/pages/index.ts +2 -2
  246. package/src/config/AppConfig.ts +133 -0
  247. package/src/config/AppConfigBuilder.ts +421 -0
  248. package/src/config/__tests__/AppConfig.test.ts +385 -0
  249. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  250. package/src/config/index.ts +24 -0
  251. package/src/config/types.ts +170 -0
  252. package/src/config.ts +25 -0
  253. package/src/contexts/PrintModeContext.tsx +332 -0
  254. package/src/contexts/QwickAppContext.tsx +2 -2
  255. package/src/contexts/ThemeContext.tsx +1 -2
  256. package/src/contexts/index.ts +2 -0
  257. package/src/hooks/index.ts +5 -1
  258. package/src/hooks/usePrintMode.ts +73 -0
  259. package/src/index.ts +3 -0
  260. package/src/schemas/CodeSchema.ts +3 -3
  261. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  262. package/src/schemas/ContentSchema.ts +2 -1
  263. package/src/schemas/GridCellSchema.ts +164 -0
  264. package/src/schemas/GridLayoutSchema.ts +133 -0
  265. package/src/schemas/HtmlSchema.ts +47 -0
  266. package/src/schemas/ImageSchema.ts +235 -0
  267. package/src/schemas/LogoSchema.ts +241 -0
  268. package/src/schemas/MarkdownSchema.ts +47 -0
  269. package/src/schemas/PageTemplateSchema.ts +186 -0
  270. package/src/schemas/PrintConfigSchema.ts +207 -0
  271. package/src/schemas/README.md +661 -0
  272. package/src/schemas/SectionSchema.ts +2 -1
  273. package/src/schemas/TextSchema.ts +329 -0
  274. package/src/schemas/ViewModelSchema.ts +115 -0
  275. package/src/schemas/index.ts +21 -2
  276. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  277. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  278. package/src/schemas/transformers/registry.ts +72 -0
  279. package/src/schemas/types/Serializable.ts +51 -0
  280. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  281. package/src/stories/Article.stories.tsx +433 -433
  282. package/src/stories/Button.stories.tsx +1 -1
  283. package/src/stories/CardListGrid.stories.tsx +451 -451
  284. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  285. package/src/stories/Code.stories.tsx +1 -1
  286. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  287. package/src/stories/Content.stories.tsx +393 -393
  288. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  289. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  290. package/src/stories/DataProvider.stories.tsx +1192 -1192
  291. package/src/stories/FeatureCard.stories.tsx +557 -557
  292. package/src/stories/FeatureGrid.stories.tsx +594 -594
  293. package/src/stories/Footer.stories.tsx +640 -640
  294. package/src/stories/FormBlock.stories.tsx +760 -760
  295. package/src/stories/FormComponents.stories.tsx +349 -541
  296. package/src/stories/GridCell.stories.tsx +417 -0
  297. package/src/stories/GridLayout.stories.tsx +353 -0
  298. package/src/stories/HeroBlock.stories.tsx +862 -373
  299. package/src/stories/HtmlInputField.stories.tsx +474 -474
  300. package/src/stories/Image.stories.tsx +819 -0
  301. package/src/stories/Introduction.stories.tsx +667 -667
  302. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  303. package/src/stories/Logo.stories.tsx +165 -6
  304. package/src/stories/Markdown.stories.tsx +137 -137
  305. package/src/stories/ModelView.stories.tsx +477 -0
  306. package/src/stories/Page.stories.tsx +688 -688
  307. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  308. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  309. package/src/stories/ProductCard.stories.tsx +424 -424
  310. package/src/stories/QwickApp.stories.tsx +368 -368
  311. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  312. package/src/stories/SafeSpan.stories.tsx +531 -531
  313. package/src/stories/Section.stories.tsx +90 -2
  314. package/src/stories/SelectInputField.stories.tsx +524 -524
  315. package/src/stories/Text.stories.tsx +560 -0
  316. package/src/stories/TextInputField.stories.tsx +443 -443
  317. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  318. package/src/utils/htmlTransform.tsx +74 -53
  319. package/src/utils/reactUtils.tsx +57 -6
  320. package/dist/index.bundled.css +0 -12
  321. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  322. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  323. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  324. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  325. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -0,0 +1,414 @@
1
+ /**
2
+ * HeroBlock Serialization Performance Tests
3
+ *
4
+ * Comprehensive test suite for HeroBlock component serialization functionality,
5
+ * including performance benchmarks and nested component serialization.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import React from 'react';
11
+ import { render } from '@testing-library/react';
12
+ import '@testing-library/jest-dom';
13
+ import HeroBlock from '../../../components/blocks/HeroBlock';
14
+ import { ComponentTransformer } from '../../../schemas/transformers/ComponentTransformer';
15
+ import '../../../schemas/transformers/registry';
16
+
17
+ describe('HeroBlock Serialization', () => {
18
+ describe('Performance Benchmarks', () => {
19
+ it('serializes basic HeroBlock in under 1ms', () => {
20
+ const heroComponent = (
21
+ <HeroBlock
22
+ title="Performance Test Hero"
23
+ subtitle="Testing serialization speed"
24
+ backgroundColor="primary"
25
+ textAlign="center"
26
+ blockHeight="medium"
27
+ />
28
+ );
29
+
30
+ const startTime = performance.now();
31
+ const serializedData = ComponentTransformer.serialize(heroComponent);
32
+ const endTime = performance.now();
33
+
34
+ const serializationTime = endTime - startTime;
35
+
36
+ expect(serializedData).toBeDefined();
37
+ expect(serializationTime).toBeLessThan(1); // Less than 1ms target
38
+
39
+ const parsedData = JSON.parse(serializedData);
40
+ expect(parsedData.tag).toBe('HeroBlock');
41
+ expect(parsedData.data.title).toBe('Performance Test Hero');
42
+ });
43
+
44
+ it('serializes complex HeroBlock with multiple actions in under 2ms', () => {
45
+ const complexHero = (
46
+ <HeroBlock
47
+ title="Complex Performance Test"
48
+ subtitle="Hero with multiple actions and configurations"
49
+ backgroundGradient="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
50
+ backgroundImage="https://example.com/hero-bg.jpg"
51
+ overlayOpacity={0.7}
52
+ textAlign="center"
53
+ blockHeight="large"
54
+ actions={[
55
+ { label: 'Primary Action', variant: 'primary', buttonSize: 'large', href: '/test' },
56
+ { label: 'Secondary Action', variant: 'secondary', buttonSize: 'medium', target: '_blank' },
57
+ { label: 'Outlined Action', variant: 'outlined', buttonSize: 'small' },
58
+ { label: 'Text Action', variant: 'text', disabled: true },
59
+ { label: 'Loading Action', variant: 'contained', loading: true }
60
+ ]}
61
+ />
62
+ );
63
+
64
+ const startTime = performance.now();
65
+ const serializedData = ComponentTransformer.serialize(complexHero);
66
+ const endTime = performance.now();
67
+
68
+ const serializationTime = endTime - startTime;
69
+
70
+ expect(serializedData).toBeDefined();
71
+ expect(serializationTime).toBeLessThan(2); // Less than 2ms target for complex components
72
+
73
+ const parsedData = JSON.parse(serializedData);
74
+ expect(parsedData.tag).toBe('HeroBlock');
75
+ expect(parsedData.data.actions).toHaveLength(5);
76
+ });
77
+
78
+ it('deserializes HeroBlock in under 1ms', () => {
79
+ const heroComponent = (
80
+ <HeroBlock
81
+ title="Deserialization Test"
82
+ subtitle="Testing deserialization speed"
83
+ backgroundColor="secondary"
84
+ actions={[
85
+ { label: 'Test Action', variant: 'primary', buttonSize: 'large' }
86
+ ]}
87
+ />
88
+ );
89
+
90
+ const serializedData = ComponentTransformer.serialize(heroComponent);
91
+
92
+ const startTime = performance.now();
93
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
94
+ const endTime = performance.now();
95
+
96
+ const deserializationTime = endTime - startTime;
97
+
98
+ expect(deserializedComponent).toBeDefined();
99
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
100
+ expect(deserializationTime).toBeLessThan(1); // Less than 1ms target
101
+ });
102
+
103
+ it('performs round-trip serialization 100 times in under 100ms', () => {
104
+ const heroComponent = (
105
+ <HeroBlock
106
+ title="Batch Test Hero"
107
+ subtitle="Testing batch serialization performance"
108
+ backgroundColor="primary"
109
+ actions={[
110
+ { label: 'Batch Action', variant: 'primary', buttonSize: 'medium' }
111
+ ]}
112
+ />
113
+ );
114
+
115
+ const startTime = performance.now();
116
+
117
+ for (let i = 0; i < 100; i++) {
118
+ const serialized = ComponentTransformer.serialize(heroComponent);
119
+ const deserialized = ComponentTransformer.deserialize(serialized);
120
+ expect(React.isValidElement(deserialized)).toBe(true);
121
+ }
122
+
123
+ const endTime = performance.now();
124
+ const totalTime = endTime - startTime;
125
+
126
+ expect(totalTime).toBeLessThan(100); // 100 round-trips in under 100ms (1ms per operation)
127
+
128
+ // Average time per operation should be under 1ms
129
+ const averageTime = totalTime / 100;
130
+ expect(averageTime).toBeLessThan(1);
131
+ });
132
+ });
133
+
134
+ describe('Nested Component Serialization', () => {
135
+ it('properly serializes HeroBlock with Button actions', () => {
136
+ const heroWithActions = (
137
+ <HeroBlock
138
+ title="Hero with Nested Actions"
139
+ subtitle="Testing nested Button component serialization"
140
+ backgroundGradient="linear-gradient(45deg, #2196F3, #21CBF3)"
141
+ textAlign="center"
142
+ blockHeight="medium"
143
+ actions={[
144
+ {
145
+ label: 'Primary Button',
146
+ variant: 'primary',
147
+ buttonSize: 'large',
148
+ href: '/primary-action'
149
+ },
150
+ {
151
+ label: 'Secondary Button',
152
+ variant: 'secondary',
153
+ buttonSize: 'medium',
154
+ disabled: false,
155
+ loading: false
156
+ }
157
+ ]}
158
+ />
159
+ );
160
+
161
+ const serializedData = ComponentTransformer.serialize(heroWithActions);
162
+ const parsedData = JSON.parse(serializedData);
163
+
164
+ // Verify HeroBlock serialization
165
+ expect(parsedData.tag).toBe('HeroBlock');
166
+ expect(parsedData.version).toBe('1.0.0');
167
+ expect(parsedData.data.title).toBe('Hero with Nested Actions');
168
+
169
+ // Verify nested actions serialization
170
+ expect(parsedData.data.actions).toHaveLength(2);
171
+ expect(parsedData.data.actions[0].label).toBe('Primary Button');
172
+ expect(parsedData.data.actions[0].variant).toBe('primary');
173
+ expect(parsedData.data.actions[0].buttonSize).toBe('large');
174
+ expect(parsedData.data.actions[0].href).toBe('/primary-action');
175
+
176
+ expect(parsedData.data.actions[1].label).toBe('Secondary Button');
177
+ expect(parsedData.data.actions[1].variant).toBe('secondary');
178
+ expect(parsedData.data.actions[1].buttonSize).toBe('medium');
179
+
180
+ // Verify deserialization works
181
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
182
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
183
+ });
184
+
185
+ it('handles complex action configurations with custom properties', () => {
186
+ const complexActionsHero = (
187
+ <HeroBlock
188
+ title="Complex Actions Test"
189
+ actions={[
190
+ {
191
+ label: 'Navigate Action',
192
+ variant: 'primary',
193
+ buttonSize: 'large',
194
+ action: { type: 'navigate', url: '/dashboard', target: '_self' }
195
+ },
196
+ {
197
+ label: 'External Action',
198
+ variant: 'outlined',
199
+ buttonSize: 'medium',
200
+ action: { type: 'external', url: 'https://example.com', target: '_blank' }
201
+ },
202
+ {
203
+ label: 'Custom Handler',
204
+ variant: 'text',
205
+ buttonSize: 'small',
206
+ action: { type: 'custom', customHandler: 'handleCustomClick' }
207
+ }
208
+ ]}
209
+ />
210
+ );
211
+
212
+ const serializedData = ComponentTransformer.serialize(complexActionsHero);
213
+ const parsedData = JSON.parse(serializedData);
214
+
215
+ expect(parsedData.data.actions).toHaveLength(3);
216
+
217
+ // Verify complex action structures are preserved
218
+ expect(parsedData.data.actions[0].action.type).toBe('navigate');
219
+ expect(parsedData.data.actions[0].action.url).toBe('/dashboard');
220
+ expect(parsedData.data.actions[0].action.target).toBe('_self');
221
+
222
+ expect(parsedData.data.actions[1].action.type).toBe('external');
223
+ expect(parsedData.data.actions[1].action.url).toBe('https://example.com');
224
+
225
+ expect(parsedData.data.actions[2].action.type).toBe('custom');
226
+ expect(parsedData.data.actions[2].action.customHandler).toBe('handleCustomClick');
227
+
228
+ // Verify deserialization maintains complex structures
229
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
230
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
231
+ });
232
+
233
+ it('preserves all background configuration options', () => {
234
+ const backgroundConfigHero = (
235
+ <HeroBlock
236
+ title="Background Config Test"
237
+ subtitle="Testing all background options"
238
+ backgroundImage="https://example.com/hero-bg.jpg"
239
+ backgroundGradient="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
240
+ backgroundColor="primary"
241
+ overlayOpacity={0.8}
242
+ textAlign="right"
243
+ blockHeight="viewport"
244
+ />
245
+ );
246
+
247
+ const serializedData = ComponentTransformer.serialize(backgroundConfigHero);
248
+ const parsedData = JSON.parse(serializedData);
249
+
250
+ expect(parsedData.data.backgroundImage).toBe('https://example.com/hero-bg.jpg');
251
+ expect(parsedData.data.backgroundGradient).toBe('linear-gradient(135deg, #667eea 0%, #764ba2 100%)');
252
+ expect(parsedData.data.backgroundColor).toBe('primary');
253
+ expect(parsedData.data.overlayOpacity).toBe(0.8);
254
+ expect(parsedData.data.textAlign).toBe('right');
255
+ expect(parsedData.data.blockHeight).toBe('viewport');
256
+
257
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
258
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
259
+ });
260
+
261
+ it('handles data binding configuration preservation', () => {
262
+ const dataBindingHero = (
263
+ <HeroBlock
264
+ dataSource="heroes.main"
265
+ bindingOptions={{ cache: true, cacheTTL: 300000, strict: false }}
266
+ title="Fallback Title"
267
+ subtitle="Fallback Subtitle"
268
+ />
269
+ );
270
+
271
+ const serializedData = ComponentTransformer.serialize(dataBindingHero);
272
+ const parsedData = JSON.parse(serializedData);
273
+
274
+ // Verify data binding properties are preserved
275
+ expect(parsedData.data.dataSource).toBe('heroes.main');
276
+ expect(parsedData.data.bindingOptions.cache).toBe(true);
277
+ expect(parsedData.data.bindingOptions.cacheTTL).toBe(300000);
278
+ expect(parsedData.data.bindingOptions.strict).toBe(false);
279
+
280
+ // Verify fallback props are preserved
281
+ expect(parsedData.data.title).toBe('Fallback Title');
282
+ expect(parsedData.data.subtitle).toBe('Fallback Subtitle');
283
+
284
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
285
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
286
+ });
287
+
288
+ it('handles edge cases with empty and undefined values', () => {
289
+ const edgeCaseHero = (
290
+ <HeroBlock
291
+ title=""
292
+ subtitle={undefined}
293
+ actions={[]}
294
+ backgroundImage={undefined}
295
+ overlayOpacity={0}
296
+ className=""
297
+ />
298
+ );
299
+
300
+ const serializedData = ComponentTransformer.serialize(edgeCaseHero);
301
+ const parsedData = JSON.parse(serializedData);
302
+
303
+ expect(parsedData.data.title).toBe('');
304
+ expect(parsedData.data.actions).toEqual([]);
305
+ expect(parsedData.data.overlayOpacity).toBe(0);
306
+
307
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
308
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
309
+ });
310
+ });
311
+
312
+ describe('Data Integrity', () => {
313
+ it('maintains complete data integrity through multiple serialization cycles', () => {
314
+ const originalHero = (
315
+ <HeroBlock
316
+ title="Data Integrity Test"
317
+ subtitle="Testing multiple serialization cycles"
318
+ backgroundGradient="linear-gradient(45deg, #FF6B6B, #4ECDC4)"
319
+ backgroundColor="secondary"
320
+ textAlign="center"
321
+ blockHeight="large"
322
+ overlayOpacity={0.6}
323
+ actions={[
324
+ {
325
+ label: 'Complex Action',
326
+ variant: 'primary',
327
+ buttonSize: 'large',
328
+ href: '/test-url',
329
+ target: '_blank',
330
+ disabled: false,
331
+ loading: false,
332
+ fullWidth: false,
333
+ action: {
334
+ type: 'navigate',
335
+ url: '/complex-url',
336
+ target: '_self'
337
+ }
338
+ }
339
+ ]}
340
+ />
341
+ );
342
+
343
+ // First serialization cycle
344
+ const serialized1 = ComponentTransformer.serialize(originalHero);
345
+ const deserialized1 = ComponentTransformer.deserialize(serialized1);
346
+
347
+ // Second serialization cycle
348
+ const serialized2 = ComponentTransformer.serialize(deserialized1 as React.ReactElement);
349
+ const deserialized2 = ComponentTransformer.deserialize(serialized2);
350
+
351
+ // Third serialization cycle
352
+ const serialized3 = ComponentTransformer.serialize(deserialized2 as React.ReactElement);
353
+ const parsedData3 = JSON.parse(serialized3);
354
+
355
+ // Verify data integrity is maintained across multiple cycles
356
+ expect(parsedData3.tag).toBe('HeroBlock');
357
+ expect(parsedData3.version).toBe('1.0.0');
358
+ expect(parsedData3.data.title).toBe('Data Integrity Test');
359
+ expect(parsedData3.data.subtitle).toBe('Testing multiple serialization cycles');
360
+ expect(parsedData3.data.backgroundGradient).toBe('linear-gradient(45deg, #FF6B6B, #4ECDC4)');
361
+ expect(parsedData3.data.backgroundColor).toBe('secondary');
362
+ expect(parsedData3.data.textAlign).toBe('center');
363
+ expect(parsedData3.data.blockHeight).toBe('large');
364
+ expect(parsedData3.data.overlayOpacity).toBe(0.6);
365
+
366
+ expect(parsedData3.data.actions).toHaveLength(1);
367
+ expect(parsedData3.data.actions[0].label).toBe('Complex Action');
368
+ expect(parsedData3.data.actions[0].variant).toBe('primary');
369
+ expect(parsedData3.data.actions[0].href).toBe('/test-url');
370
+ expect(parsedData3.data.actions[0].target).toBe('_blank');
371
+ expect(parsedData3.data.actions[0].action.type).toBe('navigate');
372
+ expect(parsedData3.data.actions[0].action.url).toBe('/complex-url');
373
+
374
+ const finalDeserialized = ComponentTransformer.deserialize(serialized3);
375
+ expect(React.isValidElement(finalDeserialized)).toBe(true);
376
+ });
377
+
378
+ it('preserves component hierarchy and structure', () => {
379
+ const structuralHero = (
380
+ <HeroBlock
381
+ title="Structural Test"
382
+ subtitle="Testing component structure preservation"
383
+ backgroundColor="primary"
384
+ actions={[
385
+ { label: 'Action 1', variant: 'primary' },
386
+ { label: 'Action 2', variant: 'secondary' },
387
+ { label: 'Action 3', variant: 'outlined' }
388
+ ]}
389
+ />
390
+ );
391
+
392
+ const serializedData = ComponentTransformer.serialize(structuralHero);
393
+ const parsedData = JSON.parse(serializedData);
394
+
395
+ // Verify component structure
396
+ expect(parsedData).toHaveProperty('tag');
397
+ expect(parsedData).toHaveProperty('version');
398
+ expect(parsedData).toHaveProperty('data');
399
+
400
+ // Verify nested structure (actions array)
401
+ expect(parsedData.data.actions).toBeInstanceOf(Array);
402
+ expect(parsedData.data.actions).toHaveLength(3);
403
+
404
+ parsedData.data.actions.forEach((action: any, index: number) => {
405
+ expect(action).toHaveProperty('label');
406
+ expect(action).toHaveProperty('variant');
407
+ expect(action.label).toBe(`Action ${index + 1}`);
408
+ });
409
+
410
+ const deserializedComponent = ComponentTransformer.deserialize(serializedData);
411
+ expect(React.isValidElement(deserializedComponent)).toBe(true);
412
+ });
413
+ });
414
+ });
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Image Component Serialization Tests
3
+ *
4
+ * Tests for the Image component's ModelView implementation and
5
+ * serialization capabilities using ComponentTransformer.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import React from 'react';
11
+ import { render, screen } from '@testing-library/react';
12
+ import '@testing-library/jest-dom';
13
+ import { ComponentTransformer } from '../../../schemas/transformers/ComponentTransformer';
14
+ import { Image } from '../../../components/blocks/Image';
15
+
16
+ describe('Image Serialization', () => {
17
+ beforeEach(() => {
18
+ // Clear component registry for clean tests
19
+ ComponentTransformer.clearRegistry();
20
+
21
+ // Register Image component
22
+ ComponentTransformer.registerComponent(Image as any);
23
+ });
24
+
25
+ afterEach(() => {
26
+ ComponentTransformer.clearRegistry();
27
+ });
28
+
29
+ describe('Basic Serialization', () => {
30
+ it('should serialize and deserialize basic image component', () => {
31
+ // Create original component
32
+ const originalComponent = (
33
+ <Image
34
+ src="https://example.com/image.jpg"
35
+ alt="Test image"
36
+ width={400}
37
+ height={300}
38
+ />
39
+ );
40
+
41
+ // Serialize
42
+ const serialized = ComponentTransformer.serialize(originalComponent);
43
+ expect(serialized).toBeTruthy();
44
+ expect(typeof serialized).toBe('string');
45
+
46
+ // Parse to check structure
47
+ const parsed = JSON.parse(serialized);
48
+ expect(parsed.tag).toBe('Image');
49
+ expect(parsed.version).toBe('1.0.0');
50
+ expect(parsed.data.src).toBe('https://example.com/image.jpg');
51
+ expect(parsed.data.alt).toBe('Test image');
52
+ expect(parsed.data.width).toBe(400);
53
+ expect(parsed.data.height).toBe(300);
54
+
55
+ // Deserialize
56
+ const deserialized = ComponentTransformer.deserialize(serialized);
57
+ expect(React.isValidElement(deserialized)).toBe(true);
58
+ });
59
+
60
+ it('should preserve all image properties through serialization', () => {
61
+ const originalComponent = (
62
+ <Image
63
+ src="https://example.com/image.jpg"
64
+ alt="Comprehensive test image"
65
+ width={600}
66
+ height={400}
67
+ objectFit="cover"
68
+ objectPosition="center"
69
+ loading="lazy"
70
+ title="Test image title"
71
+ draggable={false}
72
+ borderRadius="12px"
73
+ showLoading={true}
74
+ showError={true}
75
+ fallbackSrc="https://example.com/fallback.jpg"
76
+ />
77
+ );
78
+
79
+ const serialized = ComponentTransformer.serialize(originalComponent);
80
+ const parsed = JSON.parse(serialized);
81
+ const data = parsed.data;
82
+
83
+ expect(data.src).toBe('https://example.com/image.jpg');
84
+ expect(data.alt).toBe('Comprehensive test image');
85
+ expect(data.width).toBe(600);
86
+ expect(data.height).toBe(400);
87
+ expect(data.objectFit).toBe('cover');
88
+ expect(data.objectPosition).toBe('center');
89
+ expect(data.loading).toBe('lazy');
90
+ expect(data.title).toBe('Test image title');
91
+ expect(data.draggable).toBe(false);
92
+ expect(data.borderRadius).toBe('12px');
93
+ expect(data.showLoading).toBe(true);
94
+ expect(data.showError).toBe(true);
95
+ expect(data.fallbackSrc).toBe('https://example.com/fallback.jpg');
96
+
97
+ const deserialized = ComponentTransformer.deserialize(serialized);
98
+ expect(React.isValidElement(deserialized)).toBe(true);
99
+ });
100
+
101
+ it('should handle responsive image properties', () => {
102
+ const originalComponent = (
103
+ <Image
104
+ src="https://example.com/image.jpg"
105
+ alt="Responsive image"
106
+ sizes="(max-width: 768px) 100vw, 50vw"
107
+ srcSet="https://example.com/image-400.jpg 400w, https://example.com/image-800.jpg 800w"
108
+ />
109
+ );
110
+
111
+ const serialized = ComponentTransformer.serialize(originalComponent);
112
+ const parsed = JSON.parse(serialized);
113
+
114
+ expect(parsed.data.sizes).toBe('(max-width: 768px) 100vw, 50vw');
115
+ expect(parsed.data.srcSet).toBe('https://example.com/image-400.jpg 400w, https://example.com/image-800.jpg 800w');
116
+
117
+ const deserialized = ComponentTransformer.deserialize(serialized);
118
+ expect(React.isValidElement(deserialized)).toBe(true);
119
+ });
120
+ });
121
+
122
+ describe('ModelView Integration', () => {
123
+ it('should have correct static properties', () => {
124
+ expect((Image as any).tagName).toBe('Image');
125
+ expect((Image as any).version).toBe('1.0.0');
126
+ expect(typeof (Image as any).fromJson).toBe('function');
127
+ });
128
+
129
+ it('should create instance with toJson method', () => {
130
+ const imageInstance = new Image({
131
+ src: 'https://example.com/test.jpg',
132
+ alt: 'Test image'
133
+ });
134
+
135
+ expect(typeof imageInstance.toJson).toBe('function');
136
+
137
+ const serialized = imageInstance.toJson();
138
+ expect(serialized.src).toBe('https://example.com/test.jpg');
139
+ expect(serialized.alt).toBe('Test image');
140
+ });
141
+
142
+ it('should handle fromJson static method', () => {
143
+ const jsonData = {
144
+ src: 'https://example.com/test.jpg',
145
+ alt: 'From JSON test',
146
+ width: 300,
147
+ height: 200
148
+ };
149
+
150
+ const component = (Image as any).fromJson(jsonData);
151
+ expect(React.isValidElement(component)).toBe(true);
152
+ });
153
+ });
154
+
155
+ describe('Data Binding Serialization', () => {
156
+ it('should preserve dataSource and bindingOptions', () => {
157
+ const originalComponent = (
158
+ <Image
159
+ src="https://example.com/image.jpg"
160
+ alt="Data bound image"
161
+ dataSource="images.hero"
162
+ bindingOptions={{
163
+ cache: true,
164
+ cacheTTL: 300000,
165
+ strict: false
166
+ }}
167
+ />
168
+ );
169
+
170
+ const serialized = ComponentTransformer.serialize(originalComponent);
171
+ const parsed = JSON.parse(serialized);
172
+
173
+ expect(parsed.data.dataSource).toBe('images.hero');
174
+ expect(parsed.data.bindingOptions).toEqual({
175
+ cache: true,
176
+ cacheTTL: 300000,
177
+ strict: false
178
+ });
179
+
180
+ const deserialized = ComponentTransformer.deserialize(serialized);
181
+ expect(React.isValidElement(deserialized)).toBe(true);
182
+ });
183
+ });
184
+
185
+ describe('Error Handling', () => {
186
+ it('should handle serialization of image without required props', () => {
187
+ // Image without src should still serialize (though it may not render)
188
+ const originalComponent = <Image alt="Image without src" />;
189
+
190
+ const serialized = ComponentTransformer.serialize(originalComponent);
191
+ expect(serialized).toBeTruthy();
192
+
193
+ const parsed = JSON.parse(serialized);
194
+ expect(parsed.tag).toBe('Image');
195
+ expect(parsed.data.alt).toBe('Image without src');
196
+ expect(parsed.data.src).toBeUndefined();
197
+
198
+ const deserialized = ComponentTransformer.deserialize(serialized);
199
+ expect(React.isValidElement(deserialized)).toBe(true);
200
+ });
201
+
202
+ it('should handle empty/null values gracefully', () => {
203
+ const originalComponent = (
204
+ <Image
205
+ src=""
206
+ alt=""
207
+ width={undefined}
208
+ height={undefined}
209
+ />
210
+ );
211
+
212
+ const serialized = ComponentTransformer.serialize(originalComponent);
213
+ const parsed = JSON.parse(serialized);
214
+
215
+ expect(parsed.data.src).toBe('');
216
+ expect(parsed.data.alt).toBe('');
217
+
218
+ const deserialized = ComponentTransformer.deserialize(serialized);
219
+ expect(React.isValidElement(deserialized)).toBe(true);
220
+ });
221
+ });
222
+
223
+ describe('Component Registry', () => {
224
+ it('should be registered in ComponentTransformer', () => {
225
+ const registeredComponents = ComponentTransformer.getRegisteredComponents();
226
+ expect(registeredComponents).toContain('Image');
227
+ });
228
+
229
+ it('should support round-trip serialization consistency', () => {
230
+ const originalComponent = (
231
+ <Image
232
+ src="https://example.com/consistency-test.jpg"
233
+ alt="Consistency test image"
234
+ width={500}
235
+ height={300}
236
+ objectFit="contain"
237
+ borderRadius="8px"
238
+ showLoading={true}
239
+ />
240
+ );
241
+
242
+ // First round-trip
243
+ const serialized1 = ComponentTransformer.serialize(originalComponent);
244
+ const deserialized1 = ComponentTransformer.deserialize(serialized1);
245
+
246
+ // Second round-trip
247
+ const serialized2 = ComponentTransformer.serialize(deserialized1);
248
+ const parsed1 = JSON.parse(serialized1);
249
+ const parsed2 = JSON.parse(serialized2);
250
+
251
+ // Should be identical
252
+ expect(parsed1.tag).toBe(parsed2.tag);
253
+ expect(parsed1.version).toBe(parsed2.version);
254
+ expect(parsed1.data).toEqual(parsed2.data);
255
+ });
256
+ });
257
+ });