mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 14:13:38 -05:00
Merge pull request #3988 from overleaf/ae-use-fetch-mock
Add useFetchMock hook for use in Storybook GitOrigin-RevId: 4eb1c5edf2f94dc6ad51358e109e29c9f62d2058
This commit is contained in:
parent
29264061d8
commit
f8cb1638d1
8 changed files with 132 additions and 100 deletions
|
@ -1,23 +1,24 @@
|
|||
import React from 'react'
|
||||
|
||||
import BinaryFile from '../js/features/binary-file/components/binary-file'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
window.project_id = 'proj123'
|
||||
fetchMock.restore()
|
||||
fetchMock.head('express:/project/:project_id/file/:file_id', {
|
||||
status: 201,
|
||||
headers: { 'Content-Length': 10000 },
|
||||
})
|
||||
fetchMock.get('express:/project/:project_id/file/:file_id', 'Text file content')
|
||||
|
||||
fetchMock.post('express:/project/:project_id/linked_file/:file_id/refresh', {
|
||||
status: 204,
|
||||
})
|
||||
|
||||
fetchMock.post('express:/project/:project_id/references/indexAll', {
|
||||
status: 204,
|
||||
})
|
||||
const setupFetchMock = fetchMock => {
|
||||
fetchMock
|
||||
.head('express:/project/:project_id/file/:file_id', {
|
||||
status: 201,
|
||||
headers: { 'Content-Length': 10000 },
|
||||
})
|
||||
.get('express:/project/:project_id/file/:file_id', 'Text file content')
|
||||
.post('express:/project/:project_id/linked_file/:file_id/refresh', {
|
||||
status: 204,
|
||||
})
|
||||
.post('express:/project/:project_id/references/indexAll', {
|
||||
status: 204,
|
||||
})
|
||||
}
|
||||
|
||||
window.project_id = '1234'
|
||||
|
||||
|
@ -173,10 +174,14 @@ export default {
|
|||
storeReferencesKeys: () => {},
|
||||
},
|
||||
decorators: [
|
||||
BinaryFile => (
|
||||
Story => {
|
||||
useFetchMock(setupFetchMock)
|
||||
return <Story />
|
||||
},
|
||||
Story => (
|
||||
<>
|
||||
<style>{'html, body { height: 100%; }'}</style>
|
||||
<BinaryFile />
|
||||
<Story />
|
||||
</>
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { ContextRoot } from '../js/shared/context/root-context'
|
||||
import ChatPane from '../js/features/chat/components/chat-pane'
|
||||
import { stubMathJax } from '../../test/frontend/features/chat/components/stubs'
|
||||
import { setupContext } from './fixtures/context'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
const ONE_MINUTE = 60 * 1000
|
||||
|
||||
|
@ -43,31 +43,30 @@ function generateMessages(count) {
|
|||
stubMathJax()
|
||||
setupContext()
|
||||
|
||||
export const Conversation = args => <ChatPane {...args} />
|
||||
Conversation.parameters = {
|
||||
setupMocks: () => {
|
||||
fetchMock.restore()
|
||||
fetchMock.get(/messages/, generateMessages(35))
|
||||
fetchMock.post(/messages/, {})
|
||||
},
|
||||
export const Conversation = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.get(/messages/, generateMessages(35)).post(/messages/, {})
|
||||
})
|
||||
|
||||
return <ChatPane {...args} />
|
||||
}
|
||||
|
||||
export const NoMessages = args => <ChatPane {...args} />
|
||||
NoMessages.parameters = {
|
||||
setupMocks: () => {
|
||||
fetchMock.restore()
|
||||
export const NoMessages = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.get(/messages/, [])
|
||||
},
|
||||
})
|
||||
|
||||
return <ChatPane {...args} />
|
||||
}
|
||||
|
||||
export const Loading = args => <ChatPane {...args} />
|
||||
Loading.parameters = {
|
||||
setupMocks: () => {
|
||||
fetchMock.restore()
|
||||
export const Loading = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.get(/messages/, generateMessages(6), {
|
||||
delay: 1000 * 10,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return <ChatPane {...args} />
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@ -80,10 +79,6 @@ export default {
|
|||
resetUnreadMessages: () => {},
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters: { setupMocks } }) => {
|
||||
if (setupMocks) setupMocks()
|
||||
return <Story />
|
||||
},
|
||||
Story => (
|
||||
<>
|
||||
<style>{'html, body, .chat { height: 100%; width: 100%; }'}</style>
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
import React from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import CloneProjectModal from '../js/features/clone-project-modal/components/clone-project-modal'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
export const Interactive = ({
|
||||
mockResponse = 200,
|
||||
mockResponseDelay = 500,
|
||||
...args
|
||||
}) => {
|
||||
fetchMock.restore().post(
|
||||
'express:/project/:projectId/clone',
|
||||
() => {
|
||||
switch (mockResponse) {
|
||||
case 400:
|
||||
return { status: 400, body: 'The project name is not valid' }
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post(
|
||||
'express:/project/:projectId/clone',
|
||||
() => {
|
||||
switch (mockResponse) {
|
||||
case 400:
|
||||
return { status: 400, body: 'The project name is not valid' }
|
||||
|
||||
default:
|
||||
return mockResponse
|
||||
}
|
||||
},
|
||||
{ delay: mockResponseDelay }
|
||||
)
|
||||
default:
|
||||
return mockResponse
|
||||
}
|
||||
},
|
||||
{ delay: mockResponseDelay }
|
||||
)
|
||||
})
|
||||
|
||||
return <CloneProjectModal {...args} />
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import MockedSocket from 'socket.io-mock'
|
||||
|
||||
import { rootFolderBase } from './fixtures/file-tree-base'
|
||||
import { rootFolderLimit } from './fixtures/file-tree-limit'
|
||||
import FileTreeRoot from '../js/features/file-tree/components/file-tree-root'
|
||||
import FileTreeError from '../js/features/file-tree/components/file-tree-error'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
const MOCK_DELAY = 2000
|
||||
|
||||
|
@ -13,9 +13,8 @@ window._ide = {
|
|||
socket: new MockedSocket(),
|
||||
}
|
||||
|
||||
function defaultSetupMocks() {
|
||||
function defaultSetupMocks(fetchMock) {
|
||||
fetchMock
|
||||
.restore()
|
||||
.post(
|
||||
/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/,
|
||||
(path, req) => {
|
||||
|
@ -78,8 +77,11 @@ function defaultSetupMocks() {
|
|||
})
|
||||
}
|
||||
|
||||
export const FullTree = args => <FileTreeRoot {...args} />
|
||||
FullTree.parameters = { setupMocks: defaultSetupMocks }
|
||||
export const FullTree = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
|
||||
return <FileTreeRoot {...args} />
|
||||
}
|
||||
|
||||
export const ReadOnly = args => <FileTreeRoot {...args} />
|
||||
ReadOnly.args = { hasWritePermissions: false }
|
||||
|
@ -87,28 +89,34 @@ ReadOnly.args = { hasWritePermissions: false }
|
|||
export const Disconnected = args => <FileTreeRoot {...args} />
|
||||
Disconnected.args = { isConnected: false }
|
||||
|
||||
export const NetworkErrors = args => <FileTreeRoot {...args} />
|
||||
NetworkErrors.parameters = {
|
||||
setupMocks: () => {
|
||||
export const NetworkErrors = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock
|
||||
.restore()
|
||||
.post(/\/project\/\w+\/folder/, 500, {
|
||||
delay: MOCK_DELAY,
|
||||
})
|
||||
.post(/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/, 500, {
|
||||
delay: MOCK_DELAY,
|
||||
})
|
||||
.post(/\/project\/\w+\/(file|doc|folder)\/\w+\/move/, 500, {
|
||||
delay: MOCK_DELAY,
|
||||
})
|
||||
.delete(/\/project\/\w+\/(file|doc|folder)\/\w+/, 500, {
|
||||
delay: MOCK_DELAY,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return <FileTreeRoot {...args} />
|
||||
}
|
||||
|
||||
export const FallbackError = args => <FileTreeError {...args} />
|
||||
|
||||
export const FilesLimit = args => <FileTreeRoot {...args} />
|
||||
export const FilesLimit = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
|
||||
return <FileTreeRoot {...args} />
|
||||
}
|
||||
FilesLimit.args = { rootFolder: rootFolderLimit }
|
||||
FilesLimit.parameters = { setupMocks: defaultSetupMocks }
|
||||
|
||||
export default {
|
||||
title: 'File Tree',
|
||||
|
@ -136,10 +144,6 @@ export default {
|
|||
onSelect: { action: 'onSelect' },
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters: { setupMocks } }) => {
|
||||
if (setupMocks) setupMocks()
|
||||
return <Story />
|
||||
},
|
||||
Story => (
|
||||
<>
|
||||
<style>{'html, body, .file-tree { height: 100%; width: 100%; }'}</style>
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import { ContextRoot } from '../js/shared/context/root-context'
|
||||
import { setupContext } from './fixtures/context'
|
||||
import importOverleafModules from '../macros/import-overleaf-module.macro'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
const [
|
||||
{
|
||||
|
@ -21,7 +22,11 @@ CollaboratorModal.args = {
|
|||
type: 'collaborator',
|
||||
}
|
||||
|
||||
export const TeaserModal = args => <GitBridgeModal {...args} />
|
||||
export const TeaserModal = args => {
|
||||
useFetchMock(fetchMock => fetchMock.post('express:/event/:key', 202))
|
||||
|
||||
return <GitBridgeModal {...args} />
|
||||
}
|
||||
TeaserModal.args = {
|
||||
type: 'teaser',
|
||||
}
|
||||
|
@ -34,7 +39,6 @@ export default {
|
|||
},
|
||||
argTypes: {
|
||||
handleHide: { action: 'handleHide' },
|
||||
startFreeTrial: { action: 'startFreeTrial' },
|
||||
},
|
||||
decorators: [
|
||||
Story => (
|
||||
|
|
21
services/web/frontend/stories/hooks/use-fetch-mock.js
Normal file
21
services/web/frontend/stories/hooks/use-fetch-mock.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useEffect } from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
|
||||
/**
|
||||
* Run callback to mock fetch routes, call restore() when unmounted
|
||||
*/
|
||||
export default function useFetchMock(callback) {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
fetchMock.restore()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Running fetchMock.restore() here as well,
|
||||
// in case there was an error before the component was unmounted.
|
||||
fetchMock.restore()
|
||||
|
||||
// The callback has to be run here, rather than in useEffect,
|
||||
// so it's run before the component is rendered.
|
||||
callback(fetchMock)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import ShareProjectModal from '../js/features/share-project-modal/components/share-project-modal'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
const contacts = [
|
||||
// user with edited name
|
||||
|
@ -44,11 +44,10 @@ const contacts = [
|
|||
},
|
||||
]
|
||||
|
||||
const setupFetchMock = () => {
|
||||
const setupFetchMock = fetchMock => {
|
||||
const delay = 1000
|
||||
|
||||
fetchMock
|
||||
.restore()
|
||||
// list contacts
|
||||
.get('express:/user/contacts', { contacts }, { delay })
|
||||
// change privacy setting
|
||||
|
@ -86,7 +85,7 @@ const ideWithProject = project => {
|
|||
}
|
||||
|
||||
export const LinkSharingOff = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
@ -97,7 +96,7 @@ export const LinkSharingOff = args => {
|
|||
}
|
||||
|
||||
export const LinkSharingOn = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
@ -108,7 +107,7 @@ export const LinkSharingOn = args => {
|
|||
}
|
||||
|
||||
export const LinkSharingLoading = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
@ -167,7 +166,7 @@ export const RestrictedTokenMember = args => {
|
|||
}
|
||||
|
||||
export const LegacyLinkSharingReadAndWrite = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
@ -178,7 +177,7 @@ export const LegacyLinkSharingReadAndWrite = args => {
|
|||
}
|
||||
|
||||
export const LegacyLinkSharingReadOnly = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
@ -189,7 +188,7 @@ export const LegacyLinkSharingReadOnly = args => {
|
|||
}
|
||||
|
||||
export const LimitedCollaborators = args => {
|
||||
setupFetchMock()
|
||||
useFetchMock(setupFetchMock)
|
||||
|
||||
const project = {
|
||||
...args.project,
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
import React from 'react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import WordCountModal from '../js/features/word-count-modal/components/word-count-modal'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
|
||||
export const Interactive = ({
|
||||
mockResponse = 200,
|
||||
mockResponseDelay = 500,
|
||||
...args
|
||||
}) => {
|
||||
fetchMock.restore().get(
|
||||
'express:/project/:projectId/wordcount',
|
||||
() => {
|
||||
switch (mockResponse) {
|
||||
case 400:
|
||||
return { status: 400, body: 'The project id is not valid' }
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.get(
|
||||
'express:/project/:projectId/wordcount',
|
||||
() => {
|
||||
switch (mockResponse) {
|
||||
case 400:
|
||||
return { status: 400, body: 'The project id is not valid' }
|
||||
|
||||
case 200:
|
||||
return {
|
||||
texcount: {
|
||||
headers: 4,
|
||||
mathDisplay: 40,
|
||||
mathInline: 400,
|
||||
textWords: 4000,
|
||||
},
|
||||
}
|
||||
case 200:
|
||||
return {
|
||||
texcount: {
|
||||
headers: 4,
|
||||
mathDisplay: 40,
|
||||
mathInline: 400,
|
||||
textWords: 4000,
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
return mockResponse
|
||||
}
|
||||
},
|
||||
{ delay: mockResponseDelay }
|
||||
)
|
||||
default:
|
||||
return mockResponse
|
||||
}
|
||||
},
|
||||
{ delay: mockResponseDelay }
|
||||
)
|
||||
})
|
||||
|
||||
return <WordCountModal {...args} />
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue