ソースを参照

Merge pull request #30910 from overleaf/mj-tear-down-old-e2e

[e2e] Remove old editor E2E tests

GitOrigin-RevId: 960c5cd5f17c2a5bae225ecb53fa0eed76938939
Mathias Jakobsen 6 ヶ月 前
コミット
9e0de43638

+ 0 - 150
server-ce/test/create-and-compile-project.spec.ts

@@ -1,150 +0,0 @@
-import { ensureUserExists, login } from './helpers/login'
-import {
-  createProject,
-  openProjectViaInviteNotification,
-} from './helpers/project'
-import { isExcludedBySharding, startWith } from './helpers/config'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-
-const USER = 'user@example.com'
-const COLLABORATOR = 'collaborator@example.com'
-
-describe('Project creation and compilation', function () {
-  if (isExcludedBySharding('CE_DEFAULT')) return
-  startWith({})
-  ensureUserExists({ email: USER })
-  ensureUserExists({ email: COLLABORATOR })
-
-  it('users can create project and compile it', function () {
-    login(USER)
-    const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-    waitForCompile(() => {
-      createProject('test-project')
-    })
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() => {
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle').parent().type('\n\\section{{}Test Section}')
-    })
-    recompile()
-    cy.findByRole('region', { name: 'PDF preview and logs' }).within(() => {
-      cy.findByLabelText(/Page.*1/i).should('be.visible')
-      cy.findByText('Test Section').should('be.visible')
-    })
-  })
-
-  it('create and edit markdown file', function () {
-    const fileName = `test-${Date.now()}.md`
-    const markdownContent = '# Markdown title'
-    login(USER)
-    createProject('test-project')
-
-    cy.findByRole('navigation', { name: 'Project files and outline' })
-      .findByRole('button', { name: 'New file' })
-      .click()
-    cy.findByRole('dialog').within(() => {
-      cy.findByLabelText('File Name').as('filename').clear()
-      cy.get('@filename').type(fileName)
-      cy.findByRole('button', { name: 'Create' }).click()
-    })
-    cy.findByRole('button', { name: fileName }).click()
-    // wait until we've switched to the newly created empty file
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'have.length',
-      1
-    )
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).type(
-      markdownContent
-    )
-    cy.findByRole('button', { name: 'main.tex' }).click()
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      '\\maketitle'
-    )
-    cy.findByRole('button', { name: fileName }).click()
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      markdownContent
-    )
-  })
-
-  it('can link and display linked image from other project', function () {
-    const sourceProjectName = `test-project-${Date.now()}`
-    const targetProjectName = `${sourceProjectName}-target`
-    login(USER)
-
-    createProject(sourceProjectName, {
-      type: 'Example project',
-      open: false,
-    }).as('sourceProjectId')
-    createProject(targetProjectName)
-
-    // link the image from `projectName` into this project
-    cy.findByRole('button', { name: 'New file' }).click()
-    cy.findByRole('dialog').within(() => {
-      cy.findByRole('button', { name: 'From another project' }).click()
-      cy.findByLabelText('Select a Project').select(sourceProjectName)
-      cy.findByLabelText('Select a File').select('frog.jpg')
-      cy.findByRole('button', { name: 'Create' }).click()
-    })
-    cy.findByRole('navigation', { name: 'Project files and outline' })
-      .findByRole('treeitem', { name: 'frog.jpg' })
-      .click()
-    cy.findByRole('link', { name: 'Another project' })
-      .should('have.attr', 'href')
-      .then(href => {
-        cy.get('@sourceProjectId').then(sourceProjectId => {
-          expect(href).to.equal(`/project/${sourceProjectId}`)
-        })
-      })
-  })
-
-  it('can refresh linked files as collaborator', function () {
-    const sourceProjectName = `test-project-${Date.now()}`
-    const targetProjectName = `${sourceProjectName}-target`
-    login(USER)
-    createProject(sourceProjectName, {
-      type: 'Example project',
-      open: false,
-    }).as('sourceProjectId')
-    createProject(targetProjectName).as('targetProjectId')
-
-    // link the image from `projectName` into this project
-    cy.findByRole('navigation', { name: 'Project files and outline' })
-      .findByRole('button', { name: 'New file' })
-      .click()
-    cy.findByRole('dialog').within(() => {
-      cy.findByRole('button', { name: 'From another project' }).click()
-      cy.findByLabelText('Select a Project').select(sourceProjectName)
-      cy.findByLabelText('Select a File').select('frog.jpg')
-      cy.findByRole('button', { name: 'Create' }).click()
-    })
-
-    cy.findByRole('navigation', { name: 'Project actions' }).within(() => {
-      cy.findByRole('button', { name: 'Share' }).click()
-    })
-    cy.findByRole('dialog').within(() => {
-      cy.findByRole('combobox', { name: 'Add email address' }).type(
-        COLLABORATOR + ','
-      )
-      cy.findByRole('button', { name: 'Invite' }).click()
-      cy.findByText('Invite not yet accepted.')
-    })
-
-    login(COLLABORATOR)
-    openProjectViaInviteNotification(targetProjectName)
-    cy.get('@targetProjectId').then(targetProjectId => {
-      cy.url().should('include', targetProjectId)
-    })
-
-    cy.findByRole('navigation', { name: 'Project files and outline' })
-      .findByRole('treeitem', { name: 'frog.jpg' })
-      .click()
-    cy.findByRole('link', { name: 'Another project' })
-      .should('have.attr', 'href')
-      .then(href => {
-        cy.get('@sourceProjectId').then(sourceProjectId => {
-          expect(href).to.equal(`/project/${sourceProjectId}`)
-        })
-      })
-  })
-})

+ 0 - 338
server-ce/test/editor.spec.ts

