mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -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()}`
|
||||
let projectToDeleteId = ''
|
||||
|
||||
const findProjectRow = (projectName: string) =>
|
||||
cy.findByText(projectName).parent().parent()
|
||||
const findProjectRow = (projectName: string) => {
|
||||
cy.log('find project row')
|
||||
return cy.findByText(projectName).parent().parent()
|
||||
}
|
||||
|
||||
startWith({
|
||||
pro: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const { defineConfig } = require('cypress')
|
||||
const { readPdf, readFileInZip } = require('./helpers/read-file')
|
||||
|
||||
const specPattern = process.env.SPEC_PATTERN || './**/*.spec.{js,ts,tsx}'
|
||||
|
||||
|
@ -14,7 +15,10 @@ module.exports = defineConfig({
|
|||
e2e: {
|
||||
baseUrl: 'http://localhost',
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
on('task', {
|
||||
readPdf,
|
||||
readFileInZip,
|
||||
})
|
||||
},
|
||||
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": {
|
||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||
"@testing-library/cypress": "^10.0.1",
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/pdf-parse": "^1.1.4",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"adm-zip": "^0.5.12",
|
||||
"body-parser": "^1.20.2",
|
||||
"celebrate": "^15.0.3",
|
||||
"cypress": "13.6.6",
|
||||
"express": "^4.19.2",
|
||||
"isomorphic-git": "^1.25.10",
|
||||
"js-yaml": "^4.1.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"typescript": "^5.0.4",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
|
@ -321,6 +325,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": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.3.tgz",
|
||||
|
@ -330,11 +342,15 @@
|
|||
"version": "18.18.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.8.tgz",
|
||||
"integrity": "sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"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": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
|
||||
|
@ -371,6 +387,14 @@
|
|||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||
|
@ -2427,6 +2451,11 @@
|
|||
"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": {
|
||||
"version": "4.0.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
|
@ -3238,8 +3287,7 @@
|
|||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"optional": true
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.0",
|
||||
|
|
|
@ -11,13 +11,17 @@
|
|||
"dependencies": {
|
||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||
"@testing-library/cypress": "^10.0.1",
|
||||
"@types/adm-zip": "^0.5.5",
|
||||
"@types/pdf-parse": "^1.1.4",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"adm-zip": "^0.5.12",
|
||||
"body-parser": "^1.20.2",
|
||||
"celebrate": "^15.0.3",
|
||||
"cypress": "13.6.6",
|
||||
"express": "^4.19.2",
|
||||
"isomorphic-git": "^1.25.10",
|
||||
"js-yaml": "^4.1.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"typescript": "^5.0.4",
|
||||
"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