diff --git a/cypress/e2e/outline.spec.js b/cypress/e2e/outline.spec.js deleted file mode 100644 index b441a65d6b7..00000000000 --- a/cypress/e2e/outline.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import { randHash } from '../utils/index.js' - -const currentUser = randHash() - -const refresh = () => cy.get('.files-controls .crumb:not(.hidden) a') - .last() - .click({ force: true }) - -const clickOutline = () => { - cy.getActionEntry('headings') - .click() - - cy.get('.v-popper__wrapper .open').getActionEntry('outline') - .click() -} - -const createMarkdown = (fileName, content) => { - return cy.createFile(fileName, content, 'text/markdown') - .then(refresh) -} - -describe('Table of Contents', () => { - before(() => { - // Init user - cy.nextcloudCreateUser(currentUser, 'password') - cy.login(currentUser, 'password') - }) - - beforeEach(() => { - cy.login(currentUser, 'password') - }) - - it('sidebar toc', () => { - const fileName = 'toc.md' - - createMarkdown(fileName, '# T1 \n ## T2 \n ### T3 \n #### T4 \n ##### T5 \n ###### T6') - .then(refresh) - .then(() => cy.openFile(fileName, { force: true })) - .then(clickOutline) - - cy.getOutline() - .find('header') - .should('exist') - - cy.getTOC() - .find('ul li') - .should('have.length', 6) - cy.getTOC() - .find('ul li') - .each((el, index) => { - cy.wrap(el) - .should('have.attr', 'data-toc-level') - .and('equal', String(index + 1)) - - cy.wrap(el) - .find('a') - .should('have.attr', 'href') - .and('equal', `#t${index + 1}`) - }) - }) - - it('empty toc', () => { - const fileName = 'empty.md' - - createMarkdown(fileName, '') - .then(refresh) - .then(() => cy.openFile(fileName, { force: true })) - .then(clickOutline) - - cy.getOutline() - .find('ul') - .should('be.empty') - }) -}) diff --git a/cypress/e2e/sections.spec.js b/cypress/e2e/sections.spec.js new file mode 100644 index 00000000000..89a3991a343 --- /dev/null +++ b/cypress/e2e/sections.spec.js @@ -0,0 +1,124 @@ +import { initUserAndFiles, randHash } from '../utils/index.js' + +const currentUser = randHash() +const fileName = 'test.md' + +const refresh = () => cy.get('.files-controls .crumb:not(.hidden) a') + .last() + .click({ force: true }) + +const clickOutline = () => { + cy.getActionEntry('headings') + .click() + + cy.get('.v-popper__wrapper .open').getActionEntry('outline') + .click() +} + +describe('Content Sections', () => { + before(function() { + initUserAndFiles(currentUser, fileName) + }) + + beforeEach(function() { + cy.login(currentUser, 'password', { + onBeforeLoad(win) { + cy.stub(win, 'open') + .as('winOpen') + }, + }) + + cy.openFile(fileName) + .then(() => cy.clearContent()) + }) + + describe('Heading anchors', () => { + beforeEach(() => cy.clearContent()) + + it('Anchor exists', () => { + cy.getContent() + .type('# Heading\nText\n## Heading 2\nText\n## Heading 2') + .then(() => { + cy.getContent() + .find('a.anchor-link') + .should(($anchor) => { + expect($anchor).to.have.length(3) + expect($anchor.eq(0)).to.have.attr('href').and.equal('#heading') + expect($anchor.eq(1)).to.have.attr('href').and.equal('#heading-2') + expect($anchor.eq(2)).to.have.attr('href').and.equal('#heading-2--1') + }) + }) + }) + + it('Anchor scrolls into view', () => { + // Create link to top heading + cy.getContent() + .type('{selectAll}{backspace}move top\n{selectAll}') + .get('.menububble button[data-text-bubble-action="add-link"]') + .click({ force: true }) + .then(() => { + cy.get('.menububble .menububble__input') + .type('{shift}') + .type('#top{enter}', { force: true }) + }) + // Insert content above link + cy.getContent() + .type('{moveToStart}\n{moveToStart}# top \n') + .type('lorem ipsum \n'.repeat(25)) + .type('{moveToEnd}\n') + .find('h1#top') + .should('not.be.inViewport') + // Click link and test view moved to anchor + cy.getContent() + .find('a:not(.anchor-link)') + .click() + .then(() => { + cy.getContent() + .get('h1[id="top"]') + .should('be.inViewport') + }) + }) + }) + + describe('Table of Contents', () => { + beforeEach(() => cy.clearContent()) + + it('sidebar toc', () => { + cy.getContent() + .type('# T1 \n## T2 \n### T3 \n#### T4 \n##### T5 \n###### T6\n') + .then(refresh) + .then(() => cy.openFile(fileName, { force: true })) + .then(clickOutline) + + cy.getOutline() + .find('header') + .should('exist') + + cy.getTOC() + .find('ul li') + .should('have.length', 6) + cy.getTOC() + .find('ul li') + .each((el, index) => { + cy.wrap(el) + .should('have.attr', 'data-toc-level') + .and('equal', String(index + 1)) + + cy.wrap(el) + .find('a') + .should('have.attr', 'href') + .and('equal', `#t${index + 1}`) + }) + }) + + it('empty toc', () => { + refresh() + .then(() => cy.openFile(fileName, { force: true })) + .then(clickOutline) + + cy.getOutline() + .find('ul') + .should('be.empty') + }) + }) +}) diff --git a/cypress/support/chai.js b/cypress/support/chai.js new file mode 100644 index 00000000000..77737e3bce2 --- /dev/null +++ b/cypress/support/chai.js @@ -0,0 +1,16 @@ +export default _chai => { + _chai.Assertion.addMethod('inViewport', function() { + const subject = this._obj + + const height = Cypress.$(cy.state('window')).height() + const width = Cypress.$(cy.state('window')).width() + const rect = subject[0].getBoundingClientRect() + + this.assert( + rect.top < height && rect.bottom > 0 && rect.right <= width && rect.left >= 0, + 'expected #{this} to be in the viewport', + 'expected #{this} to not be in the viewport', + this._obj + ) + }) +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js index fa5acc87774..9942fe29d06 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -258,8 +258,8 @@ Cypress.Commands.add('getTOC', () => { Cypress.Commands.add('clearContent', () => { return cy.getContent() - .type('{selectall}') - .type('{del}') + .scrollIntoView() + .type('{selectAll}{backspace}', { force: true }) }) Cypress.Commands.add('openWorkspace', (subject, name) => { diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 73c0c67db20..614793a739a 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -1,3 +1,8 @@ // This file is loaded before all e2e tests import './commands.js' +import chaiExtension from './chai.js' + +before(() => { + chai.use(chaiExtension) +})