Improve compile request mocking in Cypress tests (#12095)

GitOrigin-RevId: fdbc53148e5437e451dab4889232923c823d649e
This commit is contained in:
Alf Eaton 2023-03-06 12:50:17 +00:00 committed by Copybot
parent cff49bd9c1
commit 488c6ff919
9 changed files with 378 additions and 248 deletions

View file

@ -43,8 +43,10 @@ const outputFiles = () => {
] ]
} }
export const interceptCompile = (prefix = 'compile') => { export const interceptCompile = (prefix = 'compile', times = 1) => {
cy.intercept('POST', '/project/*/compile*', { cy.intercept(
{ method: 'POST', url: '/project/*/compile*', times },
{
body: { body: {
status: 'success', status: 'success',
clsiServerId: 'foo', clsiServerId: 'foo',
@ -52,17 +54,100 @@ export const interceptCompile = (prefix = 'compile') => {
pdfDownloadDomain: 'https://clsi.test-overleaf.com', pdfDownloadDomain: 'https://clsi.test-overleaf.com',
outputFiles: outputFiles(), outputFiles: outputFiles(),
}, },
}).as(`${prefix}`) }
).as(`${prefix}`)
cy.intercept('/build/*/output.pdf*', {
fixture: 'build/output.pdf,null', cy.intercept(
}).as(`${prefix}-pdf`) { url: '/build/*/output.pdf*', times },
{ fixture: 'build/output.pdf,null' }
cy.intercept('/build/*/output.log*', { ).as(`${prefix}-pdf`)
fixture: 'build/output.log',
}).as(`${prefix}-log`) cy.intercept(
{ url: '/build/*/output.log*', times },
cy.intercept('/build/*/output.blg*', { { fixture: 'build/output.log' }
fixture: 'build/output.blg', ).as(`${prefix}-log`)
}).as(`${prefix}-blg`)
cy.intercept(
{ url: '/build/*/output.blg*', times },
{ fixture: 'build/output.blg' }
).as(`${prefix}-blg`)
}
export const waitForCompile = ({ prefix = 'compile', pdf = false } = {}) => {
cy.wait(`@${prefix}`)
cy.wait(`@${prefix}-log`)
cy.wait(`@${prefix}-blg`)
if (pdf) {
cy.wait(`@${prefix}-pdf`)
}
return cy.wrap(null)
}
export const interceptDeferredCompile = (beforeResponse?: () => void) => {
let resolveDeferredCompile: (value?: unknown) => void
const promise = new Promise(resolve => {
resolveDeferredCompile = resolve
})
cy.intercept(
{ method: 'POST', url: '/project/*/compile*', times: 1 },
req => {
if (beforeResponse) {
beforeResponse()
}
// only reply once the Promise is resolved
promise.then(() => {
req.reply({
body: {
status: 'success',
clsiServerId: 'foo',
compileGroup: 'priority',
pdfDownloadDomain: 'https://clsi.test-overleaf.com',
outputFiles: [
{
path: 'output.pdf',
build: '123',
url: '/build/123/output.pdf',
type: 'pdf',
},
{
path: 'output.log',
build: '123',
url: '/build/123/output.log',
type: 'log',
},
{
path: 'output.blg',
build: '123',
url: '/build/123/output.blg',
type: 'log',
},
],
},
})
})
return promise
}
).as('compile')
cy.intercept(
{ url: '/build/*/output.pdf*', times: 1 },
{ fixture: 'build/output.pdf,null' }
).as(`compile-pdf`)
cy.intercept(
{ url: '/build/*/output.log*', times: 1 },
{ fixture: 'build/output.log' }
).as(`compile-log`)
cy.intercept(
{ url: '/build/*/output.blg*', times: 1 },
{ fixture: 'build/output.blg' }
).as(`compile-blg`)
// @ts-ignore
return cy.wrap(resolveDeferredCompile)
} }

View file

@ -1,5 +1,9 @@
import '@testing-library/cypress/add-commands' import '@testing-library/cypress/add-commands'
import { interceptCompile } from './compile' import {
interceptCompile,
waitForCompile,
interceptDeferredCompile,
} from './compile'
import { interceptEvents } from './events' import { interceptEvents } from './events'
import { interceptSpelling } from './spelling' import { interceptSpelling } from './spelling'
@ -12,6 +16,8 @@ declare global {
interceptCompile: typeof interceptCompile interceptCompile: typeof interceptCompile
interceptEvents: typeof interceptEvents interceptEvents: typeof interceptEvents
interceptSpelling: typeof interceptSpelling interceptSpelling: typeof interceptSpelling
waitForCompile: typeof waitForCompile
interceptDeferredCompile: typeof interceptDeferredCompile
index: () => Chainable<number> index: () => Chainable<number>
} }
} }
@ -20,6 +26,8 @@ declare global {
Cypress.Commands.add('interceptCompile', interceptCompile) Cypress.Commands.add('interceptCompile', interceptCompile)
Cypress.Commands.add('interceptEvents', interceptEvents) Cypress.Commands.add('interceptEvents', interceptEvents)
Cypress.Commands.add('interceptSpelling', interceptSpelling) Cypress.Commands.add('interceptSpelling', interceptSpelling)
Cypress.Commands.add('waitForCompile', waitForCompile)
Cypress.Commands.add('interceptDeferredCompile', interceptDeferredCompile)
Cypress.Commands.add('index', { prevSubject: true }, subject => { Cypress.Commands.add('index', { prevSubject: true }, subject => {
return cy.wrap(subject).invoke('index') return cy.wrap(subject).invoke('index')
}) })

View file

@ -5,7 +5,7 @@ import { testDetachChannel } from '../../helpers/detach-channel'
describe('<DetachCompileButtonWrapper />', function () { describe('<DetachCompileButtonWrapper />', function () {
beforeEach(function () { beforeEach(function () {
cy.interceptCompile() window.metaAttributesCache = new Map()
cy.interceptEvents() cy.interceptEvents()
}) })
@ -14,6 +14,8 @@ describe('<DetachCompileButtonWrapper />', function () {
}) })
it('detacher mode and not linked: does not show button ', function () { it('detacher mode and not linked: does not show button ', function () {
cy.interceptCompile()
cy.window().then(win => { cy.window().then(win => {
win.metaAttributesCache = new Map([['ol-detachRole', 'detacher']]) win.metaAttributesCache = new Map([['ol-detachRole', 'detacher']])
}) })
@ -26,10 +28,14 @@ describe('<DetachCompileButtonWrapper />', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByRole('button', { name: 'Recompile' }).should('not.exist') cy.findByRole('button', { name: 'Recompile' }).should('not.exist')
}) })
it('detacher mode and linked: show button', function () { it('detacher mode and linked: show button', function () {
cy.interceptCompile()
cy.window().then(win => { cy.window().then(win => {
win.metaAttributesCache = new Map([['ol-detachRole', 'detacher']]) win.metaAttributesCache = new Map([['ol-detachRole', 'detacher']])
}) })
@ -42,6 +48,8 @@ describe('<DetachCompileButtonWrapper />', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.wrap(null).then(() => { cy.wrap(null).then(() => {
testDetachChannel.postMessage({ testDetachChannel.postMessage({
role: 'detached', role: 'detached',
@ -53,6 +61,8 @@ describe('<DetachCompileButtonWrapper />', function () {
}) })
it('not detacher mode and linked: does not show button ', function () { it('not detacher mode and linked: does not show button ', function () {
cy.interceptCompile()
cy.window().then(win => { cy.window().then(win => {
win.metaAttributesCache = new Map([['ol-detachRole', 'detached']]) win.metaAttributesCache = new Map([['ol-detachRole', 'detached']])
}) })
@ -65,6 +75,8 @@ describe('<DetachCompileButtonWrapper />', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.wrap(null).then(() => { cy.wrap(null).then(() => {
testDetachChannel.postMessage({ testDetachChannel.postMessage({
role: 'detacher', role: 'detacher',

View file

@ -6,11 +6,12 @@ import { unmountComponentAtNode } from 'react-dom'
describe('<PdfJSViewer/>', function () { describe('<PdfJSViewer/>', function () {
beforeEach(function () { beforeEach(function () {
cy.interceptCompile()
cy.interceptEvents() cy.interceptEvents()
}) })
it('loads all PDF pages', function () { it('loads all PDF pages', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -21,6 +22,8 @@ describe('<PdfJSViewer/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByLabelText('Page 1') cy.findByLabelText('Page 1')
cy.findByLabelText('Page 2') cy.findByLabelText('Page 2')
cy.findByLabelText('Page 3') cy.findByLabelText('Page 3')
@ -30,6 +33,8 @@ describe('<PdfJSViewer/>', function () {
}) })
it('renders pages in a "loading" state', function () { it('renders pages in a "loading" state', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -40,10 +45,14 @@ describe('<PdfJSViewer/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByLabelText('Loading…') cy.findByLabelText('Loading…')
}) })
it('can be unmounted while loading a document', function () { it('can be unmounted while loading a document', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -54,10 +63,14 @@ describe('<PdfJSViewer/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.then(() => unmountComponentAtNode(getContainerEl())) cy.then(() => unmountComponentAtNode(getContainerEl()))
}) })
it('can be unmounted after loading a document', function () { it('can be unmounted after loading a document', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -68,6 +81,8 @@ describe('<PdfJSViewer/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByLabelText('Page 1') cy.findByLabelText('Page 1')
cy.then(() => unmountComponentAtNode(getContainerEl())) cy.then(() => unmountComponentAtNode(getContainerEl()))

View file

@ -11,9 +11,9 @@ describe('<PdfPreviewDetachedRoot/>', function () {
['ol-project_id', 'project1'], ['ol-project_id', 'project1'],
['ol-detachRole', 'detached'], ['ol-detachRole', 'detached'],
['ol-projectName', 'Project Name'], ['ol-projectName', 'Project Name'],
['ol-preventCompileOnLoad', true],
]) ])
cy.interceptCompile()
cy.interceptEvents() cy.interceptEvents()
}) })
@ -22,6 +22,8 @@ describe('<PdfPreviewDetachedRoot/>', function () {
}) })
it('syncs compiling state', function () { it('syncs compiling state', function () {
cy.interceptCompile()
cy.mount(<PdfPreviewDetachedRoot />) cy.mount(<PdfPreviewDetachedRoot />)
cy.wrap(null).then(() => { cy.wrap(null).then(() => {
@ -51,6 +53,8 @@ describe('<PdfPreviewDetachedRoot/>', function () {
}) })
it('sends a clear cache request when the button is pressed', function () { it('sends a clear cache request when the button is pressed', function () {
cy.interceptCompile()
cy.mount(<PdfPreviewDetachedRoot />) cy.mount(<PdfPreviewDetachedRoot />)
cy.wrap(null).then(() => { cy.wrap(null).then(() => {

View file

@ -4,7 +4,8 @@ import { testDetachChannel } from '../../helpers/detach-channel'
describe('<PdfPreviewHybridToolbar/>', function () { describe('<PdfPreviewHybridToolbar/>', function () {
beforeEach(function () { beforeEach(function () {
cy.interceptCompile() window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
cy.interceptEvents() cy.interceptEvents()
}) })

View file

@ -22,11 +22,18 @@ const Layout: FC<{ layout: string; view?: string }> = ({ layout, view }) => {
describe('<PdfPreview/>', function () { describe('<PdfPreview/>', function () {
beforeEach(function () { beforeEach(function () {
cy.interceptCompile() window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
cy.interceptEvents() cy.interceptEvents()
}) })
afterEach(function () {
window.metaAttributesCache = new Map()
})
it('renders the PDF preview', function () { it('renders the PDF preview', function () {
window.metaAttributesCache.set('ol-preventCompileOnLoad', false)
cy.interceptCompile('compile')
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -38,13 +45,14 @@ describe('<PdfPreview/>', function () {
) )
// wait for "compile on load" to finish // wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.wait('@compile')
cy.findByRole('button', { name: 'Recompile' }) cy.findByRole('button', { name: 'Recompile' })
cy.wait('@compile-pdf')
}) })
it('runs a compile when the Recompile button is pressed', function () { it('runs a compile when the Recompile button is pressed', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -55,28 +63,18 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.wait('@compile')
cy.wait('@compile-pdf')
cy.interceptCompile('recompile')
// press the Recompile button => compile // press the Recompile button => compile
cy.findByRole('button', { name: 'Recompile' }).click() cy.findByRole('button', { name: 'Recompile' }).click()
// wait for "recompile" to finish // wait for compile to finish
// cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.wait('@recompile-pdf')
cy.wait('@recompile-log')
cy.wait('@recompile-blg')
cy.findByRole('button', { name: 'Recompile' })
cy.contains('Your Paper') cy.contains('Your Paper')
}) })
it('runs a compile on `pdf:recompile` event', function () { it('runs a compile on `pdf:recompile` event', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -87,70 +85,20 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.wait('@compile')
cy.wait('@compile-pdf')
cy.interceptCompile('recompile')
cy.window().then(win => { cy.window().then(win => {
win.dispatchEvent(new CustomEvent('pdf:recompile')) win.dispatchEvent(new CustomEvent('pdf:recompile'))
}) })
// wait for "recompile" to finish // wait for compile to finish
// cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.wait('@recompile')
cy.findByRole('button', { name: 'Recompile' })
cy.wait('@recompile-pdf')
cy.contains('Your Paper') cy.contains('Your Paper')
}) })
it('does not compile while compiling', function () { it('does not compile while compiling', function () {
let compileResolve: (value?: unknown) => void
let counter = 0 let counter = 0
cy.interceptDeferredCompile(() => counter++).then(
const promise = new Promise(resolve => { resolveDeferredCompile => {
compileResolve = resolve
})
cy.intercept(
'POST',
'/project/project123/compile?auto_compile=true',
req => {
counter++
promise.then(() => {
req.reply({
body: {
status: 'success',
clsiServerId: 'foo',
compileGroup: 'priority',
pdfDownloadDomain: 'https://clsi.test-overleaf.com',
outputFiles: [
{
path: 'output.pdf',
build: '123',
url: '/build/123/output.pdf',
type: 'pdf',
},
{
path: 'output.log',
build: '123',
url: '/build/123/output.log',
type: 'log',
},
],
},
})
})
return promise
}
).as('compile')
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -159,24 +107,34 @@ describe('<PdfPreview/>', function () {
<PdfPreview /> <PdfPreview />
</div> </div>
</EditorProviders> </EditorProviders>
).then(() => { )
// start compiling
cy.findByRole('button', { name: 'Recompile' })
.click()
.then(() => {
cy.findByRole('button', { name: 'Compiling…' }) cy.findByRole('button', { name: 'Compiling…' })
// trigger a recompile
cy.window().then(win => { cy.window().then(win => {
win.dispatchEvent(new CustomEvent('pdf:recompile')) win.dispatchEvent(new CustomEvent('pdf:recompile'))
}) })
compileResolve() // finish the original compile
resolveDeferredCompile()
cy.findByRole('button', { name: 'Recompile' }) // wait for the original compile to finish
cy.waitForCompile({ pdf: true })
cy.contains('Your Paper').should(() => { // NOTE: difficult to assert that a second request won't be sent, at some point
expect(counter).to.equal(1) expect(counter).to.equal(1)
}) })
}) }
)
}) })
it('disables compile button while compile is running', function () { it('disables compile button while compile is running', function () {
cy.interceptDeferredCompile().then(resolveDeferredCompile => {
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -187,11 +145,19 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.findByRole('button', { name: 'Compiling…' }).should('be.disabled') cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' })
.should('be.disabled')
.then(resolveDeferredCompile)
cy.waitForCompile()
cy.findByRole('button', { name: 'Recompile' }).should('not.be.disabled') cy.findByRole('button', { name: 'Recompile' }).should('not.be.disabled')
}) })
})
it('runs a compile on doc change if autocompile is enabled', function () { it('runs a compile on doc change if autocompile is enabled', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -202,11 +168,6 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.wait('@compile')
cy.findByRole('button', { name: 'Recompile' })
cy.window().then(win => { cy.window().then(win => {
cy.clock() cy.clock()
@ -216,16 +177,21 @@ describe('<PdfPreview/>', function () {
// fire a doc:changed event => compile // fire a doc:changed event => compile
win.dispatchEvent(new CustomEvent('doc:changed')) win.dispatchEvent(new CustomEvent('doc:changed'))
// wait enough time for the compile to start
cy.tick(6000) // > AUTO_COMPILE_DEBOUNCE cy.tick(6000) // > AUTO_COMPILE_DEBOUNCE
cy.clock().invoke('restore') cy.clock().invoke('restore')
}) })
cy.findByRole('button', { name: 'Compiling…' }) // wait for compile to finish
cy.waitForCompile({ pdf: true })
cy.findByRole('button', { name: 'Recompile' }) cy.findByRole('button', { name: 'Recompile' })
}) })
it('does not run a compile on doc change if autocompile is disabled', function () { it('does not run a compile on doc change if autocompile is disabled', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -236,10 +202,6 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.findByRole('button', { name: 'Recompile' })
cy.window().then(win => { cy.window().then(win => {
cy.clock() cy.clock()
@ -249,15 +211,19 @@ describe('<PdfPreview/>', function () {
// fire a doc:changed event => no compile // fire a doc:changed event => no compile
win.dispatchEvent(new CustomEvent('doc:changed')) win.dispatchEvent(new CustomEvent('doc:changed'))
cy.tick(5000) // AUTO_COMPILE_DEBOUNCE // wait enough time for the compile to start
cy.tick(6000) // AUTO_COMPILE_DEBOUNCE
cy.clock().invoke('restore') cy.clock().invoke('restore')
}) })
// NOTE: difficult to assert that a request hasn't been sent
cy.findByRole('button', { name: 'Recompile' }) cy.findByRole('button', { name: 'Recompile' })
}) })
it('does not run a compile on doc change if autocompile is blocked by syntax check', function () { it('does not run a compile on doc change if autocompile is blocked by syntax check', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
// enable linting in the editor // enable linting in the editor
scope.settings.syntaxValidation = true scope.settings.syntaxValidation = true
@ -272,10 +238,6 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.findByRole('button', { name: 'Recompile' })
cy.window().then(win => { cy.window().then(win => {
cy.clock() cy.clock()
@ -288,16 +250,21 @@ describe('<PdfPreview/>', function () {
// fire a doc:changed event => no compile // fire a doc:changed event => no compile
win.dispatchEvent(new CustomEvent('doc:changed')) win.dispatchEvent(new CustomEvent('doc:changed'))
cy.tick(5000) // AUTO_COMPILE_DEBOUNCE // wait enough time for the compile to start
cy.tick(6000) // AUTO_COMPILE_DEBOUNCE
cy.clock().invoke('restore') cy.clock().invoke('restore')
}) })
// NOTE: difficult to assert that a request hasn't been sent
cy.findByRole('button', { name: 'Recompile' }) cy.findByRole('button', { name: 'Recompile' })
cy.findByText('Code check failed') cy.findByText('Code check failed')
}) })
it('does not run a compile on doc change if the PDF preview is not open', function () { it('does not run a compile on doc change if the PDF preview is not open', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -309,11 +276,6 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish
cy.findByRole('button', { name: 'Compiling…' })
cy.wait('@compile')
cy.findByRole('button', { name: 'Recompile' })
cy.window().then(win => { cy.window().then(win => {
cy.clock() cy.clock()
@ -323,15 +285,17 @@ describe('<PdfPreview/>', function () {
// fire a doc:changed event => compile // fire a doc:changed event => compile
win.dispatchEvent(new CustomEvent('doc:changed')) win.dispatchEvent(new CustomEvent('doc:changed'))
// wait enough time for the compile to start
cy.tick(6000) // > AUTO_COMPILE_DEBOUNCE cy.tick(6000) // > AUTO_COMPILE_DEBOUNCE
cy.clock().invoke('restore') cy.clock().invoke('restore')
}) })
// NOTE: difficult to assert that a request hasn't been sent
cy.findByRole('button', { name: 'Recompile' }) cy.findByRole('button', { name: 'Recompile' })
}) })
describe('displays error messages', function () { describe('error messages', function () {
const compileErrorStatuses = { const compileErrorStatuses = {
'clear-cache': 'clear-cache':
'Sorry, something went wrong and your project could not be compiled. Please try again in a few moments.', 'Sorry, something went wrong and your project could not be compiled. Please try again in a few moments.',
@ -355,7 +319,7 @@ describe('<PdfPreview/>', function () {
for (const [status, message] of Object.entries(compileErrorStatuses)) { for (const [status, message] of Object.entries(compileErrorStatuses)) {
it(`displays error message for '${status}' status`, function () { it(`displays error message for '${status}' status`, function () {
cy.intercept('POST', '/project/*/compile?*', { cy.intercept('POST', '/project/*/compile*', {
body: { body: {
status, status,
clsiServerId: 'foo', clsiServerId: 'foo',
@ -373,15 +337,16 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' }) cy.wait('@compile')
cy.findByRole('button', { name: 'Recompile' })
cy.findByText(message) cy.findByText(message)
}) })
} }
})
it('displays expandable raw logs', function () { it('displays expandable raw logs', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -392,9 +357,8 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.findByRole('button', { name: 'Recompile' })
cy.findByRole('button', { name: 'View logs' }).click() cy.findByRole('button', { name: 'View logs' }).click()
cy.findByRole('button', { name: 'View PDF' }) cy.findByRole('button', { name: 'View PDF' })
@ -422,7 +386,7 @@ describe('<PdfPreview/>', function () {
], ],
} }
cy.intercept('POST', '/project/*/compile?*', { cy.intercept('POST', '/project/*/compile*', {
body: { body: {
status: 'validation-problems', status: 'validation-problems',
validationProblems, validationProblems,
@ -441,10 +405,7 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' })
cy.findByRole('button', { name: 'Recompile' })
cy.wait('@compile') cy.wait('@compile')
cy.findByText('Project too large') cy.findByText('Project too large')
@ -452,7 +413,10 @@ describe('<PdfPreview/>', function () {
cy.findByText('Conflicting Paths Found') cy.findByText('Conflicting Paths Found')
}) })
describe('clear cache', function () {
it('sends a clear cache request when the button is pressed', function () { it('sends a clear cache request when the button is pressed', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -463,16 +427,15 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.findByRole('button', { name: 'Recompile' })
cy.findByRole('button', { name: 'View logs' }).click() cy.findByRole('button', { name: 'View logs' }).click()
cy.findByRole('button', { name: 'Clear cached files' }).should( cy.findByRole('button', { name: 'Clear cached files' }).should(
'not.be.disabled' 'not.be.disabled'
) )
cy.intercept('DELETE', 'project/*/output?*', { cy.intercept('DELETE', '/project/*/output*', {
statusCode: 204, statusCode: 204,
delay: 100, delay: 100,
}).as('clear-cache') }).as('clear-cache')
@ -489,6 +452,8 @@ describe('<PdfPreview/>', function () {
}) })
it('handle "recompile from scratch"', function () { it('handle "recompile from scratch"', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -499,10 +464,13 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
// wait for "compile on load" to finish cy.findByRole('button', { name: 'Recompile' }).click()
cy.findByRole('button', { name: 'Compiling…' }) cy.waitForCompile({ pdf: true })
cy.wait('@compile') cy.interceptCompile('recompile')
cy.findByRole('button', { name: 'Recompile' }) cy.intercept('DELETE', '/project/*/output*', {
statusCode: 204,
delay: 100,
}).as('clear-cache')
// show the logs UI // show the logs UI
cy.findByRole('button', { name: 'View logs' }).click() cy.findByRole('button', { name: 'View logs' }).click()
@ -511,13 +479,6 @@ describe('<PdfPreview/>', function () {
'not.be.disabled' 'not.be.disabled'
) )
cy.interceptCompile()
cy.intercept('DELETE', 'project/*/output?*', {
statusCode: 204,
delay: 100,
}).as('clear-cache')
// TODO: open the menu? // TODO: open the menu?
cy.findByRole('menuitem', { cy.findByRole('menuitem', {
name: 'Recompile from scratch', name: 'Recompile from scratch',
@ -530,14 +491,19 @@ describe('<PdfPreview/>', function () {
cy.findByRole('button', { name: 'Compiling…' }) cy.findByRole('button', { name: 'Compiling…' })
cy.wait('@clear-cache') cy.wait('@clear-cache')
cy.findByRole('button', { name: 'Recompile' })
cy.wait('@compile') // wait for recompile from scratch to finish
cy.wait('@compile-pdf') cy.waitForCompile({ pdf: true, prefix: 'recompile' })
cy.findByRole('button', { name: 'Recompile' })
})
}) })
describe('invalid URLs and broken PDFs', function () {
it('shows an error for an invalid URL', function () { it('shows an error for an invalid URL', function () {
cy.intercept('/build/*/output.pdf?*', { cy.interceptCompile()
cy.intercept('/build/*/output.pdf*', {
statusCode: 500, statusCode: 500,
body: { body: {
message: 'something awful happened', message: 'something awful happened',
@ -555,6 +521,8 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.findByRole('button', { name: 'Recompile' }).click()
cy.waitForCompile()
cy.wait('@compile-pdf-error') cy.wait('@compile-pdf-error')
cy.contains('Something went wrong while rendering this PDF.') cy.contains('Something went wrong while rendering this PDF.')
@ -565,7 +533,9 @@ describe('<PdfPreview/>', function () {
}) })
it('shows an error for a corrupt PDF', function () { it('shows an error for a corrupt PDF', function () {
cy.intercept('/build/*/output.pdf?*', { cy.interceptCompile()
cy.intercept('/build/*/output.pdf*', {
fixture: 'build/output-corrupt.pdf,null', fixture: 'build/output-corrupt.pdf,null',
}).as('compile-pdf-corrupt') }).as('compile-pdf-corrupt')
@ -579,6 +549,8 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.findByRole('button', { name: 'Recompile' }).click()
cy.waitForCompile()
cy.wait('@compile-pdf-corrupt') cy.wait('@compile-pdf-corrupt')
cy.contains('Something went wrong while rendering this PDF.') cy.contains('Something went wrong while rendering this PDF.')
@ -591,9 +563,11 @@ describe('<PdfPreview/>', function () {
describe('human readable logs', function () { describe('human readable logs', function () {
it('shows human readable hint for undefined reference errors', function () { it('shows human readable hint for undefined reference errors', function () {
cy.intercept('/build/*/output.log?*', { cy.interceptCompile()
cy.intercept('/build/*/output.log*', {
fixture: 'build/output-human-readable.log', fixture: 'build/output-human-readable.log',
}).as('log') }).as('compile-log')
const scope = mockScope() const scope = mockScope()
@ -605,7 +579,8 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.wait('@log') cy.findByRole('button', { name: 'Recompile' }).click()
cy.waitForCompile()
cy.findByRole('button', { name: 'View logs' }).click() cy.findByRole('button', { name: 'View logs' }).click()
cy.findByText( cy.findByText(
@ -622,9 +597,10 @@ describe('<PdfPreview/>', function () {
}) })
it('does not show human readable hint when no undefined reference errors', function () { it('does not show human readable hint when no undefined reference errors', function () {
cy.interceptCompile()
cy.intercept('/build/*/output.log?*', { cy.intercept('/build/*/output.log?*', {
fixture: 'build/output-undefined-references.log', fixture: 'build/output-undefined-references.log',
}).as('log') }).as('compile-log')
const scope = mockScope() const scope = mockScope()
@ -636,7 +612,8 @@ describe('<PdfPreview/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.wait('@log') cy.findByRole('button', { name: 'Recompile' }).click()
cy.waitForCompile()
cy.findByRole('button', { name: 'View logs' }).click() cy.findByRole('button', { name: 'View logs' }).click()
cy.findByText( cy.findByText(

View file

@ -77,7 +77,7 @@ const WithSelectedEntities = ({
} }
const interceptSyncCodeAsync = () => { const interceptSyncCodeAsync = () => {
const output: { resolve: () => void } = { const deferred: { resolve: () => void } = {
resolve: () => { resolve: () => {
// do nothing // do nothing
}, },
@ -85,7 +85,7 @@ const interceptSyncCodeAsync = () => {
cy.intercept('/project/*/sync/code?*', req => { cy.intercept('/project/*/sync/code?*', req => {
return new Promise(resolve => { return new Promise(resolve => {
output.resolve = () => { deferred.resolve = () => {
req.reply({ req.reply({
body: { pdf: cloneDeep(mockHighlights) }, body: { pdf: cloneDeep(mockHighlights) },
}) })
@ -94,7 +94,7 @@ const interceptSyncCodeAsync = () => {
}) })
}).as('sync-code') }).as('sync-code')
return output return deferred
} }
const interceptSyncPdfAsync = () => { const interceptSyncPdfAsync = () => {
@ -127,12 +127,10 @@ const interceptSyncPdf = () => {
}).as('sync-pdf') }).as('sync-pdf')
} }
// eslint-disable-next-line mocha/no-skipped-tests describe('<PdfSynctexControls/>', function () {
describe.skip('<PdfSynctexControls/>', function () {
beforeEach(function () { beforeEach(function () {
window.metaAttributesCache = new Map() window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-preventCompileOnLoad', false)
cy.interceptCompile()
cy.interceptEvents() cy.interceptEvents()
}) })
@ -141,6 +139,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('handles clicks on sync buttons', function () { it('handles clicks on sync buttons', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -151,6 +151,8 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.get('.synctex-control-icon').should('have.length', 2) cy.get('.synctex-control-icon').should('have.length', 2)
// mock editor cursor position update // mock editor cursor position update
@ -162,7 +164,7 @@ describe.skip('<PdfSynctexControls/>', function () {
) )
}) })
cy.wait('@compile').then(() => { cy.wrap(null).then(() => {
setDetachedPosition(mockPosition) setDetachedPosition(mockPosition)
}) })
@ -190,6 +192,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('disables button when multiple entities are selected', function () { it('disables button when multiple entities are selected', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -202,12 +206,16 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByRole('button', { name: 'Go to code location in PDF' }).should( cy.findByRole('button', { name: 'Go to code location in PDF' }).should(
'be.disabled' 'be.disabled'
) )
}) })
it('disables button when a file is selected', function () { it('disables button when a file is selected', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -218,6 +226,8 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByRole('button', { name: 'Go to code location in PDF' }).should( cy.findByRole('button', { name: 'Go to code location in PDF' }).should(
'be.disabled' 'be.disabled'
) )
@ -229,6 +239,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('does not have go to PDF location button nor arrow icon', function () { it('does not have go to PDF location button nor arrow icon', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -239,6 +251,8 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByRole('button', { name: /^Go to PDF location in code/ }).should( cy.findByRole('button', { name: /^Go to PDF location in code/ }).should(
'not.exist' 'not.exist'
) )
@ -247,6 +261,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('send set highlights action', function () { it('send set highlights action', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -257,7 +273,7 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.wait('@compile') cy.waitForCompile()
// mock editor cursor position update // mock editor cursor position update
cy.window().then(win => { cy.window().then(win => {
@ -302,6 +318,7 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('reacts to sync to code action', function () { it('reacts to sync to code action', function () {
cy.interceptCompile()
interceptSyncPdf() interceptSyncPdf()
const scope = mockScope() const scope = mockScope()
@ -312,7 +329,9 @@ describe.skip('<PdfSynctexControls/>', function () {
<WithSelectedEntities mockSelectedEntities={mockSelectedEntities} /> <WithSelectedEntities mockSelectedEntities={mockSelectedEntities} />
<PdfSynctexControls /> <PdfSynctexControls />
</EditorProviders> </EditorProviders>
).then(() => { )
cy.waitForCompile().then(() => {
testDetachChannel.postMessage({ testDetachChannel.postMessage({
role: 'detached', role: 'detached',
event: 'action-sync-to-code', event: 'action-sync-to-code',
@ -332,6 +351,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('does not have go to code location button nor arrow icon', function () { it('does not have go to code location button nor arrow icon', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -341,6 +362,8 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.waitForCompile()
cy.findByRole('button', { cy.findByRole('button', {
name: 'Go to code location in PDF', name: 'Go to code location in PDF',
}).should('not.exist') }).should('not.exist')
@ -349,6 +372,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('send go to code line action', function () { it('send go to code line action', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -357,7 +382,7 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.wait('@compile').then(() => { cy.waitForCompile().then(() => {
testDetachChannel.postMessage({ testDetachChannel.postMessage({
role: 'detacher', role: 'detacher',
event: `state-position`, event: `state-position`,
@ -394,6 +419,8 @@ describe.skip('<PdfSynctexControls/>', function () {
}) })
it('update inflight state', function () { it('update inflight state', function () {
cy.interceptCompile()
const scope = mockScope() const scope = mockScope()
cy.mount( cy.mount(
@ -403,7 +430,7 @@ describe.skip('<PdfSynctexControls/>', function () {
</EditorProviders> </EditorProviders>
) )
cy.wrap(null).then(() => { cy.waitForCompile().then(() => {
testDetachChannel.postMessage({ testDetachChannel.postMessage({
role: 'detacher', role: 'detacher',
event: `state-position`, event: `state-position`,

View file

@ -34,12 +34,13 @@ const DetachLayoutTest = () => {
) )
} }
// eslint-disable-next-line mocha/no-skipped-tests describe('useDetachLayout', function () {
describe.skip('useDetachLayout', function () {
beforeEach(function () { beforeEach(function () {
window.metaAttributesCache = new Map() window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
cy.stub(window, 'open').as('openWindow') cy.stub(window, 'open').as('openWindow')
cy.stub(window, 'close').as('closeWindow') cy.stub(window, 'close').as('closeWindow')
cy.interceptEvents()
}) })
afterEach(function () { afterEach(function () {