Skip to content

Commit 903d10e

Browse files
add theme switch and unsupported block tests
1 parent f4a2080 commit 903d10e

File tree

7 files changed

+258
-4
lines changed

7 files changed

+258
-4
lines changed

typescript/eslint.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ module.exports = [
9797
"coverage/**",
9898
".jest/**",
9999
"scripts/**",
100-
"tests/**",
101100
"**/generated/**",
102-
"src/serialization/**",
103101
],
104102
},
105103
];

typescript/src/renderer/components/BlockRenderer.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,12 @@ export const BlockRenderer: React.FC<BlockRendererProps> = ({
225225
// Fallback for unsupported block types
226226
console.warn("Unsupported block type:", block?.type);
227227
return (
228-
<div className="notion-unsupported-block" data-block-type={block?.type}>
229-
<span>Unsupported block type: {block?.type}</span>
228+
<div
229+
className="notion-unsupported-block"
230+
data-block-type={block?.type}
231+
role="alert"
232+
>
233+
<span>Error Unsupported block type: {block?.type}</span>
230234
</div>
231235
);
232236
};

typescript/tests/BlockRenderer.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const createPageWithBlocks = (blocks: any[]) => ({
3131
describe("JsonDocRenderer - All Block Types", () => {
3232
it("renders page title correctly", () => {
3333
render(<JsonDocRenderer page={mockPageWithAllBlocks} />);
34+
3435
expect(
3536
screen.getByText("Test Page with All Block Types")
3637
).toBeInTheDocument();
@@ -49,6 +50,8 @@ describe("JsonDocRenderer - All Block Types", () => {
4950
const page = createPageWithBlocks([mockBlocks.heading_1]);
5051
render(<JsonDocRenderer page={page} />);
5152

53+
screen.debug();
54+
5255
expect(screen.getByText("Main Heading")).toBeInTheDocument();
5356
});
5457

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import "@testing-library/jest-dom";
2+
3+
import React from "react";
4+
import { render, screen } from "@testing-library/react";
5+
import { describe, it, expect } from "vitest";
6+
import { JsonDocRenderer } from "../src/renderer/JsonDocRenderer";
7+
import { mockBlocks, mockPageWithAllBlocks } from "./fixtures/test-blocks";
8+
9+
// Helper to create a simple page for theme testing
10+
const createSimplePage = () => ({
11+
object: "page",
12+
id: "theme-test-page",
13+
properties: {
14+
title: {
15+
type: "title",
16+
title: [
17+
{
18+
href: null,
19+
type: "text",
20+
text: { link: null, content: "Theme Test Page" },
21+
annotations: {},
22+
plain_text: "Theme Test Page",
23+
},
24+
],
25+
},
26+
},
27+
children: [mockBlocks.paragraph],
28+
});
29+
30+
const JSON_DOC_ROOT_TEST_ID = "jsondoc-renderer-root";
31+
32+
describe("JsonDocRenderer - Theme Switching", () => {
33+
const simplePage = createSimplePage();
34+
35+
it("renders with light theme by default", () => {
36+
const { container } = render(<JsonDocRenderer page={simplePage} />);
37+
38+
const rendererDiv = screen.getByTestId(JSON_DOC_ROOT_TEST_ID);
39+
expect(rendererDiv).toHaveClass("jsondoc-theme-light");
40+
expect(rendererDiv).not.toHaveClass("jsondoc-theme-dark");
41+
});
42+
43+
it("renders with light theme when explicitly set", () => {
44+
const { container } = render(
45+
<JsonDocRenderer page={simplePage} theme="light" />
46+
);
47+
48+
const rendererDiv = screen.getByTestId(JSON_DOC_ROOT_TEST_ID);
49+
expect(rendererDiv).toHaveClass("jsondoc-theme-light");
50+
expect(rendererDiv).not.toHaveClass("jsondoc-theme-dark");
51+
});
52+
53+
it("renders with dark theme when theme prop is set to dark", () => {
54+
const { container } = render(
55+
<JsonDocRenderer page={simplePage} theme="dark" />
56+
);
57+
58+
const rendererDiv = screen.getByTestId(JSON_DOC_ROOT_TEST_ID);
59+
expect(rendererDiv).toHaveClass("jsondoc-theme-dark");
60+
expect(rendererDiv).not.toHaveClass("jsondoc-theme-light");
61+
});
62+
63+
it("handles undefined theme prop gracefully", () => {
64+
const { container } = render(
65+
<JsonDocRenderer page={simplePage} theme={undefined} />
66+
);
67+
68+
const rendererDiv = screen.getByTestId(JSON_DOC_ROOT_TEST_ID);
69+
expect(rendererDiv).toHaveClass("jsondoc-theme-light");
70+
});
71+
72+
it("theme persists across different block types", () => {
73+
const { container } = render(
74+
<JsonDocRenderer page={mockPageWithAllBlocks} theme="dark" />
75+
);
76+
77+
const rendererDiv = screen.getByTestId(JSON_DOC_ROOT_TEST_ID);
78+
expect(rendererDiv).toHaveClass("jsondoc-theme-dark");
79+
80+
// Verify various block types are still rendered
81+
expect(screen.getByText("This is a paragraph with")).toBeInTheDocument();
82+
expect(screen.getByText("First bullet point")).toBeInTheDocument();
83+
expect(
84+
screen.getByText('console.log("Hello, World!");')
85+
).toBeInTheDocument();
86+
});
87+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from "react";
2+
import { render, screen } from "@testing-library/react";
3+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
4+
import { JsonDocRenderer } from "../src/renderer/JsonDocRenderer";
5+
import { mockBlocks } from "./fixtures/test-blocks";
6+
7+
// Helper to create a page with specific blocks
8+
const createPageWithBlocks = (blocks: any[]) => ({
9+
object: "page",
10+
id: "test-page",
11+
properties: {
12+
title: {
13+
type: "title",
14+
title: [
15+
{
16+
href: null,
17+
type: "text",
18+
text: { link: null, content: "Test Page" },
19+
annotations: {},
20+
plain_text: "Test Page",
21+
},
22+
],
23+
},
24+
},
25+
children: blocks,
26+
});
27+
28+
describe("Unsupported Block Handling", () => {
29+
// Mock console.warn to test logging behavior
30+
const originalConsoleWarn = console.warn;
31+
beforeEach(() => {
32+
console.warn = vi.fn();
33+
});
34+
afterEach(() => {
35+
console.warn = originalConsoleWarn;
36+
});
37+
38+
const warningMsg = expect.stringMatching(/Unsupported block type:/);
39+
40+
it("renders fallback UI for unsupported block types", () => {
41+
const page = createPageWithBlocks([mockBlocks.unsupported_with_content]);
42+
const { container } = render(<JsonDocRenderer page={page} />);
43+
44+
// Should still render fallback UI regardless of block content
45+
expect(
46+
screen.getByText(/Unsupported block type: ai_block/)
47+
).toBeInTheDocument();
48+
49+
expect(screen.getByRole("alert")).toBeInTheDocument();
50+
});
51+
52+
it("renders unsupported blocks with children gracefully", () => {
53+
const page = createPageWithBlocks([mockBlocks.unsupported_with_children]);
54+
const { container } = render(<JsonDocRenderer page={page} />);
55+
56+
// Should show fallback for the unsupported parent block
57+
expect(
58+
screen.getByText(/Unsupported block type: template_block/)
59+
).toBeInTheDocument();
60+
61+
expect(
62+
container.querySelector('[data-block-type="template_block"]')
63+
).toBeInTheDocument();
64+
65+
// Children should not be rendered within unsupported block fallback
66+
expect(
67+
screen.queryByText(/Content inside unsupported block/)
68+
).not.toBeInTheDocument();
69+
});
70+
71+
it("handles multiple unsupported blocks without breaking renderer", () => {
72+
const page = createPageWithBlocks([
73+
mockBlocks.unsupported_single,
74+
mockBlocks.paragraph, // Mix with supported block
75+
mockBlocks.unsupported_with_content,
76+
mockBlocks.heading_1, // Another supported block
77+
mockBlocks.unsupported_with_children,
78+
]);
79+
const { container } = render(<JsonDocRenderer page={page} />);
80+
81+
// All unsupported blocks should have fallback UI
82+
expect(
83+
screen.getByText(/Unsupported block type: custom_widget/)
84+
).toBeInTheDocument();
85+
expect(
86+
screen.getByText(/Unsupported block type: ai_block/)
87+
).toBeInTheDocument();
88+
expect(
89+
screen.getByText(/Unsupported block type: template_block/)
90+
).toBeInTheDocument();
91+
92+
// Supported blocks should still render normally
93+
expect(screen.getByText(/This is a paragraph with/)).toBeInTheDocument();
94+
expect(screen.getByText(/Main Heading/)).toBeInTheDocument();
95+
96+
// Check all unsupported blocks have correct CSS class
97+
expect(screen.getAllByRole("alert")).toHaveLength(3);
98+
99+
// Verify all warnings were logged
100+
expect(console.warn).toHaveBeenCalledTimes(3);
101+
expect(console.warn).toHaveBeenCalledWith(warningMsg, "custom_widget");
102+
expect(console.warn).toHaveBeenCalledWith(warningMsg, "ai_block");
103+
expect(console.warn).toHaveBeenCalledWith(warningMsg, "template_block");
104+
});
105+
});

typescript/tests/fixtures/test-blocks.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,59 @@ export const mockBlocks: Record<
454454
expression: "E = mc^2",
455455
},
456456
},
457+
458+
// Mock unsupported block types for testing fallback behavior
459+
unsupported_single: {
460+
object: "block",
461+
id: "unsupported-1",
462+
type: "custom_widget" as any,
463+
created_time: "2025-06-24T09:44:12.014249Z",
464+
has_children: false,
465+
},
466+
467+
unsupported_with_content: {
468+
object: "block",
469+
id: "unsupported-2",
470+
type: "ai_block" as any,
471+
created_time: "2025-06-24T09:44:12.014249Z",
472+
has_children: false,
473+
// @ts-expect-error
474+
ai_block: {
475+
prompt: "Generate something amazing",
476+
},
477+
},
478+
479+
unsupported_with_children: {
480+
object: "block",
481+
id: "unsupported-3",
482+
type: "template_block" as any,
483+
created_time: "2025-06-24T09:44:12.014249Z",
484+
has_children: true,
485+
template_block: {
486+
template_id: "template-123",
487+
},
488+
children: [
489+
{
490+
object: "block",
491+
id: "h1-1",
492+
// @ts-ignore
493+
type: "paragraph",
494+
created_time: "2025-06-24T09:44:12.014249Z",
495+
has_children: false,
496+
// @ts-ignore
497+
heading_1: {
498+
rich_text: [
499+
{
500+
type: "text",
501+
text: { content: "Main Heading" },
502+
annotations: {},
503+
plain_text: "Main Heading",
504+
},
505+
],
506+
},
507+
},
508+
],
509+
},
457510
};
458511

459512
// Complete page with all block types for comprehensive testing

typescript/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
24
"compilerOptions": {
35
"target": "es2022",
46
"module": "NodeNext",
@@ -16,6 +18,8 @@
1618
"resolveJsonModule": true,
1719
"sourceMap": true,
1820
"allowJs": true,
21+
"noUnusedLocals": true,
22+
"noUnusedParameters": true,
1923
// "noUncheckedIndexedAccess": true,
2024
"baseUrl": ".",
2125
"paths": {

0 commit comments

Comments
 (0)