@@ -1,338 +0,0 @@
-import {
-  createNewFile,
-  createProject,
-  openProjectById,
-  testNewFileUpload,
-} from './helpers/project'
-import { isExcludedBySharding, startWith } from './helpers/config'
-import { ensureUserExists, login } from './helpers/login'
-import { v4 as uuid } from 'uuid'
-import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-
-const USER = 'user@example.com'
-const COLLABORATOR = 'collaborator@example.com'
-
-describe('editor', function () {
-  if (isExcludedBySharding('PRO_DEFAULT_1')) return
-  startWith({ pro: true })
-  ensureUserExists({ email: USER })
-  ensureUserExists({ email: COLLABORATOR })
-
-  let projectName: string
-  let projectId: string
-  let recompile: () => void
-  let waitForCompile: (fn: () => void) => void
-  beforeWithReRunOnTestRetry(() => {
-    projectName = `project-${uuid()}`
-    login(USER)
-    createProject(projectName, { type: 'Example project', open: false }).then(
-      id => (projectId = id)
-    )
-    ;({ recompile, waitForCompile } = prepareWaitForNextCompileSlot())
-  })
-
-  beforeEach(function () {
-    login(USER)
-    waitForCompile(() => {
-      openProjectById(projectId)
-    })
-  })
-
-  describe('spelling', function () {
-    function changeSpellCheckLanguageTo(lng: string) {
-      cy.log(`change project language to '${lng}'`)
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-
-      cy.findByRole('dialog').within(() => {
-        cy.findByLabelText('Spell check').select(lng)
-      })
-      cy.get('body').type('{esc}')
-    }
-
-    afterEach(function () {
-      changeSpellCheckLanguageTo('Off')
-    })
-
-    it('word dictionary and spelling', function () {
-      changeSpellCheckLanguageTo('English (American)')
-      createNewFile()
-      const word = createRandomLetterString()
-
-      cy.log('edit project file')
-      cy.get('.cm-line').type(word)
-      cy.findByText(word).should('have.class', 'ol-cm-spelling-error')
-
-      changeSpellCheckLanguageTo('Off')
-      cy.findByText(word).should('not.have.class', 'ol-cm-spelling-error')
-
-      changeSpellCheckLanguageTo('Spanish')
-      cy.findByText(word).should('have.class', 'ol-cm-spelling-error')
-
-      cy.log('add word to dictionary')
-      cy.findByText(word).rightclick()
-      cy.findByRole('menuitem', { name: 'Add to dictionary' }).click()
-      cy.findByText(word).should('not.have.class', 'ol-cm-spelling-error')
-
-      cy.log('remove word from dictionary')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('dialog').within(() => {
-        cy.findByLabelText('Dictionary').click()
-      })
-      cy.findByTestId('dictionary-modal').within(() => {
-        cy.findByText(word)
-          .parent()
-          .within(() =>
-            cy.findByRole('button', { name: 'Remove from dictionary' }).click()
-          )
-
-        // the modal has 2 close buttons, this ensures the one with the visible label is
-        // clicked, otherwise it would need `force: true`
-        cy.contains('button', /close/i).click()
-      })
-
-      cy.log('close left panel')
-      cy.findByTestId('left-menu').type('{esc}')
-
-      cy.log('rewrite word to force spelling error')
-      cy.get('.cm-line').type('{selectAll}{del}' + word + '{enter}')
-
-      cy.get('.ol-cm-spelling-error').should('contain.text', word)
-    })
-  })
-
-  describe('editor', function () {
-    it('renders jpg', function () {
-      cy.findByRole('navigation', {
-        name: 'Project files and outline',
-      })
-        .findByRole('treeitem', { name: 'frog.jpg' })
-        .click()
-      cy.get('[alt="frog.jpg"]')
-        .should('be.visible')
-        .and('have.prop', 'naturalWidth')
-        .should('be.greaterThan', 0)
-    })
-
-    it('symbol palette', function () {
-      createNewFile()
-
-      cy.get('button[aria-label="Insert symbol"]').click({
-        force: true,
-      })
-      cy.get('button').contains('𝜉').click()
-      cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-        'contain.text',
-        '\\xi'
-      )
-
-      cy.log('recompile to force flush and avoid "unsaved changes" prompt')
-      recompile()
-    })
-  })
-
-  describe('add new file to project', function () {
-    beforeEach(function () {
-      cy.findByRole('button', { name: 'New file' }).click()
-    })
-
-    testNewFileUpload()
-
-    it('should not display import from URL', function () {
-      cy.findByRole('button', { name: 'From external URL' }).should('not.exist')
-    })
-  })
-
-  describe('left menu', function () {
-    beforeEach(function () {
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-    })
-
-    it('can download project sources', function () {
-      cy.findByRole('link', { name: 'Source' }).click()
-      const zipName = projectName.replaceAll('-', '_')
-      cy.task('readFileInZip', {
-        pathToZip: `cypress/downloads/${zipName}.zip`,
-        fileToRead: 'main.tex',
-      }).should('contain', 'Your introduction goes here')
-    })
-
-    it('can download project PDF', function () {
-      cy.log('ensure project is compiled')
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'contain.text',
-        'Your Paper'
-      )
-      cy.findByRole('dialog').within(() => {
-        cy.findByRole('link', { name: 'PDF' }).click()
-        const pdfName = projectName.replaceAll('-', '_')
-        cy.task('readPdf', `cypress/downloads/${pdfName}.pdf`).should(
-          'contain',
-          'Your introduction goes here'
-        )
-      })
-    })
-
-    it('word count', function () {
-      cy.log('ensure project is compiled')
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'contain.text',
-        'Your Paper'
-      )
-      cy.findByRole('button', { name: 'Word Count' }).click()
-
-      cy.findByTestId('word-count-modal').within(() => {
-        cy.findByText('Total Words:')
-        cy.findByText('607')
-        cy.findByText('Headers:')
-        cy.findByText('14')
-        cy.findByText('Math Inline:')
-        cy.findByText('6')
-        cy.findByText('Math Display:')
-        cy.findByText('1')
-      })
-    })
-  })
-
-  describe('cite key search', function () {
-    it('can insert citation from cite key', function () {
-      createNewFile()
-      cy.get('.cm-line').type('\\cite{{}gre')
-      cy.findByRole('listbox').within(() => {
-        cy.findByRole('option').should('contain.text', 'greenwade93').click()
-      })
-      cy.get('.cm-line').should('have.text', '\\cite{greenwade93}')
-    })
-
-    it('updates citation search when bib file is changed', function () {
-      createNewFile()
-      cy.get('.cm-line').type('\\cite{{}new')
-      // Wait a reasonable time to ensure the autocomplete would've appeared if there were any matches
-      // eslint-disable-next-line cypress/no-unnecessary-waiting
-      cy.wait(200)
-      cy.findByRole('listbox').should('not.exist')
-      cy.findByRole('treeitem', { name: 'sample.bib' }).click()
-      cy.get('.cm-line')
-        .last()
-        .type(
-          '\n@article{{}newkey2024,\n author = {{}Doe, John},\n title = {{}A New Article},\n journal = {{}Journal of Testing},\n year = 2024\n}\n'
-        )
-      createNewFile()
-      cy.get('.cm-line').type('\\cite{{}new')
-      cy.findByRole('listbox').within(() => {
-        cy.findByRole('option').should('contain.text', 'newkey2024').click()
-      })
-      cy.get('.cm-line').should('have.text', '\\cite{newkey2024}')
-    })
-  })
-
-  describe('layout selector', function () {
-    it('show editor only and switch between editor and pdf', function () {
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'be.visible'
-      )
-      cy.get('.cm-editor').should('be.visible')
-
-      cy.findByRole('button', { name: 'Layout' }).click()
-      cy.findByRole('menu').within(() => {
-        cy.findByRole('menuitem', { name: /Editor only/ }).click()
-      })
-
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'not.be.visible'
-      )
-      cy.get('.cm-editor').should('be.visible')
-
-      cy.findByRole('button', { name: 'Switch to PDF' }).click()
-
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'be.visible'
-      )
-      cy.get('.cm-editor').should('not.be.visible')
-
-      cy.findByRole('button', { name: 'Switch to editor' }).click()
-
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'not.be.visible'
-      )
-      cy.get('.cm-editor').should('be.visible')
-    })
-
-    it('show PDF only and go back to Editor & PDF', function () {
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'be.visible'
-      )
-      cy.get('.cm-editor').should('be.visible')
-
-      cy.findByRole('button', { name: 'Layout' }).click()
-      cy.findByRole('menu').within(() => {
-        cy.findByRole('menuitem', { name: /PDF only/ }).click()
-      })
-
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'be.visible'
-      )
-      cy.get('.cm-editor').should('not.be.visible')
-
-      cy.findByRole('button', { name: 'Layout' }).click()
-      cy.findByRole('menu').within(() => {
-        cy.findByRole('menuitem', { name: 'Editor & PDF' }).click()
-      })
-
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'be.visible'
-      )
-      cy.get('.cm-editor').should('be.visible')
-    })
-
-    it('PDF in a separate tab (tests editor only)', function () {
-      cy.findByTestId('pdf-viewer').should('be.visible')
-      cy.get('.cm-editor').should('be.visible')
-
-      cy.findByRole('button', { name: 'Layout' }).click()
-      cy.findByRole('menu').within(() => {
-        cy.findByRole('menuitem', { name: 'PDF in separate tab' }).click()
-      })
-
-      cy.findByTestId('pdf-viewer').should('not.exist')
-      cy.get('.cm-editor').should('be.visible')
-    })
-  })
-
-  describe('full project search', function () {
-    it('can search for text in project files', function () {
-      cy.get('button').contains('Search').click({ force: true })
-      cy.findByRole('searchbox', { name: 'Search' })
-        .should('be.visible')
-        .type('Some examples to get started')
-      cy.get('button').contains('Search').click()
-
-      cy.findByRole('listbox').within(() => {
-        cy.findByRole('option', {
-          name: /Some examples to get started/,
-        }).should('be.visible')
-      })
-    })
-  })
-})
-
-function createRandomLetterString() {
-  const chars = 'abcdefghijklmnopqrstuvwxyz'
-  let result = ''
-  for (let i = 0; i < 12; i++) {
-    result += chars.charAt(Math.floor(Math.random() * chars.length))
-  }
-  return result
-}

+ 0 - 492
server-ce/test/git-bridge.spec.ts

