Cypress-IDs and prettier for tests (#1634)

* Add cy.getById method and run prettier

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2021-11-19 18:04:04 +01:00 committed by GitHub
parent 8a8bacc0aa
commit d725b65140
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 758 additions and 1203 deletions

View file

@ -4,170 +4,111 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
describe('Autocompletion', () => {
describe('Autocompletion works for', () => {
beforeEach(() => {
cy.visitTestEditor()
cy.get('.CodeMirror')
.click()
.get('textarea')
.as('codeinput')
cy.get('.CodeMirror').click().get('textarea').as('codeinput')
})
describe('code block', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent('```')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '```abnf')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span')
.should('have.text', '```')
cy.getMarkdownBody().find('.code-highlighter').should('exist')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', '```abnf')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span').should('have.text', '```')
cy.getMarkdownBody().findById('highlighted-code-block').should('exist')
})
it('via doubleclick', () => {
cy.setCodemirrorContent('```')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '```abnf')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span')
.should('have.text', '```')
cy.getMarkdownBody().find('.code-highlighter').should('exist')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', '```abnf')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span').should('have.text', '```')
cy.getMarkdownBody().findById('highlighted-code-block').should('exist')
})
})
describe('container', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent(':::')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', ':::success')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span')
.should('have.text', '::: ')
cy.getMarkdownBody()
.find('div.alert')
.should('exist')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', ':::success')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span').should('have.text', '::: ')
cy.getMarkdownBody().find('div.alert').should('exist')
})
it('via doubleclick', () => {
cy.setCodemirrorContent(':::')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', ':::success')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span')
.should('have.text', '::: ')
cy.getMarkdownBody()
.find('div.alert')
.should('exist')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', ':::success')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span').should('have.text', '::: ')
cy.getMarkdownBody().find('div.alert').should('exist')
})
})
describe('emoji', () => {
describe('normal emoji', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent(':hedg')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', ':hedgehog:')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', ':hedgehog:')
})
it('via doubleclick', () => {
cy.setCodemirrorContent(':hedg')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', ':hedgehog:')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', ':hedgehog:')
})
})
describe('fork-awesome-icon', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent(':fa-face')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', ':fa-facebook:')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', ':fa-facebook:')
})
it('via doubleclick', () => {
cy.setCodemirrorContent(':fa-face')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', ':fa-facebook:')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', ':fa-facebook:')
})
})
})
describe('header', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent('#')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '# ')
cy.getMarkdownBody()
.find('h1 ')
.should('have.text', '\n ')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '# ')
cy.getMarkdownBody().find('h1').should('have.text', '\n ')
})
it('via doubleclick', () => {
cy.setCodemirrorContent('#')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '# ')
cy.getMarkdownBody()
.find('h1')
.should('have.text', '\n ')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '# ')
cy.getMarkdownBody().find('h1').should('have.text', '\n ')
})
})
describe('images', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent('!')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '![image alt](https:// "title")')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '![image alt](https:// "title")')
cy.getMarkdownBody()
.find('p > img')
.should('have.attr', 'alt', 'image alt')
@ -176,13 +117,9 @@ describe('Autocompletion', () => {
})
it('via doubleclick', () => {
cy.setCodemirrorContent('!')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '![image alt](https:// "title")')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '![image alt](https:// "title")')
cy.getMarkdownBody()
.find('p > img')
.should('have.attr', 'alt', 'image alt')
@ -192,16 +129,12 @@ describe('Autocompletion', () => {
})
describe('links', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent('[')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '[link text](https:// "title") ')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '[link text](https:// "title") ')
cy.getMarkdownBody()
.find('p > a')
.should('have.text', 'link text')
@ -210,13 +143,9 @@ describe('Autocompletion', () => {
})
it('via doubleclick', () => {
cy.setCodemirrorContent('[')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '[link text](https:// "title") ')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '[link text](https:// "title") ')
cy.getMarkdownBody()
.find('p > a')
.should('have.text', 'link text')
@ -226,62 +155,38 @@ describe('Autocompletion', () => {
})
describe('pdf', () => {
it('via Enter', () => {
it('via enter', () => {
cy.setCodemirrorContent('{')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '{%pdf https:// %}')
cy.getMarkdownBody()
.find('p')
.should('exist')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '{%pdf https:// %}')
cy.getMarkdownBody().find('p').should('exist')
})
it('via doubleclick', () => {
cy.setCodemirrorContent('{')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '{%pdf https:// %}')
cy.getMarkdownBody()
.find('p')
.should('exist')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '{%pdf https:// %}')
cy.getMarkdownBody().find('p').should('exist')
})
})
describe('collapsable blocks', () => {
it('via Enter', () => {
describe('collapsible blocks', () => {
it('via enter', () => {
cy.setCodemirrorContent('<d')
cy.get('.CodeMirror-hints')
.should('exist')
cy.get('@codeinput')
.type('{enter}')
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '</details>') // after selecting the hint, the last line of the inserted suggestion is active
cy.getMarkdownBody()
.find('details')
.should('exist')
cy.get('.CodeMirror-hints').should('exist')
cy.get('@codeinput').type('{enter}')
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '</details>') // after selecting the hint, the last line of the inserted suggestion is active
cy.getMarkdownBody().find('details').should('exist')
})
it('via doubleclick', () => {
cy.setCodemirrorContent('<d')
cy.get('.CodeMirror-hints > li')
.first()
.dblclick()
cy.get('.CodeMirror-hints')
.should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', '</details>')
cy.getMarkdownBody()
.find('details')
.should('exist')
cy.get('.CodeMirror-hints > li').first().dblclick()
cy.get('.CodeMirror-hints').should('not.exist')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', '</details>')
cy.getMarkdownBody().find('details').should('exist')
})
})
})

View file

