mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #12448 from overleaf/jpa-compile-and-download-pdf
[web] add button to project dashboard for compiling and downloading PDF GitOrigin-RevId: c243b4a30e4720116d82d9c25bdc8be8825d6d74
This commit is contained in:
parent
7a87bf4288
commit
4636f40f03
12 changed files with 396 additions and 6 deletions
|
@ -335,6 +335,19 @@ async function projectListPage(req, res, next) {
|
||||||
'failed to get "welcome-page-redesign" split test assignment'
|
'failed to get "welcome-page-redesign" split test assignment'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
// The assignment will be picked up via 'ol-splitTestVariants' in react.
|
||||||
|
await SplitTestHandler.promises.getAssignment(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
'download-pdf-dashboard'
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
{ err },
|
||||||
|
'failed to get "download-pdf-dashboard" split test assignment'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const hasPaidAffiliation = userAffiliations.some(
|
const hasPaidAffiliation = userAffiliations.some(
|
||||||
affiliation => affiliation.licence && affiliation.licence !== 'free'
|
affiliation => affiliation.licence && affiliation.licence !== 'free'
|
||||||
|
|
|
@ -284,6 +284,7 @@
|
||||||
"done": "",
|
"done": "",
|
||||||
"download": "",
|
"download": "",
|
||||||
"download_pdf": "",
|
"download_pdf": "",
|
||||||
|
"download_zip_file": "",
|
||||||
"drag_here": "",
|
"drag_here": "",
|
||||||
"drag_here_paste_an_image_or": "",
|
"drag_here_paste_an_image_or": "",
|
||||||
"dropbox_checking_sync_status": "",
|
"dropbox_checking_sync_status": "",
|
||||||
|
@ -834,6 +835,7 @@
|
||||||
"pdf_only_hide_editor": "",
|
"pdf_only_hide_editor": "",
|
||||||
"pdf_preview_error": "",
|
"pdf_preview_error": "",
|
||||||
"pdf_rendering_error": "",
|
"pdf_rendering_error": "",
|
||||||
|
"pdf_unavailable_for_download": "",
|
||||||
"pdf_viewer": "",
|
"pdf_viewer": "",
|
||||||
"pdf_viewer_error": "",
|
"pdf_viewer_error": "",
|
||||||
"pending_additional_licenses": "",
|
"pending_additional_licenses": "",
|
||||||
|
|
|
@ -11,6 +11,8 @@ import UntrashProjectButton from '../table/cells/action-buttons/untrash-project-
|
||||||
import LeaveProjectButton from '../table/cells/action-buttons/leave-project-button'
|
import LeaveProjectButton from '../table/cells/action-buttons/leave-project-button'
|
||||||
import DeleteProjectButton from '../table/cells/action-buttons/delete-project-button'
|
import DeleteProjectButton from '../table/cells/action-buttons/delete-project-button'
|
||||||
import { Project } from '../../../../../../types/project/dashboard/api'
|
import { Project } from '../../../../../../types/project/dashboard/api'
|
||||||
|
import CompileAndDownloadProjectPDFButton from '../table/cells/action-buttons/compile-and-download-project-pdf-button'
|
||||||
|
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||||
|
|
||||||
type ActionButtonProps = {
|
type ActionButtonProps = {
|
||||||
project: Project
|
project: Project
|
||||||
|
@ -30,6 +32,29 @@ function CopyProjectButtonMenuItem({ project, onClick }: ActionButtonProps) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CompileAndDownloadProjectPDFButtonMenuItem({
|
||||||
|
project,
|
||||||
|
onClick,
|
||||||
|
}: ActionButtonProps) {
|
||||||
|
return (
|
||||||
|
<CompileAndDownloadProjectPDFButton project={project}>
|
||||||
|
{(text, pendingCompile, downloadProject) => (
|
||||||
|
<MenuItemButton
|
||||||
|
onClick={() => downloadProject(onClick)}
|
||||||
|
className="projects-action-menu-item"
|
||||||
|
>
|
||||||
|
{pendingCompile ? (
|
||||||
|
<Icon type="spinner" spin className="menu-item-button-icon" />
|
||||||
|
) : (
|
||||||
|
<Icon type="file-pdf-o" className="menu-item-button-icon" />
|
||||||
|
)}{' '}
|
||||||
|
<span className="menu-item-button-text">{text}</span>
|
||||||
|
</MenuItemButton>
|
||||||
|
)}
|
||||||
|
</CompileAndDownloadProjectPDFButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function DownloadProjectButtonMenuItem({
|
function DownloadProjectButtonMenuItem({
|
||||||
project,
|
project,
|
||||||
onClick,
|
onClick,
|
||||||
|
@ -194,6 +219,12 @@ function ActionsDropdown({ project }: ActionDropdownProps) {
|
||||||
project={project}
|
project={project}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
/>
|
/>
|
||||||
|
{isSplitTestEnabled('download-pdf-dashboard') && (
|
||||||
|
<CompileAndDownloadProjectPDFButtonMenuItem
|
||||||
|
project={project}
|
||||||
|
onClick={handleClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ArchiveProjectButtonMenuItem project={project} onClick={handleClose} />
|
<ArchiveProjectButtonMenuItem project={project} onClick={handleClose} />
|
||||||
<TrashProjectButtonMenuItem project={project} onClick={handleClose} />
|
<TrashProjectButtonMenuItem project={project} onClick={handleClose} />
|
||||||
<UnarchiveProjectButtonMenuItem
|
<UnarchiveProjectButtonMenuItem
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { memo, useCallback, useState } from 'react'
|
||||||
|
import { Project } from '../../../../../../../../types/project/dashboard/api'
|
||||||
|
import Icon from '../../../../../../shared/components/icon'
|
||||||
|
import Tooltip from '../../../../../../shared/components/tooltip'
|
||||||
|
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
|
||||||
|
import { useLocation } from '../../../../../../shared/hooks/use-location'
|
||||||
|
import useAbortController from '../../../../../../shared/hooks/use-abort-controller'
|
||||||
|
import { postJSON } from '../../../../../../infrastructure/fetch-json'
|
||||||
|
import AccessibleModal from '../../../../../../shared/components/accessible-modal'
|
||||||
|
import { Button, Modal } from 'react-bootstrap'
|
||||||
|
import { isSmallDevice } from '../../../../../../infrastructure/event-tracking'
|
||||||
|
|
||||||
|
type CompileAndDownloadProjectPDFButtonProps = {
|
||||||
|
project: Project
|
||||||
|
children: (
|
||||||
|
text: string,
|
||||||
|
pendingDownload: boolean,
|
||||||
|
downloadProject: (fn: () => void) => void
|
||||||
|
) => React.ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
function CompileAndDownloadProjectPDFButton({
|
||||||
|
project,
|
||||||
|
children,
|
||||||
|
}: CompileAndDownloadProjectPDFButtonProps) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const { signal } = useAbortController()
|
||||||
|
const [pendingCompile, setPendingCompile] = useState(false)
|
||||||
|
|
||||||
|
const downloadProject = useCallback(
|
||||||
|
onDone => {
|
||||||
|
setPendingCompile(pendingCompile => {
|
||||||
|
if (pendingCompile) return true
|
||||||
|
eventTracking.sendMB('project-list-page-interaction', {
|
||||||
|
action: 'downloadPDF',
|
||||||
|
projectId: project.id,
|
||||||
|
isSmallDevice,
|
||||||
|
})
|
||||||
|
|
||||||
|
postJSON(`/project/${project.id}/compile`, {
|
||||||
|
body: {
|
||||||
|
check: 'silent',
|
||||||
|
draft: false,
|
||||||
|
incrementalCompilesEnabled: true,
|
||||||
|
},
|
||||||
|
signal,
|
||||||
|
})
|
||||||
|
.catch(() => ({ status: 'error' }))
|
||||||
|
.then(data => {
|
||||||
|
setPendingCompile(false)
|
||||||
|
if (data.status === 'success') {
|
||||||
|
const outputFile = data.outputFiles
|
||||||
|
.filter((file: { path: string }) => file.path === 'output.pdf')
|
||||||
|
.pop()
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
compileGroup: data.compileGroup,
|
||||||
|
popupDownload: 'true',
|
||||||
|
})
|
||||||
|
if (data.clsiServerId) {
|
||||||
|
params.set('clsiserverid', data.clsiServerId)
|
||||||
|
}
|
||||||
|
// Note: Triggering concurrent downloads does not work.
|
||||||
|
// Note: This is affecting the download of .zip files as well.
|
||||||
|
// When creating a dynamic `a` element with `download` attribute,
|
||||||
|
// another "actual" UI click is needed to trigger downloads.
|
||||||
|
// Forwarding the click `event` to the dynamic `a` element does
|
||||||
|
// not work either.
|
||||||
|
location.assign(
|
||||||
|
`/download/project/${project.id}/build/${outputFile.build}/output/output.pdf?${params}`
|
||||||
|
)
|
||||||
|
onDone()
|
||||||
|
} else {
|
||||||
|
setShowErrorModal(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[project, signal, location]
|
||||||
|
)
|
||||||
|
|
||||||
|
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{children(
|
||||||
|
pendingCompile ? t('compiling') + '…' : t('download_pdf'),
|
||||||
|
pendingCompile,
|
||||||
|
downloadProject
|
||||||
|
)}
|
||||||
|
{showErrorModal && (
|
||||||
|
<CompileErrorModal
|
||||||
|
project={project}
|
||||||
|
handleClose={() => {
|
||||||
|
setShowErrorModal(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CompileErrorModal({
|
||||||
|
project,
|
||||||
|
handleClose,
|
||||||
|
}: { project: Project } & { handleClose: () => void }) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AccessibleModal show onHide={handleClose}>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>
|
||||||
|
{project.name}: {t('pdf_unavailable_for_download')}
|
||||||
|
</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>{t('generic_linked_file_compile_error')}</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<a href={`/project/${project.id}`}>
|
||||||
|
<Button bsStyle="primary">{t('open_project')}</Button>
|
||||||
|
</a>
|
||||||
|
</Modal.Footer>
|
||||||
|
</AccessibleModal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompileAndDownloadProjectPDFButtonTooltip = memo(
|
||||||
|
function CompileAndDownloadProjectPDFButtonTooltip({
|
||||||
|
project,
|
||||||
|
}: Pick<CompileAndDownloadProjectPDFButtonProps, 'project'>) {
|
||||||
|
return (
|
||||||
|
<CompileAndDownloadProjectPDFButton project={project}>
|
||||||
|
{(text, pendingCompile, compileAndDownloadProject) => (
|
||||||
|
<Tooltip
|
||||||
|
key={`tooltip-compile-and-download-project-${project.id}`}
|
||||||
|
id={`compile-and-download-project-${project.id}`}
|
||||||
|
description={text}
|
||||||
|
overlayProps={{ placement: 'top', trigger: ['hover', 'focus'] }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="btn btn-link action-btn"
|
||||||
|
aria-label={text}
|
||||||
|
onClick={() => compileAndDownloadProject(() => {})}
|
||||||
|
>
|
||||||
|
{pendingCompile ? (
|
||||||
|
<Icon type="spinner" spin />
|
||||||
|
) : (
|
||||||
|
<Icon type="file-pdf-o" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</CompileAndDownloadProjectPDFButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default memo(CompileAndDownloadProjectPDFButton)
|
||||||
|
export { CompileAndDownloadProjectPDFButtonTooltip }
|
|
@ -17,7 +17,7 @@ function DownloadProjectButton({
|
||||||
children,
|
children,
|
||||||
}: DownloadProjectButtonProps) {
|
}: DownloadProjectButtonProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const text = t('download')
|
const text = t('download_zip_file')
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|
||||||
const downloadProject = useCallback(() => {
|
const downloadProject = useCallback(() => {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { UntrashProjectButtonTooltip } from './action-buttons/untrash-project-bu
|
||||||
import { DownloadProjectButtonTooltip } from './action-buttons/download-project-button'
|
import { DownloadProjectButtonTooltip } from './action-buttons/download-project-button'
|
||||||
import { LeaveProjectButtonTooltip } from './action-buttons/leave-project-button'
|
import { LeaveProjectButtonTooltip } from './action-buttons/leave-project-button'
|
||||||
import { DeleteProjectButtonTooltip } from './action-buttons/delete-project-button'
|
import { DeleteProjectButtonTooltip } from './action-buttons/delete-project-button'
|
||||||
|
import { CompileAndDownloadProjectPDFButtonTooltip } from './action-buttons/compile-and-download-project-pdf-button'
|
||||||
|
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||||
|
|
||||||
type ActionsCellProps = {
|
type ActionsCellProps = {
|
||||||
project: Project
|
project: Project
|
||||||
|
@ -17,6 +19,9 @@ export default function ActionsCell({ project }: ActionsCellProps) {
|
||||||
<>
|
<>
|
||||||
<CopyProjectButtonTooltip project={project} />
|
<CopyProjectButtonTooltip project={project} />
|
||||||
<DownloadProjectButtonTooltip project={project} />
|
<DownloadProjectButtonTooltip project={project} />
|
||||||
|
{isSplitTestEnabled('download-pdf-dashboard') && (
|
||||||
|
<CompileAndDownloadProjectPDFButtonTooltip project={project} />
|
||||||
|
)}
|
||||||
<ArchiveProjectButtonTooltip project={project} />
|
<ArchiveProjectButtonTooltip project={project} />
|
||||||
<TrashProjectButtonTooltip project={project} />
|
<TrashProjectButtonTooltip project={project} />
|
||||||
<UnarchiveProjectButtonTooltip project={project} />
|
<UnarchiveProjectButtonTooltip project={project} />
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import ProjectListTable from '../../js/features/project-list/components/table/project-list-table'
|
||||||
|
import { ProjectListProvider } from '../../js/features/project-list/context/project-list-context'
|
||||||
|
import useFetchMock from '../hooks/use-fetch-mock'
|
||||||
|
import { projectsData } from '../../../test/frontend/features/project-list/fixtures/projects-data'
|
||||||
|
|
||||||
|
export const Successful = (args: any) => {
|
||||||
|
window.user_id = '624333f147cfd8002622a1d3'
|
||||||
|
window.metaAttributesCache.set('ol-splitTestVariants', {
|
||||||
|
'download-pdf-dashboard': 'enabled',
|
||||||
|
})
|
||||||
|
useFetchMock(fetchMock => {
|
||||||
|
fetchMock.post(/\/api\/project/, {
|
||||||
|
projects: projectsData,
|
||||||
|
totalSize: projectsData.length,
|
||||||
|
})
|
||||||
|
fetchMock.post(
|
||||||
|
/\/compile/,
|
||||||
|
{
|
||||||
|
status: 'success',
|
||||||
|
compileGroup: 'standard',
|
||||||
|
clsiServerId: 'server-1',
|
||||||
|
outputFiles: [{ path: 'output.pdf', build: '123-321' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
delay: 1_000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectListProvider>
|
||||||
|
<ProjectListTable {...args} />
|
||||||
|
</ProjectListProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Failure = (args: any) => {
|
||||||
|
window.user_id = '624333f147cfd8002622a1d3'
|
||||||
|
window.metaAttributesCache.set('ol-splitTestVariants', {
|
||||||
|
'download-pdf-dashboard': 'enabled',
|
||||||
|
})
|
||||||
|
useFetchMock(fetchMock => {
|
||||||
|
fetchMock.post(/\/api\/project/, {
|
||||||
|
projects: projectsData,
|
||||||
|
totalSize: projectsData.length,
|
||||||
|
})
|
||||||
|
fetchMock.post(
|
||||||
|
/\/compile/,
|
||||||
|
{ status: 'failure', outputFiles: [] },
|
||||||
|
{ delay: 1_000 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectListProvider>
|
||||||
|
<ProjectListTable {...args} />
|
||||||
|
</ProjectListProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Project List / PDF download',
|
||||||
|
component: ProjectListTable,
|
||||||
|
decorators: [
|
||||||
|
(Story: any) => (
|
||||||
|
<div className="project-list-react">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
|
@ -702,6 +702,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.projects-dropdown-menu {
|
.projects-dropdown-menu {
|
||||||
|
&.dropdown-menu {
|
||||||
|
// Avoid line breaks for labels in menu items.
|
||||||
|
// There is enough space for these on mobile devices (checked DE and EN translations).
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
padding: 14px 20px;
|
padding: 14px 20px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
@ -806,6 +812,10 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
&.fa-spinner {
|
||||||
|
top: 30%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,7 +423,7 @@
|
||||||
"dont_have_account": "Don’t have an account?",
|
"dont_have_account": "Don’t have an account?",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"download_pdf": "Download PDF",
|
"download_pdf": "Download PDF",
|
||||||
"download_zip_file": "Download .zip File",
|
"download_zip_file": "Download .zip file",
|
||||||
"drag_here": "drag here",
|
"drag_here": "drag here",
|
||||||
"drag_here_paste_an_image_or": "Drag here, paste an image, or ",
|
"drag_here_paste_an_image_or": "Drag here, paste an image, or ",
|
||||||
"drop_files_here_to_upload": "Drop files here to upload",
|
"drop_files_here_to_upload": "Drop files here to upload",
|
||||||
|
@ -1275,6 +1275,7 @@
|
||||||
"pdf_only_hide_editor": "PDF only <0>(hide editor)</0>",
|
"pdf_only_hide_editor": "PDF only <0>(hide editor)</0>",
|
||||||
"pdf_preview_error": "There was a problem displaying the compilation results for this project.",
|
"pdf_preview_error": "There was a problem displaying the compilation results for this project.",
|
||||||
"pdf_rendering_error": "PDF Rendering Error",
|
"pdf_rendering_error": "PDF Rendering Error",
|
||||||
|
"pdf_unavailable_for_download": "PDF unavailable for download",
|
||||||
"pdf_viewer": "PDF Viewer",
|
"pdf_viewer": "PDF Viewer",
|
||||||
"pdf_viewer_error": "There was a problem displaying the PDF for this project.",
|
"pdf_viewer_error": "There was a problem displaying the PDF for this project.",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import { projectsData } from '../../../../fixtures/projects-data'
|
||||||
|
import * as useLocationModule from '../../../../../../../../frontend/js/shared/hooks/use-location'
|
||||||
|
import { CompileAndDownloadProjectPDFButtonTooltip } from '../../../../../../../../frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import * as eventTracking from '../../../../../../../../frontend/js/infrastructure/event-tracking'
|
||||||
|
|
||||||
|
describe('<CompileAndDownloadProjectPDFButton />', function () {
|
||||||
|
let assignStub: sinon.SinonStub
|
||||||
|
let locationStub: sinon.SinonStub
|
||||||
|
let sendMBSpy: sinon.SinonSpy
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sendMBSpy = sinon.spy(eventTracking, 'sendMB')
|
||||||
|
assignStub = sinon.stub()
|
||||||
|
locationStub = sinon.stub(useLocationModule, 'useLocation').returns({
|
||||||
|
assign: assignStub,
|
||||||
|
reload: sinon.stub(),
|
||||||
|
})
|
||||||
|
render(
|
||||||
|
<CompileAndDownloadProjectPDFButtonTooltip project={projectsData[0]} />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
locationStub.restore()
|
||||||
|
fetchMock.reset()
|
||||||
|
sendMBSpy.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders tooltip for button', function () {
|
||||||
|
const btn = screen.getByLabelText('Download PDF')
|
||||||
|
fireEvent.mouseOver(btn)
|
||||||
|
screen.getByRole('tooltip', { name: 'Download PDF' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('downloads the project PDF when clicked', async function () {
|
||||||
|
fetchMock.post(
|
||||||
|
`/project/${projectsData[0].id}/compile`,
|
||||||
|
{
|
||||||
|
status: 'success',
|
||||||
|
compileGroup: 'standard',
|
||||||
|
clsiServerId: 'server-1',
|
||||||
|
outputFiles: [{ path: 'output.pdf', build: '123-321' }],
|
||||||
|
},
|
||||||
|
{ delay: 10 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const btn = screen.getByLabelText('Download PDF') as HTMLButtonElement
|
||||||
|
fireEvent.click(btn)
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
screen.getByLabelText('Compiling…')
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(assignStub).to.have.been.called
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(assignStub).to.have.been.calledOnce
|
||||||
|
expect(assignStub).to.have.been.calledWith(
|
||||||
|
`/download/project/${projectsData[0].id}/build/123-321/output/output.pdf?compileGroup=standard&popupDownload=true&clsiserverid=server-1`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(sendMBSpy).to.have.been.calledOnce
|
||||||
|
expect(sendMBSpy).to.have.been.calledWith('project-list-page-interaction', {
|
||||||
|
action: 'downloadPDF',
|
||||||
|
page: '/',
|
||||||
|
projectId: projectsData[0].id,
|
||||||
|
isSmallDevice: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays a modal when the compile failed', async function () {
|
||||||
|
fetchMock.post(`/project/${projectsData[0].id}/compile`, {
|
||||||
|
status: 'failure',
|
||||||
|
})
|
||||||
|
|
||||||
|
const btn = screen.getByLabelText('Download PDF') as HTMLButtonElement
|
||||||
|
fireEvent.click(btn)
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
screen.getByText(`${projectsData[0].name}: PDF unavailable for download`)
|
||||||
|
})
|
||||||
|
expect(assignStub).to.have.not.been.called
|
||||||
|
})
|
||||||
|
})
|
|
@ -22,13 +22,13 @@ describe('<DownloadProjectButton />', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders tooltip for button', function () {
|
it('renders tooltip for button', function () {
|
||||||
const btn = screen.getByLabelText('Download')
|
const btn = screen.getByLabelText('Download .zip file')
|
||||||
fireEvent.mouseOver(btn)
|
fireEvent.mouseOver(btn)
|
||||||
screen.getByRole('tooltip', { name: 'Download' })
|
screen.getByRole('tooltip', { name: 'Download .zip file' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('downloads the project when clicked', async function () {
|
it('downloads the project when clicked', async function () {
|
||||||
const btn = screen.getByLabelText('Download') as HTMLButtonElement
|
const btn = screen.getByLabelText('Download .zip file') as HTMLButtonElement
|
||||||
fireEvent.click(btn)
|
fireEvent.click(btn)
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|
|
@ -11,6 +11,9 @@ describe('<ProjectListTable />', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
window.metaAttributesCache = new Map()
|
window.metaAttributesCache = new Map()
|
||||||
window.metaAttributesCache.set('ol-tags', [])
|
window.metaAttributesCache.set('ol-tags', [])
|
||||||
|
window.metaAttributesCache.set('ol-splitTestVariants', {
|
||||||
|
'download-pdf-dashboard': 'enabled',
|
||||||
|
})
|
||||||
window.user_id = userId
|
window.user_id = userId
|
||||||
fetchMock.reset()
|
fetchMock.reset()
|
||||||
})
|
})
|
||||||
|
@ -112,8 +115,10 @@ describe('<ProjectListTable />', function () {
|
||||||
// temporary count tests until we add filtering for archived/trashed
|
// temporary count tests until we add filtering for archived/trashed
|
||||||
const copyButtons = screen.getAllByLabelText('Copy')
|
const copyButtons = screen.getAllByLabelText('Copy')
|
||||||
expect(copyButtons.length).to.equal(currentProjects.length)
|
expect(copyButtons.length).to.equal(currentProjects.length)
|
||||||
const downloadButtons = screen.getAllByLabelText('Download')
|
const downloadButtons = screen.getAllByLabelText('Download .zip file')
|
||||||
expect(downloadButtons.length).to.equal(currentProjects.length)
|
expect(downloadButtons.length).to.equal(currentProjects.length)
|
||||||
|
const downloadPDFButtons = screen.getAllByLabelText('Download PDF')
|
||||||
|
expect(downloadPDFButtons.length).to.equal(currentProjects.length)
|
||||||
const archiveButtons = screen.getAllByLabelText('Archive')
|
const archiveButtons = screen.getAllByLabelText('Archive')
|
||||||
expect(archiveButtons.length).to.equal(currentProjects.length)
|
expect(archiveButtons.length).to.equal(currentProjects.length)
|
||||||
const trashButtons = screen.getAllByLabelText('Trash')
|
const trashButtons = screen.getAllByLabelText('Trash')
|
||||||
|
|
Loading…
Reference in a new issue