From 42138d1d723c2906f23636c17951f8bc0b236808 Mon Sep 17 00:00:00 2001 From: Ikhun Um Date: Wed, 31 Jul 2024 10:22:50 +0900 Subject: [PATCH] Set up e2e with Playwright (#1001) Motivation: E2E tests are important to verify the correctness of behavior. Mocking tests are fast and easy to inject inputs but they can't check changes in other sides. It will not be easy to test all functions with e2e tests. So I am going to write e2e tests for core features of the webapp. Modifications: - Set up Playwright for e2e tests of the webapp --- webapp/.gitignore | 4 ++ webapp/build.gradle | 6 ++ webapp/e2e/landing.spec.ts | 24 +++++++ webapp/jest.config.js | 1 + webapp/package-lock.json | 64 +++++++++++++++++ webapp/package.json | 4 ++ webapp/playwright.config.ts | 71 +++++++++++++++++++ .../common/components/Breadcrumbs.test.tsx | 0 .../dogma/feature/file/FileList.test.tsx | 0 .../feature/history/HistoryList.test.tsx | 0 .../dogma/feature/repo/RepoList.test.tsx | 0 .../feature/repo/RepoPermissionList.test.tsx | 0 .../{__tests__ => tests}/pages/index.test.tsx | 0 13 files changed, 174 insertions(+) create mode 100644 webapp/.gitignore create mode 100644 webapp/e2e/landing.spec.ts create mode 100644 webapp/playwright.config.ts rename webapp/{__tests__ => tests}/dogma/common/components/Breadcrumbs.test.tsx (100%) rename webapp/{__tests__ => tests}/dogma/feature/file/FileList.test.tsx (100%) rename webapp/{__tests__ => tests}/dogma/feature/history/HistoryList.test.tsx (100%) rename webapp/{__tests__ => tests}/dogma/feature/repo/RepoList.test.tsx (100%) rename webapp/{__tests__ => tests}/dogma/feature/repo/RepoPermissionList.test.tsx (100%) rename webapp/{__tests__ => tests}/pages/index.test.tsx (100%) diff --git a/webapp/.gitignore b/webapp/.gitignore new file mode 100644 index 0000000000..a71674ace9 --- /dev/null +++ b/webapp/.gitignore @@ -0,0 +1,4 @@ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/webapp/build.gradle b/webapp/build.gradle index e833bf1c6b..8d3ef32b67 100644 --- a/webapp/build.gradle +++ b/webapp/build.gradle @@ -18,9 +18,15 @@ dependencies { testImplementation libs.shiro.core } +task installPlayWright(type: NpmTask) { + dependsOn tasks.npmInstall + args = ['run', 'playwright:install'] +} + // Set `NEXT_ENV=development` to `.env.local` file to produce source maps for the minified JavaScript files. task buildWeb(type: NpmTask) { dependsOn tasks.npmInstall + dependsOn installPlayWright args = ['run', 'build'] inputs.dir('src') inputs.file('package.json') diff --git a/webapp/e2e/landing.spec.ts b/webapp/e2e/landing.spec.ts new file mode 100644 index 0000000000..5809e7aa80 --- /dev/null +++ b/webapp/e2e/landing.spec.ts @@ -0,0 +1,24 @@ +import { test, expect } from '@playwright/test'; + +test.beforeEach('Login', async ({ page }) => { + await page.goto('/'); + + await expect(page.getByText(/Login/)).toBeVisible(); + await page.getByPlaceholder('ID').fill('foo'); + await page.getByPlaceholder('Password').fill('bar'); + await page.getByRole('button', { name: 'Login' }).click(); +}); + +test('welcome message', async ({ page }) => { + await expect(page.getByRole('heading', { name: 'Welcome to Central Dogma!' })).toBeVisible(); +}); + +test('search project', async ({ page }) => { + await page.goto('/'); + + await expect(page.getByText('Search project ...')).toBeVisible(); + await expect(page.getByRole('combobox')).toBeVisible(); + await page.locator('#project-select').click(); + await expect(page.getByRole('option', { name: 'dogma' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'foo' })).toBeVisible(); +}); diff --git a/webapp/jest.config.js b/webapp/jest.config.js index 8196aa635b..039873f827 100644 --- a/webapp/jest.config.js +++ b/webapp/jest.config.js @@ -8,6 +8,7 @@ const createJestConfig = nextJest({ // Add any custom config to be passed to Jest const customJestConfig = { setupFilesAfterEnv: ['/jest.setup.js'], + modulePathIgnorePatterns: ['/build/', '/out/', '/e2e/'], testEnvironment: 'jest-environment-jsdom', moduleDirectories: ['node_modules', 'src'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 2527c8ccac..17691e7f71 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -29,6 +29,7 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@next/bundle-analyzer": "^14.2.4", + "@playwright/test": "^1.45.1", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", @@ -3664,6 +3665,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.1.tgz", + "integrity": "sha512-Wo1bWTzQvGA7LyKGIZc8nFSTFf2TkthGIFBR+QVNilvwouGzFd4PYukZe3rvf5PSqjHi1+1NyKSDZKcQWETzaA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.45.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.25", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", @@ -12366,6 +12383,53 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.1.tgz", + "integrity": "sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.45.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.1.tgz", + "integrity": "sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index ab22051b3e..e905bbbd9e 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -5,7 +5,9 @@ "scripts": { "test": "jest --watch", "test:ci": "jest --ci", + "test:e2e": "npx playwright test", "develop": "NEXT_PUBLIC_HOST=\"http://127.0.0.1:36462\" NEXT_ENV=\"development\" next dev", + "backend": "../gradlew runTestShiroServer -PnoWeb", "mock": "NEXT_ENV=\"development\" next dev", "build": "next build", "start": "next start", @@ -14,6 +16,7 @@ "format": "prettier --check .", "format:fix": "prettier --write .", "pretest": "npm run lint", + "playwright:install": "npx playwright install --with-deps", "analyze": "ANALYZE=true next build" }, "dependencies": { @@ -39,6 +42,7 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@next/bundle-analyzer": "^14.2.4", + "@playwright/test": "^1.45.1", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", diff --git a/webapp/playwright.config.ts b/webapp/playwright.config.ts new file mode 100644 index 0000000000..be2350559b --- /dev/null +++ b/webapp/playwright.config.ts @@ -0,0 +1,71 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Timeout for each test. 60 seconds */ + timeout: 60 * 1000, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + // Uncomment the following to run tests in other browsers. + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + // + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + ], + + // Run your local dev server before starting the tests + webServer: [ + { + command: 'npm run backend', + url: 'http://127.0.0.1:36462/monitor/l7check', + reuseExistingServer: !process.env.CI, + stdout: 'ignore', + stderr: 'pipe', + }, + { + command: 'npm run develop', + url: 'http://127.0.0.1:3000', + reuseExistingServer: !process.env.CI, + stdout: 'ignore', + stderr: 'pipe', + }, + ], +}); diff --git a/webapp/__tests__/dogma/common/components/Breadcrumbs.test.tsx b/webapp/tests/dogma/common/components/Breadcrumbs.test.tsx similarity index 100% rename from webapp/__tests__/dogma/common/components/Breadcrumbs.test.tsx rename to webapp/tests/dogma/common/components/Breadcrumbs.test.tsx diff --git a/webapp/__tests__/dogma/feature/file/FileList.test.tsx b/webapp/tests/dogma/feature/file/FileList.test.tsx similarity index 100% rename from webapp/__tests__/dogma/feature/file/FileList.test.tsx rename to webapp/tests/dogma/feature/file/FileList.test.tsx diff --git a/webapp/__tests__/dogma/feature/history/HistoryList.test.tsx b/webapp/tests/dogma/feature/history/HistoryList.test.tsx similarity index 100% rename from webapp/__tests__/dogma/feature/history/HistoryList.test.tsx rename to webapp/tests/dogma/feature/history/HistoryList.test.tsx diff --git a/webapp/__tests__/dogma/feature/repo/RepoList.test.tsx b/webapp/tests/dogma/feature/repo/RepoList.test.tsx similarity index 100% rename from webapp/__tests__/dogma/feature/repo/RepoList.test.tsx rename to webapp/tests/dogma/feature/repo/RepoList.test.tsx diff --git a/webapp/__tests__/dogma/feature/repo/RepoPermissionList.test.tsx b/webapp/tests/dogma/feature/repo/RepoPermissionList.test.tsx similarity index 100% rename from webapp/__tests__/dogma/feature/repo/RepoPermissionList.test.tsx rename to webapp/tests/dogma/feature/repo/RepoPermissionList.test.tsx diff --git a/webapp/__tests__/pages/index.test.tsx b/webapp/tests/pages/index.test.tsx similarity index 100% rename from webapp/__tests__/pages/index.test.tsx rename to webapp/tests/pages/index.test.tsx