@qwickapps/react-framework 1.3.5 → 1.4.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 (320) hide show
  1. package/README.md +1691 -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/CoverImageHeader.d.ts.map +1 -1
  28. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  30. package/dist/components/blocks/Footer.d.ts.map +1 -1
  31. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  32. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  33. package/dist/components/blocks/Image.d.ts +41 -0
  34. package/dist/components/blocks/Image.d.ts.map +1 -0
  35. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  36. package/dist/components/blocks/Section.d.ts +16 -2
  37. package/dist/components/blocks/Section.d.ts.map +1 -1
  38. package/dist/components/blocks/Text.d.ts +41 -0
  39. package/dist/components/blocks/Text.d.ts.map +1 -0
  40. package/dist/components/blocks/index.d.ts +4 -0
  41. package/dist/components/blocks/index.d.ts.map +1 -1
  42. package/dist/components/buttons/Button.d.ts +23 -7
  43. package/dist/components/buttons/Button.d.ts.map +1 -1
  44. package/dist/components/forms/FormBlock.d.ts +19 -13
  45. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  46. package/dist/components/index.d.ts +4 -0
  47. package/dist/components/index.d.ts.map +1 -1
  48. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  49. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  50. package/dist/components/input/HtmlInputField.d.ts +17 -11
  51. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  52. package/dist/components/input/SelectInputField.d.ts +16 -10
  53. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  54. package/dist/components/input/SwitchInputField.d.ts +16 -10
  55. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  56. package/dist/components/input/TextField.d.ts.map +1 -1
  57. package/dist/components/input/TextInputField.d.ts +16 -11
  58. package/dist/components/input/TextInputField.d.ts.map +1 -1
  59. package/dist/components/layout/GridCell.d.ts +23 -6
  60. package/dist/components/layout/GridCell.d.ts.map +1 -1
  61. package/dist/components/layout/GridLayout.d.ts +24 -23
  62. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  63. package/dist/components/pages/FormPage.d.ts.map +1 -1
  64. package/dist/components/pages/Page.d.ts +49 -87
  65. package/dist/components/pages/Page.d.ts.map +1 -1
  66. package/dist/components/pages/index.d.ts +2 -2
  67. package/dist/components/pages/index.d.ts.map +1 -1
  68. package/dist/config/AppConfig.d.ts +49 -0
  69. package/dist/config/AppConfig.d.ts.map +1 -0
  70. package/dist/config/AppConfigBuilder.d.ts +75 -0
  71. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  72. package/dist/config/index.d.ts +13 -0
  73. package/dist/config/index.d.ts.map +1 -0
  74. package/dist/config/types.d.ts +130 -0
  75. package/dist/config/types.d.ts.map +1 -0
  76. package/dist/config.d.ts +15 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.esm.js +451 -0
  79. package/dist/config.js +455 -0
  80. package/dist/contexts/PrintModeContext.d.ts +27 -0
  81. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  82. package/dist/contexts/QwickAppContext.d.ts +2 -2
  83. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  84. package/dist/contexts/index.d.ts +2 -0
  85. package/dist/contexts/index.d.ts.map +1 -1
  86. package/dist/hooks/index.d.ts +2 -0
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/usePrintMode.d.ts +39 -0
  89. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  90. package/dist/index.css +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +10951 -6238
  95. package/dist/index.js +11014 -6287
  96. package/dist/schemas/CodeSchema.d.ts +2 -1
  97. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  98. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  99. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  100. package/dist/schemas/ContentSchema.d.ts +2 -1
  101. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  102. package/dist/schemas/GridCellSchema.d.ts +25 -0
  103. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  104. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  105. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  106. package/dist/schemas/HtmlSchema.d.ts +14 -0
  107. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  108. package/dist/schemas/ImageSchema.d.ts +32 -0
  109. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  110. package/dist/schemas/LogoSchema.d.ts +35 -0
  111. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  112. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  113. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  114. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  115. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  116. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  117. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  118. package/dist/schemas/SectionSchema.d.ts +2 -1
  119. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  120. package/dist/schemas/TextSchema.d.ts +37 -0
  121. package/dist/schemas/TextSchema.d.ts.map +1 -0
  122. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  123. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  124. package/dist/schemas/index.d.ts +15 -1
  125. package/dist/schemas/index.d.ts.map +1 -1
  126. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  127. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  128. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  129. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  130. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  131. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  132. package/dist/schemas/transformers/registry.d.ts +15 -0
  133. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  134. package/dist/schemas/types/Serializable.d.ts +46 -0
  135. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  136. package/dist/utils/htmlTransform.d.ts.map +1 -1
  137. package/dist/utils/reactUtils.d.ts +12 -3
  138. package/dist/utils/reactUtils.d.ts.map +1 -1
  139. package/package.json +17 -3
  140. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  141. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  142. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  143. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  145. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  148. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  150. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  152. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  154. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  155. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  156. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  158. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  159. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  160. package/src/__tests__/components/Logo.test.js +3 -3
  161. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  162. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  163. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  165. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  166. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  167. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  168. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  169. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  172. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  173. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  174. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  175. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  176. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  177. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  178. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  179. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  180. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  181. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  182. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  183. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  184. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  185. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  186. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  187. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  188. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  189. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  190. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  191. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  192. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  193. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  194. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  195. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  196. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  197. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  198. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  199. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  200. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  201. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  202. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  203. package/src/components/ErrorBoundary.tsx +8 -8
  204. package/src/components/Html.tsx +147 -44
  205. package/src/components/Logo.tsx +198 -100
  206. package/src/components/Markdown.tsx +125 -16
  207. package/src/components/QwickApp.tsx +64 -31
  208. package/src/components/QwickIcon.tsx +59 -0
  209. package/src/components/SafeSpan.tsx +65 -10
  210. package/src/components/Scaffold.tsx +2 -8
  211. package/src/components/base/ModelView.tsx +199 -0
  212. package/src/components/base/index.ts +11 -0
  213. package/src/components/blocks/Article.tsx +57 -18
  214. package/src/components/blocks/Code.md +529 -0
  215. package/src/components/blocks/Code.tsx +102 -15
  216. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  217. package/src/components/blocks/FeatureCard.tsx +1 -2
  218. package/src/components/blocks/FeatureGrid.tsx +19 -1
  219. package/src/components/blocks/Footer.tsx +13 -1
  220. package/src/components/blocks/HeroBlock.tsx +87 -20
  221. package/src/components/blocks/Image.tsx +395 -0
  222. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  223. package/src/components/blocks/ProductCard.tsx +1 -1
  224. package/src/components/blocks/Section.tsx +113 -8
  225. package/src/components/blocks/Text.tsx +285 -0
  226. package/src/components/blocks/index.ts +4 -0
  227. package/src/components/buttons/Button.tsx +184 -15
  228. package/src/components/forms/FormBlock.tsx +70 -17
  229. package/src/components/index.ts +5 -0
  230. package/src/components/input/ChoiceInputField.tsx +48 -18
  231. package/src/components/input/HtmlInputField.tsx +48 -18
  232. package/src/components/input/SelectInputField.tsx +48 -16
  233. package/src/components/input/SwitchInputField.tsx +48 -17
  234. package/src/components/input/TextField.tsx +41 -1
  235. package/src/components/input/TextInputField.tsx +52 -18
  236. package/src/components/layout/GridCell.tsx +118 -9
  237. package/src/components/layout/GridLayout.tsx +125 -24
  238. package/src/components/pages/FormPage.tsx +0 -1
  239. package/src/components/pages/Page.css +304 -332
  240. package/src/components/pages/Page.tsx +307 -255
  241. package/src/components/pages/index.ts +2 -2
  242. package/src/config/AppConfig.ts +133 -0
  243. package/src/config/AppConfigBuilder.ts +421 -0
  244. package/src/config/__tests__/AppConfig.test.ts +385 -0
  245. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  246. package/src/config/index.ts +24 -0
  247. package/src/config/types.ts +170 -0
  248. package/src/config.ts +25 -0
  249. package/src/contexts/PrintModeContext.tsx +332 -0
  250. package/src/contexts/QwickAppContext.tsx +2 -2
  251. package/src/contexts/index.ts +2 -0
  252. package/src/hooks/index.ts +5 -1
  253. package/src/hooks/usePrintMode.ts +73 -0
  254. package/src/index.ts +3 -0
  255. package/src/schemas/CodeSchema.ts +3 -3
  256. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  257. package/src/schemas/ContentSchema.ts +2 -1
  258. package/src/schemas/GridCellSchema.ts +164 -0
  259. package/src/schemas/GridLayoutSchema.ts +133 -0
  260. package/src/schemas/HtmlSchema.ts +47 -0
  261. package/src/schemas/ImageSchema.ts +235 -0
  262. package/src/schemas/LogoSchema.ts +241 -0
  263. package/src/schemas/MarkdownSchema.ts +47 -0
  264. package/src/schemas/PageTemplateSchema.ts +186 -0
  265. package/src/schemas/PrintConfigSchema.ts +207 -0
  266. package/src/schemas/README.md +661 -0
  267. package/src/schemas/SectionSchema.ts +2 -1
  268. package/src/schemas/TextSchema.ts +329 -0
  269. package/src/schemas/ViewModelSchema.ts +115 -0
  270. package/src/schemas/index.ts +21 -2
  271. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  272. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  273. package/src/schemas/transformers/registry.ts +72 -0
  274. package/src/schemas/types/Serializable.ts +51 -0
  275. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  276. package/src/stories/Article.stories.tsx +433 -433
  277. package/src/stories/Button.stories.tsx +1 -1
  278. package/src/stories/CardListGrid.stories.tsx +451 -451
  279. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  280. package/src/stories/Code.stories.tsx +1 -1
  281. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  282. package/src/stories/Content.stories.tsx +393 -393
  283. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  284. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  285. package/src/stories/DataProvider.stories.tsx +1192 -1192
  286. package/src/stories/FeatureCard.stories.tsx +557 -557
  287. package/src/stories/FeatureGrid.stories.tsx +594 -594
  288. package/src/stories/Footer.stories.tsx +640 -640
  289. package/src/stories/FormBlock.stories.tsx +760 -760
  290. package/src/stories/FormComponents.stories.tsx +349 -541
  291. package/src/stories/GridCell.stories.tsx +417 -0
  292. package/src/stories/GridLayout.stories.tsx +353 -0
  293. package/src/stories/HeroBlock.stories.tsx +862 -373
  294. package/src/stories/HtmlInputField.stories.tsx +474 -474
  295. package/src/stories/Image.stories.tsx +819 -0
  296. package/src/stories/Introduction.stories.tsx +667 -667
  297. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  298. package/src/stories/Logo.stories.tsx +165 -6
  299. package/src/stories/Markdown.stories.tsx +137 -137
  300. package/src/stories/ModelView.stories.tsx +477 -0
  301. package/src/stories/Page.stories.tsx +688 -688
  302. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  303. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  304. package/src/stories/ProductCard.stories.tsx +424 -424
  305. package/src/stories/QwickApp.stories.tsx +368 -368
  306. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  307. package/src/stories/SafeSpan.stories.tsx +531 -531
  308. package/src/stories/Section.stories.tsx +90 -2
  309. package/src/stories/SelectInputField.stories.tsx +524 -524
  310. package/src/stories/Text.stories.tsx +560 -0
  311. package/src/stories/TextInputField.stories.tsx +443 -443
  312. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  313. package/src/utils/htmlTransform.tsx +74 -53
  314. package/src/utils/reactUtils.tsx +57 -6
  315. package/dist/index.bundled.css +0 -12
  316. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  317. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  318. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  319. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  320. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -0,0 +1,602 @@
