Skip to content

Commit

Permalink
Merge pull request #990 from research-software-directory/983-related-…
Browse files Browse the repository at this point in the history
…items-split

Split related topics into related software and projects sections
  • Loading branch information
dmijatovic authored Sep 22, 2023
2 parents 26273ad + 3264889 commit 686a566
Show file tree
Hide file tree
Showing 33 changed files with 478 additions and 731 deletions.
98 changes: 0 additions & 98 deletions e2e/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,104 +103,6 @@ export async function openEditSection(page:Page,name:string) {

}

// TODO! REMOVE THIS FUNCTION LATER
// export async function addOrganisation(page, organisation: Organisation, apiUrl) {

// // const findOrganisation = page.getByLabel('Find or add organisation')
// const findOrganisation = page.getByRole('combobox', {name: 'Find or add organisation'})

// // check if no organisation message is present
// const alert = await page.getByRole('alert')
// .filter({
// hasText: /No participating organisations/
// }).count() > 0

// // set breakpoint
// // console.log('alert...', alert)
// // await page.pause()

// if (alert === false) {
// //check if organisation already present
// const organisations = page.getByTestId('organisation-list-item')
// const [count] = await Promise.all([
// organisations.count(),
// page.waitForLoadState('networkidle')
// ])

// if (count > 0) {
// // check if organisation existis
// const found = await organisations
// .filter({hasText: RegExp(organisation.name)})
// .count()
// // console.log('found...', found)
// // if already exists we return false
// if (found > 0) return false
// }
// }

// // if not exists we search
// await Promise.all([
// page.waitForResponse(/api.ror.org\/organizations/),
// page.waitForLoadState('networkidle'),
// findOrganisation.fill(organisation.name)
// ])

// const options = page.getByTestId('find-organisation-option')
// const option = await options
// .filter({
// hasText:RegExp(organisation.name,'i')
// })
// .first()

// // get source information
// const source = await option.getByTestId('organisation-list-item').getAttribute('data-source')

// if (source === 'RSD') {
// await Promise.all([
// page.waitForResponse(RegExp(apiUrl)),
// option.click()
// ])
// // if rsd we just add it to list (no modal popup)
// return true
// }
// if (source === 'ROR') {
// // for ROR we can upload logo
// // in the modal/dialog so
// // we wait on modal to appear
// await Promise.all([
// page.waitForSelector('[role="dialog"]'),
// option.click()
// ])

// // console.log('logo...', organisation.logo)
// // upload logo if logo provided
// if (organisation.logo) {
// await uploadFile(
// page, '#upload-avatar-image',
// organisation.logo,
// 'img'
// )
// }
// // save new organisation
// const saveBtn = page.getByRole('button', {
// name: 'Save'
// })
// await Promise.all([
// page.waitForResponse(/organisation/),
// page.waitForResponse(RegExp(apiUrl)),
// saveBtn.click()
// ])
// }

// // validate item is added to list
// const lastItem = await page.getByTestId('organisation-list-item').last()
// const lastText = await lastItem.getByTestId('organisation-list-item-text').textContent()
// // console.log('lastText...', lastText)
// expect(lastText).toContain(organisation.name)

// return true
// }

