CI: Add Playwright UI e2e tests + CI (#614)
Adds Playwright for UI tests. Basic Playwright test to login. Playwright Github Action. --------- Co-authored-by: sua yoo <sua@suayoo.com>
This commit is contained in:
parent
e8f88a797b
commit
b61592b5ed
49
.github/workflows/ui-tests-playwright.yml
vendored
Normal file
49
.github/workflows/ui-tests-playwright.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
name: Playwright Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DEV_PASSWORD: ${{ secrets.DEV_PASSWORD }}
|
||||
API_BASE_URL: ${{ secrets.API_BASE_URL }}
|
||||
working-directory: ./frontend
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: frontend/yarn.lock
|
||||
- name: Install dependencies
|
||||
working-directory: frontend
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn add playwright && yarn playwright install --with-deps
|
||||
working-directory: ./frontend
|
||||
- name: Create env file
|
||||
run: |
|
||||
cd frontend
|
||||
touch .env
|
||||
echo DEV_PASSWORD="${{ secrets.DEV_PASSWORD }}" >> .env
|
||||
echo API_BASE_URL=${{ secrets.API_BASE_URL }} >> .env
|
||||
cat .env
|
||||
- name: Build frontend
|
||||
run: cd frontend && yarn build
|
||||
id: build-frontend
|
||||
- name: Run Playwright tests
|
||||
run: cd frontend && yarn playwright test
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: frontend/playwright-report/
|
||||
retention-days: 30
|
7
frontend/.gitignore
vendored
7
frontend/.gitignore
vendored
@ -19,8 +19,11 @@
|
||||
/dist/
|
||||
|
||||
# dotenv
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env
|
||||
.env.*
|
||||
|
||||
storybook-static
|
||||
custom-elements.json
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
39
frontend/config/dev-server.js
Normal file
39
frontend/config/dev-server.js
Normal file
@ -0,0 +1,39 @@
|
||||
const path = require("path");
|
||||
require(path.resolve(process.cwd(), "./webpack.config.js"));
|
||||
|
||||
// for testing: for prod, the Dockerfile should have the official prod version used
|
||||
const RWP_BASE_URL = process.env.RWP_BASE_URL || "https://replayweb.page/";
|
||||
|
||||
if (!process.env.API_BASE_URL) {
|
||||
throw new Error(
|
||||
"To run a dev frontend server, please set the API_BASE_URL pointing to your backend api server in '.env.local'"
|
||||
);
|
||||
}
|
||||
|
||||
const devBackendUrl = new URL(process.env.API_BASE_URL);
|
||||
|
||||
module.exports = {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: devBackendUrl.href,
|
||||
headers: {
|
||||
Host: devBackendUrl.host,
|
||||
},
|
||||
ws: true,
|
||||
},
|
||||
|
||||
"/data": {
|
||||
target: devBackendUrl.href,
|
||||
headers: {
|
||||
Host: devBackendUrl.host,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Serve replay service worker file
|
||||
onBeforeSetupMiddleware: (server) => {
|
||||
server.app.get("/replay/sw.js", (req, res) => {
|
||||
res.set("Content-Type", "application/javascript");
|
||||
res.send(`importScripts("${RWP_BASE_URL}sw.js")`);
|
||||
});
|
||||
},
|
||||
};
|
@ -33,6 +33,7 @@
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"build-dev": "webpack --mode development",
|
||||
"start": "webpack serve --mode=development --config webpack.dev.js",
|
||||
"serve": "node scripts/serve.js",
|
||||
"lint": "eslint --fix \"src/**/*.{ts,js}\"",
|
||||
"format": "prettier --write \"src/**/*.{ts,js,html,css,json}\"",
|
||||
"localize:prepare": "yarn localize:extract && yarn localize:build",
|
||||
@ -43,6 +44,7 @@
|
||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||
"@lit/localize-tools": "^0.6.5",
|
||||
"@open-wc/testing": "^3.1.7",
|
||||
"@playwright/test": "^1.31.2",
|
||||
"@rollup/plugin-commonjs": "^18.0.0",
|
||||
"@types/color": "^3.0.2",
|
||||
"@types/lodash": "^4.14.178",
|
||||
@ -55,6 +57,7 @@
|
||||
"@web/test-runner": "^0.13.22",
|
||||
"@web/test-runner-playwright": "^0.8.8",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"chromium": "^3.0.3",
|
||||
"copy-webpack-plugin": "^9.1.0",
|
||||
"css-loader": "^6.3.0",
|
||||
"del-cli": "^4.0.1",
|
||||
|
91
frontend/playwright.config.ts
Normal file
91
frontend/playwright.config.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000,
|
||||
},
|
||||
/* 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: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://localhost: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"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: "yarn serve",
|
||||
port: 9871,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
17
frontend/scripts/serve.js
Normal file
17
frontend/scripts/serve.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Serve app locally without building with webpack, e.g. for e2e
|
||||
const express = require("express");
|
||||
const { createProxyMiddleware } = require("http-proxy-middleware");
|
||||
const connectHistoryApiFallback = require("connect-history-api-fallback");
|
||||
const devServerConfig = require("../config/dev-server.js");
|
||||
|
||||
const app = express();
|
||||
|
||||
devServerConfig.onBeforeSetupMiddleware({ app });
|
||||
|
||||
app.use("/", express.static("dist"));
|
||||
Object.keys(devServerConfig.proxy).forEach((path) => {
|
||||
app.use(path, createProxyMiddleware(devServerConfig.proxy[path]));
|
||||
});
|
||||
app.use(connectHistoryApiFallback());
|
||||
|
||||
app.listen(9871);
|
28
frontend/tests/login.spec.ts
Normal file
28
frontend/tests/login.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { chromium } from 'playwright';
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('test', async ({ baseURL }) => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
||||
try {
|
||||
await page.goto(baseURL!);
|
||||
await page.waitForLoadState('load');
|
||||
await page.waitForSelector('input[name="username"]');
|
||||
await page.click('input[name="username"]');
|
||||
await page.fill('input[name="username"]', 'dev@webrecorder.net');
|
||||
await page.click('input[name="password"]');
|
||||
const devPassword = process.env.DEV_PASSWORD;
|
||||
if (!devPassword) {
|
||||
throw new Error('DEV_PASSWORD environment variable is not defined or null.');
|
||||
}
|
||||
await page.fill('input[name="password"]', devPassword);
|
||||
await page.click('a:has-text("Log In")');
|
||||
|
||||
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
});
|
||||
|
@ -2,17 +2,8 @@ const path = require("path");
|
||||
const { merge } = require("webpack-merge");
|
||||
|
||||
const [main, vnc] = require("./webpack.config.js");
|
||||
const devServerConfig = require("./config/dev-server.js");
|
||||
|
||||
// for testing: for prod, the Dockerfile should have the official prod version used
|
||||
const RWP_BASE_URL = process.env.RWP_BASE_URL || "https://replayweb.page/";
|
||||
|
||||
if (!process.env.API_BASE_URL) {
|
||||
throw new Error(
|
||||
"To run a dev frontend server, please set the API_BASE_URL pointing to your backend api server in '.env.local'"
|
||||
);
|
||||
}
|
||||
|
||||
const devBackendUrl = new URL(process.env.API_BASE_URL);
|
||||
const shoelaceAssetsSrcPath = path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||
@ -38,29 +29,8 @@ module.exports = [
|
||||
},
|
||||
],
|
||||
historyApiFallback: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: devBackendUrl.href,
|
||||
headers: {
|
||||
Host: devBackendUrl.host,
|
||||
},
|
||||
ws: true,
|
||||
},
|
||||
|
||||
"/data": {
|
||||
target: devBackendUrl.href,
|
||||
headers: {
|
||||
Host: devBackendUrl.host,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Serve replay service worker file
|
||||
onBeforeSetupMiddleware: (server) => {
|
||||
server.app.get("/replay/sw.js", (req, res) => {
|
||||
res.set("Content-Type", "application/javascript");
|
||||
res.send(`importScripts("${RWP_BASE_URL}sw.js")`);
|
||||
});
|
||||
},
|
||||
proxy: devServerConfig.proxy,
|
||||
onBeforeSetupMiddleware: devServerConfig.onBeforeSetupMiddleware,
|
||||
port: 9870,
|
||||
},
|
||||
}),
|
||||
|
4525
frontend/yarn.lock
4525
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user