-
Notifications
You must be signed in to change notification settings - Fork 18
chore: add readme components tests #1434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
8725bc3
eb0f433
a9c0140
4303ed0
8532d70
2fdb1e4
9e8c3f8
c908f8e
fd0c68a
08ac812
522c76a
4678c6b
ce1574b
68e02d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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(); | ||
| }); | ||
| }); |
| 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'; | ||
| import { render, screen } from '@testing-library/react'; | ||
| import React from 'react'; | ||
|
|
||
| import { expect } from 'vitest'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I should have explained earlier. I think typescript checks the global |
||
|
|
||
| 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', () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
|
|
||
| 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(); | ||||||||
| }); | ||||||||
| }); | ||||||||
| }); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
There was a problem hiding this comment.
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?