export async function addRelatedSoftware(page: Page, waitForResponse:string) {
const findSoftware = page
.getByTestId('find-related-software')
Expand Down
16 changes: 14 additions & 2 deletions e2e/tests/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ test.describe.serial('Project', async () => {
expect(count).toBeGreaterThanOrEqual(organisations.length)
})

test('Related items', async ({page}, {project: {name}}) => {
test('Related projects', async ({page}, {project: {name}}) => {
const project = mockProject[name]

// directly open edit software page
Expand All @@ -173,9 +173,21 @@ test.describe.serial('Project', async () => {

// await page.pause()
// navigate to organisations section
await openEditSection(page, 'Related topics')
await openEditSection(page, 'Related projects')

await addRelatedProject(page, 'project_for_project')
})

test('Related software', async ({page}, {project: {name}}) => {
const project = mockProject[name]

// directly open edit software page
const url = `/projects/${project.slug}`
await openEditPage(page, url, project.title)

// await page.pause()
// navigate to organisations section
await openEditSection(page, 'Related software')

// add related software only if not added
const relatedSoftware = page.getByTestId('related-software-item')
Expand Down
17 changes: 13 additions & 4 deletions e2e/tests/software.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,26 @@ test.describe.serial('Software', async()=> {

// We test related items as last because we
// need some items to be created and published
test('Related items', async ({page}, {project}) => {
test('Related software', async ({page}, {project}) => {
const software = mockSoftware[project.name]

// directly open edit software page
const url = `/software/${software.slug}`
await openEditPage(page, url, software.title)

// navigate to related topics section
await openEditSection(page, 'Related topics')
await openEditSection(page, 'Related software')
// add some related software randomly
await addRelatedSoftware(page, 'software_for_software')
})

// We test related items as last because we
// need some items to be created and published
test('Related projects', async ({page}, {project}) => {
const software = mockSoftware[project.name]
// directly open edit software page
const url = `/software/${software.slug}`
await openEditPage(page, url, software.title)
// navigate to related topics section
await openEditSection(page, 'Related projects')
// add some related projects randomly
await addRelatedProject(page, 'software_for_project')
})
Expand Down
19 changes: 15 additions & 4 deletions frontend/components/projects/edit/editProjectPages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import OutboundIcon from '@mui/icons-material/Outbound'
import AccessibilityNewIcon from '@mui/icons-material/AccessibilityNew'
import ShareIcon from '@mui/icons-material/Share'
import PersonAddIcon from '@mui/icons-material/PersonAdd'
import TerminalIcon from '@mui/icons-material/Terminal'
import ContentLoader from '~/components/layout/ContentLoader'


Expand Down Expand Up @@ -42,7 +43,10 @@ const ProjectImpact = dynamic(() => import('./impact'),{
const ProjectOutput = dynamic(() => import('./output'),{
loading: ()=><ContentLoader />
})
const RelatedTopics = dynamic(() => import('./related'),{
const RelatedProjects = dynamic(() => import('./related-projects'),{
loading: ()=><ContentLoader />
})
const RelatedSoftware = dynamic(() => import('./related-software'),{
loading: ()=><ContentLoader />
})
const ProjectMaintainers = dynamic(() => import('./maintainers'),{
Expand Down Expand Up @@ -94,10 +98,17 @@ export const editProjectPage: EditProjectPageProps[] = [
status: 'Optional information'
},
{
id: 'related-topics',
label: 'Related topics',
id: 'related-projects',
label: 'Related projects',
icon: <ShareIcon />,
render: () => <RelatedTopics />,
render: () => <RelatedProjects />,
status: 'Optional information'
},
{
id: 'related-software',
label: 'Related software',
icon: <TerminalIcon />,
render: () => <RelatedSoftware />,
status: 'Optional information'
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all)
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all) (dv4all)
// SPDX-FileCopyrightText: 2023 Netherlands eScience Center
// SPDX-FileCopyrightText: 2023 dv4all
//
// SPDX-License-Identifier: Apache-2.0
Expand All @@ -14,15 +16,12 @@ import RelatedItems from './index'
// MOCKS
import editProjectState from '../__mocks__/editProjectState'
import mockRelatedProjects from './__mocks__/relatedProjectsForProject.json'
import mockRelatedSoftware from './__mocks__/relatedSoftwareForProject.json'

// MOCK getRelatedProjectsForProject, getRelatedSoftwareForProject
const mockGetRelatedProjectsForProject = jest.fn(props => Promise.resolve([] as any))
const mockGetRelatedSoftwareForProject = jest.fn(props => Promise.resolve([] as any))
const mockSearchForRelatedProjectByTitle = jest.fn(props => Promise.resolve([] as any))
jest.mock('~/utils/getProjects', () => ({
getRelatedProjectsForProject: jest.fn(props => mockGetRelatedProjectsForProject(props)),
getRelatedSoftwareForProject: jest.fn(props => mockGetRelatedSoftwareForProject(props)),
searchForRelatedProjectByTitle: jest.fn(props => mockSearchForRelatedProjectByTitle(props)),
}))

Expand All @@ -44,15 +43,15 @@ jest.mock('~/utils/editRelatedSoftware', () => ({
}))


describe('frontend/components/projects/edit/related/index.tsx', () => {
describe('frontend/components/projects/edit/related-projects/index.tsx', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('renders related project and software items', async() => {
it('renders related project items', async() => {
// return mocked values
mockGetRelatedProjectsForProject.mockResolvedValueOnce(mockRelatedProjects)
mockGetRelatedSoftwareForProject.mockResolvedValueOnce(mockRelatedSoftware)
// mockGetRelatedSoftwareForProject.mockResolvedValueOnce(mockRelatedSoftware)

// render
render(
Expand All @@ -67,9 +66,6 @@ describe('frontend/components/projects/edit/related/index.tsx', () => {
const relatedProjects = await screen.findAllByTestId('related-project-item')
expect(relatedProjects.length).toEqual(mockRelatedProjects.length)


const relatedSoftware = await screen.findAllByTestId('related-software-item')
expect(relatedSoftware.length).toEqual(mockRelatedSoftware.length)
})

it('can add related project to project', async() => {
Expand All @@ -80,7 +76,6 @@ describe('frontend/components/projects/edit/related/index.tsx', () => {
]
// return mocked values
mockGetRelatedProjectsForProject.mockResolvedValueOnce([])
mockGetRelatedSoftwareForProject.mockResolvedValueOnce([])
mockSearchForRelatedProjectByTitle.mockResolvedValueOnce(relatedProjectsFound)
mockAddRelatedProject.mockResolvedValueOnce({
status: 200,
Expand Down Expand Up @@ -122,61 +117,9 @@ describe('frontend/components/projects/edit/related/index.tsx', () => {
expect(relatedProjects.length).toEqual(1)
})

it('can add related software to project', async() => {
const searchFor = 'Search for software'
const relatedSoftwareFound = [
{id:'test-id-1',slug:'test-slug-1',brand_name:'Test title 1',short_statement:'Test subtitle 1',status: 'approved'},
{id:'test-id-2',slug:'test-slug-2',brand_name:'Test title 2',short_statement:'Test subtitle 2',status:'approved'}
]
// return mocked values
mockGetRelatedProjectsForProject.mockResolvedValueOnce([])
mockGetRelatedSoftwareForProject.mockResolvedValueOnce([])
mockSearchForRelatedSoftware.mockResolvedValueOnce(relatedSoftwareFound)
mockAddRelatedSoftware.mockResolvedValueOnce({
status: 200,
message: 'OK'
})

// render
render(
<WithAppContext options={{session: mockSession}}>
<WithProjectContext state={editProjectState}>
<RelatedItems />
</WithProjectContext>
</WithAppContext>
)

// search for related project
const findRelated = screen.getByTestId('find-related-software')
const search = within(findRelated).getByRole('combobox')
fireEvent.change(search, {target: {value: searchFor}})

// validate mocked options returned
const options = await screen.findAllByTestId('related-software-option')
expect(options.length).toEqual(relatedSoftwareFound.length)

// add first item
fireEvent.click(options[0])

// validate api calls
expect(mockAddRelatedSoftware).toBeCalledTimes(1)
expect(mockAddRelatedSoftware).toBeCalledWith({
'project': editProjectState.project.id,
'software': relatedSoftwareFound[0].id,
'status': 'approved',
'token': mockSession.token,
})

// validate 1 item added to list
const relatedSoftware = await screen.findAllByTestId('related-software-item')
expect(relatedSoftware.length).toEqual(1)
})


it('can remove related project for project', async() => {
// return mocked values
mockGetRelatedProjectsForProject.mockResolvedValueOnce(mockRelatedProjects)
mockGetRelatedSoftwareForProject.mockResolvedValueOnce([])
mockDeleteRelatedProject
.mockResolvedValueOnce({
status: 200,
Expand Down Expand Up @@ -222,44 +165,4 @@ describe('frontend/components/projects/edit/related/index.tsx', () => {
})
})

it('can remove related software', async() => {
// return mocked values
mockGetRelatedProjectsForProject.mockResolvedValueOnce([])
mockGetRelatedSoftwareForProject.mockResolvedValueOnce(mockRelatedSoftware)
mockDeleteRelatedSoftware.mockResolvedValueOnce({
status: 200,
message:'OK'
})
// render
render(
<WithAppContext options={{session: mockSession}}>
<WithProjectContext state={editProjectState}>
<RelatedItems />
</WithProjectContext>
</WithAppContext>
)

// get list
const relatedSoftware = await screen.findAllByTestId('related-software-item')
// get delete btn of first item
const delBtn = within(relatedSoftware[0]).getByRole('button', {
name: 'delete'
})
// delete item
fireEvent.click(delBtn)

await waitFor(() => {
expect(mockDeleteRelatedSoftware).toBeCalledTimes(1)
expect(mockDeleteRelatedSoftware).toBeCalledWith({
'project': editProjectState.project.id,
'software': mockRelatedSoftware[0].id,
'token': mockSession.token,
})
// validate remaining items
const remainedSoftware = screen.getAllByTestId('related-software-item')
expect(remainedSoftware.length).toEqual(mockRelatedSoftware.length - 1)
})
})


})
Loading

0 comments on commit 686a566

Please sign in to comment.