mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-10 22:47:15 +00:00
Merge pull request #13947 from overleaf/mf-tw-tpr-not-original-importer
Improve user behaviour on Mendeley/Zotero refresh screen UI GitOrigin-RevId: 50f83e88f14e1708d46dcfbd53c4e7d62684b4dc
This commit is contained in:
parent
e3a99a82db
commit
8b9f69012c
13 changed files with 527 additions and 117 deletions
|
@ -499,7 +499,6 @@
|
|||
"hotkeys": "",
|
||||
"how_it_works": "",
|
||||
"i_want_to_stay": "",
|
||||
"if_error_persists_try_relinking_provider": "",
|
||||
"if_you_need_to_customize_your_table_further_you_can": "",
|
||||
"ignore_validation_errors": "",
|
||||
"ill_take_it": "",
|
||||
|
@ -752,6 +751,7 @@
|
|||
"only_group_admin_or_managers_can_delete_your_account_3": "",
|
||||
"only_group_admin_or_managers_can_delete_your_account_4": "",
|
||||
"only_group_admin_or_managers_can_delete_your_account_5": "",
|
||||
"only_importer_can_refresh": "",
|
||||
"open_file": "",
|
||||
"open_link": "",
|
||||
"open_project": "",
|
||||
|
@ -1035,6 +1035,7 @@
|
|||
"showing_x_results_of_total": "",
|
||||
"signature_algorithm": "",
|
||||
"single_sign_on_sso": "",
|
||||
"something_not_right": "",
|
||||
"something_went_wrong_loading_pdf_viewer": "",
|
||||
"something_went_wrong_processing_the_request": "",
|
||||
"something_went_wrong_rendering_pdf": "",
|
||||
|
@ -1215,6 +1216,7 @@
|
|||
"try_it_for_free": "",
|
||||
"try_premium_for_free": "",
|
||||
"try_recompile_project_or_troubleshoot": "",
|
||||
"try_relinking_provider": "",
|
||||
"try_to_compile_despite_errors": "",
|
||||
"turn_off_link_sharing": "",
|
||||
"turn_on_link_sharing": "",
|
||||
|
|
|
@ -1,30 +1,25 @@
|
|||
import { useState, useCallback, type ElementType } from 'react'
|
||||
import { useState, type ElementType } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { formatTime, relativeDate } from '../../utils/format-date'
|
||||
import { postJSON } from '../../../infrastructure/fetch-json'
|
||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||
import { useProjectContext } from '../../../shared/context/project-context'
|
||||
|
||||
import { Nullable } from '../../../../../types/utils'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
import useAbortController from '../../../shared/hooks/use-abort-controller'
|
||||
import { LinkedFileIcon } from './file-view-icons'
|
||||
import { BinaryFile, hasProvider, LinkedFile } from '../types/binary-file'
|
||||
import FileViewRefreshButton from './file-view-refresh-button'
|
||||
import FileViewNotOriginalImporter from './file-view-not-original-importer'
|
||||
import FileViewRefreshError from './file-view-refresh-error'
|
||||
|
||||
const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo') as {
|
||||
import: { LinkedFileInfo: ElementType }
|
||||
path: string
|
||||
}[]
|
||||
|
||||
const tprLinkedFileRefreshError = importOverleafModules(
|
||||
'tprLinkedFileRefreshError'
|
||||
) as {
|
||||
import: { LinkedFileRefreshError: ElementType }
|
||||
path: string
|
||||
}[]
|
||||
|
||||
const MAX_URL_LENGTH = 60
|
||||
const FRONT_OF_URL_LENGTH = 35
|
||||
const FILLER = '...'
|
||||
|
@ -55,10 +50,7 @@ export default function FileViewHeader({ file }: FileViewHeaderProps) {
|
|||
})
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [refreshError, setRefreshError] = useState(null)
|
||||
|
||||
const { signal } = useAbortController()
|
||||
const [refreshError, setRefreshError] = useState<Nullable<string>>(null)
|
||||
|
||||
let fileInfo
|
||||
if (file.linkedFileData) {
|
||||
|
@ -83,29 +75,6 @@ export default function FileViewHeader({ file }: FileViewHeaderProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const refreshFile = useCallback(() => {
|
||||
setRefreshing(true)
|
||||
// Replacement of the file handled by the file tree
|
||||
window.expectingLinkedFileRefreshedSocketFor = file.name
|
||||
const body = {
|
||||
shouldReindexReferences:
|
||||
file.linkedFileData?.provider === 'mendeley' ||
|
||||
file.linkedFileData?.provider === 'zotero' ||
|
||||
/\.bib$/.test(file.name),
|
||||
}
|
||||
postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
|
||||
signal,
|
||||
body,
|
||||
})
|
||||
.then(() => {
|
||||
setRefreshing(false)
|
||||
})
|
||||
.catch(err => {
|
||||
setRefreshing(false)
|
||||
setRefreshError(err.data?.message || err.message)
|
||||
})
|
||||
}, [file, projectId, signal])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{file.linkedFileData && fileInfo}
|
||||
|
@ -114,14 +83,7 @@ export default function FileViewHeader({ file }: FileViewHeaderProps) {
|
|||
<LinkedFileInfo key={path} file={file} />
|
||||
))}
|
||||
{file.linkedFileData && permissionsLevel !== 'readOnly' && (
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={refreshFile}
|
||||
disabled={refreshing}
|
||||
>
|
||||
<Icon type="refresh" spin={refreshing} fw />
|
||||
<span>{refreshing ? t('refreshing') + '...' : t('refresh')}</span>
|
||||
</button>
|
||||
<FileViewRefreshButton file={file} setRefreshError={setRefreshError} />
|
||||
)}
|
||||
|
||||
<a
|
||||
|
@ -133,18 +95,9 @@ export default function FileViewHeader({ file }: FileViewHeaderProps) {
|
|||
|
||||
<span>{t('download')}</span>
|
||||
</a>
|
||||
{file.linkedFileData && <FileViewNotOriginalImporter file={file} />}
|
||||
{refreshError && (
|
||||
<div className="row">
|
||||
<br />
|
||||
<div className="alert alert-danger col-md-6 col-md-offset-3">
|
||||
{t('access_denied')}: {refreshError}
|
||||
{tprLinkedFileRefreshError.map(
|
||||
({ import: { LinkedFileRefreshError }, path }) => (
|
||||
<LinkedFileRefreshError key={path} file={file} />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<FileViewRefreshError file={file} refreshError={refreshError} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { capitalize } from 'lodash'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { BinaryFile, hasProvider } from '../types/binary-file'
|
||||
|
||||
type FileViewNotOriginalImporterProps = {
|
||||
file: BinaryFile
|
||||
}
|
||||
|
||||
export default function FileViewNotOriginalImporter({
|
||||
file,
|
||||
}: FileViewNotOriginalImporterProps) {
|
||||
const { t } = useTranslation()
|
||||
const { id: userId } = useUserContext()
|
||||
|
||||
const isMendeleyOrZotero =
|
||||
hasProvider(file, 'mendeley') || hasProvider(file, 'zotero')
|
||||
|
||||
if (!isMendeleyOrZotero) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isImporter = file.linkedFileData.importer_id === userId
|
||||
|
||||
if (isImporter) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="alert">
|
||||
{t('only_importer_can_refresh', {
|
||||
provider: capitalize(file.linkedFileData.provider),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import {
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from 'classnames'
|
||||
import Icon from '@/shared/components/icon'
|
||||
import { postJSON } from '@/infrastructure/fetch-json'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import useAbortController from '@/shared/hooks/use-abort-controller'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { hasProvider, type BinaryFile } from '../types/binary-file'
|
||||
import { Nullable } from '../../../../../types/utils'
|
||||
|
||||
type FileViewRefreshButtonProps = {
|
||||
setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
|
||||
file: BinaryFile
|
||||
}
|
||||
|
||||
export default function FileViewRefreshButton({
|
||||
setRefreshError,
|
||||
file,
|
||||
}: FileViewRefreshButtonProps) {
|
||||
const { signal } = useAbortController()
|
||||
const { t } = useTranslation()
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const { _id: projectId } = useProjectContext()
|
||||
const { id: userId } = useUserContext()
|
||||
|
||||
const isMendeleyOrZotero =
|
||||
hasProvider(file, 'mendeley') || hasProvider(file, 'zotero')
|
||||
|
||||
let isImporter
|
||||
|
||||
if (isMendeleyOrZotero) {
|
||||
isImporter = file.linkedFileData.importer_id === userId
|
||||
}
|
||||
|
||||
const buttonClickable = isMendeleyOrZotero ? isImporter : true
|
||||
|
||||
const refreshFile = useCallback(() => {
|
||||
setRefreshing(true)
|
||||
// Replacement of the file handled by the file tree
|
||||
window.expectingLinkedFileRefreshedSocketFor = file.name
|
||||
const body = {
|
||||
shouldReindexReferences: isMendeleyOrZotero || /\.bib$/.test(file.name),
|
||||
}
|
||||
postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
|
||||
signal,
|
||||
body,
|
||||
})
|
||||
.then(() => {
|
||||
setRefreshing(false)
|
||||
})
|
||||
.catch(err => {
|
||||
setRefreshing(false)
|
||||
setRefreshError(err.data?.message || err.message)
|
||||
})
|
||||
}, [file, projectId, signal, setRefreshError, isMendeleyOrZotero])
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames('btn', {
|
||||
'btn-primary': buttonClickable,
|
||||
'btn-secondary': !buttonClickable,
|
||||
})}
|
||||
onClick={refreshFile}
|
||||
disabled={refreshing || !buttonClickable}
|
||||
>
|
||||
<Icon type="refresh" spin={refreshing} fw />
|
||||
<span>{refreshing ? `${t('refreshing')}…` : t('refresh')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import type { ElementType } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
import { BinaryFile, hasProvider } from '../types/binary-file'
|
||||
|
||||
const tprLinkedFileRefreshError = importOverleafModules(
|
||||
'tprLinkedFileRefreshError'
|
||||
) as {
|
||||
import: { LinkedFileRefreshError: ElementType }
|
||||
path: string
|
||||
}[]
|
||||
|
||||
type FileViewRefreshErrorProps = {
|
||||
file: BinaryFile
|
||||
refreshError: string
|
||||
}
|
||||
|
||||
export default function FileViewRefreshError({
|
||||
file,
|
||||
refreshError,
|
||||
}: FileViewRefreshErrorProps) {
|
||||
const isMendeleyOrZotero =
|
||||
hasProvider(file, 'mendeley') || hasProvider(file, 'zotero')
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<br />
|
||||
|
||||
{isMendeleyOrZotero ? (
|
||||
<FileViewMendeleyOrZoteroRefreshError file={file} />
|
||||
) : (
|
||||
<FileViewDefaultRefreshError refreshError={refreshError} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type FileViewMendeleyOrZoteroRefreshErrorProps = {
|
||||
file: BinaryFile
|
||||
}
|
||||
|
||||
function FileViewMendeleyOrZoteroRefreshError({
|
||||
file,
|
||||
}: FileViewMendeleyOrZoteroRefreshErrorProps) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div
|
||||
className="alert alert-danger col-md-10 col-md-offset-1"
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '10px' }}
|
||||
>
|
||||
<div>
|
||||
{t('something_not_right')}!
|
||||
{tprLinkedFileRefreshError.map(
|
||||
({ import: { LinkedFileRefreshError }, path }) => (
|
||||
<LinkedFileRefreshError key={path} file={file} />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<button className="btn btn-danger">
|
||||
<a
|
||||
href="/user/settings"
|
||||
target="_blank"
|
||||
style={{ fontWeight: 'bold', textDecoration: 'none' }}
|
||||
>
|
||||
{t('go_to_settings')}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type FileViewDefaultRefreshErrorProps = {
|
||||
refreshError: string
|
||||
}
|
||||
|
||||
function FileViewDefaultRefreshError({
|
||||
refreshError,
|
||||
}: FileViewDefaultRefreshErrorProps) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="alert alert-danger col-md-6 col-md-offset-3">
|
||||
{t('access_denied')}: {refreshError}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -705,7 +705,6 @@
|
|||
"how_to_insert_images": "Hvordan indsætter jeg figurer",
|
||||
"hundreds_templates_info": "Skab smukke dokumenter ved at starte fra vores galleri af LaTeX skabeloner for journaler, konferencer, afhandlinger, rapporter, CV’er og meget mere.",
|
||||
"i_want_to_stay": "Jeg ønsker at blive",
|
||||
"if_error_persists_try_relinking_provider": "Hvis denne fejl fortsætter kan du prøve at re-etablere fobindelsen til din __provider__ konto her",
|
||||
"if_have_existing_can_link": "Hvis du har en eksisterende <b>__appName__</b>-konto under en anden e-mailaddresse, kan du forbinde den til din <b>__institutionName__</b>-konto ved at klikke <b>__clickText__</b>",
|
||||
"if_owner_can_link": "Hvis du ejer <b>__appName__</b>-kontoen under <b>__email__</b>, vil du få mulighed for at forbinde den til din institutionelle konto hos <b>__institutionName__</b>.",
|
||||
"ignore_and_continue_institution_linking": "Du kan også springe det over, og <a href=\"__link__\">fortsætte til __appName__ med kontoen for <b>__email__</b></a>.",
|
||||
|
|
|
@ -711,7 +711,6 @@
|
|||
"how_to_insert_images": "So fügst du Bilder ein",
|
||||
"hundreds_templates_info": "Erstelle schöne Dokumente ausgehend von unserer Galerie mit LaTeX-Vorlagen für Zeitschriften, Konferenzen, Abschlussarbeiten, Berichte, Lebensläufe und vieles mehr.",
|
||||
"i_want_to_stay": "Ich möchte bleiben",
|
||||
"if_error_persists_try_relinking_provider": "Wenn dieser Fehler weiterhin besteht, versuche, dein __provider__-Konto hier erneut zu verknüpfen",
|
||||
"if_have_existing_can_link": "Wenn du ein vorhandenes <b>__appName__</b>-Konto mit einer anderen E-Mail-Adresse hast, kannst du es mit deinem <b>__institutionName__</b>-Konto verknüpfen, indem du auf <b>„__clickText__“</b> klickst.",
|
||||
"if_owner_can_link": "Wenn du das <b>__appName__</b> Konto mit <b>__email__</b> besitzt, kannst du es mit deinem institutionellen Konto <b>__institutionName__</b> verknüpfen.",
|
||||
"ignore_and_continue_institution_linking": "Du kannst dies auch ignorieren und <a href=\"__link__\">weiter zu __appName__ mit deinem <b>__email__</b>-Konto</a> gehen.",
|
||||
|
|
|
@ -786,7 +786,6 @@
|
|||
"how_to_insert_images": "How to insert images",
|
||||
"hundreds_templates_info": "Produce beautiful documents starting from our gallery of LaTeX templates for journals, conferences, theses, reports, CVs and much more.",
|
||||
"i_want_to_stay": "I want to stay",
|
||||
"if_error_persists_try_relinking_provider": "If this error persists, try re-linking your __provider__ account here",
|
||||
"if_have_existing_can_link": "If you have an existing <b>__appName__</b> account on another email, you can link it to your <b>__institutionName__</b> account by clicking <b>__clickText__</b>.",
|
||||
"if_owner_can_link": "If you own the <b>__appName__</b> account with <b>__email__</b>, you will be allowed to link it to your <b>__institutionName__</b> institutional account.",
|
||||
"if_you_need_to_customize_your_table_further_you_can": "If you need to customize your table further, you can. Using LaTeX code, you can change anything from table styles and border styles to colors and column widths. <0>Read our guide</0> to using tables in LaTeX to help you get started.",
|
||||
|
@ -1194,6 +1193,7 @@
|
|||
"only_group_admin_or_managers_can_delete_your_account_3": "Your group admin and group managers will be able to reassign ownership of your projects to another group member.",
|
||||
"only_group_admin_or_managers_can_delete_your_account_4": "Once you have become a managed user, you cannot change back. <0>Learn more about managed Overleaf accounts.</0>",
|
||||
"only_group_admin_or_managers_can_delete_your_account_5": "For more information, see the \"Managed Accounts\" section in our terms of use, which you agree to by clicking Accept invitation",
|
||||
"only_importer_can_refresh": "Only the person who originally imported this __provider__ file can refresh it.",
|
||||
"open_a_file_on_the_left": "Open a file on the left",
|
||||
"open_as_template": "Open as Template",
|
||||
"open_file": "Edit file",
|
||||
|
@ -1611,6 +1611,7 @@
|
|||
"skip_to_content": "Skip to content",
|
||||
"sl_gives_you_free_stuff_see_progress_below": "When someone starts using __appName__ after your recommendation we’ll give you some <strong>free stuff</strong> to say thanks! Check your progress below.",
|
||||
"sl_included_history_of_changes_blurb": "__appName__ includes a history of all of your changes so you can see exactly who changed what, and when. This makes it extremely easy to keep up to date with any progress made by your collaborators and allows you to review recent work.",
|
||||
"something_not_right": "Something’s not right",
|
||||
"something_went_wrong_canceling_your_subscription": "Something went wrong canceling your subscription. Please contact support.",
|
||||
"something_went_wrong_loading_pdf_viewer": "Something went wrong loading the PDF viewer. This might be caused by issues like <0>temporary network problems</0> or an <0>outdated web browser</0>. Please follow the <1>troubleshooting steps for access, loading and display problems</1>. If the issue persists, please <2>let us know</2>.",
|
||||
"something_went_wrong_processing_the_request": "Something went wrong processing the request",
|
||||
|
@ -1858,6 +1859,7 @@
|
|||
"try_now": "Try Now",
|
||||
"try_premium_for_free": "Try Premium for free",
|
||||
"try_recompile_project_or_troubleshoot": "Please try recompiling the project from scratch, and if that doesn’t help, follow our <0>troubleshooting guide</0>.",
|
||||
"try_relinking_provider": "It looks like you need to re-link your __provider__ account.",
|
||||
"try_to_compile_despite_errors": "Try to compile despite errors",
|
||||
"turn_off_link_sharing": "Turn off link sharing",
|
||||
"turn_on_link_sharing": "Turn on link sharing",
|
||||
|
|
|
@ -447,7 +447,6 @@
|
|||
"hotkeys": "快捷键",
|
||||
"hundreds_templates_info": "从我们的 LaTeX 模板库开始,为期刊、会议、论文、报告、简历等制作漂亮的文档。",
|
||||
"i_want_to_stay": "我要留下",
|
||||
"if_error_persists_try_relinking_provider": "如果此错误仍然存在,请尝试在此处重新链接您的__provider__帐户",
|
||||
"if_have_existing_can_link": "如果您在另一封电子邮件中有一个现有的 <b>__appName__</b> 帐户,您可以通过单击 <b>__clickText__</b> 将其链接到您的 <b>__institutionName__</b> 账户。",
|
||||
"if_owner_can_link": "如果您在<b>__appName__</b>拥有账户<b>__email__</b>,您可以将其链接到您的 <b>__institutionName__</b> 机构帐户。",
|
||||
"ignore_and_continue_institution_linking": "您也可以忽略此项,然后<a href=\"__link__\">继续在 __appName__ 上使用您的 <b>__email__</b> 帐户</a>。",
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import {
|
||||
screen,
|
||||
fireEvent,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import { screen } from '@testing-library/react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
import FileViewHeader from '../../../../../frontend/js/features/file-view/components/file-view-header'
|
||||
import { USER_ID } from '../../../helpers/editor-providers'
|
||||
|
||||
describe('<FileViewHeader/>', function () {
|
||||
const urlFile = {
|
||||
|
@ -26,6 +22,7 @@ describe('<FileViewHeader/>', function () {
|
|||
source_project_id: 'source-project-id',
|
||||
source_entity_path: '/source-entity-path.ext',
|
||||
provider: 'project_file',
|
||||
importer_id: USER_ID,
|
||||
},
|
||||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
@ -40,14 +37,6 @@ describe('<FileViewHeader/>', function () {
|
|||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
||||
const thirdPartyReferenceFile = {
|
||||
name: 'example.tex',
|
||||
linkedFileData: {
|
||||
provider: 'zotero',
|
||||
},
|
||||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
fetchMock.reset()
|
||||
})
|
||||
|
@ -85,48 +74,6 @@ describe('<FileViewHeader/>', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('The refresh button', async function () {
|
||||
it('Changes text when the file is refreshing', async function () {
|
||||
fetchMock.post(
|
||||
'express:/project/:project_id/linked_file/:file_id/refresh',
|
||||
{
|
||||
new_file_id: '5ff7418157b4e144321df5c4',
|
||||
}
|
||||
)
|
||||
|
||||
renderWithEditorContext(<FileViewHeader file={projectFile} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Refresh' }))
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText('Refreshing', { exact: false })
|
||||
)
|
||||
await screen.findByText('Refresh')
|
||||
})
|
||||
|
||||
it('Reindexes references after refreshing a file from a third-party provider', async function () {
|
||||
fetchMock.post(
|
||||
'express:/project/:project_id/linked_file/:file_id/refresh',
|
||||
{
|
||||
new_file_id: '5ff7418157b4e144321df5c4',
|
||||
}
|
||||
)
|
||||
|
||||
renderWithEditorContext(<FileViewHeader file={thirdPartyReferenceFile} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Refresh' }))
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText('Refreshing', { exact: false })
|
||||
)
|
||||
|
||||
expect(fetchMock.done()).to.be.true
|
||||
|
||||
const lastCallBody = JSON.parse(fetchMock.lastCall()[1].body)
|
||||
expect(lastCallBody.shouldReindexReferences).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
describe('The download button', function () {
|
||||
it('exists', function () {
|
||||
renderWithEditorContext(<FileViewHeader file={urlFile} />)
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import { screen } from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import FileViewNotOriginalImporter from '@/features/file-view/components/file-view-not-original-importer'
|
||||
import { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
import { USER_ID } from '../../../helpers/editor-providers'
|
||||
|
||||
describe('<FileViewNotOriginalImporter />', function () {
|
||||
describe('provider is not mendeley and zotero', function () {
|
||||
it('does not show error if provider is not mendeley and zotero', function () {
|
||||
const urlFile: BinaryFile<'url'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'url',
|
||||
url: '/url/file.png',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'file.png',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
renderWithEditorContext(<FileViewNotOriginalImporter file={urlFile} />)
|
||||
|
||||
const text = screen.queryByText(
|
||||
/Only the person who originally imported this/
|
||||
)
|
||||
|
||||
expect(text).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe('provider is mendeley or zotero', function () {
|
||||
it('does not show error if current user is the original importer of the file', function () {
|
||||
const mendeleyFile: BinaryFile<'mendeley'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'mendeley',
|
||||
importer_id: USER_ID,
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'references.bib',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
renderWithEditorContext(
|
||||
<FileViewNotOriginalImporter file={mendeleyFile} />
|
||||
)
|
||||
|
||||
const text = screen.queryByText(
|
||||
'Only the person who originally imported this Mendeley file can refresh it.'
|
||||
)
|
||||
|
||||
expect(text).to.not.exist
|
||||
})
|
||||
|
||||
it('shows error if provider is mendeley and current user is not the original importer of the file', function () {
|
||||
const mendeleyFile: BinaryFile<'mendeley'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'mendeley',
|
||||
importer_id: 'user123',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'references.bib',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
renderWithEditorContext(
|
||||
<FileViewNotOriginalImporter file={mendeleyFile} />
|
||||
)
|
||||
|
||||
const text = screen.getByText(
|
||||
'Only the person who originally imported this Mendeley file can refresh it.'
|
||||
)
|
||||
|
||||
expect(text).to.exist
|
||||
})
|
||||
|
||||
it('shows error if provider is zotero and current user is not the original importer of the file', function () {
|
||||
const zoteroFile: BinaryFile<'zotero'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'zotero',
|
||||
importer_id: 'user123',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'references.bib',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
renderWithEditorContext(<FileViewNotOriginalImporter file={zoteroFile} />)
|
||||
|
||||
const text = screen.getByText(
|
||||
'Only the person who originally imported this Zotero file can refresh it.'
|
||||
)
|
||||
|
||||
expect(text).to.exist
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,104 @@
|
|||
import {
|
||||
screen,
|
||||
fireEvent,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import FileViewRefreshButton from '@/features/file-view/components/file-view-refresh-button'
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
import { USER_ID } from '../../../helpers/editor-providers'
|
||||
|
||||
describe('<FileViewRefreshButton />', function () {
|
||||
const projectFile = {
|
||||
name: 'example.tex',
|
||||
linkedFileData: {
|
||||
v1_source_doc_id: 'v1-source-id',
|
||||
source_project_id: 'source-project-id',
|
||||
source_entity_path: '/source-entity-path.ext',
|
||||
provider: 'project_file',
|
||||
importer_id: USER_ID,
|
||||
},
|
||||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
||||
const thirdPartyReferenceFile = {
|
||||
name: 'example.tex',
|
||||
linkedFileData: {
|
||||
provider: 'zotero',
|
||||
importer_id: USER_ID,
|
||||
},
|
||||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
||||
const thirdPartyNotOriginalImporterReferenceFile = {
|
||||
name: 'references.bib',
|
||||
linkedFileData: {
|
||||
v1_source_doc_id: 'v1-source-id',
|
||||
source_project_id: 'source-project-id',
|
||||
source_entity_path: '/source-entity-path.ext',
|
||||
provider: 'mendeley',
|
||||
importer_id: '123abc',
|
||||
},
|
||||
created: new Date(2021, 1, 17, 3, 24).toISOString(),
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
fetchMock.reset()
|
||||
})
|
||||
|
||||
it('Changes text when the file is refreshing', async function () {
|
||||
fetchMock.post(
|
||||
'express:/project/:project_id/linked_file/:file_id/refresh',
|
||||
{
|
||||
new_file_id: '5ff7418157b4e144321df5c4',
|
||||
}
|
||||
)
|
||||
|
||||
renderWithEditorContext(<FileViewRefreshButton file={projectFile} />)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Refresh' }))
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText('Refreshing', { exact: false })
|
||||
)
|
||||
|
||||
await screen.findByText('Refresh')
|
||||
})
|
||||
|
||||
it('Reindexes references after refreshing a file from a third-party provider', async function () {
|
||||
fetchMock.post(
|
||||
'express:/project/:project_id/linked_file/:file_id/refresh',
|
||||
{
|
||||
new_file_id: '5ff7418157b4e144321df5c4',
|
||||
}
|
||||
)
|
||||
|
||||
renderWithEditorContext(
|
||||
<FileViewRefreshButton file={thirdPartyReferenceFile} />
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Refresh' }))
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText('Refreshing', { exact: false })
|
||||
)
|
||||
|
||||
expect(fetchMock.done()).to.be.true
|
||||
|
||||
const lastCallBody = JSON.parse(fetchMock.lastCall()[1].body)
|
||||
expect(lastCallBody.shouldReindexReferences).to.be.true
|
||||
})
|
||||
|
||||
it('is disabled when user is not original importer', function () {
|
||||
renderWithEditorContext(
|
||||
<FileViewRefreshButton
|
||||
file={thirdPartyNotOriginalImporterReferenceFile}
|
||||
/>
|
||||
)
|
||||
|
||||
const button = screen.getByRole('button', { name: 'Refresh' })
|
||||
|
||||
expect(button.disabled).to.equal(true)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,96 @@
|
|||
import { render, screen } from '@testing-library/react'
|
||||
import FileViewRefreshError from '@/features/file-view/components/file-view-refresh-error'
|
||||
import type { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('<FileViewRefreshError />', function () {
|
||||
describe('<FileViewMendeleyOrZoteroRefreshError />', function () {
|
||||
it('shows correct error message for mendeley', async function () {
|
||||
const mendeleyFile: BinaryFile<'mendeley'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'mendeley',
|
||||
importer_id: 'user123456',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'references.bib',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
render(
|
||||
<FileViewRefreshError
|
||||
file={mendeleyFile}
|
||||
refreshError="error_message"
|
||||
/>
|
||||
)
|
||||
|
||||
screen.getByText(/Something’s not right!/)
|
||||
screen.getByText(
|
||||
/It looks like you need to re-link your Mendeley account./
|
||||
)
|
||||
|
||||
const goToSettingsLink = screen.getByRole('link', {
|
||||
name: 'Go to settings',
|
||||
})
|
||||
|
||||
expect(goToSettingsLink.getAttribute('href')).to.equal('/user/settings')
|
||||
})
|
||||
|
||||
it('shows correct error message for zotero', async function () {
|
||||
const zoteroFile: BinaryFile<'zotero'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'zotero',
|
||||
importer_id: 'user123456',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'references.bib',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
render(
|
||||
<FileViewRefreshError file={zoteroFile} refreshError="error_message" />
|
||||
)
|
||||
|
||||
screen.getByText(/Something’s not right!/)
|
||||
screen.getByText(/It looks like you need to re-link your Zotero account./)
|
||||
|
||||
const goToSettingsLink = screen.getByRole('link', {
|
||||
name: 'Go to settings',
|
||||
})
|
||||
|
||||
expect(goToSettingsLink.getAttribute('href')).to.equal('/user/settings')
|
||||
})
|
||||
})
|
||||
|
||||
describe('<FileViewDefaultRefreshError />', function () {
|
||||
it('shows correct error message', function () {
|
||||
const anotherProjectFile: BinaryFile<'project_file'> = {
|
||||
id: '123abc',
|
||||
_id: '123abc',
|
||||
linkedFileData: {
|
||||
provider: 'project_file',
|
||||
source_project_id: 'some-id',
|
||||
source_entity_path: '/path/',
|
||||
},
|
||||
created: new Date(2023, 1, 17, 3, 24),
|
||||
name: 'frog.jpg',
|
||||
type: 'file',
|
||||
selected: true,
|
||||
}
|
||||
|
||||
render(
|
||||
<FileViewRefreshError
|
||||
file={anotherProjectFile}
|
||||
refreshError="An error message"
|
||||
/>
|
||||
)
|
||||
|
||||
screen.getByText('Access Denied: An error message')
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Add table
Reference in a new issue