From c5cd1646e2849f106119ac94e21355904055e2fd Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sat, 27 Aug 2022 15:49:22 +0200 Subject: [PATCH] Add tests for heading anchors and inter-page links Also added a chai assertion for checking an element is currently shown in the viewport of the window. This is needed as the cypress visibility checks fail for tiptap elements, as they are overlaid by the author color / names. Signed-off-by: Ferdinand Thiessen --- cypress/e2e/outline.spec.js | 74 --------------------- cypress/e2e/sections.spec.js | 124 +++++++++++++++++++++++++++++++++++ cypress/support/chai.js | 16 +++++ cypress/support/commands.js | 4 +- cypress/support/e2e.js | 5 ++ 5 files changed, 147 insertions(+), 76 deletions(-) delete mode 100644 cypress/e2e/outline.spec.js create mode 100644 cypress/e2e/sections.spec.js create mode 100644 cypress/support/chai.js 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) +})