@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,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComponentTransformer Tests - Comprehensive test suite
|
|
3
|
+
*
|
|
4
|
+
* Tests the core component serialization system functionality
|
|
5
|
+
* including registration, serialization, deserialization, and error handling.
|
|
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
|
+
import {
|
|
14
|
+
MockSerializableComponentClass,
|
|
15
|
+
AlternativeMockComponentClass,
|
|
16
|
+
InvalidMockComponentClass
|
|
17
|
+
} from './MockSerializableComponent';
|
|
18
|
+
|
|
19
|
+
// Legacy mock components for testing (updated with self-declaration)
|
|
20
|
+
class MockButton implements Serializable {
|
|
21
|
+
static readonly tagName = 'Button';
|
|
22
|
+
static readonly version = '1.0.0';
|
|
23
|
+
|
|
24
|
+
constructor(public props: { label?: string; variant?: string; onClick?: () => void }) {}
|
|
25
|
+
|
|
26
|
+
static fromJson(jsonData: any): ReactElement {
|
|
27
|
+
return React.createElement('button', {
|
|
28
|
+
className: `btn-${jsonData.variant || 'default'}`,
|
|
29
|
+
onClick: jsonData.onClick
|
|
30
|
+
}, jsonData.label || 'Button');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
toJson(): any {
|
|
34
|
+
return {
|
|
35
|
+
label: this.props.label,
|
|
36
|
+
variant: this.props.variant,
|
|
37
|
+
onClick: this.props.onClick ? 'function' : undefined
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class MockCard implements Serializable {
|
|
43
|
+
static readonly tagName = 'Card';
|
|
44
|
+
static readonly version = '1.0.0';
|
|
45
|
+
|
|
46
|
+
constructor(public props: { title?: string; content?: string; children?: any }) {}
|
|
47
|
+
|
|
48
|
+
static fromJson(jsonData: any): ReactElement {
|
|
49
|
+
return React.createElement('div', {
|
|
50
|
+
className: 'card'
|
|
51
|
+
}, [
|
|
52
|
+
React.createElement('h3', { key: 'title' }, jsonData.title),
|
|
53
|
+
React.createElement('p', { key: 'content' }, jsonData.content)
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toJson(): any {
|
|
58
|
+
return {
|
|
59
|
+
title: this.props.title,
|
|
60
|
+
content: this.props.content
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe('ComponentTransformer', () => {
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
// Clear registry before each test
|
|
68
|
+
ComponentTransformer.clearRegistry();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
// Clean up after each test
|
|
73
|
+
ComponentTransformer.clearRegistry();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('Component Registration', () => {
|
|
77
|
+
it('should register a component successfully', () => {
|
|
78
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
79
|
+
|
|
80
|
+
const registered = ComponentTransformer.getRegisteredComponents();
|
|
81
|
+
expect(registered).toContain('Button');
|
|
82
|
+
expect(registered).toHaveLength(1);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should register multiple components', () => {
|
|
86
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
87
|
+
ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
|
|
88
|
+
|
|
89
|
+
const registered = ComponentTransformer.getRegisteredComponents();
|
|
90
|
+
expect(registered).toContain('Button');
|
|
91
|
+
expect(registered).toContain('Card');
|
|
92
|
+
expect(registered).toHaveLength(2);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should overwrite existing component registration with same tagName', () => {
|
|
96
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
97
|
+
ComponentTransformer.registerComponent(AlternativeMockComponentClass);
|
|
98
|
+
|
|
99
|
+
const registered = ComponentTransformer.getRegisteredComponents();
|
|
100
|
+
expect(registered).toContain('Button');
|
|
101
|
+
expect(registered).toContain('AlternativeComponent');
|
|
102
|
+
expect(registered).toHaveLength(2);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should clear registry completely', () => {
|
|
106
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
107
|
+
ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
|
|
108
|
+
|
|
109
|
+
ComponentTransformer.clearRegistry();
|
|
110
|
+
|
|
111
|
+
const registered = ComponentTransformer.getRegisteredComponents();
|
|
112
|
+
expect(registered).toHaveLength(0);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('Serialization', () => {
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should serialize null to null', () => {
|
|
122
|
+
const result = ComponentTransformer.serialize(null);
|
|
123
|
+
expect(result).toBe('null');
|
|
124
|
+
expect(JSON.parse(result)).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should serialize undefined to null', () => {
|
|
128
|
+
const result = ComponentTransformer.serialize(undefined);
|
|
129
|
+
expect(result).toBe('null');
|
|
130
|
+
expect(JSON.parse(result)).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should serialize primitive values', () => {
|
|
134
|
+
expect(JSON.parse(ComponentTransformer.serialize('hello'))).toBe('hello');
|
|
135
|
+
expect(JSON.parse(ComponentTransformer.serialize(42))).toBe(42);
|
|
136
|
+
expect(JSON.parse(ComponentTransformer.serialize(true))).toBe(true);
|
|
137
|
+
expect(JSON.parse(ComponentTransformer.serialize(false))).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should serialize array of primitives', () => {
|
|
141
|
+
const input = ['hello', 42, true, null];
|
|
142
|
+
const result = ComponentTransformer.serialize(input);
|
|
143
|
+
expect(JSON.parse(result)).toEqual(input);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should serialize array of mixed content', () => {
|
|
147
|
+
const input = ['text', 123, null, undefined];
|
|
148
|
+
const result = ComponentTransformer.serialize(input);
|
|
149
|
+
const parsed = JSON.parse(result);
|
|
150
|
+
expect(parsed).toEqual(['text', 123, null, null]);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('Deserialization', () => {
|
|
155
|
+
beforeEach(() => {
|
|
156
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
157
|
+
ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should deserialize null correctly', () => {
|
|
161
|
+
expect(ComponentTransformer.deserialize('null')).toBeNull();
|
|
162
|
+
expect(ComponentTransformer.deserialize(null)).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should deserialize primitive values from strings', () => {
|
|
166
|
+
expect(ComponentTransformer.deserialize('"hello"')).toBe('hello');
|
|
167
|
+
expect(ComponentTransformer.deserialize('42')).toBe(42);
|
|
168
|
+
expect(ComponentTransformer.deserialize('true')).toBe(true);
|
|
169
|
+
expect(ComponentTransformer.deserialize('false')).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should deserialize primitive values from objects', () => {
|
|
173
|
+
expect(ComponentTransformer.deserialize('hello')).toBe('hello');
|
|
174
|
+
expect(ComponentTransformer.deserialize(42)).toBe(42);
|
|
175
|
+
expect(ComponentTransformer.deserialize(true)).toBe(true);
|
|
176
|
+
expect(ComponentTransformer.deserialize(false)).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should deserialize arrays', () => {
|
|
180
|
+
const input = ['hello', 42, true, null];
|
|
181
|
+
const result = ComponentTransformer.deserialize(input);
|
|
182
|
+
expect(result).toEqual(input);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should deserialize registered component from object', () => {
|
|
186
|
+
const componentData = {
|
|
187
|
+
tag: 'Button',
|
|
188
|
+
version: '1.0.0',
|
|
189
|
+
data: {
|
|
190
|
+
label: 'Click Me',
|
|
191
|
+
variant: 'primary'
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const result = ComponentTransformer.deserialize(componentData);
|
|
196
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
197
|
+
|
|
198
|
+
const element = result as ReactElement;
|
|
199
|
+
expect(element.type).toBe('button');
|
|
200
|
+
expect(element.props.className).toBe('btn-primary');
|
|
201
|
+
expect(element.props.children).toBe('Click Me');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should deserialize registered component from JSON string', () => {
|
|
205
|
+
const componentData = {
|
|
206
|
+
tag: 'Card',
|
|
207
|
+
version: '1.0.0',
|
|
208
|
+
data: {
|
|
209
|
+
title: 'Test Card',
|
|
210
|
+
content: 'Test content'
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const jsonString = JSON.stringify(componentData);
|
|
215
|
+
const result = ComponentTransformer.deserialize(jsonString);
|
|
216
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
217
|
+
|
|
218
|
+
const element = result as ReactElement;
|
|
219
|
+
expect(element.type).toBe('div');
|
|
220
|
+
expect(element.props.className).toBe('card');
|
|
221
|
+
expect(Array.isArray(element.props.children)).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should handle array of components', () => {
|
|
225
|
+
const input = [
|
|
226
|
+
{
|
|
227
|
+
tag: 'Button',
|
|
228
|
+
version: '1.0.0',
|
|
229
|
+
data: { label: 'First Button' }
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
tag: 'Button',
|
|
233
|
+
version: '1.0.0',
|
|
234
|
+
data: { label: 'Second Button' }
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
const result = ComponentTransformer.deserialize(input);
|
|
239
|
+
expect(Array.isArray(result)).toBe(true);
|
|
240
|
+
|
|
241
|
+
const elements = result as ReactElement[];
|
|
242
|
+
expect(elements).toHaveLength(2);
|
|
243
|
+
expect(React.isValidElement(elements[0])).toBe(true);
|
|
244
|
+
expect(React.isValidElement(elements[1])).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('Error Handling', () => {
|
|
249
|
+
it('should throw error for invalid JSON string', () => {
|
|
250
|
+
expect(() => {
|
|
251
|
+
ComponentTransformer.deserialize('invalid json {');
|
|
252
|
+
}).toThrow('Invalid JSON input');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should throw error for unknown component', () => {
|
|
256
|
+
const componentData = {
|
|
257
|
+
tag: 'UnknownComponent',
|
|
258
|
+
version: '1.0.0',
|
|
259
|
+
data: {}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// With the new fallback system, unknown components should not throw but use ReactNodeTransformer
|
|
263
|
+
const result = ComponentTransformer.deserialize(componentData);
|
|
264
|
+
// Should return a fallback React element rather than throwing
|
|
265
|
+
expect(result).toBeDefined();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should handle malformed component data gracefully', () => {
|
|
269
|
+
const malformedData = {
|
|
270
|
+
tag: 'Button',
|
|
271
|
+
// Missing version and data
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Register a button component first
|
|
275
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
276
|
+
|
|
277
|
+
expect(() => {
|
|
278
|
+
ComponentTransformer.deserialize(malformedData);
|
|
279
|
+
}).toThrow('Malformed component data: missing \'data\' property for component \'Button\'');
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('String vs Object Input Handling', () => {
|
|
284
|
+
beforeEach(() => {
|
|
285
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should handle string input correctly', () => {
|
|
289
|
+
const componentData = {
|
|
290
|
+
tag: 'Button',
|
|
291
|
+
version: '1.0.0',
|
|
292
|
+
data: { label: 'String Input Test' }
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const jsonString = JSON.stringify(componentData);
|
|
296
|
+
const result = ComponentTransformer.deserialize(jsonString);
|
|
297
|
+
|
|
298
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
299
|
+
const element = result as ReactElement;
|
|
300
|
+
expect(element.props.children).toBe('String Input Test');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should handle object input correctly', () => {
|
|
304
|
+
const componentData = {
|
|
305
|
+
tag: 'Button',
|
|
306
|
+
version: '1.0.0',
|
|
307
|
+
data: { label: 'Object Input Test' }
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const result = ComponentTransformer.deserialize(componentData);
|
|
311
|
+
|
|
312
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
313
|
+
const element = result as ReactElement;
|
|
314
|
+
expect(element.props.children).toBe('Object Input Test');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should handle array input correctly', () => {
|
|
318
|
+
const componentsData = [
|
|
319
|
+
{
|
|
320
|
+
tag: 'Button',
|
|
321
|
+
version: '1.0.0',
|
|
322
|
+
data: { label: 'Array Item 1' }
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
tag: 'Button',
|
|
326
|
+
version: '1.0.0',
|
|
327
|
+
data: { label: 'Array Item 2' }
|
|
328
|
+
}
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
const result = ComponentTransformer.deserialize(componentsData);
|
|
332
|
+
|
|
333
|
+
expect(Array.isArray(result)).toBe(true);
|
|
334
|
+
const elements = result as ReactElement[];
|
|
335
|
+
expect(elements).toHaveLength(2);
|
|
336
|
+
expect(elements[0].props.children).toBe('Array Item 1');
|
|
337
|
+
expect(elements[1].props.children).toBe('Array Item 2');
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('Nested Component Scenarios', () => {
|
|
342
|
+
beforeEach(() => {
|
|
343
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
344
|
+
ComponentTransformer.registerComponent(MockCard as SerializableConstructor);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should handle nested primitive content', () => {
|
|
348
|
+
const input = ['text', 42, ['nested', 'array'], { key: 'value' }];
|
|
349
|
+
const serialized = ComponentTransformer.serialize(input);
|
|
350
|
+
const deserialized = ComponentTransformer.deserialize(serialized);
|
|
351
|
+
|
|
352
|
+
expect(deserialized).toEqual(['text', 42, ['nested', 'array'], { key: 'value' }]);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should handle mixed content arrays', () => {
|
|
356
|
+
const input = [
|
|
357
|
+
'plain text',
|
|
358
|
+
{
|
|
359
|
+
tag: 'Button',
|
|
360
|
+
version: '1.0.0',
|
|
361
|
+
data: { label: 'Embedded Button' }
|
|
362
|
+
},
|
|
363
|
+
42,
|
|
364
|
+
null
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
const result = ComponentTransformer.deserialize(input);
|
|
368
|
+
expect(Array.isArray(result)).toBe(true);
|
|
369
|
+
|
|
370
|
+
const elements = result as any[];
|
|
371
|
+
expect(elements[0]).toBe('plain text');
|
|
372
|
+
expect(React.isValidElement(elements[1])).toBe(true);
|
|
373
|
+
expect(elements[2]).toBe(42);
|
|
374
|
+
expect(elements[3]).toBeNull();
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('Data Structure Requirements', () => {
|
|
379
|
+
beforeEach(() => {
|
|
380
|
+
ComponentTransformer.registerComponent(MockButton as SerializableConstructor);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should produce correct serialized structure format', () => {
|
|
384
|
+
// This test verifies the data structure requirement:
|
|
385
|
+
// { tag: "ComponentName", version: "1.0.0", data: {...} }
|
|
386
|
+
|
|
387
|
+
// Since we can't easily test serialization of actual React elements
|
|
388
|
+
// without a more complex setup, we'll test the deserialization format
|
|
389
|
+
const expectedStructure = {
|
|
390
|
+
tag: 'Button',
|
|
391
|
+
version: '1.0.0',
|
|
392
|
+
data: {
|
|
393
|
+
label: 'Test Button',
|
|
394
|
+
variant: 'primary'
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const result = ComponentTransformer.deserialize(expectedStructure);
|
|
399
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should handle version information correctly', () => {
|
|
403
|
+
const componentWithVersion = {
|
|
404
|
+
tag: 'Button',
|
|
405
|
+
version: '2.1.0',
|
|
406
|
+
data: { label: 'Version Test' }
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// Should not throw - version handling is delegated to component's fromJson
|
|
410
|
+
expect(() => {
|
|
411
|
+
ComponentTransformer.deserialize(componentWithVersion);
|
|
412
|
+
}).not.toThrow();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('New Architecture Features', () => {
|
|
417
|
+
it('should validate component self-declaration with tagName and version', () => {
|
|
418
|
+
ComponentTransformer.registerComponent(MockSerializableComponentClass);
|
|
419
|
+
|
|
420
|
+
const registered = ComponentTransformer.getRegisteredComponents();
|
|
421
|
+
expect(registered).toContain('MockComponent');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should throw error for components missing tagName', () => {
|
|
425
|
+
expect(() => {
|
|
426
|
+
ComponentTransformer.registerComponent(InvalidMockComponentClass);
|
|
427
|
+
}).toThrow("Component class must have a static 'tagName' property");
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should use component declared version in serialization', () => {
|
|
431
|
+
ComponentTransformer.registerComponent(AlternativeMockComponentClass);
|
|
432
|
+
|
|
433
|
+
const componentData = {
|
|
434
|
+
tag: 'AlternativeComponent',
|
|
435
|
+
version: '2.1.0',
|
|
436
|
+
data: { label: 'Version Test', type: 'primary' }
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const result = ComponentTransformer.deserialize(componentData);
|
|
440
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should handle unregistered React elements with ReactNodeTransformer fallback', () => {
|
|
444
|
+
const unregisteredElement = React.createElement('span', { className: 'test' }, 'Fallback Test');
|
|
445
|
+
const serialized = ComponentTransformer.serialize(unregisteredElement);
|
|
446
|
+
const parsed = JSON.parse(serialized);
|
|
447
|
+
|
|
448
|
+
// Should use __react_node__ tag for unregistered components
|
|
449
|
+
expect(parsed.tag).toBe('__react_node__');
|
|
450
|
+
expect(parsed.version).toBe('1.0.0');
|
|
451
|
+
|
|
452
|
+
const deserialized = ComponentTransformer.deserialize(parsed);
|
|
453
|
+
expect(deserialized).toBeDefined();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should handle mixed array of registered and unregistered components', () => {
|
|
457
|
+
ComponentTransformer.registerComponent(MockSerializableComponentClass);
|
|
458
|
+
|
|
459
|
+
const mixedArray = [
|
|
460
|
+
'Text node',
|
|
461
|
+
{
|
|
462
|
+
tag: 'MockComponent',
|
|
463
|
+
version: '1.0.0',
|
|
464
|
+
data: { title: 'Registered Component' }
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
tag: '__react_node__',
|
|
468
|
+
version: '1.0.0',
|
|
469
|
+
data: {
|
|
470
|
+
type: 'react-element',
|
|
471
|
+
elementType: 'div',
|
|
472
|
+
props: { children: 'Unregistered Element' }
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
const result = ComponentTransformer.deserialize(mixedArray);
|
|
478
|
+
expect(Array.isArray(result)).toBe(true);
|
|
479
|
+
|
|
480
|
+
const elements = result as any[];
|
|
481
|
+
expect(elements).toHaveLength(3);
|
|
482
|
+
expect(elements[0]).toBe('Text node');
|
|
483
|
+
expect(React.isValidElement(elements[1])).toBe(true);
|
|
484
|
+
expect(elements[2]).toBeDefined(); // Fallback content
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should gracefully handle unknown registered components with fallback', () => {
|
|
488
|
+
const unknownComponent = {
|
|
489
|
+
tag: 'NonExistentComponent',
|
|
490
|
+
version: '1.0.0',
|
|
491
|
+
data: { some: 'data' }
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// Should not throw, but use fallback
|
|
495
|
+
const result = ComponentTransformer.deserialize(unknownComponent);
|
|
496
|
+
expect(result).toBeDefined();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should preserve all functionality for correctly registered components', () => {
|
|
500
|
+
ComponentTransformer.registerComponent(MockSerializableComponentClass);
|
|
501
|
+
|
|
502
|
+
const componentData = {
|
|
503
|
+
tag: 'MockComponent',
|
|
504
|
+
version: '1.0.0',
|
|
505
|
+
data: {
|
|
506
|
+
title: 'Test Title',
|
|
507
|
+
content: 'Test Content',
|
|
508
|
+
variant: 'primary'
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const result = ComponentTransformer.deserialize(componentData);
|
|
513
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
514
|
+
|
|
515
|
+
const element = result as ReactElement;
|
|
516
|
+
expect(element.type).toBe('div');
|
|
517
|
+
expect(element.props.className).toBe('mock-component primary');
|
|
518
|
+
expect(element.props['data-testid']).toBe('mock-component');
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
});
|