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
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

View File

@ -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';

View File

@ -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',

View File

@ -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 }) => {

View File

@ -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 {

View File

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

View File

@ -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();

View File

@ -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<any>) => {
export const runNocoBase = async (
options?: CommonOptions<any> & {
/**
*
*/
force?: boolean;
signal?: AbortSignal;
},
) => {
// 用于存放 playwright 自动生成的相关的文件
if (!fs.existsSync('playwright')) {
fs.mkdirSync('playwright');
@ -117,6 +129,11 @@ export const runNocoBase = async (options?: CommonOptions<any>) => {
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<any>) => {
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 };
};