|
|
/**
|
|
|
* 管理后台冒烟测试 — 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)
|
|
|
})
|
|
|
})
|