@ -11,57 +11,39 @@ describe('Diagram codeblock ', () => {
it('renders markmap', () => {
cy.setCodemirrorContent('```markmap\n- pro\n- contra\n```')
cy.getMarkdownBody()
.find('[data-cypress-id=markmap]')
.children()
.should('be.visible')
cy.getMarkdownBody().findById('markmap').children().should('be.visible')
})
it('renders vega-lite', () => {
cy.setCodemirrorContent('```vega-lite\n{"$schema":"https://vega.github.io/schema/vega-lite/v4.json","data":{"values":[{"a":"","b":28}]},"mark":"bar","encoding":{"x":{"field":"a"},"y":{"field":"b"}}}\n```')
cy.getMarkdownBody()
.find('.vega-embed')
.children()
.should('be.visible')
cy.setCodemirrorContent(
'```vega-lite\n{"$schema":"https://vega.github.io/schema/vega-lite/v4.json","data":{"values":[{"a":"","b":28}]},"mark":"bar","encoding":{"x":{"field":"a"},"y":{"field":"b"}}}\n```'
)
cy.getMarkdownBody().find('.vega-embed').children().should('be.visible')
})
it('renders graphviz', () => {
cy.setCodemirrorContent('```graphviz\ngraph {\na -- b\n}\n```')
cy.getMarkdownBody()
.find('[data-cypress-id=graphviz]')
.children()
.should('be.visible')
cy.getMarkdownBody().findById('graphviz').children().should('be.visible')
})
it('renders mermaid', () => {
cy.setCodemirrorContent('```mermaid\ngraph TD;\n A-->B;\n```')
cy.getMarkdownBody()
.find('.mermaid')
.children()
.should('be.visible')
cy.getMarkdownBody().find('.mermaid').children().should('be.visible')
})
it('renders flowcharts', () => {
cy.setCodemirrorContent('```flow\nst=>start: Start\ne=>end: End\nst->e\n```')
cy.getMarkdownBody()
.find('[data-cypress-id=flowchart]')
.children()
.should('be.visible')
cy.getMarkdownBody().findById('flowchart').children().should('be.visible')
})
it('renders abc scores', () => {
cy.setCodemirrorContent('```abc\nM:4/4\nK:G\n|:GABc dedB:|\n```')
cy.getMarkdownBody()
.find('.abcjs-score')
.children()
.should('be.visible')
cy.getMarkdownBody().findById('abcjs').children().should('be.visible')
})
it('renders csv as table', () => {
cy.setCodemirrorContent('```csv delimiter=; header\na;b;c;d\n1;2;3;4\n```')
cy.getMarkdownBody()
.find('.csv-html-table')
.should('be.visible')
cy.getMarkdownBody().findById('csv-html-table').first().should('be.visible')
})
it('renders plantuml', () => {

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
describe("Document read only page", () => {
describe('Document read only page', () => {
it('renders the document mode', () => {
cy.visit('/s/test')
cy.getMarkdownBody().should("exist")
cy.getMarkdownBody().should('exist')
})
})

View file

@ -10,90 +10,74 @@ const title = 'This is a test title'
describe('Document Title', () => {
beforeEach(() => {
cy.visitTestEditor()
cy.get('.btn.active.btn-outline-secondary > i.fa-columns')
.should('exist')
cy.getById('view-mode-both').should('exist')
})
describe('title should be yaml metadata title', () => {
it('just yaml metadata title', () => {
cy.setCodemirrorContent(`---\ntitle: ${ title }\n---`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`---\ntitle: ${title}\n---`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
it('yaml metadata title and opengraph title', () => {
cy.setCodemirrorContent(`---\ntitle: ${ title }\nopengraph:\n title: False title\n---`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`---\ntitle: ${title}\nopengraph:\n title: False title\n---`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
it('yaml metadata title, opengraph title and first heading', () => {
cy.setCodemirrorContent(`---\ntitle: ${ title }\nopengraph:\n title: False title\n---\n# a first title`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`---\ntitle: ${title}\nopengraph:\n title: False title\n---\n# a first title`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
})
describe('title should be opengraph title', () => {
it('just opengraph title', () => {
cy.setCodemirrorContent(`---\nopengraph:\n title: ${ title }\n---`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`---\nopengraph:\n title: ${title}\n---`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
it('opengraph title and first heading', () => {
cy.setCodemirrorContent(`---\nopengraph:\n title: ${ title }\n---\n# a first title`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`---\nopengraph:\n title: ${title}\n---\n# a first title`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
})
describe('title should be first heading', () => {
it('just first heading', () => {
cy.setCodemirrorContent(`# ${ title }`)
cy.title()
.should('eq', `${ title } - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title}`)
cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`)
})
it('just first heading with alt-text instead of image', () => {
cy.setCodemirrorContent(`# ${ title } ![abc](https://dummyimage.com/48)`)
cy.title()
.should('eq', `${ title } abc - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title} ![abc](https://dummyimage.com/48)`)
cy.title().should('eq', `${title} abc - HedgeDoc @ ${branding.name}`)
})
it('just first heading without link syntax', () => {
cy.setCodemirrorContent(`# ${ title } [link](https://hedgedoc.org)`)
cy.title()
.should('eq', `${ title } link - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title} [link](https://hedgedoc.org)`)
cy.title().should('eq', `${title} link - HedgeDoc @ ${branding.name}`)
})
it('markdown syntax removed first', () => {
cy.setCodemirrorContent(`# ${ title } 1*2*3 4*5**`)
cy.title()
.should('eq', `${ title } 123 4*5** - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title} 1*2*3 4*5**`)
cy.title().should('eq', `${title} 123 4*5** - HedgeDoc @ ${branding.name}`)
})
it('markdown syntax removed second', () => {
cy.setCodemirrorContent(`# ${ title } **1 2*`)
cy.title()
.should('eq', `${ title } *1 2 - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title} **1 2*`)
cy.title().should('eq', `${title} *1 2 - HedgeDoc @ ${branding.name}`)
})
it('markdown syntax removed third', () => {
cy.setCodemirrorContent(`# ${ title } _asd_`)
cy.title()
.should('eq', `${ title } asd - HedgeDoc @ ${ branding.name }`)
cy.setCodemirrorContent(`# ${title} _asd_`)
cy.title().should('eq', `${title} asd - HedgeDoc @ ${branding.name}`)
})
it('katex code looks right', () => {
cy.setCodemirrorContent(`# $\\alpha$-foo`)
cy.getIframeBody()
.find('h1')
.should('contain', 'α')
cy.get('.CodeMirror textarea')
.type('{Enter}{Enter}{Enter}{Enter}{Enter}') //This is a workaround because I don't know how to make sure, that the title gets updated in time.
cy.title()
.should('eq', `α-foo - HedgeDoc @ ${ branding.name }`)
cy.getIframeBody().find('h1').should('contain', 'α')
cy.get('.CodeMirror textarea').type('{Enter}{Enter}{Enter}{Enter}{Enter}') //This is a workaround because I don't know how to make sure, that the title gets updated in time.
cy.title().should('eq', `α-foo - HedgeDoc @ ${branding.name}`)
})
})
})

View file

@ -7,25 +7,18 @@
describe('Editor mode from URL parameter is used', () => {
it('mode view', () => {
cy.visitTestEditor('view')
cy.get('.splitter.left')
.should('have.class', 'd-none')
cy.get('.splitter.right')
.should('not.have.class', 'd-none')
cy.get('.splitter.left').should('not.be.visible')
cy.get('.splitter.right').should('be.visible')
})
it('mode both', () => {
cy.visitTestEditor('both')
cy.get('.splitter.left')
.should('not.have.class', 'd-none')
cy.get('.splitter.separator')
.should('exist')
cy.get('.splitter.right')
.should('not.have.class', 'd-none')
cy.get('.splitter.left').should('be.visible')
cy.get('.splitter.separator').should('exist')
cy.get('.splitter.right').should('be.visible')
})
it('mode edit', () => {
cy.visitTestEditor('edit')
cy.get('.splitter.left')
.should('not.have.class', 'd-none')
cy.get('.splitter.right')
.should('have.class', 'd-none')
cy.get('.splitter.left').should('be.visible')
cy.get('.splitter.right').should('not.be.visible')
})
})

View file

@ -5,7 +5,6 @@
*/
describe('emojis', () => {
const HEDGEHOG_UNICODE_CHARACTER = '\n🦔\n'
beforeEach(() => {
@ -14,20 +13,16 @@ describe('emojis', () => {
it('renders an emoji shortcode', () => {
cy.setCodemirrorContent(':hedgehog:')
cy.getMarkdownBody()
.should('have.text', HEDGEHOG_UNICODE_CHARACTER)
cy.getMarkdownBody().should('have.text', HEDGEHOG_UNICODE_CHARACTER)
})
it('renders an emoji unicode character', () => {
cy.setCodemirrorContent(HEDGEHOG_UNICODE_CHARACTER)
cy.getMarkdownBody()
.should('have.text', HEDGEHOG_UNICODE_CHARACTER)
cy.getMarkdownBody().should('have.text', HEDGEHOG_UNICODE_CHARACTER)
})
it('renders an fork awesome icon', () => {
cy.setCodemirrorContent(':fa-matrix-org:')
cy.getMarkdownBody()
.find('i.fa.fa-matrix-org')
.should('be.visible')
cy.getMarkdownBody().find('i.fa.fa-matrix-org').should('be.visible')
})
})

View file

@ -6,7 +6,7 @@
describe('Export', () => {
const testTitle = 'testContent'
const testContent = `---\ntitle: ${ testTitle }\n---\nThis is some test content`
const testContent = `---\ntitle: ${testTitle}\n---\nThis is some test content`
beforeEach(() => {
cy.visitTestEditor()
@ -14,12 +14,11 @@ describe('Export', () => {
})
it('Markdown', () => {
cy.get('[data-cypress-id="menu-export"]')
.click()
cy.get('[data-cypress-id="menu-export-markdown"]')
.click()
cy.getById('menu-export').click()
cy.getById('menu-export-markdown').click()
cy.get('a[download]')
.then((anchor) => (
.then(
(anchor) =>
new Cypress.Promise((resolve: any, _: any) => {
// Use XHR to get the blob that corresponds to the object URL.
const xhr = new XMLHttpRequest()
@ -41,7 +40,7 @@ describe('Export', () => {
}
xhr.send()
})
))
)
// Now the regular Cypress assertions should work.
.should('equal', testContent)
})

View file

@ -11,106 +11,93 @@ describe('File upload', () => {
cy.visitTestEditor()
})
it('doesn\'t prevent drag\'n\'drop of plain text', () => {
it("doesn't prevent drag'n'drop of plain text", () => {
const dataTransfer = new DataTransfer()
cy.setCodemirrorContent('line 1\nline 2\ndragline')
cy.get('.CodeMirror')
.click()
cy.get('.CodeMirror-line > span')
.last()
.dblclick()
cy.get('.CodeMirror-line > span > .cm-matchhighlight')
.trigger('dragstart', { dataTransfer })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span span')
.trigger('drop', { dataTransfer })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span span')
.should('have.text', 'lindraglinee 1')
cy.get('.CodeMirror').click()
cy.get('.CodeMirror-line > span').last().dblclick()
cy.get('.CodeMirror-line > span > .cm-matchhighlight').trigger('dragstart', { dataTransfer })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').trigger('drop', { dataTransfer })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should(
'have.text',
'lindraglinee 1'
)
})
describe('upload works', () => {
describe('works', () => {
beforeEach(() => {
cy.intercept({
cy.intercept(
{
method: 'GET',
url: '/mock-backend/api/private/media/upload-post'
}, {
},
{
statusCode: 200,
body: {
link: imageUrl
}
})
}
)
})
it('via button', () => {
cy.get('.fa-upload')
.click()
cy.get('div.btn-group > input[type=file]')
.attachFile({ filePath: 'demo.png', mimeType: 'image/png' })
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ imageUrl })`)
cy.getById('editor-toolbar-upload-image-button').click()
cy.getById('editor-toolbar-upload-image-input').attachFile({ filePath: 'demo.png', mimeType: 'image/png' })
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `![](${imageUrl})`)
})
it('via paste', () => {
cy.fixture('demo.png')
.then((image: string) => {
cy.fixture('demo.png').then((image: string) => {
const pasteEvent = {
clipboardData: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')],
getData: (_: string) => ''
}
}
cy.get('.CodeMirror-scroll')
.trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ imageUrl })`)
cy.get('.CodeMirror-scroll').trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `![](${imageUrl})`)
})
})
it('via drag and drop', () => {
cy.fixture('demo.png')
.then((image: string) => {
cy.fixture('demo.png').then((image: string) => {
const dropEvent = {
dataTransfer: {
files: [Cypress.Blob.base64StringToBlob(image, 'image/png')],
effectAllowed: 'uninitialized'
}
}
cy.get('.CodeMirror-scroll')
.trigger('dragenter', dropEvent)
cy.get('.CodeMirror-scroll')
.trigger('drop', dropEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ imageUrl })`)
cy.get('.CodeMirror-scroll').trigger('dragenter', dropEvent)
cy.get('.CodeMirror-scroll').trigger('drop', dropEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `![](${imageUrl})`)
})
})
})
it('upload fails', () => {
cy.intercept({
it('fails', () => {
cy.intercept(
{
method: 'GET',
url: '/mock-backend/api/private/media/upload-post'
}, {
},
{
statusCode: 400
}
)
cy.getById('editor-toolbar-upload-image-button').click()
cy.fixture('demo.png').then(() => {
cy.getById('editor-toolbar-upload-image-input').attachFile({ filePath: 'demo.png', mimeType: 'image/png' })
})
cy.get('.fa-upload')
.click()
cy.fixture('demo.png')
.then(() => {
cy.get('input[type=file]')
.attachFile({ filePath: 'demo.png', mimeType: 'image/png' })
})
cy.get('.CodeMirror-activeline > .CodeMirror-line > span > span')
.should('have.text', String.fromCharCode(8203)) //thanks codemirror....
cy.get('.CodeMirror-activeline > .CodeMirror-line > span > span').should('have.text', String.fromCharCode(8203)) //thanks codemirror....
})
it('text paste still works', () => {
it('lets text paste still work', () => {
const testText = 'a long test text'
const pasteEvent = {
clipboardData: {
getData: (type = 'text') => testText
}
}
cy.get('.CodeMirror-scroll')
.trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${ testText }`)
cy.get('.CodeMirror-scroll').trigger('paste', pasteEvent)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `${testText}`)
})
})

View file

@ -10,10 +10,7 @@ describe('Help Dialog', () => {
})
it('ToDo-List', () => {
cy.get('.fa.fa-question-circle')
.click()
cy.get('input[type="checkbox"]')
.should('exist')
.should('not.be.checked')
cy.getById('editor-help-button').click()
cy.get('input[type="checkbox"]').should('exist').should('not.be.checked')
})
})

View file

@ -14,26 +14,19 @@ describe('Code', () => {
})
describe('with just the language', () => {
it('doesn\'t show a gutter', () => {
it("doesn't show a gutter", () => {
cy.setCodemirrorContent('```javascript \nlet x = 0\n```')
findHljsCodeBlock()
.should('not.have.class', 'showGutter')
findHljsCodeBlock().should('not.have.class', 'showGutter')
findHljsCodeBlock()
.find('.linenumber')
.should('not.be.visible')
findHljsCodeBlock().find('.linenumber').should('not.be.visible')
})
describe('and line wrapping', () => {
it('doesn\'t show a gutter', () => {
it("doesn't show a gutter", () => {
cy.setCodemirrorContent('```javascript! \nlet x = 0\n```')
findHljsCodeBlock()
.should('not.have.class', 'showGutter')
.should('have.class', 'wrapLines')
findHljsCodeBlock().should('not.have.class', 'showGutter').should('have.class', 'wrapLines')
findHljsCodeBlock()
.find('.linenumber')
.should('not.be.visible')
findHljsCodeBlock().find('.linenumber').should('not.be.visible')
})
})
})
@ -41,28 +34,17 @@ describe('Code', () => {
describe('with the language and show gutter', () => {
it('shows the correct line number', () => {
cy.setCodemirrorContent('```javascript= \nlet x = 0\n```')
findHljsCodeBlock()
.should('have.class', 'showGutter')
findHljsCodeBlock().should('have.class', 'showGutter')
findHljsCodeBlock()
.find('.linenumber')
.should('be.visible')
.text()
.should('eq', '1')
findHljsCodeBlock().find('.linenumber').should('be.visible').text().should('eq', '1')
})
describe('and line wrapping', () => {
it('shows the correct line number', () => {
cy.setCodemirrorContent('```javascript=! \nlet x = 0\n```')
findHljsCodeBlock()
.should('have.class', 'showGutter')
.should('have.class', 'wrapLines')
findHljsCodeBlock().should('have.class', 'showGutter').should('have.class', 'wrapLines')
findHljsCodeBlock()
.find('.linenumber')
.should('be.visible')
.text()
.should('eq', '1')
findHljsCodeBlock().find('.linenumber').should('be.visible').text().should('eq', '1')
})
})
})
@ -70,14 +52,9 @@ describe('Code', () => {
describe('with the language, show gutter with a start number', () => {
it('shows the correct line number', () => {
cy.setCodemirrorContent('```javascript=100 \nlet x = 0\n```')
findHljsCodeBlock()
.should('have.class', 'showGutter')
findHljsCodeBlock().should('have.class', 'showGutter')
findHljsCodeBlock()
.find('.linenumber')
.should('be.visible')
.text()
.should('eq', '100')
findHljsCodeBlock().find('.linenumber').should('be.visible').text().should('eq', '100')
})
it('shows the correct line number and continues in another codeblock', () => {
@ -90,33 +67,15 @@ describe('Code', () => {
.should('be.visible')
.text()
.should('eq', '100')
findHljsCodeBlock()
.first()
.find('.linenumber')
.last()
.should('be.visible')
.text()
.should('eq', '101')
findHljsCodeBlock()
.last()
.find('.linenumber')
.first()
.should('be.visible')
.text()
.should('eq', '102')
findHljsCodeBlock().first().find('.linenumber').last().should('be.visible').text().should('eq', '101')
findHljsCodeBlock().last().find('.linenumber').first().should('be.visible').text().should('eq', '102')
})
describe('and line wrapping', () => {
it('shows the correct line number', () => {
cy.setCodemirrorContent('```javascript=100! \nlet x = 0\n```')
findHljsCodeBlock()
.should('have.class', 'showGutter')
.should('have.class', 'wrapLines')
findHljsCodeBlock()
.find('.linenumber')
.should('be.visible')
.text()
.should('eq', '100')
findHljsCodeBlock().should('have.class', 'showGutter').should('have.class', 'wrapLines')
findHljsCodeBlock().find('.linenumber').should('be.visible').text().should('eq', '100')
})
it('shows the correct line number and continues in another codeblock', () => {
@ -130,20 +89,8 @@ describe('Code', () => {
.should('be.visible')
.text()
.should('eq', '100')
findHljsCodeBlock()
.first()
.find('.linenumber')
.last()
.should('be.visible')
.text()
.should('eq', '101')
findHljsCodeBlock()
.last()
.find('.linenumber')
.first()
.should('be.visible')
.text()
.should('eq', '102')
findHljsCodeBlock().first().find('.linenumber').last().should('be.visible').text().should('eq', '101')
findHljsCodeBlock().last().find('.linenumber').first().should('be.visible').text().should('eq', '102')
})
})
})
@ -151,22 +98,17 @@ describe('Code', () => {
it('has a working copy button', () => {
cy.setCodemirrorContent('```javascript \nlet x = 0\n```')
cy.get(`iframe[data-cypress-id="documentIframe"]`)
.then(($element: JQuery) => {
const frame = $element[0] as HTMLIFrameElement
cy.getById('documentIframe').then((element: JQuery<HTMLElement>) => {
const frame = element.get(0) as HTMLIFrameElement
if (frame === null || frame.contentWindow === null) {
return cy.wrap(null)
}
cy.spy(frame.contentWindow.navigator.clipboard, 'writeText')
.as('copy')
cy.spy(frame.contentWindow.navigator.clipboard, 'writeText').as('copy')
})
cy.getIframeBody()
.find('[data-cypress-id="copy-code-button"]')
.click()
cy.getIframeBody().findById('copy-code-button').click()
cy.get('@copy')
.should('be.calledWithExactly', 'let x = 0\n')
cy.get('@copy').should('be.calledWithExactly', 'let x = 0\n')
})
})

View file

@ -11,12 +11,12 @@ describe('History', () => {
})
it('Cards', () => {
cy.get('div.card').should('be.visible')
cy.getById('history-card').should('be.visible')
})
it('Table', () => {
cy.get('[data-cypress-id="history-mode-table"]').click()
cy.get('[data-cypress-id="history-table"]').should('be.visible')
cy.getById('history-mode-table').click()
cy.getById('history-table').should('be.visible')
})
})
@ -39,13 +39,13 @@ describe('History', () => {
})
it('in table view', () => {
cy.get('[data-cypress-id="history-mode-table"]').click()
cy.get('[data-cypress-id="history-table"]').should('be.visible')
cy.get('[data-cypress-id="history-entry-title"]').contains('Features')
cy.getById('history-mode-table').click()
cy.getById('history-table').should('be.visible')
cy.getById('history-entry-title').contains('Features')
})
it('in cards view', () => {
cy.get('[data-cypress-id="history-entry-title"]').contains('Features')
cy.getById('history-entry-title').contains('Features')
})
})
describe('is untitled when not empty', () => {
@ -66,13 +66,13 @@ describe('History', () => {
})
it('in table view', () => {
cy.get('[data-cypress-id="history-mode-table"]').click()
cy.get('[data-cypress-id="history-table"]').should('be.visible')
cy.get('[data-cypress-id="history-entry-title"]').contains('Untitled')
cy.getById('history-mode-table').click()
cy.getById('history-table').should('be.visible')
cy.getById('history-entry-title').contains('Untitled')
})
it('in cards view', () => {
cy.get('[data-cypress-id="history-entry-title"]').contains('Untitled')
cy.getById('history-entry-title').contains('Untitled')
})
})
})
@ -90,15 +90,15 @@ describe('History', () => {
})
it('Cards', () => {
cy.get('div.card').should('be.visible')
cy.get('.history-pin.btn').first().as('pin-button')
cy.getById('history-card').should('be.visible')
cy.getById('history-entry-pin-button').first().as('pin-button')
cy.get('@pin-button').should('have.class', 'pinned').click()
cy.get('@pin-button').should('not.have.class', 'pinned')
})
it('Table', () => {
cy.get('i.fa-table').click()
cy.get('.history-pin.btn').first().as('pin-button')
cy.getById('history-mode-table').click()
cy.getById('history-entry-pin-button').first().as('pin-button')
cy.get('@pin-button').should('have.class', 'pinned').click()
cy.get('@pin-button').should('not.have.class', 'pinned')
})
@ -112,15 +112,15 @@ describe('History', () => {
})
it('Cards', () => {
cy.get('div.card').should('be.visible')
cy.get('.fa-thumb-tack').first().click()
cy.get('.notifications-area .toast').should('be.visible')
cy.getById('history-card').should('be.visible')
cy.getById('history-entry-pin-button').first().click()
cy.getById('notification-toast').should('be.visible')
})
it('Table', () => {
cy.get('i.fa-table').click()
cy.get('.fa-thumb-tack').first().click()
cy.get('.notifications-area .toast').should('be.visible')
cy.getById('history-mode-table').click()
cy.getById('history-entry-pin-button').first().click()
cy.getById('notification-toast').should('be.visible')
})
})
})
@ -136,43 +136,37 @@ describe('History', () => {
})
it('works with valid file', () => {
cy.get('[data-cypress-id="import-history-file-button"]').click()
cy.get('[data-cypress-id="import-history-file-input"]').attachFile({
cy.getById('import-history-file-button').click()
cy.getById('import-history-file-input').attachFile({
filePath: 'history.json',
mimeType: 'application/json'
})
cy.get('[data-cypress-id="history-entry-title"]')
.should('have.length', 1)
.contains('cy-Test')
cy.getById('history-entry-title').should('have.length', 1).contains('cy-Test')
})
it('fails on invalid file', () => {
cy.get('[data-cypress-id="import-history-file-button"]').click()
cy.get('[data-cypress-id="import-history-file-input"]').attachFile({
cy.getById('import-history-file-button').click()
cy.getById('import-history-file-input').attachFile({
filePath: 'history.json.license',
mimeType: 'text/plain'
})
cy.get('[data-cypress-id="notification-toast"]').should('be.visible')
cy.getById('notification-toast').should('be.visible')
})
it('works when selecting two files with the same name', () => {
cy.get('[data-cypress-id="import-history-file-button"]').click()
cy.get('[data-cypress-id="import-history-file-input"]').attachFile({
cy.getById('import-history-file-button').click()
cy.getById('import-history-file-input').attachFile({
filePath: 'history.json',
mimeType: 'application/json'
})
cy.get('[data-cypress-id="history-entry-title"]')
.should('have.length', 1)
.contains('cy-Test')
cy.get('[data-cypress-id="import-history-file-button"]').click()
cy.get('[data-cypress-id="import-history-file-input"]').attachFile({
cy.getById('history-entry-title').should('have.length', 1).contains('cy-Test')
cy.getById('import-history-file-button').click()
cy.getById('import-history-file-input').attachFile({
filePath: 'history-2.json',
fileName: 'history.json',
mimeType: 'application/json'
})
cy.get('[data-cypress-id="history-entry-title"]')
.should('have.length', 2)
.contains('cy-Test2')
cy.getById('history-entry-title').should('have.length', 2).contains('cy-Test2')
})
})
})

View file

@ -10,34 +10,33 @@ describe('Import markdown file', () => {
})
it('import on blank note', () => {
cy.get('[data-cypress-id="menu-import"]')
.click()
cy.get('[data-cypress-id="menu-import-markdown"]')
.click()
cy.get('[data-cypress-id="menu-import-markdown-input"]')
.attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
.should('have.text', ':)')
cy.getById('menu-import').click()
cy.getById('menu-import-markdown').click()
cy.getById('menu-import-markdown-input').attachFile({
filePath: 'import.md',
mimeType: 'text/markdown'
})
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should(
'have.text',
'# Some short import test file'
)
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span').should('have.text', ':)')
})
it('import on note with content', () => {
cy.setCodemirrorContent('test\nabc')
cy.get('[data-cypress-id="menu-import"]')
.click()
cy.get('[data-cypress-id="menu-import-markdown"]')
.click()
cy.get('[data-cypress-id="menu-import-markdown-input"]')
.attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', 'test')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
.should('have.text', 'abc')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span')
.should('have.text', '# Some short import test file')
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span')
.should('have.text', ':)')
cy.getById('menu-import').click()
cy.getById('menu-import-markdown').click()
cy.getById('menu-import-markdown-input').attachFile({
filePath: 'import.md',
mimeType: 'text/markdown'
})
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', 'test')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span').should('have.text', 'abc')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span').should(
'have.text',
'# Some short import test file'
)
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span').should('have.text', ':)')
})
})

View file

@ -13,58 +13,48 @@ describe('Intro page', () => {
describe('customizable content', () => {
it('fetches and shows the correct intro page content', () => {
cy.getMarkdownBody()
.contains('test content')
cy.getMarkdownBody().contains('test content')
})
it('won\'t show anything if no content was found', () => {
it("won't show anything if no content was found", () => {
cy.intercept('/mock-backend/public/intro.md', {
statusCode: 404
})
cy.visit('/')
cy.get(`iframe[data-cypress-id="documentIframe"]`)
.should('not.exist')
cy.getById('documentIframe').should('not.exist')
})
})
describe('features button', () => {
it('is hidden when logged in', () => {
cy.get('[data-cypress-id="features-button"]')
.should('not.exist')
cy.getById('features-button').should('not.exist')
})
it('is visible when logged out', () => {
cy.logout()
cy.get('[data-cypress-id="features-button"]')
.should('exist')
cy.getById('features-button').should('exist')
})
})
describe('sign in button', () => {
it('is hidden when logged in', () => {
cy.get('[data-cypress-id="sign-in-button"]')
.should('not.exist')
cy.getById('sign-in-button').should('not.exist')
})
it('is visible when logged out', () => {
cy.logout()
cy.get('[data-cypress-id="sign-in-button"]')
.should('exist')
cy.getById('sign-in-button').should('exist')
})
})
describe('version dialog', () => {
it('can be opened and closed', () => {
cy.get('[data-cypress-id="version-modal"]')
.should('not.exist')
cy.get('[data-cypress-id="show-version-modal"]')
.click()
cy.get('[data-cypress-id="version-modal"]')
.should('be.visible')
cy.get('[data-cypress-id="version-modal"] .modal-header .close')
.click()
cy.get('[data-cypress-id="version-modal"]')
.should('not.exist')
cy.getById('version-modal').should('not.exist')
cy.getById('show-version-modal').click()
cy.getById('version-modal').should('be.visible')
cy.getById('version-modal').find('.modal-header .close').click()
cy.getById('version-modal').should('not.exist')
})
})
})

View file

@ -12,26 +12,17 @@ describe('Languages', () => {
})
it('all languages are available', () => {
cy.get('option')
.as('languages')
cy.get('@languages')
.should('have.length', 28)
languages.forEach(language => {
cy.get('@languages')
.contains(language)
cy.getById('language-picker').find('option').as('languages')
cy.get('@languages').should('have.length', 28)
languages.forEach((language) => {
cy.get('@languages').contains(language)
})
})
it('language changes affect the UI', () => {
cy.get('select')
.select('English')
cy.get('.d-inline-flex.btn-primary')
.find('span')
.contains('New note')
cy.get('select')
.select('Deutsch')
cy.get('.d-inline-flex.btn-primary')
.find('span')
.contains('Neue Notiz')
cy.getById('language-picker').select('English')
cy.getById('new-note-button').find('span').contains('New note')
cy.getById('language-picker').select('Deutsch')
cy.getById('new-note-button').find('span').contains('Neue Notiz')
})
})

View file

@ -11,35 +11,11 @@ describe('Links Intro', () => {
cy.visit('/')
})
describe('Cover Buttons', () => {
beforeEach(() => {
cy.logout()
})
it('Sign in Cover Button', () => {
cy.get('.cover-button.btn-success')
.click()
cy.url()
.should('include', '/login')
})
it('Features Cover Button', () => {
cy.get('.cover-button.btn-primary')
.click()
cy.url()
.should('include', '/features')
})
})
it('History', () => {
cy.get('#navLinkHistory')
.click()
cy.url()
.should('include', '/history')
cy.get('#navLinkIntro')
.click()
cy.url()
.should('include', '/intro')
cy.getById('navLinkHistory').click()
cy.url().should('include', '/history')
cy.getById('navLinkIntro').click()
cy.url().should('include', '/intro')
})
describe('Menu Buttons logged out', () => {
@ -48,119 +24,31 @@ describe('Links Intro', () => {
})
it('New guest note', () => {
cy.get('.d-inline-flex.btn-primary')
.click()
cy.url()
.should('include', '/new')
cy.getById('new-guest-note-button').click()
cy.url().should('include', '/new')
})
})
describe('Menu Buttons logged in', () => {
it('New note', () => {
cy.get('.d-inline-flex.btn-primary')
.click()
cy.url()
.should('include', '/new')
cy.getById('new-note-button').click()
cy.url().should('include', '/new')
})
describe('User Menu', () => {
beforeEach(() => {
cy.get('#dropdown-user')
.click()
cy.getById('user-dropdown').click()
})
it('Features', () => {
cy.get('a.dropdown-item > i.fa-bolt')
.click()
cy.url()
.should('include', '/features')
cy.getById('user-dropdown-features-button').click()
cy.url().should('include', '/features')
})
it('Profile', () => {
cy.get('a.dropdown-item > i.fa-user')
.click()
cy.url()
.should('include', '/profile')
cy.getById('user-dropdown-profile-button').click()
cy.url().should('include', '/profile')
})
})
})
describe('Feature Links', () => {
it('Share-Notes', () => {
cy.get('i.fa-bolt')
.click()
cy.url()
.should('include', '/features#Share-Notes')
})
it('KaTeX', () => {
cy.get('i.fa-bar-chart')
.click()
cy.url()
.should('include', '/features#MathJax')
})
it('Slide-Mode', () => {
cy.get('i.fa-television')
.click()
cy.url()
.should('include', '/features#Slide-Mode')
})
})
describe('Powered By Links', () => {
it('HedgeDoc', () => {
cy.get('a[href="https://hedgedoc.org"]')
.checkExternalLink('https://hedgedoc.org')
})
it('Releases', () => {
cy.get('a[href*="/n/release-notes"]')
.click()
cy.url()
.should('include', '/n/release-notes')
})
it('Privacy', () => {
cy.get('a[href="https://example.com/privacy"]')
.checkExternalLink('https://example.com/privacy')
})
it('TermsOfUse', () => {
cy.get('a[href="https://example.com/termsOfUse"]')
.checkExternalLink('https://example.com/termsOfUse')
})
it('Imprint', () => {
cy.get('a[href="https://example.com/imprint"]')
.checkExternalLink('https://example.com/imprint')
})
})
describe('Follow us Links', () => {
it('Github', () => {
cy.get('a[href="https://github.com/hedgedoc/"]')
.checkExternalLink('https://github.com/hedgedoc/')
})
it('Discourse', () => {
cy.get('a[href="https://community.hedgedoc.org"]')
.checkExternalLink('https://community.hedgedoc.org')
})
it('Matrix', () => {
cy.get('a[href="https://matrix.to/#/#hedgedoc:matrix.org"]')
.checkExternalLink('https://matrix.to/#/#hedgedoc:matrix.org')
})
it('Mastodon', () => {
cy.get('a[href="https://social.hedgedoc.org"]')
.checkExternalLink('https://social.hedgedoc.org')
})
it('POEditor', () => {
cy.get('a[href="https://translate.hedgedoc.org"]')
.checkExternalLink('https://translate.hedgedoc.org')
})
})
})

View file

@ -13,19 +13,15 @@ describe('Link gets replaced with embedding: ', () => {
it('GitHub Gist', () => {
cy.setCodemirrorContent('https://gist.github.com/schacon/1')
cy.getMarkdownBody()
.find('[data-cypress-id="click-shield-gist"] .preview-background')
.parent()
.click()
cy.getMarkdownBody()
.find('iframe[data-cypress-id=gh-gist]')
.should('be.visible')
cy.getMarkdownBody().findById('click-shield-gist').find('.preview-background').parent().click()
cy.getMarkdownBody().findById('gh-gist').should('be.visible')
})
it('YouTube', () => {
cy.setCodemirrorContent('https://www.youtube.com/watch?v=YE7VzlLtp-4')
cy.getMarkdownBody()
.find('[data-cypress-id="click-shield-youtube"] .preview-background')
.findById('click-shield-youtube')
.find('.preview-background')
.should('have.attr', 'src', 'https://i.ytimg.com/vi/YE7VzlLtp-4/maxresdefault.jpg')
.parent()
.click()
@ -35,36 +31,37 @@ describe('Link gets replaced with embedding: ', () => {
})
it('Vimeo', () => {
cy.intercept({
cy.intercept(
{
method: 'GET',
url: 'https://vimeo.com/api/v2/video/23237102.json'
}, {
},
{
statusCode: 200,
headers: {
'content-type': 'application/json'
},
body: '[{"thumbnail_large": "https://i.vimeocdn.com/video/503631401_640.jpg"}]'
})
}
)
cy.setCodemirrorContent('https://vimeo.com/23237102')
cy.getMarkdownBody()
.find('[data-cypress-id="click-shield-vimeo"] .preview-background')
.findById('click-shield-vimeo')
.find('.preview-background')
.should('have.attr', 'src', 'https://i.vimeocdn.com/video/503631401_640.jpg')
.parent()
.click()
cy.getMarkdownBody()
.find('iframe')
.should('have.attr', 'src', 'https://player.vimeo.com/video/23237102?autoplay=1')
cy.getMarkdownBody().find('iframe').should('have.attr', 'src', 'https://player.vimeo.com/video/23237102?autoplay=1')
})
it('Asciinema', () => {
cy.setCodemirrorContent('https://asciinema.org/a/117928')
cy.getMarkdownBody()
.find('[data-cypress-id="click-shield-asciinema"] .preview-background')
.findById('click-shield-asciinema')
.find('.preview-background')
.should('have.attr', 'src', 'https://asciinema.org/a/117928.png')
.parent()
.click()
cy.getMarkdownBody()
.find('iframe')
.should('have.attr', 'src', 'https://asciinema.org/a/117928/embed?autoplay=1')
cy.getMarkdownBody().find('iframe').should('have.attr', 'src', 'https://asciinema.org/a/117928/embed?autoplay=1')
})
})

View file

@ -5,39 +5,31 @@
*/
describe('The status bar text length info', () => {
const warningTestContent = ('0123456789'.repeat(10))
const dangerTestContent = ('0123456789'.repeat(20))
const tooMuchTestContent = `${ dangerTestContent }a`
const warningTestContent = '0123456789'.repeat(10)
const dangerTestContent = '0123456789'.repeat(20)
const tooMuchTestContent = `${dangerTestContent}a`
beforeEach(() => {
cy.visitTestEditor()
})
it('shows the maximal length of the document as number of available characters in the tooltip', () => {
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.attribute('title')
.should('contain', ' 200 ')
cy.getById('remainingCharacters').attribute('title').should('contain', ' 200 ')
})
it('color is set to "warning" on <= 100 characters remaining', () => {
cy.setCodemirrorContent(warningTestContent)
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.should('have.class', 'text-warning')
cy.getById('remainingCharacters').should('have.class', 'text-warning')
})
it('color is set to danger on <= 0 characters remaining', () => {
cy.setCodemirrorContent(dangerTestContent)
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.should('have.class', 'text-danger')
cy.getById('remainingCharacters').should('have.class', 'text-danger')
})
it('shows a warning and opens a modal', () => {
cy.setCodemirrorContent(tooMuchTestContent)
cy.get('[data-cypress-id="limitReachedModal"]')
.should('be.visible')
cy.getIframeBody()
.find('[data-cypress-id="limitReachedMessage"]')
.should('be.visible')
cy.getById('limitReachedModal').should('be.visible')
cy.getIframeBody().findById('limitReachedMessage').should('be.visible')
})
})

View file

@ -29,63 +29,63 @@ describe('Motd', () => {
it('shows the correct alert Motd text', () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.getById('motd').contains(motdMockContent)
})
it('can be dismissed', () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
cy.getById('motd').contains(motdMockContent)
cy.getById('motd-dismiss')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.getById('motd').should('not.exist')
})
it("won't show again after dismiss and reload", () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
cy.getById('motd').contains(motdMockContent)
cy.getById('motd-dismiss')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.getById('motd').should('not.exist')
cy.reload()
cy.get('main').should('exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.getById('motd').should('not.exist')
})
it("will show again after reload without dismiss", () => {
it('will show again after reload without dismiss', () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.getById('motd').contains(motdMockContent)
cy.reload()
cy.get('main').should('exist')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.getById('motd').contains(motdMockContent)
})
it("won't show again after dismiss and page navigation", () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
cy.getById('motd').contains(motdMockContent)
cy.getById('motd-dismiss')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.get('#navLinkHistory').click()
cy.getById('motd').should('not.exist')
cy.getById('navLinkHistory').click()
cy.get('main').should('exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.getById('motd').should('not.exist')
})
it("won't show if no file exists", () => {
cy.visit('/')
cy.get('main').should('exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.getById('motd').should('not.exist')
})
})

View file

@ -6,68 +6,65 @@
describe('profile page', () => {
beforeEach(() => {
cy.intercept({
cy.intercept(
{
url: '/mock-backend/api/private/tokens',
method: 'GET'
}, {
},
{
body: [
{
label: 'cypress-App',
created: 1601991518
}
]
})
cy.intercept({
}
)
cy.intercept(
{
url: '/mock-backend/api/private/tokens',
method: 'POST'
}, {
},
{
body: {
label: 'cypress',
secret: 'c-y-p-r-e-s-s',
created: Date.now()
}
})
cy.intercept({
}
)
cy.intercept(
{
url: '/mock-backend/api/private/tokens/1601991518',
method: 'DELETE'
}, {
},
{
body: []
})
}
)
cy.visit('/profile')
})
describe('access tokens', () => {
it('list existing tokens', () => {
cy.get('.card.access-tokens .list-group-item .text-start.col')
.contains('cypress-App')
cy.getById('access-token-label').contains('cypress-App')
})
it('delete token', () => {
cy.get('.modal-dialog')
.should('not.exist')
cy.get('.card.access-tokens .list-group-item .btn-danger')
.click()
cy.get('.modal-dialog')
.should('be.visible')
.get('.modal-footer .btn-danger')
.click()
cy.get('.modal-dialog')
.should('not.exist')
cy.getById('access-token-delete-button').click()
cy.getById('access-token-modal-delete').as('deletion-modal')
cy.get('@deletion-modal').should('be.visible').find('.modal-footer .btn-danger').click()
cy.get('@deletion-modal').should('not.exist')
})
it('add token', () => {
cy.get('.card.access-tokens .btn-primary')
.should('be.disabled')
cy.get('.card.access-tokens input[type=text]')
.type('cypress')
cy.get('.modal-dialog')
.should('not.exist')
cy.get('.card.access-tokens .btn-primary')
.should('not.be.disabled')
.click()
cy.get('.modal-dialog')
cy.getById('access-token-add-button').should('be.disabled')
cy.getById('access-token-add-input').type('cypress')
cy.getById('access-token-modal-add').should('not.exist')
cy.getById('access-token-add-button').should('not.be.disabled').click()
cy.getById('access-token-modal-add')
.should('be.visible')
.get('.modal-dialog input[readonly]')
.find('input[readonly]')
.should('have.value', 'c-y-p-r-e-s-s')
})
})

View file

@ -13,16 +13,9 @@ describe('Quote extra tags', function () {
it('renders correctly', () => {
cy.setCodemirrorContent('[name=testy mctestface]')
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.find('.fa-user')
.should('be.visible')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').find('.fa-user').should('be.visible')
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.contains('testy mctestface')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').contains('testy mctestface')
})
})
@ -30,16 +23,9 @@ describe('Quote extra tags', function () {
it('renders correctly', () => {
cy.setCodemirrorContent(`[time=always]`)
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.find('.fa-clock-o')
.should('be.visible')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').find('.fa-clock-o').should('be.visible')
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.contains('always')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').contains('always')
})
})
@ -47,24 +33,15 @@ describe('Quote extra tags', function () {
it('renders correctly', () => {
cy.setCodemirrorContent(`[color=#b51f08]`)
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.find('.fa-tag')
.should('be.visible')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').find('.fa-tag').should('be.visible')
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('be.visible')
.should('have.css', 'color', 'rgb(181, 31, 8)')
cy.getMarkdownBody().find('.blockquote-extra').should('be.visible').should('have.css', 'color', 'rgb(181, 31, 8)')
})
it('doesn\'t render in a blockquote and dyes the blockquote border', () => {
it("doesn't render in a blockquote and dyes the blockquote border", () => {
cy.setCodemirrorContent(`> [color=#b51f08] HedgeDoc`)
cy.getMarkdownBody()
.find('.blockquote-extra')
.should('not.exist')
cy.getMarkdownBody().find('.blockquote-extra').should('not.exist')
cy.getMarkdownBody()
.find('blockquote')

View file

@ -21,26 +21,21 @@ describe('Short code gets replaced or rendered: ', () => {
describe('slideshare', () => {
it('renders a plain link', () => {
cy.setCodemirrorContent(`{%slideshare example/123456789 %}`)
cy.getMarkdownBody()
.find('a')
.should('have.attr', 'href', 'https://www.slideshare.net/example/123456789')
cy.getMarkdownBody().find('a').should('have.attr', 'href', 'https://www.slideshare.net/example/123456789')
})
})
describe('speakerdeck', () => {
it('renders a plain link', () => {
cy.setCodemirrorContent(`{%speakerdeck example/123456789 %}`)
cy.getMarkdownBody()
.find('a')
.should('have.attr', 'href', 'https://speakerdeck.com/example/123456789')
cy.getMarkdownBody().find('a').should('have.attr', 'href', 'https://speakerdeck.com/example/123456789')
})
})
describe('youtube', () => {
it('renders click-shield', () => {
cy.setCodemirrorContent(`{%youtube YE7VzlLtp-4 %}`)
cy.getMarkdownBody()
.find('[data-cypress-id="click-shield-youtube"]')
cy.getMarkdownBody().findById('click-shield-youtube')
})
})
})

View file

@ -18,7 +18,10 @@ const authProvidersDisabled = {
openid: false
}
const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProviders: Partial<typeof authProvidersDisabled>) => {
const initLoggedOutTestWithCustomAuthProviders = (
cy: Cypress.cy,
enabledProviders: Partial<typeof authProvidersDisabled>
) => {
cy.loadConfig({
authProviders: {
...authProvidersDisabled,
@ -32,8 +35,7 @@ const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProvide
describe('When logged-in, ', () => {
it('sign-in button is hidden', () => {
cy.visit('/')
cy.get('[data-cypress-id=sign-in-button]')
.should('not.exist')
cy.getById('sign-in-button').should('not.exist')
})
})
@ -41,8 +43,7 @@ describe('When logged-out ', () => {
describe('and no auth-provider is enabled, ', () => {
it('sign-in button is hidden', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {})
cy.get('[data-cypress-id=sign-in-button]')
.should('not.exist')
cy.getById('sign-in-button').should('not.exist')
})
})
@ -51,27 +52,21 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
internal: true
})
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
cy.getById('sign-in-button').should('be.visible').should('have.attr', 'href', '/login')
})
it('sign-in button points to login route: ldap', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
ldap: true
})
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
cy.getById('sign-in-button').should('be.visible').should('have.attr', 'href', '/login')
})
it('sign-in button points to login route: openid', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
openid: true
})
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
cy.getById('sign-in-button').should('be.visible').should('have.attr', 'href', '/login')
})
})
@ -80,7 +75,7 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
saml: true
})
cy.get('[data-cypress-id=sign-in-button]')
cy.getById('sign-in-button')
.should('be.visible')
// The absolute URL is used because it is defined as API base URL absolute.
.should('have.attr', 'href', '/mock-backend/api/private/auth/saml')
@ -93,9 +88,7 @@ describe('When logged-out ', () => {
saml: true,
github: true
})
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
cy.getById('sign-in-button').should('be.visible').should('have.attr', 'href', '/login')
})
})
@ -105,9 +98,7 @@ describe('When logged-out ', () => {
saml: true,
internal: true
})
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
cy.getById('sign-in-button').should('be.visible').should('have.attr', 'href', '/login')
})
})
})

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
describe("Slideshow only page", () => {
describe('Slideshow only page', () => {
it('renders slide show mode', () => {
cy.visit('/p/test')
cy.getReveal().should("exist")
cy.getReveal().should('exist')
})
})

View file

@ -10,19 +10,19 @@ describe('Split view', () => {
})
it('can show both panes', () => {
cy.get('[data-cypress-id="view-mode-both"]').click()
cy.getById('view-mode-both').click()
cy.get('.splitter.left').should('be.visible')
cy.get('.splitter.right').should('be.visible')
})
it('can show only preview pane', () => {
cy.get('[data-cypress-id="view-mode-preview"]').click()
cy.getById('view-mode-preview').click()
cy.get('.splitter.left').should('be.not.visible')
cy.get('.splitter.right').should('be.visible')
})
it('can show only editor pane', () => {
cy.get('[data-cypress-id="view-mode-editor"]').click()
cy.getById('view-mode-editor').click()
cy.get('.splitter.left').should('be.visible')
cy.get('.splitter.right').should('be.not.visible')
})
@ -31,7 +31,7 @@ describe('Split view', () => {
cy.get('.splitter.left').then((leftPanebefore) => {
const widthBefore = leftPanebefore.outerWidth()
cy.get('[data-cypress-id="view-mode-both"]').click()
cy.getById('view-mode-both').click()
cy.get('.split-divider').should('be.visible').trigger('mousedown', { buttons: 1 })
cy.document().trigger('mousemove', { buttons: 1, pageX: 0, pageY: 0 })
cy.get('.split-divider').trigger('mouseup')

View file

@ -17,31 +17,23 @@ describe('Task lists ', () => {
describe('render with checkboxes ', () => {
it('when unchecked', () => {
cy.setCodemirrorContent(TEST_STRING_UNCHECKED)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.should('have.length', 6)
cy.getMarkdownBody().find('input[type=checkbox]').should('have.length', 6)
})
it('when checked lowercase', () => {
cy.setCodemirrorContent(TEST_STRING_CHECKED_LOWER)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.should('have.length', 6)
cy.getMarkdownBody().find('input[type=checkbox]').should('have.length', 6)
})
it('when checked uppercase', () => {
cy.setCodemirrorContent(TEST_STRING_CHECKED_UPPER)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.should('have.length', 6)
cy.getMarkdownBody().find('input[type=checkbox]').should('have.length', 6)
})
})
it('do not render as checkboxes when invalid', () => {
cy.setCodemirrorContent(TEST_STRING_INVALID)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.should('have.length', 0)
cy.getMarkdownBody().find('input[type=checkbox]').should('have.length', 0)
})
describe('are clickable and change the markdown source ', () => {
@ -49,39 +41,30 @@ describe('Task lists ', () => {
cy.setCodemirrorContent(TEST_STRING_UNCHECKED)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.each(box => {
.each((box) => {
box.trigger('click')
})
cy.get('.CodeMirror-line > span')
.should('exist')
.should('contain.text', '[x]')
.should('not.contain.text', '[ ]')
cy.get('.CodeMirror-line > span').should('exist').should('contain.text', '[x]').should('not.contain.text', '[ ]')
})
it('from checked (lowercase) to unchecked', () => {
cy.setCodemirrorContent(TEST_STRING_CHECKED_LOWER)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.each(box => {
.each((box) => {
box.trigger('click')
})
cy.get('.CodeMirror-line > span')
.should('exist')
.should('contain.text', '[ ]')
.should('not.contain.text', '[x]')
cy.get('.CodeMirror-line > span').should('exist').should('contain.text', '[ ]').should('not.contain.text', '[x]')
})
it('from checked (uppercase) to unchecked', () => {
cy.setCodemirrorContent(TEST_STRING_CHECKED_UPPER)
cy.getMarkdownBody()
.find('input[type=checkbox]')
.each(box => {
.each((box) => {
box.trigger('click')
})
cy.get('.CodeMirror-line > span')
.should('exist')
.should('contain.text', '[ ]')
.should('not.contain.text', '[X]')
cy.get('.CodeMirror-line > span').should('exist').should('contain.text', '[ ]').should('not.contain.text', '[X]')
})
})
})

View file

@ -11,295 +11,220 @@ describe('Toolbar Buttons', () => {
beforeEach(() => {
cy.visitTestEditor()
cy.get('.CodeMirror')
.click()
.get('textarea')
.as('codeinput')
cy.get('.CodeMirror').click().get('textarea').as('codeinput')
})
describe('for single line text', () => {
beforeEach(() => {
cy.setCodemirrorContent(testText)
cy.get('.CodeMirror-line > span')
.should('exist')
.should('have.text', testText)
cy.get('.CodeMirror-line > span').should('exist').should('have.text', testText)
})
describe('with selection', () => {
beforeEach(() => {
cy.get('@codeinput')
.type('{ctrl}a')
cy.get('@codeinput').type('{ctrl}a')
})
it('should format as bold', () => {
cy.get('.btn-toolbar [data-cypress-id="format-bold"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `**${ testText }**`)
cy.getById('format-bold').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `**${testText}**`)
})
it('should format as italic', () => {
cy.get('.btn-toolbar [data-cypress-id="format-italic"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `*${ testText }*`)
cy.getById('format-italic').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `*${testText}*`)
})
it('should format as underline', () => {
cy.get('.btn-toolbar [data-cypress-id="format-underline"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `++${ testText }++`)
cy.getById('format-underline').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `++${testText}++`)
})
it('should format as strikethrough', () => {
cy.get('.btn-toolbar [data-cypress-id="format-strikethrough"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `~~${ testText }~~`)
cy.get('.btn-toolbar [data-cypress-id="format-strikethrough').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `~~${testText}~~`)
})
it('should format as subscript', () => {
cy.get('.btn-toolbar [data-cypress-id="format-subscript"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `~${ testText }~`)
cy.getById('format-subscript').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `~${testText}~`)
})
it('should format as superscript', () => {
cy.get('.btn-toolbar [data-cypress-id="format-superscript"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `^${ testText }^`)
cy.getById('format-superscript').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `^${testText}^`)
})
it('should format the line as code block', () => {
cy.get('.btn-toolbar [data-cypress-id="format-code-block"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '```')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', testText)
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span')
.should('have.text', '```')
cy.getById('format-code-block').click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', '```')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should('have.text', testText)
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span').should(
'have.text',
'```'
)
})
it('should format links', () => {
cy.get('.btn-toolbar [data-cypress-id="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `[${ testText }](https://)`)
cy.getById('format-link').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `[${testText}](https://)`)
})
it('should format as image', () => {
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![${ testText }](https://)`)
cy.getById('format-image').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `![${testText}](https://)`)
})
})
it('should format line as heading', () => {
cy.get('.btn-toolbar [data-cypress-id="format-heading"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `# ${ testText }`)
cy.get('.fa-header')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `## ${ testText }`)
cy.getById('format-heading').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `# ${testText}`)
cy.get('.fa-header').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `## ${testText}`)
})
it('should format the line as code', () => {
cy.get('.btn-toolbar [data-cypress-id="format-code-block"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '```')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', testText)
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span')
.should('have.text', '```')
cy.getById('format-code-block').click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', '```')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should('have.text', testText)
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span').should('have.text', '```')
})
it('should add a quote', () => {
cy.get('.btn-toolbar [data-cypress-id="format-block-quote"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `> ${ testText }`)
cy.get('.btn-toolbar [data-cypress-id="format-block-quote"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `> > ${ testText }`)
cy.getById('format-block-quote').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `> ${testText}`)
cy.getById('format-block-quote').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `> > ${testText}`)
})
it('should format as unordered list', () => {
cy.get('.btn-toolbar [data-cypress-id="format-unordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- ${ testText }`)
cy.get('.btn-toolbar [data-cypress-id="format-unordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- - ${ testText }`)
cy.getById('format-unordered-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `- ${testText}`)
cy.getById('format-unordered-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `- - ${testText}`)
})
it('should format as ordered list', () => {
cy.get('.btn-toolbar [data-cypress-id="format-ordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `1. ${ testText }`)
cy.get('.btn-toolbar [data-cypress-id="format-ordered-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `1. 1. ${ testText }`)
cy.getById('format-ordered-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `1. ${testText}`)
cy.getById('format-ordered-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `1. 1. ${testText}`)
})
it('should format as check list', () => {
cy.get('.btn-toolbar [data-cypress-id="format-check-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- [ ] ${ testText }`)
cy.get('.btn-toolbar [data-cypress-id="format-check-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- [ ] - [ ] ${ testText }`)
cy.getById('format-check-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `- [ ] ${testText}`)
cy.getById('format-check-list').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `- [ ] - [ ] ${testText}`)
})
it('should insert links', () => {
cy.get('.btn-toolbar [data-cypress-id="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${ testText }[](https://)`)
cy.getById('format-link').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `${testText}[](https://)`)
})
it('should insert an empty image link', () => {
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${ testText }![](https://)`)
cy.getById('format-image').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `${testText}![](https://)`)
})
})
describe('for single line link with selection', () => {
beforeEach(() => {
cy.setCodemirrorContent(testLink)
cy.get('.CodeMirror-line > span')
.should('exist')
.should('have.text', testLink)
cy.get('@codeinput')
.type('{ctrl}a')
cy.get('.CodeMirror-line > span').should('exist').should('have.text', testLink)
cy.get('@codeinput').type('{ctrl}a')
})
it('should format as link', () => {
cy.get('.btn-toolbar [data-cypress-id="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `[](${ testLink })`)
cy.getById('format-link').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `[](${testLink})`)
})
it('should format as image', () => {
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ testLink })`)
cy.getById('format-image').click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span').should('have.text', `![](${testLink})`)
})
})
describe('for no text', () => {
it('should add an empty code block', () => {
cy.get('.btn-toolbar [data-cypress-id="format-code-block"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
.should('have.text', '```')
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span')
.should('have.text', '```')
cy.getById('format-code-block').click()
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span').should('have.text', '```')
cy.get('.CodeMirror-code > div.CodeMirror-activeline > .CodeMirror-line > span span').should('have.text', '```')
})
it('should insert lines', () => {
cy.get('.btn-toolbar [data-cypress-id="format-add-line"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', '----')
cy.getById('format-add-line').click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should('have.text', '----')
})
it('should add a collapsable block', () => {
cy.get('.btn-toolbar [data-cypress-id="format-collapsable-block"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', ':::spoiler Toggle label')
cy.getById('format-collapsable-block').click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should(
'have.text',
':::spoiler Toggle label'
)
})
it('should add a comment', () => {
cy.get('.btn-toolbar [data-cypress-id="format-add-comment"]')
.click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', '> []')
cy.getById('format-add-comment').click()
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should('have.text', '> []')
})
})
describe('for new tables', () => {
beforeEach(() => {
cy.get('.table-picker-container')
.should('not.be.visible')
cy.get('[data-cypress-id="show-table-overlay"]')
.last()
.click()
cy.get('.table-picker-container')
.should('be.visible')
cy.get('.table-picker-container').should('not.be.visible')
cy.getById('show-table-overlay').last().click()
cy.get('.table-picker-container').should('be.visible')
})
it('should open an overlay', () => {
cy.get('.table-container > div:nth-of-type(25)')
.trigger('mouseover')
cy.get('.table-cell.bg-primary')
.should('have.length', 15)
cy.get('.table-picker-container > p')
.contains('5x3')
cy.get('.table-container > div:nth-of-type(25)')
.click()
cy.get('.table-container > div:nth-of-type(25)').trigger('mouseover')
cy.get('.table-cell.bg-primary').should('have.length', 15)
cy.get('.table-picker-container > p').contains('5x3')
cy.get('.table-container > div:nth-of-type(25)').click()
})
it('should open a modal for custom table sizes in the overlay', () => {
cy.get('.modal-dialog')
.should('not.exist')
cy.get('[data-cypress-id="show-custom-table-modal"]')
.first()
.click()
cy.get('.modal-dialog')
.should('be.visible')
cy.get('.modal-content > .d-flex > input')
.first()
.type('5')
cy.get('.modal-content > .d-flex > input')
.last()
.type('3')
cy.get('.modal-footer > button')
.click()
cy.get('.modal-dialog').should('not.exist')
cy.getById('show-custom-table-modal').first().click()
cy.get('.modal-dialog').should('be.visible')
cy.get('.modal-content > .d-flex > input').first().type('5')
cy.get('.modal-content > .d-flex > input').last().type('3')
cy.get('.modal-footer > button').click()
})
afterEach(() => {
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span')
.should('have.text', '| # 1 | # 2 | # 3 | # 4 | # 5 |')
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span')
.should('have.text', '| ---- | ---- | ---- | ---- | ---- |')
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span span')
.should('have.text', '| Text | Text | Text | Text | Text |')
cy.get('.CodeMirror-code > div:nth-of-type(5) > .CodeMirror-line > span span')
.should('have.text', '| Text | Text | Text | Text | Text |')
cy.get('.CodeMirror-activeline > .CodeMirror-line > span ')
.should('have.text', '| Text | Text | Text | Text | Text |')
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span').should(
'have.text',
'| # 1 | # 2 | # 3 | # 4 | # 5 |'
)
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span span').should(
'have.text',
'| ---- | ---- | ---- | ---- | ---- |'
)
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span span').should(
'have.text',
'| Text | Text | Text | Text | Text |'
)
cy.get('.CodeMirror-code > div:nth-of-type(5) > .CodeMirror-line > span span').should(
'have.text',
'| Text | Text | Text | Text | Text |'
)
cy.get('.CodeMirror-activeline > .CodeMirror-line > span ').should(
'have.text',
'| Text | Text | Text | Text | Text |'
)
})
})
describe('for the emoji-picker', () => {
it('should open overlay', () => {
cy.get('emoji-picker')
.should('not.be.visible')
cy.get('[data-cypress-id="show-emoji-picker"]')
.click()
cy.get('emoji-picker')
.should('be.visible')
cy.get('emoji-picker').should('not.be.visible')
cy.getById('show-emoji-picker').click()
cy.get('emoji-picker').should('be.visible')
})
})
})

View file

@ -12,32 +12,32 @@ describe('Test word count with', () => {
it('empty note', () => {
cy.setCodemirrorContent('')
cy.wait(500)
cy.get('[data-cypress-id="sidebar-btn-document-info"]').click()
cy.get('[data-cypress-id="document-info-modal"]').should('be.visible')
cy.get('[data-cypress-id="document-info-word-count"]').should('have.text', '0')
cy.getById('sidebar-btn-document-info').click()
cy.getById('document-info-modal').should('be.visible')
cy.getById('document-info-word-count').should('have.text', '0')
})
it('simple words', () => {
cy.setCodemirrorContent('five words should be enough')
cy.wait(500)
cy.get('[data-cypress-id="sidebar-btn-document-info"]').click()
cy.get('[data-cypress-id="document-info-modal"]').should('be.visible')
cy.get('[data-cypress-id="document-info-word-count"]').should('have.text', '5')
cy.getById('sidebar-btn-document-info').click()
cy.getById('document-info-modal').should('be.visible')
cy.getById('document-info-word-count').should('have.text', '5')
})
it('excluded codeblocks', () => {
cy.setCodemirrorContent('```\nthis is should be ignored\n```\n\ntwo `words`')
cy.wait(500)
cy.get('[data-cypress-id="sidebar-btn-document-info"]').click()
cy.get('[data-cypress-id="document-info-modal"]').should('be.visible')
cy.get('[data-cypress-id="document-info-word-count"]').should('have.text', '2')
cy.getById('sidebar-btn-document-info').click()
cy.getById('document-info-modal').should('be.visible')
cy.getById('document-info-word-count').should('have.text', '2')
})
it('excluded images', () => {
cy.setCodemirrorContent('![ignored alt text](https://dummyimage.com/48) not ignored text')
cy.wait(500)
cy.get('[data-cypress-id="sidebar-btn-document-info"]').click()
cy.get('[data-cypress-id="document-info-modal"]').should('be.visible')
cy.get('[data-cypress-id="document-info-word-count"]').should('have.text', '3')
cy.getById('sidebar-btn-document-info').click()
cy.getById('document-info-modal').should('be.visible')
cy.getById('document-info-word-count').should('have.text', '3')
})
})

View file

@ -11,22 +11,16 @@ describe('YAML Array for deprecated syntax of document tags in frontmatter', ()
it('is shown when using old syntax', () => {
cy.setCodemirrorContent('---\ntags: a, b, c\n---')
cy.getIframeBody()
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('be.visible')
cy.getIframeBody().findById('yamlArrayDeprecationAlert').should('be.visible')
})
it('isn\'t shown when using inline yaml-array', () => {
cy.setCodemirrorContent('---\ntags: [\'a\', \'b\', \'c\']\n---')
cy.getIframeBody()
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('not.exist')
it("isn't shown when using inline yaml-array", () => {
cy.setCodemirrorContent("---\ntags: ['a', 'b', 'c']\n---")
cy.getIframeBody().findById('yamlArrayDeprecationAlert').should('not.exist')
})
it('isn\'t shown when using multi line yaml-array', () => {
it("isn't shown when using multi line yaml-array", () => {
cy.setCodemirrorContent('---\ntags:\n - a\n - b\n - c\n---')
cy.getIframeBody()
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('not.exist')
cy.getIframeBody().findById('yamlArrayDeprecationAlert').should('not.exist')
})
})

View file

@ -15,7 +15,5 @@ declare namespace Cypress {
}
Cypress.Commands.add('checkExternalLink', { prevSubject: 'element' }, ($element: JQuery, url: string) => {
cy.wrap($element)
.should('have.attr', 'href', url)
.should('have.attr', 'target', '_blank')
cy.wrap($element).should('have.attr', 'href', url).should('have.attr', 'target', '_blank')
})

View file

@ -50,9 +50,9 @@ export const config = {
sourceCodeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
issueTrackerUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
},
'iframeCommunication': {
'editorOrigin': 'http://127.0.0.1:3001/',
'rendererOrigin': 'http://127.0.0.1:3001/'
iframeCommunication: {
editorOrigin: 'http://127.0.0.1:3001/',
rendererOrigin: 'http://127.0.0.1:3001/'
}
}

View file

@ -16,26 +16,20 @@ declare namespace Cypress {
}
}
Cypress.Commands.add('fill', {
Cypress.Commands.add(
'fill',
{
prevSubject: 'element'
}, (subject, value) => {
return cy.wrap(subject)
.invoke('val', value)
.trigger('change', { force: true })
})
},
(subject, value) => {
return cy.wrap(subject).invoke('val', value).trigger('change', { force: true })
}
)
Cypress.Commands.add('setCodemirrorContent', (content: string) => {
const line = content.split('\n')
.find(value => value !== '')
cy.get('.CodeMirror')
.click()
.get('textarea')
.type('{ctrl}a')
.type('{backspace}')
.fill(content)
const line = content.split('\n').find((value) => value !== '')
cy.get('.CodeMirror').click().get('textarea').type('{ctrl}a').type('{backspace}').fill(content)
if (line) {
cy.get('.CodeMirror')
.find('.CodeMirror-line')
.should('contain.text', line)
cy.get('.CodeMirror').find('.CodeMirror-line').should('contain.text', line)
}
})

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare namespace Cypress {
interface Chainable {
getById(id: string): Chainable<Element>
findById(id: string): Chainable<Element>
}
}
const CYPRESS_ATTR = 'data-cypress-id'
Cypress.Commands.add('getById', (id: string) => {
return cy.get(`[${CYPRESS_ATTR}="${id}"]`)
})
Cypress.Commands.add(
'findById',
{
prevSubject: 'element'
},
(parent: JQuery<HTMLElement>, id: string) => {
return cy.wrap(parent).find(`[${CYPRESS_ATTR}="${id}"]`)
}
)

View file

@ -24,6 +24,7 @@ import 'cypress-file-upload'
import './checkLinks'
import './config'
import './fill'
import './get-by-id'
import './get-iframe-content'
import './login'
import './visit-test-editor'

View file

@ -15,8 +15,6 @@ declare namespace Cypress {
}
Cypress.Commands.add('logout', () => {
cy.get('#dropdown-user')
.click()
cy.get('.fa-sign-out')
.click()
cy.getById('user-dropdown').click()
cy.getById('user-dropdown-sign-out-button').click()
})

View file

@ -13,26 +13,26 @@ declare namespace Cypress {
export const testNoteId = 'test'
Cypress.Commands.add('visitTestEditor', (query?: string) => {
return cy.visit(`/n/${ testNoteId }${ query ? `?${ query }` : '' }`)
return cy.visit(`/n/${testNoteId}${query ? `?${query}` : ''}`)
})
beforeEach(() => {
cy.intercept(`/mock-backend/api/private/notes/${ testNoteId }-get`, {
"content": "",
"metadata": {
"id": "mock_note_id",
"alias": "mockNote",
"version": 2,
"viewCount": 0,
"updateTime": "2021-04-24T09:27:51.000Z",
"updateUser": {
"userName": "test",
"displayName": "Testy",
"photo": "",
"email": ""
cy.intercept(`/mock-backend/api/private/notes/${testNoteId}-get`, {
content: '',
metadata: {
id: 'mock_note_id',
alias: 'mockNote',
version: 2,
viewCount: 0,
updateTime: '2021-04-24T09:27:51.000Z',
updateUser: {
userName: 'test',
displayName: 'Testy',
photo: '',
email: ''
},
"createTime": "2021-04-24T09:27:51.000Z",
"editedBy": []
createTime: '2021-04-24T09:27:51.000Z',
editedBy: []
}
})
})

View file

@ -14,8 +14,8 @@
"test": "craco test",
"lint": "eslint --max-warnings=0 --ext .ts,.tsx src",
"lint:fix": "eslint --fix --ext .ts,.tsx src",
"format": "prettier -c \"src/**/*.{ts,tsx,js}\"",
"format:fix": "prettier -w \"src/**/*.{ts,tsx,js}\"",
"format": "prettier -c \"src/**/*.{ts,tsx,js}\" \"cypress/**/*.{ts,tsx}\"",
"format:fix": "prettier -w \"src/**/*.{ts,tsx,js}\" \"cypress/**/*.{ts,tsx}\"",
"eject": "react-scripts eject",
"cy:open": "cypress open",
"cy:run:chrome": "cypress run --browser chrome",

View file

@ -22,12 +22,13 @@ export const DeletionModal: React.FC<DeletionModalProps> = ({
onConfirm,
deletionButtonI18nKey,
icon,
children
children,
...props
}) => {
useTranslation()
return (
<CommonModal show={show} onHide={onHide} titleI18nKey={titleI18nKey} icon={icon} closeButton={true}>
<CommonModal show={show} onHide={onHide} titleI18nKey={titleI18nKey} icon={icon} closeButton={true} {...props}>
<Modal.Body className='text-dark'>{children}</Modal.Body>
<Modal.Footer>
<Button variant='danger' onClick={onConfirm}>

View file

@ -1,18 +0,0 @@
/*
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { Modal } from 'react-bootstrap'
import type { CommonModalProps } from './common-modal'
import { CommonModal } from './common-modal'
export const ErrorModal: React.FC<CommonModalProps> = ({ show, onHide, titleI18nKey, icon, children }) => {
return (
<CommonModal show={show} onHide={onHide} titleI18nKey={titleI18nKey} icon={icon} closeButton={true}>
<Modal.Body className='text-dark text-center'>{children}</Modal.Body>
</CommonModal>
)
}

View file

@ -9,6 +9,7 @@ import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { HelpModal } from './help-modal'
import { cypressId } from '../../../../utils/cypress-attribute'
export const HelpButton: React.FC = () => {
const { t } = useTranslation()
@ -18,6 +19,7 @@ export const HelpButton: React.FC = () => {
return (
<Fragment>
<Button
{...cypressId('editor-help-button')}
title={t('editor.documentBar.help')}
className='ml-2 text-secondary'
size='sm'

View file

@ -12,6 +12,7 @@ import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { UploadInput } from '../../sidebar/upload-input'
import { handleUpload } from '../upload-handler'
import { supportedMimeTypes } from '../../../common/upload-image-mimetypes'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface UploadImageButtonProps {
editor?: Editor
@ -42,10 +43,19 @@ export const UploadImageButton: React.FC<UploadImageButtonProps> = ({ editor })
return (
<Fragment>
<Button variant='light' onClick={buttonClick} title={t('editor.editorToolbar.uploadImage')}>
<Button
variant='light'
onClick={buttonClick}
title={t('editor.editorToolbar.uploadImage')}
{...cypressId('editor-toolbar-upload-image-button')}>
<ForkAwesomeIcon icon={'upload'} />
</Button>
<UploadInput onLoad={onUploadImage} acceptedFiles={acceptedMimeTypes} onClickRef={clickRef} />
<UploadInput
onLoad={onUploadImage}
acceptedFiles={acceptedMimeTypes}
onClickRef={clickRef}
{...cypressId('editor-toolbar-upload-image-input')}
/>
</Fragment>
)
}

View file

@ -34,7 +34,7 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
const entryTitle = useHistoryEntryTitle(entry)
return (
<div className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4'>
<div className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4' {...cypressId('history-card')}>
<Card className='card-min-height' text={'dark'} bg={'light'}>
<Card.Body className='p-2 d-flex flex-row justify-content-between'>
<div className={'d-flex flex-column'}>

View file

@ -8,6 +8,7 @@ import React from 'react'
import { Button } from 'react-bootstrap'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import './pin-button.scss'
import { cypressId } from '../../../utils/cypress-attribute'
export interface PinButtonProps {
isPinned: boolean
@ -21,7 +22,8 @@ export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDa
<Button
variant={isDark ? 'secondary' : 'light'}
className={`history-pin ${className || ''} ${isPinned ? 'pinned' : ''}`}
onClick={onPinClick}>
onClick={onPinClick}
{...cypressId('history-entry-pin-button')}>
<ForkAwesomeIcon icon='thumb-tack' />
</Button>
)

View file

@ -9,6 +9,7 @@ import React, { useCallback, useMemo } from 'react'
import { Form } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Logger } from '../../../utils/logger'
import { cypressId } from '../../../utils/cypress-attribute'
const log = new Logger('LanguagePicker')
const languages = {
@ -81,7 +82,13 @@ export const LanguagePicker: React.FC = () => {
)
return (
<Form.Control as='select' size='sm' className='mb-2 mx-auto w-auto' value={languageCode} onChange={onChangeLang}>
<Form.Control
as='select'
size='sm'
className='mb-2 mx-auto w-auto'
value={languageCode}
onChange={onChangeLang}
{...cypressId('language-picker')}>
{languageOptions}
</Form.Control>
)

View file

@ -14,6 +14,7 @@ import { NewUserNoteButton } from '../new-user-note-button'
import { SignInButton } from '../sign-in-button'
import { UserDropdown } from '../user-dropdown'
import './header-bar.scss'
import { cypressId } from '../../../../utils/cypress-attribute'
const HeaderBar: React.FC = () => {
useTranslation()
@ -22,10 +23,10 @@ const HeaderBar: React.FC = () => {
return (
<Navbar className='justify-content-between'>
<div className='nav header-nav'>
<HeaderNavLink to='/intro' id='navLinkIntro'>
<HeaderNavLink to='/intro' {...cypressId('navLinkIntro')}>
<Trans i18nKey='landing.navigation.intro' />
</HeaderNavLink>
<HeaderNavLink to='/history' id='navLinkHistory'>
<HeaderNavLink to='/history' {...cypressId('navLinkHistory')}>
<Trans i18nKey='landing.navigation.history' />
</HeaderNavLink>
</div>

View file

@ -7,17 +7,18 @@
import React from 'react'
import { Nav } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'
import type { PropsWithDataCypressId } from '../../../utils/cypress-attribute'
import { cypressId } from '../../../utils/cypress-attribute'
export interface HeaderNavLinkProps {
export interface HeaderNavLinkProps extends PropsWithDataCypressId {
to: string
id: string
}
export const HeaderNavLink: React.FC<HeaderNavLinkProps> = ({ to, id, children }) => {
export const HeaderNavLink: React.FC<HeaderNavLinkProps> = ({ to, children, ...props }) => {
return (
<Nav.Item>
<LinkContainer to={to}>
<Nav.Link id={id} className='text-light' href={to}>
<Nav.Link className='text-light' href={to} {...cypressId(props)}>
{children}
</Nav.Link>
</LinkContainer>

View file

@ -9,12 +9,17 @@ import { Button } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { LinkContainer } from 'react-router-bootstrap'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { cypressId } from '../../../utils/cypress-attribute'
export const NewGuestNoteButton: React.FC = () => {
const { t } = useTranslation()
return (
<LinkContainer to={'/new'} title={t('landing.navigation.newGuestNote')}>
<Button variant='primary' size='sm' className='d-inline-flex align-items-center'>
<Button
variant='primary'
size='sm'
className='d-inline-flex align-items-center'
{...cypressId('new-guest-note-button')}>
<ForkAwesomeIcon icon='plus' className='mx-1' />
<span>
<Trans i18nKey='landing.navigation.newGuestNote' />

View file

@ -9,12 +9,17 @@ import { Button } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { LinkContainer } from 'react-router-bootstrap'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { cypressId } from '../../../utils/cypress-attribute'
export const NewUserNoteButton: React.FC = () => {
const { t } = useTranslation()
return (
<LinkContainer to={'/new'} title={t('landing.navigation.newNote')}>
<Button variant='primary' size='sm' className='d-inline-flex align-items-center'>
<Button
variant='primary'
size='sm'
className='d-inline-flex align-items-center'
{...cypressId('new-note-button')}>
<ForkAwesomeIcon icon='plus' className='mx-1' />
<span>
<Trans i18nKey='landing.navigation.newNote' />

View file

@ -12,6 +12,7 @@ import { clearUser } from '../../../redux/user/methods'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { UserAvatar } from '../../common/user-avatar/user-avatar'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { cypressId } from '../../../utils/cypress-attribute'
export const UserDropdown: React.FC = () => {
useTranslation()
@ -23,19 +24,19 @@ export const UserDropdown: React.FC = () => {
return (
<Dropdown alignRight>
<Dropdown.Toggle size='sm' variant='dark' id='dropdown-user' className={'d-flex align-items-center'}>
<Dropdown.Toggle size='sm' variant='dark' {...cypressId('user-dropdown')} className={'d-flex align-items-center'}>
<UserAvatar name={user.name} photo={user.photo} />
</Dropdown.Toggle>
<Dropdown.Menu className='text-start'>
<LinkContainer to={'/n/features'}>
<Dropdown.Item dir='auto'>
<Dropdown.Item dir='auto' {...cypressId('user-dropdown-features-button')}>
<ForkAwesomeIcon icon='bolt' fixedWidth={true} className='mx-2' />
<Trans i18nKey='editor.help.documents.features' />
</Dropdown.Item>
</LinkContainer>
<LinkContainer to={'/profile'}>
<Dropdown.Item dir='auto'>
<Dropdown.Item dir='auto' {...cypressId('user-dropdown-profile-button')}>
<ForkAwesomeIcon icon='user' fixedWidth={true} className='mx-2' />
<Trans i18nKey='profile.userProfile' />
</Dropdown.Item>
@ -44,7 +45,8 @@ export const UserDropdown: React.FC = () => {
dir='auto'
onClick={() => {
clearUser()
}}>
}}
{...cypressId('user-dropdown-sign-out-button')}>
<ForkAwesomeIcon icon='sign-out' fixedWidth={true} className='mx-2' />
<Trans i18nKey='login.signOut' />
</Dropdown.Item>

View file

@ -8,6 +8,7 @@ import React, { useEffect, useRef } from 'react'
import './abc.scss'
import { Logger } from '../../../../utils/logger'
import type { CodeProps } from '../../replace-components/code-block-component-replacer'
import { cypressId } from '../../../../utils/cypress-attribute'
const log = new Logger('AbcFrame')
@ -28,5 +29,5 @@ export const AbcFrame: React.FC<CodeProps> = ({ code }) => {
})
}, [code])
return <div ref={container} className={'abcjs-score bg-white text-black svg-container'} />
return <div ref={container} className={'abcjs-score bg-white text-black svg-container'} {...cypressId('abcjs')} />
}

View file

@ -6,6 +6,7 @@
import React, { useMemo } from 'react'
import { parseCsv } from './csv-parser'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface CsvTableProps {
code: string
@ -63,7 +64,7 @@ export const CsvTable: React.FC<CsvTableProps> = ({
)
return (
<table className={'csv-html-table table-striped'}>
<table className={'csv-html-table table-striped'} {...cypressId('csv-html-table')}>
{renderTableHeader}
{renderTableBody}
</table>

View file

@ -66,7 +66,7 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
}, [code, language, startLineNumber])
return (
<div className={'code-highlighter'}>
<div className={'code-highlighter'} {...cypressId('highlighted-code-block')}>
<code className={`hljs ${startLineNumber !== undefined ? 'showGutter' : ''} ${wrapLines ? 'wrapLines' : ''}`}>
{dom}
</code>

View file

@ -7,8 +7,8 @@
import { MarkdownExtension } from '../markdown-extension'
import type MarkdownIt from 'markdown-it'
import plantuml from 'markdown-it-plantuml'
import type Renderer from 'markdown-it/lib/renderer'
import type { RenderRule } from 'markdown-it/lib/renderer'
import type Renderer from 'markdown-it/lib/renderer'
import type Token from 'markdown-it/lib/token'
import type { Options } from 'markdown-it/lib'
import type { ComponentReplacer } from '../../replace-components/component-replacer'

View file

@ -14,9 +14,9 @@ import type { AccessToken } from '../../../api/tokens/types'
import { CopyableField } from '../../common/copyable/copyable-field/copyable-field'
import { IconButton } from '../../common/icon-button/icon-button'
import { CommonModal } from '../../common/modals/common-modal'
import { DeletionModal } from '../../common/modals/deletion-modal'
import { ShowIf } from '../../common/show-if/show-if'
import { Logger } from '../../../utils/logger'
import { cypressId } from '../../../utils/cypress-attribute'
const log = new Logger('ProfileAccessTokens')
@ -108,7 +108,9 @@ export const ProfileAccessTokens: React.FC = () => {
return (
<ListGroup.Item className='bg-dark' key={token.created}>
<Row>
<Col className='text-start'>{token.label}</Col>
<Col className='text-start' {...cypressId('access-token-label')}>
{token.label}
</Col>
<Col className='text-start text-white-50'>
<Trans
i18nKey='profile.accessTokens.created'
@ -120,7 +122,12 @@ export const ProfileAccessTokens: React.FC = () => {
/>
</Col>
<Col xs='auto'>
<IconButton icon='trash-o' variant='danger' onClick={() => selectForDeletion(token.created)} />
<IconButton
icon='trash-o'
variant='danger'
onClick={() => selectForDeletion(token.created)}
{...cypressId('access-token-delete-button')}
/>
</Col>
</Row>
</ListGroup.Item>
@ -140,10 +147,16 @@ export const ProfileAccessTokens: React.FC = () => {
onChange={(event: ChangeEvent<HTMLInputElement>) => setNewTokenLabel(event.target.value)}
isValid={newTokenSubmittable}
required
{...cypressId('access-token-add-input')}
/>
</Col>
<Col xs={'auto'}>
<Button type='submit' variant='primary' size='sm' disabled={!newTokenSubmittable}>
<Button
type='submit'
variant='primary'
size='sm'
disabled={!newTokenSubmittable}
{...cypressId('access-token-add-button')}>
<Trans i18nKey='profile.accessTokens.createToken' />
</Button>
</Col>
@ -155,7 +168,8 @@ export const ProfileAccessTokens: React.FC = () => {
<CommonModal
show={showAddedModal}
onHide={() => setShowAddedModal(false)}
titleI18nKey='profile.modal.addedAccessToken.title'>
titleI18nKey='profile.modal.addedAccessToken.title'
{...cypressId('access-token-modal-add')}>
<Modal.Body>
<Trans i18nKey='profile.modal.addedAccessToken.message' />
<br />
@ -168,14 +182,20 @@ export const ProfileAccessTokens: React.FC = () => {
</Modal.Footer>
</CommonModal>
<DeletionModal
onConfirm={deleteToken}
deletionButtonI18nKey={'common.delete'}
<CommonModal
show={showDeleteModal}
onHide={() => setShowDeleteModal(false)}
titleI18nKey={'profile.modal.deleteAccessToken.title'}>
titleI18nKey={'profile.modal.deleteAccessToken.title'}
{...cypressId('access-token-modal-delete')}>
<Modal.Body>
<Trans i18nKey='profile.modal.deleteAccessToken.message' />
</DeletionModal>
</Modal.Body>
<Modal.Footer>
<Button variant='danger' onClick={deleteToken}>
<Trans i18nKey={'common.delete'} />
</Button>
</Modal.Footer>
</CommonModal>
</Fragment>
)
}