From e42ddc5fe32e2fdac4b2ac3e1efbaa7fdb8b9d10 Mon Sep 17 00:00:00 2001 From: Lucas Fernandez Date: Tue, 5 Sep 2023 01:30:08 +0200 Subject: [PATCH 1/3] Add e2e testing --- ui/.gitignore | 4 ++ ui/package-lock.json | 49 +++++++++++++++++++ ui/package.json | 4 ++ ui/playwright.config.ts | 30 ++++++++++++ ui/src/__tests__/e2e/UserFlow.spec.ts | 38 ++++++++++++++ ui/src/components/cards/AboutMeCard.tsx | 2 +- .../layout/__tests__/HeaderTest.test.tsx | 2 +- ui/src/components/layout/header.tsx | 3 +- ui/src/locales/en-us.json | 2 +- ui/vite.config.ts | 20 +++++--- 10 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 ui/playwright.config.ts create mode 100644 ui/src/__tests__/e2e/UserFlow.spec.ts diff --git a/ui/.gitignore b/ui/.gitignore index 800f3a8..38ee3f1 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -22,3 +22,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +# playwright +/playwright-report +/test-results \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 04aaed1..a6817b8 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -21,6 +21,7 @@ "styled-components": "^5.2.1" }, "devDependencies": { + "@playwright/test": "^1.37.1", "@storybook/addon-essentials": "^7.1.1", "@storybook/addon-interactions": "^7.1.1", "@storybook/addon-links": "^7.1.1", @@ -49,6 +50,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "jsdom": "^21.1.1", + "playwright": "^1.37.1", "prettier": "^2.8.4", "prop-types": "^15.8.1", "storybook": "^7.1.1", @@ -3165,6 +3167,25 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz", + "integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.37.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, "node_modules/@remix-run/router": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.3.tgz", @@ -12679,6 +12700,34 @@ "pathe": "^1.1.0" } }, + "node_modules/playwright": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.37.1.tgz", + "integrity": "sha512-bgUXRrQKhT48zHdxDYQTpf//0xDfDd5hLeEhjuSw8rXEGoT9YeElpfvs/izonTNY21IQZ7d3s22jLxYaAnubbQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.37.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/playwright-core": { + "version": "1.37.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", + "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/polished": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", diff --git a/ui/package.json b/ui/package.json index d790ca7..7be9c59 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,6 +8,8 @@ "docker": "vite --host", "preview": "vite preview", "test": "vitest", + "test:e2e": "playwright test ./src/__tests__/e2e", + "test:e2e:debug": "playwright test ./src/__tests__/e2e --debug", "coverage": "vitest run --coverage", "lint": "eslint src", "lint:fix": "eslint src --fix", @@ -28,6 +30,7 @@ "styled-components": "^5.2.1" }, "devDependencies": { + "@playwright/test": "^1.37.1", "@storybook/addon-essentials": "^7.1.1", "@storybook/addon-interactions": "^7.1.1", "@storybook/addon-links": "^7.1.1", @@ -56,6 +59,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "jsdom": "^21.1.1", + "playwright": "^1.37.1", "prettier": "^2.8.4", "prop-types": "^15.8.1", "storybook": "^7.1.1", diff --git a/ui/playwright.config.ts b/ui/playwright.config.ts new file mode 100644 index 0000000..abf03db --- /dev/null +++ b/ui/playwright.config.ts @@ -0,0 +1,30 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + timeout: 5000, + testDir: 'src/__tests__', + + reporter: 'html', + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + } + ], + webServer: [ + { + command: 'cd ../api && npm run dev', + port: 4000, + timeout: 120 * 1000 + }, + { + command: 'npm run dev', + port: 5173, + timeout: 120 * 1000 + } + ], + use: { + baseURL: 'http://localhost:5173' + } +}); diff --git a/ui/src/__tests__/e2e/UserFlow.spec.ts b/ui/src/__tests__/e2e/UserFlow.spec.ts new file mode 100644 index 0000000..feab232 --- /dev/null +++ b/ui/src/__tests__/e2e/UserFlow.spec.ts @@ -0,0 +1,38 @@ +import { test, expect, Page } from '@playwright/test'; + +const cleanTestProject = async (page: Page) => { + await page.goto('/'); +}; + +test.beforeEach(async ({ page }) => await cleanTestProject(page)); +test.afterEach(async ({ page }) => await cleanTestProject(page)); + +// Test Landing Page and Nav Bar +test('Landing page', async ({ page }) => { + // Check that the page title is correct + await expect(page).toHaveTitle('Portfolio'); + // Check that the page header is correct + await expect(page.locator('h1')).toHaveText('Welcome to my Portfolio'); + // Check that the page has a nav bar + await expect(page.getByTestId('navbar')).toBeVisible(); + // Check that the nav bar has the correct links + await expect(page.getByRole('link', { name: 'Home' })).toBeVisible(); + await expect(page.getByRole('link', { name: 'Dashboard' })).toBeVisible(); + await expect(page.getByRole('link', { name: 'Admin' })).toBeVisible(); +}); + +// Test Dashboard Page +test('Dashboard page', async ({ page }) => { + await page.goto('/dashboard'); + + // Check that component with test id aboutme-card is visible + await expect(page.getByTestId('aboutme-card')).toBeVisible(); +}); + +// Test Login Page +test('Admin page', async ({ page }) => { + await page.goto('/login'); + + // Check that the page header is correct + await expect(page.locator('h1')).toHaveText('Login Panel'); +}); diff --git a/ui/src/components/cards/AboutMeCard.tsx b/ui/src/components/cards/AboutMeCard.tsx index b2c7d44..eece848 100644 --- a/ui/src/components/cards/AboutMeCard.tsx +++ b/ui/src/components/cards/AboutMeCard.tsx @@ -21,7 +21,7 @@ const AboutMeCard = (props: AboutMeCardProps) => { const { t } = useTranslation(); return ( - + diff --git a/ui/src/components/layout/__tests__/HeaderTest.test.tsx b/ui/src/components/layout/__tests__/HeaderTest.test.tsx index 8f8eb8d..fac2fd0 100644 --- a/ui/src/components/layout/__tests__/HeaderTest.test.tsx +++ b/ui/src/components/layout/__tests__/HeaderTest.test.tsx @@ -25,7 +25,7 @@ vi.mock('react-i18next', () => ({ test('check exact three links', () => { render( -
+
{}} /> ); expect(screen.getAllByRole('link').length).toEqual(3); diff --git a/ui/src/components/layout/header.tsx b/ui/src/components/layout/header.tsx index f2ae006..1cd93d8 100644 --- a/ui/src/components/layout/header.tsx +++ b/ui/src/components/layout/header.tsx @@ -7,6 +7,7 @@ import { themes } from '../../styles/ColorStyles'; import { MenuButton } from '../elements/MenuButton'; import { User } from '../../model/user'; + export const home = { title: 'nav.home', link: '/' @@ -34,7 +35,7 @@ const Header = ({ user, logout }: HeaderProps) => { const [isVisible, toggle] = useToggle(false); return ( - + {t(home.title)} diff --git a/ui/src/locales/en-us.json b/ui/src/locales/en-us.json index e2e2eea..10bd317 100644 --- a/ui/src/locales/en-us.json +++ b/ui/src/locales/en-us.json @@ -4,7 +4,7 @@ "description": "Personal Portfolio Webpage" }, "app": { - "title": "Welcome to my Portfolio Webpage" + "title": "Welcome to my Portfolio Webpagesss" }, "landing": { "title": "Welcome to my Portfolio" diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 2a652a1..8441ab9 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,7 +1,7 @@ /// import { defineConfig, loadEnv } from 'vite'; import type { UserConfig as VitestUserConfigInterface } from 'vitest/config'; -import react from '@vitejs/plugin-react' +import react from '@vitejs/plugin-react'; const vitestConfig: VitestUserConfigInterface = { test: { @@ -10,11 +10,18 @@ const vitestConfig: VitestUserConfigInterface = { }, setupFiles: ['src/setupTest.ts'], environment: 'jsdom', + exclude: [ + '**/node_modules/**', + '**/dist/**', + '**/cypress/**', + '**/.{idea,git,cache,output,temp}/**', + './src/__tests__/e2e/**' + ] } }; export default ({ mode }) => { - process.env = {...process.env, ...loadEnv(mode, process.cwd())}; + process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }; return defineConfig({ test: vitestConfig.test, @@ -24,9 +31,8 @@ export default ({ mode }) => { // string shorthand: http://localhost:5173/auth -> http://localhost:4000/auth '/auth': process.env.VITE_PROXY_HOST, // string shorthand: http://localhost:5173/v1 -> http://localhost:4000/v1 - '/v1': process.env.VITE_PROXY_HOST, - }, - }, + '/v1': process.env.VITE_PROXY_HOST + } + } }); -} - +}; From eb921f6ad0d3140a0c2ee72f8749ecf69e8253e6 Mon Sep 17 00:00:00 2001 From: Lucas Fernandez Date: Tue, 5 Sep 2023 01:44:25 +0200 Subject: [PATCH 2/3] Fix lint issue --- ui/.eslintrc | 10 ++++++++-- ui/src/__tests__/e2e/UserFlow.spec.ts | 9 +-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ui/.eslintrc b/ui/.eslintrc index 1a7eabd..2211bce 100644 --- a/ui/.eslintrc +++ b/ui/.eslintrc @@ -16,7 +16,12 @@ "prettier", "plugin:prettier/recommended" ], - "plugins": ["react", "react-hooks", "@typescript-eslint", "import"], + "plugins": [ + "react", + "react-hooks", + "@typescript-eslint", + "import" + ], "env": { "browser": true, "es6": true @@ -39,7 +44,8 @@ "**/*.stories.*", "**/.storybook/**/*.*", "**/.jest-canvas-mock/**/*.*", - "**/*.d.ts" + "**/*.d.ts", + "**/*.spec.*" ], "peerDependencies": true } diff --git a/ui/src/__tests__/e2e/UserFlow.spec.ts b/ui/src/__tests__/e2e/UserFlow.spec.ts index feab232..9c0d5dd 100644 --- a/ui/src/__tests__/e2e/UserFlow.spec.ts +++ b/ui/src/__tests__/e2e/UserFlow.spec.ts @@ -1,11 +1,4 @@ -import { test, expect, Page } from '@playwright/test'; - -const cleanTestProject = async (page: Page) => { - await page.goto('/'); -}; - -test.beforeEach(async ({ page }) => await cleanTestProject(page)); -test.afterEach(async ({ page }) => await cleanTestProject(page)); +import { test, expect } from '@playwright/test'; // Test Landing Page and Nav Bar test('Landing page', async ({ page }) => { From 17579ff3866012b15bad621b5fb3f7ee92e661d4 Mon Sep 17 00:00:00 2001 From: Lucas Fernandez Date: Tue, 5 Sep 2023 01:47:11 +0200 Subject: [PATCH 3/3] Fix linting issue --- .github/workflows/ci.yml | 2 +- ui/src/components/layout/header.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0587998..090971a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,6 @@ jobs: node-version: '18.x' cache: 'npm' - run: npm install - - run: npm run test - run: npm run lint + - run: npm run test diff --git a/ui/src/components/layout/header.tsx b/ui/src/components/layout/header.tsx index 1cd93d8..f0e3d20 100644 --- a/ui/src/components/layout/header.tsx +++ b/ui/src/components/layout/header.tsx @@ -7,7 +7,6 @@ import { themes } from '../../styles/ColorStyles'; import { MenuButton } from '../elements/MenuButton'; import { User } from '../../model/user'; - export const home = { title: 'nav.home', link: '/'