diff --git a/.env.uat b/.env.uat index e79dfd6..af84e38 100644 --- a/.env.uat +++ b/.env.uat @@ -1,23 +1,25 @@ # 基础配置 BASE_URL=https://ibp-test.longi.com/main/#/login?debug=ly -USERNAME=zidonghuatest -PASSWORD=Lianyu_123 + +# 登录配置 +IBP_USERNAME=zidonghuatest +IBP_PASSWORD=Lianyu_123 # 测试配置 TEST_DATA_DIR=test-data -TEST_BATCH_SIZE=5 +TEST_BATCH_SIZE=2 TEST_RETRY_COUNT=3 -TEST_BATCH_INTERVAL=1000 -TEST_MAX_RETRY_DELAY=5000 +TEST_BATCH_INTERVAL=2000 +TEST_MAX_RETRY_DELAY=10000 # 超时配置 -MENU_TIME_OUT=30000 +MENU_TIME_OUT=60000 EXPECT_TIMEOUT=3600000 # 浏览器配置 BROWSER_HEADLESS=false -BROWSER_SLOW_MO=50 -BROWSER_TIMEOUT=60000 +BROWSER_SLOW_MO=100 +BROWSER_TIMEOUT=120000 VIEWPORT_WIDTH=1920 VIEWPORT_HEIGHT=1080 @@ -27,5 +29,5 @@ TEST_PROGRESS_FILE_PATH=test-data/test-progress.json # 页面加载配置 PAGE_LOAD_MAX_RETRIES=5 -PAGE_LOAD_RETRY_INTERVAL=2000 -PAGE_LOAD_STABILITY_DELAY=1000 \ No newline at end of file +PAGE_LOAD_RETRY_INTERVAL=3000 +PAGE_LOAD_STABILITY_DELAY=2000 \ No newline at end of file diff --git a/config.js b/config.js deleted file mode 100644 index 0519ecb..0000000 --- a/config.js +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/login-error.png b/login-error.png deleted file mode 100644 index 0a9b025..0000000 Binary files a/login-error.png and /dev/null differ diff --git a/manage-task-new.ps1 b/manage-task-new.ps1 index 8fc2b1b..ce134a4 100644 --- a/manage-task-new.ps1 +++ b/manage-task-new.ps1 @@ -9,61 +9,70 @@ $scriptPath = Join-Path $currentPath "run-tests.bat" $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name # Task Configuration -$taskName = "Playwright_AutoTest" +$taskNamePrefix = "Playwright_AutoTest" $taskDesc = "Run Playwright tests daily" function Show-Menu { Clear-Host Write-Host "`n================== Playwright Test Manager ==================`n" - Write-Host "1. View Task Status" - Write-Host "2. Set Daily Task (Default: 23:00)" - Write-Host "3. Change Task Time" + Write-Host "1. View All Tasks Status" + Write-Host "2. Set Daily Task - DEV Environment (Default: 23:00)" + Write-Host "3. Set Daily Task - UAT Environment" Write-Host "4. Run Test Now" - Write-Host "5. Delete Task" + Write-Host "5. Delete Tasks" Write-Host "Q. Exit" Write-Host "`nEnter your choice (1-5, or Q to exit): " -NoNewline } function Get-TaskStatus { - $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue - if ($task) { - Write-Host "`nTask Status:" - Write-Host "Name: $taskName" - Write-Host "State: $($task.State)" + $environments = @("Dev", "UAT") + $found = $false + + Write-Host "`nTasks Status:" + foreach ($env in $environments) { + $taskName = "${taskNamePrefix}_$env" + $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue - $trigger = $task.Triggers[0] - if ($trigger) { - Write-Host "Schedule: Daily at $($trigger.StartBoundary.Split('T')[1].Substring(0,5))" - # 安全地处理 NextRunTime - try { - if ($task.NextRunTime) { - Write-Host "Next Run: $($task.NextRunTime.ToString('yyyy-MM-dd HH:mm'))" - } else { - Write-Host "Next Run: Not yet scheduled" + if ($task) { + $found = $true + Write-Host "`n$env Environment Task:" + Write-Host "Name: $taskName" + Write-Host "State: $($task.State)" + + $trigger = $task.Triggers[0] + if ($trigger) { + Write-Host "Schedule: Daily at $($trigger.StartBoundary.Split('T')[1].Substring(0,5))" + try { + if ($task.NextRunTime) { + Write-Host "Next Run: $($task.NextRunTime.ToString('yyyy-MM-dd HH:mm'))" + } else { + Write-Host "Next Run: Not yet scheduled" + } + } catch { + Write-Host "Next Run: Not available" } - } catch { - Write-Host "Next Run: Not available" } - } else { - Write-Host "Warning: No trigger found. Task needs to be reconfigured." + + Write-Host "Last Result: $($task.LastTaskResult)" + Write-Host "Working Directory: $($task.Actions[0].WorkingDirectory)" + Write-Host "Run As User: $($task.Principal.UserId)" } - - Write-Host "Last Result: $($task.LastTaskResult)" - Write-Host "Working Directory: $($task.Actions[0].WorkingDirectory)" - Write-Host "Run As User: $($task.Principal.UserId)" - Write-Host "Run Level: $($task.Principal.RunLevel)" - } else { - Write-Host "`nNo task found. Please set up a task using option 2." + } + + if (-not $found) { + Write-Host "`nNo tasks found. Please set up tasks using options 2 or 3." } Pause-Script } function Set-DailyTask { param ( - [string]$runTime = "23:00" + [string]$runTime = "23:00", + [string]$environment ) - Write-Host "`nSetting up daily task..." + $taskName = "${taskNamePrefix}_$environment" + Write-Host "`nSetting up daily task for $environment environment..." $scriptPath = Join-Path $PSScriptRoot "run-tests.bat" if (-not (Test-Path $scriptPath)) { @@ -81,8 +90,9 @@ function Set-DailyTask { } try { - # 创建任务操作 - $action = New-ScheduledTaskAction -Execute $scriptPath -WorkingDirectory $PSScriptRoot + # 创建任务操作 - 添加环境参数 + $envArg = $environment.ToLower() + $action = New-ScheduledTaskAction -Execute $scriptPath -WorkingDirectory $PSScriptRoot -Argument $envArg if (-not $action) { throw "Failed to create task action" } @@ -127,7 +137,7 @@ function Set-DailyTask { } else { # 创建新任务 $result = Register-ScheduledTask -TaskName $taskName ` - -Description $taskDesc ` + -Description "$taskDesc ($environment Environment)" ` -Action $action ` -Trigger $trigger ` -Settings $settings ` @@ -145,6 +155,7 @@ function Set-DailyTask { if ($updatedTask) { Write-Host "`nTask Details:" Write-Host "Task Name: $taskName" + Write-Host "Environment: $environment" Write-Host "Run Time: Daily at $runTime" if ($updatedTask.NextRunTime) { Write-Host "Next Run: $($updatedTask.NextRunTime.ToString('yyyy-MM-dd HH:mm'))" @@ -166,10 +177,25 @@ function Set-DailyTask { } function Update-TaskTime { + Write-Host "`nSelect environment:" + Write-Host "1. Development (dev)" + Write-Host "2. UAT" + Write-Host "`nEnter your choice (1-2): " -NoNewline + + $envChoice = Read-Host + $environment = switch ($envChoice) { + "1" { "dev" } + "2" { "uat" } + default { + Write-Host "`nInvalid choice. Using default (dev)" + "dev" + } + } + Write-Host "`nEnter new time (HH:mm, e.g. 02:00): " -NoNewline $newTime = Read-Host if ($newTime -match "^([01]?[0-9]|2[0-3]):[0-5][0-9]$") { - Set-DailyTask -runTime $newTime + Set-DailyTask -runTime $newTime -environment $environment } else { Write-Host "`nInvalid time format" Pause-Script @@ -177,11 +203,26 @@ function Update-TaskTime { } function Start-TestNow { - Write-Host "`nStarting test..." + Write-Host "`nSelect environment:" + Write-Host "1. Development (dev)" + Write-Host "2. UAT" + Write-Host "`nEnter your choice (1-2): " -NoNewline + + $envChoice = Read-Host + $env = switch ($envChoice) { + "1" { "dev" } + "2" { "uat" } + default { + Write-Host "`nInvalid choice. Using default (dev)" + "dev" + } + } + + Write-Host "`nStarting test in $env environment..." try { $scriptPath = Join-Path $PSScriptRoot "run-tests.bat" # 使用cmd直接运行,这样可以看到实时输出 - & cmd /c $scriptPath + & cmd /c $scriptPath $env Write-Host "`nTest completed" } catch { Write-Host "`nError: $_" @@ -190,16 +231,37 @@ function Start-TestNow { } function Remove-AutomationTask { - $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue - if ($task) { - try { - Unregister-ScheduledTask -TaskName $taskName -Confirm:$false - Write-Host "`nTask deleted" - } catch { - Write-Host "`nError: $_" + Write-Host "`nSelect tasks to delete:" + Write-Host "1. DEV Environment Task" + Write-Host "2. UAT Environment Task" + Write-Host "3. Both Tasks" + Write-Host "`nEnter your choice (1-3): " -NoNewline + + $choice = Read-Host + $tasksToDelete = switch ($choice) { + "1" { @("Dev") } + "2" { @("UAT") } + "3" { @("Dev", "UAT") } + default { + Write-Host "`nInvalid choice." + Pause-Script + return + } + } + + foreach ($env in $tasksToDelete) { + $taskName = "${taskNamePrefix}_$env" + $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue + if ($task) { + try { + Unregister-ScheduledTask -TaskName $taskName -Confirm:$false + Write-Host "`nTask '$taskName' deleted" + } catch { + Write-Host "`nError deleting task '$taskName': $_" + } + } else { + Write-Host "`nTask '$taskName' not found" } - } else { - Write-Host "`nNo task found" } Pause-Script } @@ -216,8 +278,17 @@ do { switch ($choice.ToUpper()) { "1" { Get-TaskStatus } - "2" { Set-DailyTask } - "3" { Update-TaskTime } + "2" { Set-DailyTask -environment "Dev" } + "3" { + Write-Host "`nEnter time for UAT task (HH:mm, e.g. 02:00): " -NoNewline + $uatTime = Read-Host + if ($uatTime -match "^([01]?[0-9]|2[0-3]):[0-5][0-9]$") { + Set-DailyTask -runTime $uatTime -environment "UAT" + } else { + Write-Host "`nInvalid time format" + Pause-Script + } + } "4" { Start-TestNow } "5" { Remove-AutomationTask } "Q" { exit } diff --git a/playwright.config.js b/playwright.config.js index 868c75b..feb164f 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -2,6 +2,18 @@ const {defineConfig, devices} = require('@playwright/test'); const testLifecycle = require('./src/hooks/testLifecycle'); +/** + * Read environment variables and process them + */ +function getBaseUrl() { + const baseUrl = process.env.BASE_URL; + if (!baseUrl) { + throw new Error('BASE_URL environment variable is not set'); + } + // 确保基础URL不包含hash和query参数 + return baseUrl.split('#')[0].split('?')[0]; +} + /** * @see https://playwright.dev/docs/test-configuration * @type {import('@playwright/test').PlaywrightTestConfig} @@ -25,7 +37,7 @@ const config = { /* 共享设置 */ use: { /* 基础URL */ - baseURL: process.env.BASE_URL, + baseURL: getBaseUrl(), /* 收集测试追踪信息 */ trace: 'on-first-retry', /* 自动截图 */ diff --git a/run-tests.bat b/run-tests.bat index cbeae5a..922b2fd 100644 --- a/run-tests.bat +++ b/run-tests.bat @@ -5,9 +5,19 @@ cd /d %~dp0 :: 设置控制台编码为UTF-8 chcp 65001 > nul +:: 设置默认环境为dev +if "%1"=="" ( + set TEST_ENV=dev +) else ( + set TEST_ENV=%1 +) + +echo [Batch] Preparing to run tests in %TEST_ENV% environment... + :: 运行测试并发送报告 +set NODE_ENV=%TEST_ENV% node src/scripts/runTestsAndSendReport.js echo. -echo Execution completed +echo [Batch] Execution completed pause \ No newline at end of file diff --git a/src/pages/BasePage.js b/src/pages/BasePage.js index 94258dc..7bccbaa 100644 --- a/src/pages/BasePage.js +++ b/src/pages/BasePage.js @@ -76,10 +76,34 @@ class BasePage { * @param {string} url 目标URL */ async navigate(url) { + // 先进行初始导航 await this.page.goto(url, { waitUntil: 'networkidle', timeout: this.config.timeout.navigate }); + + // 获取当前URL + const currentUrl = await this.page.url(); + + // 如果原始URL包含hash或query参数,但当前URL没有,则重新导航 + if (url !== currentUrl) { + // 解析原始URL中的hash和query参数 + const urlParts = url.split('#'); + if (urlParts.length > 1) { + // 等待一下以确保页面已经初步加载 + await this.page.waitForTimeout(1000); + + // 使用evaluate来设置完整的URL + await this.page.evaluate((fullUrl) => { + window.location.href = fullUrl; + }, url); + + // 等待页面加载完成 + await this.page.waitForLoadState('networkidle', { + timeout: this.config.timeout.navigate + }); + } + } } /** diff --git a/src/pages/LongiLoginPage.js b/src/pages/LongiLoginPage.js index 9685237..3ffc8a3 100644 --- a/src/pages/LongiLoginPage.js +++ b/src/pages/LongiLoginPage.js @@ -51,8 +51,12 @@ class LongiLoginPage extends BasePage { * 导航到登录页面 */ async navigateToLoginPage() { - console.log('当前使用的 BASE_URL:', process.env.BASE_URL); - await this.navigate(process.env.BASE_URL); + const baseUrl = process.env.BASE_URL; + console.log('当前使用的 BASE_URL:', baseUrl); + + // 确保使用完整的URL,包括hash和query参数 + const fullUrl = baseUrl.includes('#') ? baseUrl : `${baseUrl}#/login?debug=ly`; + await this.navigate(fullUrl); await this.waitForPageLoad(); } diff --git a/src/scripts/runTestsAndSendReport.js b/src/scripts/runTestsAndSendReport.js index 3ab04d7..657914f 100644 --- a/src/scripts/runTestsAndSendReport.js +++ b/src/scripts/runTestsAndSendReport.js @@ -2,72 +2,122 @@ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const emailService = require('../services/EmailService'); +const dotenv = require('dotenv'); + +// 获取环境参数 +const env = process.env.TEST_ENV || 'dev'; // 默认使用dev环境 +console.log(`[Node] Starting test execution process...`); +console.log(`[Node] Current environment: ${env}`); async function runTestsAndSendReport() { let testOutput = ''; let testSuccess = false; const startTime = new Date(); + console.log(`[Node] Test started at: ${startTime.toLocaleString()}`); try { + // 根据环境设置加载对应的环境变量文件 + const envFile = `.env.${env}`; + console.log(`[Node] Loading environment file: ${envFile}`); + + // 检查环境文件是否存在 + if (!fs.existsSync(envFile)) { + console.error(`[Node] Error: Environment file ${envFile} not found!`); + throw new Error(`Environment file ${envFile} not found`); + } + console.log(`[Node] Environment file found successfully`); + + // 加载环境变量 + dotenv.config({ path: envFile }); + console.log(`[Node] Environment variables loaded successfully`); + // 运行测试并捕获输出 - console.log('开始执行测试...'); - testOutput = execSync('npx playwright test', { encoding: 'utf8' }); + console.log('[Node] =========================================='); + console.log('[Node] Starting Playwright test execution...'); + console.log('[Node] =========================================='); + + testOutput = execSync('npx playwright test', { + encoding: 'utf8', + env: { + ...process.env, + NODE_ENV: env + }, + stdio: 'inherit' // 这将允许实时显示测试输出 + }); + testSuccess = true; - console.log('测试执行完成'); + console.log('[Node] =========================================='); + console.log('[Node] Test execution completed successfully'); + console.log('[Node] =========================================='); } catch (error) { + console.log('[Node] =========================================='); + console.error('[Node] Test execution failed with error:'); + console.error(error.message); + console.log('[Node] =========================================='); testOutput = error.output ? error.output.join('\n') : error.message; - console.error('测试执行失败:', testOutput); } const endTime = new Date(); const duration = (endTime - startTime) / 1000 / 60; // 转换为分钟 + console.log(`[Node] Test duration: ${duration.toFixed(2)} minutes`); // 读取性能报告 + console.log('[Node] Attempting to read performance report...'); let performanceData = ''; try { const reportPath = path.join(process.cwd(), 'test-results', 'performance-report.html'); if (fs.existsSync(reportPath)) { performanceData = fs.readFileSync(reportPath, 'utf8'); + console.log('[Node] Performance report read successfully'); + } else { + console.log('[Node] No performance report found'); } } catch (error) { - console.error('读取性能报告失败:', error); + console.error('[Node] Failed to read performance report:', error.message); } // 构建邮件内容 + console.log('[Node] Preparing email report...'); const emailHtml = ` -