@@ -1,492 +0,0 @@
-import { v4 as uuid } from 'uuid'
-import { isExcludedBySharding, startWith } from './helpers/config'
-import { ensureUserExists, login } from './helpers/login'
-import {
-  createProject,
-  enableLinkSharing,
-  openProjectByName,
-  openProjectViaLinkSharingAsUser,
-  shareProjectByEmailAndAcceptInviteViaDash,
-} from './helpers/project'
-
-import git from 'isomorphic-git'
-import http from 'isomorphic-git/http/web'
-import LightningFS from '@isomorphic-git/lightning-fs'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-
-const USER = 'user@example.com'
-
-describe('git-bridge', function () {
-  const ENABLED_VARS = {
-    GIT_BRIDGE_ENABLED: 'true',
-    GIT_BRIDGE_HOST: 'git-bridge',
-    GIT_BRIDGE_PORT: '8000',
-    V1_HISTORY_URL: 'http://sharelatex:3100/api',
-  }
-
-  function gitURL(projectId: string) {
-    const url = new URL(Cypress.config().baseUrl!)
-    url.username = 'git'
-    url.pathname = `/git/${projectId}`
-    return url
-  }
-
-  describe('enabled in Server Pro', function () {
-    if (isExcludedBySharding('PRO_CUSTOM_1')) return
-    startWith({
-      pro: true,
-      vars: ENABLED_VARS,
-    })
-    ensureUserExists({ email: USER })
-
-    function clearAllTokens() {
-      cy.findAllByRole('button', { name: 'Remove' })
-        .not('[disabled]')
-        .each($button => {
-          cy.wrap($button).click()
-          cy.findByRole('button', { name: 'Delete token' }).click()
-        })
-      cy.findByRole('dialog').should('not.exist')
-    }
-
-    function maybeClearAllTokens() {
-      cy.visit('/user/settings')
-      cy.findByRole('heading', { name: 'Git integration' })
-      cy.findByRole('button', {
-        name: /Generate token|Add another token/i,
-      }).then(btn => {
-        if (btn.text() === 'Add another token') {
-          clearAllTokens()
-        }
-      })
-    }
-
-    beforeEach(function () {
-      login(USER)
-    })
-
-    it('should render the git-bridge UI in the settings', function () {
-      maybeClearAllTokens()
-      cy.visit('/user/settings')
-      cy.findByRole('heading', { name: 'Git integration' })
-      cy.findByRole('button', {
-        name: 'Git integration Generate token',
-      }).click()
-      cy.findByRole('dialog').within(() => {
-        cy.findByLabelText('Git authentication token')
-          .contains(/olp_[a-zA-Z0-9]{16}/)
-          .then(el => el.text())
-          .as('newToken')
-        cy.findByRole('button', { name: 'Close dialog' }).click()
-      })
-      cy.get('@newToken').then(token => {
-        // There can be more than one token with the same prefix when retrying
-        cy.findAllByText(
-          `${token.slice(0, 'olp_1234'.length)}${'*'.repeat(12)}`
-        ).should('have.length.at.least', 1)
-      })
-      cy.findByRole('button', {
-        name: 'Git integration Generate token',
-      }).should('not.exist')
-      cy.findByRole('button', { name: 'Add another token' }).should('exist')
-      clearAllTokens()
-      cy.findByRole('button', {
-        name: 'Git integration Generate token',
-      }).should('exist')
-      cy.findByRole('button', { name: 'Add another token' }).should('not.exist')
-    })
-
-    it('should render the git-bridge UI in the editor', function () {
-      maybeClearAllTokens()
-      createProject('git').as('projectId')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByTestId('left-menu').within(() => {
-        cy.findByRole('heading', { name: 'Sync' })
-        cy.findByRole('button', { name: 'Git' }).click()
-      })
-      cy.findByTestId('git-bridge-modal').within(() => {
-        cy.get('@projectId').then(id => {
-          cy.findByLabelText('Git clone project command').contains(
-            `git clone ${gitURL(id.toString())}`
-          )
-        })
-        cy.findByRole('button', {
-          name: 'Generate token',
-        }).click()
-        cy.findByLabelText('Git authentication token').contains(
-          /olp_[a-zA-Z0-9]{16}/
-        )
-      })
-
-      // Re-open
-      cy.url().then(url => cy.visit(url))
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByTestId('left-menu').within(() => {
-        cy.findByRole('button', { name: 'Git' }).click()
-      })
-
-      cy.findByTestId('git-bridge-modal').within(() => {
-        cy.get('@projectId').then(id => {
-          cy.findByLabelText('Git clone project command').contains(
-            `git clone ${gitURL(id.toString())}`
-          )
-        })
-        cy.findByRole('button', {
-          name: 'Generate token',
-        }).should('not.exist')
-        cy.findByText(/generate a new one in Account settings/)
-        cy.findByRole('link', { name: 'Go to settings' })
-          .should('have.attr', 'target', '_blank')
-          .and('have.attr', 'href', '/user/settings')
-      })
-    })
-
-    describe('git access', function () {
-      ensureUserExists({ email: 'collaborator-rw@example.com' })
-      ensureUserExists({ email: 'collaborator-ro@example.com' })
-      ensureUserExists({ email: 'collaborator-link-rw@example.com' })
-      ensureUserExists({ email: 'collaborator-link-ro@example.com' })
-
-      let projectName: string
-      let recompile: () => void
-      let waitForCompile: (triggerCompile: () => void) => void
-      beforeEach(function () {
-        projectName = uuid()
-        createProject(projectName, { open: false }).as('projectId')
-        ;({ recompile, waitForCompile } = prepareWaitForNextCompileSlot())
-      })
-
-      it('should expose r/w interface to owner', function () {
-        maybeClearAllTokens()
-        waitForCompile(() => {
-          openProjectByName(projectName)
-        })
-        checkGitAccess('readAndWrite')
-      })
-
-      it('should expose r/w interface to invited r/w collaborator', function () {
-        shareProjectByEmailAndAcceptInviteViaDash(
-          projectName,
-          'collaborator-rw@example.com',
-          'Editor'
-        )
-        maybeClearAllTokens()
-        waitForCompile(() => {
-          openProjectByName(projectName)
-        })
-        checkGitAccess('readAndWrite')
-      })
-
-      it('should expose r/o interface to invited r/o collaborator', function () {
-        shareProjectByEmailAndAcceptInviteViaDash(
-          projectName,
-          'collaborator-ro@example.com',
-          'Viewer'
-        )
-        maybeClearAllTokens()
-        waitForCompile(() => {
-          openProjectByName(projectName)
-        })
-        checkGitAccess('readOnly')
-      })
-
-      it('should expose r/w interface to link-sharing r/w collaborator', function () {
-        openProjectByName(projectName)
-        enableLinkSharing().then(({ linkSharingReadAndWrite }) => {
-          const email = 'collaborator-link-rw@example.com'
-          login(email)
-          maybeClearAllTokens()
-          waitForCompile(() => {
-            openProjectViaLinkSharingAsUser(
-              linkSharingReadAndWrite,
-              projectName,
-              email
-            )
-          })
-          checkGitAccess('readAndWrite')
-        })
-      })
-
-      it('should expose r/o interface to link-sharing r/o collaborator', function () {
-        waitForCompile(() => {
-          openProjectByName(projectName)
-        })
-        enableLinkSharing().then(({ linkSharingReadOnly }) => {
-          const email = 'collaborator-link-ro@example.com'
-          login(email)
-          maybeClearAllTokens()
-          waitForCompile(() => {
-            openProjectViaLinkSharingAsUser(
-              linkSharingReadOnly,
-              projectName,
-              email
-            )
-          })
-          checkGitAccess('readOnly')
-        })
-      })
-
-      function checkGitAccess(access: 'readOnly' | 'readAndWrite') {
-        cy.findByRole('navigation', {
-          name: 'Project actions',
-        })
-          .findByRole('button', { name: 'Menu' })
-          .click()
-        cy.findByTestId('left-menu').within(() => {
-          cy.findByRole('heading', { name: 'Sync' })
-          cy.findByRole('button', { name: 'Git' }).click()
-        })
-        cy.get('@projectId').then(projectId => {
-          cy.findByTestId('git-bridge-modal').within(() => {
-            cy.findByLabelText('Git clone project command').contains(
-              `git clone ${gitURL(projectId.toString())}`
-            )
-            cy.findByRole('heading', { name: 'Clone with Git' })
-            cy.findByRole('button', {
-              name: 'Generate token',
-            }).click()
-          })
-          cy.findByLabelText('Git authentication token')
-            .contains(/olp_[a-zA-Z0-9]{16}/)
-            .then(async tokenEl => {
-              const token = tokenEl.text()
-
-              // close Git modal
-              cy.findByRole('button', { name: 'Close dialog' }).click()
-              cy.findByTestId('git-bridge-modal').should('not.exist')
-              // close the modal
-              cy.get('body').type('{esc}')
-              cy.findByTestId('left-menu').should('not.exist')
-              const fs = new LightningFS('fs')
-              const dir = `/${projectId}`
-
-              async function readFile(path: string): Promise<string> {
-                return await new Promise((resolve, reject) => {
-                  fs.readFile(path, { encoding: 'utf8' }, (err, blob) => {
-                    if (err) return reject(err)
-                    resolve(blob as string)
-                  })
-                })
-              }
-
-              async function writeFile(path: string, data: string) {
-                return await new Promise<void>((resolve, reject) => {
-                  fs.writeFile(path, data, undefined, err => {
-                    if (err) return reject(err)
-                    resolve()
-                  })
-                })
-              }
-
-              const commonOptions = {
-                dir,
-                fs,
-              }
-              const url = gitURL(projectId.toString())
-              url.username = '' // basic auth is specified separately.
-              const httpOptions = {
-                http,
-                url: url.toString(),
-                headers: {
-                  Authorization: `Basic ${Buffer.from(`git:${token}`).toString('base64')}`,
-                },
-              }
-              const authorOptions = {
-                author: { name: 'user', email: USER },
-                committer: { name: 'user', email: USER },
-              }
-              const mainTex = `${dir}/main.tex`
-
-              // Clone
-              cy.then({ timeout: 10_000 }, async () => {
-                await git.clone({
-                  ...commonOptions,
-                  ...httpOptions,
-                })
-              })
-              cy.findByText(/\\documentclass/)
-                .parent()
-                .parent()
-                .then(async editor => {
-                  const onDisk = await readFile(mainTex)
-                  expect(onDisk.replaceAll('\n', '')).to.equal(editor.text())
-                })
-              const text = `
-\\documentclass{article}
-\\begin{document}
-Hello world
-\\end{document}
-`
-
-              // Make a change
-              cy.then(async () => {
-                await writeFile(mainTex, text)
-                await git.add({
-                  ...commonOptions,
-                  filepath: 'main.tex',
-                })
-                await git.commit({
-                  ...commonOptions,
-                  ...authorOptions,
-                  message: 'Swap main.tex',
-                })
-              })
-
-              if (access === 'readAndWrite') {
-                // check history before push
-                cy.findByRole('navigation', {
-                  name: 'Project actions',
-                })
-                  .findByRole('button', { name: 'History' })
-                  .click()
-                cy.findByRole('complementary', {
-                  name: 'Project history and labels',
-                }).within(() => {
-                  cy.findByText('(via Git)').should('not.exist')
-                })
-                cy.findByRole('navigation', {
-                  name: 'Project actions',
-                })
-                  .findByRole('button', { name: 'Back to editor' })
-                  .click()
-                cy.then(async () => {
-                  await git.push({
-                    ...commonOptions,
-                    ...httpOptions,
-                  })
-                })
-              } else {
-                cy.then(async () => {
-                  try {
-                    await git.push({
-                      ...commonOptions,
-                      ...httpOptions,
-                    })
-                    expect.fail('push should have failed')
-                  } catch (err) {
-                    expect(err).to.match(/branches were not updated/)
-                    expect(err).to.match(/forbidden/)
-                  }
-                })
-
-                return // return early, below are write access bits
-              }
-
-              // check push in editor
-              cy.findByText(/\\documentclass/)
-                .parent()
-                .parent()
-                .should('have.text', text.replaceAll('\n', ''))
-
-              // Wait for history sync - trigger flush by toggling the UI
-              cy.findByRole('navigation', {
-                name: 'Project actions',
-              }).within(() => {
-                cy.findByRole('button', { name: 'History' }).click()
-                cy.findByRole('button', { name: 'Back to editor' }).click()
-              })
-
-              // check push in history
-              cy.findByRole('navigation', {
-                name: 'Project actions',
-              })
-                .findByRole('button', { name: 'History' })
-                .click()
-              cy.findByText(/Hello world/)
-              cy.findByRole('complementary', {
-                name: 'Project history and labels',
-              }).within(() => {
-                cy.findByText('(via Git)').should('exist')
-              })
-
-              // Back to the editor
-              cy.findByRole('navigation', {
-                name: 'Project actions',
-              })
-                .findByRole('button', { name: 'Back to editor' })
-                .click()
-              cy.findByText(/\\documentclass/)
-                .parent()
-                .parent()
-                .as('documentclass')
-                .click()
-              cy.get('@documentclass').type('% via editor{enter}')
-
-              // Trigger flush via compile
-              recompile()
-
-              // Back into the history, check what we just added
-              cy.findByRole('navigation', {
-                name: 'Project actions',
-              })
-                .findByRole('button', { name: 'History' })
-                .click()
-              cy.findByText(/% via editor/)
-
-              // Pull the change
-              cy.then(async () => {
-                await git.pull({
-                  ...commonOptions,
-                  ...httpOptions,
-                  ...authorOptions,
-                })
-
-                expect(await readFile(mainTex)).to.equal(
-                  text + '% via editor\n'
-                )
-              })
-            })
-        })
-      }
-    })
-  })
-
-  function checkDisabled() {
-    ensureUserExists({ email: USER })
-
-    it('should not render the git-bridge UI in the settings', function () {
-      login(USER)
-      cy.visit('/user/settings')
-      cy.findByRole('heading', { name: 'Git integration' }).should('not.exist')
-    })
-    it('should not render the git-bridge UI in the editor', function () {
-      login(USER)
-      createProject('maybe git')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByTestId('left-menu').within(() => {
-        cy.findByRole('button', { name: 'Word Count' }) // wait for lazy loading
-        cy.findByRole('heading', { name: 'Sync' }).should('not.exist')
-        cy.findByRole('button', { name: 'Git' }).should('not.exist')
-      })
-    })
-  }
-
-  describe('disabled in Server Pro', function () {
-    if (isExcludedBySharding('PRO_DEFAULT_1')) return
-    startWith({
-      pro: true,
-    })
-    checkDisabled()
-  })
-
-  describe('unavailable in CE', function () {
-    if (isExcludedBySharding('CE_CUSTOM_1')) return
-    startWith({
-      pro: false,
-      vars: ENABLED_VARS,
-    })
-    checkDisabled()
-  })
-})

