import { isExcludedBySharding, startWith } from './helpers/config' import { ensureUserExists, login } from './helpers/login' import { createProject, NEW_PROJECT_BUTTON_MATCHER } 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 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() 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' }) 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 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() }) })