You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang-net/frontend/e2e/smoke-iis.spec.ts

286 lines
11 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 管理后台冒烟测试 — IIS部署环境
*
* 运行方式:
* cd frontend
* npx playwright test e2e/smoke-iis.spec.ts --project=chromium
*
* 断言标准:严格检查数据内容,禁止只检查元素存在
*/
import { test, expect, type Page } from '@playwright/test'
// === 登录辅助函数 ===
async function login(page: Page) {
// 直接用完整URL不依赖baseURL
await page.goto('http://127.0.0.1/admin/login')
await page.waitForLoadState('networkidle')
// 等待登录表单渲染
await page.waitForSelector('input', { timeout: 10000 })
// 用CSS选择器定位第一个input是用户名第二个是密码
const inputs = page.locator('input')
await inputs.nth(0).fill('admin')
await inputs.nth(1).fill('admin123')
await page.locator('button').last().click()
await page.waitForURL(/\/(dashboard|admin\/?$)/, { timeout: 15000 })
}
// === 辅助:断言文本不为空且非占位符 ===
function assertValidValue(text: string | null, label: string) {
expect(text, `${label} 不应为null`).toBeTruthy()
const trimmed = text!.trim()
expect(trimmed, `${label} 不应为空字符串`).not.toBe('')
expect(trimmed, `${label} 不应为占位符"-"`).not.toBe('-')
expect(trimmed, `${label} 不应为占位符"--"`).not.toBe('--')
expect(trimmed, `${label} 不应为"undefined"`).not.toBe('undefined')
expect(trimmed, `${label} 不应为"null"`).not.toBe('null')
}
// ========================
// 登录测试
// ========================
test.describe('登录', () => {
test('登录成功跳转到仪表盘', async ({ page }) => {
await login(page)
// 断言URL包含dashboard
expect(page.url()).toMatch(/dashboard/)
// 断言:侧边栏有"仪表盘"菜单项
await expect(page.getByRole('menuitem', { name: '仪表盘' })).toBeVisible()
})
})
// ========================
// 仪表盘
// ========================
test.describe('仪表盘', () => {
test.beforeEach(async ({ page }) => {
await login(page)
// 确保在仪表盘页面
if (!page.url().includes('dashboard')) {
await page.goto('http://127.0.0.1/admin/dashboard')
}
// 等待数据加载
await page.waitForTimeout(2000)
})
test('统计卡片有数值', async ({ page }) => {
// 找到所有卡片区域,检查数值部分不为空
const cards = page.locator('.el-card')
const cardCount = await cards.count()
expect(cardCount, '仪表盘应该有统计卡片').toBeGreaterThan(0)
// 检查每个卡片内的数值区域
for (let i = 0; i < cardCount; i++) {
const card = cards.nth(i)
// 卡片内的数值通常在较大的字体div中
const valueEl = card.locator('div').filter({ hasText: /^\d/ }).first()
if (await valueEl.isVisible()) {
const text = await valueEl.textContent()
// 数值应该包含数字
expect(text, `${i + 1}个卡片应该有数值`).toMatch(/\d/)
}
}
})
test('采集服务状态显示正常', async ({ page }) => {
// 检查采集服务状态区域
const statusArea = page.locator('text=采集服务')
if (await statusArea.isVisible()) {
// 状态文本不应为空
const statusText = await page.locator('.el-tag').first().textContent()
assertValidValue(statusText, '采集服务状态')
}
})
})
// ========================
// 产量报表
// ========================
test.describe('产量报表', () => {
test.beforeEach(async ({ page }) => {
await login(page)
await page.goto('http://127.0.0.1/admin/production')
// 等待页面数据加载
await page.waitForTimeout(2000)
})
test('默认加载今天数据', async ({ page }) => {
// 检查日期选择器显示今天的日期
const today = new Date()
const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
const dateInput = page.locator('input[placeholder]').first()
const inputValue = await dateInput.inputValue()
expect(inputValue, `日期选择器应显示今天 ${dateStr}`).toContain(dateStr.slice(0, 7)) // 至少年月匹配
// 检查表格有数据
const rows = page.locator('.el-table__body-wrapper .el-table__row')
const rowCount = await rows.count()
expect(rowCount, '产量报表应默认有数据').toBeGreaterThan(0)
})
test('汇总卡片有数值', async ({ page }) => {
// 检查4个卡片总产量、运行机床、切削总时、平均产量
const cardLabels = ['总产量', '运行机床', '切削总时', '平均产量']
for (const label of cardLabels) {
// 找到标签文本所在的卡片
const labelEl = page.locator(`text=${label}`).first()
await expect(labelEl, `"${label}"标签应可见`).toBeVisible()
// 同一个卡片内的数值紧跟标签的大字div
const card = labelEl.locator('..').locator('..')
const valueDiv = card.locator('div').last()
const valueText = await valueDiv.textContent()
assertValidValue(valueText, `"${label}"的数值`)
}
})
test('表格数据列有值', async ({ page }) => {
const rows = page.locator('.el-table__body-wrapper .el-table__row')
const rowCount = await rows.count()
expect(rowCount, '表格应有数据行').toBeGreaterThan(0)
// 检查第一行的关键列
const firstRow = rows.first()
const cells = firstRow.locator('td .cell')
// 日期列第1列
const dateText = await cells.nth(0).textContent()
assertValidValue(dateText, '表格第1行日期列')
// 机床名列第2列
const machineText = await cells.nth(1).textContent()
assertValidValue(machineText, '表格第1行机床名列')
// 产量列第4列
const qtyText = await cells.nth(3).textContent()
assertValidValue(qtyText, '表格第1行产量列')
})
test('车间下拉框有选项且筛选生效', async ({ page }) => {
// 1. 记录当前表格行数
const rowsBefore = page.locator('.el-table__body-wrapper .el-table__row')
const countBefore = await rowsBefore.count()
// 2. 点击车间下拉框展开点击外层wrapper
const workshopSelect = page.locator('.el-form-item').filter({ hasText: '车间' }).locator('.el-select')
await workshopSelect.click()
await page.waitForTimeout(500)
// 3. 断言:下拉列表中有选项
const options = page.locator('.el-select-dropdown__item:visible')
await expect(options.first(), '车间下拉框应有选项').toBeVisible()
const optionCount = await options.count()
expect(optionCount, '车间下拉框选项数量应>=1').toBeGreaterThanOrEqual(1)
// 4. 选择第一个选项
await options.first().click()
await page.waitForTimeout(300)
// 5. 点击查询按钮
await page.getByRole('button', { name: '查询' }).click()
await page.waitForTimeout(2000)
// 6. 断言:表格有数据(筛选后仍应有数据)
const rowsAfter = page.locator('.el-table__body-wrapper .el-table__row')
const countAfter = await rowsAfter.count()
expect(countAfter, '筛选后表格应有数据').toBeGreaterThan(0)
})
test('机床下拉框有选项', async ({ page }) => {
const machineSelect = page.locator('.el-form-item').filter({ hasText: '机床' }).locator('.el-select')
await machineSelect.click()
await page.waitForTimeout(500)
const options = page.locator('.el-select-dropdown__item:visible')
await expect(options.first(), '机床下拉框应有选项').toBeVisible()
const count = await options.count()
expect(count, '机床下拉框选项数量应>=1').toBeGreaterThanOrEqual(1)
// 关闭下拉框
await page.keyboard.press('Escape')
})
test('工人下拉框有选项', async ({ page }) => {
const workerSelect = page.locator('.el-form-item').filter({ hasText: '工人' }).locator('.el-select')
await workerSelect.click()
await page.waitForTimeout(500)
const options = page.locator('.el-select-dropdown__item:visible')
await expect(options.first(), '工人下拉框应有选项').toBeVisible()
const count = await options.count()
expect(count, '工人下拉框选项数量应>=1').toBeGreaterThanOrEqual(1)
// 关闭下拉框
await page.keyboard.press('Escape')
})
test('重置按钮清空筛选', async ({ page }) => {
// 先选择一个车间
const workshopSelect = page.locator('.el-form-item').filter({ hasText: '车间' }).locator('.el-select')
await workshopSelect.click()
await page.waitForTimeout(500)
await page.locator('.el-select-dropdown__item:visible').first().click()
await page.waitForTimeout(300)
// 点重置
await page.getByRole('button', { name: '重置' }).click()
await page.waitForTimeout(1000)
// 断言:车间下拉框恢复占位符
const placeholder = workshopSelect.locator('.el-select__placeholder')
const placeholderText = await placeholder.textContent()
expect(placeholderText, '重置后车间下拉框应显示占位符').toContain('请选择')
})
test('修正弹窗能打开', async ({ page }) => {
// 点击第一行的"修正"按钮
const adjustBtn = page.locator('.el-table__body-wrapper .el-table__row').first().getByRole('button', { name: '修正' })
await adjustBtn.click()
await page.waitForTimeout(500)
// 断言:弹窗出现,标题包含"修正"
const dialog = page.locator('.el-dialog:visible')
await expect(dialog, '修正弹窗应可见').toBeVisible()
const title = await dialog.locator('.el-dialog__title').textContent()
expect(title, '弹窗标题应包含"修正"').toContain('修正')
// 断言:当前产量输入框有值
const input = dialog.locator('input').first()
const inputValue = await input.inputValue()
expect(inputValue, '当前产量输入框应有初始值').toBeTruthy()
// 关闭弹窗
await dialog.locator('.el-dialog__headerbtn').click()
})
test('修正历史弹窗能打开', async ({ page }) => {
const historyBtn = page.locator('.el-table__body-wrapper .el-table__row').first().getByRole('button', { name: '修正历史' })
await historyBtn.click()
await page.waitForTimeout(500)
// 断言:弹窗出现
const dialog = page.locator('.el-dialog:visible')
await expect(dialog, '修正历史弹窗应可见').toBeVisible()
const title = await dialog.locator('.el-dialog__title').textContent()
expect(title, '弹窗标题应包含"修正历史"').toContain('修正历史')
// 关闭弹窗
await dialog.locator('.el-dialog__headerbtn').click()
})
})
// ========================
// 设备管理
// ========================
test.describe('设备管理', () => {
test.beforeEach(async ({ page }) => {
await login(page)
await page.goto('http://127.0.0.1/admin/machine')
await page.waitForTimeout(2000)
})
test('机床列表有数据', async ({ page }) => {
const rows = page.locator('.el-table__body-wrapper .el-table__row')
const rowCount = await rows.count()
expect(rowCount, '设备管理表格应有数据行').toBeGreaterThan(0)
})
})