feat: add e2e test (#1486)

* add i18n

* add base e2e test

* add multiple test for e2e

* extract the funciton of write memo

* change test sturct

* deteled unused dir

* use fixture

* add fixture

* restruced the project

* feat: add workflow

* feat: change playwright test position

* feat: change playwright test position

* using yarn intead of npm

* change install method

* only enable sign in test

* adjust the order of test

* change report pos

* fix style of e2e workflow

* add review test

* unify locale

* randome write content

* change report pos

* reduce unused wait time

* reduce unused folder

* stash

* merge upstream locale

* change test name

* add test item

* change action name

* add lanuage setting

* add shotscreen

* change name of test

* fix the error of import dep

* fix the error of import dep

* fix the error of filename

* fix the format of workflow

* fix the name error of test case

* feat: change the describe of test case

* feat: remove unused test

* feat: change the fixtures name

* feat: remove unused config

* feat: change docker action

* feat: change the generate method

* feat: extrace screenshot

* feat: change extra path

* feat: change extra path

* feat: screenshot and upload

* feat: change upload filename

* feat: change login method

* feat: change e2e method

* feat: change e2e test

* feat: add wait for login

---------

Co-authored-by: CorrectRoadH <a778917369@gmail.comå>
This commit is contained in:
CorrectRoadH 2023-04-11 22:13:06 +08:00 committed by GitHub
parent b5c665cb7e
commit 81d4f01b7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 230 additions and 1 deletions

62
.github/workflows/e2e-test.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: "E2E Test"
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
name: Build and Run Memos With E2E Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build Docker image
id: docker_build
uses: docker/build-push-action@v3
with:
context: ./
file: ./Dockerfile
platforms: linux/amd64
push: false
tags: neosmemo/memos:e2e
labels: neosmemo/memos:e2e
- name: Run Docker container
run: docker run -d -p 5230:5230 neosmemo/memos:e2e
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
cache-dependency-path: ./web/yarn.lock
- name: Install dependencies
working-directory: web
run: yarn
- name: Install Playwright Browsers
working-directory: web
run: npx playwright install --with-deps
- name: Run Playwright tests
working-directory: web
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: web/playwright-report/
retention-days: 30
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-screenshot
path: web/playwright-screenshot/
retention-days: 30
- name: Stop Docker container
run: docker stop $(docker ps -q)

4
web/.gitignore vendored
View File

@ -5,3 +5,7 @@ dist-ssr
*.local *.local
.yarn/* .yarn/*
/test-results/
/playwright-report/
/playwright/.cache/
/playwright-screenshot/

View File

@ -0,0 +1,13 @@
import { test } from "@playwright/test";
import { signUp } from "./utils";
test.use({
locale: "en-US",
timezoneId: "Europe/Berlin",
});
test.describe("Sign up a host account", async () => {
test("Sign Up", async ({ page }) => {
await signUp(page, "admin", "admin");
});
});

View File

@ -0,0 +1,37 @@
import { test, expect } from "@playwright/test";
import { review, login, writeMemo } from "./utils";
import randomstring from "randomstring";
test.use({
locale: "en-US",
timezoneId: "Europe/Berlin",
});
test.beforeEach(async ({ page }) => {
await login(page, "admin", "admin");
});
test.describe("Write some memos", async () => {
test("Write memos", async ({ page }) => {
const content = `${randomstring.generate()} from Write memos`;
await writeMemo(page, content);
await expect(page.getByText(content)).toBeVisible();
});
test("Write memos with Tag", async ({ page }) => {
const tag = randomstring.generate(5);
const content = `#${tag} ${randomstring.generate()} from Write memos with Tag`;
await writeMemo(page, content);
// 1.memo contentg 2.tags list of memos editor 3.tags list
await expect(page.getByText(tag)).toHaveCount(3);
});
});
test.describe("Daily Review", async () => {
test("Daily Review", async ({ page }) => {
const content = randomstring.generate();
await writeMemo(page, content);
await review(page);
await expect(page.getByText(content)).toBeVisible();
});
});

View File

@ -0,0 +1,2 @@
const baseHost = "http://localhost:5230";
export { baseHost };

52
web/e2e-tests/utils.ts Normal file
View File

@ -0,0 +1,52 @@
import { expect, Page } from "@playwright/test";
import locale from "../src/locales/en.json";
import { baseHost } from "./fixtures";
async function screenshot(page: Page, name: string) {
await page.screenshot({ path: `playwright-screenshot/${name}.png`, fullPage: true });
}
async function writeMemo(page: Page, content: string) {
await expect(page.getByRole("button", { name: locale.editor.save })).toBeDisabled();
await page.getByPlaceholder("Any thoughts...").fill(content);
await expect(page.getByRole("button", { name: locale.editor.save })).toBeEnabled();
await page.getByRole("button", { name: locale.editor.save }).click();
}
async function login(page: Page, username: string, password: string) {
page.goto(`${baseHost}/`);
await screenshot(page, "explore-page");
await page.waitForURL("**/explore");
await screenshot(page, "explore-page-after-wait");
await page.getByRole("link", { name: locale.common["sign-in"] }).click();
await screenshot(page, "auth-page");
await page.waitForURL("**/auth");
await page.locator('input[type="text"]').click();
await page.locator('input[type="text"]').fill(username);
await page.locator('input[type="password"]').click();
await page.locator('input[type="password"]').fill(password);
await page.getByRole("button", { name: locale.common["sign-in"] }).click();
await page.waitForTimeout(1000);
await screenshot(page, "home-page-login-success");
}
async function signUp(page: Page, username: string, password: string) {
await page.goto(`${baseHost}/`);
await page.waitForURL("**/auth");
await screenshot(page, "sign-up-page");
await page.locator('input[type="text"]').click();
await page.locator('input[type="text"]').fill(username);
await page.locator('input[type="password"]').click();
await page.locator('input[type="password"]').fill(password);
await page.getByRole("button", { name: locale.auth["signup-as-host"] }).click();
await page.waitForTimeout(1000);
await screenshot(page, "home-page-sign-up-success");
}
async function review(page: Page) {
await page.goto(`${baseHost}/`);
await page.getByRole("link", { name: locale["daily-review"]["title"] }).click();
await screenshot(page, "review");
}
export { writeMemo, login, signUp, review };

