Skip to content

Commit

Permalink
test(ui-pages): migrate old Pages tests
Browse files Browse the repository at this point in the history
  • Loading branch information
git-nandor committed Feb 5, 2025
1 parent 359b73c commit 66b1ad9
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 74 deletions.
72 changes: 72 additions & 0 deletions cypress/component/Page.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 - present Instructure, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { useState } from 'react'
import { Pages, PagesPage } from '@instructure/ui-pages'

import '../support/component'
import 'cypress-real-events'

describe('<Page/>', () => {
it('should focus default element', async () => {
const Example = () => {
const [activePageIndex, setActivePageIndex] = useState(0)
let inputRef

const handleNextPageClick = () => {
setActivePageIndex(1)
}

const handleBackButtonClick = () => {
setActivePageIndex(0)
}

return (
<Pages activePageIndex={activePageIndex} onPageIndexChange={cy.spy()}>
<PagesPage defaultFocusElement={() => inputRef}>
<button onClick={handleNextPageClick}> Next Page </button>
</PagesPage>

<PagesPage defaultFocusElement={() => inputRef}>
<input type="text" />
<input
id="default-input"
type="text"
ref={(el) => {
inputRef = el
}}
/>
<input type="text" />
<button onClick={handleBackButtonClick}> Back </button>
</PagesPage>
</Pages>
)
}

cy.mount(<Example />)

cy.contains('button', 'Next Page').click()

cy.get('#default-input').should('be.focused')
})
})
6 changes: 5 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion packages/ui-pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
"@instructure/ui-babel-preset": "10.11.0",
"@instructure/ui-color-utils": "10.11.0",
"@instructure/ui-test-utils": "10.11.0",
"@instructure/ui-themes": "10.11.0"
"@instructure/ui-themes": "10.11.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"vitest": "^2.1.8"
},
"dependencies": {
"@babel/runtime": "^7.26.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,66 +23,41 @@
*/

import React from 'react'
import { expect, mount, within } from '@instructure/ui-test-utils'
import { render } from '@testing-library/react'
import { vi, expect } from 'vitest'
import type { MockInstance } from 'vitest'

import '@testing-library/jest-dom'
import { Page } from '../index'

describe('<Page />', async () => {
let _input: HTMLInputElement | null
describe('<Page />', () => {
let consoleErrorMock: ReturnType<typeof vi.spyOn>

it('should render with a function as child', async () => {
const subject = await mount(
<Page
defaultFocusElement={() => {
return _input
}}
>
{() => {
return (
<div>
<input
type="text"
ref={(el) => {
_input = el
}}
/>
<span>Hello World</span>
</div>
)
}}
</Page>
)
beforeEach(() => {
// Mocking console to prevent test output pollution
consoleErrorMock = vi
.spyOn(console, 'error')
.mockImplementation(() => {}) as MockInstance
})

expect(subject.getDOMNode().textContent).to.equal('Hello World')
afterEach(() => {
consoleErrorMock.mockRestore()
})

it('should focus default element', async () => {
const subject = await mount(
<Page
defaultFocusElement={() => {
return _input
}}
>
it('should render with a function as child', async () => {
const { container } = render(
<Page>
{() => {
return (
<div>
<input
type="text"
ref={(el) => {
_input = el
}}
/>
<input type="text" />
<span>Hello World</span>
</div>
)
}}
</Page>
)

const page = within(subject.getDOMNode())
const input = await page.find(':focusable')

await page.focus()

expect(input.focused()).to.be.true()
expect(container).toHaveTextContent('Hello World')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,81 +23,115 @@
*/

import React from 'react'
import { expect, mount, spy, within, stub } from '@instructure/ui-test-utils'
import { render, waitFor } from '@testing-library/react'
import { vi, expect } from 'vitest'
import type { MockInstance } from 'vitest'

import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'
import { Pages, Page } from '../index'

describe('<Pages />', async () => {
describe('<Pages />', () => {
let consoleErrorMock: ReturnType<typeof vi.spyOn>

beforeEach(() => {
// Mocking console to prevent test output pollution and expect for messages
consoleErrorMock = vi
.spyOn(console, 'error')
.mockImplementation(() => {}) as MockInstance
})

afterEach(() => {
consoleErrorMock.mockRestore()
})

it('should render', async () => {
const subject = await mount(
const { container } = render(
<Pages>
<Page>{() => 'Foo'}</Page>
<Page>{() => 'Bar'}</Page>
</Pages>
)
const pages = container.querySelector('div[id^="Pages_"]')

expect(subject.getDOMNode()).to.exist()
expect(pages).toBeInTheDocument()
})

it('should render a Page', async () => {
const subject = await mount(
const { container } = render(
<Pages>
<Page>{() => 'Hello World'}</Page>
</Pages>
)
const pages = container.querySelector('div[id^="Pages_"]')

expect(subject.getDOMNode().textContent).to.equal('Hello World')
expect(pages).toHaveTextContent('Hello World')
})

it('should render the 0th Page by default', async () => {
const subject = await mount(
const { container } = render(
<Pages>
<Page>{() => 'Foo'}</Page>
<Page>{() => 'Bar'}</Page>
</Pages>
)
expect(subject.getDOMNode().textContent).to.equal('Foo')
const pages = container.querySelector('div[id^="Pages_"]')

expect(pages).toHaveTextContent('Foo')
})

it('should render the active Page', async () => {
const subject = await mount(
const { container } = render(
<Pages activePageIndex={1} onPageIndexChange={() => {}}>
<Page>{() => 'Foo'}</Page>
<Page>{() => 'Bar'}</Page>
</Pages>
)
const pages = container.querySelector('div[id^="Pages_"]')

expect(subject.getDOMNode().textContent).to.equal('Bar')
expect(pages).toHaveTextContent('Bar')
})

it('should throw error if onPageIndexChange is not passed together with activePageIndex', async () => {
const consoleError = stub(console, 'error')
await mount(
render(
<Pages activePageIndex={1}>
<Page>{() => 'Foo'}</Page>
<Page>{() => 'Bar'}</Page>
</Pages>
)

expect(consoleError).to.have.been.called()
const expectedErrorMessage =
"You provided a 'activePageIndex' prop without an 'onPageIndexChange' handler on 'Pages'."

expect(consoleErrorMock).toHaveBeenCalledWith(
expect.any(String),
expect.any(String),
expect.stringContaining(expectedErrorMessage),
expect.any(String)
)
})

it('should pass history and navigateToPreviousPage to Page', async () => {
const pageSpy = spy()
await mount(
const pageSpy = vi.fn()
render(
<Pages>
<Page>{pageSpy}</Page>
</Pages>
)
// Called twice because props.makeStyles() is called in Pages.componentDidMount()
expect(pageSpy).to.have.been.called()
expect(Array.isArray(pageSpy.args[0][0])).to.equal(true)
expect(typeof pageSpy.args[0][1]).to.equal('function')

await waitFor(() => {
const args = pageSpy.mock.calls[0]

expect(pageSpy).toHaveBeenCalledTimes(1)
expect(Array.isArray(args[0])).toEqual(true)
expect(typeof args[1]).toBe('function')
})
})

it('should fire onPageIndexChange event', async () => {
const onPageIndexChange = spy()
const onPageIndexChange = vi.fn()

const subject = await mount(
const { container, rerender } = render(
<Pages activePageIndex={0} onPageIndexChange={onPageIndexChange}>
<Page key={0}>{() => 'Foo'}</Page>
<Page key={1}>
Expand All @@ -106,14 +140,23 @@ describe('<Pages />', async () => {
</Pages>
)

await subject.setProps({ activePageIndex: 1 })
// Set prop: activePageIndex
rerender(
<Pages activePageIndex={1} onPageIndexChange={onPageIndexChange}>
<Page key={0}>{() => 'Foo'}</Page>
<Page key={1}>
{(_history, navigate) => <button onClick={navigate}>Back</button>}
</Page>
</Pages>
)

const pages = within(subject.getDOMNode())
const button = await pages.find('button')
const button = container.querySelector('button')!

await button.click()
userEvent.click(button)

expect(onPageIndexChange).to.have.been.calledOnce()
expect(onPageIndexChange).to.have.been.calledWith(0, 1)
await waitFor(() => {
expect(onPageIndexChange).toHaveBeenCalledTimes(1)
expect(onPageIndexChange).toHaveBeenCalledWith(0, 1)
})
})
})

0 comments on commit 66b1ad9

Please sign in to comment.