@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.
- package/README.md +1688 -2
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +7 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/Html.d.ts +28 -18
- package/dist/components/Html.d.ts.map +1 -1
- package/dist/components/Logo.d.ts +12 -35
- package/dist/components/Logo.d.ts.map +1 -1
- package/dist/components/Markdown.d.ts +18 -13
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/QwickApp.d.ts +16 -3
- package/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/QwickIcon.d.ts +23 -0
- package/dist/components/QwickIcon.d.ts.map +1 -0
- package/dist/components/SafeSpan.d.ts +12 -5
- package/dist/components/SafeSpan.d.ts.map +1 -1
- package/dist/components/Scaffold.d.ts.map +1 -1
- package/dist/components/base/ModelView.d.ts +101 -0
- package/dist/components/base/ModelView.d.ts.map +1 -0
- package/dist/components/base/index.d.ts +11 -0
- package/dist/components/base/index.d.ts.map +1 -0
- package/dist/components/blocks/Article.d.ts +12 -2
- package/dist/components/blocks/Article.d.ts.map +1 -1
- package/dist/components/blocks/Code.d.ts +13 -2
- package/dist/components/blocks/Code.d.ts.map +1 -1
- package/dist/components/blocks/Content.d.ts.map +1 -1
- package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
- package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
- package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
- package/dist/components/blocks/Footer.d.ts.map +1 -1
- package/dist/components/blocks/HeroBlock.d.ts +27 -13
- package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
- package/dist/components/blocks/Image.d.ts +41 -0
- package/dist/components/blocks/Image.d.ts.map +1 -0
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
- package/dist/components/blocks/ProductCard.d.ts.map +1 -1
- package/dist/components/blocks/Section.d.ts +16 -2
- package/dist/components/blocks/Section.d.ts.map +1 -1
- package/dist/components/blocks/Text.d.ts +41 -0
- package/dist/components/blocks/Text.d.ts.map +1 -0
- package/dist/components/blocks/index.d.ts +4 -0
- package/dist/components/blocks/index.d.ts.map +1 -1
- package/dist/components/buttons/Button.d.ts +23 -7
- package/dist/components/buttons/Button.d.ts.map +1 -1
- package/dist/components/forms/FormBlock.d.ts +19 -13
- package/dist/components/forms/FormBlock.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/ChoiceInputField.d.ts +17 -11
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
- package/dist/components/input/HtmlInputField.d.ts +17 -11
- package/dist/components/input/HtmlInputField.d.ts.map +1 -1
- package/dist/components/input/SelectInputField.d.ts +16 -10
- package/dist/components/input/SelectInputField.d.ts.map +1 -1
- package/dist/components/input/SwitchInputField.d.ts +16 -10
- package/dist/components/input/SwitchInputField.d.ts.map +1 -1
- package/dist/components/input/TextField.d.ts.map +1 -1
- package/dist/components/input/TextInputField.d.ts +16 -11
- package/dist/components/input/TextInputField.d.ts.map +1 -1
- package/dist/components/layout/GridCell.d.ts +23 -6
- package/dist/components/layout/GridCell.d.ts.map +1 -1
- package/dist/components/layout/GridLayout.d.ts +24 -23
- package/dist/components/layout/GridLayout.d.ts.map +1 -1
- package/dist/components/pages/FormPage.d.ts.map +1 -1
- package/dist/components/pages/Page.d.ts +49 -87
- package/dist/components/pages/Page.d.ts.map +1 -1
- package/dist/components/pages/index.d.ts +2 -2
- package/dist/components/pages/index.d.ts.map +1 -1
- package/dist/config/AppConfig.d.ts +49 -0
- package/dist/config/AppConfig.d.ts.map +1 -0
- package/dist/config/AppConfigBuilder.d.ts +75 -0
- package/dist/config/AppConfigBuilder.d.ts.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/types.d.ts +130 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.esm.js +451 -0
- package/dist/config.js +455 -0
- package/dist/contexts/PrintModeContext.d.ts +27 -0
- package/dist/contexts/PrintModeContext.d.ts.map +1 -0
- package/dist/contexts/QwickAppContext.d.ts +2 -2
- package/dist/contexts/QwickAppContext.d.ts.map +1 -1
- package/dist/contexts/ThemeContext.d.ts.map +1 -1
- package/dist/contexts/index.d.ts +2 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/usePrintMode.d.ts +39 -0
- package/dist/hooks/usePrintMode.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +20722 -16021
- package/dist/index.js +20725 -16010
- package/dist/schemas/CodeSchema.d.ts +2 -1
- package/dist/schemas/CodeSchema.d.ts.map +1 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
- package/dist/schemas/ContentSchema.d.ts +2 -1
- package/dist/schemas/ContentSchema.d.ts.map +1 -1
- package/dist/schemas/GridCellSchema.d.ts +25 -0
- package/dist/schemas/GridCellSchema.d.ts.map +1 -0
- package/dist/schemas/GridLayoutSchema.d.ts +23 -0
- package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
- package/dist/schemas/HtmlSchema.d.ts +14 -0
- package/dist/schemas/HtmlSchema.d.ts.map +1 -0
- package/dist/schemas/ImageSchema.d.ts +32 -0
- package/dist/schemas/ImageSchema.d.ts.map +1 -0
- package/dist/schemas/LogoSchema.d.ts +35 -0
- package/dist/schemas/LogoSchema.d.ts.map +1 -0
- package/dist/schemas/MarkdownSchema.d.ts +14 -0
- package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
- package/dist/schemas/PageTemplateSchema.d.ts +31 -0
- package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
- package/dist/schemas/PrintConfigSchema.d.ts +31 -0
- package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
- package/dist/schemas/SectionSchema.d.ts +2 -1
- package/dist/schemas/SectionSchema.d.ts.map +1 -1
- package/dist/schemas/TextSchema.d.ts +37 -0
- package/dist/schemas/TextSchema.d.ts.map +1 -0
- package/dist/schemas/ViewModelSchema.d.ts +23 -0
- package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +15 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
- package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
- package/dist/schemas/transformers/registry.d.ts +15 -0
- package/dist/schemas/transformers/registry.d.ts.map +1 -0
- package/dist/schemas/types/Serializable.d.ts +46 -0
- package/dist/schemas/types/Serializable.d.ts.map +1 -0
- package/dist/utils/htmlTransform.d.ts.map +1 -1
- package/dist/utils/reactUtils.d.ts +12 -3
- package/dist/utils/reactUtils.d.ts.map +1 -1
- package/package.json +17 -3
- package/src/{components/__tests__ β __tests__/components}/AccessibilityProvider.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Article.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Breadcrumbs.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Button.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/CardListGrid.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/ChoiceInputField.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Code.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Content.integration.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Content.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/CoverImageHeader.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/ErrorBoundary.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/FeatureCard.integration.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/FeatureGrid.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/Footer.test.tsx +4 -4
- package/src/{components/__tests__ β __tests__/components}/FormBlock.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/HeroBlock.integration.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/HeroBlock.test.tsx +233 -7
- package/src/{components/__tests__ β __tests__/components}/Html.test.tsx +11 -2
- package/src/{components/__tests__ β __tests__/components}/HtmlInputField.test.tsx +3 -3
- package/src/__tests__/components/Logo.test.js +3 -3
- package/src/{components/__tests__ β __tests__/components}/Markdown.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/PageBannerHeader.test.tsx +3 -3
- package/src/{components/__tests__ β __tests__/components}/PaletteSwitcher.test.tsx +3 -3
- package/src/{components/__tests__ β __tests__/components}/ProductCard.test.tsx +4 -4
- package/src/{components/__tests__ β __tests__/components}/SafeSpan.integration.test.tsx +2 -2
- package/src/{components/__tests__ β __tests__/components}/SafeSpan.simple.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/SafeSpan.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Section.integration.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/Section.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/SelectInputField.test.tsx +1 -1
- package/src/{components/__tests__ β __tests__/components}/TextInputField.test.tsx +3 -3
- package/src/{components/__tests__ β __tests__/components}/ThemeSwitcher.test.tsx +3 -3
- package/src/__tests__/components/base/ModelView.test.tsx +220 -0
- package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
- package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
- package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
- package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
- package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
- package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
- package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
- package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
- package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
- package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
- package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
- package/src/{components/layout/CollapsibleLayout/__tests__ β __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
- package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
- package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
- package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
- package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
- package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
- package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
- package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
- package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
- package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
- package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
- package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
- package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
- package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
- package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
- package/src/{utils/__tests__ β __tests__/utils}/nested-dom-fix.test.tsx +1 -1
- package/src/components/ErrorBoundary.tsx +8 -8
- package/src/components/Html.tsx +147 -44
- package/src/components/Logo.tsx +198 -100
- package/src/components/Markdown.tsx +125 -16
- package/src/components/QwickApp.tsx +64 -31
- package/src/components/QwickIcon.tsx +59 -0
- package/src/components/SafeSpan.tsx +65 -10
- package/src/components/Scaffold.tsx +2 -8
- package/src/components/base/ModelView.tsx +199 -0
- package/src/components/base/index.ts +11 -0
- package/src/components/blocks/Article.tsx +57 -18
- package/src/components/blocks/Code.md +529 -0
- package/src/components/blocks/Code.tsx +102 -15
- package/src/components/blocks/Content.tsx +25 -77
- package/src/components/blocks/CoverImageHeader.tsx +9 -4
- package/src/components/blocks/FeatureCard.tsx +1 -2
- package/src/components/blocks/FeatureGrid.tsx +19 -1
- package/src/components/blocks/Footer.tsx +13 -1
- package/src/components/blocks/HeroBlock.tsx +87 -20
- package/src/components/blocks/Image.tsx +395 -0
- package/src/components/blocks/PageBannerHeader.tsx +14 -12
- package/src/components/blocks/ProductCard.tsx +51 -52
- package/src/components/blocks/Section.tsx +113 -8
- package/src/components/blocks/Text.tsx +285 -0
- package/src/components/blocks/index.ts +4 -0
- package/src/components/buttons/Button.tsx +184 -15
- package/src/components/forms/FormBlock.tsx +70 -17
- package/src/components/index.ts +5 -0
- package/src/components/input/ChoiceInputField.tsx +48 -18
- package/src/components/input/HtmlInputField.tsx +48 -18
- package/src/components/input/SelectInputField.tsx +48 -16
- package/src/components/input/SwitchInputField.tsx +48 -17
- package/src/components/input/TextField.tsx +41 -1
- package/src/components/input/TextInputField.tsx +52 -18
- package/src/components/layout/GridCell.tsx +118 -9
- package/src/components/layout/GridLayout.tsx +125 -24
- package/src/components/pages/FormPage.tsx +0 -1
- package/src/components/pages/Page.css +304 -332
- package/src/components/pages/Page.tsx +307 -255
- package/src/components/pages/index.ts +2 -2
- package/src/config/AppConfig.ts +133 -0
- package/src/config/AppConfigBuilder.ts +421 -0
- package/src/config/__tests__/AppConfig.test.ts +385 -0
- package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
- package/src/config/index.ts +24 -0
- package/src/config/types.ts +170 -0
- package/src/config.ts +25 -0
- package/src/contexts/PrintModeContext.tsx +332 -0
- package/src/contexts/QwickAppContext.tsx +2 -2
- package/src/contexts/ThemeContext.tsx +1 -2
- package/src/contexts/index.ts +2 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/usePrintMode.ts +73 -0
- package/src/index.ts +3 -0
- package/src/schemas/CodeSchema.ts +3 -3
- package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
- package/src/schemas/ContentSchema.ts +2 -1
- package/src/schemas/GridCellSchema.ts +164 -0
- package/src/schemas/GridLayoutSchema.ts +133 -0
- package/src/schemas/HtmlSchema.ts +47 -0
- package/src/schemas/ImageSchema.ts +235 -0
- package/src/schemas/LogoSchema.ts +241 -0
- package/src/schemas/MarkdownSchema.ts +47 -0
- package/src/schemas/PageTemplateSchema.ts +186 -0
- package/src/schemas/PrintConfigSchema.ts +207 -0
- package/src/schemas/README.md +661 -0
- package/src/schemas/SectionSchema.ts +2 -1
- package/src/schemas/TextSchema.ts +329 -0
- package/src/schemas/ViewModelSchema.ts +115 -0
- package/src/schemas/index.ts +21 -2
- package/src/schemas/transformers/ComponentTransformer.ts +403 -0
- package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
- package/src/schemas/transformers/registry.ts +72 -0
- package/src/schemas/types/Serializable.ts +51 -0
- package/src/stories/AccessibilityProvider.stories.tsx +253 -253
- package/src/stories/Article.stories.tsx +433 -433
- package/src/stories/Button.stories.tsx +1 -1
- package/src/stories/CardListGrid.stories.tsx +451 -451
- package/src/stories/ChoiceInputField.stories.tsx +503 -503
- package/src/stories/Code.stories.tsx +1 -1
- package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
- package/src/stories/Content.stories.tsx +393 -393
- package/src/stories/CoverImageHeader.stories.tsx +701 -701
- package/src/stories/DataBinding.advanced.stories.tsx +432 -432
- package/src/stories/DataProvider.stories.tsx +1192 -1192
- package/src/stories/FeatureCard.stories.tsx +557 -557
- package/src/stories/FeatureGrid.stories.tsx +594 -594
- package/src/stories/Footer.stories.tsx +640 -640
- package/src/stories/FormBlock.stories.tsx +760 -760
- package/src/stories/FormComponents.stories.tsx +349 -541
- package/src/stories/GridCell.stories.tsx +417 -0
- package/src/stories/GridLayout.stories.tsx +353 -0
- package/src/stories/HeroBlock.stories.tsx +862 -373
- package/src/stories/HtmlInputField.stories.tsx +474 -474
- package/src/stories/Image.stories.tsx +819 -0
- package/src/stories/Introduction.stories.tsx +667 -667
- package/src/stories/LayoutBlocks.stories.tsx +324 -324
- package/src/stories/Logo.stories.tsx +165 -6
- package/src/stories/Markdown.stories.tsx +137 -137
- package/src/stories/ModelView.stories.tsx +477 -0
- package/src/stories/Page.stories.tsx +688 -688
- package/src/stories/PageBannerHeader.stories.tsx +864 -864
- package/src/stories/PaletteSwitcher.stories.tsx +119 -119
- package/src/stories/ProductCard.stories.tsx +424 -424
- package/src/stories/QwickApp.stories.tsx +368 -368
- package/src/stories/ResponsiveMenu.stories.tsx +249 -249
- package/src/stories/SafeSpan.stories.tsx +531 -531
- package/src/stories/Section.stories.tsx +90 -2
- package/src/stories/SelectInputField.stories.tsx +524 -524
- package/src/stories/Text.stories.tsx +560 -0
- package/src/stories/TextInputField.stories.tsx +443 -443
- package/src/stories/ThemeSwitcher.stories.tsx +123 -123
- package/src/utils/htmlTransform.tsx +74 -53
- package/src/utils/reactUtils.tsx +57 -6
- package/dist/index.bundled.css +0 -12
- /package/src/{hooks/__tests__ β __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
- /package/src/{schemas/__tests__ β __tests__/schemas}/builders.test.ts +0 -0
- /package/src/{utils/__tests__ β __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
- /package/src/{utils/__tests__ β __tests__/utils}/htmlTransform.test.tsx +0 -0
- /package/src/{utils/__tests__ β __tests__/utils}/optional-logging.test.ts +0 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Serialization Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive integration testing for the entire serialization system
|
|
5
|
+
* covering complex scenarios, nested components, and real-world usage patterns.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React, { ReactElement } from 'react';
|
|
11
|
+
import { ComponentTransformer } from '../ComponentTransformer';
|
|
12
|
+
import { Serializable, SerializableConstructor } from '../../types/Serializable';
|
|
13
|
+
|
|
14
|
+
// Mock ErrorBoundary for testing unknown component handling
|
|
15
|
+
class MockErrorBoundary extends React.Component<
|
|
16
|
+
{ children: React.ReactNode; onError?: (error: Error) => void },
|
|
17
|
+
{ hasError: boolean; error?: Error }
|
|
18
|
+
> {
|
|
19
|
+
constructor(props: any) {
|
|
20
|
+
super(props);
|
|
21
|
+
this.state = { hasError: false };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static getDerivedStateFromError(error: Error) {
|
|
25
|
+
return { hasError: true, error };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
componentDidCatch(error: Error) {
|
|
29
|
+
this.props.onError?.(error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render() {
|
|
33
|
+
if (this.state.hasError) {
|
|
34
|
+
return React.createElement('div', {
|
|
35
|
+
'data-testid': 'error-boundary',
|
|
36
|
+
'data-error': this.state.error?.message
|
|
37
|
+
}, 'Component Error');
|
|
38
|
+
}
|
|
39
|
+
return this.props.children;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Complex Mock Components for Integration Testing
|
|
44
|
+
class MockButton implements Serializable {
|
|
45
|
+
constructor(public props: {
|
|
46
|
+
label?: string;
|
|
47
|
+
variant?: 'primary' | 'secondary' | 'danger';
|
|
48
|
+
onClick?: () => void;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
children?: any;
|
|
51
|
+
}) {}
|
|
52
|
+
|
|
53
|
+
static fromJson(jsonData: any): ReactElement {
|
|
54
|
+
return React.createElement('button', {
|
|
55
|
+
className: `btn btn-${jsonData.variant || 'primary'}`,
|
|
56
|
+
disabled: jsonData.disabled,
|
|
57
|
+
onClick: jsonData.onClick,
|
|
58
|
+
'data-testid': 'mock-button'
|
|
59
|
+
}, jsonData.label || jsonData.children || 'Button');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
toJson(): any {
|
|
63
|
+
return {
|
|
64
|
+
label: this.props.label,
|
|
65
|
+
variant: this.props.variant,
|
|
66
|
+
disabled: this.props.disabled,
|
|
67
|
+
children: this.props.children,
|
|
68
|
+
onClick: this.props.onClick ? 'function' : undefined
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class MockSection implements Serializable {
|
|
74
|
+
constructor(public props: {
|
|
75
|
+
title?: string;
|
|
76
|
+
className?: string;
|
|
77
|
+
children?: any;
|
|
78
|
+
background?: string;
|
|
79
|
+
}) {}
|
|
80
|
+
|
|
81
|
+
static fromJson(jsonData: any): ReactElement {
|
|
82
|
+
// Section components often need to deserialize their children
|
|
83
|
+
const children = jsonData.children ?
|
|
84
|
+
ComponentTransformer.deserialize(jsonData.children) : null;
|
|
85
|
+
|
|
86
|
+
return React.createElement('section', {
|
|
87
|
+
className: jsonData.className || 'section',
|
|
88
|
+
style: jsonData.background ? { backgroundColor: jsonData.background } : undefined,
|
|
89
|
+
'data-testid': 'mock-section'
|
|
90
|
+
}, [
|
|
91
|
+
jsonData.title ? React.createElement('h2', { key: 'title' }, jsonData.title) : null,
|
|
92
|
+
children ? React.createElement('div', { key: 'content', className: 'content' }, children) : null
|
|
93
|
+
].filter(Boolean));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
toJson(): any {
|
|
97
|
+
return {
|
|
98
|
+
title: this.props.title,
|
|
99
|
+
className: this.props.className,
|
|
100
|
+
background: this.props.background,
|
|
101
|
+
children: this.props.children ? ComponentTransformer.serialize(this.props.children) : null
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class MockCard implements Serializable {
|
|
107
|
+
constructor(public props: {
|
|
108
|
+
title?: string;
|
|
109
|
+
content?: string;
|
|
110
|
+
image?: string;
|
|
111
|
+
actions?: any[];
|
|
112
|
+
metadata?: Record<string, any>;
|
|
113
|
+
}) {}
|
|
114
|
+
|
|
115
|
+
static fromJson(jsonData: any): ReactElement {
|
|
116
|
+
const actions = jsonData.actions ?
|
|
117
|
+
ComponentTransformer.deserialize(jsonData.actions) : null;
|
|
118
|
+
|
|
119
|
+
return React.createElement('div', {
|
|
120
|
+
className: 'card',
|
|
121
|
+
'data-testid': 'mock-card'
|
|
122
|
+
}, [
|
|
123
|
+
jsonData.image ? React.createElement('img', {
|
|
124
|
+
key: 'image',
|
|
125
|
+
src: jsonData.image,
|
|
126
|
+
alt: jsonData.title || 'Card image'
|
|
127
|
+
}) : null,
|
|
128
|
+
React.createElement('div', { key: 'body', className: 'card-body' }, [
|
|
129
|
+
jsonData.title ? React.createElement('h3', { key: 'title' }, jsonData.title) : null,
|
|
130
|
+
jsonData.content ? React.createElement('p', { key: 'content' }, jsonData.content) : null,
|
|
131
|
+
jsonData.metadata ? React.createElement('pre', {
|
|
132
|
+
key: 'metadata',
|
|
133
|
+
className: 'metadata'
|
|
134
|
+
}, JSON.stringify(jsonData.metadata, null, 2)) : null,
|
|
135
|
+
actions ? React.createElement('div', { key: 'actions', className: 'actions' }, actions) : null
|
|
136
|
+
].filter(Boolean))
|
|
137
|
+
].filter(Boolean));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
toJson(): any {
|
|
141
|
+
return {
|
|
142
|
+
title: this.props.title,
|
|
143
|
+
content: this.props.content,
|
|
144
|
+
image: this.props.image,
|
|
145
|
+
metadata: this.props.metadata,
|
|
146
|
+
actions: this.props.actions ? ComponentTransformer.serialize(this.props.actions) : null
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class MockCode implements Serializable {
|
|
152
|
+
constructor(public props: {
|
|
153
|
+
code?: string;
|
|
154
|
+
language?: string;
|
|
155
|
+
showLineNumbers?: boolean;
|
|
156
|
+
highlightLines?: number[];
|
|
157
|
+
}) {}
|
|
158
|
+
|
|
159
|
+
static fromJson(jsonData: any): ReactElement {
|
|
160
|
+
return React.createElement('pre', {
|
|
161
|
+
className: `code-block language-${jsonData.language || 'text'}`,
|
|
162
|
+
'data-testid': 'mock-code',
|
|
163
|
+
'data-language': jsonData.language,
|
|
164
|
+
'data-line-numbers': jsonData.showLineNumbers
|
|
165
|
+
}, React.createElement('code', {}, jsonData.code || ''));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
toJson(): any {
|
|
169
|
+
return {
|
|
170
|
+
code: this.props.code,
|
|
171
|
+
language: this.props.language,
|
|
172
|
+
showLineNumbers: this.props.showLineNumbers,
|
|
173
|
+
highlightLines: this.props.highlightLines
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
describe('Component Serialization Integration Tests', () => {
|
|
179
|
+
beforeEach(() => {
|
|
180
|
+
ComponentTransformer.clearRegistry();
|
|
181
|
+
ComponentTransformer.registerComponent('Button', MockButton as SerializableConstructor);
|
|
182
|
+
ComponentTransformer.registerComponent('Section', MockSection as SerializableConstructor);
|
|
183
|
+
ComponentTransformer.registerComponent('Card', MockCard as SerializableConstructor);
|
|
184
|
+
ComponentTransformer.registerComponent('Code', MockCode as SerializableConstructor);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
afterEach(() => {
|
|
188
|
+
ComponentTransformer.clearRegistry();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('Multi-Component Serialization', () => {
|
|
192
|
+
it('should serialize and deserialize array of different component types', () => {
|
|
193
|
+
const components = [
|
|
194
|
+
{
|
|
195
|
+
tag: 'Button',
|
|
196
|
+
version: '1.0.0',
|
|
197
|
+
data: { label: 'Click Me', variant: 'primary' }
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
tag: 'Card',
|
|
201
|
+
version: '1.0.0',
|
|
202
|
+
data: {
|
|
203
|
+
title: 'Test Card',
|
|
204
|
+
content: 'Card content',
|
|
205
|
+
metadata: { author: 'Test User', date: '2025-01-01' }
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
tag: 'Code',
|
|
210
|
+
version: '1.0.0',
|
|
211
|
+
data: {
|
|
212
|
+
code: 'const x = 1;',
|
|
213
|
+
language: 'javascript',
|
|
214
|
+
showLineNumbers: true
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const result = ComponentTransformer.deserialize(components);
|
|
220
|
+
expect(Array.isArray(result)).toBe(true);
|
|
221
|
+
|
|
222
|
+
const elements = result as ReactElement[];
|
|
223
|
+
expect(elements).toHaveLength(3);
|
|
224
|
+
|
|
225
|
+
// Verify each component type
|
|
226
|
+
expect(React.isValidElement(elements[0])).toBe(true);
|
|
227
|
+
expect(elements[0].props['data-testid']).toBe('mock-button');
|
|
228
|
+
expect(elements[0].props.className).toBe('btn btn-primary');
|
|
229
|
+
|
|
230
|
+
expect(React.isValidElement(elements[1])).toBe(true);
|
|
231
|
+
expect(elements[1].props['data-testid']).toBe('mock-card');
|
|
232
|
+
|
|
233
|
+
expect(React.isValidElement(elements[2])).toBe(true);
|
|
234
|
+
expect(elements[2].props['data-testid']).toBe('mock-code');
|
|
235
|
+
expect(elements[2].props['data-language']).toBe('javascript');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle mixed content arrays with primitives and components', () => {
|
|
239
|
+
const mixedContent = [
|
|
240
|
+
'Plain text content',
|
|
241
|
+
{
|
|
242
|
+
tag: 'Button',
|
|
243
|
+
version: '1.0.0',
|
|
244
|
+
data: { label: 'Action Button' }
|
|
245
|
+
},
|
|
246
|
+
42,
|
|
247
|
+
{
|
|
248
|
+
tag: 'Code',
|
|
249
|
+
version: '1.0.0',
|
|
250
|
+
data: { code: 'console.log("Hello");', language: 'javascript' }
|
|
251
|
+
},
|
|
252
|
+
null,
|
|
253
|
+
true
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
const result = ComponentTransformer.deserialize(mixedContent);
|
|
257
|
+
expect(Array.isArray(result)).toBe(true);
|
|
258
|
+
|
|
259
|
+
const elements = result as any[];
|
|
260
|
+
expect(elements).toHaveLength(6);
|
|
261
|
+
expect(elements[0]).toBe('Plain text content');
|
|
262
|
+
expect(React.isValidElement(elements[1])).toBe(true);
|
|
263
|
+
expect(elements[2]).toBe(42);
|
|
264
|
+
expect(React.isValidElement(elements[3])).toBe(true);
|
|
265
|
+
expect(elements[4]).toBeNull();
|
|
266
|
+
expect(elements[5]).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('Nested Component Scenarios', () => {
|
|
271
|
+
it('should handle deeply nested component structures', () => {
|
|
272
|
+
const nestedStructure = {
|
|
273
|
+
tag: 'Section',
|
|
274
|
+
version: '1.0.0',
|
|
275
|
+
data: {
|
|
276
|
+
title: 'Main Section',
|
|
277
|
+
className: 'hero-section',
|
|
278
|
+
background: '#f0f0f0',
|
|
279
|
+
children: [
|
|
280
|
+
{
|
|
281
|
+
tag: 'Card',
|
|
282
|
+
version: '1.0.0',
|
|
283
|
+
data: {
|
|
284
|
+
title: 'Feature Card',
|
|
285
|
+
content: 'This card contains actions',
|
|
286
|
+
actions: [
|
|
287
|
+
{
|
|
288
|
+
tag: 'Button',
|
|
289
|
+
version: '1.0.0',
|
|
290
|
+
data: { label: 'Learn More', variant: 'primary' }
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
tag: 'Button',
|
|
294
|
+
version: '1.0.0',
|
|
295
|
+
data: { label: 'Try Now', variant: 'secondary' }
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
tag: 'Code',
|
|
302
|
+
version: '1.0.0',
|
|
303
|
+
data: {
|
|
304
|
+
code: 'function example() {\n return "nested code";\n}',
|
|
305
|
+
language: 'javascript',
|
|
306
|
+
showLineNumbers: true
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const result = ComponentTransformer.deserialize(nestedStructure);
|
|
314
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
315
|
+
|
|
316
|
+
const element = result as ReactElement;
|
|
317
|
+
expect(element.props['data-testid']).toBe('mock-section');
|
|
318
|
+
expect(element.props.className).toBe('hero-section');
|
|
319
|
+
expect(element.props.style?.backgroundColor).toBe('#f0f0f0');
|
|
320
|
+
|
|
321
|
+
// Verify nested structure exists
|
|
322
|
+
expect(Array.isArray(element.props.children)).toBe(true);
|
|
323
|
+
const sectionChildren = element.props.children.filter(Boolean);
|
|
324
|
+
expect(sectionChildren).toHaveLength(2); // title + content
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should preserve component hierarchy through roundtrip serialization', () => {
|
|
328
|
+
const originalData = {
|
|
329
|
+
tag: 'Section',
|
|
330
|
+
version: '1.0.0',
|
|
331
|
+
data: {
|
|
332
|
+
title: 'Test Section',
|
|
333
|
+
children: [
|
|
334
|
+
{
|
|
335
|
+
tag: 'Card',
|
|
336
|
+
version: '1.0.0',
|
|
337
|
+
data: {
|
|
338
|
+
title: 'Nested Card',
|
|
339
|
+
actions: [
|
|
340
|
+
{
|
|
341
|
+
tag: 'Button',
|
|
342
|
+
version: '1.0.0',
|
|
343
|
+
data: { label: 'Action 1' }
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Deserialize to React elements
|
|
353
|
+
const reactElements = ComponentTransformer.deserialize(originalData);
|
|
354
|
+
|
|
355
|
+
// Serialize back to JSON
|
|
356
|
+
const serialized = ComponentTransformer.serialize(reactElements);
|
|
357
|
+
const parsedSerialized = JSON.parse(serialized);
|
|
358
|
+
|
|
359
|
+
// Deserialize again
|
|
360
|
+
const finalResult = ComponentTransformer.deserialize(parsedSerialized);
|
|
361
|
+
|
|
362
|
+
expect(React.isValidElement(finalResult)).toBe(true);
|
|
363
|
+
const element = finalResult as ReactElement;
|
|
364
|
+
expect(element.props['data-testid']).toBe('mock-section');
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should handle circular delegation correctly', () => {
|
|
368
|
+
// This tests the scenario where components call ComponentTransformer.deserialize
|
|
369
|
+
// for their children, ensuring no infinite loops or stack overflows
|
|
370
|
+
const circularTestData = {
|
|
371
|
+
tag: 'Section',
|
|
372
|
+
version: '1.0.0',
|
|
373
|
+
data: {
|
|
374
|
+
children: [
|
|
375
|
+
{
|
|
376
|
+
tag: 'Section',
|
|
377
|
+
version: '1.0.0',
|
|
378
|
+
data: {
|
|
379
|
+
children: [
|
|
380
|
+
{
|
|
381
|
+
tag: 'Button',
|
|
382
|
+
version: '1.0.0',
|
|
383
|
+
data: { label: 'Deep Button' }
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
expect(() => {
|
|
393
|
+
const result = ComponentTransformer.deserialize(circularTestData);
|
|
394
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
395
|
+
}).not.toThrow();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
describe('Real-World Page Structures', () => {
|
|
400
|
+
it('should handle complex page layout with mixed components', () => {
|
|
401
|
+
const pageStructure = [
|
|
402
|
+
{
|
|
403
|
+
tag: 'Section',
|
|
404
|
+
version: '1.0.0',
|
|
405
|
+
data: {
|
|
406
|
+
title: 'Header Section',
|
|
407
|
+
className: 'header',
|
|
408
|
+
children: [
|
|
409
|
+
{
|
|
410
|
+
tag: 'Button',
|
|
411
|
+
version: '1.0.0',
|
|
412
|
+
data: { label: 'Get Started', variant: 'primary' }
|
|
413
|
+
}
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
tag: 'Section',
|
|
419
|
+
version: '1.0.0',
|
|
420
|
+
data: {
|
|
421
|
+
title: 'Features',
|
|
422
|
+
className: 'features',
|
|
423
|
+
children: [
|
|
424
|
+
{
|
|
425
|
+
tag: 'Card',
|
|
426
|
+
version: '1.0.0',
|
|
427
|
+
data: {
|
|
428
|
+
title: 'Fast Performance',
|
|
429
|
+
content: 'Optimized for speed',
|
|
430
|
+
actions: [
|
|
431
|
+
{
|
|
432
|
+
tag: 'Button',
|
|
433
|
+
version: '1.0.0',
|
|
434
|
+
data: { label: 'Learn More' }
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
tag: 'Card',
|
|
441
|
+
version: '1.0.0',
|
|
442
|
+
data: {
|
|
443
|
+
title: 'Easy Integration',
|
|
444
|
+
content: 'Simple to implement',
|
|
445
|
+
actions: [
|
|
446
|
+
{
|
|
447
|
+
tag: 'Code',
|
|
448
|
+
version: '1.0.0',
|
|
449
|
+
data: {
|
|
450
|
+
code: 'npm install @qwickapps/react-framework',
|
|
451
|
+
language: 'bash'
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
]
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
const result = ComponentTransformer.deserialize(pageStructure);
|
|
463
|
+
expect(Array.isArray(result)).toBe(true);
|
|
464
|
+
|
|
465
|
+
const sections = result as ReactElement[];
|
|
466
|
+
expect(sections).toHaveLength(2);
|
|
467
|
+
|
|
468
|
+
// Verify first section (header)
|
|
469
|
+
expect(sections[0].props['data-testid']).toBe('mock-section');
|
|
470
|
+
expect(sections[0].props.className).toBe('header');
|
|
471
|
+
|
|
472
|
+
// Verify second section (features)
|
|
473
|
+
expect(sections[1].props['data-testid']).toBe('mock-section');
|
|
474
|
+
expect(sections[1].props.className).toBe('features');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should handle empty and null children gracefully', () => {
|
|
478
|
+
const structureWithEmpties = {
|
|
479
|
+
tag: 'Section',
|
|
480
|
+
version: '1.0.0',
|
|
481
|
+
data: {
|
|
482
|
+
title: 'Test Section',
|
|
483
|
+
children: [
|
|
484
|
+
null,
|
|
485
|
+
{
|
|
486
|
+
tag: 'Card',
|
|
487
|
+
version: '1.0.0',
|
|
488
|
+
data: {
|
|
489
|
+
title: 'Valid Card',
|
|
490
|
+
actions: []
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
undefined,
|
|
494
|
+
{
|
|
495
|
+
tag: 'Section',
|
|
496
|
+
version: '1.0.0',
|
|
497
|
+
data: {
|
|
498
|
+
children: null
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
expect(() => {
|
|
506
|
+
const result = ComponentTransformer.deserialize(structureWithEmpties);
|
|
507
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
508
|
+
}).not.toThrow();
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
describe('Error Boundary Integration', () => {
|
|
513
|
+
it('should trigger error boundary for unknown components', () => {
|
|
514
|
+
const unknownComponent = {
|
|
515
|
+
tag: 'UnknownWidget',
|
|
516
|
+
version: '1.0.0',
|
|
517
|
+
data: { prop: 'value' }
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
let capturedError: Error | null = null;
|
|
521
|
+
const onError = (error: Error) => {
|
|
522
|
+
capturedError = error;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// Wrap in error boundary and attempt deserialization
|
|
526
|
+
expect(() => {
|
|
527
|
+
try {
|
|
528
|
+
ComponentTransformer.deserialize(unknownComponent);
|
|
529
|
+
} catch (error) {
|
|
530
|
+
capturedError = error as Error;
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
533
|
+
}).toThrow('Unknown component: UnknownWidget');
|
|
534
|
+
|
|
535
|
+
expect(capturedError?.message).toContain('Unknown component: UnknownWidget');
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('should handle malformed component data gracefully', () => {
|
|
539
|
+
const malformedComponents = [
|
|
540
|
+
{ tag: 'Button' }, // Missing version and data
|
|
541
|
+
{ tag: 'Button', version: '1.0.0' }, // Missing data
|
|
542
|
+
{ tag: 'Button', version: '1.0.0', data: null }, // Null data
|
|
543
|
+
];
|
|
544
|
+
|
|
545
|
+
malformedComponents.forEach((malformed, index) => {
|
|
546
|
+
expect(() => {
|
|
547
|
+
ComponentTransformer.deserialize(malformed);
|
|
548
|
+
}).toThrow(/Malformed component data/);
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe('Performance Scenarios', () => {
|
|
554
|
+
it('should handle large component trees efficiently', () => {
|
|
555
|
+
const startTime = performance.now();
|
|
556
|
+
|
|
557
|
+
// Generate large component tree (100 components)
|
|
558
|
+
const largeTree = Array.from({ length: 100 }, (_, i) => ({
|
|
559
|
+
tag: 'Card',
|
|
560
|
+
version: '1.0.0',
|
|
561
|
+
data: {
|
|
562
|
+
title: `Card ${i}`,
|
|
563
|
+
content: `Content for card ${i}`,
|
|
564
|
+
metadata: { index: i, timestamp: Date.now() },
|
|
565
|
+
actions: [
|
|
566
|
+
{
|
|
567
|
+
tag: 'Button',
|
|
568
|
+
version: '1.0.0',
|
|
569
|
+
data: { label: `Button ${i}` }
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
}));
|
|
574
|
+
|
|
575
|
+
const result = ComponentTransformer.deserialize(largeTree);
|
|
576
|
+
const endTime = performance.now();
|
|
577
|
+
|
|
578
|
+
expect(Array.isArray(result)).toBe(true);
|
|
579
|
+
expect(result).toHaveLength(100);
|
|
580
|
+
|
|
581
|
+
// Performance assertion - should complete within reasonable time (< 100ms)
|
|
582
|
+
expect(endTime - startTime).toBeLessThan(100);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('should handle deeply nested structures without stack overflow', () => {
|
|
586
|
+
// Create deeply nested structure (20 levels)
|
|
587
|
+
let nestedStructure: any = {
|
|
588
|
+
tag: 'Button',
|
|
589
|
+
version: '1.0.0',
|
|
590
|
+
data: { label: 'Deep Button' }
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
for (let i = 0; i < 20; i++) {
|
|
594
|
+
nestedStructure = {
|
|
595
|
+
tag: 'Section',
|
|
596
|
+
version: '1.0.0',
|
|
597
|
+
data: {
|
|
598
|
+
title: `Level ${i}`,
|
|
599
|
+
children: [nestedStructure]
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
expect(() => {
|
|
605
|
+
const result = ComponentTransformer.deserialize(nestedStructure);
|
|
606
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
607
|
+
}).not.toThrow();
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
describe('Edge Cases and Data Integrity', () => {
|
|
612
|
+
it('should preserve all props through complex roundtrip', () => {
|
|
613
|
+
const complexComponent = {
|
|
614
|
+
tag: 'Card',
|
|
615
|
+
version: '1.0.0',
|
|
616
|
+
data: {
|
|
617
|
+
title: 'Complex Card',
|
|
618
|
+
content: 'Content with special chars: @#$%^&*()_+{}[]|\\:";\'<>?,./`~',
|
|
619
|
+
image: 'https://example.com/image.jpg',
|
|
620
|
+
metadata: {
|
|
621
|
+
tags: ['react', 'serialization', 'testing'],
|
|
622
|
+
ratings: { stars: 4.5, count: 123 },
|
|
623
|
+
nested: { deep: { value: 'preserved' } }
|
|
624
|
+
},
|
|
625
|
+
actions: [
|
|
626
|
+
{
|
|
627
|
+
tag: 'Button',
|
|
628
|
+
version: '1.0.0',
|
|
629
|
+
data: {
|
|
630
|
+
label: 'Special Button',
|
|
631
|
+
variant: 'danger',
|
|
632
|
+
disabled: true
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
]
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// Deserialize
|
|
640
|
+
const reactElement = ComponentTransformer.deserialize(complexComponent);
|
|
641
|
+
expect(React.isValidElement(reactElement)).toBe(true);
|
|
642
|
+
|
|
643
|
+
// Serialize back
|
|
644
|
+
const serialized = ComponentTransformer.serialize(reactElement);
|
|
645
|
+
const parsed = JSON.parse(serialized);
|
|
646
|
+
|
|
647
|
+
// Verify data preservation
|
|
648
|
+
expect(parsed.tag).toBe('Card');
|
|
649
|
+
expect(parsed.data.title).toBe('Complex Card');
|
|
650
|
+
expect(parsed.data.content).toContain('special chars');
|
|
651
|
+
expect(parsed.data.metadata.nested.deep.value).toBe('preserved');
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it('should handle Unicode and special characters correctly', () => {
|
|
655
|
+
const unicodeData = {
|
|
656
|
+
tag: 'Code',
|
|
657
|
+
version: '1.0.0',
|
|
658
|
+
data: {
|
|
659
|
+
code: 'const π = "rocket";\nconst δΈζ = "Chinese";\nconst Ξ¨ = "psi";',
|
|
660
|
+
language: 'javascript'
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const result = ComponentTransformer.deserialize(unicodeData);
|
|
665
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
666
|
+
|
|
667
|
+
const element = result as ReactElement;
|
|
668
|
+
expect(element.props.children.props.children).toContain('π');
|
|
669
|
+
expect(element.props.children.props.children).toContain('δΈζ');
|
|
670
|
+
expect(element.props.children.props.children).toContain('Ξ¨');
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it('should handle very large text content', () => {
|
|
674
|
+
const largeText = 'Lorem ipsum '.repeat(1000); // ~11KB of text
|
|
675
|
+
|
|
676
|
+
const largeContentComponent = {
|
|
677
|
+
tag: 'Card',
|
|
678
|
+
version: '1.0.0',
|
|
679
|
+
data: {
|
|
680
|
+
title: 'Large Content Card',
|
|
681
|
+
content: largeText
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
expect(() => {
|
|
686
|
+
const result = ComponentTransformer.deserialize(largeContentComponent);
|
|
687
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
688
|
+
}).not.toThrow();
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
});
|