Skip to content

Commit c4d2b5a

Browse files
committed
Add Unit for RichText component
1 parent 90b95d5 commit c4d2b5a

File tree

6 files changed

+717
-6
lines changed

6 files changed

+717
-6
lines changed

packages/optimizely-cms-sdk/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@
4646
}
4747
},
4848
"devDependencies": {
49+
"@testing-library/jest-dom": "^6.9.1",
50+
"@testing-library/react": "^16.3.0",
4951
"@types/node": "^22.13.14",
5052
"@types/react": "^19",
53+
"jsdom": "^27.0.0",
5154
"rimraf": "^6.0.1",
5255
"typescript": "^5.8.2",
5356
"vitest": "^3.1.1"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from 'react';
2+
import { describe, it, expect } from 'vitest';
3+
import { render, screen } from '@testing-library/react';
4+
import '@testing-library/jest-dom';
5+
6+
import { RichText } from '../richText/component.js';
7+
import {
8+
simpleTextContent,
9+
mockRichTextContent,
10+
htmlEntitiesContent,
11+
unknownElementContent,
12+
markedTextContent,
13+
} from './mockData.js';
14+
15+
describe('RichText Component', () => {
16+
it('should render simple text content', () => {
17+
render(<RichText content={simpleTextContent} />);
18+
expect(screen.getByText('Hello, World!')).toBeInTheDocument();
19+
});
20+
21+
it('should render different elements correctly', () => {
22+
render(<RichText content={mockRichTextContent} />);
23+
24+
// Check for different elements
25+
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(
26+
'Main Heading'
27+
);
28+
expect(screen.getByRole('list')).toBeInTheDocument();
29+
expect(screen.getByRole('link')).toHaveTextContent('This is a link');
30+
expect(screen.getByText('bold text').tagName.toLowerCase()).toBe('strong');
31+
});
32+
33+
it('should handle empty/null content gracefully', () => {
34+
render(<RichText content={null as any} />);
35+
// Should not throw errors
36+
expect(document.body.firstChild?.firstChild).toBe(null);
37+
});
38+
39+
it('should decode HTML entities by default', () => {
40+
render(<RichText content={htmlEntitiesContent} />);
41+
// Should decode HTML entities
42+
expect(
43+
screen.getByText('Text with <HTML> entities & symbols')
44+
).toBeInTheDocument();
45+
});
46+
47+
it('should handle unknown elements with fallback', () => {
48+
render(<RichText content={unknownElementContent} />);
49+
// Should fallback to div and render content
50+
expect(screen.getByText('Content in unknown element')).toBeInTheDocument();
51+
});
52+
53+
it('should render text with various formatting marks', () => {
54+
render(<RichText content={markedTextContent} />);
55+
56+
// Check for different text formatting
57+
expect(screen.getByText('bold text').tagName.toLowerCase()).toBe('strong');
58+
expect(screen.getByText('italic text').tagName.toLowerCase()).toBe('em');
59+
expect(screen.getByText('underlined text').tagName.toLowerCase()).toBe('u');
60+
expect(screen.getByText('code text').tagName.toLowerCase()).toBe('span');
61+
expect(screen.getByText('strikethrough text').tagName.toLowerCase()).toBe(
62+
's'
63+
);
64+
});
65+
});
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import type { Node } from '../../components/richText/renderer.js';
2+
3+
/**
4+
* Mock RichText content for testing
5+
*/
6+
export const mockRichTextContent = {
7+
type: 'richText' as const,
8+
children: [
9+
{
10+
type: 'paragraph',
11+
children: [
12+
{
13+
text: 'This is a simple paragraph with ',
14+
},
15+
{
16+
text: 'bold text',
17+
bold: true,
18+
},
19+
{
20+
text: ' and ',
21+
},
22+
{
23+
text: 'italic text',
24+
italic: true,
25+
},
26+
{
27+
text: '.',
28+
},
29+
],
30+
},
31+
{
32+
type: 'heading-one',
33+
children: [
34+
{
35+
text: 'Main Heading',
36+
},
37+
],
38+
},
39+
{
40+
type: 'heading-two',
41+
children: [
42+
{
43+
text: 'Subheading',
44+
},
45+
],
46+
},
47+
{
48+
type: 'bulleted-list',
49+
children: [
50+
{
51+
type: 'list-item',
52+
children: [
53+
{
54+
text: 'First list item',
55+
},
56+
],
57+
},
58+
{
59+
type: 'list-item',
60+
children: [
61+
{
62+
text: 'Second list item with ',
63+
},
64+
{
65+
text: 'underlined text',
66+
underline: true,
67+
},
68+
],
69+
},
70+
],
71+
},
72+
{
73+
type: 'link',
74+
url: 'https://example.com',
75+
children: [
76+
{
77+
text: 'This is a link',
78+
},
79+
],
80+
},
81+
] as Node[],
82+
};
83+
84+
/**
85+
* Simple text content for basic tests
86+
*/
87+
export const simpleTextContent = {
88+
type: 'richText' as const,
89+
children: [
90+
{
91+
type: 'paragraph',
92+
children: [
93+
{
94+
text: 'Hello, World!',
95+
},
96+
],
97+
},
98+
] as Node[],
99+
};
100+
101+
/**
102+
* Content with HTML entities for decoding tests
103+
*/
104+
export const htmlEntitiesContent = {
105+
type: 'richText' as const,
106+
children: [
107+
{
108+
type: 'paragraph',
109+
children: [
110+
{
111+
text: 'Text with &lt;HTML&gt; entities &amp; symbols',
112+
},
113+
],
114+
},
115+
] as Node[],
116+
};
117+
118+
/**
119+
* Content with unknown element type for fallback testing
120+
*/
121+
export const unknownElementContent = {
122+
type: 'richText' as const,
123+
children: [
124+
{
125+
type: 'unknown-element',
126+
children: [
127+
{
128+
text: 'Content in unknown element',
129+
},
130+
],
131+
},
132+
] as Node[],
133+
};
134+
135+
/**
136+
* Content with various text marks for leaf testing
137+
*/
138+
export const markedTextContent = {
139+
type: 'richText' as const,
140+
children: [
141+
{
142+
type: 'paragraph',
143+
children: [
144+
{
145+
text: 'Normal text, ',
146+
},
147+
{
148+
text: 'bold text',
149+
bold: true,
150+
},
151+
{
152+
text: ', ',
153+
},
154+
{
155+
text: 'italic text',
156+
italic: true,
157+
},
158+
{
159+
text: ', ',
160+
},
161+
{
162+
text: 'underlined text',
163+
underline: true,
164+
},
165+
{
166+
text: ', ',
167+
},
168+
{
169+
text: 'code text',
170+
code: true,
171+
},
172+
{
173+
text: ', and ',
174+
},
175+
{
176+
text: 'strikethrough text',
177+
strikethrough: true,
178+
},
179+
{
180+
text: '.',
181+
},
182+
],
183+
},
184+
] as Node[],
185+
};

packages/optimizely-cms-sdk/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@
66
"module": "NodeNext",
77
"moduleResolution": "nodenext",
88
"noEmit": true
9-
}
9+
},
10+
"include": ["src/**/*"],
11+
"exclude": ["vitest.config.ts"]
1012
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference types="vitest" />
2+
import { defineConfig } from 'vitest/config';
3+
4+
export default defineConfig({
5+
test: {
6+
environment: 'jsdom',
7+
globals: true,
8+
},
9+
esbuild: {
10+
jsx: 'automatic',
11+
},
12+
resolve: {
13+
alias: {
14+
// Handle .js imports in TypeScript files
15+
'~/': new URL('./src/', import.meta.url).pathname,
16+
},
17+
},
18+
});

0 commit comments

Comments
 (0)