View File

@ -22,6 +22,7 @@
"lucide-react": "^0.105.0", "lucide-react": "^0.105.0",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"qs": "^6.11.0", "qs": "^6.11.0",
"randomstring": "^1.2.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hot-toast": "^2.4.0", "react-hot-toast": "^2.4.0",
@ -34,6 +35,7 @@
"zustand": "^4.3.6" "zustand": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.32.2",
"@tailwindcss/line-clamp": "^0.4.2", "@tailwindcss/line-clamp": "^0.4.2",
"@types/lodash-es": "^4.17.5", "@types/lodash-es": "^4.17.5",
"@types/node": "^18.0.3", "@types/node": "^18.0.3",

19
web/playwright.config.ts Normal file
View File

@ -0,0 +1,19 @@
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e-tests",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: [["html", { outputFolder: "playwright-report", open: "never" }]],
use: {
trace: "on-first-retry",
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
});

View File

@ -487,6 +487,16 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@playwright/test@^1.32.2":
version "1.32.2"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.32.2.tgz#3cbd76b3f94d0f7f50bf054dbd02e504e85e3865"
integrity sha512-nhaTSDpEdTTttdkDE8Z6K3icuG1DVRxrl98Qq0Lfc63SS9a2sjc9+x8ezysh7MzCKz6Y+nArml3/mmt+gqRmQQ==
dependencies:
"@types/node" "*"
playwright-core "1.32.2"
optionalDependencies:
fsevents "2.3.2"
"@popperjs/core@^2.11.6": "@popperjs/core@^2.11.6":
version "2.11.6" version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
@ -787,6 +797,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa"
integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==
"@types/node@*":
version "18.15.11"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
"@types/node@^18.0.3": "@types/node@^18.0.3":
version "18.11.18" version "18.11.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
@ -1032,6 +1047,11 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
array-uniq@1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/array-uniq/-/array-uniq-1.0.2.tgz#5fcc373920775723cfd64d65c64bef53bf9eba6d"
integrity sha512-GVYjmpL05al4dNlKJm53mKE4w9OOLiuVHWorsIA3YVz+Hu0hcn6PtE3Ydl0EqU7v+7ABC4mjjWsnLUxbpno+CA==
array.prototype.flatmap@^1.3.1: array.prototype.flatmap@^1.3.1:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183"
@ -1785,7 +1805,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2: fsevents@2.3.2, fsevents@~2.3.2:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@ -2595,6 +2615,11 @@ pify@^4.0.1:
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
playwright-core@1.32.2:
version "1.32.2"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.2.tgz#608810c3c4486fb86a224732ac0d3560a96ded8b"
integrity sha512-zD7aonO+07kOTthsrCR3YCVnDcqSHIJpdFUtZEMOb6//1Rc7/6mZDRdw+nlzcQiQltOOsiqI3rrSyn/SlyjnJQ==
postcss-import@^14.1.0: postcss-import@^14.1.0:
version "14.1.0" version "14.1.0"
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0"
@ -2706,6 +2731,19 @@ quick-lru@^5.1.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
randombytes@2.0.3:
version "2.0.3"
resolved "https://registry.npmmirror.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"
integrity sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg==
randomstring@^1.2.3:
version "1.2.3"
resolved "https://registry.npmmirror.com/randomstring/-/randomstring-1.2.3.tgz#49d2bc34ff6bc2bd0f6bb8e7d876e1d4433564c8"
integrity sha512-3dEFySepTzp2CvH6W/ASYGguPPveBuz5MpZ7MuoUkoVehmyNl9+F9c9GFVrz2QPbM9NXTIHGcmJDY/3j4677kQ==
dependencies:
array-uniq "1.0.2"
randombytes "2.0.3"
react-dom@^18.2.0: react-dom@^18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"