mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04:00
added e2e tests (#298)
- added e2e tests for - banner - history - intro - language - link - added e2e workflow - added cypress badge to README
This commit is contained in:
parent
1a5d4f6db8
commit
f0fe7f5ac2
26 changed files with 1332 additions and 77 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -2,9 +2,9 @@ name: lint and build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master, dev]
|
branches: [master]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master, dev]
|
branches: [master]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
34
.github/workflows/e2e.yml
vendored
Normal file
34
.github/workflows/e2e.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: e2e
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
end2end:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
browser: ['e2e:chrome', 'e2e:firefox']
|
||||||
|
name: ${{ matrix.browser }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Cache node_modules
|
||||||
|
uses: actions/cache@v1.1.0
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: node_modules
|
||||||
|
- name: Cache ~/.cache
|
||||||
|
uses: actions/cache@v1.1.0
|
||||||
|
with:
|
||||||
|
path: ~/.cache
|
||||||
|
key: cache
|
||||||
|
- name: install cypress dependencies
|
||||||
|
run: sudo apt-get install libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Run e2e in chrome
|
||||||
|
run: yarn ${{ matrix.browser }}
|
13
README.md
13
README.md
|
@ -1,5 +1,7 @@
|
||||||
# CodiMD - React Client
|
# CodiMD - React Client
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
This is the new, improved and better looking frontend for CodiMD 2.0.
|
This is the new, improved and better looking frontend for CodiMD 2.0.
|
||||||
Our goal is to recreate the current frontend in react and to improve it.
|
Our goal is to recreate the current frontend in react and to improve it.
|
||||||
|
|
||||||
|
@ -18,6 +20,17 @@ This should run the app in the development mode and open [http://localhost:3000]
|
||||||
The page will reload if you make edits.
|
The page will reload if you make edits.
|
||||||
You will also see any lint errors in the console.
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
#### End2End
|
||||||
|
|
||||||
|
We use [cypress](https://cypress.io) for e2e tests.
|
||||||
|
|
||||||
|
1. Run the frontend with `yarn start`
|
||||||
|
2. RUn `yarn cy:open` to open the cypress test loader
|
||||||
|
3. Choose your browser and test
|
||||||
|
4. Let the tests run
|
||||||
|
|
||||||
## Production mode
|
## Production mode
|
||||||
|
|
||||||
1. Clone this repo (e.g. `git clone https://github.com/codimd/react-client.git codimd-react-client`)
|
1. Clone this repo (e.g. `git clone https://github.com/codimd/react-client.git codimd-react-client`)
|
||||||
|
|
8
cypress.json
Normal file
8
cypress.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"baseUrl": "http://localhost:3000/",
|
||||||
|
"experimentalFetchPolyfill": true,
|
||||||
|
"firefoxGcInterval": {
|
||||||
|
"runMode": null,
|
||||||
|
"openMode": null
|
||||||
|
}
|
||||||
|
}
|
23
cypress/.eslintrc.json
Normal file
23
cypress/.eslintrc.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"parserOptions": {
|
||||||
|
"tsconfigRootDir": "",
|
||||||
|
"project": [
|
||||||
|
"./cypress/tsconfig.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"cypress",
|
||||||
|
"chai-friendly"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:cypress/recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unused-expressions": 0,
|
||||||
|
"no-unused-expressions": 0,
|
||||||
|
"chai-friendly/no-unused-expressions": 2
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"cypress/globals": true
|
||||||
|
}
|
||||||
|
}
|
30
cypress/fixtures/languages.ts
Normal file
30
cypress/fixtures/languages.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
export const languages: string[] = [
|
||||||
|
'English',
|
||||||
|
'简体中文',
|
||||||
|
'繁體中文',
|
||||||
|
'Français',
|
||||||
|
'Deutsch',
|
||||||
|
'日本語',
|
||||||
|
'Español',
|
||||||
|
'Català',
|
||||||
|
'Ελληνικά',
|
||||||
|
'Português',
|
||||||
|
'Italiano',
|
||||||
|
'Türkçe',
|
||||||
|
'Русский',
|
||||||
|
'Nederlands',
|
||||||
|
'Hrvatski',
|
||||||
|
'Polski',
|
||||||
|
'Українська',
|
||||||
|
'हिन्दी',
|
||||||
|
'Svenska',
|
||||||
|
'Esperanto',
|
||||||
|
'Dansk',
|
||||||
|
'한국어',
|
||||||
|
'Bahasa Indonesia',
|
||||||
|
'Cрпски',
|
||||||
|
'Tiếng Việt',
|
||||||
|
'العربية',
|
||||||
|
'Česky',
|
||||||
|
'Slovensky'
|
||||||
|
]
|
26
cypress/integration/banner.spec.ts
Normal file
26
cypress/integration/banner.spec.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { banner } from '../support/config'
|
||||||
|
|
||||||
|
describe('Banner', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/')
|
||||||
|
expect(localStorage.getItem('bannerTimeStamp')).to.be.null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the correct alert banner text', () => {
|
||||||
|
cy.get('.alert-primary.show')
|
||||||
|
.contains(banner.text)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can be dismissed', () => {
|
||||||
|
cy.get('.alert-primary.show')
|
||||||
|
.contains(banner.text)
|
||||||
|
cy.get('.alert-primary.show')
|
||||||
|
.find('.fa-times')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
expect(localStorage.getItem('bannerTimeStamp')).to.equal(banner.timestamp)
|
||||||
|
})
|
||||||
|
cy.get('.alert-primary.show')
|
||||||
|
.should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
35
cypress/integration/history.spec.ts
Normal file
35
cypress/integration/history.spec.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
describe('History', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/history')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('History Mode', () => {
|
||||||
|
it('Cards', () => {
|
||||||
|
cy.get('div.card')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Table', () => {
|
||||||
|
cy.get('i.fa-table')
|
||||||
|
.click()
|
||||||
|
cy.get('table.history-table')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Pinning', () => {
|
||||||
|
it('Cards', () => {
|
||||||
|
cy.get('.fa-thumb-tack')
|
||||||
|
.first()
|
||||||
|
.click()
|
||||||
|
cy.get('.modal-dialog')
|
||||||
|
.should('be.visible')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Table', () => {
|
||||||
|
cy.get('.fa-thumb-tack')
|
||||||
|
.first()
|
||||||
|
.click()
|
||||||
|
cy.get('.modal-dialog')
|
||||||
|
.should('be.visible')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
58
cypress/integration/intro.spec.ts
Normal file
58
cypress/integration/intro.spec.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
describe('Intro', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Cover Button are hidden when logged in', () => {
|
||||||
|
it('Sign in Cover Button', () => {
|
||||||
|
cy.get('.cover-button.btn-success')
|
||||||
|
.should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Features Cover Button', () => {
|
||||||
|
cy.get('.cover-button.btn-primary')
|
||||||
|
.should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Cover Button are shown when logged out', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.logout()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Sign in Cover Button', () => {
|
||||||
|
cy.get('.cover-button.btn-success')
|
||||||
|
.should('exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Features Cover Button', () => {
|
||||||
|
cy.get('.cover-button.btn-primary')
|
||||||
|
.should('exist')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Version', () => {
|
||||||
|
it('can be opened', () => {
|
||||||
|
cy.get('#versionModal')
|
||||||
|
.should('not.be.visible')
|
||||||
|
cy.get('#version')
|
||||||
|
.click()
|
||||||
|
cy.get('#versionModal')
|
||||||
|
.should('be.visible')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can be closed', () => {
|
||||||
|
cy.get('#versionModal')
|
||||||
|
.should('not.be.visible')
|
||||||
|
cy.get('#version')
|
||||||
|
.click()
|
||||||
|
cy.get('#versionModal')
|
||||||
|
.should('be.visible')
|
||||||
|
cy.get('body')
|
||||||
|
.click()
|
||||||
|
cy.get('#versionModal')
|
||||||
|
.should('not.be.visible')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
30
cypress/integration/language.spec.ts
Normal file
30
cypress/integration/language.spec.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { languages } from '../fixtures/languages'
|
||||||
|
|
||||||
|
describe('Languages', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('/')
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
165
cypress/integration/link.spec.ts
Normal file
165
cypress/integration/link.spec.ts
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import '../support/index'
|
||||||
|
|
||||||
|
describe('Links Intro', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
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')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Menu Buttons logged out', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.logout()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('New guest note', () => {
|
||||||
|
cy.get('.d-inline-flex.btn-primary')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/new')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Sign In', () => {
|
||||||
|
cy.get('.btn-success.btn-sm')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/login')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Menu Buttons logged in', () => {
|
||||||
|
it('New note', () => {
|
||||||
|
cy.get('.d-inline-flex.btn-primary').click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/new')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('User Menu', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get('#dropdown-user').click()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Features', () => {
|
||||||
|
cy.get('a.dropdown-item > i.fa-bolt')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/features')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Features', () => {
|
||||||
|
cy.get('a.dropdown-item > i.fa-user')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/profile')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Feature Links', () => {
|
||||||
|
it('Share-Notes', () => {
|
||||||
|
cy.get('i.fa-bolt.fa-3x')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/features#Share-Notes')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('MathJax', () => {
|
||||||
|
cy.get('i.fa-bar-chart.fa-3x')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/features#MathJax')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Slide-Mode', () => {
|
||||||
|
cy.get('i.fa-television.fa-3x')
|
||||||
|
.click()
|
||||||
|
cy.url()
|
||||||
|
.should('include', '/features#Slide-Mode')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Powered By Links', () => {
|
||||||
|
it('CodiMD', () => {
|
||||||
|
cy.get('a[href="https://codimd.org"]')
|
||||||
|
.checkExternalLink('https://codimd.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/codimd/server"]')
|
||||||
|
.checkExternalLink('https://github.com/codimd/server')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Discourse', () => {
|
||||||
|
cy.get('a[href="https://community.codimd.org"]')
|
||||||
|
.checkExternalLink('https://community.codimd.org')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Matrix', () => {
|
||||||
|
cy.get('a[href="https://riot.im/app/#/room/#codimd:matrix.org"]')
|
||||||
|
.checkExternalLink('https://riot.im/app/#/room/#codimd:matrix.org')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Mastodon', () => {
|
||||||
|
cy.get('a[href="https://social.codimd.org/mastodon"]')
|
||||||
|
.checkExternalLink('https://social.codimd.org/mastodon')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('POEditor', () => {
|
||||||
|
cy.get('a[href="https://translate.codimd.org"]')
|
||||||
|
.checkExternalLink('https://translate.codimd.org')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
15
cypress/support/checkLinks.ts
Normal file
15
cypress/support/checkLinks.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
declare namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
/**
|
||||||
|
* Custom command to check an external Link.
|
||||||
|
* @example cy.get(a#extern).checkExternalLink('http://example.com')
|
||||||
|
*/
|
||||||
|
checkExternalLink (url: string): Chainable<Element>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cypress.Commands.add('checkExternalLink', { prevSubject: 'element' }, ($element: JQuery, url: string) => {
|
||||||
|
cy.wrap($element).should('have.attr', 'href', url)
|
||||||
|
.should('have.attr', 'target', '_blank')
|
||||||
|
})
|
47
cypress/support/config.ts
Normal file
47
cypress/support/config.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
export const banner = {
|
||||||
|
text: 'This is the mock banner call',
|
||||||
|
timestamp: '2020-05-22T20:46:08.962Z'
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.server()
|
||||||
|
cy.route({
|
||||||
|
url: '/api/v2/config',
|
||||||
|
response: {
|
||||||
|
allowAnonymous: true,
|
||||||
|
authProviders: {
|
||||||
|
facebook: true,
|
||||||
|
github: true,
|
||||||
|
twitter: true,
|
||||||
|
gitlab: true,
|
||||||
|
dropbox: true,
|
||||||
|
ldap: true,
|
||||||
|
google: true,
|
||||||
|
saml: true,
|
||||||
|
oauth2: true,
|
||||||
|
email: true,
|
||||||
|
openid: true
|
||||||
|
},
|
||||||
|
branding: {
|
||||||
|
name: 'ACME Corp',
|
||||||
|
logo: 'http://localhost:3000/acme.png'
|
||||||
|
},
|
||||||
|
banner: banner,
|
||||||
|
customAuthNames: {
|
||||||
|
ldap: 'FooBar',
|
||||||
|
oauth2: 'Olaf2',
|
||||||
|
saml: 'aufSAMLn.de'
|
||||||
|
},
|
||||||
|
specialLinks: {
|
||||||
|
privacy: 'https://example.com/privacy',
|
||||||
|
termsOfUse: 'https://example.com/termsOfUse',
|
||||||
|
imprint: 'https://example.com/imprint'
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
version: 'mock',
|
||||||
|
sourceCodeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||||
|
issueTrackerUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
18
cypress/support/index.ts
Normal file
18
cypress/support/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// ***********************************************************
|
||||||
|
// This example support/index.ts is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
import './checkLinks'
|
||||||
|
import './config'
|
||||||
|
import './login'
|
15
cypress/support/login.ts
Normal file
15
cypress/support/login.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
declare namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
/**
|
||||||
|
* Custom command to log the user out.
|
||||||
|
* @example cy.logout()
|
||||||
|
*/
|
||||||
|
logout (): Chainable<Window>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cypress.Commands.add('logout', () => {
|
||||||
|
cy.get('#dropdown-user').click()
|
||||||
|
cy.get('.fa-sign-out').click()
|
||||||
|
})
|
12
cypress/tsconfig.json
Normal file
12
cypress/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"baseUrl": "../node_modules",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es5", "dom"],
|
||||||
|
"types": ["cypress"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
22
cypress/webpack.config.js
Normal file
22
cypress/webpack.config.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/index.ts',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js']
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: 'bundle.js',
|
||||||
|
path: path.resolve(__dirname, 'dist')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
17
package.json
17
package.json
|
@ -85,7 +85,12 @@
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"cy:open": "cypress open",
|
||||||
|
"cy:run:chrome": "cypress run --browser chrome",
|
||||||
|
"cy:run:firefox": "cypress run --browser firefox",
|
||||||
|
"e2e:chrome": "start-server-and-test start http-get://localhost:3000 cy:run:chrome",
|
||||||
|
"e2e:firefox": "start-server-and-test start http-get://localhost:3000 cy:run:firefox"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
@ -121,9 +126,17 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@cypress/webpack-preprocessor": "^5.4.1",
|
||||||
|
"@testing-library/cypress": "^6.0.0",
|
||||||
"@types/redux-devtools": "3.0.47",
|
"@types/redux-devtools": "3.0.47",
|
||||||
"@types/redux-devtools-extension": "2.13.2",
|
"@types/redux-devtools-extension": "2.13.2",
|
||||||
|
"@types/testing-library__cypress": "^5.0.6",
|
||||||
|
"cypress": "^4.9.0",
|
||||||
|
"eslint-plugin-chai-friendly": "^0.6.0",
|
||||||
|
"eslint-plugin-cypress": "^2.11.1",
|
||||||
"redux-devtools": "3.5.0",
|
"redux-devtools": "3.5.0",
|
||||||
"redux-devtools-extension": "2.13.8"
|
"redux-devtools-extension": "2.13.8",
|
||||||
|
"start-server-and-test": "^1.11.0",
|
||||||
|
"ts-loader": "^7.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
"saml": "aufSAMLn.de"
|
"saml": "aufSAMLn.de"
|
||||||
},
|
},
|
||||||
"specialLinks": {
|
"specialLinks": {
|
||||||
"privacy": "test",
|
"privacy": "https://example.com/privacy",
|
||||||
"termsOfUse": "test",
|
"termsOfUse": "https://example.com/termsOfUse",
|
||||||
"imprint": "test"
|
"imprint": "https://example.com/imprint"
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"version": "mock",
|
"version": "mock",
|
||||||
|
|
|
@ -3,11 +3,12 @@ import { ForkAwesomeIcon, IconName } from '../fork-awesome/fork-awesome-icon'
|
||||||
import { ShowIf } from '../show-if/show-if'
|
import { ShowIf } from '../show-if/show-if'
|
||||||
import { LinkWithTextProps } from './types'
|
import { LinkWithTextProps } from './types'
|
||||||
|
|
||||||
export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, className = 'text-light' }) => {
|
export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = 'text-light' }) => {
|
||||||
return (
|
return (
|
||||||
<a href={href}
|
<a href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
id={id}
|
||||||
className={className}
|
className={className}
|
||||||
dir='auto'
|
dir='auto'
|
||||||
>
|
>
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { ForkAwesomeIcon, IconName } from '../fork-awesome/fork-awesome-icon'
|
||||||
import { ShowIf } from '../show-if/show-if'
|
import { ShowIf } from '../show-if/show-if'
|
||||||
import { LinkWithTextProps } from './types'
|
import { LinkWithTextProps } from './types'
|
||||||
|
|
||||||
export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, className = 'text-light' }) => {
|
export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = 'text-light' }) => {
|
||||||
return (
|
return (
|
||||||
<Link to={href}
|
<Link to={href}
|
||||||
className={className}>
|
className={className}
|
||||||
|
id={id}
|
||||||
|
>
|
||||||
<ShowIf condition={!!icon}>
|
<ShowIf condition={!!icon}>
|
||||||
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true}/>
|
<ForkAwesomeIcon icon={icon as IconName} fixedWidth={true}/>
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
|
|
|
@ -2,16 +2,17 @@ import { StringMap, TOptionsBase } from 'i18next'
|
||||||
import { IconName } from '../fork-awesome/fork-awesome-icon'
|
import { IconName } from '../fork-awesome/fork-awesome-icon'
|
||||||
|
|
||||||
export interface GeneralLinkProp {
|
export interface GeneralLinkProp {
|
||||||
href: string;
|
href: string
|
||||||
icon?: IconName;
|
icon?: IconName
|
||||||
|
id?: string
|
||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LinkWithTextProps extends GeneralLinkProp {
|
export interface LinkWithTextProps extends GeneralLinkProp {
|
||||||
text: string;
|
text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TranslatedLinkProps extends GeneralLinkProp{
|
export interface TranslatedLinkProps extends GeneralLinkProp{
|
||||||
i18nKey: string;
|
i18nKey: string
|
||||||
i18nOption?: (TOptionsBase & StringMap) | string
|
i18nOption?: (TOptionsBase & StringMap) | string
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,10 @@ const HeaderBar: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Navbar className="justify-content-between">
|
<Navbar className="justify-content-between">
|
||||||
<div className="nav header-nav">
|
<div className="nav header-nav">
|
||||||
<HeaderNavLink to="/intro">
|
<HeaderNavLink to="/intro" id='navLinkIntro'>
|
||||||
<Trans i18nKey="landing.navigation.intro"/>
|
<Trans i18nKey="landing.navigation.intro"/>
|
||||||
</HeaderNavLink>
|
</HeaderNavLink>
|
||||||
<HeaderNavLink to="/history">
|
<HeaderNavLink to="/history" id='navLinkHistory'>
|
||||||
<Trans i18nKey="landing.navigation.history"/>
|
<Trans i18nKey="landing.navigation.history"/>
|
||||||
</HeaderNavLink>
|
</HeaderNavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
import React from 'react'
|
||||||
import { Nav } from 'react-bootstrap'
|
import { Nav } from 'react-bootstrap'
|
||||||
import { LinkContainer } from 'react-router-bootstrap'
|
import { LinkContainer } from 'react-router-bootstrap'
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
export interface HeaderNavLinkProps {
|
export interface HeaderNavLinkProps {
|
||||||
to: string
|
to: string
|
||||||
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderNavLink: React.FC<HeaderNavLinkProps> = (props) => {
|
export const HeaderNavLink: React.FC<HeaderNavLinkProps> = ({ to, id, children }) => {
|
||||||
return (
|
return (
|
||||||
<Nav.Item>
|
<Nav.Item>
|
||||||
<LinkContainer to={props.to}>
|
<LinkContainer to={to}>
|
||||||
<Nav.Link className="text-light" href={props.to}>{props.children}</Nav.Link>
|
<Nav.Link id={id} className="text-light" href={to}>{children}</Nav.Link>
|
||||||
</LinkContainer>
|
</LinkContainer>
|
||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
)
|
)
|
||||||
|
|
|
@ -34,8 +34,8 @@ export const VersionInfo: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Link to={'#'} className={'text-light'} onClick={handleShow}><Trans i18nKey={'landing.versionInfo.versionInfo'}/></Link>
|
<Link id='version' to={'#'} className={'text-light'} onClick={handleShow}><Trans i18nKey={'landing.versionInfo.versionInfo'}/></Link>
|
||||||
<Modal show={show} onHide={handleClose} animation={true}>
|
<Modal id='versionModal' show={show} onHide={handleClose} animation={true}>
|
||||||
<Modal.Body className="text-dark">
|
<Modal.Body className="text-dark">
|
||||||
<h3><Trans i18nKey={'landing.versionInfo.title'}/></h3>
|
<h3><Trans i18nKey={'landing.versionInfo.title'}/></h3>
|
||||||
<Row>
|
<Row>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue