From 8735a49c8ee2a1ae8a1b4bfcad985a093daf4127 Mon Sep 17 00:00:00 2001 From: Chris Simmons Date: Thu, 15 Aug 2024 14:27:51 -0500 Subject: [PATCH] NEXT: Resolve Unit Tests - Part 2 (#2814) --- .changeset/strong-tables-whisper.md | 5 + .../ProgressRing/ProgressRing.test.tsx | 40 +- .../src/lib/components/Rating/Rating.test.tsx | 54 +++ .../src/lib/components/Rating/Rating.tsx | 7 +- .../lib/components/Segment/Segment.test.tsx | 135 +++++-- .../src/lib/components/Switch/Switch.test.tsx | 29 +- .../src/lib/components/Tabs/Tabs.test.tsx | 351 +++++++----------- .../src/routes/components/ratings.tsx | 1 - 8 files changed, 348 insertions(+), 274 deletions(-) create mode 100644 .changeset/strong-tables-whisper.md create mode 100644 packages/skeleton-react/src/lib/components/Rating/Rating.test.tsx diff --git a/.changeset/strong-tables-whisper.md b/.changeset/strong-tables-whisper.md new file mode 100644 index 000000000..cb6337e08 --- /dev/null +++ b/.changeset/strong-tables-whisper.md @@ -0,0 +1,5 @@ +--- +"@skeletonlabs/skeleton-react": patch +--- + +Resolved regressions in the following test cases: progress ring, ratings, segment control, switch, and tabs diff --git a/packages/skeleton-react/src/lib/components/ProgressRing/ProgressRing.test.tsx b/packages/skeleton-react/src/lib/components/ProgressRing/ProgressRing.test.tsx index 13b52feb8..084587cba 100644 --- a/packages/skeleton-react/src/lib/components/ProgressRing/ProgressRing.test.tsx +++ b/packages/skeleton-react/src/lib/components/ProgressRing/ProgressRing.test.tsx @@ -10,7 +10,7 @@ describe('', () => { expect(component).toBeInTheDocument(); }); - it('should render the component with a value and max', () => { + it('should render the component with a value and max props', () => { const value = 50; const max = 100; const { getByTestId } = render(); @@ -18,16 +18,46 @@ describe('', () => { expect(component).toBeInTheDocument(); }); - it('should render the component with a default child', () => { + it('should render the value percentage text', () => { const value = 50; const max = 100; - const defaultChildText = 'TestChild'; + const { getByTestId } = render(); + const component = getByTestId('progress-ring-label'); + expect(component).toHaveTextContent(`${value}%`); + }); + + it('should render the component with child content', () => { + const value = 50; + const max = 100; + const childContent = 'TestChild'; const { getByText } = render( - {defaultChildText} + {childContent} ); - const text = getByText(defaultChildText); + const text = getByText(childContent); expect(text).toBeInTheDocument(); }); + + it('should render in indeterminate mode', () => { + const { getByTestId } = render(); + const componentRoot = getByTestId('progress-ring'); + const componentSvg = getByTestId('progress-ring-svg'); + expect(componentRoot.dataset.state).toBe('indeterminate'); + expect(componentSvg).toHaveClass('animate-spin'); + }); + + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('progress-ring'); + expect(component).toHaveClass(testClass); + }); + + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('progress-ring'); + expect(component).toHaveClass(testClass); + }); }); diff --git a/packages/skeleton-react/src/lib/components/Rating/Rating.test.tsx b/packages/skeleton-react/src/lib/components/Rating/Rating.test.tsx new file mode 100644 index 000000000..946895da5 --- /dev/null +++ b/packages/skeleton-react/src/lib/components/Rating/Rating.test.tsx @@ -0,0 +1,54 @@ +import { describe, expect, it } from 'vitest'; +import { render } from '@testing-library/react'; + +import { Rating } from './Rating.js'; + +describe('', () => { + it('should render the component', () => { + const { getByTestId } = render(); + const component = getByTestId('rating'); + expect(component).toBeInTheDocument(); + }); + + it('should render with a value', () => { + const testValue = 14; + const { getByTestId } = render(); + const input = getByTestId('rating-input'); + expect(input.getAttribute('value')).eq(`${testValue}`); + }); + + it('should render with custom iconEmpty', () => { + const testIconEmpty = 'testIconEmpty'; + const { getAllByTestId } = render(); + const elementIcons = getAllByTestId('rating-item'); + expect(elementIcons[0]).toHaveTextContent(testIconEmpty); + }); + + it('should render with custom iconHalf', () => { + const testIconHalf = 'testIconHalf'; + const { getAllByTestId } = render(); + const elementIcons = getAllByTestId('rating-item'); + expect(elementIcons[0]).toHaveTextContent(testIconHalf); + }); + + it('should render with custom iconFull', () => { + const testIconFull = 'testIconFull'; + const { getAllByTestId } = render(); + const elementIcons = getAllByTestId('rating-item'); + expect(elementIcons[0]).toHaveTextContent(testIconFull); + }); + + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('rating'); + expect(component).toHaveClass(testClass); + }); + + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('rating'); + expect(component).toHaveClass(testClass); + }); +}); diff --git a/packages/skeleton-react/src/lib/components/Rating/Rating.tsx b/packages/skeleton-react/src/lib/components/Rating/Rating.tsx index d099afb23..337ad3583 100644 --- a/packages/skeleton-react/src/lib/components/Rating/Rating.tsx +++ b/packages/skeleton-react/src/lib/components/Rating/Rating.tsx @@ -65,8 +65,9 @@ export const Rating: FC = ({ className={`${controlBase} ${controlGap} ${rxInteractive} ${rxReadOnly} ${rxDisabled} ${controlClasses}`} data-testid="rating-control" > - {api.items.map((index) => { - const itemState = api.getItemState({ index }); + {/* FIXME: `item` is causing React key error; is not unique on re-render? */} + {api.items.map((item) => { + const itemState = api.getItemState({ index: item }); const icon = (() => { if (!itemState.highlighted) { return iconEmpty; @@ -79,7 +80,7 @@ export const Rating: FC = ({ return ( <> {/* Item */} - + {icon} diff --git a/packages/skeleton-react/src/lib/components/Segment/Segment.test.tsx b/packages/skeleton-react/src/lib/components/Segment/Segment.test.tsx index ac3712443..121c5bf93 100644 --- a/packages/skeleton-react/src/lib/components/Segment/Segment.test.tsx +++ b/packages/skeleton-react/src/lib/components/Segment/Segment.test.tsx @@ -27,15 +27,46 @@ describe('', () => { expect(component.getAttribute('class')).toContain(testClasses); }); - it('should render children', () => { - const testTextContent = 'testTextContent'; + it('should render in the disabled state', () => { const { getByTestId } = render( - - {testTextContent} + + TestItem1 ); const component = getByTestId('segment'); - expect(component).toHaveTextContent(testTextContent); + expect(component.dataset.disabled).toBeDefined(); + }); + + it('should render in the read-only state', () => { + const { getByTestId } = render( + + TestItem1 + + ); + const component = getByTestId('segment'); + expect(component).toHaveClass('pointer-events-none'); + }); + + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + TestItem1 + + ); + const component = getByTestId('segment'); + expect(component).toHaveClass(testClass); + }); + + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + TestItem1 + + ); + const component = getByTestId('segment'); + expect(component).toHaveClass(testClass); }); }); @@ -52,7 +83,7 @@ describe('', () => { expect(component).toBeInTheDocument(); }); - it('should render children', () => { + it('should render custom child content', () => { const testTextContent = 'testTextContent'; const { getByTestId } = render( @@ -63,37 +94,63 @@ describe('', () => { expect(component).toHaveTextContent(testTextContent); }); - // FIXME: resolve after Zag migration - - // it('should render the component in the unchecked state', () => { - // const { getByTestId } = render( - // - // Foo - // - // ); - // const component = getByTestId('segment-item'); - // const ariaSelected = component.getAttribute('aria-selected'); - // expect(ariaSelected).toBeFalsy; - // }); - - // it('should render the component in the checked state', () => { - // const { getByTestId } = render( - // - // Foo - // - // ); - // const component = getByTestId('segment-item'); - // const ariaSelected = component.getAttribute('aria-selected'); - // expect(ariaSelected).toBeTruthy; - // }); - - // it('should render the component in the disabled state', () => { - // const { getByTestId } = render( - // - // Foo - // - // ); - // const component = getByTestId('segment-item'); - // expect(component).toHaveAttribute('disabled'); - // }); + it('should render in the unchecked state', () => { + const { getByTestId } = render( + + TestItem1 + + ); + const component = getByTestId('segment-item'); + const ariaSelected = component.getAttribute('aria-selected'); + expect(ariaSelected).toBeFalsy; + }); + + it('should render in the checked state', () => { + const { getByTestId } = render( + + TestItem1 + + ); + const component = getByTestId('segment-item'); + const ariaSelected = component.getAttribute('aria-selected'); + expect(ariaSelected).toBeTruthy; + }); + + it('should render in the disabled state', () => { + const { getByTestId } = render( + + + TestItem1 + + + ); + const component = getByTestId('segment-item-input'); + expect(component).toHaveAttribute('disabled'); + }); + + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + TestItem1 + + + ); + const component = getByTestId('segment-item'); + expect(component).toHaveClass(testClass); + }); + + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + TestItem1 + + + ); + const component = getByTestId('segment-item'); + expect(component).toHaveClass(testClass); + }); }); diff --git a/packages/skeleton-react/src/lib/components/Switch/Switch.test.tsx b/packages/skeleton-react/src/lib/components/Switch/Switch.test.tsx index ae9a597ee..e3ad3b0f2 100644 --- a/packages/skeleton-react/src/lib/components/Switch/Switch.test.tsx +++ b/packages/skeleton-react/src/lib/components/Switch/Switch.test.tsx @@ -12,16 +12,14 @@ describe('', () => { it('should render the component in the off state', () => { const { getByTestId } = render(); - const component = getByTestId('switch'); - const ariaChecked = component.getAttribute('aria-checked'); - expect(ariaChecked).toBeFalsy; + const component = getByTestId('switch-input'); + expect(component).not.toHaveAttribute('checked'); }); it('should render the component in the on state', () => { const { getByTestId } = render(); - const component = getByTestId('switch'); - const ariaChecked = component.getAttribute('aria-checked'); - expect(ariaChecked).toBeTruthy; + const component = getByTestId('switch-input'); + expect(component).toHaveAttribute('checked'); }); it('should render the component with an inactive icon', () => { @@ -42,9 +40,8 @@ describe('', () => { it('should render the component in the disabled state', () => { const { getByTestId } = render(); - const component = getByTestId('switch-control'); - expect(component).toHaveClass('opacity-50'); - expect(component).toHaveClass('cursor-not-allowed'); + const component = getByTestId('switch-input'); + expect(component).toHaveAttribute('disabled'); }); it('should render the component in the compact mode', () => { @@ -52,4 +49,18 @@ describe('', () => { const component = getByTestId('switch-control'); expect(component).toHaveClass('aspect-square'); }); + + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('switch'); + expect(component).toHaveClass(testClass); + }); + + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render(); + const component = getByTestId('switch'); + expect(component).toHaveClass(testClass); + }); }); diff --git a/packages/skeleton-react/src/lib/components/Tabs/Tabs.test.tsx b/packages/skeleton-react/src/lib/components/Tabs/Tabs.test.tsx index 657c2d11c..50055aff8 100644 --- a/packages/skeleton-react/src/lib/components/Tabs/Tabs.test.tsx +++ b/packages/skeleton-react/src/lib/components/Tabs/Tabs.test.tsx @@ -1,234 +1,151 @@ -// import { describe, expect, it, vi } from 'vitest'; -// import { act, render } from '@testing-library/react'; -// import { Tabs } from './Tabs.js'; -// import userEvent from '@testing-library/user-event'; - import { describe, expect, it } from 'vitest'; import { render } from '@testing-library/react'; -import { Tabs } from './Tabs.js'; - -// // ************************* -// // Integration Tests -// // ************************* - -// describe('Tabs usage', () => { -// it('should show the panel when the control is clicked', async () => { -// const setGroup = vi.fn(); - -// const tabsComponent = (group: string) => ( -// -// -// -// test control 1 -// -// -// test control 2 -// -// -// -// test Panel 1 -// -// -// test Panel 2 -// -// -// ); - -// const { getByText, queryByText, rerender } = render(tabsComponent('test1')); - -// const control1 = getByText('test control 1'); -// const control2 = getByText('test control 2'); - -// // expect controls to be visible -// expect(control1).toBeInTheDocument(); -// expect(control2).toBeInTheDocument(); - -// // expect first panel to be visible -// expect(queryByText('test Panel 1')).toBeInTheDocument(); - -// // expect second panel to be hidden -// expect(queryByText('test control 2')).toBeInTheDocument(); -// // click second control -// await act(async () => { -// await userEvent.click(control2); -// }); - -// // make sure setGroup is called after click -// expect(setGroup).toHaveBeenCalledWith('test2'); -// // rerender the component with the new group value -// rerender(tabsComponent('test2')); - -// // expect first panel to be hidden -// expect(queryByText('test Panel 1')).not.toBeInTheDocument(); -// expect(queryByText('test Panel 2')).toBeInTheDocument(); -// }); -// }); - -// // ************************* -// // Unit Tests -// // ************************* +import { Tabs } from './Tabs.js'; -// // Tabs --- +// Tabs (integration) describe('', () => { - it('should render the component', () => { - const { getByTestId } = render(); - expect(getByTestId('tabs')).toBeInTheDocument(); + it('should render the entire component group', () => { + const { getByTestId, getAllByTestId } = render( + + + Control-1 + Control-2 + + + Panel-1 + Panel-2 + + + ); + + const componentRoot = getByTestId('tabs'); + const componentList = getByTestId('tabs-list'); + const componentControls = getAllByTestId('tabs-control'); + const componentContent = getByTestId('tabs-content'); + const componentPanel = getAllByTestId('tabs-panel'); + + expect(componentRoot).toBeInTheDocument(); + expect(componentList).toBeInTheDocument(); + expect(componentControls[0]).toBeInTheDocument(); + expect(componentContent).toBeInTheDocument(); + expect(componentPanel[0]).toBeInTheDocument(); }); - - // it('should allow for children', () => { - // const value = 'children value'; - // const { getByTestId } = render({value}); - // expect(getByTestId('tabs').innerHTML).toContain(value); - // }); - - // it('should allow you to set the `base` style prop', () => { - // const tailwindClasses = 'bg-red-600'; - // const { getByTestId } = render(); - // expect(getByTestId('tabs')).toHaveClass(tailwindClasses); - // }); - - // it('should allow you to set the `classes` style prop', () => { - // const tailwindClasses = 'bg-green-600'; - // const { getByTestId } = render(); - // expect(getByTestId('tabs')).toHaveClass(tailwindClasses); - // }); }); -// // List --- - -// describe('', () => { -// it('should render the component', () => { -// const { getByRole } = render(); -// expect(getByRole('tablist')).toBeInTheDocument(); -// }); - -// it('should allow for children', () => { -// const value = 'children value'; -// const { getByRole } = render({value}); -// expect(getByRole('tablist').innerHTML).toContain(value); -// }); - -// it('should allow you to set the `base` style prop', () => { -// const tailwindClasses = 'flex-col'; -// const { getByRole } = render(); -// expect(getByRole('tablist')).toHaveClass(tailwindClasses); -// }); +// Tabs --- -// it('should allow you to set the `classes` style prop', () => { -// const tailwindClasses = 'bg-green-600'; -// const { getByRole } = render(); -// expect(getByRole('tablist')).toHaveClass(tailwindClasses); -// }); -// }); - -// // Control --- - -// describe('', () => { -// it('should render the component', () => { -// const { getByTestId } = render(); -// expect(getByTestId('tabs-control')).toBeInTheDocument(); -// }); - -// it('should render with `name` prop', () => { -// const name = 'testName'; -// const { getByRole } = render(); -// const radioInput = getByRole('radio'); -// expect(radioInput).toBeInTheDocument(); -// expect(radioInput).toHaveAttribute('name', name); -// expect(radioInput).toHaveAttribute('value', name); -// }); - -// it('should set `aria-controls` to `controls` value', () => { -// const controls = 'test controls'; -// const { getByTestId } = render(); -// expect(getByTestId('tabs-control')).toHaveAttribute('aria-controls', controls); -// }); - -// it('should set `aria-label` to `label` value', () => { -// const label = 'test label'; -// const { getByTestId } = render(); -// expect(getByTestId('tabs-control').parentElement).toHaveAttribute('aria-label', label); -// }); - -// it('should allow for children', () => { -// const value = 'children value'; -// const { getByTestId } = render( -// -// {value} -// -// ); -// expect(getByTestId('tabs-control').innerHTML).toContain(value); -// }); - -// it('should allow you to set the `base` style prop', () => { -// const tailwindClasses = 'bg-blue-600'; -// const { getByTestId } = render(); -// expect(getByTestId('tabs-control').parentElement).toHaveClass(tailwindClasses); -// }); - -// it('should allow you to set the `classes` style prop', () => { -// const tailwindClasses = 'bg-blue-600'; -// const { getByTestId } = render(); -// expect(getByTestId('tabs-control').parentElement).toHaveClass(tailwindClasses); -// }); -// }); +describe('', () => { + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + Test + + ); + const component = getByTestId('tabs'); + expect(component).toHaveClass(testClass); + }); -// // Panel --- + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + Test + + ); + const component = getByTestId('tabs'); + expect(component).toHaveClass(testClass); + }); +}); -// describe('', () => { -// it('should render the component', () => { -// const { getByRole } = render( -// -// content -// -// ); -// expect(getByRole('tabpanel')).toBeInTheDocument(); -// }); +describe('', () => { + it('should render custom child content', () => { + const testContent = 'someTextContent'; + const { getByTestId } = render( + + + {testContent} + + + ); + const componentControl = getByTestId('tabs-control'); + expect(componentControl).toHaveTextContent(testContent); + }); -// it('should not render the component with no content', () => { -// const { queryByRole } = render(); -// expect(queryByRole('tabpanel')).not.toBeInTheDocument(); -// }); + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + + Test + + + + ); + const component = getByTestId('tabs-control'); + expect(component).toHaveClass(testClass); + }); -// it('should set `id` to `id` value', () => { -// const id = 'test id'; -// const { getByRole } = render( -// -// content -// -// ); -// expect(getByRole('tabpanel')).toHaveAttribute('id', id); -// }); + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + + Test + + + + ); + const component = getByTestId('tabs-control'); + expect(component).toHaveClass(testClass); + }); +}); -// it('should set `aria-labelledby` to `labelledby` value', () => { -// const labelledBy = 'test labelledby'; -// const { getByRole } = render( -// -// content -// -// ); -// expect(getByRole('tabpanel')).toHaveAttribute('aria-labelledby', labelledBy); -// }); +describe('', () => { + it('should render custom child content', () => { + const testContent = 'someTextContent'; + const { getByTestId } = render( + + + {testContent} + + + ); + const componentControl = getByTestId('tabs-panel'); + expect(componentControl).toHaveTextContent(testContent); + }); -// it('should allow for children', () => { -// const value = 'children value'; -// const { getByRole } = render( -// -// {value} -// -// ); -// expect(getByRole('tabpanel').innerHTML).toContain(value); -// }); + it('should render base classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + + Test + + + + ); + const component = getByTestId('tabs-panel'); + expect(component).toHaveClass(testClass); + }); -// it('should allow you to set the `classes` style prop', () => { -// const tailwindClasses = 'bg-blue-600'; -// const { getByRole } = render( -// -// content -// -// ); -// expect(getByRole('tabpanel')).toHaveClass(tailwindClasses); -// }); -// }); + it('should render arbitrary classes on root', () => { + const testClass = 'bg-green-500'; + const { getByTestId } = render( + + + + Test + + + + ); + const component = getByTestId('tabs-panel'); + expect(component).toHaveClass(testClass); + }); +}); diff --git a/packages/skeleton-react/src/routes/components/ratings.tsx b/packages/skeleton-react/src/routes/components/ratings.tsx index 119fb7c69..702cc8a29 100644 --- a/packages/skeleton-react/src/routes/components/ratings.tsx +++ b/packages/skeleton-react/src/routes/components/ratings.tsx @@ -34,7 +34,6 @@ export function Component() {

Disabled

-

RTL