+ 0 - 111
server-ce/test/graceful-shutdown.spec.ts

@@ -1,111 +0,0 @@
-import { ensureUserExists, login } from './helpers/login'
-import {
-  isExcludedBySharding,
-  STARTUP_TIMEOUT,
-  startWith,
-} from './helpers/config'
-import { dockerCompose, getRedisKeys } from './helpers/hostAdminClient'
-import { createProject } from './helpers/project'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-
-const USER = 'user@example.com'
-const PROJECT_NAME = 'Old Project'
-
-function bringServerProBackUp() {
-  cy.log('bring server pro back up')
-  cy.then({ timeout: STARTUP_TIMEOUT }, async () => {
-    await dockerCompose('up', '--detach', '--wait', 'sharelatex')
-  })
-}
-
-describe('GracefulShutdown', function () {
-  if (isExcludedBySharding('PRO_CUSTOM_1')) return
-  startWith({
-    pro: true,
-    withDataDir: true,
-    resetData: true,
-  })
-  ensureUserExists({ email: USER })
-
-  let projectId: string
-  it('should display banner and flush changes out of redis', function () {
-    bringServerProBackUp()
-    login(USER)
-    const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-    waitForCompile(() => {
-      createProject(PROJECT_NAME).then(id => {
-        projectId = id
-      })
-    })
-
-    cy.log('add additional content')
-    cy.findByRole('region', { name: 'Editor' }).within(() => {
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle').parent().type(`\n\\section{{}New Section}`)
-    })
-    recompile()
-
-    cy.log(
-      'check flush from frontend to backend: should include new section in PDF'
-    )
-    cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-      'contain.text',
-      'New Section'
-    )
-
-    cy.log('should have unflushed content in redis before shutdown')
-    cy.then(async () => {
-      const keys = await getRedisKeys()
-      expect(keys).to.contain(`DocsIn:${projectId}`)
-      expect(keys).to.contain(`ProjectHistory:Ops:{${projectId}}`)
-    })
-
-    cy.log('trigger graceful shutdown')
-    let pendingShutdown: Promise<any>
-    cy.then(() => {
-      pendingShutdown = dockerCompose('stop', '--timeout=60', 'sharelatex')
-    })
-
-    cy.log('wait for banner')
-    cy.findByRole('dialog').findByText(/performing maintenance/)
-    cy.log('wait for page reload')
-    cy.findByRole('heading', { name: 'Maintenance' })
-    cy.findByText(/is currently down for maintenance/)
-
-    cy.log('wait for shutdown to complete')
-    cy.then({ timeout: 60 * 1000 }, async () => {
-      await pendingShutdown
-    })
-
-    cy.log('should not have any unflushed content in redis after shutdown')
-    cy.then(async () => {
-      const keys = await getRedisKeys()
-      expect(keys).to.not.contain(`DocsIn:${projectId}`)
-      expect(keys).to.not.contain(`ProjectHistory:Ops:{${projectId}}`)
-    })
-
-    bringServerProBackUp()
-
-    cy.then(() => {
-      cy.visit(
-        `/project/${projectId}?trick-cypress-into-page-reload=true&old-editor-override=true`
-      )
-    })
-
-    cy.log('check loading doc from mongo')
-    cy.findByRole('region', { name: 'Editor' }).findByText('New Section')
-
-    cy.log('check PDF')
-    cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-      'contain.text',
-      'New Section'
-    )
-    cy.log('check history')
-    cy.findByRole('navigation', {
-      name: 'Project actions',
-    })
-      .findByRole('button', { name: 'History' })
-      .click()
-    cy.findByText(/\\section\{New Section}/)
-  })
-})

+ 0 - 164
server-ce/test/history.spec.ts

@@ -1,164 +0,0 @@
-import { createProject } from './helpers/project'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-import { ensureUserExists, login } from './helpers/login'
-import { isExcludedBySharding, startWith } from './helpers/config'
-
-describe('History', function () {
-  if (isExcludedBySharding('CE_DEFAULT')) return
-  startWith({})
-  ensureUserExists({ email: 'user@example.com' })
-  beforeEach(function () {
-    login('user@example.com')
-  })
-
-  function addLabel(name: string) {
-    cy.log(`add label ${JSON.stringify(name)}`)
-    // The input is not clickable due to being visually hidden, click its label instead
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      cy.findByRole('group', {
-        name: 'Show all of the project history or only labelled versions.',
-      }).within(() => {
-        cy.findByText('Labels').click()
-      })
-      cy.findByRole('radio', { name: 'Labels' }).should('be.checked')
-      cy.findByRole('radio', { name: 'All history' }).should('not.be.checked')
-      cy.findAllByTestId('history-version-details')
-        .first()
-        .within(() => {
-          cy.findByRole('button', { name: 'More actions' }).click()
-          cy.findByRole('menuitem', { name: 'Label this version' }).click()
-        })
-    })
-    cy.findByRole('dialog').within(() => {
-      cy.findByLabelText('New label name').type(`${name}{enter}`)
-    })
-  }
-
-  function downloadVersion(name: string) {
-    cy.log(`download version ${JSON.stringify(name)}`)
-    // The input is not clickable due to being visually hidden, click its label instead
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      cy.findByRole('group', {
-        name: 'Show all of the project history or only labelled versions.',
-      }).within(() => {
-        cy.findByText('Labels').click()
-      })
-      cy.findByRole('radio', { name: 'Labels' }).should('be.checked')
-      cy.findByRole('radio', { name: 'All history' }).should('not.be.checked')
-      cy.findByText(name)
-        .closest('[data-testid="history-version-details"]')
-        .within(() => {
-          cy.findByRole('button', { name: 'More actions' }).click()
-          cy.findByRole('menuitem', { name: 'Download this version' }).click()
-        })
-    })
-  }
-
-  const CLASS_ADDITION = 'ol-cm-addition-marker'
-  const CLASS_DELETION = 'ol-cm-deletion-marker'
-
-  it('should support labels, comparison and download', function () {
-    const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-    waitForCompile(() => {
-      createProject('labels')
-    })
-
-    cy.log('add content, including a line that will get removed soon')
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() => {
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle').parent().type('\n% added')
-      cy.findByText('\\maketitle').parent().type('\n% to be removed')
-    })
-    recompile()
-    cy.findByRole('button', { name: 'History' }).click()
-
-    cy.log('expect to see additions in history')
-    cy.get('.document-diff-container').within(() => {
-      cy.findByText('% to be removed').should('have.class', CLASS_ADDITION)
-      cy.findByText('% added').should('have.class', CLASS_ADDITION)
-    })
-
-    addLabel('Before removal')
-
-    cy.log('remove content')
-    cy.findByRole('button', { name: 'Back to editor' }).click()
-    cy.findByText('% to be removed').parent().type('{end}{shift}{upArrow}{del}')
-    recompile()
-    cy.findByRole('button', { name: 'History' }).click()
-
-    cy.log('expect to see annotation for newly removed content in history')
-    cy.get('.document-diff-container').within(() => {
-      cy.findByText('% to be removed').should('have.class', CLASS_DELETION)
-      cy.findByText('% added').should('not.have.class', CLASS_ADDITION)
-    })
-
-    addLabel('After removal')
-
-    cy.log('add more content after labeling')
-    cy.findByRole('button', { name: 'Back to editor' }).click()
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() => {
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle').parent().type('\n% more')
-    })
-    recompile()
-
-    cy.log('compare non current versions')
-    cy.findByRole('button', { name: 'History' }).click()
-    // The input is not clickable due to being visually hidden, click its label instead
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      cy.findByRole('group', {
-        name: 'Show all of the project history or only labelled versions.',
-      }).within(() => {
-        cy.findByText('Labels').click()
-      })
-      cy.findByRole('radio', { name: 'Labels' }).should('be.checked')
-      cy.findByRole('radio', { name: 'All history' }).should('not.be.checked')
-      cy.findAllByTestId('compare-icon-version').last().click()
-      cy.findAllByTestId('compare-icon-version').filter(':visible').click()
-      cy.findByRole('menuitem', {
-        name: 'Compare up to this version',
-      }).click()
-    })
-    cy.log(
-      'expect to see annotation for removed content between the two versions'
-    )
-    cy.get('.document-diff-container').within(() => {
-      cy.findByText('% to be removed').should('have.class', CLASS_DELETION)
-      cy.findByText('% added').should('not.have.class', CLASS_ADDITION)
-      cy.findByText('% more').should('not.exist')
-    })
-
-    downloadVersion('Before removal')
-    cy.task('readFileInZip', {
-      pathToZip: `cypress/downloads/labels (Version 2).zip`,
-      fileToRead: 'main.tex',
-    })
-      .should('contain', '% added')
-      .should('contain', '% to be removed')
-      .should('not.contain', '% more')
-
-    downloadVersion('After removal')
-    cy.task('readFileInZip', {
-      pathToZip: `cypress/downloads/labels (Version 3).zip`,
-      fileToRead: 'main.tex',
-    })
-      .should('contain', '% added')
-      .should('not.contain', '% to be removed')
-      .should('not.contain', '% more')
-
-    downloadVersion('Current state')
-    cy.task('readFileInZip', {
-      pathToZip: `cypress/downloads/labels (Version 4).zip`,
-      fileToRead: 'main.tex',
-    })
-      .should('contain', '% added')
-      .should('not.contain', '% to be removed')
-      .should('contain', '% more')
-  })
-})

