Merge pull request #10028 from overleaf/ab-fix-flaky-fe-tests

[web] Fix flaky project list frontend tests

GitOrigin-RevId: 2b2a90e6a294ec7dd5e7203dadd708f7986a56fc
This commit is contained in:
Alexandre Bourdin 2022-10-20 11:32:04 +02:00 committed by Copybot
parent 22336942a8
commit 0c170bd4c0
2 changed files with 85 additions and 101 deletions

View file

@ -28,17 +28,28 @@ function CopyProjectMenuItem() {
} }
}, [isMounted]) }, [isMounted])
const handleAfterCloned = (clonedProject: Project) => { const handleAfterCloned = useCallback(
const project = selectedProjects[0] (clonedProject: Project) => {
eventTracking.send( const project = selectedProjects[0]
'project-list-page-interaction', eventTracking.send(
'project action', 'project-list-page-interaction',
'Clone' 'project action',
) 'Clone'
addClonedProjectToViewData(clonedProject) )
updateProjectViewData({ ...project, selected: false }) addClonedProjectToViewData(clonedProject)
setShowModal(false) updateProjectViewData({ ...project, selected: false })
}
if (isMounted.current) {
setShowModal(false)
}
},
[
isMounted,
selectedProjects,
addClonedProjectToViewData,
updateProjectViewData,
]
)
if (selectedProjects.length !== 1) return null if (selectedProjects.length !== 1) return null

View file

