Skip to content
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

test(releases): fixing issues with virtual list and releases overview and summary tests #8123

Merged
merged 3 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {act, fireEvent, render, screen, within} from '@testing-library/react'
import {cloneElement, type FC, type PropsWithChildren, type ReactElement, useState} from 'react'
import {route, RouterProvider} from 'sanity/router'
import {beforeEach, describe, expect, it, vi} from 'vitest'

import {getByDataUi} from '../../../../../../test/setup/customQueries'
import {setupVirtualListEnv} from '../../../../../../test/testUtils/setupVirtualListEnv'
import {createTestProvider} from '../../../../../../test/testUtils/TestProvider'
import {DefaultPreview} from '../../../../components/previews/general/DefaultPreview'
import {
Expand Down Expand Up @@ -78,6 +80,16 @@ const releaseDocuments: DocumentInRelease[] = [
},
]

const ScrollContainer: FC<PropsWithChildren> = ({children}) => {
const [ref, setRef] = useState<HTMLDivElement | null>(null)

return (
<div style={{height: '400px'}} ref={setRef}>
{cloneElement(children as ReactElement, {scrollContainerRef: {current: ref}})}
</div>
)
}

const renderTest = async (props: Partial<ReleaseSummaryProps>) => {
const wrapper = await createTestProvider({
resources: [releasesUsEnglishLocaleBundle],
Expand All @@ -91,88 +103,90 @@ const renderTest = async (props: Partial<ReleaseSummaryProps>) => {
onNavigate={vi.fn()}
router={route.create('/', [route.create('/:releaseId'), route.intents('/intents')])}
>
<ReleaseSummary
scrollContainerRef={{current: null}}
documents={releaseDocuments}
documentsHistory={{
'123': {
createdBy: 'created-author-id-1',
lastEditedBy: 'edited-author-id-1',
editors: ['edited-author-id-1'],
history: [
{
id: '123',
timestamp: '2024-11-04T07:53:25Z',
author: 'pJ61yWhkD',
documentIDs: ['versions.abc.123'],
effects: {
'versions.abc.123': {
apply: [
0,
{
_createdAt: '2024-11-04T07:53:25Z',
_id: 'versions.abc.123',
_type: 'book',
_updatedAt: '2024-11-04T07:53:25Z',
address: {
city: 'Stockholm',
country: 'Sweden',
},
publishedAt: '2020-02-03T21:36:34.980Z',
title: 'sdfsadfadsf sdf',
translations: {
no: '0',
se: '0',
<ScrollContainer>
<ReleaseSummary
scrollContainerRef={{current: null}}
documents={releaseDocuments}
documentsHistory={{
'123': {
createdBy: 'created-author-id-1',
lastEditedBy: 'edited-author-id-1',
editors: ['edited-author-id-1'],
history: [
{
id: '123',
timestamp: '2024-11-04T07:53:25Z',
author: 'pJ61yWhkD',
documentIDs: ['versions.abc.123'],
effects: {
'versions.abc.123': {
apply: [
0,
{
_createdAt: '2024-11-04T07:53:25Z',
_id: 'versions.abc.123',
_type: 'book',
_updatedAt: '2024-11-04T07:53:25Z',
address: {
city: 'Stockholm',
country: 'Sweden',
},
publishedAt: '2020-02-03T21:36:34.980Z',
title: 'sdfsadfadsf sdf',
translations: {
no: '0',
se: '0',
},
},
},
],
revert: [0, null],
],
revert: [0, null],
},
},
},
},
],
},
'456': {
createdBy: 'created-author-id-2',
lastEditedBy: 'edited-author-id-2',
editors: ['edited-author-id-1', 'edited-author-id-2'],
history: [
{
id: '456',
timestamp: '2024-11-04T07:53:25Z',
author: 'pJ61yWhkD',
documentIDs: ['versions.abc.456'],
effects: {
'versions.abc.456': {
apply: [
0,
{
_createdAt: '2024-11-04T07:53:25Z',
_id: 'versions.abc.456',
_type: 'book',
_updatedAt: '2024-11-04T07:53:25Z',
address: {
city: 'Stockholm',
country: 'Sweden',
},
publishedAt: '2020-02-03T21:36:34.980Z',
title: 'sdfsadfadsf sdf',
translations: {
no: '0',
se: '0',
],
},
'456': {
createdBy: 'created-author-id-2',
lastEditedBy: 'edited-author-id-2',
editors: ['edited-author-id-1', 'edited-author-id-2'],
history: [
{
id: '456',
timestamp: '2024-11-04T07:53:25Z',
author: 'pJ61yWhkD',
documentIDs: ['versions.abc.456'],
effects: {
'versions.abc.456': {
apply: [
0,
{
_createdAt: '2024-11-04T07:53:25Z',
_id: 'versions.abc.456',
_type: 'book',
_updatedAt: '2024-11-04T07:53:25Z',
address: {
city: 'Stockholm',
country: 'Sweden',
},
publishedAt: '2020-02-03T21:36:34.980Z',
title: 'sdfsadfadsf sdf',
translations: {
no: '0',
se: '0',
},
},
},
],
revert: [0, null],
],
revert: [0, null],
},
},
},
},
],
},
}}
release={activeASAPRelease}
{...props}
/>
],
},
}}
release={activeASAPRelease}
{...props}
/>
</ScrollContainer>
</RouterProvider>,
{
wrapper,
Expand All @@ -181,6 +195,8 @@ const renderTest = async (props: Partial<ReleaseSummaryProps>) => {
}

describe('ReleaseSummary', () => {
setupVirtualListEnv()

describe('for an active release', () => {
beforeEach(async () => {
await renderTest({})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {act, fireEvent, render, screen, waitFor, within} from '@testing-library/react'
import {format, set} from 'date-fns'
import {useState} from 'react'
import {useRouter} from 'sanity/router'
import {beforeEach, describe, expect, it, vi} from 'vitest'

import {getByDataUi, queryByDataUi} from '../../../../../../test/setup/customQueries'
import {setupVirtualListEnv} from '../../../../../../test/testUtils/setupVirtualListEnv'
import {createTestProvider} from '../../../../../../test/testUtils/TestProvider'
import {
getLocalTimeZoneMockReturn,
Expand Down Expand Up @@ -77,11 +79,34 @@ vi.mock('../../../../scheduledPublishing/hooks/useTimeZone', async (importOrigin
default: vi.fn(() => useTimeZoneMockReturn),
}))

const getWrapper = () =>
createTestProvider({
resources: [releasesUsEnglishLocaleBundle],
})

/**
* To resolve issues with size render with Virtual list (as described
* here: https://github.com/TanStack/virtual/issues/641), must rerender
* ReleasesOverview once the exact height wrapper has mounted
*/
const TestComponent = () => {
const [hasWrapperRendered, setHasWrapperRendered] = useState<boolean>(false)
const updateWrapperRendered = () => setHasWrapperRendered(true)

return (
<div style={{height: '400px'}} ref={updateWrapperRendered}>
<ReleasesOverview data-wrapperRendered={hasWrapperRendered?.toString()} />
</div>
)
}

describe('ReleasesOverview', () => {
beforeEach(() => {
mockUseReleases.mockRestore()
})

setupVirtualListEnv()

describe('when loading releases', () => {
beforeEach(async () => {
mockUseReleases.mockReturnValue({...useReleasesMockReturn, loading: true})
Expand All @@ -90,7 +115,7 @@ describe('ReleasesOverview', () => {
resources: [releasesUsEnglishLocaleBundle],
})

return render(<ReleasesOverview />, {wrapper})
return render(<TestComponent />, {wrapper})
})

it('does not show releases table but shows loader', () => {
Expand Down Expand Up @@ -124,7 +149,7 @@ describe('ReleasesOverview', () => {
resources: [releasesUsEnglishLocaleBundle],
})

return render(<ReleasesOverview />, {wrapper})
return render(<TestComponent />, {wrapper})
})

it('shows a message about releases', () => {
Expand Down Expand Up @@ -165,6 +190,14 @@ describe('ReleasesOverview', () => {

let activeRender: ReturnType<typeof render>

const rerender = async () => {
activeRender.unmount()

const wrapper = await getWrapper()

return render(<TestComponent />, {wrapper})
}

beforeEach(async () => {
mockUseTimeZone.mockRestore()
mockUseReleases.mockReturnValue({
Expand All @@ -184,12 +217,10 @@ describe('ReleasesOverview', () => {
),
})

const wrapper = await createTestProvider({
resources: [releasesUsEnglishLocaleBundle],
})
const wrapper = await getWrapper()

return act(() => {
activeRender = render(<ReleasesOverview />, {wrapper})
activeRender = render(<TestComponent />, {wrapper})
})
})

Expand Down Expand Up @@ -249,15 +280,15 @@ describe('ReleasesOverview', () => {
expect(usePerspectiveMockReturn.setPerspective).toHaveBeenCalledWith('rASAP')
})

it('will show pinned release in release list', () => {
it('will show pinned release in release list', async () => {
mockUsePerspective.mockReturnValue({
...usePerspectiveMockReturn,
selectedPerspective: activeASAPRelease,
selectedReleaseId: 'rASAP',
})

// re-render to apply the update to global bundle id
activeRender.rerender(<ReleasesOverview />)
await rerender()

const releaseRows = screen.getAllByTestId('table-row')
const pinnedReleaseRow = releaseRows[0]
Expand Down Expand Up @@ -329,7 +360,7 @@ describe('ReleasesOverview', () => {
within(getByDataUi(document.body, 'DialogCard')).getByText('Select time zone')
})

it('shows dates with timezone abbreviation when it is not the locale', () => {
it('shows dates with timezone abbreviation when it is not the locale', async () => {
mockGetLocaleTimeZone.mockReturnValue({
abbreviation: 'NST', // Not Sanity Time
namePretty: 'Not Sanity Time',
Expand All @@ -340,7 +371,7 @@ describe('ReleasesOverview', () => {
value: 'Not Sanity Time',
})

activeRender.rerender(<ReleasesOverview />)
await rerender()

const scheduledReleaseRow = screen.getAllByTestId('table-row')[2]

Expand All @@ -355,8 +386,6 @@ describe('ReleasesOverview', () => {
// spoof a timezone that is 8 hours ahead of UTC
zoneDateToUtc: vi.fn((date) => set(date, {hours: new Date(date).getHours() - 8})),
})

activeRender.rerender(<ReleasesOverview />)
})

it('shows today as having no releases', () => {
Expand Down
38 changes: 38 additions & 0 deletions packages/sanity/test/testUtils/setupVirtualListEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {afterEach, beforeEach, vi} from 'vitest'

export const setupVirtualListEnv = (
dataTestId?: string,
rectWidth: number = 350,
rectHeight: number = 800,
) => {
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect

const getDOMRect = (width: number, height: number) => ({
width,
height,
top: 0,
left: 0,
bottom: 0,
right: 0,
x: 0,
y: 0,
toJSON: () => {},
})

beforeEach(() => {
// Virtual list will return an empty list of items unless we have some size,
// so we need to mock getBoundingClientRect to return a size for the list.
// Not pretty, but it's what they recommend for testing outside of browsers:
// https://github.com/TanStack/virtual/issues/641
Element.prototype.getBoundingClientRect = vi.fn(function (this: Element) {
if (!dataTestId || this.getAttribute('data-testid') === dataTestId) {
return getDOMRect(rectWidth, rectHeight)
}
return getDOMRect(0, 0)
})
})

afterEach(() => {
Element.prototype.getBoundingClientRect = originalGetBoundingClientRect
})
}
Loading