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(app-project): mock Zooniverse APIs in stories with MSW #6406

Merged
merged 14 commits into from
Nov 14, 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
1 change: 1 addition & 0 deletions packages/app-project/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.next
storybook-static
1 change: 1 addition & 0 deletions packages/app-project/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const config = {
docs: {
autodocs: 'tag'
},
staticDirs: ['../public', '../storybook-public'],
webpackFinal
}
export default config
10 changes: 10 additions & 0 deletions packages/app-project/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { initialize, mswLoader } from 'msw-storybook-addon'
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
import i18n from './lib/i18n'
import zooTheme from '@zooniverse/grommet-theme'
Expand All @@ -9,6 +10,14 @@ const background = {
light: 'light-1'
}

// Initialize MSW
initialize({
serviceWorker: {
url: './mockServiceWorker.js',
},
onUnhandledRequest: 'bypass',
})

const globalTypes = {
theme: {
name: 'Grommet Theme',
Expand Down Expand Up @@ -56,6 +65,7 @@ const preview = {
},
decorators,
globalTypes,
loaders: [mswLoader]
}

export default preview
7 changes: 7 additions & 0 deletions packages/app-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
"eslint-plugin-jsx-a11y": "~6.10.1",
"jsdom": "~24.0.0",
"mocha": "~10.8.2",
"msw": "~2.5.1",
"msw-storybook-addon": "~2.0.3",
"nock": "~13.5.1",
"selfsigned": "~2.4.1",
"sinon": "~17.0.0",
Expand All @@ -88,5 +90,10 @@
},
"engines": {
"node": ">=20.5"
},
"msw": {
"workerDirectory": [
"storybook-public"
]
}
}
4 changes: 2 additions & 2 deletions packages/app-project/src/hooks/useAssignedLevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { panoptes } from '@zooniverse/panoptes-js'
import useSWR from 'swr'

