From 135718a8c39c8189d4d60a58fb7726e4490ca1e0 Mon Sep 17 00:00:00 2001 From: LingandRX Date: Mon, 10 Mar 2025 23:36:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor(tests):=20=E4=BC=98=E5=8C=96=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=AF=BC=E5=85=A5=E8=B7=AF=E5=BE=84=E5=92=8C=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E6=A1=86=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/pages/components/index.ts | 4 +- tests/pages/components/numberInput.ts | 62 ++++--- tests/pages/customer/customerAnalysisPage.ts | 23 +-- tests/pages/customer/customerDetailsPage.ts | 27 ++- tests/pages/customer/customerPage.ts | 34 ++-- .../marketing/marketingInviteGuestsPage.ts | 2 +- tests/touch/boss_cashier.spec.ts | 54 +++--- tests/touch/boss_customer.spec.ts | 166 +++++++++--------- tests/touch/boss_report.spec.ts | 2 +- tests/utils/{utils.js => utils.ts} | 30 +++- 10 files changed, 236 insertions(+), 168 deletions(-) rename tests/utils/{utils.js => utils.ts} (86%) diff --git a/tests/pages/components/index.ts b/tests/pages/components/index.ts index c4545ba..0107cfa 100644 --- a/tests/pages/components/index.ts +++ b/tests/pages/components/index.ts @@ -1,4 +1,4 @@ -import { NumberInput } from '@/pages/components/numberInput'; -import { PopupContent } from '@/pages/components/popupContent'; +import { NumberInput } from './numberInput'; +import { PopupContent } from './popupContent'; export { NumberInput, PopupContent }; diff --git a/tests/pages/components/numberInput.ts b/tests/pages/components/numberInput.ts index 8e5ef2f..2c1eadd 100644 --- a/tests/pages/components/numberInput.ts +++ b/tests/pages/components/numberInput.ts @@ -3,12 +3,13 @@ import { Locator, Page } from '@playwright/test'; export class NumberInput { private readonly page: Page; private readonly popupLocator: Locator; - private readonly inputLocator: Locator; + private readonly inputLocators: { [key: string]: Locator } = {}; private readonly confirmButtonLocator: Locator; private readonly delButtonLocator: Locator; private readonly delAllButtonLocator: Locator; - private readonly pointInputLocator: Locator; - private readonly commonInputLocator: Locator; + // private readonly inputLocator: Locator; + // private readonly pointInputLocator: Locator; + // private readonly commonInputLocator: Locator; /** * 数字键盘组件 @@ -17,41 +18,60 @@ export class NumberInput { constructor(page: Page) { this.page = page; this.popupLocator = this.page.locator('div.popup_content'); - this.commonInputLocator = this.popupLocator.getByPlaceholder(''); - this.inputLocator = this.popupLocator.getByPlaceholder('请输入内容'); - this.pointInputLocator = this.popupLocator.getByPlaceholder('请输入积分'); + this.inputLocators = { + common: this.popupLocator.getByPlaceholder(''), + normal: this.popupLocator.getByPlaceholder('请输入内容'), + point: this.popupLocator.getByPlaceholder('请输入积分'), + }; + // this.commonInputLocator = this.popupLocator.getByPlaceholder(''); + // this.inputLocator = this.popupLocator.getByPlaceholder('请输入内容'); + // this.pointInputLocator = this.popupLocator.getByPlaceholder('请输入积分'); this.confirmButtonLocator = this.popupLocator.locator('button.sure'); this.delButtonLocator = this.popupLocator.locator('button.del'); this.delAllButtonLocator = this.popupLocator.locator('button.delAll'); } + /** + * 设置值 + * @param type 输入框类型 (common, normal, point) + * @param value 输入值 + */ + async setValue(type: 'common' | 'normal' | 'point', value: number | string): Promise { + const locator = this.inputLocators[type]; + if (!locator) { + throw new Error(`Invalid input type: ${type}`); + } + await locator.fill(value.toString()); + } + /** * 设置通用值 */ - async setCommonValue(value: number): Promise { - await this.commonInputLocator.fill(value.toString()); - } + // async setCommonValue(value: number): Promise { + // await this.commonInputLocator.fill(value.toString()); + // } /** * 设置值 */ - async setValue(value: number): Promise { - await this.inputLocator.fill(value.toString()); - } - - /** - * 设置文本 - */ - async setString(value: string): Promise { - await this.inputLocator.fill(value); - } + // async setValue(value: number): Promise { + // await this.inputLocator.fill(value.toString()); + // } /** * 设置积分值 * @param value */ - async setPointValue(value: number): Promise { - await this.pointInputLocator.fill(value.toString()); + // async setPointValue(value: number): Promise { + // await this.pointInputLocator.fill(value.toString()); + // } + + /** + * 设置文本 + */ + async setString(value: string): Promise { + // await this.inputLocator.fill(value); + await this.inputLocators.normal.fill(value); } /** diff --git a/tests/pages/customer/customerAnalysisPage.ts b/tests/pages/customer/customerAnalysisPage.ts index d7c405d..b060501 100644 --- a/tests/pages/customer/customerAnalysisPage.ts +++ b/tests/pages/customer/customerAnalysisPage.ts @@ -14,20 +14,23 @@ export class CustomerAnalysisPage { /** * 跳转到子页面 - * @param {string} subPageName - * - 项目余量分析 - * - 套餐消耗升单分析 - * - 顾客项目分析 + * @param {string} subPageName - 子页面名称,支持以下选项: + * - "项目余量分析" + * - "套餐消耗升单分析" + * - "顾客项目分析" */ gotoSubPage = async (subPageName: string) => { - if (!this.subPages.some(({ name }) => name === subPageName)) { + // 输入验证 + if (!subPageName || typeof subPageName !== 'string') { + throw new Error('Invalid subPageName parameter'); + } + + // 异常处理 + if (!this.subPages || !this.subPages.some(({ name }) => name === subPageName)) { throw new Error(`${subPageName} is not a valid sub page name`); } - const $dropDown = this.page - .locator('.top_tab .tab_item', { - hasText: '顾客分析', - }) - .locator('.ant-dropdown-link'); + + const $dropDown = this.page.locator('.top_tab .tab_item', { hasText: '顾客分析' }).locator('.ant-dropdown-link'); await $dropDown.click(); await this.page.getByRole('menuitem', { name: subPageName }).click(); diff --git a/tests/pages/customer/customerDetailsPage.ts b/tests/pages/customer/customerDetailsPage.ts index ac81ec5..0eb5e1a 100644 --- a/tests/pages/customer/customerDetailsPage.ts +++ b/tests/pages/customer/customerDetailsPage.ts @@ -8,7 +8,12 @@ export class CustomerDetailsPage { */ constructor(page: Page) { this.page = page; - this.subPages = [{ name: '基本资料' }, { name: '流水' }, { name: '动态' }, { name: '日志' }]; + this.subPages = [ + { name: '基本资料' }, + { name: '流水' }, + { name: '动态' }, + { name: '日志' } + ]; } /** @@ -20,12 +25,22 @@ export class CustomerDetailsPage { * - 日志 */ gotoSubPage = async (subPageName: string) => { - if (!this.subPages.findIndex(e => e.name === subPageName)) { + // 标准化输入字符串,去除前后空格并统一为小写 + const normalizedSubPageName = subPageName.trim().toLowerCase(); + + // 检查子页面是否存在 + if (!this.subPages.some(e => e.name.trim().toLowerCase() === normalizedSubPageName)) { throw new Error(`${subPageName} is not in the subPages list`); } - const $subPage = this.page.getByRole('tab', { name: subPageName }); - await $subPage.click(); - await expect($subPage).toHaveClass(/active/); - await this.page.waitForLoadState(); + + try { + // 获取子页面元素并点击 + const $subPage = this.page.getByRole('tab', { name: subPageName }); + await $subPage.click(); + await expect($subPage).toHaveClass(/active/); + } catch (error) { + // 捕获并抛出更明确的错误信息 + throw new Error(`Failed to navigate to ${subPageName}: ${error.message}`); + } }; } diff --git a/tests/pages/customer/customerPage.ts b/tests/pages/customer/customerPage.ts index edd6c14..13b014f 100644 --- a/tests/pages/customer/customerPage.ts +++ b/tests/pages/customer/customerPage.ts @@ -60,8 +60,14 @@ export class CustomerPage { * - 服务日志 */ gotoSubPage = async (subPageName: string) => { + // 输入验证 + if (!subPageName || typeof subPageName !== 'string' || subPageName.trim() === '') { + throw new Error('子页面名称不能为空或无效'); + } + const subPage = this.subPages.find(e => e.name === subPageName); if (!subPage) { + console.error(`子页面 ${subPageName} 不存在`); throw new Error(`子页面 ${subPageName} 不存在`); } @@ -69,18 +75,24 @@ export class CustomerPage { await $subPageTab.waitFor(); - const classAttribute = await $subPageTab.getAttribute('class', { timeout: 5000 }); - if (classAttribute && classAttribute.includes('active')) { - return; + try { + const classAttribute = await $subPageTab.getAttribute('class', { timeout: 5000 }); + if (classAttribute && classAttribute.includes('active')) { + return; + } + } catch (error) { + console.error(`获取子页面 ${subPageName} 的 class 属性超时`); + throw new Error(`获取子页面 ${subPageName} 的 class 属性超时`); + } + + try { + await $subPageTab.click(); + await expect($subPageTab).toHaveClass(/active/, { timeout: 5000 }); + await waitSpecifyApiLoad(this.page, subPage.url); + } catch (error) { + console.error(`点击子页面 ${subPageName} 或等待 API 加载失败`); + throw new Error(`点击子页面 ${subPageName} 或等待 API 加载失败`); } - - await Promise.all([ - expect(async () => { - await $subPageTab.click(); - await expect($subPageTab).toHaveClass(/active/); - }).toPass(), - waitSpecifyApiLoad(this.page, subPage.url), - ]); }; /** diff --git a/tests/pages/marketing/marketingInviteGuestsPage.ts b/tests/pages/marketing/marketingInviteGuestsPage.ts index d103706..8fd484a 100644 --- a/tests/pages/marketing/marketingInviteGuestsPage.ts +++ b/tests/pages/marketing/marketingInviteGuestsPage.ts @@ -1,5 +1,5 @@ import { type Locator, type Page } from '@playwright/test'; -import { waitSpecifyApiLoad } from '@/utils/utils.js'; +import { waitSpecifyApiLoad } from '@/utils/utils'; export class MarketingInviteGuestsPage { page: Page; diff --git a/tests/touch/boss_cashier.spec.ts b/tests/touch/boss_cashier.spec.ts index be442be..0cbbc2c 100644 --- a/tests/touch/boss_cashier.spec.ts +++ b/tests/touch/boss_cashier.spec.ts @@ -280,9 +280,17 @@ test.describe('挂单', () => { await page.getByText('已过期 / 删除单据').click(); await page.getByText('警告:已过期服务无法取单结算,请至收银台处理').waitFor(); await $slidingMenu.locator('div.item_box').first().waitFor(); - await expect($slidingMenu.locator('div.item_box').first().getByRole('button', { name: /^取\s单$/ })).not.toBeVisible(); + await expect( + $slidingMenu + .locator('div.item_box') + .first() + .getByRole('button', { name: /^取\s单$/ }), + ).not.toBeVisible(); - const $delete = $slidingMenu.locator('div.item_box').first().locator('.comment > div:nth-child(2) > .touchIcon'); + const $delete = $slidingMenu + .locator('div.item_box') + .first() + .locator('.comment > div:nth-child(2) > .touchIcon'); await $delete.click(); await page.getByPlaceholder('请输入1-100个字符备注内容').click(); await page.getByPlaceholder('请输入1-100个字符备注内容').fill('测试备注'); @@ -582,13 +590,13 @@ test.describe('收银-房态', () => { }); test('占用床位', async ({ - page, - homeNavigation, - createCustomer, - cashierRoomPage, - customerPage, - appointmentPage, - }) => { + page, + homeNavigation, + createCustomer, + cashierRoomPage, + customerPage, + appointmentPage, + }) => { const customer = createCustomer; const employee = Employees.FirstShop.Employee_6; @@ -872,13 +880,13 @@ test.describe('收银-房态', () => { test.describe('收银-开单&结算', () => { test('开单-反结算-撤单', async ({ - page, - homeNavigation, - createCustomer, - customerPage, - wasteBookBusinessRecordPage, - numberInput, - }) => { + page, + homeNavigation, + createCustomer, + customerPage, + wasteBookBusinessRecordPage, + numberInput, + }) => { // 定义一个随机单号 const randomBillNo1 = faker.helpers.fromRegExp(/1[3-9][0-9]{8}/); const randomBillNo2 = faker.helpers.fromRegExp(/1[3-9][0-9]{9}/); @@ -1179,12 +1187,12 @@ test.describe('收银-开单&结算', () => { }); test('开卡-使用卡金和赠金-充值卡金', async ({ - page, - homeNavigation, - createCustomer, - customerPage, - numberInput, - }) => { + page, + homeNavigation, + createCustomer, + customerPage, + numberInput, + }) => { const c = createCustomer; const username = c.username; const phone = c.phone; @@ -1256,7 +1264,7 @@ test.describe('收银-开单&结算', () => { await page.getByText('卡金').nth(4).click(); await page.getByRole('button', { name: '增加收款' }).click(); // 输入金额 - await numberInput.setCommonValue(100); + await numberInput.setValue('common', 100); await numberInput.confirmValue(); // 选择赠送金支付 await page.getByText('赠金').nth(2).click(); diff --git a/tests/touch/boss_customer.spec.ts b/tests/touch/boss_customer.spec.ts index bca478d..2ad8880 100644 --- a/tests/touch/boss_customer.spec.ts +++ b/tests/touch/boss_customer.spec.ts @@ -2025,7 +2025,7 @@ test.describe('顾客分配', () => { const index = titleList.findIndex(text => text === '现金总额'); // 拿取前三个顾客的现金总额 - let amountArray: number[]; + let amountArray: number[] = new Array(3); for (let i = 0; i < 3; i++) { const amountStr = await amountList.nth(i).locator('td').nth(index).innerText(); const amount = convertAmountText(amountStr).amount; @@ -2219,7 +2219,7 @@ test.describe('顾客分配', () => { test.describe('顾客分析', () => { test('查看顾客项目分析', async ({ page, homeNavigation, createCustomers, customerPage, numberInput }) => { - let customers: Customer[]; + let customers: Customer[] = new Array(2); await test.step('创建两个顾客', async () => { customers = await createCustomers(2); }); @@ -2227,38 +2227,32 @@ test.describe('顾客分析', () => { const ca = customers[0]; const cb = customers[1]; - // 获取姓名、手机号、档案号 - const usernameA = ca.username; - const phoneA = ca.phone; - const usernameB = cb.username; - const phoneB = cb.phone; - - const ProjectA1 = ProjectName.Projects.Projects_1; - const ProjectA1Quantity = 2; - const ProjectA2 = ProjectName.Projects.Projects_239; - const ProjectA2Quantity = 1; - const ProjectA3 = ProjectName.Projects.Projects_2; - const ProjectA3Quantity = 2; - const ProjectA12Quantity = 1; + const projects_a1 = { num: '100012', name: '雪肌晶纯护理', price: 300 }; + const projects_a1_quantity = 2; + const projects_a2 = { num: '100258', name: '出水芙蓉SPA水疗', price: 380 }; + const projects_a2_quantity = 1; + const projects_a3 = { num: '100013', name: '净透驻氧护理', price: 380 }; + const projects_a3_quantity = 2; + const projects_a12_quantity = 1; // 顾客A await test.step('顾客A', async () => { await homeNavigation.gotoModule('收银'); await page.getByRole('button', { name: /开\s单/ }).click(); - await customerPage.searchCustomer(phoneA); - await customerPage.selectSearchCustomer(usernameA); + await customerPage.searchCustomer(ca.phone); + await customerPage.selectSearchCustomer(ca.username); // 购买项目1-普通,2次 - await page.getByText(ProjectA1.num).click(); + await page.getByText(projects_a1.num).click(); await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectA1Quantity); + await numberInput.setValue(projects_a1_quantity); await numberInput.confirmValue(); // 点击面部 await page.getByText('面部').click(); // 购买项目2-普通,1次 - await page.getByText(ProjectA2.num).click(); + await page.getByText(projects_a2.num).click(); await page.locator('.type_btn').first().click(); await page.locator('.type_item', { hasText: '普通' }).click(); @@ -2266,21 +2260,21 @@ test.describe('顾客分析', () => { await page.getByText('护理', { exact: true }).click(); // 购买项目3-赠送,3次 - await page.getByText(ProjectA3.num).click(); + await page.getByText(projects_a3.num).click(); await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectA3Quantity); + await numberInput.setValue(projects_a3_quantity); await numberInput.confirmValue(); await page.locator('.type_btn').first().click(); await page.locator('.type_item', { hasText: '赠送' }).click(); // 选择组合项目2-项目1,普通 - await page.getByText(ProjectA1.num).first().click(); + await page.getByText(projects_a1.num).first().click(); await page.locator('.add_btn', { hasText: '设置' }).last().click(); - await page.getByRole('textbox').fill(ProjectA2.num); + await page.getByRole('textbox').fill(projects_a2.num); await page.getByRole('button', { name: /搜\s索/ }).click(); await expect(async () => { - await page.getByLabel(ProjectA2.name).uncheck(); - await page.getByLabel(ProjectA2.name).check(); + await page.getByLabel(projects_a2.name).uncheck(); + await page.getByLabel(projects_a2.name).check(); await page.locator('.menu-item-dot', { hasText: '2' }).first().waitFor({ timeout: 2000 }); }).toPass(); await page.getByRole('button', { name: '确定选择' }).click(); @@ -2300,50 +2294,52 @@ test.describe('顾客分析', () => { await page.getByRole('button', { name: /^结\s算$/ }).click(); }); - const ProjectB3 = ProjectName.Projects.Projects_2; - const ProjectB3Quantity = 20; - const ProjectB4 = ProjectName.Projects.Projects_661; - const ProjectB4Quantity = 30; - const ProjectB5 = ProjectName.Projects.Projects_676; - const ProjectB5Quantity = 9; - const ProjectB1 = ProjectName.Projects.Projects_1; - const ProjectB2 = ProjectName.Projects.Projects_239; - const ProjectB12Quantity = 2; - const ProjectB34Quantity = 3; + const project_b3 = { num: '100013', name: '净透驻氧护理', price: 380 }; + const project_b3_quantity = 20; + + const project_b4 = { num: '100719', name: '艾灸', price: 0 }; + const project_b4_quantity = 30; + + const project_b5 = { num: '100735', name: '脱毛', price: 88 }; + const project_b5_quantity = 9; + + const project_b1 = { num: '100012', name: '雪肌晶纯护理', price: 300 }; + const project_b2 = { num: '100258', name: '出水芙蓉SPA水疗', Price: 380 }; + const project_b12_quantity = 2; + const project_b34_quantity = 3; await test.step('顾客B', async () => { - //顾客B await page.reload(); await page.getByRole('button', { name: /开\s单/ }).click(); - await customerPage.searchCustomer(phoneB); - await customerPage.selectSearchCustomer(usernameB); + await customerPage.searchCustomer(cb.phone); + await customerPage.selectSearchCustomer(cb.username); // 购买项目3-普通,20次 - await page.getByText(ProjectB3.num).click(); + await page.getByText(project_b3.num).click(); await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectB3Quantity); + await numberInput.setValue(project_b3_quantity); await numberInput.confirmValue(); // 点击身体 await page.locator('.type_tab_item', { hasText: '身体' }).click(); // 购买项目4-普通,30次 - await page.getByText(ProjectB4.num).click(); + await page.getByText(project_b4.num).click(); await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectB4Quantity); + await numberInput.setValue(project_b4_quantity); await numberInput.confirmValue(); // 购买项目5,9次 - await page.getByText(ProjectB5.num).click(); + await page.getByText(project_b5.num).click(); await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectB5Quantity); + await numberInput.setValue(project_b5_quantity); await numberInput.confirmValue(); // 购买项目B1-B2混合,2次 await page.locator('.type_tab_item', { hasText: '护理' }).click(); - await page.getByText(ProjectB1.num).first().click(); + await page.getByText(project_b1.num).first().click(); await page.locator('.add_btn', { hasText: '设置' }).last().click(); - await page.getByRole('textbox').fill(ProjectB2.num); + await page.getByRole('textbox').fill(project_b2.num); await page.getByRole('button', { name: /搜\s索/ }).click(); await expect(async () => { await page.locator('.list_box .ant-checkbox-input').click(); @@ -2352,13 +2348,13 @@ test.describe('顾客分析', () => { await page.getByRole('button', { name: '确定选择' }).click(); // 点击选择数量 await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectB12Quantity); + await numberInput.setValue(project_b12_quantity); await numberInput.confirmValue(); // 购买项目B4-B4混合,3次 - await page.getByText(ProjectB3.num).first().click(); + await page.getByText(project_b3.num).first().click(); await page.locator('.add_btn', { hasText: '设置' }).last().click(); - await page.getByRole('textbox').fill(ProjectB4.num); + await page.getByRole('textbox').fill(project_b4.num); await page.getByRole('button', { name: '搜 索' }).click(); await expect(async () => { await page.locator('.list_box .ant-checkbox-input').click(); @@ -2367,7 +2363,7 @@ test.describe('顾客分析', () => { await page.getByRole('button', { name: '确定选择' }).click(); // 点击选择数量 await page.locator('.edit_txt div:nth-child(2)').first().click(); - await numberInput.setValue(ProjectB34Quantity); + await numberInput.setValue(project_b34_quantity); await numberInput.confirmValue(); await page.locator('.commodity_item').last().click(); @@ -2385,15 +2381,13 @@ test.describe('顾客分析', () => { await page.reload(); await homeNavigation.gotoModule('顾客'); await customerPage.gotoSubPage('顾客分析'); - await page - .locator('div') - .filter({ hasText: /^排序$/ }) - .nth(1) - .click(); - await page.getByRole('menuitem', { name: '上次到店时间从近到远' }).click(); - await page.locator('.loading_container').waitFor({ state: 'hidden' }); - const Customer1A = page.locator('.m-table__body-wrapper tbody tr', { hasText: phoneA }); - const Customer1B = page.locator('.m-table__body-wrapper tbody tr', { hasText: phoneB }); + await page.getByText('排序', { exact: true }).click(); + await Promise.all([ + page.waitForResponse(res => res.url().includes('analysis') && res.ok()), + page.getByRole('menuitem', { name: '上次到店时间从近到远' }).click(), + ]); + const Customer1A = page.locator('.m-table__body-wrapper tbody tr', { hasText: ca.phone }); + const Customer1B = page.locator('.m-table__body-wrapper tbody tr', { hasText: cb.phone }); // 1 查找到顾客A 顾客B await expect(Customer1A).toBeVisible(); await expect(Customer1B).toBeVisible(); @@ -2406,19 +2400,19 @@ test.describe('顾客分析', () => { .locator('.m-table__body-wrapper tbody tr') .allInnerTexts() .then(async text => { - return text.findIndex(item => item.includes(phoneB)); + return text.findIndex(item => item.includes(cb.phone)); }); // 查看各单元格内容 // 护理内容 - await expect.soft(allTr.nth(nowRow).locator('td').nth(2)).toContainText(`${ProjectB3Quantity - 1}`); + await expect.soft(allTr.nth(nowRow).locator('td').nth(2)).toContainText(`${project_b3_quantity - 1}`); // 身体内容 await expect .soft(allTr.nth(nowRow).locator('td').nth(5)) - .toContainText(`${ProjectB12Quantity + ProjectB34Quantity}`); + .toContainText(`${project_b12_quantity + project_b34_quantity}`); // 组合内容 await expect(allTr.nth(nowRow).locator('td').nth(5)).toContainText( - `${ProjectB12Quantity + ProjectB34Quantity}`, + `${project_b12_quantity + project_b34_quantity}`, ); const $productName = page.locator('.treat_box .treat_card:nth-child(1) .name_row .auto_desc'); @@ -2430,67 +2424,67 @@ test.describe('顾客分析', () => { // 点击护理 await allTr.nth(nowRow).locator('td').nth(2).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectB3.name); + await expect.soft($productName).toContainText(project_b3.name); // 剩余次数 - await expect($productResidue).toContainText(`${ProjectB3Quantity - 1}`); + await expect($productResidue).toContainText(`${project_b34_quantity - 1}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); // 点击身体 await allTr.nth(nowRow).locator('td').nth(4).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectB4.name); + await expect.soft($productName).toContainText(project_b4.name); // 剩余次数 - await expect.soft($productResidue).toContainText(`${ProjectB4Quantity}`); + await expect.soft($productResidue).toContainText(`${project_b4_quantity}`); // 项目名称 - await expect.soft($productName2).toContainText(ProjectB5.name); + await expect.soft($productName2).toContainText(project_b5.name); // 剩余次数 - await expect.soft($productResidue2).toContainText(`${ProjectB5Quantity}`); + await expect.soft($productResidue2).toContainText(`${project_b5_quantity}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); // 点击组合 await allTr.nth(nowRow).locator('td').nth(5).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectB1.name + ',' + ProjectB2.name); + await expect.soft($productName).toContainText(project_b1.name + ',' + project_b2.name); // 剩余次数 - await expect.soft($productResidue).toContainText(`${ProjectB12Quantity}`); + await expect.soft($productResidue).toContainText(`${project_b12_quantity}`); // 项目名称 - await expect.soft($productName2).toContainText(ProjectB3.name + ',' + ProjectB4.name); + await expect.soft($productName2).toContainText(project_b3.name + ',' + project_b4.name); // 剩余次数 - await expect($productResidue2).toContainText(`${ProjectB34Quantity}`); + await expect($productResidue2).toContainText(`${project_b34_quantity}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); // 查看顾客A的各单元格内容 const allTrA = page.locator('.m-table__body-wrapper tbody tr'); const nowRowA = await allTrA.allInnerTexts().then(async text => { - return text.findIndex(item => item.includes(phoneA)); + return text.findIndex(item => item.includes(ca.phone)); }); // 点击护理 await allTrA.nth(nowRowA).locator('td').nth(2).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectA1.name); + await expect.soft($productName).toContainText(projects_a1.name); // 剩余次数 - await expect($productResidue).toContainText(`${ProjectA1Quantity - 1}`); + await expect($productResidue).toContainText(`${projects_a1_quantity - 1}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); // 点击面部 await allTrA.nth(nowRowA).locator('td').nth(3).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectA2.name); + await expect.soft($productName).toContainText(projects_a2.name); // 剩余次数 - await expect($productResidue).toContainText(`${ProjectA2Quantity}`); + await expect($productResidue).toContainText(`${projects_a2_quantity}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); // 点击组合 await allTrA.nth(nowRowA).locator('td').nth(5).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectA1.name + ',' + ProjectA2.name); + await expect.soft($productName).toContainText(projects_a1.name + ',' + projects_a2.name); // 剩余次数 - await expect($productResidue).toContainText(`${ProjectA12Quantity}`); + await expect($productResidue).toContainText(`${projects_a12_quantity}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); @@ -2506,18 +2500,18 @@ test.describe('顾客分析', () => { // 护理内容 const allTrAA = page.locator('.m-table__body-wrapper tbody tr'); const nowRowAA = await allTrAA.allInnerTexts().then(async text => { - return text.findIndex(item => item.includes(phoneA)); + return text.findIndex(item => item.includes(ca.phone)); }); // 点击护理 await allTrAA.nth(nowRowAA).locator('td').nth(2).click(); // 项目名称 - await expect.soft($productName).toContainText(ProjectA1.name); + await expect.soft($productName).toContainText(projects_a1.name); // 剩余次数 - await expect.soft($productResidue).toContainText(`${ProjectA1Quantity - 1}`); + await expect.soft($productResidue).toContainText(`${projects_a1_quantity - 1}`); // 项目名称 - await expect.soft($productName2).toContainText(ProjectA3.name); + await expect.soft($productName2).toContainText(projects_a3.name); // 剩余次数 - await expect($productResidue2).toContainText(`${ProjectA3Quantity}`); + await expect($productResidue2).toContainText(`${projects_a3_quantity}`); // 关闭弹窗 await page.locator('.close_icon').last().click(); diff --git a/tests/touch/boss_report.spec.ts b/tests/touch/boss_report.spec.ts index b054ee3..8d8b175 100644 --- a/tests/touch/boss_report.spec.ts +++ b/tests/touch/boss_report.spec.ts @@ -280,7 +280,7 @@ test.describe('业绩明细表', () => { test.describe('项目销耗存表', () => { test('数据校验', async ({ page, createCustomers, homeNavigation, reportPage, customerPage, numberInput }) => { - let customers: Customer[]; + let customers: Customer[] = new Array(2); await test.step('创建两个顾客', async () => { customers = await createCustomers(2); }); diff --git a/tests/utils/utils.js b/tests/utils/utils.ts similarity index 86% rename from tests/utils/utils.js rename to tests/utils/utils.ts index 3e2fd7a..0525788 100644 --- a/tests/utils/utils.js +++ b/tests/utils/utils.ts @@ -1,3 +1,5 @@ +import { Page } from "playwright"; + // 解析二维码 const decodeImage = require('jimp').read; const { readFile, unlinkSync } = require('fs'); @@ -95,17 +97,31 @@ export function CleanPunctuation(str) { /** * 等待指定接口加载完成 - * @param {import('@playwright/test').Page} page - * @param {string[]} apiArray 接口名称数组 + * @param {Page} page - Playwright Page 对象 + * @param {string[]} apiArray - 接口名称数组 * @returns Promise */ -export const waitSpecifyApiLoad = (page, apiArray) => { - if (apiArray === undefined || apiArray.length === 0) { - return Promise.resolve([]); +export const waitSpecifyApiLoad = async (page: Page, apiArray: string[]): Promise => { + // 输入参数检查 + if (!page || !Array.isArray(apiArray) || apiArray.length === 0 || !apiArray.every(item => typeof item === 'string')) { + return []; } - return Promise.all( - apiArray.map(api => page.waitForResponse(res => res.url().includes(api) && res.status() === 200)), + + // 去重 + const uniqueApiArray = Array.from(new Set(apiArray)); + + // 精确匹配 URL + const urlMatchers = uniqueApiArray.map(api => new RegExp(`^.*${api}$`)); + + // 等待所有接口加载完成 + const responses = await Promise.allSettled( + urlMatchers.map(matcher => page.waitForResponse(res => matcher.test(res.url()) && res.status() === 200)) ); + + // 处理结果 + return responses + .filter(response => response.status === 'fulfilled') + .map(response => (response as unknown as PromiseFulfilledResult).value); }; /**