Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 21 additions & 50 deletions __tests__/components/Anchor.test.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,37 @@
import { render, screen } from '@testing-library/react';
import React from 'react';

import Anchor from '../../components/Anchor';
import { renderingEngines } from './utils';

describe('Anchor', () => {
it('renders a basic anchor', () => {
render(<Anchor href="https://example.com">Click me</Anchor>);
it.each(renderingEngines)('%s: renders a basic anchor', (_label, renderContent) => {
const md = '<Anchor href="https://example.com">Click me</Anchor>';
const Content = renderContent(md);

expect(screen.getByRole('link')).toMatchInlineSnapshot(`
<a
href="https://example.com"
target=""
title=""
>
Click me
</a>
`);
render(<Content />);

expect(screen.getByRole('link')).toMatchSnapshot();
});

it('unwraps nested anchor elements', () => {
// Simulates what happens when GFM autolinks URL-like text inside an Anchor
const { container } = render(
<Anchor href="https://example.com" target="_blank">
<a href="https://example.com">https://example.com</a>
</Anchor>,
);
it.each(renderingEngines)('%s: unwraps nested anchor elements', (_label, renderContent) => {
// GFM autolinks URL-like text inside an Anchor; output must be a single <a>
const md = '<Anchor href="https://example.com" target="_blank">https://example.com</Anchor>';
const Content = renderContent(md);

const { container } = render(<Content />);

// Should only have one <a> tag, not nested
const anchors = container.querySelectorAll('a');
expect(anchors).toHaveLength(1);
expect(anchors[0]).toMatchInlineSnapshot(`
<a
href="https://example.com"
target="_blank"
title=""
>
https://example.com
</a>
`);
expect(anchors[0]).toMatchSnapshot();
});

it('preserves non-anchor children', () => {
render(
<Anchor href="https://example.com">
<strong>Bold</strong> and <em>italic</em>
</Anchor>,
);
it.each(renderingEngines)('%s: preserves non-anchor children', (_label, renderContent) => {
const md =
'<Anchor href="https://example.com"><strong>Bold</strong> and <em>italic</em></Anchor>';
const Content = renderContent(md);

render(<Content />);

expect(screen.getByRole('link')).toMatchInlineSnapshot(`
<a
href="https://example.com"
target=""
title=""
>
<strong>
Bold
</strong>
and
<em>
italic
</em>
</a>
`);
expect(screen.getByRole('link')).toMatchSnapshot();
});
});
108 changes: 87 additions & 21 deletions __tests__/components/Callout.test.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,104 @@
import type { Element } from 'hast';

import '@testing-library/jest-dom/vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this import here?

import { render, screen } from '@testing-library/react';
import React from 'react';

import { expect } from 'vitest';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think expect is available globally with vitest and we don't need to actually import it here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I should have explained earlier. I think typescript checks the global expect from jest and not vitest's and I was getting type errors in some expect methods. Even though it's actually fine to run, because as you said vitest is available, it only injects its expect at runtime, so it runs but typescript doesn't see it. There's a bit of difference in the expect of jest & vitest, some methods we use here are not available in jest. Hence, I explicitly imported the vitest types with the import '@testing-library/jest-dom/vitest'; and the expect to fix the errors


import Callout from '../../components/Callout';
import { mdxish } from '../../lib';

import { renderingEngines } from './utils';

describe('Callout', () => {
it('render _all_ its children', () => {
render(
<Callout icon="icon" theme="theme" title="Title">
<p>Title</p>
<p>First Paragraph</p>
<p>Second Paragraph</p>
</Callout>,
);

expect(screen.getByText('Second Paragraph')).toBeVisible();
describe('general component rendering', () => {
it('render _all_ its children', () => {
render(
<Callout icon="icon" theme="theme" title="Title">
<p>Title</p>
<p>First Paragraph</p>
<p>Second Paragraph</p>
</Callout>,
);

expect(screen.getByText('Second Paragraph')).toBeVisible();
});

it("doesn't render all its children if it's **empty**", () => {
render(
<Callout empty icon="icon" theme="theme">
<p>Title</p>
<p>First Paragraph</p>
<p>Second Paragraph</p>
</Callout>,
);

expect(screen.queryByText('Title')).toBeNull();
});
});

it("doesn't render all its children if it's **empty**", () => {
render(
<Callout empty icon="icon" theme="theme">
<p>Title</p>
<p>First Paragraph</p>
<p>Second Paragraph</p>
</Callout>,
);
it.each(renderingEngines)('%s: renders a callout with icon and body', (_label, renderContent) => {
const md = `<Callout theme="info" icon="📘">
Hello there
</Callout>`;
const Content = renderContent(md);
const { container } = render(<Content />);

const blockquote = container.querySelector('blockquote');
expect(blockquote).toHaveClass('callout', 'callout_info');
expect(blockquote?.querySelector('.callout-icon')).toHaveTextContent('📘');
expect(blockquote?.querySelector('p')).toHaveTextContent('Hello there');
expect(container).toMatchSnapshot();
});

it.each(renderingEngines)('%s: renders the markdown inside the callout body', (_label, renderContent) => {
const md = `<Callout theme="info" icon="📘">
### This should be a heading

This should be **strong** text, *italic* text, and a ~strikethrough~ text.
</Callout>`;
const Content = renderContent(md);
const { container } = render(<Content />);

const blockquote = container.querySelector('blockquote');
expect(blockquote).toHaveClass('callout', 'callout_info');
expect(blockquote?.querySelector('h3')).toHaveTextContent('This should be a heading');
expect(blockquote?.querySelector('strong')).toHaveTextContent('strong');
expect(blockquote?.querySelector('em')).toHaveTextContent('italic');
expect(blockquote?.querySelector('del')).toHaveTextContent('strikethrough');
expect(container).toMatchSnapshot();
});

expect(screen.queryByText('Title')).toBeNull();
describe('given various callout structures', () => {
it.each(renderingEngines)('%s: should parse where there is space after the opening tag and before the closing tag', (_label, renderContent) => {
const mdWithSpaces = `<Callout theme="info" icon="📘">


## Heading here

Body with **markdown** support.



</Callout>`;
const mdWithoutSpaces = `<Callout theme="info" icon="📘">
## Heading here

Body with **markdown** support.
</Callout>`;

const ContentWithSpaces = renderContent(mdWithSpaces);
const { container: containerWithSpaces } = render(<ContentWithSpaces />);
const ContentWithoutSpaces = renderContent(mdWithoutSpaces);
const { container: containerWithoutSpaces } = render(<ContentWithoutSpaces />);

expect(containerWithSpaces.innerHTML).toBe(containerWithoutSpaces.innerHTML);
});
});

describe('mdxish', () => {
it('renders a callout with no title with no empty blank heading', () => {
describe('mdxish-specific behaviours', () => {
it('should correctly parse content when', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the description here get cut short? "should correctly parse content when"

When what?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry silly mistake from me, updated this!

const md = `<Callout theme="info" icon="📘">
### Title here

Expand Down
170 changes: 170 additions & 0 deletions __tests__/components/Cards.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import React from 'react';

import CardsGrid, { Card } from '../../components/Cards';

import { renderingEngines } from './utils';

describe('Cards', () => {
describe('general component rendering', () => {
it('renders a CardsGrid wrapper', () => {
const { container } = render(
<CardsGrid>
<Card title="First">Content</Card>
</CardsGrid>,
);
expect(container.querySelector('.CardsGrid')).toBeInTheDocument();
});

it('renders Card children with titles', () => {
const { container } = render(
<CardsGrid>
<Card title="First">First</Card>
<Card title="Second">Second</Card>
</CardsGrid>,
);
const cards = container.querySelectorAll('.Card');
expect(cards).toHaveLength(2);
expect(container.querySelectorAll('.Card-title')[0]).toHaveTextContent('First');
});

it('renders Card as an anchor when href is provided', () => {
const { container } = render(
<CardsGrid>
<Card href="https://example.com" title="Link">Linked</Card>
</CardsGrid>,
);
const card = container.querySelector('a.Card');
expect(card).toHaveAttribute('href', 'https://example.com');
expect(container.querySelector('.Card-arrow')).toBeInTheDocument();
});

it('renders Card as a div when no href', () => {
const { container } = render(
<CardsGrid>
<Card title="Static">Static</Card>
</CardsGrid>,
);
expect(container.querySelector('div.Card')).toBeInTheDocument();
});

it('renders icon and badge props', () => {
const { container } = render(
<CardsGrid>
<Card badge="New" icon="fa-star" title="Featured">Content</Card>
</CardsGrid>,
);
expect(container.querySelector('.Card-icon')).toBeInTheDocument();
expect(container.querySelector('.Card-badge')).toHaveTextContent('New');
});
});

describe('given a Cards with Card children', () => {
const md = `
<Cards>
<Card title="First">First content</Card>
<Card title="Second">Second content</Card>
</Cards>
`;

it.each(renderingEngines)('%s: renders a CardsGrid wrapper containing Card children', (_label, renderContent) => {
const Content = renderContent(md);
const { container } = render(<Content />);

expect(container.querySelector('.CardsGrid')).toBeInTheDocument();
expect(container.querySelectorAll('.Card')).toHaveLength(2);
expect(container.querySelectorAll('.Card-title')).toHaveLength(2);
expect(container).toMatchSnapshot();
});
});

describe('given a Card with href', () => {
const md = `
<Cards>
<Card title="Link Card" href="https://example.com">Linked</Card>
</Cards>
`;

it.each(renderingEngines)('%s: renders a Card as an anchor element', (_label, renderContent) => {
const Content = renderContent(md);
const { container } = render(<Content />);

expect(container.querySelector('a.Card')).toBeInTheDocument();
expect(container.querySelector('a.Card')).toHaveAttribute('href', 'https://example.com');
expect(container.querySelector('.Card-arrow')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

describe('given a Card without href', () => {
const md = `
<Cards>
<Card title="Static Card">Static</Card>
</Cards>
`;

it.each(renderingEngines)('%s: renders a Card as a div element', (_label, renderContent) => {
const Content = renderContent(md);
const { container } = render(<Content />);

expect(container.querySelector('div.Card')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

describe('given a Card with icon and badge props', () => {
const md = `
<Cards>
<Card title="Featured" icon="fa-star" badge="New">Featured content</Card>
</Cards>
`;

it.each(renderingEngines)('%s: renders an icon and badge element', (_label, renderContent) => {
const Content = renderContent(md);
const { container } = render(<Content />);

expect(container.querySelector('.Card-icon')).toBeInTheDocument();
expect(container.querySelector('.Card-badge')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

describe('given various card structures', () => {
it.each(renderingEngines)('%s: should parse where there is space after the opening tag and before the closing tag', (_label, renderContent) => {
const mdWithSpaces = `
<Cards>
<Card title="First">Content</Card>
<Card title="Second">More</Card>
</Cards>
`;
const mdWithoutSpaces = `

<Cards>

<Card title="First">Content</Card>

<Card title="Second">More</Card>
</Cards>
`;

const ContentWithSpaces = renderContent(mdWithSpaces);
const { container: containerWithSpaces } = render(<ContentWithSpaces />);
const ContentWithoutSpaces = renderContent(mdWithoutSpaces);
const { container: containerWithoutSpaces } = render(<ContentWithoutSpaces />);

expect(containerWithSpaces.innerHTML).toBe(containerWithoutSpaces.innerHTML);
});

it.each(renderingEngines)('%s: should parse when the code is in one line', (_label, renderContent) => {
const md = '<Cards><Card title="First">Content</Card><Card title="Second">More</Card></Cards>';
const Content = renderContent(md);
const { container } = render(<Content />);

expect(container.querySelector('.CardsGrid')).toBeInTheDocument();
const cards = container.querySelectorAll('.Card');
expect(cards).toHaveLength(2);
expect(container).toMatchSnapshot();
});
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
});
});

Loading