Major improvements to WooNooW Page Editor system: Schema & Architecture: - Canonical section schema with unified sectionSchema.ts - Normalized feature-grid to use items (not features) - Standardized default values across all section types - Schema versioning with automatic migration on read Backend (PHP): - Enhanced PlaceholderRenderer with typed output contracts - Added fallback behavior for empty/invalid dynamic sources - Added caching support for post data resolution - New SchemaMigration class for backward compatibility - New Features class for feature flags - Enhanced PageSSR with full style support - Removed controller-level special-casing for related_posts Frontend (Admin SPA): - Updated CanvasRenderer with schema-aware transformation - Enhanced InspectorPanel with canonical schema metadata - Added new section renderers Frontend (Customer SPA): - New section components: BentoCategoryGrid, MarqueeBanner, ProductCarousel, ShoppableImage - Updated FeatureGridSection for items prop contract Testing: - Add PHP tests: SchemaMigrationTest, PlaceholderRendererTest, PageSSRTest - Add TypeScript tests: schema-integration, feature-grid-regression - Add parity tests for React vs SSR content matching - Add CI script: check-schema-drift.mjs - Add VERIFICATION_CHECKLIST.md Documentation: - RELEASE_NOTES-v1.0.md with full release notes - docs/PAGE_EDITOR_SECTION_SCHEMA_V1.md - docs/PAGE_EDITOR_SSR_COVERAGE_AUDIT.md
749 lines
23 KiB
TypeScript
749 lines
23 KiB
TypeScript
/**
|
|
* React vs SSR Parity Tests
|
|
* Tests that compare React-rendered content against SSR HTML output
|
|
* to ensure consistent rendering between client and server
|
|
*
|
|
* Note: These are snapshot-based tests. Run with --update to regenerate snapshots.
|
|
*/
|
|
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import {
|
|
HeroSection,
|
|
ContentSection,
|
|
ImageTextSection,
|
|
FeatureGridSection,
|
|
CTABannerSection,
|
|
ContactFormSection,
|
|
BentoCategoryGrid,
|
|
ProductCarousel,
|
|
ShoppableImage,
|
|
MarqueeBanner,
|
|
} from '../../customer-spa/src/pages/DynamicPage/sections';
|
|
|
|
// Mock Lucide icons
|
|
vi.mock('lucide-react', () => ({
|
|
Star: () => null,
|
|
Heart: () => null,
|
|
Shield: () => null,
|
|
Zap: () => null,
|
|
Award: () => null,
|
|
Clock: () => null,
|
|
Truck: () => null,
|
|
User: () => null,
|
|
Settings: () => null,
|
|
}));
|
|
|
|
// Helper to extract text content from HTML for comparison
|
|
function extractTextContent(html: string): string {
|
|
return html
|
|
.replace(/<[^>]*>/g, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
// Helper to check if class exists in HTML
|
|
function hasClass(html: string, className: string): boolean {
|
|
return html.includes(`class="${className}"`) || html.includes(`class="${className} `) || html.includes(` ${className} `);
|
|
}
|
|
|
|
// Helper to check if style attribute exists
|
|
function hasStyle(html: string, stylePart: string): boolean {
|
|
return html.includes(`style="`) && html.includes(stylePart);
|
|
}
|
|
|
|
// Test data fixtures
|
|
const FIXTURES = {
|
|
hero: {
|
|
id: 'hero-test',
|
|
title: 'Welcome to Our Store',
|
|
subtitle: 'Discover amazing products',
|
|
image: 'https://example.com/hero.jpg',
|
|
cta_text: 'Shop Now',
|
|
cta_url: '/shop',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
content: {
|
|
id: 'content-test',
|
|
content: '<p>This is rich content with formatting.</p>',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
imageText: {
|
|
id: 'image-text-test',
|
|
title: 'About Our Company',
|
|
text: 'We are passionate about quality.',
|
|
image: 'https://example.com/about.jpg',
|
|
cta_text: 'Learn More',
|
|
cta_url: '/about',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
featureGrid: {
|
|
id: 'features-test',
|
|
heading: 'Our Features',
|
|
items: [
|
|
{ title: 'Feature 1', description: 'Description 1', icon: 'Star' },
|
|
{ title: 'Feature 2', description: 'Description 2', icon: 'Heart' },
|
|
{ title: 'Feature 3', description: 'Description 3', icon: 'Shield' },
|
|
],
|
|
layout: 'grid-3',
|
|
colorScheme: 'default',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
ctaBanner: {
|
|
id: 'cta-test',
|
|
title: 'Ready to Get Started?',
|
|
text: 'Join thousands of happy customers.',
|
|
button_text: 'Sign Up Now',
|
|
button_url: '/signup',
|
|
layout: 'default',
|
|
colorScheme: 'primary',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
contactForm: {
|
|
id: 'contact-test',
|
|
title: 'Contact Us',
|
|
webhook_url: 'https://example.com/webhook',
|
|
redirect_url: '/thank-you',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
bentoGrid: {
|
|
id: 'bento-test',
|
|
title: 'Shop by Category',
|
|
items: [
|
|
{ label: 'Electronics', url: '/category/electronics', image: 'https://example.com/elec.jpg', size: 'large' },
|
|
{ label: 'Clothing', url: '/category/clothing', size: 'medium' },
|
|
],
|
|
colorScheme: 'default',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
productCarousel: {
|
|
id: 'carousel-test',
|
|
title: 'Trending Products',
|
|
subtitle: 'Popular right now',
|
|
cta_text: 'View All',
|
|
cta_url: '/products',
|
|
products: [
|
|
{ name: 'Product 1', url: '/product/1', image: 'https://example.com/p1.jpg', price: '$29.99' },
|
|
{ name: 'Product 2', url: '/product/2', image: 'https://example.com/p2.jpg', price: '$39.99' },
|
|
],
|
|
colorScheme: 'default',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
shoppableImage: {
|
|
id: 'shoppable-test',
|
|
title: 'Shop the Look',
|
|
subtitle: 'Get inspired',
|
|
image: 'https://example.com/look.jpg',
|
|
hotspots: [
|
|
{ product_name: 'Item 1', product_slug: 'item-1', product_price: '$49.99', x: '25', y: '30' },
|
|
{ product_name: 'Item 2', product_slug: 'item-2', product_price: '$59.99', x: '65', y: '50' },
|
|
],
|
|
colorScheme: 'default',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
marquee: {
|
|
id: 'marquee-test',
|
|
text: 'Free Shipping*Easy Returns*24/7 Support',
|
|
separator: '*',
|
|
styles: {},
|
|
elementStyles: {},
|
|
},
|
|
};
|
|
|
|
describe('React vs SSR Parity Tests', () => {
|
|
describe('Hero Section', () => {
|
|
it('renders title content', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Welcome to Our Store');
|
|
});
|
|
|
|
it('renders subtitle content', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Discover amazing products');
|
|
});
|
|
|
|
it('renders CTA button', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('href="/shop"');
|
|
expect(extractTextContent(html)).toContain('Shop Now');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-hero')).toBe(true);
|
|
expect(hasClass(html, 'wn-section')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Content Section', () => {
|
|
it('renders content with HTML', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContentSection({
|
|
...FIXTURES.content,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('This is rich content');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContentSection({
|
|
...FIXTURES.content,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-content')).toBe(true);
|
|
expect(hasClass(html, 'wn-section')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Image + Text Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
ImageTextSection({
|
|
...FIXTURES.imageText,
|
|
layout: 'image-left',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('About Our Company');
|
|
});
|
|
|
|
it('renders text content', () => {
|
|
const html = renderToStaticMarkup(
|
|
ImageTextSection({
|
|
...FIXTURES.imageText,
|
|
layout: 'image-left',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('We are passionate');
|
|
});
|
|
|
|
it('renders image with correct src', () => {
|
|
const html = renderToStaticMarkup(
|
|
ImageTextSection({
|
|
...FIXTURES.imageText,
|
|
layout: 'image-left',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('https://example.com/about.jpg');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
ImageTextSection({
|
|
...FIXTURES.imageText,
|
|
layout: 'image-left',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-image-text')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Feature Grid Section', () => {
|
|
it('renders heading', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Our Features');
|
|
});
|
|
|
|
it('renders all feature items', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Feature 1');
|
|
expect(extractTextContent(html)).toContain('Feature 2');
|
|
expect(extractTextContent(html)).toContain('Feature 3');
|
|
});
|
|
|
|
it('renders feature descriptions', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Description 1');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-feature-grid')).toBe(true);
|
|
});
|
|
|
|
it('has grid layout class', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
layout: 'grid-3',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-feature-grid--grid-3')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('CTA Banner Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Ready to Get Started?');
|
|
});
|
|
|
|
it('renders description', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Join thousands');
|
|
});
|
|
|
|
it('renders button with href', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('href="/signup"');
|
|
expect(extractTextContent(html)).toContain('Sign Up Now');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-cta-banner')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Contact Form Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContactFormSection({
|
|
...FIXTURES.contactForm,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Contact Us');
|
|
});
|
|
|
|
it('renders form inputs', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContactFormSection({
|
|
...FIXTURES.contactForm,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('name="name"');
|
|
expect(html).toContain('name="email"');
|
|
expect(html).toContain('name="message"');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContactFormSection({
|
|
...FIXTURES.contactForm,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-contact-form')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Bento Category Grid Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
BentoCategoryGrid({
|
|
...FIXTURES.bentoGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Shop by Category');
|
|
});
|
|
|
|
it('renders category labels', () => {
|
|
const html = renderToStaticMarkup(
|
|
BentoCategoryGrid({
|
|
...FIXTURES.bentoGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Electronics');
|
|
expect(extractTextContent(html)).toContain('Clothing');
|
|
});
|
|
|
|
it('renders links', () => {
|
|
const html = renderToStaticMarkup(
|
|
BentoCategoryGrid({
|
|
...FIXTURES.bentoGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('href="/category/electronics"');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
BentoCategoryGrid({
|
|
...FIXTURES.bentoGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-bento-grid')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Product Carousel Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
ProductCarousel({
|
|
...FIXTURES.productCarousel,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Trending Products');
|
|
});
|
|
|
|
it('renders products', () => {
|
|
const html = renderToStaticMarkup(
|
|
ProductCarousel({
|
|
...FIXTURES.productCarousel,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Product 1');
|
|
expect(extractTextContent(html)).toContain('Product 2');
|
|
});
|
|
|
|
it('renders product prices', () => {
|
|
const html = renderToStaticMarkup(
|
|
ProductCarousel({
|
|
...FIXTURES.productCarousel,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('$29.99');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
ProductCarousel({
|
|
...FIXTURES.productCarousel,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-product-carousel')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Shoppable Image Section', () => {
|
|
it('renders title', () => {
|
|
const html = renderToStaticMarkup(
|
|
ShoppableImage({
|
|
...FIXTURES.shoppableImage,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Shop the Look');
|
|
});
|
|
|
|
it('renders image', () => {
|
|
const html = renderToStaticMarkup(
|
|
ShoppableImage({
|
|
...FIXTURES.shoppableImage,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(html).toContain('https://example.com/look.jpg');
|
|
});
|
|
|
|
it('renders hotspots', () => {
|
|
const html = renderToStaticMarkup(
|
|
ShoppableImage({
|
|
...FIXTURES.shoppableImage,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Item 1');
|
|
expect(extractTextContent(html)).toContain('Item 2');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
ShoppableImage({
|
|
...FIXTURES.shoppableImage,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-shoppable-image')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Marquee Banner Section', () => {
|
|
it('renders text items', () => {
|
|
const html = renderToStaticMarkup(
|
|
MarqueeBanner({
|
|
...FIXTURES.marquee,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(extractTextContent(html)).toContain('Free Shipping');
|
|
expect(extractTextContent(html)).toContain('Easy Returns');
|
|
expect(extractTextContent(html)).toContain('24/7 Support');
|
|
});
|
|
|
|
it('has section class for SSR matching', () => {
|
|
const html = renderToStaticMarkup(
|
|
MarqueeBanner({
|
|
...FIXTURES.marquee,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
|
|
expect(hasClass(html, 'wn-marquee')).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('SSR Class Matching Verification', () => {
|
|
/**
|
|
* These tests verify that React components use the same CSS classes
|
|
* as the PHP SSR renderers. This ensures visual parity.
|
|
*/
|
|
const SECTION_CLASSES = {
|
|
hero: 'wn-hero',
|
|
content: 'wn-content',
|
|
'image-text': 'wn-image-text',
|
|
'feature-grid': 'wn-feature-grid',
|
|
'cta-banner': 'wn-cta-banner',
|
|
'contact-form': 'wn-contact-form',
|
|
'bento-category-grid': 'wn-bento-grid',
|
|
'product-carousel': 'wn-product-carousel',
|
|
'shoppable-image': 'wn-shoppable-image',
|
|
'marquee-banner': 'wn-marquee',
|
|
};
|
|
|
|
it('hero section uses wn-hero class', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES.hero)).toBe(true);
|
|
});
|
|
|
|
it('content section uses wn-content class', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContentSection({
|
|
...FIXTURES.content,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES.content)).toBe(true);
|
|
});
|
|
|
|
it('image-text section uses wn-image-text class', () => {
|
|
const html = renderToStaticMarkup(
|
|
ImageTextSection({
|
|
...FIXTURES.imageText,
|
|
layout: 'image-left',
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['image-text'])).toBe(true);
|
|
});
|
|
|
|
it('feature-grid section uses wn-feature-grid class', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['feature-grid'])).toBe(true);
|
|
});
|
|
|
|
it('cta-banner section uses wn-cta-banner class', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['cta-banner'])).toBe(true);
|
|
});
|
|
|
|
it('contact-form section uses wn-contact-form class', () => {
|
|
const html = renderToStaticMarkup(
|
|
ContactFormSection({
|
|
...FIXTURES.contactForm,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['contact-form'])).toBe(true);
|
|
});
|
|
|
|
it('bento-category-grid section uses wn-bento-grid class', () => {
|
|
const html = renderToStaticMarkup(
|
|
BentoCategoryGrid({
|
|
...FIXTURES.bentoGrid,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['bento-category-grid'])).toBe(true);
|
|
});
|
|
|
|
it('product-carousel section uses wn-product-carousel class', () => {
|
|
const html = renderToStaticMarkup(
|
|
ProductCarousel({
|
|
...FIXTURES.productCarousel,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['product-carousel'])).toBe(true);
|
|
});
|
|
|
|
it('shoppable-image section uses wn-shoppable-image class', () => {
|
|
const html = renderToStaticMarkup(
|
|
ShoppableImage({
|
|
...FIXTURES.shoppableImage,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['shoppable-image'])).toBe(true);
|
|
});
|
|
|
|
it('marquee-banner section uses wn-marquee class', () => {
|
|
const html = renderToStaticMarkup(
|
|
MarqueeBanner({
|
|
...FIXTURES.marquee,
|
|
colorScheme: 'default',
|
|
})
|
|
);
|
|
expect(hasClass(html, SECTION_CLASSES['marquee-banner'])).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Color Scheme Parity', () => {
|
|
it('hero supports colorScheme prop', () => {
|
|
const html = renderToStaticMarkup(
|
|
HeroSection({
|
|
...FIXTURES.hero,
|
|
colorScheme: 'primary',
|
|
})
|
|
);
|
|
expect(hasClass(html, 'wn-scheme--primary')).toBe(true);
|
|
});
|
|
|
|
it('feature-grid supports colorScheme prop', () => {
|
|
const html = renderToStaticMarkup(
|
|
FeatureGridSection({
|
|
...FIXTURES.featureGrid,
|
|
colorScheme: 'muted',
|
|
})
|
|
);
|
|
expect(hasClass(html, 'wn-scheme--muted')).toBe(true);
|
|
});
|
|
|
|
it('cta-banner supports colorScheme prop', () => {
|
|
const html = renderToStaticMarkup(
|
|
CTABannerSection({
|
|
...FIXTURES.ctaBanner,
|
|
colorScheme: 'secondary',
|
|
})
|
|
);
|
|
expect(hasClass(html, 'wn-scheme--secondary')).toBe(true);
|
|
});
|
|
});
|