+ 0 - 518
server-ce/test/project-sharing.spec.ts

@@ -1,518 +0,0 @@
-import { v4 as uuid } from 'uuid'
-import {
-  isExcludedBySharding,
-  startWith,
-  reloadWith,
-  STARTUP_TIMEOUT,
-} from './helpers/config'
-import { ensureUserExists, login } from './helpers/login'
-import {
-  createProject,
-  enableLinkSharing,
-  getSpamSafeProjectName,
-  openProjectByName,
-  openProjectViaLinkSharingAsAnon,
-  openProjectViaLinkSharingAsUser,
-  shareProjectByEmailAndAcceptInviteViaDash,
-  shareProjectByEmailAndAcceptInviteViaEmail,
-} from './helpers/project'
-import { prepareWaitForNextCompileSlot } from './helpers/compile'
-import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
-
-describe('Project Sharing', function () {
-  if (isExcludedBySharding('PRO_CUSTOM_4')) return
-  ensureUserExists({ email: 'user@example.com' })
-  startWith({ withDataDir: true, pro: true })
-
-  let projectName: string
-  let recompile: () => void
-  let waitForCompile: (triggerCompile: () => void) => void
-  beforeWithReRunOnTestRetry(() => {
-    projectName = getSpamSafeProjectName()
-    ;({ recompile, waitForCompile } = prepareWaitForNextCompileSlot())
-    setupTestProject()
-  })
-
-  beforeEach(function () {
-    // Always start with a fresh session
-    cy.session([uuid()], () => {})
-  })
-
-  let linkSharingReadOnly: string
-  let linkSharingReadAndWrite: string
-
-  function setupTestProject() {
-    login('user@example.com')
-    waitForCompile(() => {
-      createProject(projectName)
-    })
-
-    // Add chat message
-    cy.findByRole('button', { name: 'Chat' }).click()
-    // wait for lazy loading of the chat pane
-    cy.findByRole('complementary', { name: 'Chat' }).findByText(
-      'Send your first message to your collaborators'
-    )
-    cy.findByLabelText('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.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      '\\maketitle'
-    )
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'have.attr',
-      'contenteditable',
-      'false'
-    )
-  }
-
-  function expectContentWriteAccess() {
-    const section = `Test Section ${uuid()}`
-    cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
-    // wait for the editor to finish loading
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      '\\maketitle'
-    )
-    // the editor should be writable
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'have.attr',
-      'contenteditable',
-      'true'
-    )
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() => {
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle').parent().type(`\n\\section{{}${section}}`)
-    })
-    // should have written
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      `\\section{${section}}`
-    )
-    // check PDF
-    recompile()
-    cy.findByRole('region', { name: 'PDF preview and logs' }).within(() => {
-      cy.findByLabelText(/Page.*1/i).should('be.visible')
-      cy.findByText(projectName).should('be.visible')
-    })
-    cy.findByRole('region', { name: 'PDF preview and logs' }).within(() => {
-      cy.findByLabelText(/Page.*1/i).should('be.visible')
-      cy.contains(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.findByRole('button', { name: 'Chat' }).click()
-    cy.findByRole('complementary', { name: 'Chat' }).findByText(
-      'New Chat Message'
-    )
-  }
-
-  function expectHistoryAccess() {
-    cy.findByRole('button', { name: 'History' }).click()
-    // The input is not clickable due to being visually hidden, click its label instead
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      cy.findByRole('group', {
-        name: 'Show all of the project history or only labelled versions.',
-      }).within(() => {
-        cy.findByText('All history').click()
-      })
-      cy.findByRole('radio', { name: 'Labels' }).should('not.be.checked')
-      cy.findByRole('radio', { name: 'All history' }).should('be.checked')
-    })
-    cy.findByText(/\\begin\{document}/)
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      cy.findAllByTestId('history-version-metadata-users')
-        .last()
-        .should('have.text', 'user')
-    })
-    cy.findByRole('button', { name: 'Back to editor' }).click()
-  }
-
-  function expectNoChatAccess() {
-    cy.findByRole('button', { name: 'Layout' }) // wait for lazy loading
-    cy.findByRole('button', { name: 'Chat' }).should('not.exist')
-  }
-
-  function expectNoHistoryAccess() {
-    cy.findByRole('button', { name: 'Layout' }) // wait for lazy loading
-    cy.findByRole('button', { name: 'History' }).should('not.exist')
-  }
-
-  function expectCommentAccess() {
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      '\\maketitle'
-    )
-
-    cy.findByText('\\maketitle').parent().dblclick()
-
-    cy.findByRole('button', { name: 'Add comment' }).should('be.visible')
-
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).click()
-  }
-
-  function expectNoCommentAccess() {
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).should(
-      'contain.text',
-      '\\maketitle'
-    )
-
-    cy.findByText('\\maketitle').parent().dblclick()
-
-    cy.findByRole('button', { name: 'Add comment' }).should('not.exist')
-    cy.findByRole('textbox', { name: 'Source Editor editing' }).click()
-  }
-
-  function expectFullReadOnlyAccess() {
-    expectContentReadOnlyAccess()
-    expectChatAccess()
-    expectHistoryAccess()
-    expectNoCommentAccess()
-  }
-
-  function expectRestrictedReadOnlyAccess() {
-    expectContentReadOnlyAccess()
-    expectNoChatAccess()
-    expectNoHistoryAccess()
-    expectNoCommentAccess()
-  }
-
-  function expectFullReadAndWriteAccess() {
-    expectContentWriteAccess()
-    expectChatAccess()
-    expectHistoryAccess()
-    expectCommentAccess()
-  }
-
-  function expectAnonymousReadAndWriteAccess() {
-    expectContentWriteAccess()
-    expectChatAccess()
-    expectHistoryAccess()
-    expectNoCommentAccess()
-  }
-
-  function expectProjectDashboardEntry() {
-    cy.visit('/project')
-    cy.findByText(projectName)
-  }
-
-  function expectEditAuthoredAs(author: string) {
-    cy.findByRole('button', { name: 'History' }).click()
-    cy.findByRole('complementary', {
-      name: 'Project history and labels',
-    }).within(() => {
-      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, 'Viewer')
-    })
-
-    it('should grant the collaborator read access', function () {
-      expectFullReadOnlyAccess()
-      expectProjectDashboardEntry()
-    })
-  })
-
-  describe('read only', function () {
-    const email = 'collaborator-ro@example.com'
-    ensureUserExists({ email })
-
-    beforeWithReRunOnTestRetry(() => {
-      login('user@example.com')
-      shareProjectByEmailAndAcceptInviteViaDash(projectName, email, 'Viewer')
-    })
-
-    it('should grant the collaborator read access', function () {
-      login(email)
-      openProjectByName(projectName)
-      expectFullReadOnlyAccess()
-      expectProjectDashboardEntry()
-    })
-  })
-
-  describe('read and write', function () {
-    const email = 'collaborator-rw@example.com'
-    ensureUserExists({ email })
-
-    beforeWithReRunOnTestRetry(() => {
-      login('user@example.com')
-      shareProjectByEmailAndAcceptInviteViaDash(projectName, email, 'Editor')
-    })
-
-    it('should grant the collaborator write access', function () {
-      login(email)
-      openProjectByName(projectName)
-      expectFullReadAndWriteAccess()
-      expectEditAuthoredAs('You')
-      expectProjectDashboardEntry()
-    })
-  })
-
-  describe('token access', function () {
-    describe('logged in', function () {
-      describe('read only', function () {
-        const email = 'collaborator-link-ro@example.com'
-        ensureUserExists({ email })
-
-        it('should grant restricted read access', function () {
-          login(email)
-          openProjectViaLinkSharingAsUser(
-            linkSharingReadOnly,
-            projectName,
-            email
-          )
-          expectRestrictedReadOnlyAccess()
-          expectProjectDashboardEntry()
-        })
-      })
-
-      describe('read and write', function () {
-        const email = 'collaborator-link-rw@example.com'
-        ensureUserExists({ email })
-
-        it('should grant full write access', function () {
-          login(email)
-          openProjectViaLinkSharingAsUser(
-            linkSharingReadAndWrite,
-            projectName,
-            email
-          )
-          expectFullReadAndWriteAccess()
-          expectEditAuthoredAs('You')
-          expectProjectDashboardEntry()
-        })
-      })
-    })
-
-    describe('with OVERLEAF_ALLOW_PUBLIC_ACCESS=false', function () {
-      describe('wrap startup', function () {
-        startWith({
-          pro: true,
-          vars: {
-            OVERLEAF_ALLOW_PUBLIC_ACCESS: 'false',
-          },
-          withDataDir: true,
-        })
-        it('should block access', function () {
-          expectNoAccess()
-        })
-      })
-
-      describe('with OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING=true', function () {
-        startWith({
-          pro: true,
-          vars: {
-            OVERLEAF_ALLOW_PUBLIC_ACCESS: 'false',
-            OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true',
-          },
-          withDataDir: true,
-        })
-        it('should block access', function () {
-          expectNoAccess()
-        })
-      })
-    })
-
-    describe('with OVERLEAF_ALLOW_PUBLIC_ACCESS=true', function () {
-      describe('wrap startup', function () {
-        startWith({
-          pro: true,
-          vars: {
-            OVERLEAF_ALLOW_PUBLIC_ACCESS: 'true',
-          },
-          withDataDir: true,
-        })
-        it('should grant read access with read link', function () {
-          openProjectViaLinkSharingAsAnon(linkSharingReadOnly)
-          expectRestrictedReadOnlyAccess()
-        })
-
-        it('should prompt for login with write link', function () {
-          cy.visit(linkSharingReadAndWrite)
-          cy.url().should('match', /\/login/)
-        })
-      })
-
-      describe('with OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING=true', function () {
-        startWith({
-          pro: true,
-          vars: {
-            OVERLEAF_ALLOW_PUBLIC_ACCESS: 'true',
-            OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true',
-          },
-          withDataDir: true,
-        })
-
-        it('should grant read access with read link', function () {
-          openProjectViaLinkSharingAsAnon(linkSharingReadOnly)
-          expectRestrictedReadOnlyAccess()
-        })
-
-        it('should grant write access with write link', function () {
-          openProjectViaLinkSharingAsAnon(linkSharingReadAndWrite)
-          expectAnonymousReadAndWriteAccess()
-          expectEditAuthoredAs('Anonymous')
-        })
-      })
-    })
-
-    describe('with OVERLEAF_DISABLE_LINK_SHARING=true', function () {
-      const email = 'collaborator-email@example.com'
-      ensureUserExists({ email })
-
-      const invitedEmail = 'invited-email@example.com'
-      ensureUserExists({ email: invitedEmail })
-
-      const retainedViewerEmail = 'collaborator-retained-viewer@example.com'
-      ensureUserExists({ email: retainedViewerEmail })
-
-      const retainedEditorEmail = 'collaborator-retained-editor@example.com'
-      ensureUserExists({ email: retainedEditorEmail })
-
-      // Link-sharing urls have to be created before disabling link sharing.
-      // We use the `beforeEach` hook to reload the server with link sharing
-      // disabled **after** the initial setup which happens in the `before`
-      // block. The `before` hook always runs prior to the `beforeEach` hook.
-
-      // Set up retained access before disabling link sharing
-      before(function () {
-        // Set up retained viewer access
-        login(retainedViewerEmail)
-        openProjectViaLinkSharingAsUser(
-          linkSharingReadOnly,
-          projectName,
-          retainedViewerEmail
-        )
-
-        // Set up retained editor access
-        login(retainedEditorEmail)
-        openProjectViaLinkSharingAsUser(
-          linkSharingReadAndWrite,
-          projectName,
-          retainedEditorEmail
-        )
-      })
-
-      beforeEach(function () {
-        this.timeout(STARTUP_TIMEOUT) // Increase timeout for server reload
-
-        return cy.wrap(
-          reloadWith({
-            pro: true,
-            vars: {
-              OVERLEAF_ALLOW_PUBLIC_ACCESS: 'true',
-              OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true',
-              OVERLEAF_DISABLE_LINK_SHARING: 'true',
-            },
-            withDataDir: true,
-          }),
-          { timeout: STARTUP_TIMEOUT }
-        )
-      })
-
-      it('should not display link sharing in the sharing modal', function () {
-        login('user@example.com')
-        openProjectByName(projectName)
-        cy.findByRole('navigation', {
-          name: 'Project actions',
-        })
-          .findByRole('button', { name: 'Share' })
-          .click()
-        cy.findByRole('button', { name: 'Turn on link sharing' }).should(
-          'not.exist'
-        )
-      })
-
-      it('should block new access to read-only link shared projects', function () {
-        login(email)
-
-        // Test read-only link returns 404
-        cy.request({
-          url: linkSharingReadOnly,
-          failOnStatusCode: false,
-        }).then(response => {
-          expect(response.status).to.eq(404)
-        })
-      })
-
-      it('should block new access to read-write link shared projects', function () {
-        login(email)
-
-        // Test read-write link returns 404
-        cy.request({
-          url: linkSharingReadAndWrite,
-          failOnStatusCode: false,
-        }).then(response => {
-          expect(response.status).to.eq(404)
-        })
-      })
-
-      it('should continue to allow email sharing', function () {
-        login('user@example.com')
-        shareProjectByEmailAndAcceptInviteViaEmail(
-          projectName,
-          invitedEmail,
-          'Viewer'
-        )
-        expectFullReadOnlyAccess()
-        expectProjectDashboardEntry()
-      })
-
-      it('should retain read-only access when project was joined via link before link sharing was turned off', function () {
-        login(retainedViewerEmail)
-        openProjectByName(projectName)
-        expectRestrictedReadOnlyAccess()
-        expectProjectDashboardEntry()
-      })
-
-      it('should retain read-write access when project was joined via link before link sharing was turned off', function () {
-        login(retainedEditorEmail)
-        openProjectByName(projectName)
-        expectFullReadAndWriteAccess()
-        expectProjectDashboardEntry()
-      })
-    })
-  })
-})

