first commit
This commit is contained in:
243
services/web/cypress/support/shared/commands/compile.ts
Normal file
243
services/web/cypress/support/shared/commands/compile.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
const outputFiles = () => {
|
||||
const build = uuid()
|
||||
|
||||
return [
|
||||
{
|
||||
path: 'output.pdf',
|
||||
build,
|
||||
url: `/build/${build}/output.pdf`,
|
||||
type: 'pdf',
|
||||
},
|
||||
{
|
||||
path: 'output.bbl',
|
||||
build,
|
||||
url: `/build/${build}/output.bbl`,
|
||||
type: 'bbl',
|
||||
},
|
||||
{
|
||||
path: 'output.bib',
|
||||
build,
|
||||
url: `/build/${build}/output.bib`,
|
||||
type: 'bib',
|
||||
},
|
||||
{
|
||||
path: 'example.txt',
|
||||
build,
|
||||
url: `/build/${build}/example.txt`,
|
||||
type: 'txt',
|
||||
},
|
||||
{
|
||||
path: 'output.log',
|
||||
build,
|
||||
url: `/build/${build}/output.log`,
|
||||
type: 'log',
|
||||
},
|
||||
{
|
||||
path: 'output.blg',
|
||||
build,
|
||||
url: `/build/${build}/output.blg`,
|
||||
type: 'blg',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const compileFromCacheResponse = () => {
|
||||
return {
|
||||
fromCache: true,
|
||||
status: 'success',
|
||||
clsiServerId: 'foo',
|
||||
compileGroup: 'priority',
|
||||
pdfDownloadDomain: 'https://clsi.test-overleaf.com',
|
||||
outputFiles: outputFiles(),
|
||||
options: {
|
||||
rootResourcePath: 'main.tex',
|
||||
imageName: 'texlive-full:2024.1',
|
||||
compiler: 'pdflatex',
|
||||
stopOnFirstError: false,
|
||||
draft: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const interceptCompileFromCacheRequest = ({
|
||||
times,
|
||||
promise,
|
||||
}: {
|
||||
times: number
|
||||
promise: Promise<void>
|
||||
}) => {
|
||||
return cy.intercept(
|
||||
{ path: '/project/*/output/cached/output.overleaf.json', times },
|
||||
async req => {
|
||||
await promise
|
||||
req.reply({ body: compileFromCacheResponse() })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const interceptCompileRequest = ({ times = 1 } = {}) => {
|
||||
return cy.intercept(
|
||||
{ method: 'POST', pathname: '/project/*/compile', times },
|
||||
{
|
||||
body: {
|
||||
status: 'success',
|
||||
clsiServerId: 'foo',
|
||||
compileGroup: 'priority',
|
||||
pdfDownloadDomain: 'https://clsi.test-overleaf.com',
|
||||
outputFiles: outputFiles(),
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const interceptCompile = ({
|
||||
prefix = 'compile',
|
||||
times = 1,
|
||||
cached = false,
|
||||
regular = true,
|
||||
outputPDFFixture = 'output.pdf',
|
||||
} = {}) => {
|
||||
if (cached) {
|
||||
cy.intercept(
|
||||
{ path: '/project/*/output/cached/output.overleaf.json', times },
|
||||
{ body: compileFromCacheResponse() }
|
||||
).as(`${prefix}-cached`)
|
||||
} else {
|
||||
cy.intercept(
|
||||
{ pathname: '/project/*/output/cached/output.overleaf.json', times },
|
||||
{ statusCode: 404 }
|
||||
).as(`${prefix}-cached`)
|
||||
}
|
||||
|
||||
if (regular) {
|
||||
interceptCompileRequest({ times }).as(`${prefix}`)
|
||||
} else {
|
||||
cy.intercept(
|
||||
{ method: 'POST', pathname: '/project/*/compile', times },
|
||||
{
|
||||
body: {
|
||||
status: 'unavailable',
|
||||
clsiServerId: 'foo',
|
||||
compileGroup: 'priority',
|
||||
pdfDownloadDomain: 'https://clsi.test-overleaf.com',
|
||||
outputFiles: [],
|
||||
},
|
||||
}
|
||||
).as(`${prefix}`)
|
||||
}
|
||||
|
||||
cy.intercept(
|
||||
{ pathname: '/build/*/output.pdf', times },
|
||||
{ fixture: `build/${outputPDFFixture},null` }
|
||||
).as(`${prefix}-pdf`)
|
||||
|
||||
cy.intercept(
|
||||
{ pathname: '/build/*/output.log', times },
|
||||
{ fixture: 'build/output.log' }
|
||||
).as(`${prefix}-log`)
|
||||
|
||||
cy.intercept(
|
||||
{ pathname: '/build/*/output.blg', times },
|
||||
{ fixture: 'build/output.blg' }
|
||||
).as(`${prefix}-blg`)
|
||||
}
|
||||
|
||||
export const waitForCompile = ({
|
||||
prefix = 'compile',
|
||||
pdf = false,
|
||||
cached = false,
|
||||
regular = true,
|
||||
} = {}) => {
|
||||
if (cached) {
|
||||
cy.wait(`@${prefix}-cached`)
|
||||
}
|
||||
if (regular) {
|
||||
cy.wait(`@${prefix}`)
|
||||
}
|
||||
return waitForCompileOutput({ prefix, pdf, cached })
|
||||
}
|
||||
|
||||
export const waitForCompileOutput = ({
|
||||
prefix = 'compile',
|
||||
pdf = false,
|
||||
cached = false,
|
||||
} = {}) => {
|
||||
cy.wait(`@${prefix}-log`)
|
||||
.its('request.query.clsiserverid')
|
||||
.should('eq', cached ? 'cache' : 'foo') // straight from cache if cached
|
||||
cy.wait(`@${prefix}-blg`)
|
||||
.its('request.query.clsiserverid')
|
||||
.should('eq', cached ? 'cache' : 'foo') // straight from cache if cached
|
||||
if (pdf) {
|
||||
cy.wait(`@${prefix}-pdf`)
|
||||
.its('request.query.clsiserverid')
|
||||
.should('eq', 'foo') // always from VM first
|
||||
}
|
||||
return cy.wrap(null)
|
||||
}
|
||||
|
||||
export const interceptDeferredCompile = (beforeResponse?: () => void) => {
|
||||
const { promise, resolve } = Promise.withResolvers<void>()
|
||||
|
||||
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(
|
||||
{ pathname: '/build/*/output.pdf', times: 1 },
|
||||
{ fixture: 'build/output.pdf,null' }
|
||||
).as(`compile-pdf`)
|
||||
|
||||
cy.intercept(
|
||||
{ pathname: '/build/*/output.log', times: 1 },
|
||||
{ fixture: 'build/output.log' }
|
||||
).as(`compile-log`)
|
||||
|
||||
cy.intercept(
|
||||
{ pathname: '/build/*/output.blg', times: 1 },
|
||||
{ fixture: 'build/output.blg' }
|
||||
).as(`compile-blg`)
|
||||
|
||||
return cy.wrap(resolve)
|
||||
}
|
5
services/web/cypress/support/shared/commands/events.ts
Normal file
5
services/web/cypress/support/shared/commands/events.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const interceptEvents = () => {
|
||||
cy.intercept('POST', '/event/*', {
|
||||
statusCode: 204,
|
||||
}).as('createEvent')
|
||||
}
|
56
services/web/cypress/support/shared/commands/index.ts
Normal file
56
services/web/cypress/support/shared/commands/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import '@testing-library/cypress/add-commands'
|
||||
import {
|
||||
interceptCompile,
|
||||
interceptCompileFromCacheRequest,
|
||||
waitForCompile,
|
||||
interceptDeferredCompile,
|
||||
interceptCompileRequest,
|
||||
} from './compile'
|
||||
import { interceptEvents } from './events'
|
||||
import { interceptAsync } from './intercept-async'
|
||||
import { interceptFileUpload } from './upload'
|
||||
import { interceptProjectListing } from './project-list'
|
||||
import { interceptLinkedFile } from './linked-file'
|
||||
import { interceptMathJax } from './mathjax'
|
||||
import { interceptMetadata } from './metadata'
|
||||
import { interceptTutorials } from './tutorials'
|
||||
|
||||
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-namespace
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace,no-unused-vars
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
interface Chainable {
|
||||
interceptAsync: typeof interceptAsync
|
||||
interceptCompile: typeof interceptCompile
|
||||
interceptCompileRequest: typeof interceptCompileRequest
|
||||
interceptCompileFromCacheRequest: typeof interceptCompileFromCacheRequest
|
||||
interceptEvents: typeof interceptEvents
|
||||
interceptMetadata: typeof interceptMetadata
|
||||
waitForCompile: typeof waitForCompile
|
||||
interceptDeferredCompile: typeof interceptDeferredCompile
|
||||
interceptFileUpload: typeof interceptFileUpload
|
||||
interceptProjectListing: typeof interceptProjectListing
|
||||
interceptLinkedFile: typeof interceptLinkedFile
|
||||
interceptMathJax: typeof interceptMathJax
|
||||
interceptTutorials: typeof interceptTutorials
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('interceptAsync', interceptAsync)
|
||||
Cypress.Commands.add('interceptCompile', interceptCompile)
|
||||
Cypress.Commands.add('interceptCompileRequest', interceptCompileRequest)
|
||||
Cypress.Commands.add(
|
||||
'interceptCompileFromCacheRequest',
|
||||
interceptCompileFromCacheRequest
|
||||
)
|
||||
Cypress.Commands.add('interceptEvents', interceptEvents)
|
||||
Cypress.Commands.add('interceptMetadata', interceptMetadata)
|
||||
Cypress.Commands.add('waitForCompile', waitForCompile)
|
||||
Cypress.Commands.add('interceptDeferredCompile', interceptDeferredCompile)
|
||||
Cypress.Commands.add('interceptFileUpload', interceptFileUpload)
|
||||
Cypress.Commands.add('interceptProjectListing', interceptProjectListing)
|
||||
Cypress.Commands.add('interceptLinkedFile', interceptLinkedFile)
|
||||
Cypress.Commands.add('interceptMathJax', interceptMathJax)
|
||||
Cypress.Commands.add('interceptTutorials', interceptTutorials)
|
@@ -0,0 +1,19 @@
|
||||
import { RouteHandler, RouteMatcher } from 'cypress/types/net-stubbing'
|
||||
|
||||
export const interceptAsync = (route: RouteMatcher, alias: string) => {
|
||||
const deferred: { resolve: (value: RouteHandler) => void } = {
|
||||
resolve: () => {
|
||||
console.error('This should never be called')
|
||||
},
|
||||
}
|
||||
|
||||
const promise = new Promise<RouteHandler>(resolve => {
|
||||
deferred.resolve = resolve
|
||||
})
|
||||
|
||||
cy.intercept(route, req => {
|
||||
return promise.then(response => req.reply(response))
|
||||
}).as(alias)
|
||||
|
||||
return cy.wrap(deferred)
|
||||
}
|
12
services/web/cypress/support/shared/commands/linked-file.ts
Normal file
12
services/web/cypress/support/shared/commands/linked-file.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { HttpRequestInterceptor } from 'cypress/types/net-stubbing'
|
||||
|
||||
export const interceptLinkedFile = () => {
|
||||
cy.intercept(
|
||||
{ method: 'POST', url: '/project/*/linked_file' },
|
||||
cy
|
||||
.spy((req: Parameters<HttpRequestInterceptor>[0]) => {
|
||||
req.reply({ statusCode: 200, body: { success: true } })
|
||||
})
|
||||
.as('linked-file-request')
|
||||
)
|
||||
}
|
32
services/web/cypress/support/shared/commands/mathjax.ts
Normal file
32
services/web/cypress/support/shared/commands/mathjax.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
const MATHJAX_STUB = `
|
||||
window.MathJax = {
|
||||
startup: {
|
||||
promise: Promise.resolve()
|
||||
},
|
||||
svgStylesheet: () => document.createElement("STYLE"),
|
||||
typesetPromise: (elements) => {
|
||||
for (const element of elements) {
|
||||
// This will keep math delimeters around the text
|
||||
element.classList.add('MathJax')
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
tex2svgPromise: (content) => {
|
||||
const text = document.createElement('SPAN')
|
||||
text.classList.add('MathJax')
|
||||
text.innerText = content
|
||||
return Promise.resolve(text)
|
||||
},
|
||||
getMetricsFor: () => ({}),
|
||||
texReset: () => {},
|
||||
}
|
||||
`
|
||||
|
||||
export const interceptMathJax = () => {
|
||||
// NOTE: this is just a URL to be intercepted with the stub, not the real (versioned) MathJax URL
|
||||
const url = '/js/libs/mathjax/es5/tex-svg-full.js'
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-mathJaxPath', url)
|
||||
})
|
||||
cy.intercept('GET', url, MATHJAX_STUB).as('mathjax-load-request')
|
||||
}
|
3
services/web/cypress/support/shared/commands/metadata.ts
Normal file
3
services/web/cypress/support/shared/commands/metadata.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const interceptMetadata = () => {
|
||||
cy.intercept('POST', '/project/*/doc/*/metadata', {})
|
||||
}
|
22
services/web/cypress/support/shared/commands/project-list.ts
Normal file
22
services/web/cypress/support/shared/commands/project-list.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const interceptProjectListing = () => {
|
||||
cy.intercept('GET', '/user/projects', {
|
||||
projects: [
|
||||
{
|
||||
_id: 'fake-project-1',
|
||||
accessLevel: 'owner',
|
||||
name: 'My first project',
|
||||
},
|
||||
{
|
||||
_id: 'fake-project-2',
|
||||
accessLevel: 'owner',
|
||||
name: 'My second project',
|
||||
},
|
||||
],
|
||||
})
|
||||
cy.intercept('GET', '/project/*/entities', {
|
||||
entities: [
|
||||
{ path: '/frog.jpg', type: 'file' },
|
||||
{ path: 'figures/unicorn.png', type: 'file' },
|
||||
],
|
||||
})
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
export const interceptTutorials = () => {
|
||||
cy.intercept('POST', '/tutorial/**', {
|
||||
statusCode: 204,
|
||||
}).as('completeTutorial')
|
||||
}
|
18
services/web/cypress/support/shared/commands/upload.ts
Normal file
18
services/web/cypress/support/shared/commands/upload.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HttpRequestInterceptor } from 'cypress/types/net-stubbing'
|
||||
|
||||
export const interceptFileUpload = () => {
|
||||
cy.intercept(
|
||||
{ method: 'POST', url: /\/project\/.*\/upload/ },
|
||||
cy
|
||||
.spy((req: Parameters<HttpRequestInterceptor>[0]) => {
|
||||
const folderMatch = req.url.match(
|
||||
/project\/.*\/upload\?folder_id=[a-f0-9]{24}/
|
||||
)
|
||||
if (!folderMatch) {
|
||||
req.reply({ statusCode: 500, body: { success: false } })
|
||||
}
|
||||
req.reply({ statusCode: 200, body: { success: true } })
|
||||
})
|
||||
.as('uploadRequest')
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user