mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -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 React from 'react'
|
||||||
|
|
||||||
import BinaryFile from '../js/features/binary-file/components/binary-file'
|
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'
|
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', {
|
const setupFetchMock = fetchMock => {
|
||||||
status: 204,
|
fetchMock
|
||||||
})
|
.head('express:/project/:project_id/file/:file_id', {
|
||||||
|
status: 201,
|
||||||
fetchMock.post('express:/project/:project_id/references/indexAll', {
|
headers: { 'Content-Length': 10000 },
|
||||||
status: 204,
|
})
|
||||||
})
|
.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'
|
window.project_id = '1234'
|
||||||
|
|
||||||
|
@ -173,10 +174,14 @@ export default {
|
||||||
storeReferencesKeys: () => {},
|
storeReferencesKeys: () => {},
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
BinaryFile => (
|
Story => {
|
||||||
|
useFetchMock(setupFetchMock)
|
||||||
|
return <Story />
|
||||||
|
},
|
||||||
|
Story => (
|
||||||
<>
|
<>
|
||||||
<style>{'html, body { height: 100%; }'}</style>
|
<style>{'html, body { height: 100%; }'}</style>
|
||||||
<BinaryFile />
|
<Story />
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import fetchMock from 'fetch-mock'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
import { ContextRoot } from '../js/shared/context/root-context'
|
import { ContextRoot } from '../js/shared/context/root-context'
|
||||||
import ChatPane from '../js/features/chat/components/chat-pane'
|
import ChatPane from '../js/features/chat/components/chat-pane'
|
||||||
import { stubMathJax } from '../../test/frontend/features/chat/components/stubs'
|
import { stubMathJax } from '../../test/frontend/features/chat/components/stubs'
|
||||||
import { setupContext } from './fixtures/context'
|
import { setupContext } from './fixtures/context'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
const ONE_MINUTE = 60 * 1000
|
const ONE_MINUTE = 60 * 1000
|
||||||
|
|
||||||
|
@ -43,31 +43,30 @@ function generateMessages(count) {
|
||||||
stubMathJax()
|
stubMathJax()
|
||||||
setupContext()
|
setupContext()
|
||||||
|
|
||||||
export const Conversation = args => <ChatPane {...args} />
|
export const Conversation = args => {
|
||||||
Conversation.parameters = {
|
useFetchMock(fetchMock => {
|
||||||
setupMocks: () => {
|
fetchMock.get(/messages/, generateMessages(35)).post(/messages/, {})
|
||||||
fetchMock.restore()
|
})
|
||||||
fetchMock.get(/messages/, generateMessages(35))
|
|
||||||
fetchMock.post(/messages/, {})
|
return <ChatPane {...args} />
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NoMessages = args => <ChatPane {...args} />
|
export const NoMessages = args => {
|
||||||
NoMessages.parameters = {
|
useFetchMock(fetchMock => {
|
||||||
setupMocks: () => {
|
|
||||||
fetchMock.restore()
|
|
||||||
fetchMock.get(/messages/, [])
|
fetchMock.get(/messages/, [])
|
||||||
},
|
})
|
||||||
|
|
||||||
|
return <ChatPane {...args} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Loading = args => <ChatPane {...args} />
|
export const Loading = args => {
|
||||||
Loading.parameters = {
|
useFetchMock(fetchMock => {
|
||||||
setupMocks: () => {
|
|
||||||
fetchMock.restore()
|
|
||||||
fetchMock.get(/messages/, generateMessages(6), {
|
fetchMock.get(/messages/, generateMessages(6), {
|
||||||
delay: 1000 * 10,
|
delay: 1000 * 10,
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
|
||||||
|
return <ChatPane {...args} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -80,10 +79,6 @@ export default {
|
||||||
resetUnreadMessages: () => {},
|
resetUnreadMessages: () => {},
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story, { parameters: { setupMocks } }) => {
|
|
||||||
if (setupMocks) setupMocks()
|
|
||||||
return <Story />
|
|
||||||
},
|
|
||||||
Story => (
|
Story => (
|
||||||
<>
|
<>
|
||||||
<style>{'html, body, .chat { height: 100%; width: 100%; }'}</style>
|
<style>{'html, body, .chat { height: 100%; width: 100%; }'}</style>
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import fetchMock from 'fetch-mock'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import CloneProjectModal from '../js/features/clone-project-modal/components/clone-project-modal'
|
import CloneProjectModal from '../js/features/clone-project-modal/components/clone-project-modal'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
export const Interactive = ({
|
export const Interactive = ({
|
||||||
mockResponse = 200,
|
mockResponse = 200,
|
||||||
mockResponseDelay = 500,
|
mockResponseDelay = 500,
|
||||||
...args
|
...args
|
||||||
}) => {
|
}) => {
|
||||||
fetchMock.restore().post(
|
useFetchMock(fetchMock => {
|
||||||
'express:/project/:projectId/clone',
|
fetchMock.post(
|
||||||
() => {
|
'express:/project/:projectId/clone',
|
||||||
switch (mockResponse) {
|
() => {
|
||||||
case 400:
|
switch (mockResponse) {
|
||||||
return { status: 400, body: 'The project name is not valid' }
|
case 400:
|
||||||
|
return { status: 400, body: 'The project name is not valid' }
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return mockResponse
|
return mockResponse
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ delay: mockResponseDelay }
|
{ delay: mockResponseDelay }
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return <CloneProjectModal {...args} />
|
return <CloneProjectModal {...args} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import fetchMock from 'fetch-mock'
|
|
||||||
import MockedSocket from 'socket.io-mock'
|
import MockedSocket from 'socket.io-mock'
|
||||||
|
|
||||||
import { rootFolderBase } from './fixtures/file-tree-base'
|
import { rootFolderBase } from './fixtures/file-tree-base'
|
||||||
import { rootFolderLimit } from './fixtures/file-tree-limit'
|
import { rootFolderLimit } from './fixtures/file-tree-limit'
|
||||||
import FileTreeRoot from '../js/features/file-tree/components/file-tree-root'
|
import FileTreeRoot from '../js/features/file-tree/components/file-tree-root'
|
||||||
import FileTreeError from '../js/features/file-tree/components/file-tree-error'
|
import FileTreeError from '../js/features/file-tree/components/file-tree-error'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
const MOCK_DELAY = 2000
|
const MOCK_DELAY = 2000
|
||||||
|
|
||||||
|
@ -13,9 +13,8 @@ window._ide = {
|
||||||
socket: new MockedSocket(),
|
socket: new MockedSocket(),
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultSetupMocks() {
|
function defaultSetupMocks(fetchMock) {
|
||||||
fetchMock
|
fetchMock
|
||||||
.restore()
|
|
||||||
.post(
|
.post(
|
||||||
/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/,
|
/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/,
|
||||||
(path, req) => {
|
(path, req) => {
|
||||||
|
@ -78,8 +77,11 @@ function defaultSetupMocks() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FullTree = args => <FileTreeRoot {...args} />
|
export const FullTree = args => {
|
||||||
FullTree.parameters = { setupMocks: defaultSetupMocks }
|
useFetchMock(defaultSetupMocks)
|
||||||
|
|
||||||
|
return <FileTreeRoot {...args} />
|
||||||
|
}
|
||||||
|
|
||||||
export const ReadOnly = args => <FileTreeRoot {...args} />
|
export const ReadOnly = args => <FileTreeRoot {...args} />
|
||||||
ReadOnly.args = { hasWritePermissions: false }
|
ReadOnly.args = { hasWritePermissions: false }
|
||||||
|
@ -87,28 +89,34 @@ ReadOnly.args = { hasWritePermissions: false }
|
||||||
export const Disconnected = args => <FileTreeRoot {...args} />
|
export const Disconnected = args => <FileTreeRoot {...args} />
|
||||||
Disconnected.args = { isConnected: false }
|
Disconnected.args = { isConnected: false }
|
||||||
|
|
||||||
export const NetworkErrors = args => <FileTreeRoot {...args} />
|
export const NetworkErrors = args => {
|
||||||
NetworkErrors.parameters = {
|
useFetchMock(fetchMock => {
|
||||||
setupMocks: () => {
|
|
||||||
fetchMock
|
fetchMock
|
||||||
.restore()
|
|
||||||
.post(/\/project\/\w+\/folder/, 500, {
|
.post(/\/project\/\w+\/folder/, 500, {
|
||||||
delay: MOCK_DELAY,
|
delay: MOCK_DELAY,
|
||||||
})
|
})
|
||||||
.post(/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/, 500, {
|
.post(/\/project\/\w+\/(file|doc|folder)\/\w+\/rename/, 500, {
|
||||||
delay: MOCK_DELAY,
|
delay: MOCK_DELAY,
|
||||||
})
|
})
|
||||||
|
.post(/\/project\/\w+\/(file|doc|folder)\/\w+\/move/, 500, {
|
||||||
|
delay: MOCK_DELAY,
|
||||||
|
})
|
||||||
.delete(/\/project\/\w+\/(file|doc|folder)\/\w+/, 500, {
|
.delete(/\/project\/\w+\/(file|doc|folder)\/\w+/, 500, {
|
||||||
delay: MOCK_DELAY,
|
delay: MOCK_DELAY,
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
|
||||||
|
return <FileTreeRoot {...args} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FallbackError = args => <FileTreeError {...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.args = { rootFolder: rootFolderLimit }
|
||||||
FilesLimit.parameters = { setupMocks: defaultSetupMocks }
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'File Tree',
|
title: 'File Tree',
|
||||||
|
@ -136,10 +144,6 @@ export default {
|
||||||
onSelect: { action: 'onSelect' },
|
onSelect: { action: 'onSelect' },
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story, { parameters: { setupMocks } }) => {
|
|
||||||
if (setupMocks) setupMocks()
|
|
||||||
return <Story />
|
|
||||||
},
|
|
||||||
Story => (
|
Story => (
|
||||||
<>
|
<>
|
||||||
<style>{'html, body, .file-tree { height: 100%; width: 100%; }'}</style>
|
<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 { ContextRoot } from '../js/shared/context/root-context'
|
||||||
import { setupContext } from './fixtures/context'
|
import { setupContext } from './fixtures/context'
|
||||||
import importOverleafModules from '../macros/import-overleaf-module.macro'
|
import importOverleafModules from '../macros/import-overleaf-module.macro'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
const [
|
const [
|
||||||
{
|
{
|
||||||
|
@ -21,7 +22,11 @@ CollaboratorModal.args = {
|
||||||
type: 'collaborator',
|
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 = {
|
TeaserModal.args = {
|
||||||
type: 'teaser',
|
type: 'teaser',
|
||||||
}
|
}
|
||||||
|
@ -34,7 +39,6 @@ export default {
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
handleHide: { action: 'handleHide' },
|
handleHide: { action: 'handleHide' },
|
||||||
startFreeTrial: { action: 'startFreeTrial' },
|
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
Story => (
|
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 React, { useEffect } from 'react'
|
||||||
import fetchMock from 'fetch-mock'
|
|
||||||
import ShareProjectModal from '../js/features/share-project-modal/components/share-project-modal'
|
import ShareProjectModal from '../js/features/share-project-modal/components/share-project-modal'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
const contacts = [
|
const contacts = [
|
||||||
// user with edited name
|
// user with edited name
|
||||||
|
@ -44,11 +44,10 @@ const contacts = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const setupFetchMock = () => {
|
const setupFetchMock = fetchMock => {
|
||||||
const delay = 1000
|
const delay = 1000
|
||||||
|
|
||||||
fetchMock
|
fetchMock
|
||||||
.restore()
|
|
||||||
// list contacts
|
// list contacts
|
||||||
.get('express:/user/contacts', { contacts }, { delay })
|
.get('express:/user/contacts', { contacts }, { delay })
|
||||||
// change privacy setting
|
// change privacy setting
|
||||||
|
@ -86,7 +85,7 @@ const ideWithProject = project => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LinkSharingOff = args => {
|
export const LinkSharingOff = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
@ -97,7 +96,7 @@ export const LinkSharingOff = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LinkSharingOn = args => {
|
export const LinkSharingOn = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
@ -108,7 +107,7 @@ export const LinkSharingOn = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LinkSharingLoading = args => {
|
export const LinkSharingLoading = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
@ -167,7 +166,7 @@ export const RestrictedTokenMember = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LegacyLinkSharingReadAndWrite = args => {
|
export const LegacyLinkSharingReadAndWrite = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
@ -178,7 +177,7 @@ export const LegacyLinkSharingReadAndWrite = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LegacyLinkSharingReadOnly = args => {
|
export const LegacyLinkSharingReadOnly = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
@ -189,7 +188,7 @@ export const LegacyLinkSharingReadOnly = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LimitedCollaborators = args => {
|
export const LimitedCollaborators = args => {
|
||||||
setupFetchMock()
|
useFetchMock(setupFetchMock)
|
||||||
|
|
||||||
const project = {
|
const project = {
|
||||||
...args.project,
|
...args.project,
|
||||||
|
|
|
@ -1,37 +1,39 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import fetchMock from 'fetch-mock'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import WordCountModal from '../js/features/word-count-modal/components/word-count-modal'
|
import WordCountModal from '../js/features/word-count-modal/components/word-count-modal'
|
||||||
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
|
|
||||||
export const Interactive = ({
|
export const Interactive = ({
|
||||||
mockResponse = 200,
|
mockResponse = 200,
|
||||||
mockResponseDelay = 500,
|
mockResponseDelay = 500,
|
||||||
...args
|
...args
|
||||||
}) => {
|
}) => {
|
||||||
fetchMock.restore().get(
|
useFetchMock(fetchMock => {
|
||||||
'express:/project/:projectId/wordcount',
|
fetchMock.get(
|
||||||
() => {
|
'express:/project/:projectId/wordcount',
|
||||||
switch (mockResponse) {
|
() => {
|
||||||
case 400:
|
switch (mockResponse) {
|
||||||
return { status: 400, body: 'The project id is not valid' }
|
case 400:
|
||||||
|
return { status: 400, body: 'The project id is not valid' }
|
||||||
|
|
||||||
case 200:
|
case 200:
|
||||||
return {
|
return {
|
||||||
texcount: {
|
texcount: {
|
||||||
headers: 4,
|
headers: 4,
|
||||||
mathDisplay: 40,
|
mathDisplay: 40,
|
||||||
mathInline: 400,
|
mathInline: 400,
|
||||||
textWords: 4000,
|
textWords: 4000,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return mockResponse
|
return mockResponse
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ delay: mockResponseDelay }
|
{ delay: mockResponseDelay }
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return <WordCountModal {...args} />
|
return <WordCountModal {...args} />
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue