mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[CE/SP] Add e2e tests for project list (#18475)
* [CE/SP] Add e2e tests for project list GitOrigin-RevId: 0368d74b47c79f74b3520177d8bb697013d851d4
This commit is contained in:
parent
df2898a911
commit
0cfc64786d
6 changed files with 232 additions and 6 deletions
|
@ -14,8 +14,10 @@ describe('admin panel', function () {
|
||||||
const deletedProjectName = `deleted-project-${uuid()}`
|
const deletedProjectName = `deleted-project-${uuid()}`
|
||||||
let projectToDeleteId = ''
|
let projectToDeleteId = ''
|
||||||
|
|
||||||
const findProjectRow = (projectName: string) =>
|
const findProjectRow = (projectName: string) => {
|
||||||
cy.findByText(projectName).parent().parent()
|
cy.log('find project row')
|
||||||
|
return cy.findByText(projectName).parent().parent()
|
||||||
|
}
|
||||||
|
|
||||||
startWith({
|
startWith({
|
||||||
pro: true,
|
pro: true,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const { defineConfig } = require('cypress')
|
const { defineConfig } = require('cypress')
|
||||||
|
const { readPdf, readFileInZip } = require('./helpers/read-file')
|
||||||
|
|
||||||
const specPattern = process.env.SPEC_PATTERN || './**/*.spec.{js,ts,tsx}'
|
const specPattern = process.env.SPEC_PATTERN || './**/*.spec.{js,ts,tsx}'
|
||||||
|
|
||||||
|
@ -14,7 +15,10 @@ module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost',
|
baseUrl: 'http://localhost',
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
// implement node event listeners here
|
on('task', {
|
||||||
|
readPdf,
|
||||||
|
readFileInZip,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
specPattern,
|
specPattern,
|
||||||
},
|
},
|
||||||
|
|
52
server-ce/test/helpers/read-file.ts
Normal file
52
server-ce/test/helpers/read-file.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import pdf from 'pdf-parse'
|
||||||
|
import AdmZip from 'adm-zip'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
|
||||||
|
const sleep = promisify(setTimeout)
|
||||||
|
|
||||||
|
const MAX_ATTEMPTS = 15
|
||||||
|
const POLL_INTERVAL = 500
|
||||||
|
|
||||||
|
type ReadFileInZipArgs = {
|
||||||
|
pathToZip: string
|
||||||
|
fileToRead: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readFileInZip({
|
||||||
|
pathToZip,
|
||||||
|
fileToRead,
|
||||||
|
}: ReadFileInZipArgs) {
|
||||||
|
let attempt = 0
|
||||||
|
while (attempt < MAX_ATTEMPTS) {
|
||||||
|
if (fs.existsSync(pathToZip)) {
|
||||||
|
const zip = new AdmZip(path.resolve(pathToZip))
|
||||||
|
const entry = zip
|
||||||
|
.getEntries()
|
||||||
|
.find(entry => entry.entryName == fileToRead)
|
||||||
|
if (entry) {
|
||||||
|
return entry.getData().toString('utf8')
|
||||||
|
} else {
|
||||||
|
throw new Error(`${fileToRead} not found in ${pathToZip}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await sleep(POLL_INTERVAL)
|
||||||
|
attempt++
|
||||||
|
}
|
||||||
|
throw new Error(`${pathToZip} not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readPdf(file: string) {
|
||||||
|
let attempt = 0
|
||||||
|
while (attempt < MAX_ATTEMPTS) {
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
const dataBuffer = fs.readFileSync(path.resolve(file))
|
||||||
|
const { text } = await pdf(dataBuffer)
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
await sleep(POLL_INTERVAL)
|
||||||
|
attempt++
|
||||||
|
}
|
||||||
|
throw new Error(`${file} not found`)
|
||||||
|
}
|
54
server-ce/test/package-lock.json
generated
54
server-ce/test/package-lock.json
generated
|
@ -8,13 +8,17 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||||
"@testing-library/cypress": "^10.0.1",
|
"@testing-library/cypress": "^10.0.1",
|
||||||
|
"@types/adm-zip": "^0.5.5",
|
||||||
|
"@types/pdf-parse": "^1.1.4",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
|
"adm-zip": "^0.5.12",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"celebrate": "^15.0.3",
|
"celebrate": "^15.0.3",
|
||||||
"cypress": "13.6.6",
|
"cypress": "13.6.6",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"isomorphic-git": "^1.25.10",
|
"isomorphic-git": "^1.25.10",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"pdf-parse": "^1.1.1",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
}
|
}
|
||||||
|
@ -321,6 +325,14 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/adm-zip": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/aria-query": {
|
"node_modules/@types/aria-query": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.3.tgz",
|
||||||
|
@ -330,11 +342,15 @@
|
||||||
"version": "18.18.8",
|
"version": "18.18.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.8.tgz",
|
||||||
"integrity": "sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==",
|
"integrity": "sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pdf-parse": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg=="
|
||||||
|
},
|
||||||
"node_modules/@types/sinonjs__fake-timers": {
|
"node_modules/@types/sinonjs__fake-timers": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
|
||||||
|
@ -371,6 +387,14 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/adm-zip": {
|
||||||
|
"version": "0.5.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.12.tgz",
|
||||||
|
"integrity": "sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/aggregate-error": {
|
"node_modules/aggregate-error": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
|
@ -2427,6 +2451,11 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-ensure": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
|
||||||
|
"integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
|
||||||
|
},
|
||||||
"node_modules/npm-run-path": {
|
"node_modules/npm-run-path": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||||
|
@ -2572,6 +2601,26 @@
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pdf-parse": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^3.1.0",
|
||||||
|
"node-ensure": "^0.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pdf-parse/node_modules/debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pend": {
|
"node_modules/pend": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||||
|
@ -3238,8 +3287,7 @@
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "5.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
|
|
@ -11,13 +11,17 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||||
"@testing-library/cypress": "^10.0.1",
|
"@testing-library/cypress": "^10.0.1",
|
||||||
|
"@types/adm-zip": "^0.5.5",
|
||||||
|
"@types/pdf-parse": "^1.1.4",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
|
"adm-zip": "^0.5.12",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"celebrate": "^15.0.3",
|
"celebrate": "^15.0.3",
|
||||||
"cypress": "13.6.6",
|
"cypress": "13.6.6",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"isomorphic-git": "^1.25.10",
|
"isomorphic-git": "^1.25.10",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"pdf-parse": "^1.1.1",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
}
|
}
|
||||||
|
|
116
server-ce/test/project-list.spec.ts
Normal file
116
server-ce/test/project-list.spec.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import { ensureUserExists, login } from './helpers/login'
|
||||||
|
import { createProject } from './helpers/project'
|
||||||
|
import { startWith } from './helpers/config'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
|
const WITHOUT_PROJECTS_USER = 'user-without-projects@example.com'
|
||||||
|
const REGULAR_USER = 'user@example.com'
|
||||||
|
|
||||||
|
describe('Project List', () => {
|
||||||
|
startWith({ pro: true })
|
||||||
|
|
||||||
|
const findProjectRow = (projectName: string) => {
|
||||||
|
cy.log('find project row')
|
||||||
|
return cy.findByText(projectName).parent().parent()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('user with no projects', () => {
|
||||||
|
ensureUserExists({ email: WITHOUT_PROJECTS_USER })
|
||||||
|
|
||||||
|
it("'Import from Github' is not displayed in the welcome page", () => {
|
||||||
|
login(WITHOUT_PROJECTS_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
cy.findByText('Create a new project').click()
|
||||||
|
cy.findByText(/Import from Github/i).should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('user with projects', () => {
|
||||||
|
const projectName = `test-project-${uuid()}`
|
||||||
|
let projectId: string | undefined
|
||||||
|
ensureUserExists({ email: REGULAR_USER })
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
login(REGULAR_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
createProject(projectName, { type: 'Example Project' }).then(
|
||||||
|
id => (projectId = id)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Can download project sources', () => {
|
||||||
|
login(REGULAR_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
|
||||||
|
findProjectRow(projectName).within(() =>
|
||||||
|
cy.get(`[aria-label="Download .zip file"]`).click()
|
||||||
|
)
|
||||||
|
|
||||||
|
cy.task('readFileInZip', {
|
||||||
|
pathToZip: `cypress/downloads/${projectName}.zip`,
|
||||||
|
fileToRead: 'main.tex',
|
||||||
|
}).should('contain', 'Your introduction goes here')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Can download project PDF', () => {
|
||||||
|
login(REGULAR_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
|
||||||
|
findProjectRow(projectName).within(() =>
|
||||||
|
cy.get(`[aria-label="Download PDF"]`).click()
|
||||||
|
)
|
||||||
|
|
||||||
|
const pdfName = projectName.replaceAll('-', '_')
|
||||||
|
cy.task('readPdf', `cypress/downloads/${pdfName}.pdf`).should(
|
||||||
|
'contain',
|
||||||
|
'Your introduction goes here'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can assign and remove tags to projects', () => {
|
||||||
|
const tagName = uuid().slice(0, 7) // long tag names are truncated in the UI, which affects selectors
|
||||||
|
login(REGULAR_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
|
||||||
|
cy.log('select project')
|
||||||
|
cy.get(`[id="select-project-${projectId}"`).click()
|
||||||
|
|
||||||
|
cy.log('add tag to project')
|
||||||
|
cy.get('button[aria-label="Tags"]').click()
|
||||||
|
cy.findByText('Create new tag').click()
|
||||||
|
cy.get('input[name="new-tag-form-name"]').type(`${tagName}{enter}`)
|
||||||
|
cy.get(`button[aria-label="Select tag ${tagName}"]`) // tag label in project row
|
||||||
|
|
||||||
|
cy.log('remove tag')
|
||||||
|
cy.get(`button[aria-label="Remove tag ${tagName}"]`)
|
||||||
|
.first()
|
||||||
|
.click({ force: true })
|
||||||
|
cy.get(`button[aria-label="Select tag ${tagName}"]`).should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can filter by tag', () => {
|
||||||
|
cy.log('create a separate project to filter')
|
||||||
|
const nonTaggedProjectName = `project-${uuid()}`
|
||||||
|
login(REGULAR_USER)
|
||||||
|
cy.visit('/project')
|
||||||
|
createProject(nonTaggedProjectName)
|
||||||
|
cy.visit('/project')
|
||||||
|
|
||||||
|
cy.log('select project')
|
||||||
|
cy.get(`[id="select-project-${projectId}"`).click()
|
||||||
|
|
||||||
|
cy.log('add tag to project')
|
||||||
|
const tagName = uuid().slice(0, 7) // long tag names are truncated in the UI, which affects selectors
|
||||||
|
cy.get('button[aria-label="Tags"]').click()
|
||||||
|
cy.findByText('Create new tag').click()
|
||||||
|
cy.get('input[name="new-tag-form-name"]').type(`${tagName}{enter}`)
|
||||||
|
|
||||||
|
cy.log(
|
||||||
|
'check the non-tagged project is filtered out after clicking the tag'
|
||||||
|
)
|
||||||
|
cy.findByText(nonTaggedProjectName).should('exist')
|
||||||
|
cy.get('button').contains(tagName).click({ force: true })
|
||||||
|
cy.findByText(nonTaggedProjectName).should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue