new
This commit is contained in:
@@ -60,6 +60,7 @@ cpu.out
|
||||
/tests/e2e/reports
|
||||
/tests/e2e/test-artifacts
|
||||
/tests/e2e/test-snapshots
|
||||
/tests/e2e/test-data
|
||||
/tests/*.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
@@ -69,6 +70,8 @@ cpu.out
|
||||
/public/assets/css
|
||||
/public/assets/fonts
|
||||
/public/assets/img/avatar
|
||||
/reports/
|
||||
/test-data/
|
||||
/vendor
|
||||
/VERSION
|
||||
/.air
|
||||
|
||||
6
.env.e2e
6
.env.e2e
@@ -1,6 +0,0 @@
|
||||
export GITEA_URL=""
|
||||
|
||||
export TEST_USER=""
|
||||
export TEST_PASS=""
|
||||
export TEST_ADMIN_USER_ID="1"
|
||||
|
||||
3
.gitignore
repo.diff.vendored
3
.gitignore
repo.diff.vendored
@@ -69,11 +69,14 @@ cpu.out
|
||||
/tests/e2e/gitea-e2e-*
|
||||
/tests/e2e/indexers-*
|
||||
/tests/e2e/reports
|
||||
/tests/e2e/test-data
|
||||
/tests/e2e/test-artifacts
|
||||
/tests/e2e/test-snapshots
|
||||
/tests/*.ini
|
||||
/tests/**/*.git/**/*.sample
|
||||
/node_modules
|
||||
/test-data
|
||||
/reports
|
||||
/.venv
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
|
||||
5
Makefile
5
Makefile
@@ -925,6 +925,11 @@ docker:
|
||||
|
||||
docker build --disable-content-trust=false -t $(DOCKER_REF) .
|
||||
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
|
||||
.PHONY: e2e-test
|
||||
e2e-test:
|
||||
@echo "正在启动E2E-TEST..."
|
||||
@./run-e2e-tests.sh
|
||||
|
||||
|
||||
# This endif closes the if at the top of the file
|
||||
endif
|
||||
|
||||
50
docker-compose.test.yml
Normal file
50
docker-compose.test.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# 服务一: DevStar
|
||||
devstar:
|
||||
# 我们不再拉取镜像
|
||||
# image: mengning997/devstar-studio:latest
|
||||
# pull_policy: always
|
||||
# 我们告诉 Compose 在本地构建
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.devstar #
|
||||
|
||||
image: devstar-e2e-test:latest #
|
||||
pull_policy: never
|
||||
ports:
|
||||
- "80:3000"
|
||||
- "2222:2222"
|
||||
volumes:
|
||||
# 挂载 Docker Socket,允许 DevStar 创建 Devcontainer
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# 挂载数据卷,使用相对路径,保证测试环境可移植
|
||||
- ./tests/e2e/test-data/devstar_data:/var/lib/gitea
|
||||
- ./tests/e2e/test-data/devstar_data:/etc/gitea
|
||||
# 健康检查。test-runner 会等待这个检查通过
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
# 服务二: Playwright
|
||||
test-runner:
|
||||
# 从 'tests/' 目录下的 Dockerfile 构建
|
||||
build:
|
||||
context: ./tests/e2e
|
||||
# 等待 devstar 的 "healthcheck" 通过后,才启动
|
||||
depends_on:
|
||||
devstar:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# 将 DevStar 的 URL 传给 Playwright
|
||||
- DEVSTAR_URL=http://devstar:3000
|
||||
volumes:
|
||||
# 也挂载 Docker Socket
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# 将测试报告写回到宿主机的 ./reports 目录
|
||||
- ./tests/e2e/reports:/app/playwright-report
|
||||
# 覆盖默认命令,强制运行测试并生成我们想要的报告
|
||||
command: >
|
||||
npx playwright test
|
||||
56
run-e2e-tests.sh
Executable file
56
run-e2e-tests.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# 这是一个“一键运行”E2E 测试的脚本
|
||||
# 它会处理所有清理、权限、拉取和执行工作
|
||||
# 任何命令失败立即退出
|
||||
set -e
|
||||
|
||||
echo "===== [1/5] 清理旧的测试环境... ====="
|
||||
# 彻底销毁旧的 compose 环境,-v 会删除关联的数据卷
|
||||
docker compose -f docker-compose.test.yml down -v --remove-orphans
|
||||
docker image prune -f
|
||||
docker builder prune -f
|
||||
# 清理并重建报告和数据目录
|
||||
sudo rm -rf ./tests/e2e/reports ./tests/e2e/test-data
|
||||
mkdir -p ./tests/e2e/reports/html ./tests/e2e/test-data/devstar_data
|
||||
echo "清理完成。"
|
||||
echo ""
|
||||
|
||||
echo "===== [2/5] 设置权限... ====="
|
||||
# 容器内的用户(通常 UID 1000)需要写入数据目录
|
||||
chmod -R 777 ./tests/e2e/test-data/devstar_data
|
||||
|
||||
# 【关键】允许容器访问宿主机的 Docker Socket
|
||||
sudo chmod 666 /var/run/docker.sock
|
||||
echo "权限设置完成。"
|
||||
echo ""
|
||||
|
||||
echo "===== [3/5] 构建/拉取依赖镜像... ====="
|
||||
# 根据 Dockerfile 里的注释 ,我们必须先在本地构建这两个“地基”
|
||||
echo "正在构建 dev-container 基础镜像..."
|
||||
docker build -t devstar.cn/devstar/devstar-dev-container:latest -f docker/Dockerfile.devContainer .
|
||||
|
||||
echo "正在构建 runtime-container 基础镜像..."
|
||||
docker build -t devstar.cn/devstar/devstar-runtime-container:latest -f docker/Dockerfile.runtimeContainer .
|
||||
|
||||
echo "===== [4/5] 启动并运行测试... ====="
|
||||
# --build: 确保 test-runner 镜像是最新的
|
||||
# --abort-on-container-exit: 如果 devstar 挂了, 测试立即停止
|
||||
# --exit-code-from test-runner: 运行结束后,将 test-runner 的退出码(0=成功, 1=失败)作为本命令的退出码
|
||||
docker compose -f docker-compose.test.yml up \
|
||||
--build \
|
||||
--abort-on-container-exit \
|
||||
--exit-code-from test-runner
|
||||
|
||||
# 捕获 test-runner 的退出码
|
||||
EXIT_CODE=$?
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
echo "===== [5/5] 测试运行完成 ====="
|
||||
echo "HTML 报告已生成在: ./reports/html"
|
||||
ls -l ./reports/html
|
||||
echo ""
|
||||
|
||||
# 以 test-runner 的退出码退出
|
||||
# 这将告诉 CI (或你自己) 测试是成功还是失败
|
||||
exit $EXIT_CODE
|
||||
24
tests/e2e/Dockerfile
Normal file
24
tests/e2e/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# 这是 "test-runner" 服务的 Dockerfile
|
||||
# 它构建了一个包含所有浏览器和我们测试代码的镜像
|
||||
#
|
||||
|
||||
# 1. 使用微软官方的 Playwright 镜像
|
||||
# 它已经内置了所有浏览器 (Chromium, Firefox, WebKit) 和操作系统依赖
|
||||
FROM mcr.microsoft.com/playwright:v1.53.2-jammy
|
||||
|
||||
# 2. 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 3. 复制 "依赖清单" 文件
|
||||
COPY package*.json ./
|
||||
|
||||
# 4. 安装我们的 npm 依赖 (即 @playwright/test)
|
||||
RUN npm install
|
||||
|
||||
# 5. 复制我们所有的测试代码到容器中
|
||||
# (包括 playwright.config.ts, global-setup.ts 和 specs/ 目录)
|
||||
COPY . .
|
||||
|
||||
# 6. 默认命令
|
||||
CMD ["npx", "playwright", "test"]
|
||||
@@ -1,22 +1,6 @@
|
||||
# End to end tests
|
||||
使用PLAYWRIGHT测试框架执行test/e2e下的自动测试脚本
|
||||
|
||||
1.在custom/conf下的app.ini中关闭验证码:ENABLE_CAPTCHA = false
|
||||
2.在执行脚本前请下载相关依赖:npx playwright install
|
||||
3.请在.env.e2e文件中配置相关项:
|
||||
GITEA_URL:devstar实例的url,TEST_USER:测试用户,TEST_USER_ADMIN:管理员在实例里的id,现在这个测试用户需要是管理员,测试结束时会清理掉所有痕迹
|
||||
4.在项目根目录执行命令source .env.e2e && npx playwright test tests/e2e/devcontainer.test.e2e.ts ,生成的报告在test/e2e下
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1.执行make e2e-test执行自动化测试
|
||||
|
||||
|
||||
E2e tests largely follow the same syntax as [integration tests](../integration).
|
||||
|
||||
78
tests/e2e/global-setup.ts
Normal file
78
tests/e2e/global-setup.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
import { chromium, type FullConfig } from '@playwright/test';async function globalSetup(config: FullConfig) {
|
||||
|
||||
const { baseURL } = config.projects[0].use;
|
||||
|
||||
if (!baseURL) {
|
||||
|
||||
throw new Error('baseURL is not defined in playwright.config.ts');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const browser = await chromium.launch();
|
||||
|
||||
const page = await browser.newPage();
|
||||
try {
|
||||
|
||||
await page.goto(baseURL!, { timeout: 15000 });
|
||||
|
||||
|
||||
|
||||
console.log('[GlobalSetup] 检测到安装界面!正在开始自动化安装...');
|
||||
|
||||
|
||||
|
||||
await page.getByRole('textbox', { name: 'Server Domain *' }).click();
|
||||
await page.getByRole('textbox', { name: 'Server Domain *' }).fill('172.17.0.1');
|
||||
await page.getByRole('textbox', { name: 'Gitea Base URL *' }).click();
|
||||
await page.getByRole('textbox', { name: 'Gitea Base URL *' }).fill('http://172.17.0.1:80');
|
||||
await page.getByText('Server and Third-Party Service Settings').click();
|
||||
await page.getByRole('checkbox', { name: 'Enable user sign-in via Wechat QR Code.' }).uncheck();
|
||||
|
||||
await page.getByRole('checkbox', { name: 'Require a CAPTCHA for user' }).uncheck();
|
||||
|
||||
|
||||
await page.getByText('Administrator Account Settings').click();
|
||||
await page.getByRole('textbox', { name: 'Administrator Username' }).click();
|
||||
await page.getByRole('textbox', { name: 'Administrator Username' }).fill('testuser');
|
||||
await page.getByRole('textbox', { name: 'Email Address' }).click();
|
||||
await page.getByRole('textbox', { name: 'Email Address' }).fill('ilovcatlyn750314@gmail.com');
|
||||
await page.getByRole('textbox', { name: 'Password', exact: true }).click();
|
||||
await page.getByRole('textbox', { name: 'Password', exact: true }).fill('12345678');
|
||||
await page.getByRole('textbox', { name: 'Confirm Password' }).click();
|
||||
await page.getByRole('textbox', { name: 'Confirm Password' }).fill('12345678');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await page.getByRole('button', { name: 'Install Gitea'}).click(); // 'Install' (英文) 或 '安装' (中文) 都能匹配
|
||||
|
||||
|
||||
|
||||
// 4. 等待安装完成
|
||||
|
||||
await page.waitForTimeout(240*1000);
|
||||
|
||||
console.log('[GlobalSetup] 安装成功!');
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
|
||||
// 5. 截图并报错
|
||||
|
||||
console.error('[GlobalSetup] 自动化安装失败!');
|
||||
|
||||
await page.screenshot({ path: 'playwright-report/global-setup-failure.png' });
|
||||
|
||||
console.error('[GlobalSetup] 失败截图已保存到: ./reports/global-setup-failure.png');
|
||||
|
||||
throw new Error(`[GlobalSetup] 自动化安装失败。查看截图。 \n原始错误: ${error}`);
|
||||
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
}export default globalSetup;
|
||||
20
tests/e2e/package.json
Normal file
20
tests/e2e/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "devstar-e2e-runner",
|
||||
"version": "1.0.0",
|
||||
"description": "Isolated E2E test runner for DevStar Studio",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "npx playwright test"
|
||||
},
|
||||
"keywords": [
|
||||
"playwright",
|
||||
"e2e",
|
||||
"devstar"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.53.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
47
tests/e2e/playwright.config.ts
Normal file
47
tests/e2e/playwright.config.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { devices } from '@playwright/test';
|
||||
import { env } from 'node:process';
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const BASE_URL = env.DEVSTAR_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
|
||||
|
||||
export default {
|
||||
testDir: './specs',
|
||||
|
||||
|
||||
testMatch: /specs\/.*\.ts/,
|
||||
|
||||
timeout: 500000, //
|
||||
expect: {
|
||||
timeout: 15000, //
|
||||
},
|
||||
forbidOnly: Boolean(env.CI),
|
||||
retries: env.CI ? 2 : 0,
|
||||
|
||||
reporter: env.CI ? 'list' : [['list'], ['html', {
|
||||
outputFolder: 'playwright-report/html', // 写入 /app/playwright-report/html
|
||||
open: 'never'
|
||||
}]],
|
||||
|
||||
use: {
|
||||
headless: true,
|
||||
locale: 'en-US',
|
||||
actionTimeout: 15000,
|
||||
navigationTimeout: 15000,
|
||||
baseURL: BASE_URL,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputDir: 'playwright-report/test-artifacts/',
|
||||
snapshotDir: 'playwright-report/test-snapshots/',
|
||||
globalSetup: require.resolve('./global-setup.ts'),
|
||||
|
||||
} satisfies PlaywrightTestConfig;
|
||||
@@ -1,19 +1,12 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { link } from 'node:fs';
|
||||
const {
|
||||
GITEA_URL,
|
||||
TEST_USER,
|
||||
TEST_PASS,
|
||||
TEST_ADMIN_USER_ID,
|
||||
} = process.env;
|
||||
|
||||
const GITEA_URL= `http://devstar:3000`;
|
||||
const TEST_USER= `testuser`;
|
||||
const TEST_PASS= `12345678`;
|
||||
const TEST_ADMIN_USER_ID=`1`;
|
||||
const repoName = `e2e-devcontainer-test`;
|
||||
// 检查关键配置是否存在
|
||||
if (!GITEA_URL || !TEST_USER || !TEST_PASS ) {
|
||||
throw new Error("请确保 .env.e2e 配置文件已加载,并包含 GITEA_URL, TEST_USER, TEST_PASS, TEST_REPO_VALID");
|
||||
}
|
||||
test('DevContainer 功能和配置', async ({ page,context }) => {
|
||||
|
||||
console.log("正在登陆");
|
||||
await page.goto(GITEA_URL + '/user/login');
|
||||
await page.fill('#user_name',TEST_USER);
|
||||
@@ -35,69 +28,70 @@ test('DevContainer 功能和配置', async ({ page,context }) => {
|
||||
|
||||
console.log("正在点击 'Create' (创建模板) 按钮...");
|
||||
await page.getByRole('button', { name: /Create/i }).click();
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
console.log("模板已创建. 正在点击 'Edit' 按钮...");
|
||||
await expect(page.getByText('devcontainer.json')).toBeVisible();
|
||||
await page.getByRole('link', { name: 'Edit' }).click();
|
||||
|
||||
console.log("已跳转到编辑器. 正在修改内容");
|
||||
const newJsonAsObject = {
|
||||
"name": "Gitea DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "lts"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
||||
"ghcr.io/devcontainers-extra/features/poetry:2": {},
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"version": "3.12"
|
||||
},
|
||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"golang.go",
|
||||
"stylelint.vscode-stylelint",
|
||||
"DavidAnson.vscode-markdownlint",
|
||||
"Vue.volar",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"vitest.explorer",
|
||||
"cweijan.vscode-database-client2",
|
||||
"GitHub.vscode-pull-request-github",
|
||||
"Azurite.azurite"
|
||||
]
|
||||
}
|
||||
},
|
||||
"portsAttributes": {
|
||||
"3000": {
|
||||
"label": "Gitea Web",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "make deps"
|
||||
};
|
||||
//await page.getByRole('link', { name: 'Edit' }).click();
|
||||
await page.waitForTimeout(5000);
|
||||
//console.log("已跳转到编辑器. 正在修改内容");
|
||||
//const newJsonAsObject = {
|
||||
//"name": "Gitea DevContainer",
|
||||
//"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
||||
//"features": {
|
||||
//"ghcr.io/devcontainers/features/node:1": {
|
||||
//"version": "lts"
|
||||
//},
|
||||
// "ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
||||
// "ghcr.io/devcontainers-extra/features/poetry:2": {},
|
||||
// "ghcr.io/devcontainers/features/python:1": {
|
||||
// "version": "3.12"
|
||||
// },
|
||||
//"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||
//},
|
||||
//"customizations": {
|
||||
// "vscode": {
|
||||
// "settings": {},
|
||||
// "extensions": [
|
||||
// "editorconfig.editorconfig",
|
||||
// "dbaeumer.vscode-eslint",
|
||||
// "golang.go",
|
||||
// "stylelint.vscode-stylelint",
|
||||
/// "DavidAnson.vscode-markdownlint",
|
||||
// "Vue.volar",
|
||||
// "ms-azuretools.vscode-docker",
|
||||
//// "vitest.explorer",
|
||||
// "cweijan.vscode-database-client2",
|
||||
// "GitHub.vscode-pull-request-github",
|
||||
// "Azurite.azurite"
|
||||
//]
|
||||
// }
|
||||
//},
|
||||
//"portsAttributes": {
|
||||
//"3000": {
|
||||
// "label": "Gitea Web",
|
||||
// "onAutoForward": "notify"
|
||||
// }
|
||||
// },
|
||||
//"postCreateCommand": "make deps"
|
||||
// };
|
||||
|
||||
// 转换为JSON 字符串
|
||||
const newJsonString = JSON.stringify(newJsonAsObject);
|
||||
//const newJsonString = JSON.stringify(newJsonAsObject);
|
||||
|
||||
// 设置焦点
|
||||
await page.locator('.view-lines > div:nth-child(20)').click();
|
||||
console.log("正在手动删除模板内容 ");
|
||||
for (let i = 0; i < 500; i++) {
|
||||
await page.keyboard.press('Backspace');
|
||||
}
|
||||
// 粘贴字符串
|
||||
console.log("正在粘贴JSON 内容...");
|
||||
await page.keyboard.insertText(newJsonString);
|
||||
await page.getByRole('button', { name: 'Commit Changes' }).click();
|
||||
console.log("devcontainer.json 修改并提交成功.");
|
||||
console.log("正在导航回 Dev Container 标签页进行验证...");
|
||||
await page.getByRole('link', { name: 'Dev Container' }).click();
|
||||
// await page.locator('.view-lines > div:nth-child(20)').click();
|
||||
// console.log("正在手动删除模板内容 ");
|
||||
//for (let i = 0; i < 500; i++) {
|
||||
// await page.keyboard.press('Backspace');
|
||||
// }
|
||||
/// 粘贴字符串
|
||||
//console.log("正在粘贴JSON 内容...");
|
||||
// await page.keyboard.insertText(newJsonString);
|
||||
//await page.getByRole('button', { name: 'Commit Changes' }).click();
|
||||
//console.log("devcontainer.json 修改并提交成功.");
|
||||
//console.log("正在导航回 Dev Container 标签页进行验证...");
|
||||
//await page.getByRole('link', { name: 'Dev Container' }).click();
|
||||
|
||||
console.log("创建开发容器");
|
||||
await page.getByRole('button', { name: 'Create Dev Container' }).click();
|
||||
Reference in New Issue
Block a user