+ 0 - 352
server-ce/test/sandboxed-compiles.spec.ts

@@ -1,352 +0,0 @@
-import { ensureUserExists, login } from './helpers/login'
-import { createProject } from './helpers/project'
-import { isExcludedBySharding, startWith } from './helpers/config'
-import { prepareWaitForNextCompileSlot, stopCompile } from './helpers/compile'
-import { v4 as uuid } from 'uuid'
-import { waitUntilScrollingFinished } from './helpers/waitUntilScrollingFinished'
-
-const LABEL_TEX_LIVE_VERSION = 'TeX Live version'
-
-describe('SandboxedCompiles', function () {
-  const enabledVars = {
-    SANDBOXED_COMPILES: 'true',
-    ALL_TEX_LIVE_DOCKER_IMAGE_NAMES: '2023,2022',
-  }
-
-  describe('enabled in Server Pro', function () {
-    if (isExcludedBySharding('PRO_CUSTOM_2')) return
-    startWith({
-      pro: true,
-      vars: enabledVars,
-      resetData: true,
-    })
-    ensureUserExists({ email: 'user@example.com' })
-    beforeEach(function () {
-      login('user@example.com')
-    })
-
-    it('should offer TexLive images and switch the compiler', function () {
-      const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-      waitForCompile(() => {
-        createProject('sandboxed')
-      })
-      cy.log('wait for compile')
-      cy.findByRole('region', { name: 'PDF preview and logs' }).should(
-        'contain.text',
-        'sandboxed'
-      )
-
-      cy.log('Check which compiler version was used, expect 2023')
-      cy.findByRole('button', { name: 'View logs' }).click()
-      cy.findByLabelText('Raw logs from the LaTeX compiler').findByText(
-        /This is pdfTeX, Version .+ \(TeX Live 2023\) /
-      )
-
-      cy.log('Switch TeXLive version from 2023 to 2022')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('dialog').within(() => {
-        cy.findByRole('option', { name: '2023' }).should('be.selected')
-        cy.findByRole('combobox', { name: LABEL_TEX_LIVE_VERSION }).select(
-          '2022'
-        )
-      })
-      cy.get('body').type('{esc}')
-      cy.findByRole('dialog').should('not.exist')
-      cy.log('Trigger compile with other TeX Live version')
-      recompile()
-
-      cy.log('Check which compiler version was used, expect 2022')
-      cy.findByRole('button', { name: 'View logs' }).click()
-      cy.findByLabelText('Raw logs from the LaTeX compiler').findByText(
-        /This is pdfTeX, Version .+ \(TeX Live 2022\) /
-      )
-    })
-
-    checkSyncTeX()
-    checkXeTeX()
-    checkRecompilesAfterErrors()
-    checkStopCompile()
-  })
-
-  function checkStopCompile() {
-    it('users can stop a running compile', function () {
-      login('user@example.com')
-      const { recompile, waitForCompile, waitForCompileRateLimitCoolOff } =
-        prepareWaitForNextCompileSlot()
-      waitForCompile(() => {
-        createProject('test-project')
-      })
-      // create an infinite loop in the main document
-      // this will cause the compile to run indefinitely
-      cy.findByText('\\maketitle').parent().click()
-      cy.findByText('\\maketitle')
-        .parent()
-        .type('\n\\def\\x{{}Hello!\\par\\x}\\x')
-      waitForCompileRateLimitCoolOff()
-      cy.log('Start compile')
-      // We need to start the compile manually because we do not want to wait for it to finish
-      cy.findByRole('button', { name: 'Recompile' }).click()
-      // Now stop the compile and kill the latex process
-      stopCompile({ delay: 1000 })
-      cy.findByRole('region', { name: 'PDF preview and logs' })
-        .invoke('text')
-        .should('match', /PDF Rendering Error|Compilation cancelled/)
-      // Check that the previous compile is not running in the background by
-      // disabling the infinite loop and recompiling
-      cy.findByText('\\def').parent().click()
-      cy.findByText('\\def').parent().type('{home}disabled loop% ')
-      recompile()
-      cy.findByRole('region', { name: 'PDF preview and logs' })
-        .should('contain.text', 'disabled loop')
-        .should('not.contain.text', 'A previous compile is still running')
-    })
-  }
-
-  function checkSyncTeX() {
-    describe('SyncTeX', function () {
-      let projectName: string
-      beforeEach(function () {
-        projectName = `Project ${uuid()}`
-        const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-        waitForCompile(() => {
-          createProject(projectName)
-        })
-        cy.findByRole('textbox', { name: 'Source Editor editing' }).within(
-          () => {
-            cy.findByText('\\maketitle').parent().click()
-            cy.findByText('\\maketitle')
-              .parent()
-              .type(
-                `\n\\pagebreak\n\\section{{}Section A}\n\\pagebreak\n\\section{{}Section B}\n\\pagebreak`
-              )
-          }
-        )
-        recompile()
-        cy.log('wait for pdf-rendering')
-        cy.findByRole('region', { name: 'PDF preview and logs' }).findByText(
-          projectName
-        )
-      })
-
-      it('should sync to code', function () {
-        cy.log('navigate to \\maketitle using double click in PDF')
-        cy.findByRole('region', { name: 'PDF preview and logs' })
-          .findByText(projectName)
-          .dblclick()
-        cy.get('.cm-activeLine').should('have.text', '\\maketitle')
-
-        cy.log('navigate to Section A using double click in PDF')
-        cy.findByRole('region', { name: 'PDF preview and logs' })
-          .findByText('Section A')
-          .dblclick()
-        cy.get('.cm-activeLine').should('have.text', '\\section{Section A}')
-
-        cy.log('navigate to Section B using arrow button')
-        cy.findByTestId('pdfjs-viewer-inner')
-          .should('have.prop', 'scrollTop')
-          .as('start')
-        cy.findByRole('region', { name: 'PDF preview and logs' })
-          .findByText('Section B')
-          .scrollIntoView()
-        cy.get('@start').then((start: any) => {
-          waitUntilScrollingFinished(
-            '[data-testid="pdfjs-viewer-inner"]',
-            start
-          )
-        })
-        // The sync button is swapped as the position in the PDF changes.
-        // Cypress appears to click on a button that references a stale position.
-        // Adding a cy.wait() statement is the most reliable "fix" so far :/
-        // eslint-disable-next-line cypress/no-unnecessary-waiting
-        cy.wait(1000)
-        cy.findByRole('button', {
-          name: 'Go to PDF location in code (Tip: double click on the PDF for best results)',
-        }).click()
-        cy.get('.cm-activeLine').should('have.text', '\\section{Section B}')
-      })
-
-      it('should sync to pdf', function () {
-        cy.log('zoom in')
-        cy.findByRole('button', { name: 'PDF zoom level' }).click()
-        cy.findByRole('menuitem', { name: '400%' }).click()
-        cy.log('scroll to top')
-        cy.findByTestId('pdfjs-viewer-inner').scrollTo('top')
-        waitUntilScrollingFinished('[data-testid="pdfjs-viewer-inner"]', -1).as(
-          'start'
-        )
-
-        cy.log('navigate to title')
-        cy.findByRole('textbox', { name: 'Source Editor editing' }).within(
-          () => {
-            cy.findByText('\\maketitle').parent().click()
-          }
-        )
-        cy.findByRole('button', { name: 'Go to code location in PDF' }).click()
-        cy.get('@start').then((start: any) => {
-          waitUntilScrollingFinished(
-            '[data-testid="pdfjs-viewer-inner"]',
-            start
-          )
-            .as('title')
-            .should('be.greaterThan', start)
-        })
-
-        cy.log('navigate to Section A')
-        cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() =>
-          cy.findByText('Section A').click()
-        )
-        cy.findByRole('button', { name: 'Go to code location in PDF' }).click()
-        cy.get('@title').then((title: any) => {
-          waitUntilScrollingFinished(
-            '[data-testid="pdfjs-viewer-inner"]',
-            title
-          )
-            .as('sectionA')
-            .should('be.greaterThan', title)
-        })
-
-        cy.log('navigate to Section B')
-        cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() =>
-          cy.findByText('Section B').click()
-        )
-        cy.findByRole('button', { name: 'Go to code location in PDF' }).click()
-        cy.get('@sectionA').then((title: any) => {
-          waitUntilScrollingFinished(
-            '[data-testid="pdfjs-viewer-inner"]',
-            title
-          )
-            .as('sectionB')
-            .should('be.greaterThan', title)
-        })
-      })
-    })
-  }
-
-  function checkRecompilesAfterErrors() {
-    it('recompiles even if there are Latex errors', function () {
-      login('user@example.com')
-      const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-      waitForCompile(() => {
-        createProject('test-project')
-      })
-      cy.findByRole('textbox', { name: 'Source Editor editing' }).within(() => {
-        cy.findByText('\\maketitle').parent().click()
-        cy.findByText('\\maketitle')
-          .parent()
-          .type('\n\\fakeCommand{} \n\\section{{}Test Section}')
-      })
-      recompile()
-      recompile()
-      cy.findByRole('region', { name: 'PDF preview and logs' })
-        .findByText('Test Section')
-        .should('not.contain.text', 'No PDF')
-    })
-  }
-
-  function checkXeTeX() {
-    it('should be able to use XeLaTeX', function () {
-      const { recompile, waitForCompile } = prepareWaitForNextCompileSlot()
-      waitForCompile(() => {
-        createProject('XeLaTeX')
-      })
-      cy.log('wait for compile')
-      cy.findByRole('region', { name: 'PDF preview and logs' }).findByText(
-        'XeLaTeX'
-      )
-      cy.log('Check which compiler was used, expect pdfLaTeX')
-      cy.findByRole('button', { name: 'View logs' }).click()
-      cy.findByLabelText('Raw logs from the LaTeX compiler').findByText(
-        /This is pdfTeX/
-      )
-
-      cy.log('Switch compiler to from pdfLaTeX to XeLaTeX')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('dialog').within(() => {
-        cy.findByRole('option', { name: 'pdfLaTeX' }).should('be.selected')
-        cy.findByRole('combobox', { name: 'Compiler' }).select('XeLaTeX')
-      })
-      cy.get('body').type('{esc}')
-      cy.findByRole('dialog').should('not.exist')
-
-      cy.log('Trigger compile with other compiler')
-      recompile()
-
-      cy.log('Check which compiler was used, expect XeLaTeX')
-      cy.findByRole('button', { name: 'View logs' }).click()
-      cy.findByLabelText('Raw logs from the LaTeX compiler').findByText(
-        /This is XeTeX/
-      )
-    })
-  }
-
-  function checkUsesDefaultCompiler() {
-    beforeEach(function () {
-      login('user@example.com')
-    })
-
-    it('should not offer TexLive images and use default compiler', function () {
-      createProject('sandboxed')
-      cy.log('wait for compile')
-      cy.findByRole('region', { name: 'PDF preview and logs' }).findByText(
-        'sandboxed'
-      )
-
-      cy.log('Check which compiler version was used, expect 2025')
-      cy.findByRole('button', { name: 'View logs' }).click()
-      cy.findByLabelText('Raw logs from the LaTeX compiler').findByText(
-        /This is pdfTeX, Version .+ \(TeX Live 2025\) /
-      )
-
-      cy.log('Check that there is no TeX Live version toggle')
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByTestId('left-menu').within(() => {
-        cy.findByRole('button', { name: 'Word Count' }) // wait for lazy loading
-        cy.findByText(LABEL_TEX_LIVE_VERSION).should('not.exist')
-      })
-    })
-  }
-
-  describe('disabled in Server Pro', function () {
-    if (isExcludedBySharding('PRO_DEFAULT_2')) return
-    startWith({ pro: true })
-    ensureUserExists({ email: 'user@example.com' })
-    beforeEach(function () {
-      login('user@example.com')
-    })
-
-    checkUsesDefaultCompiler()
-    checkSyncTeX()
-    checkXeTeX()
-    checkRecompilesAfterErrors()
-    checkStopCompile()
-  })
-
-  // https://github.com/overleaf/internal/issues/20216
-  // eslint-disable-next-line mocha/no-skipped-tests
-  describe.skip('unavailable in CE', function () {
-    if (isExcludedBySharding('CE_CUSTOM_1')) return
-    startWith({ pro: false, vars: enabledVars, resetData: true })
-    ensureUserExists({ email: 'user@example.com' })
-    beforeEach(function () {
-      login('user@example.com')
-    })
-
-    checkUsesDefaultCompiler()
-    checkSyncTeX()
-    checkXeTeX()
-    checkRecompilesAfterErrors()
-    checkStopCompile()
-  })
-})

