@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,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
+ });