Fix markdown rendering on Windows (#10185)

This commit is contained in:
Jacob Richman
2025-09-29 13:38:27 -07:00
committed by GitHub
parent 5478b58166
commit 94f43c79d0
3 changed files with 238 additions and 145 deletions
+131 -125
View File
@@ -7,7 +7,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MarkdownDisplay } from './MarkdownDisplay.js';
import { LoadedSettings } from '../../config/settings.js';
import { EOL } from 'node:os';
import { renderWithProviders } from '../../test-utils/render.js';
describe('<MarkdownDisplay />', () => {
@@ -36,132 +35,138 @@ describe('<MarkdownDisplay />', () => {
expect(lastFrame()).toMatchSnapshot();
});
it('renders headers with correct levels', () => {
const text = `
const lineEndings = [
{ name: 'Windows', eol: '\r\n' },
{ name: 'Unix', eol: '\n' },
];
describe.each(lineEndings)('with $name line endings', ({ eol }) => {
it('renders headers with correct levels', () => {
const text = `
# Header 1
## Header 2
### Header 3
#### Header 4
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders a fenced code block with a language', () => {
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```'.replace(
/\n/g,
EOL,
);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders a fenced code block with a language', () => {
const text = '```javascript\nconst x = 1;\nconsole.log(x);\n```'.replace(
/\n/g,
eol,
);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders a fenced code block without a language', () => {
const text = '```\nplain text\n```'.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders a fenced code block without a language', () => {
const text = '```\nplain text\n```'.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('handles unclosed (pending) code blocks', () => {
const text = '```typescript\nlet y = 2;'.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} isPending={true} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('handles unclosed (pending) code blocks', () => {
const text = '```typescript\nlet y = 2;'.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} isPending={true} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders unordered lists with different markers', () => {
const text = `
it('renders unordered lists with different markers', () => {
const text = `
- item A
* item B
+ item C
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders nested unordered lists', () => {
const text = `
it('renders nested unordered lists', () => {
const text = `
* Level 1
* Level 2
* Level 3
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders ordered lists', () => {
const text = `
it('renders ordered lists', () => {
const text = `
1. First item
2. Second item
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders horizontal rules', () => {
const text = `
it('renders horizontal rules', () => {
const text = `
Hello
---
World
***
Test
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('renders tables correctly', () => {
const text = `
it('renders tables correctly', () => {
const text = `
| Header 1 | Header 2 |
|----------|:--------:|
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('handles a table at the end of the input', () => {
const text = `
it('handles a table at the end of the input', () => {
const text = `
Some text before.
| A | B |
|---|
| 1 | 2 |`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
| 1 | 2 |`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('inserts a single space between paragraphs', () => {
const text = `Paragraph 1.
it('inserts a single space between paragraphs', () => {
const text = `Paragraph 1.
Paragraph 2.`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
Paragraph 2.`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('correctly parses a mix of markdown elements', () => {
const text = `
it('correctly parses a mix of markdown elements', () => {
const text = `
# Main Title
Here is a paragraph.
@@ -174,42 +179,43 @@ some code
\`\`\`
Another paragraph.
`.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
`.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
});
it('hides line numbers in code blocks when showLineNumbers is false', () => {
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
const settings = new LoadedSettings(
{ path: '', settings: {}, originalSettings: {} },
{ path: '', settings: {}, originalSettings: {} },
{
path: '',
settings: { ui: { showLineNumbers: false } },
originalSettings: { ui: { showLineNumbers: false } },
},
{ path: '', settings: {}, originalSettings: {} },
true,
new Set(),
);
it('hides line numbers in code blocks when showLineNumbers is false', () => {
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, eol);
const settings = new LoadedSettings(
{ path: '', settings: {}, originalSettings: {} },
{ path: '', settings: {}, originalSettings: {} },
{
path: '',
settings: { ui: { showLineNumbers: false } },
originalSettings: { ui: { showLineNumbers: false } },
},
{ path: '', settings: {}, originalSettings: {} },
true,
new Set(),
);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
{ settings },
);
expect(lastFrame()).toMatchSnapshot();
expect(lastFrame()).not.toContain(' 1 ');
});
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
{ settings },
);
expect(lastFrame()).toMatchSnapshot();
expect(lastFrame()).not.toContain(' 1 ');
});
it('shows line numbers in code blocks by default', () => {
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, EOL);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
expect(lastFrame()).toContain(' 1 ');
it('shows line numbers in code blocks by default', () => {
const text = '```javascript\nconst x = 1;\n```'.replace(/\n/g, eol);
const { lastFrame } = renderWithProviders(
<MarkdownDisplay {...baseProps} text={text} />,
);
expect(lastFrame()).toMatchSnapshot();
expect(lastFrame()).toContain(' 1 ');
});
});
});