test: optimize command (#3030)

* test: optimize command

* chore: add force options

* chore: kill child process
This commit is contained in:
被雨水过滤的空气-Rain 2023-11-14 10:04:09 +08:00 committed by GitHub
parent 4d4b73767f
commit 887ddf0ed1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 54 additions and 30 deletions

View File

@ -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 # !!! 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_ENV=development
APP_PORT=20000 APP_PORT=20000
APP_BASE_URL=http://localhost:20000
APP_KEY=test-key-e2e APP_KEY=test-key-e2e
SOCKET_PATH=storage/gateway-e2e.sock SOCKET_PATH=storage/gateway-e2e.sock
__E2E__=true __E2E__=true
# 指定运行测试的服务地址;当 APP_BASE_URL 不为空时,将不会在本地启动测试服务
# APP_BASE_URL=http://localhost:20000
# 启用 mock-collections 插件 # 启用 mock-collections 插件
APPEND_PRESET_BUILT_IN_PLUGINS=mock-collections APPEND_PRESET_BUILT_IN_PLUGINS=mock-collections

View File

@ -114,6 +114,9 @@ interface CreatePageOptions {
pageSchema?: any; pageSchema?: any;
} }
const PORT = process.env.APP_PORT || 20000;
const APP_BASE_URL = process.env.APP_BASE_URL || `http://localhost:${PORT}`;
class NocoPage { class NocoPage {
private url: string; private url: string;
private uid: string; private uid: string;
@ -213,7 +216,7 @@ export const test = Object.assign(_test, {
const getStorageItem = (key: string, storageState: any) => { const getStorageItem = (key: string, storageState: any) => {
return storageState.origins 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; ?.localStorage.find((item) => item.name === key)?.value;
}; };
@ -470,8 +473,8 @@ function getHeaders(storageState: any) {
const headers: any = {}; const headers: any = {};
const token = getStorageItem('NOCOBASE_TOKEN', storageState); const token = getStorageItem('NOCOBASE_TOKEN', storageState);
const auth = getStorageItem('NOCOBASE_AUTH', storageState); const auth = getStorageItem('NOCOBASE_AUTH', storageState);
const subAppName = new URL(process.env.APP_BASE_URL).pathname.match(/^\/apps\/([^/]*)\/*/)?.[1]; const subAppName = new URL(APP_BASE_URL).pathname.match(/^\/apps\/([^/]*)\/*/)?.[1];
const hostName = new URL(process.env.APP_BASE_URL).host; const hostName = new URL(APP_BASE_URL).host;
const locale = getStorageItem('NOCOBASE_LOCALE', storageState); const locale = getStorageItem('NOCOBASE_LOCALE', storageState);
const timezone = '+08:00'; const timezone = '+08:00';
const withAclMeta = 'true'; const withAclMeta = 'true';

View File

@ -28,7 +28,7 @@ export default defineConfig({
use: { use: {
// Base URL to use in actions like `await page.goto('/')`. // 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. // Collect trace when retrying the failed test.
trace: 'on-first-retry', trace: 'on-first-retry',

View File

@ -5,9 +5,7 @@ import path from 'path';
const adminFile = 'playwright/.auth/admin.json'; 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 }) => { setup('admin', async ({ page }) => {

View File

@ -1,16 +1,16 @@
import { execSync } from 'node:child_process'; import { execSync } from 'node:child_process';
import { commonConfig, runNocoBase } from './utils'; import { APP_BASE_URL, commonConfig, runNocoBase } from './utils';
const runCodegenSync = () => { const runCodegenSync = () => {
try { try {
execSync( 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, commonConfig,
); );
} catch (err) { } catch (err) {
if (err.message.includes('auth.json')) { if (err.message.includes('auth.json')) {
execSync( 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, commonConfig,
); );
} else { } else {

View File

@ -1,3 +1,5 @@
import { runNocoBase } from './utils'; import { runNocoBase } from './utils';
runNocoBase(); runNocoBase({
force: true,
});

View File

@ -1,15 +1,23 @@
import { runCommand, runNocoBase } from './utils'; import { runCommand, runNocoBase } from './utils';
const abortController = new AbortController();
process.on('SIGINT', () => {
abortController.abort();
process.exit();
});
const run = async () => { const run = async () => {
const { kill, awaitForNocoBase } = await runNocoBase({ const { awaitForNocoBase } = await runNocoBase({
stdio: 'ignore', // 不输出服务的日志,避免干扰测试的日志 stdio: 'ignore', // 不输出服务的日志,避免干扰测试的日志
signal: abortController.signal,
}); });
await awaitForNocoBase(); await awaitForNocoBase();
console.log('Start running tests...'); console.log('Start running tests...');
await runCommand('npx', ['playwright', 'test', ...process.argv.slice(2)]); await runCommand('npx', ['playwright', 'test', ...process.argv.slice(2)]);
kill?.('SIGKILL'); abortController.abort();
}; };
run(); run();

View File

@ -2,19 +2,23 @@ import axios from 'axios';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import type { CommonOptions } from 'execa'; import type { CommonOptions } from 'execa';
import execa from 'execa'; import execa from 'execa';
import _ from 'lodash';
import net from 'net'; import net from 'net';
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'path'; 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 = { export const commonConfig: any = {
stdio: 'inherit', stdio: 'inherit',
}; };
export const runCommand = (command, argv, options = {}) => { export const runCommand = (command, argv, options: any = {}) => {
return execa(command, argv, { return execa(command, argv, {
shell: true, shell: true,
stdio: 'inherit', stdio: 'inherit',
...options, ..._.omit(options, 'force'),
env: { env: {
...process.env, ...process.env,
}, },
@ -53,12 +57,12 @@ const checkServer = async (duration = 1000, max = 60 * 10) => {
return reject(new Error('Server start timeout.')); return reject(new Error('Server start timeout.'));
} }
if (!(await checkPort(process.env.APP_PORT))) { if (!(await checkPort(PORT))) {
return; return;
} }
axios axios
.get(`${process.env.APP_BASE_URL}/api/__health_check`) .get(`${APP_BASE_URL}/api/__health_check`)
.then((response) => { .then((response) => {
if (response.status === 200) { if (response.status === 200) {
clearInterval(timer); clearInterval(timer);
@ -86,7 +90,7 @@ const checkUI = async (duration = 1000, max = 60 * 10) => {
} }
axios axios
.get(`${process.env.APP_BASE_URL}/__umi/api/bundle-status`) .get(`${APP_BASE_URL}/__umi/api/bundle-status`)
.then((response) => { .then((response) => {
if (response.data.bundleStatus.done) { if (response.data.bundleStatus.done) {
clearInterval(timer); clearInterval(timer);
@ -100,7 +104,15 @@ const checkUI = async (duration = 1000, max = 60 * 10) => {
}); });
}; };
export const runNocoBase = async (options?: CommonOptions<any>) => { export const runNocoBase = async (
options?: CommonOptions<any> & {
/**
*
*/
force?: boolean;
signal?: AbortSignal;
},
) => {
// 用于存放 playwright 自动生成的相关的文件 // 用于存放 playwright 自动生成的相关的文件
if (!fs.existsSync('playwright')) { if (!fs.existsSync('playwright')) {
fs.mkdirSync('playwright'); fs.mkdirSync('playwright');
@ -117,6 +129,11 @@ export const runNocoBase = async (options?: CommonOptions<any>) => {
dotenv.config({ path: path.resolve(process.cwd(), '.env.e2e') }); 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 () => { const awaitForNocoBase = async () => {
if (process.env.CI) { if (process.env.CI) {
console.log('check server...'); console.log('check server...');
@ -133,28 +150,22 @@ export const runNocoBase = async (options?: CommonOptions<any>) => {
if (process.env.CI) { if (process.env.CI) {
console.log('yarn nocobase install'); console.log('yarn nocobase install');
await runCommand('yarn', ['nocobase', 'install'], options); await runCommand('yarn', ['nocobase', 'install'], options);
console.log(`yarn start -d -p ${process.env.APP_PORT}`); console.log(`yarn start -d -p ${PORT}`);
await runCommand('yarn', ['start', '-d', `-p ${process.env.APP_PORT}`], options); await runCommand('yarn', ['start', '-d', `-p ${PORT}`], options);
return { awaitForNocoBase }; return { awaitForNocoBase };
} }
if (!process.env.APP_BASE_URL.includes('localhost')) {
return {
awaitForNocoBase: async () => {},
};
}
// 加上 -f 会清空数据库 // 加上 -f 会清空数据库
console.log('yarn nocobase install -f'); console.log('yarn nocobase install -f');
await runCommand('yarn', ['nocobase', 'install', '-f'], options); 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.'); console.log('Server is running, skip starting server.');
return { awaitForNocoBase }; return { awaitForNocoBase };
} }
console.log('starting server...'); 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 }; return { cancel, kill, awaitForNocoBase };
}; };