+ 0 - 280
server-ce/test/templates.spec.ts

@@ -1,280 +0,0 @@
-import { isExcludedBySharding, startWith } from './helpers/config'
-import { ensureUserExists, login } from './helpers/login'
-import {
-  createProject,
-  NEW_PROJECT_BUTTON_MATCHER,
-  redirectEditorUrlWithQueryParams,
-} from './helpers/project'
-
-const WITHOUT_PROJECTS_USER = 'user-without-projects@example.com'
-const ADMIN_USER = 'admin@example.com'
-const REGULAR_USER = 'user@example.com'
-const TEMPLATES_USER = 'templates@example.com'
-
-// Re-use value for "exists" and "does not exist" tests
-const LABEL_BROWSE_TEMPLATES = 'Browse templates'
-
-describe('Templates', function () {
-  ensureUserExists({ email: TEMPLATES_USER })
-  ensureUserExists({ email: WITHOUT_PROJECTS_USER })
-
-  let OVERLEAF_TEMPLATES_USER_ID: string
-  before(function () {
-    login(TEMPLATES_USER)
-    cy.visit('/')
-    cy.get('meta[name="ol-user_id"]').then(el => {
-      OVERLEAF_TEMPLATES_USER_ID = el.attr('content')!
-    })
-  })
-
-  function varsFn() {
-    return {
-      OVERLEAF_TEMPLATES_USER_ID,
-      OVERLEAF_NEW_PROJECT_TEMPLATE_LINKS:
-        '[{"name":"All Templates","url":"/templates/all"}]',
-    }
-  }
-
-  describe('enabled in Server Pro', function () {
-    if (isExcludedBySharding('PRO_CUSTOM_2')) return
-    startWith({
-      pro: true,
-      varsFn,
-    })
-    ensureUserExists({ email: REGULAR_USER })
-    ensureUserExists({ email: ADMIN_USER, isAdmin: true })
-
-    it('should show templates link on welcome page', function () {
-      login(WITHOUT_PROJECTS_USER)
-      cy.visit('/')
-      cy.findByRole('link', { name: LABEL_BROWSE_TEMPLATES }).click()
-      cy.url().should('match', /\/templates$/)
-    })
-
-    it('should have templates feature', function () {
-      login(TEMPLATES_USER)
-      const name = `Template ${Date.now()}`
-      const description = `Template Description ${Date.now()}`
-
-      cy.visit('/')
-      createProject(name, { type: 'Example project' }).as('templateProjectId')
-
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Manage Template' }).click()
-
-      cy.findByLabelText('Template Description').type(description)
-      cy.findByRole('button', { name: 'Publish' }).click()
-      cy.findByRole('button', { name: 'Publishing…' }).should('be.disabled')
-      cy.findByRole('button', { name: 'Publish' }).should('not.exist')
-      cy.findByRole('button', { name: 'Unpublish', timeout: 60_000 })
-      cy.findByRole('button', { name: 'Republish' })
-
-      cy.findByRole('link', { name: 'View it in the template gallery' }).click()
-      cy.url()
-        .should('match', /\/templates\/[a-f0-9]{24}$/)
-        .as('templateURL')
-
-      cy.findByRole('heading', { level: 2 }).findByText(name)
-      cy.findByText(description)
-      cy.findByRole('link', { name: 'Open as Template' })
-      cy.findByRole('button', { name: 'Unpublish' })
-      cy.findByRole('button', { name: 'Republish' })
-      cy.get('img')
-        .should('have.attr', 'src')
-        .and('match', /\/v\/0\//)
-      cy.findByRole('button', { name: 'Republish' }).click()
-      cy.findByRole('button', { name: 'Publishing…' }).should('be.disabled')
-      cy.findByRole('button', { name: 'Republish', timeout: 60_000 })
-      cy.get('img', { timeout: 60_000 })
-        .should('have.attr', 'src')
-        .and('match', /\/v\/1\//)
-
-      // custom tag
-      const tagName = `${Date.now()}`
-      cy.visit('/')
-      cy.findByRole('checkbox', { name: `Select ${name}` }).check()
-      cy.findByRole('navigation', { name: 'Project categories and tags' })
-        .findByRole('button', { name: 'New tag' })
-        .click()
-      cy.focused().type(tagName)
-      cy.findByRole('button', { name: 'Create' }).click()
-      cy.findByRole('navigation', {
-        name: 'Project categories and tags',
-      }).should('contain', `${tagName} (1)`)
-
-      // Check listing
-      cy.visit('/templates')
-      cy.findByRole('link', { name: tagName })
-      cy.visit('/templates/all')
-      cy.findByRole('heading', { name })
-      cy.visit(`/templates/${tagName}`)
-      cy.findByRole('heading', { name })
-
-      // Unpublish via template page
-      cy.get('@templateURL').then(url => cy.visit(`${url}`))
-      cy.findByRole('button', { name: 'Unpublish' }).click()
-      cy.url().should('match', /\/templates$/)
-      cy.get('@templateURL').then(url =>
-        cy.visit(`${url}`, {
-          failOnStatusCode: false,
-        })
-      )
-      cy.findByRole('heading', { name: 'Not found' })
-      cy.visit('/templates/all')
-      cy.findByRole('heading', { name }).should('not.exist')
-      cy.visit(`/templates/${tagName}`)
-      cy.findByRole('heading', { name }).should('not.exist')
-
-      // Publish again
-      redirectEditorUrlWithQueryParams(false)
-      cy.get('@templateProjectId').then(projectId =>
-        cy.visit(`/project/${projectId}`)
-      )
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Manage Template' }).click()
-      cy.findByRole('button', { name: 'Publish' }).click()
-      cy.findByRole('button', { name: 'Unpublish', timeout: 60_000 })
-
-      // Should assign a new template id
-      cy.findByRole('link', { name: 'View it in the template gallery' }).click()
-      cy.url()
-        .should('match', /\/templates\/[a-f0-9]{24}$/)
-        .as('newTemplateURL')
-      cy.get('@newTemplateURL').then(newURL => {
-        cy.get('@templateURL').then(prevURL => {
-          expect(newURL).to.match(/\/templates\/[a-f0-9]{24}$/)
-          expect(prevURL).to.not.equal(newURL)
-        })
-      })
-
-      // Open project from template
-      login(REGULAR_USER)
-      cy.visit('/templates')
-      cy.findByRole('link', { name: tagName }).click()
-      cy.findByRole('link', { name }).click()
-      redirectEditorUrlWithQueryParams(false)
-      cy.findByRole('link', { name: 'Open as Template' }).click()
-      cy.findByRole('navigation', { name: 'Project actions' }).findByText(
-        /Your Paper/i
-      ) // might have (1) suffix
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Word Count' }).click() // wait for lazy loading
-      cy.findByRole('button', { name: 'Manage Template' }).should('not.exist')
-
-      // Check management as regular user
-      cy.get('@newTemplateURL').then(url => cy.visit(`${url}`))
-      cy.findByRole('link', { name: 'Open as Template' })
-      cy.findByRole('button', { name: 'Unpublish' }).should('not.exist')
-      cy.findByRole('button', { name: 'Republish' }).should('not.exist')
-
-      // Check management as admin user
-      login(ADMIN_USER)
-      cy.get('@newTemplateURL').then(url => cy.visit(`${url}`))
-      cy.findByRole('link', { name: 'Open as Template' })
-      cy.findByRole('button', { name: 'Unpublish' })
-      cy.findByRole('button', { name: 'Republish' })
-      redirectEditorUrlWithQueryParams(false)
-      cy.get('@templateProjectId').then(projectId =>
-        cy.visit(`/project/${projectId}`)
-      )
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Manage Template' }).click()
-      cy.findByRole('button', { name: 'Unpublish' })
-
-      // Back to templates user
-      login(TEMPLATES_USER)
-
-      // Unpublish via editor
-      redirectEditorUrlWithQueryParams(false)
-      cy.get('@templateProjectId').then(projectId =>
-        cy.visit(`/project/${projectId}`)
-      )
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Manage Template' }).click()
-      cy.findByRole('button', { name: 'Unpublish' }).click()
-      cy.findByRole('button', { name: 'Publish' })
-      cy.visit('/templates/all')
-      cy.findByRole('link', { name }).should('not.exist')
-
-      // check for template links, after creating the first project
-      cy.visit('/')
-      cy.findAllByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }).click()
-      cy.findByRole('menuitem', { name: /All Templates/ }).should(
-        'have.attr',
-        'href',
-        '/templates/all'
-      )
-    })
-  })
-
-  function checkDisabled() {
-    it('should not have templates feature', function () {
-      login(TEMPLATES_USER)
-
-      cy.visit('/')
-      createProject('maybe templates')
-
-      cy.findByRole('navigation', {
-        name: 'Project actions',
-      })
-        .findByRole('button', { name: 'Menu' })
-        .click()
-      cy.findByRole('button', { name: 'Word Count' }) // wait for lazy loading
-      cy.findByRole('button', { name: 'Manage Template' }).should('not.exist')
-
-      cy.visit('/templates', { failOnStatusCode: false })
-      cy.findByRole('heading', { name: 'Not found' })
-      cy.visit('/templates/all', { failOnStatusCode: false })
-      cy.findByRole('heading', { name: 'Not found' })
-
-      // check for template links, after creating the first project
-      cy.visit('/')
-      cy.findAllByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }).click()
-      cy.findByRole('menuitem', { name: /All Templates/ }).should('not.exist')
-    })
-
-    it('should not show templates link on welcome page', function () {
-      login(WITHOUT_PROJECTS_USER)
-      cy.visit('/')
-      cy.findByRole('button', { name: NEW_PROJECT_BUTTON_MATCHER }) // wait for lazy loading
-      cy.findByRole('link', { name: LABEL_BROWSE_TEMPLATES }).should(
-        'not.exist'
-      )
-    })
-  }
-
-  describe('disabled Server Pro', function () {
-    if (isExcludedBySharding('PRO_DEFAULT_2')) return
-    startWith({ pro: true })
-    checkDisabled()
-  })
-
-  describe('unavailable in CE', function () {
-    if (isExcludedBySharding('CE_CUSTOM_1')) return
-    startWith({
-      pro: false,
-      varsFn,
-    })
-    checkDisabled()
-  })
-})

+ 1 - 6
services/web/frontend/js/features/ide-redesign/utils/new-editor-utils.ts

@@ -1,12 +1,7 @@
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 
-// For e2e tests purposes, allow overriding to old editor
-export const oldEditorOverride =
-  new URLSearchParams(window.location.search).get('old-editor-override') ===
-  'true'
-
 export const canUseNewEditor = () => {
-  return !oldEditorOverride
+  return true
 }
 
 export const useIsNewEditorEnabled = () => {