mirror of
				https://github.com/theoludwig/theoludwig.git
				synced 2025-10-14 20:23:25 +02:00 
			
		
		
		
	test: add cypress e2e (#159)
This commit is contained in:
		
							
								
								
									
										32
									
								
								.github/workflows/Divlo.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/Divlo.yml
									
									
									
									
										vendored
									
									
								
							| @@ -46,7 +46,24 @@ jobs: | ||||
|       - run: 'npm run lint:markdown' | ||||
|       - run: 'npm run lint:typescript' | ||||
|  | ||||
|   build: | ||||
|   test-unit: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v2.3.4' | ||||
|  | ||||
|       - name: 'Use Node.js' | ||||
|         uses: 'actions/setup-node@v2.4.0' | ||||
|         with: | ||||
|           node-version: '16.x' | ||||
|           cache: 'npm' | ||||
|  | ||||
|       - name: 'Install' | ||||
|         run: 'npm install' | ||||
|  | ||||
|       - name: 'Unit Test' | ||||
|         run: 'npm run test:unit' | ||||
|  | ||||
|   test-lighthouse: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v2.3.4' | ||||
| @@ -64,11 +81,11 @@ jobs: | ||||
|         run: 'npm run build' | ||||
|  | ||||
|       - name: 'Lighthouse' | ||||
|         run: 'npm run lighthouse' | ||||
|         run: 'npm run test:lighthouse' | ||||
|         env: | ||||
|           LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} | ||||
|  | ||||
|   test: | ||||
|   test-e2e: | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v2.3.4' | ||||
| @@ -82,12 +99,15 @@ jobs: | ||||
|       - name: 'Install' | ||||
|         run: 'npm install' | ||||
|  | ||||
|       - name: 'Test' | ||||
|         run: 'npm run test' | ||||
|       - name: 'Build' | ||||
|         run: 'npm run build' | ||||
|  | ||||
|       - name: 'End To End (e2e) Test' | ||||
|         run: 'npm run test:e2e' | ||||
|  | ||||
|   release: | ||||
|     if: github.ref == 'refs/heads/master' && github.event_name == 'push' | ||||
|     needs: [analyze, lint, build, test] | ||||
|     needs: [analyze, lint, test-unit, test-e2e, test-lighthouse] | ||||
|     runs-on: 'ubuntu-latest' | ||||
|     steps: | ||||
|       - uses: 'actions/checkout@v2.3.4' | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -14,6 +14,9 @@ dist | ||||
|  | ||||
| # testing | ||||
| coverage | ||||
| cypress/screenshots | ||||
| cypress/videos | ||||
| cypress/downloads | ||||
|  | ||||
| # PWA | ||||
| **/workbox-*.js | ||||
|   | ||||
| @@ -1,10 +0,0 @@ | ||||
| import { render } from '@testing-library/react' | ||||
|  | ||||
| import Error404 from 'pages/404' | ||||
|  | ||||
| describe('GET /404', () => { | ||||
|   it('should render', async () => { | ||||
|     const { getByText } = render(<Error404 />) | ||||
|     expect(getByText('404')).toBeInTheDocument() | ||||
|   }) | ||||
| }) | ||||
| @@ -1,10 +0,0 @@ | ||||
| import { render } from '@testing-library/react' | ||||
|  | ||||
| import Error500 from 'pages/500' | ||||
|  | ||||
| describe('GET /500', () => { | ||||
|   it('should render', async () => { | ||||
|     const { getByText } = render(<Error500 />) | ||||
|     expect(getByText('500')).toBeInTheDocument() | ||||
|   }) | ||||
| }) | ||||
| @@ -14,7 +14,12 @@ export const ErrorPage: React.FC<ErrorPageProps> = (props) => { | ||||
|     <> | ||||
|       <h1 className='my-6 font-semibold text-4xl'> | ||||
|         {t('errors:error')}{' '} | ||||
|         <span className='text-yellow dark:text-yellow-dark'>{statusCode}</span> | ||||
|         <span | ||||
|           className='text-yellow dark:text-yellow-dark' | ||||
|           data-cy='status-code' | ||||
|         > | ||||
|           {statusCode} | ||||
|         </span> | ||||
|       </h1> | ||||
|       <p className='text-center text-lg'> | ||||
|         {message}{' '} | ||||
|   | ||||
| @@ -15,7 +15,9 @@ export const LanguageFlag: React.FC<LanguageFlagProps> = (props) => { | ||||
|         src={`/images/languages/${language}.svg`} | ||||
|         alt={language} | ||||
|       /> | ||||
|       <p className='mx-2 text-base'>{language.toUpperCase()}</p> | ||||
|       <p data-cy='language-flag-text' className='mx-2 text-base'> | ||||
|         {language.toUpperCase()} | ||||
|       </p> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -33,12 +33,19 @@ export const Language: React.FC = () => { | ||||
|  | ||||
|   return ( | ||||
|     <div className='flex flex-col justify-center items-center cursor-pointer'> | ||||
|       <div className='flex items-center mr-5' onClick={handleHiddenMenu}> | ||||
|       <div | ||||
|         data-cy='language-click' | ||||
|         className='flex items-center mr-5' | ||||
|         onClick={handleHiddenMenu} | ||||
|       > | ||||
|         <LanguageFlag language={currentLanguage} /> | ||||
|         <Arrow /> | ||||
|       </div> | ||||
|       {!hiddenMenu && ( | ||||
|         <ul className='flex flex-col justify-center items-center absolute p-0 top-14 z-10 w-24 mt-3 mr-4 rounded-lg list-none shadow-light dark:shadow-dark bg-white dark:bg-black'> | ||||
|         <ul | ||||
|           data-cy='languages-list' | ||||
|           className='flex flex-col justify-center items-center absolute p-0 top-14 z-10 w-24 mt-3 mr-4 rounded-lg list-none shadow-light dark:shadow-dark bg-white dark:bg-black' | ||||
|         > | ||||
|           {i18n.locales.map((language, index) => { | ||||
|             if (language === currentLanguage) { | ||||
|               return null | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { useEffect, useState } from 'react' | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import { useTheme } from 'next-themes' | ||||
|  | ||||
| export const SwitchTheme: React.FC = () => { | ||||
| @@ -13,23 +13,29 @@ export const SwitchTheme: React.FC = () => { | ||||
|     return null | ||||
|   } | ||||
|  | ||||
|   const handleClick = (): void => { | ||||
|     setTheme(theme === 'dark' ? 'light' : 'dark') | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <div | ||||
|         className='toggle-button' | ||||
|         onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} | ||||
|         data-cy='switch-theme-click' | ||||
|         onClick={handleClick} | ||||
|       > | ||||
|         <div className='toggle-theme-button'> | ||||
|           <div className='toggle-track'> | ||||
|             <div className='toggle-track-check'> | ||||
|             <div data-cy='switch-theme-dark' className='toggle-track-check'> | ||||
|               <span className='toggle_Dark'>🌜</span> | ||||
|             </div> | ||||
|             <div className='toggle-track-x'> | ||||
|             <div data-cy='switch-theme-light' className='toggle-track-x'> | ||||
|               <span className='toggle_Light'>🌞</span> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div className='toggle-thumb' /> | ||||
|           <input | ||||
|             data-cy='switch-theme-input' | ||||
|             type='checkbox' | ||||
|             aria-label='Dark mode toggle' | ||||
|             className='toggle-screenreader-only' | ||||
|   | ||||
| @@ -3,10 +3,14 @@ import { render } from '@testing-library/react' | ||||
| import { Footer } from '../Footer' | ||||
|  | ||||
| describe('<Footer />', () => { | ||||
|   it('should render', async () => { | ||||
|   it('should render with appropriate link tag version', async () => { | ||||
|     const version = '1.0.0' | ||||
|     const { getByText } = render(<Footer version={version} />) | ||||
|     const versionLink = getByText(version) as HTMLAnchorElement | ||||
|     expect(getByText('Divlo')).toBeInTheDocument() | ||||
|     expect(getByText(version)).toBeInTheDocument() | ||||
|     expect(versionLink).toBeInTheDocument() | ||||
|     expect(versionLink.href).toEqual( | ||||
|       `https://github.com/Divlo/Divlo/releases/tag/v${version}` | ||||
|     ) | ||||
|   }) | ||||
| }) | ||||
|   | ||||
							
								
								
									
										8
									
								
								cypress.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								cypress.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "baseUrl": "http://localhost:3000", | ||||
|   "pluginsFile": false, | ||||
|   "supportFile": false, | ||||
|   "fixturesFolder": false, | ||||
|   "video": false, | ||||
|   "screenshotOnRunFailure": false | ||||
| } | ||||
							
								
								
									
										47
									
								
								cypress/integration/common/Header.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								cypress/integration/common/Header.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| describe('Common > Header', () => { | ||||
|   beforeEach(() => cy.visit('/')) | ||||
|  | ||||
|   describe('Switch theme color (dark/light)', () => { | ||||
|     it('should switch theme from `dark` (default) to `light`', () => { | ||||
|       cy.get('[data-cy=switch-theme-dark]').should('be.visible') | ||||
|       cy.get('[data-cy=switch-theme-light]').should('not.be.visible') | ||||
|       cy.get('body').should( | ||||
|         'not.have.css', | ||||
|         'background-color', | ||||
|         'rgb(255, 255, 255)' | ||||
|       ) | ||||
|  | ||||
|       cy.get('[data-cy=switch-theme-click]').click() | ||||
|  | ||||
|       cy.get('[data-cy=switch-theme-dark]').should('not.be.visible') | ||||
|       cy.get('[data-cy=switch-theme-light]').should('be.visible') | ||||
|       cy.get('body').should( | ||||
|         'have.css', | ||||
|         'background-color', | ||||
|         'rgb(255, 255, 255)' | ||||
|       ) | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   describe('Switch Language', () => { | ||||
|     it('should switch language from EN (default) to FR', () => { | ||||
|       cy.get('h1').contains('I am Divlo') | ||||
|       cy.get('[data-cy=language-flag-text]').contains('EN') | ||||
|       cy.get('[data-cy=languages-list]').should('not.exist') | ||||
|       cy.get('[data-cy=language-click]').click() | ||||
|       cy.get('[data-cy=languages-list]').should('exist') | ||||
|       cy.get('[data-cy=languages-list] > li:first-child').contains('FR').click() | ||||
|       cy.get('[data-cy=languages-list]').should('not.exist') | ||||
|       cy.get('[data-cy=language-flag-text]').contains('FR') | ||||
|       cy.get('h1').contains('Je suis Divlo') | ||||
|     }) | ||||
|  | ||||
|     it('should close the language list menu when clicking outside', () => { | ||||
|       cy.get('[data-cy=languages-list]').should('not.exist') | ||||
|       cy.get('[data-cy=language-click]').click() | ||||
|       cy.get('[data-cy=languages-list]').should('exist') | ||||
|       cy.get('h1').click() | ||||
|       cy.get('[data-cy=languages-list]').should('not.exist') | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										7
									
								
								cypress/integration/pages/404.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cypress/integration/pages/404.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| describe('Page /404', () => { | ||||
|   beforeEach(() => cy.visit('/404', { failOnStatusCode: false })) | ||||
|  | ||||
|   it('should display the statusCode of 404', () => { | ||||
|     cy.get('[data-cy=status-code]').contains('404') | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										7
									
								
								cypress/integration/pages/500.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cypress/integration/pages/500.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| describe('Page /500', () => { | ||||
|   beforeEach(() => cy.visit('/500', { failOnStatusCode: false })) | ||||
|  | ||||
|   it('should display the statusCode of 500', () => { | ||||
|     cy.get('[data-cy=status-code]').contains('500') | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										19
									
								
								cypress/integration/pages/index.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								cypress/integration/pages/index.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| describe('Page /', () => { | ||||
|   beforeEach(() => cy.visit('/')) | ||||
|  | ||||
|   it('should reveals the sections while scrolling except the about section', () => { | ||||
|     const sectionsReveals = [ | ||||
|       '#interests', | ||||
|       '#skills', | ||||
|       '#portfolio', | ||||
|       '#open-source' | ||||
|     ] | ||||
|     cy.get('#about').should('be.visible') | ||||
|     for (const section of sectionsReveals) { | ||||
|       cy.get(section) | ||||
|         .should('not.be.visible') | ||||
|         .scrollIntoView() | ||||
|         .should('be.visible') | ||||
|     } | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										9
									
								
								cypress/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cypress/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| { | ||||
|   "extends": "../tsconfig.json", | ||||
|   "compilerOptions": { | ||||
|     "noEmit": true, | ||||
|     "types": ["cypress"], | ||||
|     "isolatedModules": false | ||||
|   }, | ||||
|   "include": ["../node_modules/cypress", "./**/*.ts"] | ||||
| } | ||||
| @@ -4,13 +4,11 @@ | ||||
|     "^.+\\.(js|jsx|ts|tsx)$": "babel-jest" | ||||
|   }, | ||||
|   "moduleDirectories": ["node_modules", "./"], | ||||
|   "modulePathIgnorePatterns": ["<rootDir>/cypress"], | ||||
|   "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], | ||||
|   "testEnvironment": "jsdom", | ||||
|   "setupFilesAfterEnv": [ | ||||
|     "@testing-library/jest-dom/extend-expect", | ||||
|     "@testing-library/react" | ||||
|   ], | ||||
|   "collectCoverage": true, | ||||
|   "coverageDirectory": "./coverage", | ||||
|   "coverageReporters": ["text", "cobertura"] | ||||
|   ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										4049
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4049
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -21,8 +21,10 @@ | ||||
|     "lint:markdown": "markdownlint '**/*.md' --dot --ignore node_modules", | ||||
|     "lint:typescript": "eslint '**/*.{js,ts,jsx,tsx}'", | ||||
|     "lint:staged": "lint-staged", | ||||
|     "lighthouse": "lhci autorun", | ||||
|     "test": "jest", | ||||
|     "test:unit": "jest", | ||||
|     "test:lighthouse": "lhci autorun", | ||||
|     "test:e2e": "start-server-and-test 'start' 'http://localhost:3000' 'cypress run'", | ||||
|     "test:e2e:dev": "start-server-and-test 'dev' 'http://localhost:3000' 'cypress open'", | ||||
|     "release": "semantic-release", | ||||
|     "deploy": "vercel", | ||||
|     "postinstall": "husky install" | ||||
| @@ -34,7 +36,7 @@ | ||||
|     "@fortawesome/free-solid-svg-icons": "5.15.4", | ||||
|     "@fortawesome/react-fontawesome": "0.1.15", | ||||
|     "classnames": "2.3.1", | ||||
|     "html-react-parser": "1.2.7", | ||||
|     "html-react-parser": "1.2.8", | ||||
|     "next": "11.1.0", | ||||
|     "next-pwa": "5.2.24", | ||||
|     "next-themes": "0.0.15", | ||||
| @@ -53,13 +55,14 @@ | ||||
|     "@semantic-release/git": "9.0.0", | ||||
|     "@testing-library/jest-dom": "5.14.1", | ||||
|     "@testing-library/react": "12.0.0", | ||||
|     "@types/jest": "27.0.0", | ||||
|     "@types/node": "16.6.0", | ||||
|     "@types/jest": "27.0.1", | ||||
|     "@types/node": "16.6.1", | ||||
|     "@types/react": "17.0.17", | ||||
|     "@types/styled-jsx": "2.2.9", | ||||
|     "@typescript-eslint/eslint-plugin": "4.29.1", | ||||
|     "autoprefixer": "10.3.1", | ||||
|     "babel-jest": "27.0.6", | ||||
|     "cypress": "8.2.0", | ||||
|     "dockerfilelint": "1.8.0", | ||||
|     "editorconfig-checker": "4.0.2", | ||||
|     "eslint": "7.32.0", | ||||
| @@ -79,6 +82,7 @@ | ||||
|     "postcss": "8.3.6", | ||||
|     "prettier": "2.3.2", | ||||
|     "semantic-release": "17.4.4", | ||||
|     "start-server-and-test": "1.13.1", | ||||
|     "tailwindcss": "2.2.7", | ||||
|     "typescript": "4.3.5", | ||||
|     "vercel": "23.1.2" | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|     "removeComments": true, | ||||
|     "noEmit": true, | ||||
|     "strict": true, | ||||
|     "types": ["jest", "@testing-library/jest-dom", "@testing-library/react"], | ||||
|     "baseUrl": ".", | ||||
|     "esModuleInterop": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user