const SWRoptions = {
revalidateIfStale: false,
revalidateOnMount: false,
revalidateIfStale: true,
revalidateOnMount: true, // project teams can change their workflow.configuration.level at any time
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,42 @@
import { Grommet } from 'grommet'
import nock from 'nock'
import { within } from '@testing-library/dom'
import { render, screen } from '@testing-library/react'
import zooTheme from '@zooniverse/grommet-theme'
import { composeStory } from '@storybook/react'
import { applyRequestHandlers } from 'msw-storybook-addon'
import nock from 'nock'

import SubjectPicker, { StyledBox, SubjectDataTable } from './SubjectPicker'
import Meta, { Default } from './SubjectPicker.stories.js'

describe('Components > Subject Picker', function () {
let columnHeadings, displayName, link, tableRows

function Wrapper({ children }) {
return (
<Grommet theme={zooTheme}>
{children}
</Grommet>
)
}

const workflow = {
id: '123345'
}
const subjectSet = {
id: '4567',
display_name: 'Test subject set',
metadata: {
indexFields: 'Date,Page'
}
}

before(async function () {
// allow a generous timeout for subject data to load.
this.timeout(10000)

const columns = [
'subject_id',
'Page',
'Date'
]
const rows = [
[1, '43', '23 January 1916'],
[2, '44', '24 January 1916'],
[3, '45', '25 January 1916']
]
nock('https://subject-set-search-api.zooniverse.org/subjects')
.get('/4567.json')
.query(true)
.reply(200, {
columns,
rows
})
nock('https://panoptes-staging.zooniverse.org/api')
.get('/subjects/selection')
.query(true)
.reply(200, {
subjects: [
{ id: 1, already_seen: false, retired: false },
{ id: 2, already_seen: true, retired: false },
{ id: 3, already_seen: true, retired: true }
]
})
render(
<SubjectPicker
baseUrl="/workflow/12345"
subjectSet={subjectSet}
workflow={workflow}
/>,
{ wrapper: Wrapper }
)
// workaround to wait for the mock API calls to resolve
await new Promise((resolve) => setTimeout(resolve, 100));
displayName = screen.getByText(subjectSet.display_name)
nock('https://panoptes-staging.zooniverse.org')
.get('/api/subjects/selection')
.query(true)
.reply(200, {
subjects: [
{ id: 1, already_seen: false, retired: false },
{ id: 2, already_seen: true, retired: false },
{ id: 3, already_seen: true, retired: true }
]
})
const DefaultStory = composeStory(Default, Meta)
await applyRequestHandlers(DefaultStory.parameters.msw)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed in Storybook 7, but 8 handles it automatically for you.

render(<DefaultStory />)
displayName = await screen.findByText('Anti-Slavery Letters: 1800-1839')
link = screen.getByRole('link', { name: 'SubjectPicker.back' })
const dataTable = screen.getByRole('table')
const [ tableHeader, tableContent ] = within(dataTable).getAllByRole('rowgroup')
columnHeadings = within(tableHeader).getByRole('row', {
name: 'subject_id Date FormSearch Page FormSearch status'
name: 'subject_id date FormSearch page FormSearch status'
})
const subjects = [
'1 23 January 1916 43 SubjectPicker.unclassified',
'2 24 January 1916 44 SubjectPicker.alreadySeen',
'3 25 January 1916 45 SubjectPicker.retired'
]
tableRows = subjects.map(subject => within(tableContent).getByRole('row', { name: subject }))
tableRows = subjects.map(async subject => await within(tableContent).findByRole('row', { name: subject }))
await Promise.all(tableRows)
})

it('should show the subject set name', function () {
Expand All @@ -97,6 +52,6 @@ describe('Components > Subject Picker', function () {
})

it('should link to the base URL', function () {
expect(link.href).to.equal('https://localhost/workflow/12345')
expect(link.href).to.equal('https://localhost/projects/test-owner/test-project/classify/workflow/12345')
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Box } from 'grommet'
import SubjectPicker from './SubjectPicker'
import { http, HttpResponse } from 'msw'

function DecoratedStory(Story) {
return (
Expand All @@ -9,10 +10,46 @@ function DecoratedStory(Story) {
)
}

const PANOPTES_HOST = process.env.NODE_ENV === 'production'
? 'https://www.zooniverse.org'
: 'https://panoptes-staging.zooniverse.org'

export default {
title: 'Project App / Shared / Subject Picker',
component: SubjectPicker,
decorators: [DecoratedStory]
decorators: [DecoratedStory],
parameters: {
msw: {
handlers: [
http.get('https://subject-set-search-api.zooniverse.org/subjects/15582.json', ({ request }) => {
const columns = [
'subject_id',
'Page',
'Date'
]
const rows = [
[1, '43', '23 January 1916'],
[2, '44', '24 January 1916'],
[3, '45', '25 January 1916']
]

return HttpResponse.json({
columns,
rows
})
}),
http.get(`${PANOPTES_HOST}/api/subjects/selection`, ({ request }) => {
return HttpResponse.json({
subjects: [
{ id: 1, already_seen: false, retired: false },
{ id: 2, already_seen: true, retired: false },
{ id: 3, already_seen: true, retired: true }
]
})
}),
],
},
}
}

export function Default(args) {
Expand All @@ -25,9 +62,9 @@ Default.args = {
closeFn: e => true,
subjectSet: {
id: '15582',
title: 'Anti-Slavery Letters: 1800-1839',
display_name: 'Anti-Slavery Letters: 1800-1839',
metadata: {
indexFields: 'date,title,creators'
indexFields: 'date,page'
}
},
workflow: {
Expand All @@ -47,9 +84,9 @@ Tablet.args = {
closeFn: e => true,
subjectSet: {
id: '15582',
title: 'Anti-Slavery Letters: 1800-1839',
display_name: 'Anti-Slavery Letters: 1800-1839',
metadata: {
indexFields: 'date,title,creators'
indexFields: 'date,page'
}
},
workflow: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
import nock from 'nock'
import sinon from 'sinon'
import { fetchStatuses, fetchSubjects } from './'
import { fetchStatuses } from './'

describe('Components > Subject Picker > helpers > fetchStatuses', function () {
let subjects
const expectedData = [
{ subject_id: 1, page: '43', date_with_space: '23 January 1916', status: 'SubjectPicker.unclassified' },
{ subject_id: 2, page: '44', date_with_space: '24 January 1916', status: 'SubjectPicker.alreadySeen' },
{ subject_id: 3, page: '45', date_with_space: '25 January 1916', status: 'SubjectPicker.retired' },
{ subject_id: 1, page: '43', date: '23 January 1916', status: 'SubjectPicker.unclassified' },
{ subject_id: 2, page: '44', date: '24 January 1916', status: 'SubjectPicker.alreadySeen' },
{ subject_id: 3, page: '45', date: '25 January 1916', status: 'SubjectPicker.retired' },
]

before(async function () {
const columns = [
'subject_id',
'Page',
'Date. With space'
]
const rows = [
[1, '43', '23 January 1916'],
[2, '44', '24 January 1916'],
[3, '45', '25 January 1916']
]
const workflow = {
id: '1'
}
const subjectsAPI = nock('https://subject-set-search-api.zooniverse.org/subjects')
.get('/1.json')
.query(true)
.reply(200, { columns, rows })
const panoptes = nock('https://panoptes-staging.zooniverse.org/api')
.get('/subjects/selection')
.query(true)
Expand All @@ -38,7 +23,27 @@ describe('Components > Subject Picker > helpers > fetchStatuses', function () {
{ id: 3, already_seen: true, retired: true }
]
})
const panoptesSubjects = await fetchSubjects('1')
const panoptesSubjects = [
{
subject_id: 1,
page: '43',
date: '23 January 1916',
status: 'loading'
},
{
subject_id: 2,
page: '44',
date: '24 January 1916',
status: 'loading'
},
{
subject_id: 3,
page: '45',
date: '25 January 1916',
status: 'loading'
}
]

subjects = await fetchStatuses(panoptesSubjects, workflow)
})

Expand Down
Loading