diff --git a/playwright.config.ts b/playwright.config.ts index b56d2df9eb..3fed80c178 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -41,11 +41,12 @@ const config: PlaywrightTestConfig = { snapshotDir: 'playwright-report/test-snapshots/', }; -if (BASE_URL.includes('http://devstar:3000')) { - console.log(' 已启用安装'); - config.globalSetup = require.resolve('./global-setup.ts'); -} else { - console.log(` 已跳过 安装`); -} +const skipInstall = env.E2E_SKIP_INSTALL === 'true'; +if (skipInstall) { + console.log(`已跳过 global-setup.`); +} else { + console.log(`[Playwright Config] 探测结果: E2E_SKIP_INSTALL is not 'true'. 已启用 global-setup.`); + config.globalSetup = require.resolve('./global-setup.ts'); +} export default config satisfies PlaywrightTestConfig; diff --git a/tests/e2e/docker-compose.test.yml b/tests/e2e/docker-compose.test.yml index a79a2a5cda..4f2a428b8a 100644 --- a/tests/e2e/docker-compose.test.yml +++ b/tests/e2e/docker-compose.test.yml @@ -26,9 +26,12 @@ services: context: . # 等待 devstar 的 "healthcheck" 通过后,才启动 environment: - # 将 DevStar 的 URL 传给 Playwright - - DEVSTAR_URL=${DEVSTAR_URL:-http://devstar:3000} - - DOCKER_HOST_GATEWAY=${DOCKER_HOST_GATEWAY} + - DEVSTAR_URL=${DEVSTAR_URL} + - E2E_SKIP_INSTALL=${E2E_SKIP_INSTALL} + - E2E_USERNAME=${E2E_USERNAME} + - E2E_PASSWORD=${E2E_PASSWORD} + - E2E_ADMIN_ID=${E2E_ADMIN_ID} + - E2E_MODE=${E2E_MODE} volumes: # 也挂载 Docker Socket - /var/run/docker.sock:/var/run/docker.sock diff --git a/tests/e2e/global-setup.ts b/tests/e2e/global-setup.ts index 8b9e46b327..7bb126addb 100644 --- a/tests/e2e/global-setup.ts +++ b/tests/e2e/global-setup.ts @@ -1,59 +1,70 @@ - import { chromium, type FullConfig } from '@playwright/test';async function globalSetup(config: FullConfig) { - - const { baseURL } = config.projects[0].use; +import { chromium, type FullConfig } from '@playwright/test'; +import { env } from 'node:process'; +import { URL } from 'url'; +async function globalSetup(config: FullConfig) { + const mode = env.E2E_MODE; + const {baseURL} = config.projects[0].use; + const isInstalledMode=env.E2E_SKIP_INSTALL; + const DEFAULT_E2E_USER = 'testuser'; + const DEFAULT_E2E_PASS = '12345678'; if (!baseURL) { - - throw new Error('baseURL is not defined in playwright.config.ts'); - + throw new Error('[GlobalSetup] 致命错误: baseURL 或 storageState 未定义!'); } - const hostGateway = process.env.DOCKER_HOST_GATEWAY || '172.17.0.1'; - - const giteaBaseURL = `http://${hostGateway}:80`; - const serverDomain = hostGateway; 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(hostGateway); - await page.getByRole('textbox', { name: 'Gitea Base URL *' }).click(); - await page.getByRole('textbox', { name: 'Gitea Base URL *' }).fill(giteaBaseURL); - 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}`); - + if (mode === 'url') { + try { + const url1=env.DEVSTAR_URL; + const url2 = `http://${url1}`; + await page.goto(url2, { timeout: 15000 }); + console.log('[GlobalSetup] 检测到安装界面!正在开始自动化安装...'); + // (你提供的所有 "testuser" 安装步骤...) + 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' }).fill('testuser'); + await page.getByRole('textbox', { name: 'Email Address' }).fill('ilovcatlyn750314@gmail.com'); + await page.getByRole('textbox', { name: 'Password', exact: true }).fill('12345678'); + await page.getByRole('textbox', { name: 'Confirm Password' }).fill('12345678'); + await page.getByRole('button', { name: 'Install Gitea'}).click(); + await page.waitForTimeout(90000); + } catch (error) { + console.error('[GlobalSetup] "URL 模式" 登录失败:', error); + await page.screenshot({ path: 'playwright-report/global-setup-login-failure.png' }); + throw error; + } + } else if (mode === 'compose') { + console.log('[GlobalSetup] 模式: 构建(Compose). 正在执行构建模式的安装脚本...'); + try { + const hostGateway = env.DOCKER_HOST_GATEWAY || '172.17.0.1'; + const giteaBaseURL = `http://${hostGateway}:80`; + const serverDomain = hostGateway; + const browser = await chromium.launch(); + await page.goto(baseURL, { timeout: 15000 }); + console.log('[GlobalSetup] 检测到安装界面!正在开始自动化安装...'); + await page.getByRole('textbox', { name: 'Server Domain *' }).fill(serverDomain); + await page.getByRole('textbox', { name: 'Gitea Base URL *' }).fill(giteaBaseURL); + // (你提供的所有 "testuser" 安装步骤...) + 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' }).fill('testuser'); + await page.getByRole('textbox', { name: 'Email Address' }).fill('ilovcatlyn750314@gmail.com'); + await page.getByRole('textbox', { name: 'Password', exact: true }).fill('12345678'); + await page.getByRole('textbox', { name: 'Confirm Password' }).fill('12345678'); + await page.getByRole('button', { name: 'Install Gitea'}).click(); + await page.waitForTimeout(90000); + } catch (error) { + console.error('[GlobalSetup] "构建模式" 安装失败:', error); + await page.screenshot({ path: 'playwright-report/global-setup-failure.png' }); + throw error; + } + } else { + throw new Error(`[GlobalSetup] 未知的 E2E_MODE: "${mode}"`); } - await browser.close(); - -}export default globalSetup; +} +export default globalSetup; \ No newline at end of file diff --git a/tests/e2e/run-e2e-tests.sh b/tests/e2e/run-e2e-tests.sh index 1a01c7b7b1..f11aa2eb16 100755 --- a/tests/e2e/run-e2e-tests.sh +++ b/tests/e2e/run-e2e-tests.sh @@ -29,36 +29,52 @@ sudo chmod 666 /var/run/docker.sock echo "权限设置完成。" echo "" -echo "===== [3/5] 构建/拉取依赖镜像... =====" -#现在make devstar 处理镜像的构建 -docker pull mengning997/webterminal:latest -docker tag mengning997/webterminal:latest devstar.cn/devstar/webterminal:latest +if [ -z "$TARGET_URL" ]; then -echo "===== 现在检测Docker宿主机网关=====" -DOCKER_HOST_GATEWAY="172.17.0.1" -if [ "$OS_TYPE" = "Darwin" ]; then + echo "===== [3/5] 构建模式: 构建/拉取依赖镜像... =====" + #现在make devstar 处理镜像的构建 + docker pull mengning997/webterminal:latest + docker tag mengning997/webterminal:latest devstar.cn/devstar/webterminal:latest + + echo "===== 现在检测Docker宿主机网关=====" + DOCKER_HOST_GATEWAY="172.17.0.1" + if [ "$OS_TYPE" = "Darwin" ]; then DOCKER_HOST_GATEWAY="host.docker.internal" -elif [ "$OS_TYPE" = "Linux" ]; then + elif [ "$OS_TYPE" = "Linux" ]; then LINUX_IP=$(ip addr show docker0 | grep -Po 'inet \K[\d\.]+') - if [ -n "$LINUX_IP" ]; then - DOCKER_HOST_GATEWAY=$LINUX_IP - else + if [ -n "$LINUX_IP" ]; then + DOCKER_HOST_GATEWAY=$LINUX_IP + else echo "警告: 无法动态检测 'docker0' IP, 回退到 172.17.0.1" + fi fi + export DOCKER_HOST_GATEWAY +else + echo "===== [3/5] URL 模式: 跳过依赖镜像和网关检测。 =====" fi -echo "==> 宿主机网关被设置为: $DOCKER_HOST_GATEWAY" -export DOCKER_HOST_GATEWAY - echo "===== [4/5] 启动并运行测试... =====" # 检查从 Makefile 传来的 TARGET_URL 变量是否为空 if [ -n "$TARGET_URL" ]; then # --- [A] URL 模式 --- - echo "URL模式" - - # 1. 将目标 URL 导出为 DEVSTAR_URL, 供 docker-compose.yml 读取 + echo " 模式: [URL模式]. 目标: $TARGET_URL" export DEVSTAR_URL=$TARGET_URL - # 2. [关键] 只启动 test-runner 服务 - # 我们不需要 devstar, 也不需要 --abort-on-container-exit + export E2E_MODE="url" + echo " 正在检查安装状态..." + PATH_TO_CHECK="/user/login" + EXPECTED_CODE_IF_INSTALLED="200" + PROBE_URL="${TARGET_URL}${PATH_TO_CHECK}" + HTTP_CODE=$(curl -L -s -o /dev/null -w "%{http_code}" "$PROBE_URL") + + if [ "$HTTP_CODE" -eq "$EXPECTED_CODE_IF_INSTALLED" ]; then + echo " 探测结果: 目标已安装 (在 ${PROBE_URL} 收到 HTTP ${HTTP_CODE})." + export E2E_SKIP_INSTALL="true" + export E2E_USERNAME + export E2E_PASSWORD + export E2E_ADMIN_ID + else + echo "没有安装,下面执行安装脚本! " + export E2E_SKIP_INSTALL="false" + fi command docker compose \ -f tests/e2e/docker-compose.test.yml \ up \ @@ -71,6 +87,8 @@ else # 1. 导出内部 URL (这会成为 compose 里的默认值) export DEVSTAR_URL="http://devstar:3000" + export E2E_MODE="compose" + export E2E_SKIP_INSTALL="false" echo "即将执行: docker compose -f tests/e2e/docker-compose.test.yml up --build --wait --exit-code-from test-runner" # 2. [关键] 运行你原来的命令, 启动所有服务 command docker compose \ @@ -81,7 +99,6 @@ else --abort-on-container-exit \ --exit-code-from test-runner fi - # 捕获 test-runner 的退出码 EXIT_CODE=$? echo "" diff --git a/tests/e2e/specs/devcontainer.test.e2e.ts b/tests/e2e/specs/devcontainer.test.e2e.ts index 1095462619..6f6713c564 100644 --- a/tests/e2e/specs/devcontainer.test.e2e.ts +++ b/tests/e2e/specs/devcontainer.test.e2e.ts @@ -1,18 +1,28 @@ import { test, expect } from '@playwright/test'; import { link } from 'node:fs'; +import { env } from 'node:process'; +import { Login } from './utils.e2e.test'; -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`; +const isAlreadyInstalled = env.E2E_SKIP_INSTALL === 'true'; +const url1=env.DEVSTAR_URL; +const url2 = `http://${url1}`; +const GITEA_URL = (env.E2E_MODE === 'url') ? url2 : 'http://devstar:3000'; +const TEST_USER = isAlreadyInstalled ? env.E2E_USERNAME : 'testuser'; +const TEST_PASS = isAlreadyInstalled ? env.E2E_PASSWORD : '12345678'; +const TEST_ADMIN_USER_ID=isAlreadyInstalled?env.E2E_ADMIN_ID:'1'; +const repoName='e2e-test-repo'; +test.describe('devcontainer 相关测试',()=>{ +test.beforeEach(async ({ page }) => { + + await Login(page); + }); test('DevContainer 功能和配置', async ({ page,context }) => { - console.log("正在登陆"); - await page.goto(GITEA_URL + '/user/login'); - await page.fill('#user_name',TEST_USER); - await page.fill('#password', TEST_PASS); - await page.getByRole('button', { name: 'Sign In' }).click(); - await expect(page).toHaveURL(GITEA_URL + '/'); + //console.log("正在登陆"); + //await page.goto(GITEA_URL + '/user/login'); + //await page.fill('#user_name',TEST_USER); + //await page.fill('#password', TEST_PASS); + //await page.getByRole('button', { name: 'Sign In' }).click(); + //await expect(page).toHaveURL(GITEA_URL + '/'); console.log(`正在创建新仓库: ${repoName}`); await page.goto(GITEA_URL + '/repo/create'); @@ -137,13 +147,13 @@ test('DevContainer 功能和配置', async ({ page,context }) => { //console.log("test3检查通过"); }); test('权限修改相关', async ({ page }) => { - console.log("正在登陆"); - await page.goto(GITEA_URL + '/user/login'); - await page.fill('#user_name', TEST_USER); - await page.fill('#password', TEST_PASS); - await page.getByRole('button', { name: 'Sign In' }).click(); - await expect(page).toHaveURL(GITEA_URL + '/'); - console.log("登录成功! "); + //console.log("正在登陆"); + //await page.goto(GITEA_URL + '/user/login'); + //await page.fill('#user_name', TEST_USER); + //await page.fill('#password', TEST_PASS); + //await page.getByRole('button', { name: 'Sign In' }).click(); + //await expect(page).toHaveURL(GITEA_URL + '/'); + //console.log("登录成功! "); console.log('权限配置'); await page.goto(GITEA_URL+ '/-/admin/users/' + TEST_ADMIN_USER_ID + '/edit'); @@ -175,5 +185,6 @@ test('权限修改相关', async ({ page }) => { }); +}) diff --git a/tests/e2e/specs/utils.e2e.test.ts b/tests/e2e/specs/utils.e2e.test.ts new file mode 100644 index 0000000000..8b0e1c3158 --- /dev/null +++ b/tests/e2e/specs/utils.e2e.test.ts @@ -0,0 +1,48 @@ +import { type Page, expect } from '@playwright/test'; +import { env } from 'node:process'; + +const DEFAULT_E2E_USER = 'testuser'; +const DEFAULT_E2E_PASS = '12345678'; +const mode=env.E2E_MODE; + +export async function Login(page: Page) { + + const isInstalled = env.E2E_SKIP_INSTALL === 'true'; + + let username: string | undefined; + let password: string | undefined; +if(mode === 'url'){ + const url1=env.DEVSTAR_URL; + const url2 = `http://${url1}`; + if (isInstalled) { + // 【"已安装" 模式 + username = env.E2E_USERNAME || DEFAULT_E2E_USER; + password = env.E2E_PASSWORD || DEFAULT_E2E_PASS; + console.log(`"已安装"模式, 尝试用 ${username} 登录...`); + } else { + // 【"未安装" 模式】 + username = DEFAULT_E2E_USER; + password = DEFAULT_E2E_PASS; + console.log(`[LoginHelper] "刚安装"模式, 尝试用 ${username} 登录...`); + } + + // 4. (检查验证码和执行登录的逻辑) + await page.goto(url2 + '/user/login'); + const captchaInput = page.locator('input[name="captcha"]'); + if (await captchaInput.isVisible()) { + throw new Error('登录失败: 检测到验证码! 请禁用它。'); + } + await page.fill('#user_name',username); + await page.fill('#password', password); + await page.getByRole('button', { name: 'Sign In' }).click(); + await expect(page).toHaveURL(url2 + '/'); + console.log(`[LoginHelper] 登录成功!`); +} +else if(mode=='compose'){ + await page.fill('#user_name',username); + await page.fill('#password', password); + await page.getByRole('button', { name: 'Sign In' }).click(); + await expect(page).toHaveURL(env.DEVSTAR_URL+ '/'); + console.log(`[LoginHelper] 登录成功!`); +} +} \ No newline at end of file