@ -82,9 +82,7 @@ describe('<ProjectListRoot />', function () {
projects: fullList, projects: fullList,
}) })
await fetchMock.flush(true) await fetchMock.flush(true)
await waitFor(() => { await screen.findByRole('table')
screen.findByRole('table')
})
}) })
describe('checkboxes', function () { describe('checkboxes', function () {
@ -125,15 +123,8 @@ describe('<ProjectListRoot />', function () {
}) })
it('opens archive modal for all selected projects and archives all', async function () { it('opens archive modal for all selected projects and archives all', async function () {
fetchMock.post( const archiveProjectMock = fetchMock.post(
`express:/project/${project1Id}/archive`, `express:/project/:projectId/archive`,
{
status: 200,
},
{ delay: 0 }
)
fetchMock.post(
`express:/project/${project2Id}/archive`,
{ {
status: 200, status: 200,
}, },
@ -149,26 +140,21 @@ describe('<ProjectListRoot />', function () {
await waitFor( await waitFor(
() => () =>
expect(fetchMock.called(`/project/${project1Id}/archive`)).to.be expect(
.true archiveProjectMock.called(`/project/${project1Id}/archive`)
).to.be.true
) )
await waitFor( await waitFor(
() => () =>
expect(fetchMock.called(`/project/${project2Id}/archive`)).to.be expect(
.true archiveProjectMock.called(`/project/${project2Id}/archive`)
).to.be.true
) )
}) })
it('opens trash modal for all selected projects and trashes all', async function () { it('opens trash modal for all selected projects and trashes all', async function () {
fetchMock.post( const trashProjectMock = fetchMock.post(
`express:/project/${project1Id}/trash`, `express:/project/:projectId/trash`,
{
status: 200,
},
{ delay: 0 }
)
fetchMock.post(
`express:/project/${project2Id}/trash`,
{ {
status: 200, status: 200,
}, },
@ -184,13 +170,13 @@ describe('<ProjectListRoot />', function () {
await waitFor( await waitFor(
() => () =>
expect(fetchMock.called(`/project/${project1Id}/trash`)).to.be expect(trashProjectMock.called(`/project/${project1Id}/trash`)).to
.true .be.true
) )
await waitFor( await waitFor(
() => () =>
expect(fetchMock.called(`/project/${project2Id}/trash`)).to.be expect(trashProjectMock.called(`/project/${project2Id}/trash`)).to
.true .be.true
) )
}) })
@ -407,21 +393,16 @@ describe('<ProjectListRoot />', function () {
}) })
fireEvent.click(createButton) fireEvent.click(createButton)
await waitFor( await fetchMock.flush(true)
() =>
expect(fetchMock.called('/tag', { name: this.newTagName })).to.be expect(fetchMock.called('/tag', { name: this.newTagName })).to.be.true
.true expect(
) fetchMock.called(`/tag/${this.newTagId}/projects`, {
await waitFor( body: {
() => projectIds: [projectsData[0].id, projectsData[1].id],
expect( },
fetchMock.called(`/tag/${this.newTagId}/projects`, { })
body: { ).to.be.true
projectIds: [projectsData[0].id, projectsData[1].id],
},
})
).to.be.true
)
screen.getByRole('button', { name: `${this.newTagName} (2)` }) screen.getByRole('button', { name: `${this.newTagName} (2)` })
}) })
@ -433,32 +414,26 @@ describe('<ProjectListRoot />', function () {
status: 204, status: 204,
} }
) )
screen.getByRole('button', { name: `${this.tagName} (2)` }) screen.getByRole('button', { name: `${this.tagName} (2)` })
const tagsDropdown = within(actionsToolbar).getByLabelText('Tags') const tagsDropdown = within(actionsToolbar).getByLabelText('Tags')
fireEvent.click(tagsDropdown) fireEvent.click(tagsDropdown)
screen.getByText('Add to folder') within(actionsToolbar).getByText('Add to folder')
const tagButton = screen.getByLabelText( const tagButton = within(actionsToolbar).getByLabelText(
`Add or remove project from tag ${this.tagName}` `Add or remove project from tag ${this.tagName}`
) )
fireEvent.click(tagButton) fireEvent.click(tagButton)
await waitFor( await fetchMock.flush(true)
() =>
expect(
deleteProjectsFromTagMock.called(
`/tag/${this.tagId}/projects`,
{
body: {
projectIds: [projectsData[0].id, projectsData[1].id],
},
}
)
).to.be.true
)
expect(
deleteProjectsFromTagMock.called(`/tag/${this.tagId}/projects`, {
body: {
projectIds: [projectsData[0].id, projectsData[1].id],
},
})
).to.be.true
screen.getByRole('button', { name: `${this.tagName} (0)` }) screen.getByRole('button', { name: `${this.tagName} (0)` })
}) })
@ -476,24 +451,22 @@ describe('<ProjectListRoot />', function () {
const tagsDropdown = within(actionsToolbar).getByLabelText('Tags') const tagsDropdown = within(actionsToolbar).getByLabelText('Tags')
fireEvent.click(tagsDropdown) fireEvent.click(tagsDropdown)
screen.getByText('Add to folder') within(actionsToolbar).getByText('Add to folder')
const tagButton = screen.getByLabelText( const tagButton = within(actionsToolbar).getByLabelText(
`Add or remove project from tag ${this.tagName}` `Add or remove project from tag ${this.tagName}`
) )
fireEvent.click(tagButton) fireEvent.click(tagButton)
await waitFor( await fetchMock.flush(true)
() =>
expect(
addProjectsToTagMock.called(`/tag/${this.tagId}/projects`, {
body: {
projectIds: [projectsData[2].id],
},
})
).to.be.true
)
expect(
addProjectsToTagMock.called(`/tag/${this.tagId}/projects`, {
body: {
projectIds: [projectsData[2].id],
},
})
).to.be.true
screen.getByRole('button', { name: `${this.tagName} (3)` }) screen.getByRole('button', { name: `${this.tagName} (3)` })
}) })
}) })
@ -561,7 +534,7 @@ describe('<ProjectListRoot />', function () {
fireEvent.click(moreDropdown) fireEvent.click(moreDropdown)
const renameButton = const renameButton =
screen.getAllByText<HTMLInputElement>('Rename')[1] // first one is for the tag in the sidebar within(actionsToolbar).getByText<HTMLInputElement>('Rename') // first one is for the tag in the sidebar
fireEvent.click(renameButton) fireEvent.click(renameButton)
const modals = await screen.findAllByRole('dialog') const modals = await screen.findAllByRole('dialog')
@ -569,7 +542,7 @@ describe('<ProjectListRoot />', function () {
// a valid name // a valid name
const newProjectName = 'A new project name' const newProjectName = 'A new project name'
const input = (await screen.findByLabelText( const input = (await within(modal).findByLabelText(
'New Name' 'New Name'
)) as HTMLButtonElement )) as HTMLButtonElement
const oldName = input.value const oldName = input.value
@ -582,14 +555,11 @@ describe('<ProjectListRoot />', function () {
expect(confirmButton.disabled).to.be.false expect(confirmButton.disabled).to.be.false
fireEvent.click(confirmButton) fireEvent.click(confirmButton)
await waitFor( await fetchMock.flush(true)
() =>
expect( expect(
renameProjectMock.called( renameProjectMock.called(`/project/${projectsData[1].id}/rename`)
`/project/${projectsData[1].id}/rename` ).to.be.true
)
).to.be.true
)
const table = await screen.findByRole('table') const table = await screen.findByRole('table')
within(table).getByText(newProjectName) within(table).getByText(newProjectName)
@ -644,12 +614,11 @@ describe('<ProjectListRoot />', function () {
) as HTMLElement ) as HTMLElement
fireEvent.click(copyConfirmButton) fireEvent.click(copyConfirmButton)
await waitFor( await fetchMock.flush(true)
() =>
expect( expect(
cloneProjectMock.called(`/project/${projectsData[1].id}/clone`) cloneProjectMock.called(`/project/${projectsData[1].id}/clone`)
).to.be.true ).to.be.true
)
expect(sendSpy).to.be.calledOnce expect(sendSpy).to.be.calledOnce
expect(sendSpy).calledWith('project-list-page-interaction') expect(sendSpy).calledWith('project-list-page-interaction')
@ -716,9 +685,13 @@ describe('<ProjectListRoot />', function () {
it('shows correct list after closing modal, changing selecting, and reopening modal', async function () { it('shows correct list after closing modal, changing selecting, and reopening modal', async function () {
selectedMatchesDisplayed(2) selectedMatchesDisplayed(2)
const cancelButton = screen.getByRole('button', { name: 'Cancel' }) const modal = screen.getAllByRole('dialog', { hidden: false })[0]
const cancelButton = within(modal).getByRole('button', {
name: 'Cancel',
})
fireEvent.click(cancelButton) fireEvent.click(cancelButton)
expect(screen.queryByRole('dialog', { hidden: false })).to.be.null expect(screen.queryByRole('dialog', { hidden: false })).to.be.null
await screen.findAllByRole('checkbox') await screen.findAllByRole('checkbox')
fireEvent.click(allCheckboxes[3]) fireEvent.click(allCheckboxes[3])
selectedMatchesDisplayed(3) selectedMatchesDisplayed(3)
@ -733,7 +706,7 @@ describe('<ProjectListRoot />', function () {
fetchMock.post('express:/project/:id/archive', { fetchMock.post('express:/project/:id/archive', {
status: 200, status: 200,
}) })
fetchMock.post(`express:/${project2Id}/:id/archive`, { fetchMock.post(`/project/${project2Id}/archive`, {
status: 500, status: 500,
}) })