| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- import { v4 as uuid } from 'uuid'
- import { isExcludedBySharding, startWith } from './helpers/config'
- import { ensureUserExists, login } from './helpers/login'
- import {
- createProject,
- enableLinkSharing,
- shareProjectByEmailAndAcceptInviteViaDash,
- shareProjectByEmailAndAcceptInviteViaEmail,
- } from './helpers/project'
- import { throttledRecompile } from './helpers/compile'
- import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
- describe('Project Sharing', function () {
- if (isExcludedBySharding('CE_CUSTOM_2')) return
- ensureUserExists({ email: 'user@example.com' })
- startWith({ withDataDir: true })
- let projectName: string
- beforeWithReRunOnTestRetry(function () {
- projectName = `Project ${uuid()}`
- setupTestProject()
- })
- beforeEach(() => {
- // Always start with a fresh session
- cy.session([uuid()], () => {})
- })
- let linkSharingReadOnly: string
- let linkSharingReadAndWrite: string
- function setupTestProject() {
- login('user@example.com')
- cy.visit('/project')
- createProject(projectName)
- // Add chat message
- cy.findByText('Chat').click()
- // wait for lazy loading of the chat pane
- cy.findByText('Send your first message to your collaborators')
- cy.get(
- 'textarea[placeholder="Send a message to your collaborators…"]'
- ).type('New Chat Message{enter}')
- // Get link sharing links
- enableLinkSharing().then(
- ({ linkSharingReadOnly: ro, linkSharingReadAndWrite: rw }) => {
- linkSharingReadAndWrite = rw
- linkSharingReadOnly = ro
- }
- )
- }
- function expectContentReadOnlyAccess() {
- cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
- cy.get('.cm-content').should('contain.text', '\\maketitle')
- cy.get('.cm-content').should('have.attr', 'contenteditable', 'false')
- }
- function expectContentWriteAccess() {
- const section = `Test Section ${uuid()}`
- cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
- const recompile = throttledRecompile()
- // wait for the editor to finish loading
- cy.get('.cm-content').should('contain.text', '\\maketitle')
- // the editor should be writable
- cy.get('.cm-content').should('have.attr', 'contenteditable', 'true')
- cy.findByText('\\maketitle').parent().click()
- cy.findByText('\\maketitle').parent().type(`\n\\section{{}${section}}`)
- // should have written
- cy.get('.cm-content').should('contain.text', `\\section{${section}}`)
- // check PDF
- recompile()
- cy.get('.pdf-viewer').should('contain.text', projectName)
- cy.get('.pdf-viewer').should('contain.text', section)
- }
- function expectNoAccess() {
- // try read only access link
- cy.visit(linkSharingReadOnly)
- cy.url().should('match', /\/login/)
- // Cypress bugs: cypress resolves the link-sharing link outside the browser, and it carries over the hash of the link-sharing link to the login page redirect (bug 1).
- // Effectively, cypress then instructs the browser to change the page from /login#read-only-hash to /login#read-and-write-hash.
- // This is turn does not trigger a "page load", but rather just "scrolling", which in turn trips up the "page loaded" detection in cypress (bug 2).
- // Work around this by navigating away from the /login page in between checks.
- cy.visit('/user/password/reset')
- // try read and write access link
- cy.visit(linkSharingReadAndWrite)
- cy.url().should('match', /\/login/)
- }
- function expectChatAccess() {
- cy.findByText('Chat').click()
- cy.findByText('New Chat Message')
- }
- function expectHistoryAccess() {
- cy.findByText('History').click()
- cy.findByText('Labels')
- cy.findByText(/\\begin\{document}/)
- cy.findAllByTestId('history-version-metadata-users')
- .last()
- .should('have.text', 'user')
- cy.findByText('Back to editor').click()
- }
- function expectNoChatAccess() {
- cy.findByText('Layout') // wait for lazy loading
- cy.findByText('Chat').should('not.exist')
- }
- function expectNoHistoryAccess() {
- cy.findByText('Layout') // wait for lazy loading
- cy.findByText('History').should('not.exist')
- }
- function expectFullReadOnlyAccess() {
- expectContentReadOnlyAccess()
- expectChatAccess()
- expectHistoryAccess()
- }
- function expectRestrictedReadOnlyAccess() {
- expectContentReadOnlyAccess()
- expectNoChatAccess()
- expectNoHistoryAccess()
- }
- function expectReadAndWriteAccess() {
- expectContentWriteAccess()
- expectChatAccess()
- expectHistoryAccess()
- }
- function expectProjectDashboardEntry() {
- cy.visit('/project')
- cy.findByText(projectName)
- }
- function expectEditAuthoredAs(author: string) {
- cy.findByText('History').click()
- cy.findAllByTestId('history-version-metadata-users')
- .first()
- .should('contain.text', author) // might have other edits in the same group
- }
- describe('via email', function () {
- const email = 'collaborator-email@example.com'
- ensureUserExists({ email })
- beforeEach(function () {
- login('user@example.com')
- shareProjectByEmailAndAcceptInviteViaEmail(
- projectName,
- email,
- 'Read only'
- )
- })
- it('should grant the collaborator read access', () => {
- cy.visit('/project')
- cy.findByText(projectName).click()
- expectFullReadOnlyAccess()
- expectProjectDashboardEntry()
- })
- })
- describe('read only', () => {
- const email = 'collaborator-ro@example.com'
- ensureUserExists({ email })
- beforeWithReRunOnTestRetry(function () {
- login('user@example.com')
- shareProjectByEmailAndAcceptInviteViaDash(projectName, email, 'Read only')
- })
- it('should grant the collaborator read access', () => {
- login(email)
- cy.visit('/project')
- cy.findByText(projectName).click()
- expectFullReadOnlyAccess()
- expectProjectDashboardEntry()
- })
- })
- describe('read and write', () => {
- const email = 'collaborator-rw@example.com'
- ensureUserExists({ email })
- beforeWithReRunOnTestRetry(function () {
- login('user@example.com')
- shareProjectByEmailAndAcceptInviteViaDash(projectName, email, 'Can edit')
- })
- it('should grant the collaborator write access', () => {
- login(email)
- cy.visit('/project')
- cy.findByText(projectName).click()
- expectReadAndWriteAccess()
- expectEditAuthoredAs('You')
- expectProjectDashboardEntry()
- })
- })
- describe('token access', () => {
- describe('logged in', () => {
- describe('read only', () => {
- const email = 'collaborator-link-ro@example.com'
- ensureUserExists({ email })
- it('should grant restricted read access', () => {
- login(email)
- cy.visit(linkSharingReadOnly)
- cy.findByText(projectName) // wait for lazy loading
- cy.findByText('Join Project').click()
- expectRestrictedReadOnlyAccess()
- expectProjectDashboardEntry()
- })
- })
- describe('read and write', () => {
- const email = 'collaborator-link-rw@example.com'
- ensureUserExists({ email })
- it('should grant full write access', () => {
- login(email)
- cy.visit(linkSharingReadAndWrite)
- cy.findByText(projectName) // wait for lazy loading
- cy.findByText('Join Project').click()
- expectReadAndWriteAccess()
- expectEditAuthoredAs('You')
- expectProjectDashboardEntry()
- })
- })
- })
- describe('with OVERLEAF_ALLOW_PUBLIC_ACCESS=false', () => {
- describe('wrap startup', () => {
- startWith({
- vars: {
- OVERLEAF_ALLOW_PUBLIC_ACCESS: 'false',
- },
- withDataDir: true,
- })
- it('should block access', () => {
- expectNoAccess()
- })
- })
- describe('with OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING=true', () => {
- startWith({
- vars: {
- OVERLEAF_ALLOW_PUBLIC_ACCESS: 'false',
- OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true',
- },
- withDataDir: true,
- })
- it('should block access', () => {
- expectNoAccess()
- })
- })
- })
- describe('with OVERLEAF_ALLOW_PUBLIC_ACCESS=true', () => {
- describe('wrap startup', () => {
- startWith({
- vars: {
- OVERLEAF_ALLOW_PUBLIC_ACCESS: 'true',
- },
- withDataDir: true,
- })
- it('should grant read access with read link', () => {
- cy.visit(linkSharingReadOnly)
- expectRestrictedReadOnlyAccess()
- })
- it('should prompt for login with write link', () => {
- cy.visit(linkSharingReadAndWrite)
- cy.url().should('match', /\/login/)
- })
- })
- describe('with OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING=true', () => {
- startWith({
- vars: {
- OVERLEAF_ALLOW_PUBLIC_ACCESS: 'true',
- OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true',
- },
- withDataDir: true,
- })
- it('should grant read access with read link', () => {
- cy.visit(linkSharingReadOnly)
- expectRestrictedReadOnlyAccess()
- })
- // eslint-disable-next-line mocha/no-skipped-tests
- it.skip('should grant write access with write link', () => {
- cy.visit(linkSharingReadAndWrite)
- expectReadAndWriteAccess()
- expectEditAuthoredAs('Anonymous')
- })
- })
- })
- })
- })
|