Add cypress id attribute only in test mode (#1566)

* Add function for test attribute

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Adjust components

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Fix naming of attribute

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Rename method

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Rename method, interface, attribute and use interface

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>

* Lint and format fix

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-10-17 21:20:23 +02:00 committed by GitHub
parent 2abe40ef1d
commit a398660c18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 229 additions and 167 deletions

View file

@ -12,7 +12,7 @@ describe('Diagram codeblock ', () => {
it('renders markmap', () => {
cy.setCodemirrorContent('```markmap\n- pro\n- contra\n```')
cy.getMarkdownBody()
.find('[data-cy=markmap]')
.find('[data-cypress-id=markmap]')
.children()
.should('be.visible')
})
@ -28,7 +28,7 @@ describe('Diagram codeblock ', () => {
it('renders graphviz', () => {
cy.setCodemirrorContent('```graphviz\ngraph {\na -- b\n}\n```')
cy.getMarkdownBody()
.find('[data-cy=graphviz]')
.find('[data-cypress-id=graphviz]')
.children()
.should('be.visible')
})
@ -44,7 +44,7 @@ describe('Diagram codeblock ', () => {
it('renders flowcharts', () => {
cy.setCodemirrorContent('```flow\nst=>start: Start\ne=>end: End\nst->e\n```')
cy.getMarkdownBody()
.find('[data-cy=flowchart]')
.find('[data-cypress-id=flowchart]')
.children()
.should('be.visible')
})

View file

@ -14,9 +14,9 @@ describe('Export', () => {
})
it('Markdown', () => {
cy.get('[data-cy="menu-export"]')
cy.get('[data-cypress-id="menu-export"]')
.click()
cy.get('[data-cy="menu-export-markdown"]')
cy.get('[data-cypress-id="menu-export-markdown"]')
.click()
cy.get('a[download]')
.then((anchor) => (

View file

@ -151,7 +151,7 @@ describe('Code', () => {
it('has a working copy button', () => {
cy.setCodemirrorContent('```javascript \nlet x = 0\n```')
cy.get(`iframe[data-cy="documentIframe"]`)
cy.get(`iframe[data-cypress-id="documentIframe"]`)
.then(($element: JQuery) => {
const frame = $element[0] as HTMLIFrameElement
if (frame === null || frame.contentWindow === null) {
@ -163,7 +163,7 @@ describe('Code', () => {
})
cy.getIframeBody()
.find('[data-cy="copy-code-button"]')
.find('[data-cypress-id="copy-code-button"]')
.click()
cy.get('@copy')

View file

@ -10,11 +10,11 @@ describe('Import markdown file', () => {
})
it('import on blank note', () => {
cy.get('[data-cy="menu-import"]')
cy.get('[data-cypress-id="menu-import"]')
.click()
cy.get('[data-cy="menu-import-markdown"]')
cy.get('[data-cypress-id="menu-import-markdown"]')
.click()
cy.get('[data-cy="menu-import-markdown-input"]')
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')
@ -25,11 +25,11 @@ describe('Import markdown file', () => {
it('import on note with content', () => {
cy.setCodemirrorContent('test\nabc')
cy.get('[data-cy="menu-import"]')
cy.get('[data-cypress-id="menu-import"]')
.click()
cy.get('[data-cy="menu-import-markdown"]')
cy.get('[data-cypress-id="menu-import-markdown"]')
.click()
cy.get('[data-cy="menu-import-markdown-input"]')
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')

View file

@ -22,48 +22,48 @@ describe('Intro page', () => {
statusCode: 404
})
cy.get(`iframe[data-cy="documentIframe"]`)
cy.get(`iframe[data-cypress-id="documentIframe"]`)
.should('not.exist')
})
})
describe('features button', () => {
it('is hidden when logged in', () => {
cy.get('[data-cy="features-button"]')
cy.get('[data-cypress-id="features-button"]')
.should('not.exist')
})
it('is visible when logged out', () => {
cy.logout()
cy.get('[data-cy="features-button"]')
cy.get('[data-cypress-id="features-button"]')
.should('exist')
})
})
describe('sign in button', () => {
it('is hidden when logged in', () => {
cy.get('[data-cy="sign-in-button"]')
cy.get('[data-cypress-id="sign-in-button"]')
.should('not.exist')
})
it('is visible when logged out', () => {
cy.logout()
cy.get('[data-cy="sign-in-button"]')
cy.get('[data-cypress-id="sign-in-button"]')
.should('exist')
})
})
describe('version dialog', () => {
it('can be opened and closed', () => {
cy.get('[data-cy="version-modal"]')
cy.get('[data-cypress-id="version-modal"]')
.should('not.exist')
cy.get('[data-cy="show-version-modal"]')
cy.get('[data-cypress-id="show-version-modal"]')
.click()
cy.get('[data-cy="version-modal"]')
cy.get('[data-cypress-id="version-modal"]')
.should('be.visible')
cy.get('[data-cy="version-modal"] .modal-header .close')
cy.get('[data-cypress-id="version-modal"] .modal-header .close')
.click()
cy.get('[data-cy="version-modal"]')
cy.get('[data-cypress-id="version-modal"]')
.should('not.exist')
})
})

View file

@ -17,7 +17,7 @@ describe('Link gets replaced with embedding: ', () => {
.find('.one-click-embedding.gist-frame')
.click()
cy.getMarkdownBody()
.find('iframe[data-cy=gh-gist]')
.find('iframe[data-cypress-id=gh-gist]')
.should('be.visible')
})

View file

@ -14,29 +14,29 @@ describe('The status bar text length info', () => {
})
it('shows the maximal length of the document as number of available characters in the tooltip', () => {
cy.get('.status-bar [data-cy="remainingCharacters"]')
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.attribute('title')
.should('contain', ' 200 ')
})
it('color is set to "warning" on <= 100 characters remaining', () => {
cy.setCodemirrorContent(warningTestContent)
cy.get('.status-bar [data-cy="remainingCharacters"]')
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.should('have.class', 'text-warning')
})
it('color is set to danger on <= 0 characters remaining', () => {
cy.setCodemirrorContent(dangerTestContent)
cy.get('.status-bar [data-cy="remainingCharacters"]')
cy.get('.status-bar [data-cypress-id="remainingCharacters"]')
.should('have.class', 'text-danger')
})
it('shows a warning and opens a modal', () => {
cy.setCodemirrorContent(tooMuchTestContent)
cy.get('[data-cy="limitReachedModal"]')
cy.get('[data-cypress-id="limitReachedModal"]')
.should('be.visible')
cy.getIframeBody()
.find('[data-cy="limitReachedMessage"]')
.find('[data-cypress-id="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-cy="motd"]').contains(motdMockContent)
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
})
it('can be dismissed', () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cy="motd"]').contains(motdMockContent)
cy.get('button[data-cy="motd-dismiss"]')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
})
it("won't show again after dismiss and reload", () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cy="motd"]').contains(motdMockContent)
cy.get('button[data-cy="motd-dismiss"]')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.reload()
cy.get('main').should('exist')
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
})
it("will show again after reload without dismiss", () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cy="motd"]').contains(motdMockContent)
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.reload()
cy.get('main').should('exist')
cy.get('[data-cy="motd"]').contains(motdMockContent)
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
})
it("won't show again after dismiss and page navigation", () => {
mockExistingMotd()
cy.visit('/')
cy.get('[data-cy="motd"]').contains(motdMockContent)
cy.get('button[data-cy="motd-dismiss"]')
cy.get('[data-cypress-id="motd"]').contains(motdMockContent)
cy.get('button[data-cypress-id="motd-dismiss"]')
.click()
.then(() => {
expect(localStorage.getItem(MOTD_LOCAL_STORAGE_KEY)).to.equal(MOCK_LAST_MODIFIED)
})
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
cy.get('#navLinkHistory').click()
cy.get('main').should('exist')
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
})
it("won't show if no file exists", () => {
cy.visit('/')
cy.get('main').should('exist')
cy.get('[data-cy="motd"]').should('not.exist')
cy.get('[data-cypress-id="motd"]').should('not.exist')
})
})

View file

@ -32,7 +32,7 @@ const initLoggedOutTestWithCustomAuthProviders = (cy: Cypress.cy, enabledProvide
describe('When logged-in, ', () => {
it('sign-in button is hidden', () => {
cy.visit('/')
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('not.exist')
})
})
@ -41,7 +41,7 @@ describe('When logged-out ', () => {
describe('and no auth-provider is enabled, ', () => {
it('sign-in button is hidden', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('not.exist')
})
})
@ -51,7 +51,7 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
internal: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
})
@ -60,7 +60,7 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
ldap: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
})
@ -69,7 +69,7 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
openid: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
})
@ -80,7 +80,7 @@ describe('When logged-out ', () => {
initLoggedOutTestWithCustomAuthProviders(cy, {
saml: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=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,7 +93,7 @@ describe('When logged-out ', () => {
saml: true,
github: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
})
@ -105,7 +105,7 @@ describe('When logged-out ', () => {
saml: true,
internal: true
})
cy.get('[data-cy=sign-in-button]')
cy.get('[data-cypress-id=sign-in-button]')
.should('be.visible')
.should('have.attr', 'href', '/login')
})

View file

@ -10,19 +10,19 @@ describe('Split view', () => {
})
it('can show both panes', () => {
cy.get('[data-cy="view-mode-both"]').click()
cy.get('[data-cypress-id="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-cy="view-mode-preview"]').click()
cy.get('[data-cypress-id="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-cy="view-mode-editor"]').click()
cy.get('[data-cypress-id="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-cy="view-mode-both"]').click()
cy.get('[data-cypress-id="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

@ -32,49 +32,49 @@ describe('Toolbar Buttons', () => {
})
it('should format as bold', () => {
cy.get('.btn-toolbar [data-cy="format-bold"]')
cy.get('.btn-toolbar [data-cypress-id="format-bold"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `**${ testText }**`)
})
it('should format as italic', () => {
cy.get('.btn-toolbar [data-cy="format-italic"]')
cy.get('.btn-toolbar [data-cypress-id="format-italic"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `*${ testText }*`)
})
it('should format as underline', () => {
cy.get('.btn-toolbar [data-cy="format-underline"]')
cy.get('.btn-toolbar [data-cypress-id="format-underline"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `++${ testText }++`)
})
it('should format as strikethrough', () => {
cy.get('.btn-toolbar [data-cy="format-strikethrough"]')
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-cy="format-subscript"]')
cy.get('.btn-toolbar [data-cypress-id="format-subscript"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `~${ testText }~`)
})
it('should format as superscript', () => {
cy.get('.btn-toolbar [data-cy="format-superscript"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-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', '```')
@ -85,14 +85,14 @@ describe('Toolbar Buttons', () => {
})
it('should format links', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-image"]')
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![${ testText }](https://)`)
@ -100,7 +100,7 @@ describe('Toolbar Buttons', () => {
})
it('should format line as heading', () => {
cy.get('.btn-toolbar [data-cy="format-heading"]')
cy.get('.btn-toolbar [data-cypress-id="format-heading"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `# ${ testText }`)
@ -111,7 +111,7 @@ describe('Toolbar Buttons', () => {
})
it('should format the line as code', () => {
cy.get('.btn-toolbar [data-cy="format-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', '```')
@ -122,58 +122,58 @@ describe('Toolbar Buttons', () => {
})
it('should add a quote', () => {
cy.get('.btn-toolbar [data-cy="format-block-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-cy="format-block-quote"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-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-cy="format-unordered-list"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-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-cy="format-ordered-list"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-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-cy="format-check-list"]')
cy.get('.btn-toolbar [data-cypress-id="format-check-list"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `- [ ] - [ ] ${ testText }`)
})
it('should insert links', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
cy.get('.btn-toolbar [data-cypress-id="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-cy="format-image"]')
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `${ testText }![](https://)`)
@ -191,14 +191,14 @@ describe('Toolbar Buttons', () => {
})
it('should format as link', () => {
cy.get('.btn-toolbar [data-cy="format-link"]')
cy.get('.btn-toolbar [data-cypress-id="format-link"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `[](${ testLink })`)
})
it('should format as image', () => {
cy.get('.btn-toolbar [data-cy="format-image"]')
cy.get('.btn-toolbar [data-cypress-id="format-image"]')
.click()
cy.get('.CodeMirror-activeline > .CodeMirror-line > span')
.should('have.text', `![](${ testLink })`)
@ -207,7 +207,7 @@ describe('Toolbar Buttons', () => {
describe('for no text', () => {
it('should add an empty code block', () => {
cy.get('.btn-toolbar [data-cy="format-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', '```')
@ -216,21 +216,21 @@ describe('Toolbar Buttons', () => {
})
it('should insert lines', () => {
cy.get('.btn-toolbar [data-cy="format-add-line"]')
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', '----')
})
it('should add a collapsable block', () => {
cy.get('.btn-toolbar [data-cy="format-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')
})
it('should add a comment', () => {
cy.get('.btn-toolbar [data-cy="format-add-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', '> []')
@ -241,7 +241,7 @@ describe('Toolbar Buttons', () => {
beforeEach(() => {
cy.get('.table-picker-container')
.should('not.be.visible')
cy.get('[data-cy="show-table-overlay"]')
cy.get('[data-cypress-id="show-table-overlay"]')
.last()
.click()
cy.get('.table-picker-container')
@ -262,7 +262,7 @@ describe('Toolbar Buttons', () => {
it('should open a modal for custom table sizes in the overlay', () => {
cy.get('.modal-dialog')
.should('not.exist')
cy.get('[data-cy="show-custom-table-modal"]')
cy.get('[data-cypress-id="show-custom-table-modal"]')
.first()
.click()
cy.get('.modal-dialog')
@ -295,7 +295,7 @@ describe('Toolbar Buttons', () => {
it('should open overlay', () => {
cy.get('emoji-picker')
.should('not.be.visible')
cy.get('[data-cy="show-emoji-picker"]')
cy.get('[data-cypress-id="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-cy="sidebar-btn-document-info"]').click()
cy.get('[data-cy="document-info-modal"]').should('be.visible')
cy.get('[data-cy="document-info-word-count"]').should('have.text', '0')
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')
})
it('simple words', () => {
cy.setCodemirrorContent('five words should be enough')
cy.wait(500)
cy.get('[data-cy="sidebar-btn-document-info"]').click()
cy.get('[data-cy="document-info-modal"]').should('be.visible')
cy.get('[data-cy="document-info-word-count"]').should('have.text', '5')
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')
})
it('excluded codeblocks', () => {
cy.setCodemirrorContent('```\nthis is should be ignored\n```\n\ntwo `words`')
cy.wait(500)
cy.get('[data-cy="sidebar-btn-document-info"]').click()
cy.get('[data-cy="document-info-modal"]').should('be.visible')
cy.get('[data-cy="document-info-word-count"]').should('have.text', '2')
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')
})
it('excluded images', () => {
cy.setCodemirrorContent('![ignored alt text](https://dummyimage.com/48) not ignored text')
cy.wait(500)
cy.get('[data-cy="sidebar-btn-document-info"]').click()
cy.get('[data-cy="document-info-modal"]').should('be.visible')
cy.get('[data-cy="document-info-word-count"]').should('have.text', '3')
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')
})
})

View file

@ -12,21 +12,21 @@ 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-cy="yamlArrayDeprecationAlert"]')
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('be.visible')
})
it('isn\'t shown when using inline yaml-array', () => {
cy.setCodemirrorContent('---\ntags: [\'a\', \'b\', \'c\']\n---')
cy.getIframeBody()
.find('[data-cy="yamlArrayDeprecationAlert"]')
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('not.exist')
})
it('isn\'t shown when using multi line yaml-array', () => {
cy.setCodemirrorContent('---\ntags:\n - a\n - b\n - c\n---')
cy.getIframeBody()
.find('[data-cy="yamlArrayDeprecationAlert"]')
.find('[data-cypress-id="yamlArrayDeprecationAlert"]')
.should('not.exist')
})
})

View file

@ -14,7 +14,7 @@ declare namespace Cypress {
Cypress.Commands.add('getIframeBody', () => {
return cy
.get(`iframe[data-cy="documentIframe"][data-content-ready="true"]`)
.get(`iframe[data-cypress-id="documentIframe"][data-content-ready="true"]`)
.should('be.visible')
.its('0.contentDocument')
.should('exist')

View file

@ -10,12 +10,13 @@ import type { Variant } from 'react-bootstrap/types'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../fork-awesome/fork-awesome-icon'
import { CopyOverlay } from '../copy-overlay'
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface CopyToClipboardButtonProps {
export interface CopyToClipboardButtonProps extends PropsWithDataCypressId {
content: string
size?: 'sm' | 'lg'
variant?: Variant
'data-cy'?: string
}
export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
@ -34,7 +35,7 @@ export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
size={size}
variant={variant}
title={t('renderer.highlightCode.copyCode')}
data-cy={props['data-cy']}>
{...cypressId(props)}>
<ForkAwesomeIcon icon='files-o' />
</Button>
<CopyOverlay content={content} clickComponent={button} />

View file

@ -10,8 +10,10 @@ import { Trans, useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../fork-awesome/fork-awesome-icon'
import type { IconName } from '../fork-awesome/types'
import { ShowIf } from '../show-if/show-if'
import type { PropsWithDataCypressId } from '../../../utils/cypress-attribute'
import { cypressId } from '../../../utils/cypress-attribute'
export interface CommonModalProps {
export interface CommonModalProps extends PropsWithDataCypressId {
show: boolean
onHide?: () => void
titleI18nKey?: string
@ -20,7 +22,6 @@ export interface CommonModalProps {
icon?: IconName
size?: 'lg' | 'sm' | 'xl'
additionalClasses?: string
'data-cy'?: string
}
export const CommonModal: React.FC<CommonModalProps> = ({
@ -39,7 +40,7 @@ export const CommonModal: React.FC<CommonModalProps> = ({
return (
<Modal
data-cy={props['data-cy']}
{...cypressId(props)}
show={show}
onHide={onHide}
animation={true}

View file

@ -10,6 +10,7 @@ import { CommonModal } from '../modals/common-modal'
import { Trans, useTranslation } from 'react-i18next'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { dismissMotd } from '../../../redux/motd/methods'
import { cypressId } from '../../../utils/cypress-attribute'
/**
* Reads the motd from the global application state and shows it in a modal.
@ -47,10 +48,10 @@ export const MotdModal: React.FC = () => {
return null
} else {
return (
<CommonModal data-cy={'motd'} show={!!motdState} titleI18nKey={'motd.title'}>
<CommonModal {...cypressId('motd')} show={!!motdState} titleI18nKey={'motd.title'}>
<Modal.Body>{domContent}</Modal.Body>
<Modal.Footer>
<Button variant={'success'} onClick={dismiss} data-cy={'motd-dismiss'}>
<Button variant={'success'} onClick={dismiss} {...cypressId('motd-dismiss')}>
<Trans i18nKey={'common.dismiss'} />
</Button>
</Modal.Footer>

View file

@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'
import { setEditorMode } from '../../../redux/editor/methods'
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { cypressId } from '../../../utils/cypress-attribute'
export enum EditorMode {
PREVIEW = 'view',
@ -30,21 +31,21 @@ export const EditorViewMode: React.FC = () => {
setEditorMode(value)
}}>
<ToggleButton
data-cy={'view-mode-preview'}
{...cypressId('view-mode-preview')}
value={EditorMode.PREVIEW}
variant='outline-secondary'
title={t('editor.viewMode.view')}>
<ForkAwesomeIcon icon='eye' />
</ToggleButton>
<ToggleButton
data-cy={'view-mode-both'}
{...cypressId('view-mode-both')}
value={EditorMode.BOTH}
variant='outline-secondary'
title={t('editor.viewMode.both')}>
<ForkAwesomeIcon icon='columns' />
</ToggleButton>
<ToggleButton
data-cy={'view-mode-editor'}
{...cypressId('view-mode-editor')}
value={EditorMode.EDITOR}
variant='outline-secondary'
title={t('editor.viewMode.edit')}>

View file

@ -42,7 +42,7 @@ export const DocumentInfoLineWordCount: React.FC = () => {
</ShowIf>
<ShowIf condition={wordCount !== null}>
<Trans i18nKey={'editor.modal.documentInfo.words'}>
<UnitalicBoldText text={wordCount ?? ''} dataCy={'document-info-word-count'} />
<UnitalicBoldText text={wordCount ?? ''} data-cypress-id={'document-info-word-count'} />
</Trans>
</ShowIf>
</DocumentInfoLine>

View file

@ -14,6 +14,7 @@ import { DocumentInfoLineWithTimeMode, DocumentInfoTimeLine } from './document-i
import { UnitalicBoldText } from './unitalic-bold-text'
import { useCustomizeAssetsUrl } from '../../../../hooks/common/use-customize-assets-url'
import { DocumentInfoLineWordCount } from './document-info-line-word-count'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface DocumentInfoModalProps {
show: boolean
@ -31,7 +32,7 @@ export const DocumentInfoModal: React.FC<DocumentInfoModalProps> = ({ show, onHi
onHide={onHide}
closeButton={true}
titleI18nKey={'editor.modal.documentInfo.title'}
data-cy={'document-info-modal'}>
{...cypressId('document-info-modal')}>
<Modal.Body>
<ListGroup>
<ListGroup.Item>

View file

@ -5,15 +5,16 @@
*/
import React from 'react'
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface UnitalicBoldTextProps {
export interface UnitalicBoldTextProps extends PropsWithDataCypressId {
text: string | number
dataCy?: string
}
export const UnitalicBoldText: React.FC<UnitalicBoldTextProps> = ({ text, dataCy }) => {
export const UnitalicBoldText: React.FC<UnitalicBoldTextProps> = ({ text, ...props }) => {
return (
<b className={'font-style-normal mr-1'} data-cy={dataCy}>
<b className={'font-style-normal mr-1'} {...cypressId(props)}>
{text}
</b>
)

View file

@ -8,6 +8,7 @@ import React from 'react'
import { Button, Modal } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { CommonModal } from '../../common/modals/common-modal'
import { cypressId } from '../../../utils/cypress-attribute'
export interface MaxLengthWarningModalProps {
show: boolean
@ -20,7 +21,7 @@ export const MaxLengthWarningModal: React.FC<MaxLengthWarningModalProps> = ({ sh
return (
<CommonModal
data-cy={'limitReachedModal'}
{...cypressId('limitReachedModal')}
show={show}
onHide={onHide}
titleI18nKey={'editor.error.limitReached.title'}

View file

@ -9,6 +9,7 @@ import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { ShowIf } from '../../../common/show-if/show-if'
import './status-bar.scss'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface StatusBarInfo {
position: Position
@ -74,7 +75,7 @@ export const StatusBar: React.FC<StatusBarInfo> = ({
<span>{t('editor.statusBar.lines', { lines: linesInDocument })}</span>
&nbsp;&nbsp;
<span
data-cy={'remainingCharacters'}
{...cypressId('remainingCharacters')}
title={getLengthTooltip}
className={remainingCharacters <= 0 ? 'text-danger' : remainingCharacters <= 100 ? 'text-warning' : ''}>
{t('editor.statusBar.length', { length: charactersInDocument })}

View file

@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
import { addEmoji } from '../utils/toolbarButtonUtils'
import { EmojiPicker } from './emoji-picker'
import { cypressId } from '../../../../../utils/cypress-attribute'
export interface EmojiPickerButtonProps {
editor: CodeMirror.Editor
@ -31,7 +32,7 @@ export const EmojiPickerButton: React.FC<EmojiPickerButtonProps> = ({ editor })
onDismiss={() => setShowEmojiPicker(false)}
/>
<Button
data-cy={'show-emoji-picker'}
{...cypressId('show-emoji-picker')}
variant='light'
onClick={() => setShowEmojiPicker((old) => !old)}
title={t('editor.editorToolbar.emoji')}>

View file

@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
import { addTable } from '../utils/toolbarButtonUtils'
import { TablePicker } from './table-picker'
import { cypressId } from '../../../../../utils/cypress-attribute'
export interface TablePickerButtonProps {
editor: CodeMirror.Editor
@ -31,7 +32,7 @@ export const TablePickerButton: React.FC<TablePickerButtonProps> = ({ editor })
}}
/>
<Button
data-cy={'show-table-overlay'}
{...cypressId('show-table-overlay')}
variant='light'
onClick={() => setShowTablePicker((old) => !old)}
title={t('editor.editorToolbar.table.title')}>

View file

@ -12,6 +12,7 @@ import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-ic
import { createNumberRangeArray } from '../../../../common/number-range/number-range'
import { CustomTableSizeModal } from './custom-table-size-modal'
import './table-picker.scss'
import { cypressId } from '../../../../../utils/cypress-attribute'
export interface TablePickerProps {
show: boolean
@ -75,7 +76,7 @@ export const TablePicker: React.FC<TablePickerProps> = ({ show, onDismiss, onTab
)}
</div>
<div className='d-flex justify-content-center mt-2'>
<Button data-cy={'show-custom-table-modal'} className={'text-center'} onClick={() => setShowDialog(true)}>
<Button {...cypressId('show-custom-table-modal')} className={'text-center'} onClick={() => setShowDialog(true)}>
<ForkAwesomeIcon icon='table' />
&nbsp;{t('editor.editorToolbar.table.customSize')}
</Button>

View file

@ -33,6 +33,7 @@ import {
superscriptSelection,
underlineSelection
} from './utils/toolbarButtonUtils'
import { cypressId } from '../../../../utils/cypress-attribute'
export interface ToolBarProps {
editor?: Editor
@ -49,42 +50,42 @@ export const ToolBar: React.FC<ToolBarProps> = ({ editor }) => {
<ButtonToolbar className='bg-light'>
<ButtonGroup className={'mx-1 flex-wrap'}>
<Button
data-cy={'format-bold'}
{...cypressId('format-bold')}
variant='light'
onClick={() => makeSelectionBold(editor)}
title={t('editor.editorToolbar.bold')}>
<ForkAwesomeIcon icon='bold' />
</Button>
<Button
data-cy={'format-italic'}
{...cypressId('format-italic')}
variant='light'
onClick={() => makeSelectionItalic(editor)}
title={t('editor.editorToolbar.italic')}>
<ForkAwesomeIcon icon='italic' />
</Button>
<Button
data-cy={'format-underline'}
{...cypressId('format-underline')}
variant='light'
onClick={() => underlineSelection(editor)}
title={t('editor.editorToolbar.underline')}>
<ForkAwesomeIcon icon='underline' />
</Button>
<Button
data-cy={'format-strikethrough'}
{...cypressId('format-strikethrough')}
variant='light'
onClick={() => strikeThroughSelection(editor)}
title={t('editor.editorToolbar.strikethrough')}>
<ForkAwesomeIcon icon='strikethrough' />
</Button>
<Button
data-cy={'format-subscript'}
{...cypressId('format-subscript')}
variant='light'
onClick={() => subscriptSelection(editor)}
title={t('editor.editorToolbar.subscript')}>
<ForkAwesomeIcon icon='subscript' />
</Button>
<Button
data-cy={'format-superscript'}
{...cypressId('format-superscript')}
variant='light'
onClick={() => superscriptSelection(editor)}
title={t('editor.editorToolbar.superscript')}>
@ -93,42 +94,42 @@ export const ToolBar: React.FC<ToolBarProps> = ({ editor }) => {
</ButtonGroup>
<ButtonGroup className={'mx-1 flex-wrap'}>
<Button
data-cy={'format-heading'}
{...cypressId('format-heading')}
variant='light'
onClick={() => addHeaderLevel(editor)}
title={t('editor.editorToolbar.header')}>
<ForkAwesomeIcon icon='header' />
</Button>
<Button
data-cy={'format-code-block'}
{...cypressId('format-code-block')}
variant='light'
onClick={() => addCodeFences(editor)}
title={t('editor.editorToolbar.code')}>
<ForkAwesomeIcon icon='code' />
</Button>
<Button
data-cy={'format-block-quote'}
{...cypressId('format-block-quote')}
variant='light'
onClick={() => addQuotes(editor)}
title={t('editor.editorToolbar.blockquote')}>
<ForkAwesomeIcon icon='quote-right' />
</Button>
<Button
data-cy={'format-unordered-list'}
{...cypressId('format-unordered-list')}
variant='light'
onClick={() => addList(editor)}
title={t('editor.editorToolbar.unorderedList')}>
<ForkAwesomeIcon icon='list' />
</Button>
<Button
data-cy={'format-ordered-list'}
{...cypressId('format-ordered-list')}
variant='light'
onClick={() => addOrderedList(editor)}
title={t('editor.editorToolbar.orderedList')}>
<ForkAwesomeIcon icon='list-ol' />
</Button>
<Button
data-cy={'format-check-list'}
{...cypressId('format-check-list')}
variant='light'
onClick={() => addTaskList(editor)}
title={t('editor.editorToolbar.checkList')}>
@ -137,14 +138,14 @@ export const ToolBar: React.FC<ToolBarProps> = ({ editor }) => {
</ButtonGroup>
<ButtonGroup className={'mx-1 flex-wrap'}>
<Button
data-cy={'format-link'}
{...cypressId('format-link')}
variant='light'
onClick={() => addLink(editor)}
title={t('editor.editorToolbar.link')}>
<ForkAwesomeIcon icon='link' />
</Button>
<Button
data-cy={'format-image'}
{...cypressId('format-image')}
variant='light'
onClick={() => addImage(editor)}
title={t('editor.editorToolbar.image')}>
@ -155,21 +156,21 @@ export const ToolBar: React.FC<ToolBarProps> = ({ editor }) => {
<ButtonGroup className={'mx-1 flex-wrap'}>
<TablePickerButton editor={editor} />
<Button
data-cy={'format-add-line'}
{...cypressId('format-add-line')}
variant='light'
onClick={() => addLine(editor)}
title={t('editor.editorToolbar.line')}>
<ForkAwesomeIcon icon='minus' />
</Button>
<Button
data-cy={'format-collapsable-block'}
{...cypressId('format-collapsable-block')}
variant='light'
onClick={() => addCollapsableBlock(editor)}
title={t('editor.editorToolbar.collapsableBlock')}>
<ForkAwesomeIcon icon='caret-square-o-down' />
</Button>
<Button
data-cy={'format-add-comment'}
{...cypressId('format-add-comment')}
variant='light'
onClick={() => addComment(editor)}
title={t('editor.editorToolbar.comment')}>

View file

@ -26,6 +26,7 @@ import { useSendScrollState } from './hooks/use-send-scroll-state'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { Logger } from '../../../utils/logger'
import { useEffectOnRenderTypeChange } from './hooks/use-effect-on-render-type-change'
import { cypressId } from '../../../utils/cypress-attribute'
export interface RenderIframeProps extends RendererProps {
rendererType: RendererType
@ -139,7 +140,7 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
<CommunicatorImageLightbox />
<iframe
style={{ height: `${frameHeight}px` }}
data-cy={'documentIframe'}
{...cypressId('documentIframe')}
onLoad={onIframeLoad}
title='render'
{...(isTestMode() ? {} : { sandbox: 'allow-downloads allow-same-origin allow-scripts allow-popups' })}

View file

@ -11,13 +11,14 @@ import links from '../../../links.json'
import { TranslatedExternalLink } from '../../common/links/translated-external-link'
import { ShowIf } from '../../common/show-if/show-if'
import type { CommonModalProps } from '../../common/modals/common-modal'
import { cypressId } from '../../../utils/cypress-attribute'
export const YamlArrayDeprecationAlert: React.FC<Partial<CommonModalProps>> = ({ show }) => {
useTranslation()
return (
<ShowIf condition={!!show}>
<Alert data-cy={'yamlArrayDeprecationAlert'} className={'text-wrap'} variant='warning' dir='auto'>
<Alert {...cypressId('yamlArrayDeprecationAlert')} className={'text-wrap'} variant='warning' dir='auto'>
<span className={'text-wrap'}>
<span className={'text-wrap'}>
<Trans i18nKey='editor.deprecatedTags' />

View file

@ -9,6 +9,7 @@ import { Trans, useTranslation } from 'react-i18next'
import { DocumentInfoModal } from '../document-bar/document-info/document-info-modal'
import { SidebarButton } from './sidebar-button'
import type { SpecificSidebarEntryProps } from './types'
import { cypressId } from '../../../utils/cypress-attribute'
export const DocumentInfoSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({ className, hide }) => {
const [showModal, setShowModal] = useState(false)
@ -21,7 +22,7 @@ export const DocumentInfoSidebarEntry: React.FC<SpecificSidebarEntryProps> = ({
className={className}
icon={'line-chart'}
onClick={() => setShowModal(true)}
data-cy={'sidebar-btn-document-info'}>
{...cypressId('sidebar-btn-document-info')}>
<Trans i18nKey={'editor.modal.documentInfo.title'} />
</SidebarButton>
<DocumentInfoModal show={showModal} onHide={() => setShowModal(false)} />

View file

@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next'
import { download } from '../../common/download/download'
import { SidebarButton } from './sidebar-button'
import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-content'
import { cypressId } from '../../../utils/cypress-attribute'
export const ExportMarkdownSidebarEntry: React.FC = () => {
const { t } = useTranslation()
@ -21,7 +22,7 @@ export const ExportMarkdownSidebarEntry: React.FC = () => {
}, [markdownContent, t])
return (
<SidebarButton data-cy={'menu-export-markdown'} onClick={onClick} icon={'file-text'}>
<SidebarButton {...cypressId('menu-export-markdown')} onClick={onClick} icon={'file-text'}>
<Trans i18nKey={'editor.export.markdown-file'} />
</SidebarButton>
)

View file

@ -12,6 +12,7 @@ import { SidebarButton } from './sidebar-button'
import { SidebarMenu } from './sidebar-menu'
import type { SpecificSidebarMenuProps } from './types'
import { DocumentSidebarMenuSelection } from './types'
import { cypressId } from '../../../utils/cypress-attribute'
export const ExportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
className,
@ -30,7 +31,7 @@ export const ExportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
return (
<Fragment>
<SidebarButton
data-cy={'menu-export'}
{...cypressId('menu-export')}
hide={hide}
icon={expand ? 'arrow-left' : 'cloud-download'}
className={className}

View file

@ -10,6 +10,7 @@ import { useNoteMarkdownContent } from '../../../hooks/common/use-note-markdown-
import { setNoteContent } from '../../../redux/note-details/methods'
import { SidebarButton } from './sidebar-button'
import { UploadInput } from './upload-input'
import { cypressId } from '../../../utils/cypress-attribute'
export const ImportMarkdownSidebarEntry: React.FC = () => {
const markdownContent = useNoteMarkdownContent()
@ -42,12 +43,12 @@ export const ImportMarkdownSidebarEntry: React.FC = () => {
return (
<Fragment>
<SidebarButton data-cy={'menu-import-markdown'} icon={'file-text-o'} onClick={buttonClick}>
<SidebarButton {...cypressId('menu-import-markdown')} icon={'file-text-o'} onClick={buttonClick}>
<Trans i18nKey={'editor.import.file'} />
</SidebarButton>
<UploadInput
onLoad={onImportMarkdown}
data-cy={'menu-import-markdown-input'}
{...cypressId('menu-import-markdown-input')}
acceptedFiles={'.md, text/markdown, text/plain'}
onClickRef={clickRef}
/>

View file

@ -11,6 +11,7 @@ import { SidebarButton } from './sidebar-button'
import { SidebarMenu } from './sidebar-menu'
import type { SpecificSidebarMenuProps } from './types'
import { DocumentSidebarMenuSelection } from './types'
import { cypressId } from '../../../utils/cypress-attribute'
export const ImportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
className,
@ -29,7 +30,7 @@ export const ImportMenuSidebarMenu: React.FC<SpecificSidebarMenuProps> = ({
return (
<Fragment>
<SidebarButton
data-cy={'menu-import'}
{...cypressId('menu-import')}
hide={hide}
icon={expand ? 'arrow-left' : 'cloud-upload'}
className={className}

View file

@ -7,6 +7,7 @@
import type { RefObject } from 'react'
import type { IconName } from '../../common/fork-awesome/types'
import type { SidebarEntryVariant } from './sidebar-button'
import type { PropsWithDataCypressId } from '../../../utils/cypress-attribute'
export interface SpecificSidebarEntryProps {
className?: string
@ -14,14 +15,13 @@ export interface SpecificSidebarEntryProps {
onClick?: () => void
}
export interface SidebarEntryProps {
export interface SidebarEntryProps extends PropsWithDataCypressId {
icon?: IconName
variant?: SidebarEntryVariant
buttonRef?: RefObject<HTMLButtonElement>
hide?: boolean
className?: string
onClick?: () => void
'data-cy'?: string
}
export interface SidebarMenuProps {

View file

@ -7,14 +7,15 @@
import type { MutableRefObject } from 'react'
import React, { useCallback, useEffect, useRef } from 'react'
import { Logger } from '../../../utils/logger'
import type { PropsWithDataCypressId } from '../../../utils/cypress-attribute'
import { cypressId } from '../../../utils/cypress-attribute'
const log = new Logger('UploadInput')
export interface UploadInputProps {
export interface UploadInputProps extends PropsWithDataCypressId {
onLoad: (file: File) => Promise<void>
acceptedFiles: string
onClickRef: MutableRefObject<(() => void) | undefined>
'data-cy'?: string
}
export const UploadInput: React.FC<UploadInputProps> = ({ onLoad, acceptedFiles, onClickRef, ...props }) => {
@ -44,7 +45,5 @@ export const UploadInput: React.FC<UploadInputProps> = ({ onLoad, acceptedFiles,
onClickRef.current = onClick
})
return (
<input data-cy={props['data-cy']} type='file' ref={fileInputReference} className='d-none' accept={acceptedFiles} />
)
return <input {...cypressId(props)} type='file' ref={fileInputReference} className='d-none' accept={acceptedFiles} />
}

View file

@ -12,6 +12,7 @@ import { useApplicationState } from '../../../hooks/common/use-application-state
import { ShowIf } from '../../common/show-if/show-if'
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
import './cover-buttons.scss'
import { cypressId } from '../../../utils/cypress-attribute'
export const CoverButtons: React.FC = () => {
useTranslation()
@ -33,7 +34,7 @@ export const CoverButtons: React.FC = () => {
</span>
</ShowIf>
<Link to='/n/features'>
<Button data-cy={'features-button'} className='cover-button' variant='primary' size='lg'>
<Button {...cypressId('features-button')} className='cover-button' variant='primary' size='lg'>
<Trans i18nKey='landing.intro.exploreFeatures' />
</Button>
</Link>

View file

@ -8,6 +8,7 @@ import React, { Fragment, useCallback, useState } from 'react'
import { Trans } from 'react-i18next'
import { Link } from 'react-router-dom'
import { VersionInfoModal } from './version-info-modal'
import { cypressId } from '../../../../utils/cypress-attribute'
export const VersionInfoLink: React.FC = () => {
const [show, setShow] = useState(false)
@ -16,7 +17,7 @@ export const VersionInfoLink: React.FC = () => {
return (
<Fragment>
<Link data-cy={'show-version-modal'} to={'#'} className={'text-light'} onClick={showModal}>
<Link {...cypressId('show-version-modal')} to={'#'} className={'text-light'} onClick={showModal}>
<Trans i18nKey={'landing.versionInfo.versionInfo'} />
</Link>
<VersionInfoModal onHide={closeModal} show={show} />

View file

@ -13,6 +13,7 @@ import frontendVersion from '../../../../version.json'
import links from '../../../../links.json'
import type { BackendVersion } from '../../../../api/config/types'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
import { cypressId } from '../../../../utils/cypress-attribute'
export const VersionInfoModal: React.FC<CommonModalProps> = ({ onHide, show }) => {
const serverVersion: BackendVersion = useApplicationState((state) => state.config.version)
@ -31,7 +32,7 @@ export const VersionInfoModal: React.FC<CommonModalProps> = ({ onHide, show }) =
return (
<CommonModal
data-cy={'version-modal'}
{...cypressId('version-modal')}
show={show}
onHide={onHide}
closeButton={true}

View file

@ -13,6 +13,7 @@ import { ShowIf } from '../../common/show-if/show-if'
import { getApiUrl } from '../../../api/utils'
import { INTERACTIVE_LOGIN_METHODS } from '../../../api/auth'
import { useApplicationState } from '../../../hooks/common/use-application-state'
import { cypressId } from '../../../utils/cypress-attribute'
export type SignInButtonProps = Omit<ButtonProps, 'href'>
@ -36,7 +37,7 @@ export const SignInButton: React.FC<SignInButtonProps> = ({ variant, ...props })
return (
<ShowIf condition={authEnabled}>
<LinkContainer to={loginLink} title={t('login.signIn')}>
<Button data-cy={'sign-in-button'} variant={variant || 'success'} {...props}>
<Button {...cypressId('sign-in-button')} variant={variant || 'success'} {...props}>
<Trans i18nKey='login.signIn' />
</Button>
</LinkContainer>

View file

@ -8,6 +8,7 @@ import React from 'react'
import { Alert } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { useApplicationState } from '../../hooks/common/use-application-state'
import { cypressId } from '../../utils/cypress-attribute'
import { ShowIf } from '../common/show-if/show-if'
import type { SimpleAlertProps } from '../common/simple-alert/simple-alert-props'
@ -18,7 +19,7 @@ export const DocumentLengthLimitReachedAlert: React.FC<SimpleAlertProps> = ({ sh
return (
<ShowIf condition={show}>
<Alert variant='danger' dir={'auto'} data-cy={'limitReachedMessage'}>
<Alert variant='danger' dir={'auto'} {...cypressId('limitReachedMessage')}>
<Trans i18nKey={'editor.error.limitReached.description'} values={{ maxLength }} />
</Alert>
</ShowIf>

View file

@ -9,6 +9,7 @@ import { Alert } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { useIsDarkModeActivated } from '../../../../../hooks/common/use-is-dark-mode-activated'
import { Logger } from '../../../../../utils/logger'
import { cypressId } from '../../../../../utils/cypress-attribute'
const log = new Logger('FlowChart')
@ -60,6 +61,6 @@ export const FlowChart: React.FC<FlowChartProps> = ({ code }) => {
</Alert>
)
} else {
return <div ref={diagramRef} data-cy={'flowchart'} className={'text-center'} />
return <div ref={diagramRef} {...cypressId('flowchart')} className={'text-center'} />
}
}

View file

@ -5,6 +5,7 @@
*/
import React, { useCallback } from 'react'
import { cypressId } from '../../../../utils/cypress-attribute'
import './gist-frame.scss'
import { useResizeGistFrame } from './use-resize-gist-frame'
@ -31,7 +32,7 @@ export const GistFrame: React.FC<GistFrameProps> = ({ id }) => {
<span>
<iframe
sandbox=''
data-cy={'gh-gist'}
{...cypressId('gh-gist')}
width='100%'
height={`${frameHeight}px`}
frameBorder='0'

View file

@ -9,6 +9,7 @@ import { Alert } from 'react-bootstrap'
import { ShowIf } from '../../../common/show-if/show-if'
import { useFrontendBaseUrl } from '../../../../hooks/common/use-frontend-base-url'
import { Logger } from '../../../../utils/logger'
import { cypressId } from '../../../../utils/cypress-attribute'
const log = new Logger('GraphvizFrame')
@ -66,7 +67,7 @@ export const GraphvizFrame: React.FC<GraphvizFrameProps> = ({ code }) => {
<ShowIf condition={!!error}>
<Alert variant={'warning'}>{error}</Alert>
</ShowIf>
<div className={'svg-container'} data-cy={'graphviz'} ref={container} />
<div className={'svg-container'} {...cypressId('graphviz')} ref={container} />
</Fragment>
)
}

View file

@ -11,6 +11,7 @@ import { CopyToClipboardButton } from '../../../../common/copyable/copy-to-clipb
import '../../../utils/button-inside.scss'
import './highlighted-code.scss'
import { Logger } from '../../../../../utils/logger'
import { cypressId } from '../../../../../utils/cypress-attribute'
const log = new Logger('HighlightedCode')
@ -70,7 +71,7 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
{dom}
</code>
<div className={'text-right button-inside'}>
<CopyToClipboardButton content={code} data-cy='copy-code-button' />
<CopyToClipboardButton content={code} {...cypressId('copy-code-button')} />
</div>
</div>
)

View file

@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next'
import { LockButton } from '../../../common/lock-button/lock-button'
import '../../utils/button-inside.scss'
import { Logger } from '../../../../utils/logger'
import { cypressId } from '../../../../utils/cypress-attribute'
const log = new Logger('MarkmapFrame')
@ -66,7 +67,7 @@ export const MarkmapFrame: React.FC<MarkmapFrameProps> = ({ code }) => {
}, [code])
return (
<div data-cy={'markmap'} className={'position-relative'}>
<div {...cypressId('markmap')} className={'position-relative'}>
<div className={'svg-container'} ref={diagramContainer} />
<div className={'text-right button-inside'}>
<LockButton

View file

@ -8,13 +8,14 @@ import React from 'react'
import { Alert } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import links from '../../../../links.json'
import { cypressId } from '../../../../utils/cypress-attribute'
import { TranslatedExternalLink } from '../../../common/links/translated-external-link'
export const DeprecationWarning: React.FC = () => {
useTranslation()
return (
<Alert data-cy={'yaml'} className={'mt-2'} variant={'warning'}>
<Alert {...cypressId('yaml')} className={'mt-2'} variant={'warning'}>
<span className={'text-wrap'}>
<Trans i18nKey={'renderer.sequence.deprecationWarning'} />
</span>

View file

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { isTestMode } from './test-modes'
export interface PropsWithDataCypressId {
'data-cypress-id'?: string | undefined
}
/**
* Returns an object with the "data-cypress-id" attribute that is used to find
* elements for integration tests.
* This works only if the runtime is built in test mode.
*
* @param identifier The identifier that is used to find the element
* @return An object if in test mode, undefined otherwise.
*/
export const cypressId = (
identifier: string | undefined | PropsWithDataCypressId
): Record<'data-cypress-id', string> | undefined => {
if (!isTestMode() || !identifier) {
return
}
const attributeContent = typeof identifier === 'string' ? identifier : identifier['data-cypress-id']
if (attributeContent !== undefined) {
return { 'data-cypress-id': attributeContent }
}
}