1
+ /**
2
+ * Component Serialization Error Handling Tests
3
+ *
4
+ * Comprehensive error handling validation for the serialization system
5
+ * covering malformed data, unknown components, version incompatibility,
6
+ * and various failure scenarios.
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+
11
+ import React, { ReactElement } from 'react';
12
+ import { ComponentTransformer } from '../ComponentTransformer';
13
+ import { Serializable, SerializableConstructor } from '../../types/Serializable';
14
+
15
+ // Mock components for error testing
16
+ class ErrorTestButton implements Serializable {
17
+ constructor(public props: { label?: string; throwOnFromJson?: boolean; throwOnToJson?: boolean }) {}
18
+
19
+ static fromJson(jsonData: any): ReactElement {
20
+ if (jsonData.throwOnFromJson) {
21
+ throw new Error('Intentional fromJson error for testing');
22
+ }
23
+ return React.createElement('button', {}, jsonData.label || 'Button');
24
+ }
25
+
26
+ toJson(): any {
27
+ if (this.props.throwOnToJson) {
28
+ throw new Error('Intentional toJson error for testing');
29
+ }
30
+ return {
31
+ label: this.props.label,
32
+ throwOnFromJson: this.props.throwOnFromJson,
33
+ throwOnToJson: this.props.throwOnToJson
34
+ };
35
+ }
36
+ }
37
+
38
+ class InvalidComponentClass {
39
+ // This class does not implement Serializable interface
40
+ static fromJson(jsonData: any): ReactElement {
41
+ return React.createElement('div', {}, 'Invalid component');
42
+ }
43
+ // Missing toJson method
44
+ }
45
+
46
+ class MalformedFromJsonComponent implements Serializable {
47
+ constructor(public props: any) {}
48
+
49
+ static fromJson(jsonData: any): any {
50
+ // Returns non-ReactElement (invalid)
51
+ return { invalid: 'return value' };
52
+ }
53
+
54
+ toJson(): any {
55
+ return this.props;
56
+ }
57
+ }
58
+
59
+ describe('Component Serialization Error Handling', () => {
60
+ beforeEach(() => {
61
+ ComponentTransformer.clearRegistry();
62
+ });
63
+
64
+ afterEach(() => {
65
+ ComponentTransformer.clearRegistry();
66
+ });
67
+
68
+ describe('Unknown Component Errors', () => {
69
+ it('should throw descriptive error for unregistered component', () => {
70
+ const unknownComponent = {
71
+ tag: 'UnknownWidget',
72
+ version: '1.0.0',
73
+ data: { prop: 'value' }
74
+ };
75
+
76
+ // With the new fallback system, unknown components should not throw but use ReactNodeTransformer
77
+ const result = ComponentTransformer.deserialize(unknownComponent);
78
+ expect(result).toBeDefined(); // Should fallback gracefully
79
+ });
80
+
81
+ it('should throw error for component with special characters in tag name', () => {
82
+ const invalidTagComponent = {
83
+ tag: 'Invalid<>Tag',
84
+ version: '1.0.0',
85
+ data: {}
86
+ };
87
+
88
+ expect(() => {
89
+ ComponentTransformer.deserialize(invalidTagComponent);
90
+ }).toThrow('Unknown component: Invalid<>Tag');
91
+ });
92
+
93
+ it('should throw error for component with numeric tag name', () => {
94
+ const numericTagComponent = {
95
+ tag: '123Component',
96
+ version: '1.0.0',
97
+ data: {}
98
+ };
99
+
100
+ expect(() => {
101
+ ComponentTransformer.deserialize(numericTagComponent);
102
+ }).toThrow('Unknown component: 123Component');
103
+ });
104
+
105
+ it('should throw error for empty tag name', () => {
106
+ const emptyTagComponent = {
107
+ tag: '',
108
+ version: '1.0.0',
109
+ data: {}
110
+ };
111
+
112
+ expect(() => {
113
+ ComponentTransformer.deserialize(emptyTagComponent);
114
+ }).toThrow('Unknown component: ');
115
+ });
116
+
117
+ it('should handle array of components with unknown component gracefully', () => {
118
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
119
+
120
+ const mixedComponents = [
121
+ {
122
+ tag: 'ErrorTestButton',
123
+ version: '1.0.0',
124
+ data: { label: 'Valid Button' }
125
+ },
126
+ {
127
+ tag: 'UnknownComponent',
128
+ version: '1.0.0',
129
+ data: {}
130
+ }
131
+ ];
132
+
133
+ expect(() => {
134
+ ComponentTransformer.deserialize(mixedComponents);
135
+ }).toThrow('Unknown component: UnknownComponent');
136
+ });
137
+ });
138
+
139
+ describe('Malformed Component Data Errors', () => {
140
+ beforeEach(() => {
141
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
142
+ });
143
+
144
+ it('should throw error when data property is missing', () => {
145
+ const missingDataComponent = {
146
+ tag: 'ErrorTestButton',
147
+ version: '1.0.0'
148
+ // Missing data property
149
+ };
150
+
151
+ expect(() => {
152
+ ComponentTransformer.deserialize(missingDataComponent);
153
+ }).toThrow('Malformed component data: missing \'data\' property for component \'ErrorTestButton\'');
154
+ });
155
+
156
+ it('should handle undefined data property', () => {
157
+ const undefinedDataComponent = {
158
+ tag: 'ErrorTestButton',
159
+ version: '1.0.0',
160
+ data: undefined
161
+ };
162
+
163
+ expect(() => {
164
+ ComponentTransformer.deserialize(undefinedDataComponent);
165
+ }).toThrow('Malformed component data: missing \'data\' property for component \'ErrorTestButton\'');
166
+ });
167
+
168
+ it('should handle null data property gracefully', () => {
169
+ const nullDataComponent = {
170
+ tag: 'ErrorTestButton',
171
+ version: '1.0.0',
172
+ data: null
173
+ };
174
+
175
+ expect(() => {
176
+ const result = ComponentTransformer.deserialize(nullDataComponent);
177
+ expect(React.isValidElement(result)).toBe(true);
178
+ }).not.toThrow();
179
+ });
180
+
181
+ it('should throw error when tag property is missing', () => {
182
+ const missingTagComponent = {
183
+ version: '1.0.0',
184
+ data: { label: 'Test' }
185
+ };
186
+
187
+ expect(() => {
188
+ ComponentTransformer.deserialize(missingTagComponent);
189
+ }).not.toThrow(); // Should be handled as non-component data
190
+
191
+ // The result should be the object itself
192
+ const result = ComponentTransformer.deserialize(missingTagComponent);
193
+ expect(result).toEqual(missingTagComponent);
194
+ });
195
+
196
+ it('should handle missing version property', () => {
197
+ const missingVersionComponent = {
198
+ tag: 'ErrorTestButton',
199
+ data: { label: 'Test' }
200
+ // Missing version property
201
+ };
202
+
203
+ expect(() => {
204
+ const result = ComponentTransformer.deserialize(missingVersionComponent);
205
+ expect(React.isValidElement(result)).toBe(true);
206
+ }).not.toThrow();
207
+ });
208
+
209
+ it('should handle invalid version format', () => {
210
+ const invalidVersions = [
211
+ { tag: 'ErrorTestButton', version: 123, data: {} },
212
+ { tag: 'ErrorTestButton', version: null, data: {} },
213
+ { tag: 'ErrorTestButton', version: {}, data: {} },
214
+ { tag: 'ErrorTestButton', version: 'invalid.version.format.too.long', data: {} }
215
+ ];
216
+
217
+ invalidVersions.forEach((component, index) => {
218
+ expect(() => {
219
+ const result = ComponentTransformer.deserialize(component);
220
+ expect(React.isValidElement(result)).toBe(true);
221
+ }).not.toThrow(`Should not throw for invalid version case ${index}`);
222
+ });
223
+ });
224
+ });
225
+
226
+ describe('Invalid JSON Input Errors', () => {
227
+ it('should throw descriptive error for malformed JSON string', () => {
228
+ const malformedJsonInputs = [
229
+ '{invalid json',
230
+ '{"unclosed": "object"',
231
+ '{key: "value"}', // Missing quotes around key
232
+ '[1, 2, 3,]', // Trailing comma
233
+ '{"duplicate": "key", "duplicate": "key2"}', // Valid JSON but semantically questionable
234
+ ];
235
+
236
+ malformedJsonInputs.forEach((input, index) => {
237
+ if (input !== '{"duplicate": "key", "duplicate": "key2"}') { // This is actually valid JSON
238
+ expect(() => {
239
+ ComponentTransformer.deserialize(input);
240
+ }).toThrow(/Invalid JSON input/);
241
+ }
242
+ });
243
+ });
244
+
245
+ it('should handle very large JSON input', () => {
246
+ const largeObject = {
247
+ tag: 'ErrorTestButton',
248
+ version: '1.0.0',
249
+ data: {
250
+ largeText: 'x'.repeat(10000000) // 10MB string
251
+ }
252
+ };
253
+
254
+ expect(() => {
255
+ const jsonString = JSON.stringify(largeObject);
256
+ ComponentTransformer.deserialize(jsonString);
257
+ }).not.toThrow();
258
+ });
259
+
260
+ it('should handle deeply nested JSON structures', () => {
261
+ let deepObject: any = { tag: 'ErrorTestButton', version: '1.0.0', data: {} };
262
+
263
+ // Create 1000 levels of nesting
264
+ for (let i = 0; i < 1000; i++) {
265
+ deepObject = { nested: deepObject };
266
+ }
267
+
268
+ expect(() => {
269
+ const jsonString = JSON.stringify(deepObject);
270
+ ComponentTransformer.deserialize(jsonString);
271
+ }).not.toThrow();
272
+ });
273
+
274
+ it('should handle JSON with circular references (after JSON.stringify)', () => {
275
+ // Since JSON.stringify removes circular references, we test the behavior
276
+ const obj: any = { tag: 'ErrorTestButton', version: '1.0.0', data: {} };
277
+ obj.circular = obj;
278
+
279
+ expect(() => {
280
+ // This will throw during JSON.stringify, not during deserialize
281
+ JSON.stringify(obj);
282
+ }).toThrow();
283
+ });
284
+ });
285
+
286
+ describe('Component Implementation Errors', () => {
287
+ it('should handle component fromJson method throwing errors', () => {
288
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
289
+
290
+ const componentThatWillThrow = {
291
+ tag: 'ErrorTestButton',
292
+ version: '1.0.0',
293
+ data: { throwOnFromJson: true }
294
+ };
295
+
296
+ expect(() => {
297
+ ComponentTransformer.deserialize(componentThatWillThrow);
298
+ }).toThrow('Intentional fromJson error for testing');
299
+ });
300
+
301
+ it('should handle component returning non-ReactElement from fromJson', () => {
302
+ ComponentTransformer.registerComponent(MalformedFromJsonComponent as SerializableConstructor);
303
+
304
+ const malformedComponent = {
305
+ tag: 'MalformedFromJsonComponent',
306
+ version: '1.0.0',
307
+ data: {}
308
+ };
309
+
310
+ // This should not crash, but the result might not be a valid React element
311
+ expect(() => {
312
+ const result = ComponentTransformer.deserialize(malformedComponent);
313
+ // The result will be whatever fromJson returns, even if invalid
314
+ expect(result).toEqual({ invalid: 'return value' });
315
+ }).not.toThrow();
316
+ });
317
+
318
+ it('should handle component class without proper static methods', () => {
319
+ // Try to register a class that doesn't properly implement the interface
320
+ expect(() => {
321
+ ComponentTransformer.registerComponent('InvalidClass', InvalidComponentClass as any);
322
+ }).not.toThrow(); // Registration succeeds, but usage will fail
323
+
324
+ const invalidComponent = {
325
+ tag: 'InvalidClass',
326
+ version: '1.0.0',
327
+ data: {}
328
+ };
329
+
330
+ expect(() => {
331
+ ComponentTransformer.deserialize(invalidComponent);
332
+ }).not.toThrow(); // fromJson exists, so it will be called
333
+ });
334
+
335
+ it('should handle component toJson method throwing errors during serialization', () => {
336
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
337
+
338
+ // Create an instance that will throw during toJson
339
+ const instance = new ErrorTestButton({ throwOnToJson: true });
340
+
341
+ expect(() => {
342
+ instance.toJson();
343
+ }).toThrow('Intentional toJson error for testing');
344
+ });
345
+ });
346
+
347
+ describe('Type Safety and Validation Errors', () => {
348
+ beforeEach(() => {
349
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
350
+ });
351
+
352
+ it('should handle non-object input gracefully', () => {
353
+ const nonObjectInputs = [
354
+ 'plain string',
355
+ 42,
356
+ true,
357
+ false,
358
+ null,
359
+ undefined,
360
+ Symbol('test'),
361
+ () => {}
362
+ ];
363
+
364
+ nonObjectInputs.forEach((input, index) => {
365
+ expect(() => {
366
+ const result = ComponentTransformer.deserialize(input as any);
367
+ // Should return the input as-is for primitive values
368
+ if (input !== undefined && typeof input !== 'symbol' && typeof input !== 'function') {
369
+ expect(result).toBe(input);
370
+ }
371
+ }).not.toThrow(`Should not throw for input type ${typeof input} at index ${index}`);
372
+ });
373
+ });
374
+
375
+ it('should handle array with mixed valid and invalid elements', () => {
376
+ const mixedArray = [
377
+ 'string',
378
+ {
379
+ tag: 'ErrorTestButton',
380
+ version: '1.0.0',
381
+ data: { label: 'Valid' }
382
+ },
383
+ 42,
384
+ {
385
+ tag: 'UnknownComponent',
386
+ version: '1.0.0',
387
+ data: {}
388
+ },
389
+ null
390
+ ];
391
+
392
+ expect(() => {
393
+ ComponentTransformer.deserialize(mixedArray);
394
+ }).toThrow('Unknown component: UnknownComponent');
395
+ });
396
+
397
+ it('should handle object with component-like structure but missing required fields', () => {
398
+ const fakeComponentData = [
399
+ { tag: 'ErrorTestButton' }, // Missing version and data
400
+ { version: '1.0.0', data: {} }, // Missing tag
401
+ { tag: null, version: '1.0.0', data: {} }, // Null tag
402
+ { tag: 123, version: '1.0.0', data: {} }, // Non-string tag
403
+ ];
404
+
405
+ fakeComponentData.forEach((fake, index) => {
406
+ expect(() => {
407
+ const result = ComponentTransformer.deserialize(fake);
408
+ // Should handle gracefully - some might deserialize, others might throw
409
+ }).not.toThrow(`Fake component ${index} should not crash the system`);
410
+ });
411
+ });
412
+ });
413
+
414
+ describe('Nested Error Propagation', () => {
415
+ beforeEach(() => {
416
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
417
+ });
418
+
419
+ it('should propagate errors from deeply nested components', () => {
420
+ const deeplyNested = {
421
+ tag: 'ErrorTestButton',
422
+ version: '1.0.0',
423
+ data: {
424
+ children: [
425
+ {
426
+ tag: 'ErrorTestButton',
427
+ version: '1.0.0',
428
+ data: {
429
+ children: [
430
+ {
431
+ tag: 'UnknownDeepComponent',
432
+ version: '1.0.0',
433
+ data: {}
434
+ }
435
+ ]
436
+ }
437
+ }
438
+ ]
439
+ }
440
+ };
441
+
442
+ expect(() => {
443
+ ComponentTransformer.deserialize(deeplyNested);
444
+ }).toThrow('Unknown component: UnknownDeepComponent');
445
+ });
446
+
447
+ it('should handle partial failures in component arrays gracefully', () => {
448
+ const componentsWithError = [
449
+ {
450
+ tag: 'ErrorTestButton',
451
+ version: '1.0.0',
452
+ data: { label: 'Button 1' }
453
+ },
454
+ {
455
+ tag: 'ErrorTestButton',
456
+ version: '1.0.0',
457
+ data: { label: 'Button 2' }
458
+ },
459
+ {
460
+ tag: 'UnknownComponent',
461
+ version: '1.0.0',
462
+ data: {}
463
+ }
464
+ ];
465
+
466
+ expect(() => {
467
+ ComponentTransformer.deserialize(componentsWithError);
468
+ }).toThrow('Unknown component: UnknownComponent');
469
+ });
470
+ });
471
+
472
+ describe('Error Message Quality', () => {
473
+ it('should provide helpful error messages for common mistakes', () => {
474
+ const commonMistakes = [
475
+ {
476
+ data: { tag: 'Button', version: '1.0.0', data: {} },
477
+ expectedError: /Unknown component: Button/
478
+ },
479
+ {
480
+ data: { tag: 'ErrorTestButton', version: '1.0.0' },
481
+ expectedError: /missing 'data' property/
482
+ },
483
+ {
484
+ data: '{"invalid": json}',
485
+ expectedError: /Invalid JSON input/
486
+ }
487
+ ];
488
+
489
+ commonMistakes.forEach(({ data, expectedError }, index) => {
490
+ expect(() => {
491
+ if (index === 0) {
492
+ // Don't register the component for the first test
493
+ ComponentTransformer.deserialize(data);
494
+ } else {
495
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
496
+ ComponentTransformer.deserialize(data);
497
+ }
498
+ }).toThrow(expectedError);
499
+ });
500
+ });
501
+
502
+ it('should include context information in error messages', () => {
503
+ const contextualErrors = [
504
+ {
505
+ component: 'SpecialCharComponent!@#',
506
+ expectedInError: 'SpecialCharComponent!@#'
507
+ },
508
+ {
509
+ component: 'VeryLongComponentNameThatShouldStillBeIncludedInErrorMessage',
510
+ expectedInError: 'VeryLongComponentNameThatShouldStillBeIncludedInErrorMessage'
511
+ }
512
+ ];
513
+
514
+ contextualErrors.forEach(({ component, expectedInError }) => {
515
+ expect(() => {
516
+ ComponentTransformer.deserialize({
517
+ tag: component,
518
+ version: '1.0.0',
519
+ data: {}
520
+ });
521
+ }).toThrow(expect.stringContaining(expectedInError));
522
+ });
523
+ });
524
+ });
525
+
526
+ describe('Recovery and Graceful Degradation', () => {
527
+ beforeEach(() => {
528
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
529
+ });
530
+
531
+ it('should handle registry corruption gracefully', () => {
532
+ // Simulate registry corruption
533
+ ComponentTransformer.clearRegistry();
534
+
535
+ expect(() => {
536
+ ComponentTransformer.deserialize({
537
+ tag: 'ErrorTestButton',
538
+ version: '1.0.0',
539
+ data: { label: 'Test' }
540
+ });
541
+ }).toThrow('Unknown component: ErrorTestButton');
542
+
543
+ // Registry should still work after error
544
+ ComponentTransformer.registerComponent(ErrorTestButton as SerializableConstructor);
545
+ expect(() => {
546
+ const result = ComponentTransformer.deserialize({
547
+ tag: 'ErrorTestButton',
548
+ version: '1.0.0',
549
+ data: { label: 'Recovery Test' }
550
+ });
551
+ expect(React.isValidElement(result)).toBe(true);
552
+ }).not.toThrow();
553
+ });
554
+
555
+ it('should maintain component registry state after errors', () => {
556
+ // Verify initial state
557
+ const initialComponents = ComponentTransformer.getRegisteredComponents();
558
+ expect(initialComponents).toContain('ErrorTestButton');
559
+
560
+ // Cause an error
561
+ expect(() => {
562
+ ComponentTransformer.deserialize({
563
+ tag: 'UnknownComponent',
564
+ version: '1.0.0',
565
+ data: {}
566
+ });
567
+ }).toThrow();
568
+
569
+ // Verify registry is unchanged after error
570
+ const componentsAfterError = ComponentTransformer.getRegisteredComponents();
571
+ expect(componentsAfterError).toEqual(initialComponents);
572
+ expect(componentsAfterError).toContain('ErrorTestButton');
573
+ });
574
+
575
+ it('should handle memory pressure during error conditions', () => {
576
+ // Create memory pressure with large failing operations
577
+ const largeFailingOperations = Array.from({ length: 100 }, (_, i) => ({
578
+ tag: `UnknownComponent${i}`,
579
+ version: '1.0.0',
580
+ data: {
581
+ largeData: 'x'.repeat(1000)
582
+ }
583
+ }));
584
+
585
+ largeFailingOperations.forEach((operation) => {
586
+ expect(() => {
587
+ ComponentTransformer.deserialize(operation);
588
+ }).toThrow(/Unknown component/);
589
+ });
590
+
591
+ // System should still be responsive
592
+ expect(() => {
593
+ const result = ComponentTransformer.deserialize({
594
+ tag: 'ErrorTestButton',
595
+ version: '1.0.0',
596
+ data: { label: 'After Memory Pressure' }
597
+ });
598
+ expect(React.isValidElement(result)).toBe(true);
599
+ }).not.toThrow();
600
+ });
601
+ });
602
+ });