@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,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Browser Compatibility Tests for Component Serialization
|
|
3
|
+
*
|
|
4
|
+
* Tests serialization system behavior across different JavaScript engines,
|
|
5
|
+
* browser environments, and runtime conditions to ensure consistent
|
|
6
|
+
* functionality regardless of platform.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, { ReactElement } from 'react';
|
|
12
|
+
import { ComponentTransformer } from '../ComponentTransformer';
|
|
13
|
+
import { Serializable, SerializableConstructor } from '../../types/Serializable';
|
|
14
|
+
|
|
15
|
+
// Mock different browser environments
|
|
16
|
+
interface BrowserEnvironment {
|
|
17
|
+
name: string;
|
|
18
|
+
userAgent: string;
|
|
19
|
+
jsonSupport: {
|
|
20
|
+
parse: typeof JSON.parse;
|
|
21
|
+
stringify: typeof JSON.stringify;
|
|
22
|
+
};
|
|
23
|
+
features: {
|
|
24
|
+
es6: boolean;
|
|
25
|
+
weakMap: boolean;
|
|
26
|
+
symbol: boolean;
|
|
27
|
+
proxy: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Browser environment simulations
|
|
32
|
+
const browserEnvironments: BrowserEnvironment[] = [
|
|
33
|
+
{
|
|
34
|
+
name: 'Chrome Latest',
|
|
35
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
36
|
+
jsonSupport: { parse: JSON.parse, stringify: JSON.stringify },
|
|
37
|
+
features: { es6: true, weakMap: true, symbol: true, proxy: true }
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Firefox Latest',
|
|
41
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/120.0',
|
|
42
|
+
jsonSupport: { parse: JSON.parse, stringify: JSON.stringify },
|
|
43
|
+
features: { es6: true, weakMap: true, symbol: true, proxy: true }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Safari Latest',
|
|
47
|
+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Version/17.0 Safari/537.36',
|
|
48
|
+
jsonSupport: { parse: JSON.parse, stringify: JSON.stringify },
|
|
49
|
+
features: { es6: true, weakMap: true, symbol: true, proxy: true }
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Edge Latest',
|
|
53
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
|
|
54
|
+
jsonSupport: { parse: JSON.parse, stringify: JSON.stringify },
|
|
55
|
+
features: { es6: true, weakMap: true, symbol: true, proxy: true }
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Test component for cross-browser testing
|
|
60
|
+
class CrossBrowserTestComponent implements Serializable {
|
|
61
|
+
constructor(public props: {
|
|
62
|
+
text?: string;
|
|
63
|
+
number?: number;
|
|
64
|
+
boolean?: boolean;
|
|
65
|
+
array?: any[];
|
|
66
|
+
object?: Record<string, any>;
|
|
67
|
+
date?: string;
|
|
68
|
+
unicode?: string;
|
|
69
|
+
specialChars?: string;
|
|
70
|
+
}) {}
|
|
71
|
+
|
|
72
|
+
static fromJson(jsonData: any): ReactElement {
|
|
73
|
+
return React.createElement('div', {
|
|
74
|
+
'data-testid': 'cross-browser-test',
|
|
75
|
+
'data-text': jsonData.text,
|
|
76
|
+
'data-number': jsonData.number,
|
|
77
|
+
'data-boolean': jsonData.boolean,
|
|
78
|
+
'data-array': jsonData.array ? JSON.stringify(jsonData.array) : undefined,
|
|
79
|
+
'data-object': jsonData.object ? JSON.stringify(jsonData.object) : undefined,
|
|
80
|
+
'data-date': jsonData.date,
|
|
81
|
+
'data-unicode': jsonData.unicode,
|
|
82
|
+
'data-special-chars': jsonData.specialChars
|
|
83
|
+
}, [
|
|
84
|
+
jsonData.text ? React.createElement('span', { key: 'text' }, jsonData.text) : null,
|
|
85
|
+
jsonData.number !== undefined ? React.createElement('span', { key: 'number' }, jsonData.number.toString()) : null,
|
|
86
|
+
jsonData.boolean !== undefined ? React.createElement('span', { key: 'boolean' }, jsonData.boolean.toString()) : null,
|
|
87
|
+
jsonData.unicode ? React.createElement('span', { key: 'unicode' }, jsonData.unicode) : null
|
|
88
|
+
].filter(Boolean));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
toJson(): any {
|
|
92
|
+
return {
|
|
93
|
+
text: this.props.text,
|
|
94
|
+
number: this.props.number,
|
|
95
|
+
boolean: this.props.boolean,
|
|
96
|
+
array: this.props.array,
|
|
97
|
+
object: this.props.object,
|
|
98
|
+
date: this.props.date,
|
|
99
|
+
unicode: this.props.unicode,
|
|
100
|
+
specialChars: this.props.specialChars
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe('Cross-Browser Compatibility Tests', () => {
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
ComponentTransformer.clearRegistry();
|
|
108
|
+
ComponentTransformer.registerComponent('CrossBrowserTest', CrossBrowserTestComponent as SerializableConstructor);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
afterEach(() => {
|
|
112
|
+
ComponentTransformer.clearRegistry();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('JSON Parsing and Serialization', () => {
|
|
116
|
+
it('should handle JSON parsing consistently across environments', () => {
|
|
117
|
+
const testData = {
|
|
118
|
+
tag: 'CrossBrowserTest',
|
|
119
|
+
version: '1.0.0',
|
|
120
|
+
data: {
|
|
121
|
+
text: 'Hello World',
|
|
122
|
+
number: 42,
|
|
123
|
+
boolean: true,
|
|
124
|
+
array: [1, 2, 3, 'four', true, null],
|
|
125
|
+
object: { nested: 'value', deep: { deeper: 'deepValue' } }
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
browserEnvironments.forEach(env => {
|
|
130
|
+
// Test JSON parsing with each environment's parser
|
|
131
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
132
|
+
expect(() => {
|
|
133
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
134
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
135
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
136
|
+
}).not.toThrow();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle special JSON edge cases consistently', () => {
|
|
141
|
+
const edgeCases = [
|
|
142
|
+
{ name: 'null', value: null },
|
|
143
|
+
{ name: 'empty object', value: {} },
|
|
144
|
+
{ name: 'empty array', value: [] },
|
|
145
|
+
{ name: 'zero', value: 0 },
|
|
146
|
+
{ name: 'false', value: false },
|
|
147
|
+
{ name: 'empty string', value: '' },
|
|
148
|
+
{ name: 'whitespace string', value: ' \n\t ' },
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
edgeCases.forEach(({ name, value }) => {
|
|
152
|
+
const testData = {
|
|
153
|
+
tag: 'CrossBrowserTest',
|
|
154
|
+
version: '1.0.0',
|
|
155
|
+
data: { [name.replace(/\s+/g, '_')]: value }
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
browserEnvironments.forEach(env => {
|
|
159
|
+
expect(() => {
|
|
160
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
161
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
162
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
163
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
164
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle large numbers consistently', () => {
|
|
170
|
+
const numberTests = [
|
|
171
|
+
{ name: 'small integer', value: 42 },
|
|
172
|
+
{ name: 'large integer', value: 9007199254740991 }, // Number.MAX_SAFE_INTEGER
|
|
173
|
+
{ name: 'small float', value: 3.14159 },
|
|
174
|
+
{ name: 'very small float', value: 0.000000001 },
|
|
175
|
+
{ name: 'negative number', value: -123456.789 },
|
|
176
|
+
{ name: 'zero', value: 0 },
|
|
177
|
+
{ name: 'negative zero', value: -0 }
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
numberTests.forEach(({ name, value }) => {
|
|
181
|
+
const testData = {
|
|
182
|
+
tag: 'CrossBrowserTest',
|
|
183
|
+
version: '1.0.0',
|
|
184
|
+
data: { number: value }
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
browserEnvironments.forEach(env => {
|
|
188
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
189
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
190
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
191
|
+
|
|
192
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
193
|
+
|
|
194
|
+
const element = result as ReactElement;
|
|
195
|
+
const numberValue = parseFloat(element.props['data-number']);
|
|
196
|
+
|
|
197
|
+
if (name === 'negative zero') {
|
|
198
|
+
// Negative zero becomes positive zero in JSON
|
|
199
|
+
expect(numberValue).toBe(0);
|
|
200
|
+
} else {
|
|
201
|
+
expect(numberValue).toBeCloseTo(value, 10);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('Unicode and Character Encoding', () => {
|
|
209
|
+
it('should handle Unicode characters consistently', () => {
|
|
210
|
+
const unicodeTests = [
|
|
211
|
+
{ name: 'emoji', text: '🚀🎉💻🌟⭐' },
|
|
212
|
+
{ name: 'chinese', text: '你好世界' },
|
|
213
|
+
{ name: 'arabic', text: 'مرحبا بالعالم' },
|
|
214
|
+
{ name: 'russian', text: 'Привет мир' },
|
|
215
|
+
{ name: 'greek', text: 'Γεια σου κόσμε' },
|
|
216
|
+
{ name: 'mathematical', text: '∑∏∆√∞≈≠±×÷' },
|
|
217
|
+
{ name: 'currency', text: '$€£¥₹₿' },
|
|
218
|
+
{ name: 'mixed', text: 'Hello 世界 🌍 مرحبا Привет!' }
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
unicodeTests.forEach(({ name, text }) => {
|
|
222
|
+
const testData = {
|
|
223
|
+
tag: 'CrossBrowserTest',
|
|
224
|
+
version: '1.0.0',
|
|
225
|
+
data: { unicode: text }
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
browserEnvironments.forEach(env => {
|
|
229
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
230
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
231
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
232
|
+
|
|
233
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
234
|
+
|
|
235
|
+
const element = result as ReactElement;
|
|
236
|
+
expect(element.props['data-unicode']).toBe(text);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should handle special characters and escaping', () => {
|
|
242
|
+
const specialCharTests = [
|
|
243
|
+
{ name: 'quotes', text: 'He said "Hello \'World\'"' },
|
|
244
|
+
{ name: 'backslashes', text: 'C:\\Users\\Name\\File.txt' },
|
|
245
|
+
{ name: 'control chars', text: 'Line 1\nLine 2\tTabbed\rCarriage Return' },
|
|
246
|
+
{ name: 'html entities', text: '<script>alert("xss")</script><>&' },
|
|
247
|
+
{ name: 'json-like', text: '{"fake": "json", "array": [1,2,3]}' },
|
|
248
|
+
{ name: 'regex special', text: '^$.*+?()[]{}|\\' }
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
specialCharTests.forEach(({ name, text }) => {
|
|
252
|
+
const testData = {
|
|
253
|
+
tag: 'CrossBrowserTest',
|
|
254
|
+
version: '1.0.0',
|
|
255
|
+
data: { specialChars: text }
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
browserEnvironments.forEach(env => {
|
|
259
|
+
expect(() => {
|
|
260
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
261
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
262
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
263
|
+
|
|
264
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
265
|
+
|
|
266
|
+
const element = result as ReactElement;
|
|
267
|
+
expect(element.props['data-special-chars']).toBe(text);
|
|
268
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('Date and Time Handling', () => {
|
|
275
|
+
it('should handle date strings consistently', () => {
|
|
276
|
+
const dateTests = [
|
|
277
|
+
{ name: 'ISO string', date: '2025-01-01T12:00:00.000Z' },
|
|
278
|
+
{ name: 'ISO with timezone', date: '2025-01-01T12:00:00+05:30' },
|
|
279
|
+
{ name: 'short date', date: '2025-01-01' },
|
|
280
|
+
{ name: 'human readable', date: 'January 1, 2025' },
|
|
281
|
+
{ name: 'timestamp string', date: '1735732800000' }
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
dateTests.forEach(({ name, date }) => {
|
|
285
|
+
const testData = {
|
|
286
|
+
tag: 'CrossBrowserTest',
|
|
287
|
+
version: '1.0.0',
|
|
288
|
+
data: { date }
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
browserEnvironments.forEach(env => {
|
|
292
|
+
expect(() => {
|
|
293
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
294
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
295
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
296
|
+
|
|
297
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
298
|
+
|
|
299
|
+
const element = result as ReactElement;
|
|
300
|
+
expect(element.props['data-date']).toBe(date);
|
|
301
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('Array and Object Handling', () => {
|
|
308
|
+
it('should handle complex nested structures consistently', () => {
|
|
309
|
+
const nestedTests = [
|
|
310
|
+
{
|
|
311
|
+
name: 'deeply nested object',
|
|
312
|
+
data: {
|
|
313
|
+
level1: {
|
|
314
|
+
level2: {
|
|
315
|
+
level3: {
|
|
316
|
+
level4: {
|
|
317
|
+
value: 'deep value'
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'mixed array types',
|
|
326
|
+
data: {
|
|
327
|
+
array: [
|
|
328
|
+
'string',
|
|
329
|
+
42,
|
|
330
|
+
true,
|
|
331
|
+
null,
|
|
332
|
+
{ nested: 'object' },
|
|
333
|
+
['nested', 'array'],
|
|
334
|
+
undefined // Will be converted to null in JSON
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'sparse array',
|
|
340
|
+
data: {
|
|
341
|
+
sparse: [1, , , 4, , 6] // Sparse array with holes
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
nestedTests.forEach(({ name, data }) => {
|
|
347
|
+
const testData = {
|
|
348
|
+
tag: 'CrossBrowserTest',
|
|
349
|
+
version: '1.0.0',
|
|
350
|
+
data
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
browserEnvironments.forEach(env => {
|
|
354
|
+
expect(() => {
|
|
355
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
356
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
357
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
358
|
+
|
|
359
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
360
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should handle object property order consistently', () => {
|
|
366
|
+
const testData = {
|
|
367
|
+
tag: 'CrossBrowserTest',
|
|
368
|
+
version: '1.0.0',
|
|
369
|
+
data: {
|
|
370
|
+
z_property: 'last',
|
|
371
|
+
a_property: 'first',
|
|
372
|
+
m_property: 'middle'
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
browserEnvironments.forEach(env => {
|
|
377
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
378
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
379
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
380
|
+
|
|
381
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
382
|
+
|
|
383
|
+
// Property order might differ, but values should be preserved
|
|
384
|
+
const element = result as ReactElement;
|
|
385
|
+
const objectData = JSON.parse(element.props['data-object'] || '{}');
|
|
386
|
+
expect(objectData.z_property).toBe('last');
|
|
387
|
+
expect(objectData.a_property).toBe('first');
|
|
388
|
+
expect(objectData.m_property).toBe('middle');
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe('Error Handling Consistency', () => {
|
|
394
|
+
it('should handle malformed JSON consistently', () => {
|
|
395
|
+
const malformedJsonTests = [
|
|
396
|
+
'{"unclosed": object',
|
|
397
|
+
'{key: "missing quotes"}',
|
|
398
|
+
'[1, 2, 3,]', // Trailing comma
|
|
399
|
+
'{"duplicate": "key1", "duplicate": "key2"}' // Actually valid JSON
|
|
400
|
+
];
|
|
401
|
+
|
|
402
|
+
malformedJsonTests.forEach((malformedJson, index) => {
|
|
403
|
+
if (index === 3) return; // Skip the valid JSON case
|
|
404
|
+
|
|
405
|
+
browserEnvironments.forEach(env => {
|
|
406
|
+
expect(() => {
|
|
407
|
+
env.jsonSupport.parse(malformedJson);
|
|
408
|
+
}).toThrow();
|
|
409
|
+
|
|
410
|
+
expect(() => {
|
|
411
|
+
ComponentTransformer.deserialize(malformedJson);
|
|
412
|
+
}).toThrow(/Invalid JSON input/);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should handle component errors consistently', () => {
|
|
418
|
+
const invalidComponent = {
|
|
419
|
+
tag: 'NonExistentComponent',
|
|
420
|
+
version: '1.0.0',
|
|
421
|
+
data: {}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
browserEnvironments.forEach(env => {
|
|
425
|
+
expect(() => {
|
|
426
|
+
const jsonString = env.jsonSupport.stringify(invalidComponent);
|
|
427
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
428
|
+
ComponentTransformer.deserialize(parsed);
|
|
429
|
+
}).toThrow('Unknown component: NonExistentComponent');
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe('Performance Consistency', () => {
|
|
435
|
+
it('should maintain consistent performance across browsers', () => {
|
|
436
|
+
const largeTestData = {
|
|
437
|
+
tag: 'CrossBrowserTest',
|
|
438
|
+
version: '1.0.0',
|
|
439
|
+
data: {
|
|
440
|
+
array: Array.from({ length: 1000 }, (_, i) => ({
|
|
441
|
+
id: i,
|
|
442
|
+
text: `Item ${i}`,
|
|
443
|
+
nested: { value: i * 2 }
|
|
444
|
+
}))
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const performanceResults: { [browser: string]: number } = {};
|
|
449
|
+
|
|
450
|
+
browserEnvironments.forEach(env => {
|
|
451
|
+
const startTime = performance.now();
|
|
452
|
+
|
|
453
|
+
const jsonString = env.jsonSupport.stringify(largeTestData);
|
|
454
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
455
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
456
|
+
|
|
457
|
+
const endTime = performance.now();
|
|
458
|
+
performanceResults[env.name] = endTime - startTime;
|
|
459
|
+
|
|
460
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Verify that performance is within reasonable bounds
|
|
464
|
+
const times = Object.values(performanceResults);
|
|
465
|
+
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
466
|
+
const maxTime = Math.max(...times);
|
|
467
|
+
const minTime = Math.min(...times);
|
|
468
|
+
|
|
469
|
+
console.log('Cross-browser performance:', performanceResults);
|
|
470
|
+
|
|
471
|
+
// No browser should be more than 3x slower than the fastest
|
|
472
|
+
expect(maxTime / minTime).toBeLessThan(3);
|
|
473
|
+
|
|
474
|
+
// Average time should be reasonable (< 100ms for this test)
|
|
475
|
+
expect(avgTime).toBeLessThan(100);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe('Memory Usage Consistency', () => {
|
|
480
|
+
it('should handle memory usage consistently', () => {
|
|
481
|
+
const memoryTestData = Array.from({ length: 100 }, (_, i) => ({
|
|
482
|
+
tag: 'CrossBrowserTest',
|
|
483
|
+
version: '1.0.0',
|
|
484
|
+
data: {
|
|
485
|
+
text: `Memory test item ${i}`,
|
|
486
|
+
array: Array.from({ length: 100 }, (_, j) => `item-${i}-${j}`)
|
|
487
|
+
}
|
|
488
|
+
}));
|
|
489
|
+
|
|
490
|
+
browserEnvironments.forEach(env => {
|
|
491
|
+
const initialMemory = process.memoryUsage().heapUsed;
|
|
492
|
+
|
|
493
|
+
// Process the data multiple times
|
|
494
|
+
for (let iteration = 0; iteration < 10; iteration++) {
|
|
495
|
+
memoryTestData.forEach(testItem => {
|
|
496
|
+
const jsonString = env.jsonSupport.stringify(testItem);
|
|
497
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
498
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
499
|
+
|
|
500
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const finalMemory = process.memoryUsage().heapUsed;
|
|
505
|
+
const memoryGrowth = (finalMemory - initialMemory) / (1024 * 1024); // MB
|
|
506
|
+
|
|
507
|
+
// Memory growth should be reasonable (< 50MB for this test)
|
|
508
|
+
expect(memoryGrowth).toBeLessThan(50);
|
|
509
|
+
|
|
510
|
+
console.log(`${env.name} memory growth: ${memoryGrowth.toFixed(2)}MB`);
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe('Edge Case Compatibility', () => {
|
|
516
|
+
it('should handle boundary values consistently', () => {
|
|
517
|
+
const boundaryTests = [
|
|
518
|
+
{ name: 'max safe integer', value: Number.MAX_SAFE_INTEGER },
|
|
519
|
+
{ name: 'min safe integer', value: Number.MIN_SAFE_INTEGER },
|
|
520
|
+
{ name: 'max value', value: Number.MAX_VALUE },
|
|
521
|
+
{ name: 'min value', value: Number.MIN_VALUE },
|
|
522
|
+
{ name: 'positive infinity', value: Number.POSITIVE_INFINITY },
|
|
523
|
+
{ name: 'negative infinity', value: Number.NEGATIVE_INFINITY },
|
|
524
|
+
{ name: 'NaN', value: Number.NaN }
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
boundaryTests.forEach(({ name, value }) => {
|
|
528
|
+
const testData = {
|
|
529
|
+
tag: 'CrossBrowserTest',
|
|
530
|
+
version: '1.0.0',
|
|
531
|
+
data: { number: value }
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
browserEnvironments.forEach(env => {
|
|
535
|
+
if (name.includes('infinity') || name === 'NaN') {
|
|
536
|
+
// JSON doesn't support these values - they become null
|
|
537
|
+
expect(() => {
|
|
538
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
539
|
+
expect(jsonString).toContain('null');
|
|
540
|
+
}).not.toThrow();
|
|
541
|
+
} else {
|
|
542
|
+
expect(() => {
|
|
543
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
544
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
545
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
546
|
+
|
|
547
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
548
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('should handle string length limits consistently', () => {
|
|
555
|
+
const lengthTests = [
|
|
556
|
+
{ name: 'empty string', length: 0 },
|
|
557
|
+
{ name: 'short string', length: 10 },
|
|
558
|
+
{ name: 'medium string', length: 1000 },
|
|
559
|
+
{ name: 'long string', length: 10000 },
|
|
560
|
+
{ name: 'very long string', length: 100000 }
|
|
561
|
+
];
|
|
562
|
+
|
|
563
|
+
lengthTests.forEach(({ name, length }) => {
|
|
564
|
+
const longString = 'x'.repeat(length);
|
|
565
|
+
const testData = {
|
|
566
|
+
tag: 'CrossBrowserTest',
|
|
567
|
+
version: '1.0.0',
|
|
568
|
+
data: { text: longString }
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
browserEnvironments.forEach(env => {
|
|
572
|
+
expect(() => {
|
|
573
|
+
const jsonString = env.jsonSupport.stringify(testData);
|
|
574
|
+
const parsed = env.jsonSupport.parse(jsonString);
|
|
575
|
+
const result = ComponentTransformer.deserialize(parsed);
|
|
576
|
+
|
|
577
|
+
expect(React.isValidElement(result)).toBe(true);
|
|
578
|
+
|
|
579
|
+
const element = result as ReactElement;
|
|
580
|
+
expect(element.props['data-text']).toBe(longString);
|
|
581
|
+
}).not.toThrow(`${name} should work in ${env.name}`);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockSerializableComponent - Test helper component implementing new SerializableConstructor interface
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This is not a test file - it's a helper module for testing.
|
|
5
|
+
* Jest should not run this as a test suite.
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates component self-declaration pattern with static tagName and version properties
|
|
8
|
+
* Used for testing the updated ComponentTransformer architecture
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React, { ReactElement } from 'react';
|
|
14
|
+
import { Serializable, SerializableConstructor } from '../../../schemas';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Mock component implementing the new Serializable interface with self-declaration
|
|
18
|
+
*/
|
|
19
|
+
export class MockSerializableComponent implements Serializable {
|
|
20
|
+
static readonly tagName = 'MockComponent';
|
|
21
|
+
static readonly version = '1.0.0';
|
|
22
|
+
|
|
23
|
+
constructor(public props: {
|
|
24
|
+
title?: string;
|
|
25
|
+
content?: string;
|
|
26
|
+
variant?: string;
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
}) {}
|
|
29
|
+
|
|
30
|
+
static fromJson(jsonData: any): ReactElement {
|
|
31
|
+
return React.createElement('div', {
|
|
32
|
+
className: `mock-component ${jsonData.variant || 'default'}`,
|
|
33
|
+
'data-testid': 'mock-component'
|
|
34
|
+
}, [
|
|
35
|
+
jsonData.title && React.createElement('h3', { key: 'title' }, jsonData.title),
|
|
36
|
+
jsonData.content && React.createElement('p', { key: 'content' }, jsonData.content),
|
|
37
|
+
jsonData.children
|
|
38
|
+
].filter(Boolean));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
toJson(): any {
|
|
42
|
+
return {
|
|
43
|
+
title: this.props.title,
|
|
44
|
+
content: this.props.content,
|
|
45
|
+
variant: this.props.variant,
|
|
46
|
+
children: this.props.children
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Type assertion to ensure it meets the interface requirements
|
|
52
|
+
export const MockSerializableComponentClass = MockSerializableComponent as unknown as SerializableConstructor;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Alternative mock component with different tagName and version for testing conflicts
|
|
56
|
+
*/
|
|
57
|
+
export class AlternativeMockComponent implements Serializable {
|
|
58
|
+
static readonly tagName = 'AlternativeComponent';
|
|
59
|
+
static readonly version = '2.1.0';
|
|
60
|
+
|
|
61
|
+
constructor(public props: {
|
|
62
|
+
label?: string;
|
|
63
|
+
type?: string;
|
|
64
|
+
active?: boolean;
|
|
65
|
+
}) {}
|
|
66
|
+
|
|
67
|
+
static fromJson(jsonData: any): ReactElement {
|
|
68
|
+
return React.createElement('button', {
|
|
69
|
+
className: `alt-mock ${jsonData.type || 'default'}`,
|
|
70
|
+
'data-active': jsonData.active,
|
|
71
|
+
'data-testid': 'alternative-mock'
|
|
72
|
+
}, jsonData.label || 'Alternative Component');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
toJson(): any {
|
|
76
|
+
return {
|
|
77
|
+
label: this.props.label,
|
|
78
|
+
type: this.props.type,
|
|
79
|
+
active: this.props.active
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const AlternativeMockComponentClass = AlternativeMockComponent as unknown as SerializableConstructor;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Mock component missing required static properties (for error testing)
|
|
88
|
+
*/
|
|
89
|
+
export class InvalidMockComponent implements Serializable {
|
|
90
|
+
// Missing static tagName and version properties intentionally
|
|
91
|
+
|
|
92
|
+
constructor(public props: any) {}
|
|
93
|
+
|
|
94
|
+
static fromJson(jsonData: any): ReactElement {
|
|
95
|
+
return React.createElement('div', {}, 'Invalid Component');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
toJson(): any {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const InvalidMockComponentClass = InvalidMockComponent as any; // Intentionally not typed as SerializableConstructor
|