| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import { ensureUserExists, login } from './helpers/login'
- import { isExcludedBySharding, startWith } from './helpers/config'
- import { dockerCompose, runScript } from './helpers/hostAdminClient'
- import { createProject, openProjectByName } from './helpers/project'
- import { throttledRecompile } from './helpers/compile'
- import { v4 as uuid } from 'uuid'
- const USER = 'user@example.com'
- const PROJECT_NAME = 'Old Project'
- describe('Upgrading', function () {
- if (isExcludedBySharding('PRO_CUSTOM_3')) return
- function testUpgrade(
- steps: {
- version: string
- vars?: Object
- newProjectButtonMatcher?: RegExp
- hook?: () => void
- }[]
- ) {
- const startOptions = steps.shift()!
- before(async () => {
- cy.log('Create old instance')
- })
- startWith({
- pro: true,
- version: startOptions.version,
- withDataDir: true,
- resetData: true,
- vars: startOptions.vars,
- })
- before(function () {
- cy.log('Create initial user after deleting it')
- })
- ensureUserExists({ email: USER })
- before(() => {
- cy.log('Populate old instance')
- login(USER)
- createProject(PROJECT_NAME, {
- newProjectButtonMatcher: startOptions.newProjectButtonMatcher,
- })
- const recompile = throttledRecompile()
- cy.log('Wait for successful compile')
- cy.get('.pdf-viewer').should('contain.text', PROJECT_NAME)
- cy.log('Increment the doc version three times')
- for (let i = 0; i < 3; i++) {
- cy.log('Add content')
- cy.findByText('\\maketitle').parent().click()
- cy.findByText('\\maketitle')
- .parent()
- .type(`\n\\section{{}Old Section ${i}}`)
- cy.log('Trigger full flush')
- recompile()
- cy.findByRole('navigation', {
- name: /Project Layout, Sharing, and Submission/i,
- })
- .findByRole('button', { name: /Menu/i })
- .click()
- cy.findByText('Source').click()
- cy.get('.left-menu-modal-backdrop').click({ force: true })
- }
- cy.log('Check compile and history')
- for (let i = 0; i < 3; i++) {
- cy.get('.pdf-viewer').should('contain.text', `Old Section ${i}`)
- }
- cy.findByText('History').click()
- for (let i = 0; i < 3; i++) {
- cy.findByText(new RegExp(`\\\\section\{Old Section ${i}}`))
- }
- })
- for (const step of steps) {
- before(() => {
- cy.log(`Upgrade to version ${step.version}`)
- // Navigate way from editor to avoid redirect to /login when the next instance comes up (which slows down tests)
- cy.visit('/project', {})
- })
- before(async function () {
- cy.log('Graceful shutdown: flush all the things')
- this.timeout(20 * 1000)
- // Ideally we could use the container shutdown procedure, but it's too slow and unreliable for tests.
- // TODO(das7pad): adopt the below after speeding up the graceful shutdown procedure on all supported releases
- // await dockerCompose('stop', 'sharelatex')
- // For now, we are stuck with manually flushing things
- await runScript({
- cwd: 'services/document-updater',
- script: 'scripts/flush_all.js',
- })
- await runScript({
- cwd: 'services/project-history',
- script: 'scripts/flush_all.js',
- })
- })
- startWith({
- pro: true,
- version: step.version,
- vars: step.vars,
- withDataDir: true,
- })
- step.hook?.()
- }
- beforeEach(() => {
- login(USER)
- })
- it('should list the old project', () => {
- cy.visit('/project')
- cy.findByText(PROJECT_NAME)
- })
- it('should open the old project', () => {
- openProjectByName(PROJECT_NAME)
- cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
- cy.findByRole('navigation', {
- name: /Project actions/i,
- }).within(() => {
- cy.findByText(PROJECT_NAME)
- })
- const recompile = throttledRecompile()
- cy.log('wait for successful compile')
- cy.get('.pdf-viewer').should('contain.text', PROJECT_NAME)
- cy.get('.pdf-viewer').should('contain.text', 'Old Section 2')
- cy.log('Add more content')
- const newSection = `New Section ${uuid()}`
- cy.findByText('\\maketitle').parent().click()
- cy.findByText('\\maketitle').parent().type(`\n\\section{{}${newSection}}`)
- cy.log('Check compile and history')
- recompile()
- cy.get('.pdf-viewer').should('contain.text', newSection)
- cy.findByText('History').click()
- cy.findByText(/\\section\{Old Section 2}/)
- cy.findByText(new RegExp(`\\\\section\\{${newSection}}`))
- })
- }
- const optionsFourDotTwo = {
- version: '4.2',
- vars: {
- // Add core vars with old branding
- SHARELATEX_SITE_URL: 'http://sharelatex',
- SHARELATEX_MONGO_URL: 'mongodb://mongo/sharelatex',
- SHARELATEX_REDIS_HOST: 'redis',
- },
- newProjectButtonMatcher: /create first project/i,
- }
- describe('from 4.2 to latest', () => {
- testUpgrade([optionsFourDotTwo, { version: 'latest' }])
- })
- describe('from 5.0 to latest', () => {
- testUpgrade([{ version: '5.0' }, { version: 'latest' }])
- })
- describe('doc version recovery', () => {
- testUpgrade([
- optionsFourDotTwo,
- {
- version: '5.0.1-RC1',
- hook() {
- before(function () {
- login(USER)
- cy.visit('/')
- cy.findByText(PROJECT_NAME).click()
- const recompile = throttledRecompile()
- cy.log('Make a change')
- cy.findByText('\\maketitle').parent().click()
- cy.findByText('\\maketitle')
- .parent()
- .type('\n\\section{{}FiveOOne Section}')
- cy.log('Trigger flush')
- recompile()
- cy.get('.pdf-viewer').should('contain.text', 'FiveOOne Section')
- cy.log('Check for broken history, i.e. not synced with latest edit')
- cy.findByText('History').click()
- cy.findByText(/\\section\{Old Section 2}/) // wait for lazy loading
- cy.findByText(/\\section\{FiveOOne Section}/).should('not.exist')
- })
- },
- },
- {
- version: 'latest',
- hook() {
- before(async function () {
- this.timeout(20_000)
- const needle = 'Finished resyncing history for all projects.'
- for (let i = 0; i < 30; i++) {
- const { stdout } = await dockerCompose('logs', 'sharelatex')
- if (stdout.includes(needle)) {
- return
- }
- await new Promise(resolve => setTimeout(resolve, 500))
- }
- const { stdout } = await dockerCompose('logs', 'sharelatex')
- expect(stdout).to.contain(
- needle,
- 'Doc version recovery did not finish yet.'
- )
- })
- before(function () {
- login(USER)
- cy.visit('/')
- cy.findByText(PROJECT_NAME).click()
- cy.log(
- 'The edit that was made while the history was broken should be there now.'
- )
- cy.findByText('History').click()
- cy.findByText(/\\section\{FiveOOne Section}/)
- // TODO(das7pad): restore after https://github.com/overleaf/internal/issues/19588 is fixed.
- // cy.log('Check indicator of force resync')
- // cy.findByText('Overleaf History System')
- })
- },
- },
- ])
- })
- })
|