diff --git a/.editorconfig b/.editorconfig index f5af6a6df..e4cfe071d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -118,7 +118,7 @@ ij_typescript_import_sort_module_name = true ij_typescript_import_use_node_resolution = true ij_typescript_imports_wrap = on_every_item ij_typescript_indent_case_from_switch = true -ij_typescript_indent_chained_calls = false +ij_typescript_indent_chained_calls = true ij_typescript_indent_package_children = 0 ij_typescript_jsdoc_include_types = false ij_typescript_jsx_attribute_value = braces diff --git a/cypress/integration/autocompletion.spec.ts b/cypress/integration/autocompletion.spec.ts index 105d4210e..6585bf24e 100644 --- a/cypress/integration/autocompletion.spec.ts +++ b/cypress/integration/autocompletion.spec.ts @@ -8,19 +8,21 @@ describe('Autocompletion', () => { beforeEach(() => { cy.visit('/n/test') cy.get('.btn.active.btn-outline-secondary > i.fa-columns') - .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') + .should('exist') + + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') }) describe('code block', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type('```') + cy.get('@codeinput') + .fill('```') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -32,8 +34,8 @@ describe('Autocompletion', () => { .should('exist') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type('```') + cy.get('@codeinput') + .fill('```') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -50,11 +52,11 @@ describe('Autocompletion', () => { describe('container', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type(':::') + cy.get('@codeinput') + .fill(':::') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -66,8 +68,8 @@ describe('Autocompletion', () => { .should('exist') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type(':::') + cy.get('@codeinput') + .fill(':::') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -85,11 +87,11 @@ describe('Autocompletion', () => { describe('emoji', () => { describe('normal emoji', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type(':hedg') + cy.get('@codeinput') + .fill(':hedg') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -99,8 +101,8 @@ describe('Autocompletion', () => { .should('have.text', '🦔') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type(':hedg') + cy.get('@codeinput') + .fill(':hedg') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -115,11 +117,11 @@ describe('Autocompletion', () => { describe('fork-awesome-icon', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type(':fa-face') + cy.get('@codeinput') + .fill(':fa-face') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -129,8 +131,8 @@ describe('Autocompletion', () => { .should('exist') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type(':fa-face') + cy.get('@codeinput') + .fill(':fa-face') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -146,11 +148,11 @@ describe('Autocompletion', () => { describe('header', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type('#') + cy.get('@codeinput') + .fill('#') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -160,8 +162,8 @@ describe('Autocompletion', () => { .should('have.text', ' ') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type('#') + cy.get('@codeinput') + .fill('#') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -176,11 +178,11 @@ describe('Autocompletion', () => { describe('images', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type('!') + cy.get('@codeinput') + .fill('!') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -192,8 +194,8 @@ describe('Autocompletion', () => { .should('have.attr', 'title', 'title') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type('!') + cy.get('@codeinput') + .fill('!') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -210,11 +212,11 @@ describe('Autocompletion', () => { describe('links', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type('[') + cy.get('@codeinput') + .fill('[') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -226,8 +228,8 @@ describe('Autocompletion', () => { .should('have.attr', 'title', 'title') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type('[') + cy.get('@codeinput') + .fill('[') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -244,11 +246,11 @@ describe('Autocompletion', () => { describe('pdf', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type('{') + cy.get('@codeinput') + .fill('{') cy.get('.CodeMirror-hints') .should('exist') - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('{enter}') cy.get('.CodeMirror-hints') .should('not.exist') @@ -258,8 +260,8 @@ describe('Autocompletion', () => { .should('exist') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type('{') + cy.get('@codeinput') + .fill('{') cy.get('.CodeMirror-hints > li') .first() .dblclick() @@ -274,31 +276,31 @@ describe('Autocompletion', () => { describe('collapsable blocks', () => { it('via Enter', () => { - cy.get('.CodeMirror textarea') - .type(' .CodeMirror-line > span') - .should('have.text', '') // after selecting the hint, the last line of the inserted suggestion is active + .should('have.text', '') // after selecting the hint, the last line of the inserted suggestion is active cy.get('.markdown-body > details') - .should('exist') + .should('exist') }) it('via doubleclick', () => { - cy.get('.CodeMirror textarea') - .type(' li') - .first() - .dblclick() + .first() + .dblclick() cy.get('.CodeMirror-hints') - .should('not.exist') + .should('not.exist') cy.get('.CodeMirror-activeline > .CodeMirror-line > span') - .should('have.text', '') + .should('have.text', '') cy.get('.markdown-body > details') - .should('exist') + .should('exist') }) }) }) diff --git a/cypress/integration/code.spec.ts b/cypress/integration/code.spec.ts index dcf5d29fe..89e1c0b91 100644 --- a/cypress/integration/code.spec.ts +++ b/cypress/integration/code.spec.ts @@ -13,22 +13,24 @@ describe('Code', () => { }) cy.get('.btn.active.btn-outline-secondary > i.fa-columns') .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') + + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') }) describe('without = doesn\'t show gutter', () => { it('without wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript \nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript \nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') }) it('with wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript!\nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript!\nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') .should('have.class', 'wrapLines') @@ -37,8 +39,8 @@ describe('Code', () => { describe('with = shows gutter', () => { it('without wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript=\nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript=\nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') .should('have.class', 'showGutter') @@ -48,8 +50,8 @@ describe('Code', () => { }) it('with wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript=! \nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript=! \nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') .should('have.class', 'showGutter') @@ -62,8 +64,8 @@ describe('Code', () => { describe('with = shows gutter and number is used as startline', () => { it('without wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript=100\nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript=100\nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') .should('have.class', 'showGutter') @@ -73,8 +75,8 @@ describe('Code', () => { }) it('with wrapLines active', () => { - cy.get('.CodeMirror textarea') - .type('```javascript=100! \nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript=100! \nlet x = 0\n```') cy.get('.markdown-body > pre > code') .should('have.class', 'hljs') .should('have.class', 'showGutter') @@ -86,11 +88,11 @@ describe('Code', () => { }) it('has a button', () => { - cy.get('.CodeMirror textarea') - .type('```javascript \nlet x = 0\n```') + cy.get('@codeinput') + .fill('```javascript \nlet x = 0\n```') cy.get('.markdown-body > pre > div > button > i') .should('have.class', 'fa-files-o') .click() - cy.get('@copy').should('be.calledWithExactly', 'let x = 0\n'); + cy.get('@copy').should('be.calledWithExactly', 'let x = 0\n') }) }) diff --git a/cypress/integration/documentTitle.spec.ts b/cypress/integration/documentTitle.spec.ts index 23a15af5b..a23f4104d 100644 --- a/cypress/integration/documentTitle.spec.ts +++ b/cypress/integration/documentTitle.spec.ts @@ -12,80 +12,94 @@ describe('Document Title', () => { cy.visit('/n/test') cy.get('.btn.active.btn-outline-secondary > i.fa-columns') .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') + + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') }) describe('title should be yaml metadata title', () => { it('just yaml metadata title', () => { - cy.get('.CodeMirror textarea') - .type(`---\ntitle: ${title}\n---`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`---\ntitle: ${title}\n---`) + cy.title() + .should('eq', `${title} - HedgeDoc @ ${branding.name}`) }) it('yaml metadata title and opengraph title', () => { - cy.get('.CodeMirror textarea') - .type(`---\ntitle: ${title}\nopengraph:\n title: False title\n{backspace}{backspace}---`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`---\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.get('.CodeMirror textarea') - .type(`---\ntitle: ${title}\nopengraph:\n title: False title\n{backspace}{backspace}---\n# a first title`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`---\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.get('.CodeMirror textarea') - .type(`---\nopengraph:\n title: ${title}\n{backspace}{backspace}---`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`---\nopengraph:\n title: ${title}\n---`) + cy.title() + .should('eq', `${title} - HedgeDoc @ ${branding.name}`) }) it('opengraph title and first heading', () => { - cy.get('.CodeMirror textarea') - .type(`---\nopengraph:\n title: ${title}\n{backspace}{backspace}---\n# a first title`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`---\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.get('.CodeMirror textarea') - .type(`# ${title}`) - cy.title().should('eq', `${title} - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`# ${title}`) + cy.title() + .should('eq', `${title} - HedgeDoc @ ${branding.name}`) }) it('just first heading with alt-text instead of image', () => { - cy.get('.CodeMirror textarea') - .type(`# ${title} ![abc](https://dummyimage.com/48)`) - cy.title().should('eq', `${title} abc - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`# ${title} ![abc](https://dummyimage.com/48)`) + cy.title() + .should('eq', `${title} abc - HedgeDoc @ ${branding.name}`) }) it('just first heading without link syntax', () => { - cy.get('.CodeMirror textarea') - .type(`# ${title} [link](https://hedgedoc.org)`) - cy.title().should('eq', `${title} link - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`# ${title} [link](https://hedgedoc.org)`) + cy.title() + .should('eq', `${title} link - HedgeDoc @ ${branding.name}`) }) it('markdown syntax removed first', () => { - cy.get('.CodeMirror textarea') - .type(`# ${title} 1*2*3 4*5**`) - cy.title().should('eq', `${title} 123 4*5** - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`# ${title} 1*2*3 4*5**`) + cy.title() + .should('eq', `${title} 123 4*5** - HedgeDoc @ ${branding.name}`) }) it('markdown syntax removed second', () => { - cy.get('.CodeMirror textarea') - .type(`# ${title} **1 2*`) - cy.title().should('eq', `${title} *1 2 - HedgeDoc @ ${branding.name}`) + cy.get('@codeinput') + .fill(`# ${title} **1 2*`) + cy.title() + .should('eq', `${title} *1 2 - HedgeDoc @ ${branding.name}`) }) it('markdown syntax removed third', () => { - cy.get('.CodeMirror textarea') - .type(`# ${title} _asd_`) - cy.title().should('eq', `${title} asd - HedgeDoc @ ${branding.name}`) + cy.get('a') + .get('@codeinput') + .fill(`# ${title} _asd_`) + cy.title() + .should('eq', `${title} asd - HedgeDoc @ ${branding.name}`) }) }) }) diff --git a/cypress/integration/editorMode.spec.ts b/cypress/integration/editorMode.spec.ts index 5fbecc894..4574e1b2f 100644 --- a/cypress/integration/editorMode.spec.ts +++ b/cypress/integration/editorMode.spec.ts @@ -7,18 +7,25 @@ describe('Editor mode from URL parameter is used', () => { it('mode view', () => { cy.visit('/n/features?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('have.class', 'd-none') + cy.get('.splitter.right') + .should('not.have.class', 'd-none') }) it('mode both', () => { cy.visit('/n/features?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('not.have.class', 'd-none') + cy.get('.splitter.separator') + .should('exist') + cy.get('.splitter.right') + .should('not.have.class', 'd-none') }) it('mode edit', () => { cy.visit('/n/features?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('not.have.class', 'd-none') + cy.get('.splitter.right') + .should('have.class', 'd-none') }) }) diff --git a/cypress/integration/export.spec.ts b/cypress/integration/export.spec.ts index 9cc60fd32..05ebfdf89 100644 --- a/cypress/integration/export.spec.ts +++ b/cypress/integration/export.spec.ts @@ -4,19 +4,18 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -const testTitle = 'testContent' -const testContent = `---\ntitle: ${testTitle}\n---\nThis is some test content` - describe('Export', () => { + const testTitle = 'testContent' + const testContent = `---\ntitle: ${testTitle}\n---\nThis is some test content` + beforeEach(() => { cy.visit('/n/test') cy.get('.btn.active.btn-outline-secondary > i.fa-columns') .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') - cy.get('.CodeMirror textarea') - .type(testContent) + cy.get('.CodeMirror') + .click() + .get('textarea') + .fill(testContent) }) it('Markdown', () => { @@ -28,27 +27,27 @@ describe('Export', () => { .then((anchor) => ( new Cypress.Promise((resolve: any, _: any) => { // Use XHR to get the blob that corresponds to the object URL. - const xhr = new XMLHttpRequest(); - xhr.open('GET', anchor.prop('href'), true); - xhr.responseType = 'blob'; + const xhr = new XMLHttpRequest() + xhr.open('GET', anchor.prop('href'), true) + xhr.responseType = 'blob' // Once loaded, use FileReader to get the string back from the blob. xhr.onload = () => { if (xhr.status === 200) { - const blob = xhr.response; - const reader = new FileReader(); + const blob = xhr.response + const reader = new FileReader() reader.onload = () => { // Once we have a string, resolve the promise to let // the Cypress chain continue, e.g. to assert on the result. - resolve(reader.result); - }; - reader.readAsText(blob); + resolve(reader.result) + } + reader.readAsText(blob) } - }; - xhr.send(); + } + xhr.send() }) )) // Now the regular Cypress assertions should work. - .should('equal', testContent); + .should('equal', testContent) }) }) diff --git a/cypress/integration/upload.spec.ts b/cypress/integration/fileUpload.spec.ts similarity index 80% rename from cypress/integration/upload.spec.ts rename to cypress/integration/fileUpload.spec.ts index 37eae2b3f..8e2866459 100644 --- a/cypress/integration/upload.spec.ts +++ b/cypress/integration/fileUpload.spec.ts @@ -6,28 +6,30 @@ const imageUrl = 'http://example.com/non-existing.png' -describe('Upload', () => { +describe('File upload', () => { beforeEach(() => { cy.visit('/n/test') - cy.get('.btn.active.btn-outline-secondary > i.fa-columns') - .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') }) - it('check that text drag\'n\'drop still works', () => { + it('doesn\'t prevent drag\'n\'drop of plain text', () => { const dataTransfer = new DataTransfer() - cy.get('.CodeMirror textarea') - .type('line 1\nline 2\nline3') - cy.get('.CodeMirror-activeline > .CodeMirror-line > span') + cy.get('@codeinput') + .fill('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', 'linline3e 1') + .should('have.text', 'lindraglinee 1') }) describe('upload works', () => { @@ -60,7 +62,7 @@ describe('Upload', () => { } cy.get('.CodeMirror-scroll').trigger('paste', pasteEvent) cy.get('.CodeMirror-activeline > .CodeMirror-line > span') - .should('have.text', `![](${imageUrl})`) + .should('have.text', `![](${imageUrl})`) }) }) @@ -75,13 +77,13 @@ describe('Upload', () => { 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})`) + .should('have.text', `![](${imageUrl})`) }) }) }) it('upload fails', () => { - cy.get('.CodeMirror textarea') + cy.get('@codeinput') .type('not empty') cy.intercept({ method: 'POST', @@ -91,8 +93,10 @@ describe('Upload', () => { }) cy.get('.fa-upload') .click() - cy.get('input[type=file]') - .attachFile({ filePath: 'acme.png', mimeType: 'image/png' }) + cy.fixture('acme.png').then(() => { + cy.get('input[type=file]') + .attachFile({ filePath: 'acme.png', mimeType: 'image/png' }) + }) cy.get('.CodeMirror-activeline > .CodeMirror-line > span') .should('have.text', 'not empty') }) diff --git a/cypress/integration/import.spec.ts b/cypress/integration/import.spec.ts index 051ab7551..1fe81f0da 100644 --- a/cypress/integration/import.spec.ts +++ b/cypress/integration/import.spec.ts @@ -9,9 +9,6 @@ describe('Import markdown file', () => { cy.visit('/n/test') cy.get('.btn.active.btn-outline-secondary > i.fa-columns') .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') }) it('import on blank note', () => { @@ -28,8 +25,11 @@ describe('Import markdown file', () => { }) it('import on note with content', () => { - cy.get('.CodeMirror textarea') - .type('test\nabc', { force: true }) + + cy.get('.CodeMirror') + .click() + .get('textarea') + .fill('test\nabc') cy.get('button#editor-menu-import') .click() cy.get('.import-md-file') diff --git a/cypress/integration/maxLength.spec.ts b/cypress/integration/maxLength.spec.ts index 6cd5de400..79ac62296 100644 --- a/cypress/integration/maxLength.spec.ts +++ b/cypress/integration/maxLength.spec.ts @@ -4,52 +4,47 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -const tenChars = '0123456789' +describe('The status bar text length info', () => { + const warningTestContent = ('0123456789'.repeat(10)) + const dangerTestContent = ('0123456789'.repeat(20)) + const tooMuchTestContent = `${dangerTestContent}a` -describe('status-bar text-length info', () => { beforeEach(() => { cy.visit('/n/test') + + cy.get('.CodeMirror ') + .click() cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') + .as('codeinput') }) - it('tooltip shows full remaining on empty text', () => { - cy.get('.status-bar div:nth-child(2) span:nth-child(2)') + it('shows the maximal length of the document as number of available characters in the tooltip', () => { + cy.get('.status-bar [data-cy="remainingCharacters"]') .attribute('title') .should('contain', ' 200 ') }) - it('color is warning on <= 100 chars remaining', () => { - cy.get('.CodeMirror textarea') - .fill(tenChars.repeat(10)) - cy.get('.status-bar div:nth-child(2) span:nth-child(2)') + it('color is set to "warning" on <= 100 characters remaining', () => { + cy.get('@codeinput') + .fill(warningTestContent) + cy.get('.status-bar [data-cy="remainingCharacters"]') .should('have.class', 'text-warning') }) - it('color is danger on <= 0 chars remaining', () => { - cy.get('.CodeMirror textarea') - .fill(tenChars.repeat(20)) - cy.get('.status-bar div:nth-child(2) span:nth-child(2)') - .should('have.class', 'text-danger') - }) -}) - -describe('show warning if content length > configured max length', () => { - beforeEach(() => { - cy.visit('/n/test') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') - .fill(tenChars.repeat(20)) + it('color is set to danger on <= 0 characters remaining', () => { + cy.get('@codeinput') + .fill(dangerTestContent) + cy.get('.status-bar [data-cy="remainingCharacters"]') + .should('have.class', 'text-danger') }) - it('show warning alert in renderer and as modal', () => { - cy.get('.CodeMirror textarea') - .type('a') - cy.get('.modal-body.limit-warning') + it('shows a warning and opens a modal', () => { + cy.get('@codeinput') + .fill(tooMuchTestContent) + cy.get('[data-cy="limitReachedModal"]') + .should('be.visible') + cy.get('[data-cy="limitReachedMessage"]') .should('be.visible') - cy.get('.splitter .alert-danger') - .should('be.visible') }) + }) diff --git a/cypress/integration/metadata.spec.ts b/cypress/integration/metadata.spec.ts deleted file mode 100644 index 9e7185edd..000000000 --- a/cypress/integration/metadata.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -describe('yaml-metadata: tags', () => { - beforeEach(() => { - cy.visit('/n/features') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') - }) - - it('show deprecation notice on old syntax', () => { - cy.get('.CodeMirror textarea') - .type('---\ntags: a, b, c\n---') - cy.get('.splitter.right .w-100.h-100 .alert.alert-warning') - .should('be.visible') - }) - - it('show no deprecation notice on yaml-array (1)', () => { - cy.get('.CodeMirror textarea') - .type('---\ntags: [\'a\', \'b\', \'c\']\n---') - cy.get('.splitter.right .w-100.h-100 .alert.alert-warning') - .should('not.exist') - }) - - it('show no deprecation notice on yaml-array (2)', () => { - cy.get('.CodeMirror textarea') - .type('---\ntags:\n - a\nb\nc\n') - .type('{backspace}{backspace}{backspace}{backspace}') - .type('---') - cy.get('.splitter.right .w-100.h-100 .alert.alert-warning') - .should('not.exist') - }) -}) diff --git a/cypress/integration/toolbar.spec.ts b/cypress/integration/toolbar.spec.ts index de46aaa32..2e3a96b38 100644 --- a/cypress/integration/toolbar.spec.ts +++ b/cypress/integration/toolbar.spec.ts @@ -4,23 +4,38 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -const testText = 'textText' -const testLink = 'http://hedgedoc.org' - describe('Toolbar', () => { + const testText = 'textText' + const testLink = 'http://hedgedoc.org' + beforeEach(() => { cy.visit('/n/test') - cy.get('.btn.active.btn-outline-secondary > i.fa-columns') - .should('exist') - cy.get('.CodeMirror textarea') - .type('{ctrl}a', { force: true }) - .type('{backspace}') - cy.viewport(1920, 1080) + + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') }) + const fillTestText = () => { + cy.get('@codeinput') + .fill(testText) + cy.get('.CodeMirror-line > span') + .should("exist") + .should('have.text', testText) + } + + const fillTestLink = () => { + cy.get('@codeinput') + .fill(testLink) + cy.get('.CodeMirror-line > span') + .should("exist") + .should('have.text', testLink) + } + it('bold', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-bold') .click() @@ -29,8 +44,8 @@ describe('Toolbar', () => { }) it('italic', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-italic') .click() @@ -39,8 +54,8 @@ describe('Toolbar', () => { }) it('underline', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-underline') .click() @@ -49,8 +64,8 @@ describe('Toolbar', () => { }) it('strikethrough', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-strikethrough') .click() @@ -59,8 +74,8 @@ describe('Toolbar', () => { }) it('subscript', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-subscript') .click() @@ -69,8 +84,8 @@ describe('Toolbar', () => { }) it('superscript', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-superscript') .click() @@ -79,8 +94,7 @@ describe('Toolbar', () => { }) it('heading', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-header') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -93,8 +107,8 @@ describe('Toolbar', () => { describe('code', () => { it('nothing selected empty line', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') .type('{backspace}') cy.get('.fa-code') @@ -106,8 +120,8 @@ describe('Toolbar', () => { }) it('nothing selected non line', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') .type('{leftArrow}') cy.get('.fa-code') @@ -121,8 +135,8 @@ describe('Toolbar', () => { }) it('line selected', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-code') .click() @@ -136,8 +150,7 @@ describe('Toolbar', () => { }) it('quote', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-quote-right') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -149,8 +162,7 @@ describe('Toolbar', () => { }) it('unordered list', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-list') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -162,8 +174,7 @@ describe('Toolbar', () => { }) it('ordered list', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-list-ol') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -175,8 +186,7 @@ describe('Toolbar', () => { }) it('todo list', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-check-square') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -189,8 +199,8 @@ describe('Toolbar', () => { describe('link', () => { it('with selection text', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-link') .click() @@ -199,8 +209,7 @@ describe('Toolbar', () => { }) it('without selection', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-link') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -208,8 +217,8 @@ describe('Toolbar', () => { }) it('with selection link', () => { - cy.get('.CodeMirror textarea') - .type(`${testLink}`) + fillTestLink() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-link') .click() @@ -220,8 +229,8 @@ describe('Toolbar', () => { describe('image', () => { it('with selection', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-picture-o') .click() @@ -230,8 +239,7 @@ describe('Toolbar', () => { }) it('without selection', () => { - cy.get('.CodeMirror textarea') - .type(`${testText}`) + fillTestText() cy.get('.fa-picture-o') .click() cy.get('.CodeMirror-activeline > .CodeMirror-line > span') @@ -239,8 +247,8 @@ describe('Toolbar', () => { }) it('with selection link', () => { - cy.get('.CodeMirror textarea') - .type(`${testLink}`) + fillTestLink() + cy.get('@codeinput') .type('{ctrl}a') cy.get('.fa-picture-o') .click() @@ -312,9 +320,9 @@ describe('Toolbar', () => { it('collapsable block', () => { cy.get('.fa-caret-square-o-down') - .click() + .click() cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span span') - .should('have.text', '
') + .should('have.text', '
') }) it('comment', () => { diff --git a/cypress/integration/yamlArrayDeprecationMessage.spec.ts b/cypress/integration/yamlArrayDeprecationMessage.spec.ts new file mode 100644 index 000000000..4747ab111 --- /dev/null +++ b/cypress/integration/yamlArrayDeprecationMessage.spec.ts @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +describe('YAML Array for deprecated syntax of document tags in frontmatter', () => { + beforeEach(() => { + cy.visit('/n/features') + + cy.get('.CodeMirror') + .click() + .get('textarea') + .as('codeinput') + }) + + it('is shown when using old syntax', () => { + cy.get('@codeinput') + .fill('---\ntags: a, b, c\n---') + cy.get('[data-cy="yamlArrayDeprecationAlert"]') + .should('be.visible') + }) + + it('isn\'t shown when using inline yaml-array', () => { + cy.get('@codeinput') + .fill('---\ntags: [\'a\', \'b\', \'c\']\n---') + cy.get('[data-cy="yamlArrayDeprecationAlert"]') + .should('not.exist') + }) + + it('isn\'t shown when using multi line yaml-array', () => { + cy.get('@codeinput') + .fill('---\ntags:\n - a\n - b\n - c\n---') + cy.get('[data-cy="yamlArrayDeprecationAlert"]') + .should('not.exist') + }) +}) diff --git a/src/components/common/modals/common-modal.tsx b/src/components/common/modals/common-modal.tsx index 0f2ed265e..e2c655ed0 100644 --- a/src/components/common/modals/common-modal.tsx +++ b/src/components/common/modals/common-modal.tsx @@ -26,7 +26,7 @@ export const CommonModal: React.FC = ({ show, onHide, titleI18 useTranslation() return ( - + diff --git a/src/components/editor/document-renderer-pane/document-render-pane.tsx b/src/components/editor/document-renderer-pane/document-render-pane.tsx index 460d94442..88b2c0334 100644 --- a/src/components/editor/document-renderer-pane/document-render-pane.tsx +++ b/src/components/editor/document-renderer-pane/document-render-pane.tsx @@ -6,20 +6,18 @@ SPDX-License-Identifier: AGPL-3.0-only import { TocAst } from 'markdown-it-toc-done-right' import React, { RefObject, useRef, useState } from 'react' -import { Alert, Dropdown } from 'react-bootstrap' -import { Trans } from 'react-i18next' +import { Dropdown } from 'react-bootstrap' import { useSelector } from 'react-redux' import useResizeObserver from 'use-resize-observer' -import links from '../../../links.json' import { ApplicationState } from '../../../redux' import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon' -import { TranslatedExternalLink } from '../../common/links/translated-external-link' import { ShowIf } from '../../common/show-if/show-if' import { FullMarkdownRenderer } from '../../markdown-renderer/full-markdown-renderer' import { LineMarkerPosition } from '../../markdown-renderer/types' import { TableOfContents } from '../table-of-contents/table-of-contents' import { YAMLMetaData } from '../yaml-metadata/yaml-metadata' import { useAdaptedLineMarkerCallback } from './use-adapted-line-markers-callback' +import { YamlArrayDeprecationAlert } from './yaml-array-deprecation-alert' export interface DocumentRenderPaneProps { extraClasses?: string @@ -58,11 +56,7 @@ export const DocumentRenderPane: React.FC = ({
- - -
- -
+
{ + return ( + + +
+ +
+ ); +} diff --git a/src/components/editor/editor-modals/max-length-warning-modal.tsx b/src/components/editor/editor-modals/max-length-warning-modal.tsx index 75594a28e..b7ced8838 100644 --- a/src/components/editor/editor-modals/max-length-warning-modal.tsx +++ b/src/components/editor/editor-modals/max-length-warning-modal.tsx @@ -19,8 +19,8 @@ export const MaxLengthWarningModal: React.FC = ({ sh useTranslation() return ( - - + + diff --git a/src/components/editor/editor-pane/status-bar/status-bar.tsx b/src/components/editor/editor-pane/status-bar/status-bar.tsx index a2e529a3c..faa0e6f93 100644 --- a/src/components/editor/editor-pane/status-bar/status-bar.tsx +++ b/src/components/editor/editor-pane/status-bar/status-bar.tsx @@ -67,6 +67,7 @@ export const StatusBar: React.FC = ({ position, selectedColumns, {t('editor.statusBar.lines', { lines: linesInDocument })}  â€“  diff --git a/src/components/markdown-renderer/basic-markdown-renderer.tsx b/src/components/markdown-renderer/basic-markdown-renderer.tsx index 4e4ed36f2..1d00e467a 100644 --- a/src/components/markdown-renderer/basic-markdown-renderer.tsx +++ b/src/components/markdown-renderer/basic-markdown-renderer.tsx @@ -62,7 +62,7 @@ export const BasicMarkdownRenderer: React.FC maxLength}> - + diff --git a/src/components/markdown-renderer/replace-components/sequence-diagram/deprecation-warning.tsx b/src/components/markdown-renderer/replace-components/sequence-diagram/deprecation-warning.tsx index bea4bee5c..92fcd681c 100644 --- a/src/components/markdown-renderer/replace-components/sequence-diagram/deprecation-warning.tsx +++ b/src/components/markdown-renderer/replace-components/sequence-diagram/deprecation-warning.tsx @@ -14,7 +14,7 @@ export const DeprecationWarning: React.FC = () => { useTranslation() return ( - +   diff --git a/src/index.tsx b/src/index.tsx index d2b160dad..e6176ea73 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -82,7 +82,7 @@ ReactDOM.render( ) if (isTestMode()) { - console.log("This build runs in test mode") + console.log("This build runs in test mode. This means:\n - No default content in the editor") } // If you want your app to work offline and load faster, you can change