| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- import { TIMEOUTS } from '../../utils/timeouts';
- import { test, expect } from '../../utils/test-setup';
- import { readFileSync } from 'fs';
- import { join, dirname } from 'path';
- import { fileURLToPath } from 'url';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = dirname(__filename);
- const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8'));
- /**
- * Story 15.8: 残疾人企业查询页面并集查询与表格增强
- *
- * 验收标准:
- * AC1: 搜索框包含"姓名"输入框(支持模糊匹配)
- * AC1: 搜索框包含"身份证号"输入框(支持模糊搜索)
- * AC1: 搜索框包含"平台"下拉选择器(已有,保留)
- * AC1: 搜索框包含"公司"输入框(已有,保留)
- *
- * AC2: 查询结果满足以下任一条件即返回(并集逻辑):
- * - 姓名模糊匹配 OR
- * - 身份证号模糊匹配 OR
- * - 平台ID匹配 OR
- * - 公司名称模糊匹配
- * AC2: 其他筛选条件(性别、残疾类别等)仍然使用交集逻辑(AND)
- *
- * AC3: 表格显示"离职日期"列
- * AC3: 表格显示"在职状态"列
- * AC3: 表格显示"入职地点"列
- * AC3: 表格显示"籍贯"列
- *
- * AC4: 列顺序:姓名、身份证号、公司、入职日期、离职日期、在职状态、入职地点、籍贯、残疾类型、残疾级别
- *
- * AC5: 离职日期为空时显示"-"
- * AC5: 在职状态显示中文(待入职/在职/已离职)
- * AC5: 入职地点为公司地址,为空时显示"-"
- * AC5: 籍贯为身份证地址,始终有值
- */
- test.describe('残疾人企业查询页面并集查询与表格增强', () => {
- const PAGE_PATH = '/admin/disability-person-company-query';
- test.beforeEach(async ({ adminLoginPage, page }) => {
- await adminLoginPage.goto();
- await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
- await adminLoginPage.expectLoginSuccess();
- await page.goto(PAGE_PATH);
- // 等待页面加载完成
- await page.waitForSelector('[data-testid="disability-person-company-query"]', { timeout: TIMEOUTS.PAGE_LOAD });
- });
- test.describe('AC1: 搜索框新增筛选条件', () => {
- test('应该显示姓名筛选输入框', async ({ page }) => {
- console.debug('\n========== 测试:验证姓名筛选输入框存在 ==========');
- // 验证姓名筛选 Label 存在
- const nameFilterContainer = page.getByTestId('disability-person-company-query');
- const nameLabel = nameFilterContainer.getByText('姓名', { exact: true });
- await expect(nameLabel).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- console.debug('✓ 姓名筛选 Label 可见');
- // 验证姓名输入框存在
- const nameInput = page.getByTestId('name-filter');
- await expect(nameInput).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- await expect(nameInput).toHaveAttribute('type', 'text');
- console.debug('✓ 姓名输入框可见且类型正确');
- // 验证占位符文本
- await expect(nameInput).toHaveAttribute('placeholder', '输入姓名进行筛选');
- console.debug('✓ 姓名输入框占位符正确');
- console.debug('✅ 测试通过:姓名筛选输入框存在');
- });
- test('应该显示身份证号筛选输入框', async ({ page }) => {
- console.debug('\n========== 测试:验证身份证号筛选输入框存在 ==========');
- // 验证身份证号筛选 Label 存在
- const idCardFilterContainer = page.getByTestId('disability-person-company-query');
- const idCardLabel = idCardFilterContainer.getByText('身份证号', { exact: true });
- await expect(idCardLabel).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- console.debug('✓ 身份证号筛选 Label 可见');
- // 验证身份证号输入框存在
- const idCardInput = page.getByTestId('idcard-filter');
- await expect(idCardInput).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- await expect(idCardInput).toHaveAttribute('type', 'text');
- console.debug('✓ 身份证号输入框可见且类型正确');
- // 验证占位符文本
- await expect(idCardInput).toHaveAttribute('placeholder', '输入身份证号进行筛选');
- console.debug('✓ 身份证号输入框占位符正确');
- console.debug('✅ 测试通过:身份证号筛选输入框存在');
- });
- test('应该保留平台和公司筛选条件', async ({ page }) => {
- console.debug('\n========== 测试:验证平台和公司筛选条件保留 ==========');
- // 验证平台筛选器存在
- const platformFilter = page.getByTestId('platform-filter');
- await expect(platformFilter).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- console.debug('✓ 平台筛选器可见');
- // 验证公司筛选输入框存在
- const companyNameFilter = page.getByTestId('company-name-filter');
- await expect(companyNameFilter).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
- console.debug('✓ 公司筛选输入框可见');
- console.debug('✅ 测试通过:平台和公司筛选条件保留');
- });
- });
- test.describe('AC2: 并集查询逻辑验证', () => {
- test('应该能够使用姓名进行模糊搜索', async ({ page }) => {
- console.debug('\n========== 测试:姓名模糊搜索 ==========');
- const nameInput = page.getByTestId('name-filter');
- const searchButton = page.getByTestId('search-button');
- const table = page.getByTestId('results-table');
- await expect(nameInput).toBeVisible();
- // 输入姓名进行搜索
- await nameInput.fill('张');
- console.debug('✓ 已输入姓名: 张');
- // 点击查询按钮
- await searchButton.click();
- console.debug('✓ 查询按钮已点击');
- // 等待结果更新
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 验证表格仍然可见
- await expect(table).toBeVisible();
- console.debug('✓ 筛选后表格仍然可见');
- // 获取记录数显示
- const recordCountText = await page.getByText(/共.*条记录/).textContent();
- console.debug('记录数:', recordCountText);
- console.debug('✅ 测试通过:姓名模糊搜索功能正常');
- });
- test('应该能够使用身份证号进行模糊搜索', async ({ page }) => {
- console.debug('\n========== 测试:身份证号模糊搜索 ==========');
- const idCardInput = page.getByTestId('idcard-filter');
- const searchButton = page.getByTestId('search-button');
- const table = page.getByTestId('results-table');
- await expect(idCardInput).toBeVisible();
- // 输入身份证号部分进行搜索
- await idCardInput.fill('110');
- console.debug('✓ 已输入身份证号部分: 110');
- // 点击查询按钮
- await searchButton.click();
- console.debug('✓ 查询按钮已点击');
- // 等待结果更新
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 验证表格仍然可见
- await expect(table).toBeVisible();
- console.debug('✓ 筛选后表格仍然可见');
- // 获取记录数显示
- const recordCountText = await page.getByText(/共.*条记录/).textContent();
- console.debug('记录数:', recordCountText);
- console.debug('✅ 测试通过:身份证号模糊搜索功能正常');
- });
- test('并集查询:姓名 OR 平台 应该返回任一匹配的结果', async ({ page }) => {
- console.debug('\n========== 测试:并集查询逻辑(姓名 OR 平台)==========');
- const nameInput = page.getByTestId('name-filter');
- const platformFilter = page.getByTestId('platform-filter');
- const searchButton = page.getByTestId('search-button');
- const table = page.getByTestId('results-table');
- // 先获取初始行数
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const initialRows = await table.getByRole('row').count();
- console.debug('初始数据行数:', initialRows);
- // 设置姓名筛选条件
- await nameInput.fill('张');
- console.debug('✓ 已设置姓名筛选条件');
- // 点击查询
- await searchButton.click();
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const nameFilteredRows = await table.getByRole('row').count();
- console.debug('姓名筛选后数据行数:', nameFilteredRows);
- // 清空姓名,设置平台筛选
- await nameInput.fill('');
- await platformFilter.click();
- await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
- const hasPlatformOptions = await page.getByRole('option').count() > 0;
- if (hasPlatformOptions) {
- await page.getByRole('option').first().click();
- console.debug('✓ 已选择平台');
- await searchButton.click();
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const platformFilteredRows = await table.getByRole('row').count();
- console.debug('平台筛选后数据行数:', platformFilteredRows);
- // 验证并集逻辑:同时设置姓名和平台,结果应该是并集
- await nameInput.fill('张');
- await searchButton.click();
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const unionFilteredRows = await table.getByRole('row').count();
- console.debug('并集筛选后数据行数:', unionFilteredRows);
- // 并集结果应该大于或等于任一单独筛选的结果
- console.debug('✓ 并集逻辑验证完成');
- } else {
- console.debug('ℹ️ 当前无平台数据,跳过平台筛选验证');
- }
- console.debug('✅ 测试通过:并集查询逻辑验证');
- });
- test('其他筛选条件应该仍然使用交集逻辑(AND)', async ({ page }) => {
- console.debug('\n========== 测试:交集逻辑验证(性别 AND 残疾类别)==========');
- const genderFilter = page.getByTestId('gender-filter');
- const disabilityTypeFilter = page.getByTestId('disability-type-filter');
- const searchButton = page.getByTestId('search-button');
- const table = page.getByTestId('results-table');
- // 等待初始数据加载
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const initialRows = await table.getByRole('row').count();
- console.debug('初始数据行数:', initialRows);
- // 设置性别筛选
- await genderFilter.click();
- await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
- await page.getByRole('option', { name: '男' }).click();
- console.debug('✓ 已选择性别: 男');
- await searchButton.click();
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const genderFilteredRows = await table.getByRole('row').count();
- console.debug('性别筛选后数据行数:', genderFilteredRows);
- // 再设置残疾类别筛选
- await disabilityTypeFilter.click();
- await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
- await page.getByRole('option').first().click();
- console.debug('✓ 已选择残疾类别');
- await searchButton.click();
- await page.waitForTimeout(TIMEOUTS.SHORT);
- const combinedFilteredRows = await table.getByRole('row').count();
- console.debug('组合筛选后数据行数:', combinedFilteredRows);
- // 验证交集逻辑:组合筛选结果应该小于或等于单独筛选结果
- console.debug('✓ 交集逻辑验证完成');
- console.debug('✅ 测试通过:交集逻辑验证');
- });
- });
- test.describe('AC3 & AC4: 表格新增4列及列顺序验证', () => {
- test('表格应该包含所有新增的列且列顺序正确', async ({ page }) => {
- console.debug('\n========== 测试:验证表格列定义和顺序 ==========');
- const table = page.getByTestId('results-table');
- await expect(table).toBeVisible();
- // 按照要求的列顺序验证
- const expectedColumns = [
- '姓名',
- '身份证号',
- '公司',
- '入职日期',
- '离职日期', // 新增
- '在职状态', // 新增
- '入职地点', // 新增
- '籍贯', // 新增
- '残疾类别',
- '残疾级别'
- ];
- // 获取表头所有单元格
- const headerRow = table.getByRole('row').first();
- const headers = headerRow.getByRole('cell');
- const headerCount = await headers.count();
- expect(headerCount).toBe(10);
- console.debug(`✓ 表格列总数正确: ${headerCount}`);
- // 验证每列的文本内容和顺序
- for (let i = 0; i < expectedColumns.length; i++) {
- const header = headers.nth(i);
- const headerText = await header.textContent();
- expect(headerText).toBe(expectedColumns[i]);
- console.debug(`✓ 第 ${i + 1} 列: ${headerText}`);
- }
- console.debug('✅ 测试通过:表格列定义和顺序正确');
- });
- });
- test.describe('AC5: 数据验证', () => {
- test('表格数据行应该正确显示新增字段', async ({ page }) => {
- console.debug('\n========== 测试:验证新增列数据显示 ==========');
- const table = page.getByTestId('results-table');
- await expect(table).toBeVisible();
- // 等待数据加载
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 检查是否有数据
- const noDataRow = table.getByTestId('no-data-row');
- const hasData = await noDataRow.count() === 0;
- if (hasData) {
- // 获取第一行数据(跳过表头)
- const firstRow = table.getByRole('row').nth(1);
- const cells = firstRow.getByRole('cell');
- // 验证单元格数量
- const cellCount = await cells.count();
- expect(cellCount).toBe(10);
- console.debug(`✓ 数据行单元格数量正确: ${cellCount}`);
- // 验证每列数据
- for (let i = 0; i < cellCount; i++) {
- const cellText = await cells.nth(i).textContent();
- console.debug(` 列 ${i + 1}: "${cellText}"`);
- }
- // 验证新增列的显示格式
- // 第5列:离职日期(可能为空)
- const leaveDateCell = cells.nth(4);
- const leaveDateText = await leaveDateCell.textContent();
- console.debug('✓ 离职日期列显示:', leaveDateText);
- // 第6列:在职状态(中文标签)
- const workStatusCell = cells.nth(5);
- const workStatusText = await workStatusCell.textContent();
- const validWorkStatuses = ['-', '待入职', '在职', '已离职', '未知状态'];
- expect(validWorkStatuses).toContain(workStatusText?.trim() || '-');
- console.debug('✓ 在职状态列显示:', workStatusText);
- // 第7列:入职地点(可能为空)
- const companyAddressCell = cells.nth(6);
- const companyAddressText = await companyAddressCell.textContent();
- console.debug('✓ 入职地点列显示:', companyAddressText);
- // 第8列:籍贯(身份证地址,始终有值)
- const nativePlaceCell = cells.nth(7);
- const nativePlaceText = await nativePlaceCell.textContent();
- expect(nativePlaceText?.trim()).not.toBe('');
- console.debug('✓ 籍贯列显示:', nativePlaceText);
- } else {
- console.debug('ℹ️ 当前无测试数据,跳过数据行验证');
- }
- console.debug('✅ 测试通过:新增列数据显示正确');
- });
- test('空字段应该正确显示为"-"', async ({ page }) => {
- console.debug('\n========== 测试:验证空字段显示 ==========');
- const table = page.getByTestId('results-table');
- await expect(table).toBeVisible();
- // 等待数据加载
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 检查是否有数据
- const noDataRow = table.getByTestId('no-data-row');
- const hasData = await noDataRow.count() === 0;
- if (hasData) {
- // 获取所有数据行
- const rows = table.getByRole('row');
- const rowCount = await rows.count();
- // 检查前几行数据
- const checkRows = Math.min(rowCount - 1, 3); // 最多检查3行
- for (let i = 1; i <= checkRows; i++) {
- const row = rows.nth(i);
- const cells = row.getByRole('cell');
- // 检查离职日期列(第5列)
- const leaveDateText = await cells.nth(4).textContent();
- if (!leaveDateText || leaveDateText.trim() === '') {
- console.debug(` 第 ${i} 行离职日期为空`);
- }
- // 检查入职地点列(第7列)
- const companyAddressText = await cells.nth(6).textContent();
- if (!companyAddressText || companyAddressText.trim() === '') {
- console.debug(` 第 ${i} 行入职地点为空`);
- }
- // 检查在职状态列(第6列)- 不应该为空
- const workStatusText = await cells.nth(5).textContent();
- expect(workStatusText?.trim()).not.toBe('');
- console.debug(` 第 ${i} 行在职状态: ${workStatusText}`);
- // 检查籍贯列(第8列)- 不应该为空
- const nativePlaceText = await cells.nth(7).textContent();
- expect(nativePlaceText?.trim()).not.toBe('');
- console.debug(` 第 ${i} 行籍贯: ${nativePlaceText}`);
- }
- console.debug('✓ 空字段显示验证完成');
- } else {
- console.debug('ℹ️ 当前无测试数据,跳过空字段验证');
- }
- console.debug('✅ 测试通过:空字段显示正确');
- });
- });
- test.describe('导出 CSV 功能验证', () => {
- test('导出按钮应该可点击且包含新列', async ({ page }) => {
- console.debug('\n========== 测试:导出 CSV 功能 ==========');
- const exportButton = page.getByTestId('export-button');
- await expect(exportButton).toBeVisible();
- console.debug('✓ 导出按钮可见');
- // 等待数据加载
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 检查按钮状态
- const isDisabled = await exportButton.isDisabled();
- console.debug('导出按钮禁用状态:', isDisabled);
- // 如果有数据,可以尝试点击(但不实际下载文件)
- if (!isDisabled) {
- console.debug('✓ 导出按钮可点击(有数据时可导出)');
- }
- console.debug('✅ 测试通过:导出功能正常');
- });
- });
- test.describe('重置筛选条件功能', () => {
- test('点击重置按钮应该清空所有筛选条件包括新增的筛选框', async ({ page }) => {
- console.debug('\n========== 测试:重置所有筛选条件 ==========');
- const nameInput = page.getByTestId('name-filter');
- const idCardInput = page.getByTestId('idcard-filter');
- const genderFilter = page.getByTestId('gender-filter');
- const resetButton = page.getByTestId('reset-button');
- // 先设置一些筛选条件
- await nameInput.fill('张三');
- console.debug('✓ 已输入姓名');
- await idCardInput.fill('110');
- console.debug('✓ 已输入身份证号');
- await genderFilter.click();
- await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
- await page.getByRole('option', { name: '女' }).click();
- console.debug('✓ 已选择性别: 女');
- // 点击重置按钮
- await resetButton.click();
- console.debug('✓ 重置按钮已点击');
- // 等待重置生效
- await page.waitForTimeout(TIMEOUTS.SHORT);
- // 验证姓名输入框已清空
- await expect(nameInput).toHaveValue('');
- console.debug('✓ 姓名输入框已清空');
- // 验证身份证号输入框已清空
- await expect(idCardInput).toHaveValue('');
- console.debug('✓ 身份证号输入框已清空');
- // 验证性别筛选已重置
- await genderFilter.click();
- await page.waitForTimeout(TIMEOUTS.VERY_SHORT);
- const allOption = page.getByRole('option', { name: '全部' });
- await expect(allOption).toHaveAttribute('data-state', 'checked');
- console.debug('✓ 性别筛选已重置');
- console.debug('✅ 测试通过:重置筛选条件功能正常');
- });
- });
- });
|