Playwright 自动化测试报告

+

Playwright 自动化测试报告 (${env}环境)

测试结果: ${testSuccess ? '成功 ✅' : '失败 ❌'}

+

执行环境: ${env}

执行时间: ${startTime.toLocaleString()}

持续时间: ${duration.toFixed(2)} 分钟

-

测试输出

-
-${testOutput}
-        
- ${performanceData ? '

性能报告

' + performanceData : ''} `; // 发送邮件 try { - console.log('正在发送测试报告邮件...'); + console.log('[Node] =========================================='); + console.log('[Node] Sending test report email...'); const result = await emailService.sendMail({ to: 'dengqichen@iscmtech.com', - subject: `[${testSuccess ? '成功' : '失败'}] Playwright自动化测试报告 - ${startTime.toLocaleDateString()}`, + subject: `[${testSuccess ? '成功' : '失败'}] Playwright自动化测试报告 (${env}) - ${startTime.toLocaleDateString()}`, html: emailHtml }); if (result.success) { - console.log('测试报告邮件发送成功'); + console.log('[Node] Test report email sent successfully'); } else { - console.error('测试报告邮件发送失败:', result.error); + console.error('[Node] Failed to send test report email:', result.error); } + console.log('[Node] =========================================='); } catch (error) { - console.error('发送邮件时发生错误:', error); + console.log('[Node] =========================================='); + console.error('[Node] Error sending email:', error.message); + console.log('[Node] =========================================='); } } // 执行脚本 -runTestsAndSendReport(); \ No newline at end of file +console.log('[Node] =========================================='); +console.log('[Node] Starting test automation process...'); +console.log('[Node] =========================================='); +runTestsAndSendReport().catch(error => { + console.error('[Node] Fatal error:', error); + process.exit(1); +}); \ No newline at end of file