From 887ddf0ed12c18a8ec3060e2976d237d295f6c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=AB=E9=9B=A8=E6=B0=B4=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=B0=94-Rain?= <958414905@qq.com> Date: Tue, 14 Nov 2023 10:04:09 +0800 Subject: [PATCH] test: optimize command (#3030) * test: optimize command * chore: add force options * chore: kill child process --- .env.e2e.example | 4 ++- packages/core/test/src/client/e2eUtils.ts | 9 +++-- playwright.config.ts | 2 +- scripts/auth.setup.ts | 4 +-- scripts/codegen.setup.ts | 6 ++-- scripts/nocobase.setup.ts | 4 ++- scripts/runE2e.setup.ts | 12 +++++-- scripts/utils.ts | 43 ++++++++++++++--------- 8 files changed, 54 insertions(+), 30 deletions(-) diff --git a/.env.e2e.example b/.env.e2e.example index 23c975abf..d2a03e948 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -12,11 +12,13 @@ VERDACCIO_PORT=10104 # !!! It is recommended to use nginx to proxy static files. For example https://github.com/nocobase/nocobase/blob/main/docker/nocobase/nocobase.conf APP_ENV=development APP_PORT=20000 -APP_BASE_URL=http://localhost:20000 APP_KEY=test-key-e2e SOCKET_PATH=storage/gateway-e2e.sock __E2E__=true +# 指定运行测试的服务地址;当 APP_BASE_URL 不为空时,将不会在本地启动测试服务 +# APP_BASE_URL=http://localhost:20000 + # 启用 mock-collections 插件 APPEND_PRESET_BUILT_IN_PLUGINS=mock-collections diff --git a/packages/core/test/src/client/e2eUtils.ts b/packages/core/test/src/client/e2eUtils.ts index 7b55216d8..4f0747433 100644 --- a/packages/core/test/src/client/e2eUtils.ts +++ b/packages/core/test/src/client/e2eUtils.ts @@ -114,6 +114,9 @@ interface CreatePageOptions { pageSchema?: any; } +const PORT = process.env.APP_PORT || 20000; +const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`; + class NocoPage { private url: string; private uid: string; @@ -213,7 +216,7 @@ export const test = Object.assign(_test, { const getStorageItem = (key: string, storageState: any) => { return storageState.origins - .find((item) => item.origin === process.env.APP_BASE_URL) + .find((item) => item.origin === APP_BASE_URL) ?.localStorage.find((item) => item.name === key)?.value; }; @@ -470,8 +473,8 @@ function getHeaders(storageState: any) { const headers: any = {}; const token = getStorageItem('NOCOBASE_TOKEN', storageState); const auth = getStorageItem('NOCOBASE_AUTH', storageState); - const subAppName = new URL(process.env.APP_BASE_URL).pathname.match(/^\/apps\/([^/]*)\/*/)?.[1]; - const hostName = new URL(process.env.APP_BASE_URL).host; + const subAppName = new URL(APP_BASE_URL).pathname.match(/^\/apps\/([^/]*)\/*/)?.[1]; + const hostName = new URL(APP_BASE_URL).host; const locale = getStorageItem('NOCOBASE_LOCALE', storageState); const timezone = '+08:00'; const withAclMeta = 'true'; diff --git a/playwright.config.ts b/playwright.config.ts index 7a882c6d2..b830a0fd6 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -28,7 +28,7 @@ export default defineConfig({ use: { // Base URL to use in actions like `await page.goto('/')`. - baseURL: process.env.APP_BASE_URL, + baseURL: process.env.APP_BASE_URL || `http://localhost:${process.env.APP_PORT || 20000}`, // Collect trace when retrying the failed test. trace: 'on-first-retry', diff --git a/scripts/auth.setup.ts b/scripts/auth.setup.ts index d5cfc37f1..3e788dc6b 100644 --- a/scripts/auth.setup.ts +++ b/scripts/auth.setup.ts @@ -5,9 +5,7 @@ import path from 'path'; const adminFile = 'playwright/.auth/admin.json'; // 加载变量 -if (!process.env.APP_BASE_URL) { - dotenv.config({ path: path.resolve(process.cwd(), '.env.e2e') }); -} +dotenv.config({ path: path.resolve(process.cwd(), '.env.e2e') }); // 保存登录状态,避免每次都要登录 setup('admin', async ({ page }) => { diff --git a/scripts/codegen.setup.ts b/scripts/codegen.setup.ts index f0ece84bd..534d2558a 100644 --- a/scripts/codegen.setup.ts +++ b/scripts/codegen.setup.ts @@ -1,16 +1,16 @@ import { execSync } from 'node:child_process'; -import { commonConfig, runNocoBase } from './utils'; +import { APP_BASE_URL, commonConfig, runNocoBase } from './utils'; const runCodegenSync = () => { try { execSync( - `npx playwright codegen --load-storage=playwright/.auth/codegen.auth.json ${process.env.APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`, + `npx playwright codegen --load-storage=playwright/.auth/codegen.auth.json ${APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`, commonConfig, ); } catch (err) { if (err.message.includes('auth.json')) { execSync( - `npx playwright codegen ${process.env.APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`, + `npx playwright codegen ${APP_BASE_URL} --save-storage=playwright/.auth/codegen.auth.json`, commonConfig, ); } else { diff --git a/scripts/nocobase.setup.ts b/scripts/nocobase.setup.ts index 5cc1a7522..5a4ed1f56 100644 --- a/scripts/nocobase.setup.ts +++ b/scripts/nocobase.setup.ts @@ -1,3 +1,5 @@ import { runNocoBase } from './utils'; -runNocoBase(); +runNocoBase({ + force: true, +}); diff --git a/scripts/runE2e.setup.ts b/scripts/runE2e.setup.ts index a01bbaa30..b19c021b8 100644 --- a/scripts/runE2e.setup.ts +++ b/scripts/runE2e.setup.ts @@ -1,15 +1,23 @@ import { runCommand, runNocoBase } from './utils'; +const abortController = new AbortController(); + +process.on('SIGINT', () => { + abortController.abort(); + process.exit(); +}); + const run = async () => { - const { kill, awaitForNocoBase } = await runNocoBase({ + const { awaitForNocoBase } = await runNocoBase({ stdio: 'ignore', // 不输出服务的日志,避免干扰测试的日志 + signal: abortController.signal, }); await awaitForNocoBase(); console.log('Start running tests...'); await runCommand('npx', ['playwright', 'test', ...process.argv.slice(2)]); - kill?.('SIGKILL'); + abortController.abort(); }; run(); diff --git a/scripts/utils.ts b/scripts/utils.ts index 1abf08602..2ef033fa8 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -2,19 +2,23 @@ import axios from 'axios'; import dotenv from 'dotenv'; import type { CommonOptions } from 'execa'; import execa from 'execa'; +import _ from 'lodash'; import net from 'net'; import fs from 'node:fs'; import path from 'path'; +const PORT = process.env.APP_PORT || 20000; +export const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`; + export const commonConfig: any = { stdio: 'inherit', }; -export const runCommand = (command, argv, options = {}) => { +export const runCommand = (command, argv, options: any = {}) => { return execa(command, argv, { shell: true, stdio: 'inherit', - ...options, + ..._.omit(options, 'force'), env: { ...process.env, }, @@ -53,12 +57,12 @@ const checkServer = async (duration = 1000, max = 60 * 10) => { return reject(new Error('Server start timeout.')); } - if (!(await checkPort(process.env.APP_PORT))) { + if (!(await checkPort(PORT))) { return; } axios - .get(`${process.env.APP_BASE_URL}/api/__health_check`) + .get(`${APP_BASE_URL}/api/__health_check`) .then((response) => { if (response.status === 200) { clearInterval(timer); @@ -86,7 +90,7 @@ const checkUI = async (duration = 1000, max = 60 * 10) => { } axios - .get(`${process.env.APP_BASE_URL}/__umi/api/bundle-status`) + .get(`${APP_BASE_URL}/__umi/api/bundle-status`) .then((response) => { if (response.data.bundleStatus.done) { clearInterval(timer); @@ -100,7 +104,15 @@ const checkUI = async (duration = 1000, max = 60 * 10) => { }); }; -export const runNocoBase = async (options?: CommonOptions) => { +export const runNocoBase = async ( + options?: CommonOptions & { + /** + * 是否强制启动服务 + */ + force?: boolean; + signal?: AbortSignal; + }, +) => { // 用于存放 playwright 自动生成的相关的文件 if (!fs.existsSync('playwright')) { fs.mkdirSync('playwright'); @@ -117,6 +129,11 @@ export const runNocoBase = async (options?: CommonOptions) => { dotenv.config({ path: path.resolve(process.cwd(), '.env.e2e') }); + if (process.env.APP_BASE_URL && !options?.force) { + console.log('APP_BASE_URL is setting, skip starting server.'); + return { awaitForNocoBase: () => {} }; + } + const awaitForNocoBase = async () => { if (process.env.CI) { console.log('check server...'); @@ -133,28 +150,22 @@ export const runNocoBase = async (options?: CommonOptions) => { if (process.env.CI) { console.log('yarn nocobase install'); await runCommand('yarn', ['nocobase', 'install'], options); - console.log(`yarn start -d -p ${process.env.APP_PORT}`); - await runCommand('yarn', ['start', '-d', `-p ${process.env.APP_PORT}`], options); + console.log(`yarn start -d -p ${PORT}`); + await runCommand('yarn', ['start', '-d', `-p ${PORT}`], options); return { awaitForNocoBase }; } - if (!process.env.APP_BASE_URL.includes('localhost')) { - return { - awaitForNocoBase: async () => {}, - }; - } - // 加上 -f 会清空数据库 console.log('yarn nocobase install -f'); await runCommand('yarn', ['nocobase', 'install', '-f'], options); - if (await checkPort(process.env.APP_PORT)) { + if (await checkPort(PORT)) { console.log('Server is running, skip starting server.'); return { awaitForNocoBase }; } console.log('starting server...'); - const { cancel, kill } = runCommand('yarn', ['dev', `-p ${process.env.APP_PORT}`, ...process.argv.slice(2)], options); + const { cancel, kill } = runCommand('yarn', ['dev', `-p ${PORT}`, ...process.argv.slice(2)], options); return { cancel, kill, awaitForNocoBase }; };