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