// @ts-check import { test, expect } from '@/fixtures/boss_common.js'; import { faker } from '@faker-js/faker/locale/zh_CN'; import { Customer } from '@/utils/customer'; import { KeepOnlyNumbers } from '@/utils/utils.js'; import fs from 'fs'; import path from 'path'; import { Employees, ProjectName } from '@/fixtures/userconfig.js'; import { staffData } from '@/common/staff'; test.describe('营业记录', () => { test.beforeEach(async ({ page }) => { await page.addLocatorHandler(page.getByRole('button', { name: '我知道了' }), async () => { await page.getByRole('button', { name: '我知道了' }).click(); await expect(page.getByRole('button', { name: '我知道了' })).not.toBeVisible(); await page.reload(); }); await page.addLocatorHandler(page.locator('.ant-notification', { hasText: '反结算成功' }), async () => { await page.locator('.ant-notification', { hasText: '反结算成功' }).locator('a').click(); await expect(page.locator('.ant-notification', { hasText: '反结算成功' })).not.toBeVisible(); }); }); test('根据条件搜索营业记录', async ({ page, homeNavigation, customerPage, createCustomCustomer }) => { const customer = new Customer(2, 1); const project = { num: '100012', name: '雪肌晶纯护理', Price: 300 }; let billNo = ''; await test.step('创建顾客', async () => { await createCustomCustomer(customer); }); await test.step('开单结算拿取单号', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); await expect(page.locator('div').filter({ hasText: /^结\s算$/ })).toBeVisible(); // 选择项目1 await page.getByText(project.num).click(); await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); await page.locator('.paymentInfoItem').filter({ hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); // 结算 const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && (await res.json()).code === 'SUCCESS'; }), page.getByRole('button', { name: /^结\s算$/ }).click(), ]); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; }); expect(billNo).not.toBeNull(); const $bill = page.getByRole('cell', { name: billNo }).nth(1); await test.step('条件搜索', async () => { await homeNavigation.gotoModule('流水'); await expect(page.getByText('营业记录').first()).toBeVisible(); await page.getByRole('combobox').first().click(); await page.getByRole('option', { name: '购买项目/卖品' }).click(); await page.getByRole('combobox').nth(1).click(); await page.getByRole('option', { name: '已结算' }).click(); await page.getByRole('combobox').nth(2).click(); await page.getByRole('option', { name: '未对单' }).click(); await expect($bill).toBeVisible(); }); await test.step('搜索水单号', async () => { await page.getByRole('button').click(); await page.getByPlaceholder('输入流水单号搜索').fill(billNo); await page.locator('.search_btn > svg').click(); await expect($bill).toBeVisible(); }); await test.step('进入水单详情和顾客详情', async () => { await $bill.click(); await expect(page.locator('li').getByText(billNo)).toBeVisible(); await page .locator('div') .filter({ hasText: /^单据明细$/ }) .locator('use') .click(); await page.locator('.main-table-body_tr').getByText(customer.username).first().click(); await expect(page.getByRole('tabpanel').getByText(customer.username)).toBeVisible(); }); }); test.describe('单据明细', () => { test.beforeAll(async () => { const currentFileName = path.basename(__filename); // 构造快照目录 const snapshotDir = path.resolve('tests/imgs/__screenshots__/touch', currentFileName); // 如果目录不存在,则创建它 if (!fs.existsSync(snapshotDir)) { fs.mkdirSync(snapshotDir, { recursive: true }); } }); test('反结算', async ({ page, homeNavigation, customerPage, createCustomer, numberInput }) => { // 定义随机单号 let ReceiptNum = faker.helpers.fromRegExp(/1[3-9][0-9]{8}/); const project_1 = ProjectName.Projects.Projects_2; // 项目 const employee_1 = Employees.FirstShop.Employee_6; // 员工 const project_2 = ProjectName.Projects.Projects_3; // 项目 const employee_2 = Employees.FirstShop.Employee_4; // 员工 // 创建顾客A随机姓名 手机 const customer = createCustomer; await test.step('开单结算拿取单号', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); // 选择项目1 await page.getByText(project_1.num).click(); // 点击添加员工 await page.locator('#buyList').getByRole('button').nth(1).click(); // 选择员工1 await page.locator('.hand_txt .name_txt').getByText(employee_1.name).click(); await page.getByRole('button', { name: /^确\s认$/ }).click(); await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); // 点击修改单号 await page.locator('.input165').click(); await page.getByPlaceholder('请输入内容').fill(ReceiptNum); await page.locator('.tools_icon').last().click(); // 点击现金 await page.locator('.paymentInfoItem').filter({ hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); await page.getByRole('button', { name: /^结\s算$/ }).click(); await expect(page.locator('.ant-message')).toContainText('结算成功'); }); await test.step('进入流水模块,进行反结算', async () => { await homeNavigation.gotoModule('流水'); // 点击指定单号 await page .locator('.m-table__fixed-left .m-table-cell .bill .m-dropdown-link ', { hasText: ReceiptNum, }) .click(); // 点击反结算 await page.locator('.oversized', { hasText: '反结算' }).click(); await page.locator('.custom_content').waitFor(); // 删除单据内的项目 await page.locator('.buy_item .buy_name .del_btn').last().click(); // 选择项目C await page.getByText(project_2.num).click(); await page.locator('#buyList').getByText(project_2.name).click(); // 点击添加员工 await page.locator('#buyList').getByRole('button').nth(1).click(); await page.getByText(employee_2.name).first().click(); // 确认 await page.getByRole('button', { name: /^确\s认$/ }).click(); // 修改数量 await page.locator('.edit_txt div:nth-child(2)').first().click(); await numberInput.setValue(2); await numberInput.confirmValue(); // 结算 await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); // 使用银联支付 await page.locator('.paymentInfoItem', { hasText: '银联' }).click(); await page.getByLabel('结算签字').uncheck(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByRole('button', { name: /^结\s算$/ }).click(); await expect(page.locator('.ant-message', { hasText: '结算成功' })).toBeVisible(); await page.addLocatorHandler(page.getByRole('button', { name: '我知道了' }), async () => { await page.getByRole('button', { name: '我知道了' }).click(); await expect(page.getByRole('button', { name: '我知道了' })).not.toBeVisible(); await page.reload(); await page .locator('.m-table__fixed-left .m-table-cell .bill .m-dropdown-link ', { hasText: ReceiptNum, }) .click(); }); }); await test.step('进入单据明细,查看反结算内容', async () => { await page.locator('.m-receiptDetail_tip', { hasText: '单据明细' }).waitFor(); // 对比第一个项目 await expect.soft(page.locator('.items_name .name_txt').first()).toContainText(project_2.name); // 对比员工 await expect .soft( page .locator( '.m-detailComponent_report .main-table-body_tr:nth-child(1) .billUser_item .userName', ) .first(), ) .toContainText(employee_2.name); // 对比数量 await expect.soft(page.locator('.m-detailComponent-cell .num').last()).toContainText('2'); // 对比总金额 const project1ResultCollect = Number(ProjectName.Projects.Projects_3.Price) * 2 + ''; await expect(page.locator('.m-detailComponent_consume_title .amount')).toContainText( project1ResultCollect, ); }); }); test('撤单', async ({ page, homeNavigation, customerPage, createCustomer }) => { const ReceiptNum = faker.helpers.fromRegExp(/1[3-9][0-9]{8}/); // 随机单号 let OddNumber = page.locator('.m-table__fixed-left .m-table-cell .bill .m-dropdown-link ', { hasText: ReceiptNum, }); const project = ProjectName.Projects.Projects_3; const customer = createCustomer; await test.step('开单获取单号', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); // 选择项目1 await page.getByText(project.num).click(); // 结算 await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); // 点击修改单号 await page.locator('.input165').click(); // 输入随机单号 await page.getByPlaceholder('请输入内容').fill(ReceiptNum); // 确认 await page.locator('.tools_icon').last().click(); await page.locator('.paymentInfoItem', { hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); await page.getByRole('button', { name: /^结\s算$/ }).click(); await expect(page.locator('.ant-message')).toContainText('结算成功'); }); await test.step('根据单号进行撤单,判断撤单成功', async () => { // 点击流水 await page.reload(); await homeNavigation.gotoModule('流水'); // 点击下拉框 await page.locator('.ant-dropdown-link > .anticon > svg').first().click(); // 选择营业记录 await page.getByRole('menuitem', { name: '营业记录' }).click(); // 点击指定单号 await page .locator('.m-table__fixed-left .m-table-cell .bill .m-dropdown-link ', { hasText: ReceiptNum, }) .click(); // 点击撤单 await page.locator('.ant-btn', { hasText: '撤单' }).click(); // 点击输入备注 await page.locator('.label_text').last().click(); await page.getByPlaceholder('请输入1-100个字符备注内容').fill('撤单'); await page.locator('.saveCheck').waitFor(); await page.getByRole('button', { name: '确 认' }).click(); // 点击下拉框 await page.locator('.ant-dropdown-link > .anticon > svg').first().click(); // 选择营业记录 await page.getByRole('menuitem', { name: '营业记录' }).click(); // 点击撤单查看 await page.locator('.search_select').first().click(); await page.locator('.ant-select-dropdown-menu-item', { hasText: '已撤单' }).click(); // 判断撤的单子是否还在 await expect(OddNumber).toBeVisible(); }); }); test('补签', async ({ page, homeNavigation, customerPage, createCustomer }) => { const customer = createCustomer; const project = { num: '100012', name: '雪肌晶纯护理', Price: 300 }; let billNo = ''; await test.step('开单结算,不勾选签字,拿取单号', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); // 选择项目1 await page.getByText(project.num).click(); await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); await page.locator('.paymentInfoItem').filter({ hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); // 结算 const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && (await res.json()).code === 'SUCCESS'; }), page.getByRole('button', { name: /^结\s算$/ }).click(), ]); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; expect(billNo).not.toBeNull(); }); const $bill = page.getByRole('cell', { name: billNo }).nth(1); await test.step('进入水单详情进行补签', async () => { await homeNavigation.gotoModule('流水'); await expect(page.getByText('营业记录').first()).toBeVisible(); await $bill.click(); await expect(page.locator('li').getByText(billNo)).toBeVisible(); await page.getByText('补签').click(); await expect(page.getByText('请签字确认消费')).toBeVisible(); const $canvas = page.locator('canvas').first(); const canvasBoundingBox = await $canvas.boundingBox(); if (canvasBoundingBox) { const { x, y, width, height } = canvasBoundingBox; // 模拟鼠标按下操作 await page.mouse.move(x + width / 4, y + height / 2); await page.mouse.down(); // 模拟签字路径(可以自定义路径) await page.mouse.move(x + width / 2, y + height / 2); await page.mouse.move(x + width / 1.5, y + height / 3); await page.mouse.move(x + width / 1.2, y + height / 1.8); // 释放鼠标 await page.mouse.up(); } else { throw new Error('无法找到签字区域'); } await page.getByRole('button', { name: '签好了' }).click(); await expect(page.locator('li').getByText(billNo)).toBeVisible(); await page.waitForLoadState('load'); const $img = page.locator('.slider_list_item', { has: page.getByText('签名') }).locator('img'); await expect($img).toHaveScreenshot({ threshold: 0.25, // 相似度阈值(0.1 表示 10% 以内的差异可以接受) animations: 'disabled', // 处理有动画的元素 }); }); }); test('查看配方', async ({ page, homeNavigation, customerPage, createCustomer }) => { const project = { num: '100012', name: '雪肌晶纯护理', Price: 300 }; const customer = createCustomer; let billNo = ''; let useProductName = ''; await test.step('开单,购买项目并消耗,消耗时选择项目配方', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); // 选择项目1 await page.getByText(project.num).click(); await page.locator('.commodity_item').first().click(); // 打开使用区-项目配方 await page.locator('.staff_setting').first().click(); await page.getByLabel('项目配方').check(); await page.getByRole('button', { name: /确\s认/ }).click(); // 选择项目配方 await page.locator('.formula_noData').click(); await expect(async () => { await page.locator('.list_box').getByRole('checkbox').first().check(); await expect(page.locator('.menu-item-dot').first()).toBeVisible(); }).toPass({ timeout: 30_000 }); useProductName = (await page.locator('.list_box .label').first().innerText()).trim(); await page.getByRole('button', { name: '确定选择' }).click(); await page.locator('input[type="tel"]').fill('1'); await page.getByRole('button', { name: /保\s存/ }).click(); await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); await page.locator('.paymentInfoItem').filter({ hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); // 结算 const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && (await res.json()).code === 'SUCCESS'; }), page.getByRole('button', { name: /^结\s算$/ }).click(), ]); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; }); expect(billNo).not.toBeNull(); expect(useProductName).not.toBeNull(); await test.step('查看单据明细的配方消耗', async () => { await homeNavigation.gotoModule('流水'); await page.locator('.m-table-fixed-body').getByText(billNo).first().click(); const $$listItem = page.locator('.slider_list_item'); const $openConsumptionRecord = $$listItem .filter({ has: page.getByText('开用记录'), }) .getByText('查看详情'); await $openConsumptionRecord.click(); const $popup = page.locator('.popup_content'); await expect($popup.getByText(useProductName)).toBeVisible(); const useProductNumber = ( await $popup.locator('.main-table-body_tr').first().locator('td').nth(1).innerText() ).trim(); expect(useProductNumber).toBe('1'); }); }); }); }); test.describe('业绩流水', () => { test('根据条件搜索业绩流水', async ({ page, customerPage, homeNavigation, createCustomer, wasteBookBusinessRecordPage, }) => { const customer = createCustomer; let billNo = ''; const firstProject = { num: '', name: '' }; const firstGoods = { num: '', name: '' }; await test.step('开单,购买项目、卖品并消耗,消耗购买项目', async () => { // 进入顾客详情页面 await homeNavigation.gotoModule('顾客'); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.username); await customerPage.openCustomerDetail(customer.username, customer.phone); await page.locator('span').filter({ hasText: '去开单' }).first().click(); await expect(page.locator('div').filter({ hasText: /^结\s算$/ })).toBeVisible(); // 选择项目1 const $firstProject = page.locator('.list_box .project_list').first(); await $firstProject.click(); firstProject.num = await $firstProject.locator('.number').innerText(); firstProject.name = await $firstProject.locator('.title').innerText(); await page.locator('.commodity_item').first().click(); // 选择卖品 await page.getByText('卖品', { exact: true }).click(); await $firstProject.click(); firstGoods.num = await $firstProject.locator('.number').innerText(); firstGoods.name = await $firstProject.locator('.title').innerText(); await page .locator('div') .filter({ hasText: /^结\s算$/ }) .click(); await page.locator('.paymentInfoItem').filter({ hasText: '现金' }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && (await res.json()).code === 'SUCCESS'; }), page.getByRole('button', { name: /^结\s算$/ }).click(), ]); await page.getByRole('button', { name: '不寄存' }).click(); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; }); expect(billNo).not.toBeNull(); expect(firstProject.num).not.toBe(''); expect(firstProject.name).not.toBe(''); expect(firstGoods.num).not.toBe(''); expect(firstGoods.name).not.toBe(''); await test.step('根据筛选条件进行搜索水单', async () => { await Promise.all([homeNavigation.gotoModule('流水'), page.waitForLoadState()]); await wasteBookBusinessRecordPage.gotoSubPage('业绩流水'); // 流水单 const $flowChart = page.getByRole('row', { name: billNo, exact: true }).first(); const $projectViewSelect = page .locator('.com_picker') .filter({ hasText: '选择' }) .locator('.menu-item-dot') .first(); const $goodsViewSelect = page .locator('.com_picker') .filter({ hasText: '选择卖品' }) .locator('.menu-item-dot') .first(); await page.getByTitle('全部水单类型').click(); await page.getByRole('option', { name: '购买项目/卖品' }).click(); // 选择项目 await page.locator('span').filter({ hasText: '选择项目' }).getByRole('list').click(); await page.getByRole('textbox', { name: '请输入首字母或关键字检索' }).fill(firstProject.num); await page.getByRole('button', { name: /搜\s索/ }).click(); await expect(async () => { await page.getByLabel(firstProject.name).uncheck(); await page.getByLabel(firstProject.name).check(); await expect($projectViewSelect).toBeVisible({ timeout: 2000 }); }).toPass({ timeout: 30_000 }); await page.getByRole('button', { name: '确定选择' }).click(); // 选择卖品 await page.getByTitle('购买项目/卖品').click(); await page.getByRole('option', { name: '全部水单类型' }).click(); await page.getByTitle('全部水单类型').click(); await page.getByRole('option', { name: '购买项目/卖品' }).click(); await page.locator('span').filter({ hasText: '选择卖品' }).getByRole('list').click(); await page.getByRole('textbox').nth(2).fill(firstGoods.num); await page.getByRole('button', { name: /搜\s索/ }).click(); await expect(async () => { await page.getByLabel(firstGoods.name).uncheck(); await page.getByLabel(firstGoods.name).check(); await expect($goodsViewSelect).toBeVisible({ timeout: 2000 }); }).toPass({ timeout: 30_000 }); await page.getByRole('button', { name: '确定选择' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('购买项目/卖品').click(); await page.getByRole('option', { name: '消耗项目' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('消耗项目').click(); await page.getByRole('option', { name: '开卡' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('开卡').click(); await page.getByRole('option', { name: '充值' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('充值').click(); await page.getByRole('option', { name: '还款' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('还款').click(); await page.getByRole('option', { name: '换项目/产品' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('换项目/产品').click(); await page.getByRole('option', { name: '抹欠款' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('抹欠款').click(); await page.getByRole('option', { name: '补录' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('补录').click(); await page.getByRole('option', { name: '跨店帮忙' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('跨店帮忙').click(); await page.getByRole('option', { name: '会员卡退卡' }).click(); await expect($flowChart).not.toBeVisible(); await page.reload(); await page.getByRole('button').click(); await page.getByPlaceholder('输入流水单号搜索').fill(billNo); await page.locator('.search_btn > svg').click(); await expect($flowChart).toBeVisible(); }); }); test('查询业绩流水列表明细', async ({ page, homeNavigation, createCustomer, wasteBookBusinessRecordPage }) => { // 创建顾客A随机姓名 手机 const ca = createCustomer; const usernameA = ca.username; const phoneA = ca.phone; await test.step('开单购买会员卡', async () => { await homeNavigation.gotoModule('收银'); await page.getByRole('button', { name: /^开\s单$/ }).click(); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(phoneA); await page .locator('.search_text') .filter({ hasText: /^搜\s索$/ }) .click(); // 选择会员 await page.locator('.custom_content').getByText(usernameA).click(); await page.locator('.loading_container').waitFor({ state: 'hidden' }); await page.locator('.custom_content').waitFor(); // 点击开卡 const ActivateCards = page.getByRole('button', { name: /^开\s卡$/ }); if (await ActivateCards.isVisible()) { await page.getByRole('button', { name: /^开\s卡$/ }).click(); } else { await page.locator('.more').first().click(); await page.getByText('去开卡').click(); } // 选择会员卡A await page .locator('.memberCard_box > .needsclick') .getByText(/^会员卡$/) .click(); // 结算 await page.getByRole('button', { name: '去结算' }).click(); // 选择现金支付 await page.getByText('现金').click(); // 取消推送消费提醒 await page.getByLabel('推送消费提醒').uncheck(); // 取消结算签字 await page.getByLabel('结算签字').uncheck(); // 结算 await page.getByRole('button', { name: /^结\s算$/ }).click(); // 会员协议签署确认(需判断 常报错) try { await page.locator('.modal_title', { hasText: '会员协议签署确认' }).waitFor(); await page.getByRole('button', { name: /跳\s过/ }).click(); } catch { console.log('无会员协议签署'); } await expect(page.locator('.ant-message')).toContainText('结算成功'); }); await test.step('进入顾客详情', async () => { await page.reload(); await homeNavigation.gotoModule('顾客'); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(phoneA); await page.getByText('搜索', { exact: true }).click(); await page.getByText(usernameA).click(); // 点击进入详情 await page.locator('.user_info_head .user_name', { hasText: usernameA }).last().click(); }); // 定义随机单号 const ReceiptNum = faker.helpers.fromRegExp(/1[3-9][0-9]{8}/); const Cash = '30'; const CardAmount = '10'; const FreeAmount = '20'; const Arrears = '20'; // 选择员工A const employee1 = Employees.FirstShop.Employee_6.name; await test.step('开单消耗项目,选择员工,混合支付,随机单号', async () => { await page.locator('span').filter({ hasText: '去开单' }).first().click(); await page.getByText(ProjectName.Projects.Projects_17.num).click(); // 点击添加员工 await page.locator('#buyList').getByRole('button').nth(1).click(); await page.locator('.hand_txt .name_txt').getByText(employee1).click(); // 确认 await page.getByRole('button', { name: /^确\s认$/ }).click(); // 消耗该项目1次 await page.locator('.commodity_item').last().click(); // 点击添加员工 await page.locator('.use_item .staff_btn').click(); // 选择员工A await page.locator('.hand_txt .name_txt').getByText(employee1).click(); // 确认 await page.getByRole('button', { name: /^确\s认$/ }).click(); await page .locator('div') .filter({ hasText: /^结 算$/ }) .click(); // 点击修改单号 await page.locator('.input165').click(); // 输入随机单号 await page.getByPlaceholder('请输入内容').fill(ReceiptNum); // 确认 await page.locator('.tools_icon').last().click(); //点击混合支付 await page.locator('.paytype').first().getByText('混合支付').click(); const rightPaymentInfoItem = page.locator('.right .paymentmain .paymentInfoItem'); //点击卡金 await rightPaymentInfoItem.getByText('卡金', { exact: true }).click(); //增加收款 await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.money_discount_input').fill(CardAmount); //确认金额 await page.locator('.sure .tools_icon').click(); //点击赠金 await rightPaymentInfoItem.getByText('赠金', { exact: true }).click(); //增加收款 await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.money_discount_input').fill(FreeAmount); //确认金额 await page.locator('.sure .tools_icon').click(); //点击现金 await rightPaymentInfoItem.getByText('现金', { exact: true }).click(); //增加收款 await page.getByRole('button', { name: '增加收款' }).click(); //输入金额 await page.locator('.money_discount_input').fill(Cash); //确认金额 await page.locator('.sure .tools_icon').click(); //点击欠款 await rightPaymentInfoItem.getByText('欠款', { exact: true }).click(); //增加收款 await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.money_discount_input').fill(Arrears); //确认金额 await page.locator('.sure .tools_icon').click(); //取消推送消息提醒 await page.getByLabel('推送消费提醒').uncheck(); //取消结算签字 await page.getByLabel('结算签字').uncheck(); //结算 await page.getByRole('button', { name: /^结\s算$/ }).click(); }); await test.step('进入水单详情页,查看数据', async () => { // 进入业绩流水 await homeNavigation.gotoModule('流水'); await wasteBookBusinessRecordPage.gotoSubPage('业绩流水'); // 点击搜索水单 await expect(async () => { await page.getByRole('button').click(); await page.locator('.searchButton .search_input .ant-input').waitFor({ timeout: 2000 }); }).toPass(); await page.locator('.searchButton .search_input .ant-input').fill(ReceiptNum); await page.locator('.searchButton .search_input .search_btn').click(); await page.locator('.loading_container').waitFor({ state: 'hidden' }); await expect(page.locator('.m-table__fixed-left .m-dropdown-link').first()).toContainText(ReceiptNum); // 现金业绩 const CashPerformance = KeepOnlyNumbers( await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(1) .locator('.is-right') .first() .innerText(), ); const Cashs = Number(Cash) * 0.8 + ''; console.log('现金业绩' + CashPerformance); // 划卡业绩 const CardPerformance = KeepOnlyNumbers( await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(1) .locator('.is-right') .nth(1) .innerText(), ); console.log('划卡业绩' + CardPerformance); // 划赠金业绩 const FreePerformance = KeepOnlyNumbers( await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(1) .locator('.is-right') .nth(2) .innerText(), ); console.log('赠金业绩' + FreePerformance); // 消耗业绩 const ExpendPerformance = KeepOnlyNumbers( await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(0) .locator('.is-right') .nth(4) .innerText(), ); const ProjectPrice = ProjectName.Projects.Projects_17.Price; // 项目价格 const ProjectPrices = Number(ProjectPrice) * 0.8 + ''; // 会员卡打8折 console.log('消耗业绩' + ExpendPerformance); // 消耗 员工 const ExpendEmployee = await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(0) .locator('.billUser span') .last() .innerText(); console.log('消耗 员工' + ExpendEmployee); // 购买 员工 const BuyEmployee = await page .locator('.m-table__body-wrapper .main-table-body_tr') .nth(1) .locator('.billUser span') .last() .innerText(); console.log('购买 员工' + BuyEmployee); expect(CashPerformance).toBe(Cashs); // 现金业绩 expect(CardPerformance).toBe(CardAmount); // 划卡业绩 expect(FreePerformance).toBe(FreeAmount); // 划赠金业绩 expect(ExpendPerformance).toBe(ProjectPrices); // 消耗业绩 expect(ExpendEmployee).toBe(employee1); // 消耗 员工 expect(BuyEmployee).toBe(employee1); // 购买 员工 }); }); }); test.describe('对账流水', () => { test('根据条件搜索对账流水', async ({ page, homeNavigation, createCustomer, wasteBookBusinessRecordPage }) => { const customer = createCustomer; const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: '980' }; let billNo: string; await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => { await homeNavigation.gotoModule('收银'); await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(customer.phone); await page.locator('.ant-input-suffix', { hasText: '搜索' }).click(); await page.locator('.member_list .phone', { hasText: customer.phone }).click(); await page.locator('.project_list .number', { hasText: project.no }).click(); await page.locator('.pay_btn', { hasText: /^结\s算$/ }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); const rightPaymentInfoItem = page.locator('.right .paymentmain .paymentInfoItem'); const leftPaymentInfoItem = page.locator('.left .paymentmain .paymentInfoItem'); page.locator('.left .paymentmain .paymentInfoItem').filter({ hasText: '混合支付', }); await leftPaymentInfoItem.filter({ hasText: '混合支付' }).click(); // 银联 await rightPaymentInfoItem.filter({ hasText: '银联' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 支付宝 await rightPaymentInfoItem.filter({ hasText: '支付宝' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 微信 await rightPaymentInfoItem.filter({ hasText: '微信' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 欠款 await rightPaymentInfoItem.filter({ hasText: '欠款' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 现金 await rightPaymentInfoItem.filter({ hasText: '现金' }).first().click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 等待 /bill 请求完成并获取流水单号 const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && res.status() === 200; }), page.getByRole('button', { name: /结\s算/ }).click(), ]); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; expect(billNo).not.toBeNull(); }); await test.step('根据筛选条件进行搜索水单', async () => { await Promise.all([homeNavigation.gotoModule('流水'), page.waitForLoadState()]); await wasteBookBusinessRecordPage.gotoSubPage('对账流水'); // 流水单 const $flowChart = page.getByRole('cell', { name: billNo, exact: true }).first(); await page.getByTitle('全部水单类型').click(); await page.getByTitle('全部记账流水').click(); await page.getByRole('option', { name: '有记账无收款流水' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('全部水单类型').click(); await page.getByRole('option', { name: '购买项目/卖品' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('购买项目/卖品').click(); await page.getByRole('option', { name: '开卡' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('开卡').click(); await page.getByRole('option', { name: '充值' }).click(); await expect($flowChart).not.toBeVisible(); await page.getByTitle('充值').click(); await page.getByRole('option', { name: '全部水单类型' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('全部支付方式').click(); await page.getByRole('option', { name: '现金' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('现金').click(); await page.getByRole('option', { name: '银联' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('银联').click(); await page.getByRole('option', { name: '微信' }).click(); await expect($flowChart).toBeVisible(); await page.getByTitle('微信').click(); await page.getByRole('option', { name: '支付宝' }).click(); await expect($flowChart).toBeVisible(); await page.reload(); await page.getByRole('button').click(); await page.getByPlaceholder('输入流水单号搜索').fill(billNo); await page.locator('.search_btn > svg').click(); await expect($flowChart).toBeVisible(); }); }); test('对账流水只展示现金、支付宝、银联、微信', async ({ page, homeNavigation, createCustomer }) => { const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: '980' }; const customer = createCustomer; let billNo:string; await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => { await homeNavigation.gotoModule('收银'); await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(customer.phone); await page.locator('.ant-input-suffix', { hasText: '搜索' }).click(); await page.locator('.member_list .phone', { hasText: customer.phone }).click(); await page.locator('.project_list .number', { hasText: project.no }).click(); await page.locator('.pay_btn', { hasText: /^结\s算$/ }).click(); await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); const rightPaymentInfoItem = page.locator('.right .paymentmain .paymentInfoItem'); const leftPaymentInfoItem = page.locator('.left .paymentmain .paymentInfoItem'); page.locator('.left .paymentmain .paymentInfoItem').filter({ hasText: '混合支付', }); await leftPaymentInfoItem.filter({ hasText: '混合支付' }).click(); // 银联 await rightPaymentInfoItem.filter({ hasText: '银联' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 支付宝 await rightPaymentInfoItem.filter({ hasText: '支付宝' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 微信 await rightPaymentInfoItem.filter({ hasText: '微信' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 欠款 await rightPaymentInfoItem.filter({ hasText: '欠款' }).click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.popup_content input').fill('1'); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 现金 await rightPaymentInfoItem.filter({ hasText: '现金' }).first().click(); await page.getByRole('button', { name: '增加收款' }).click(); await page.locator('.number_tr').nth(2).getByRole('button').nth(3).click(); // 等待 /bill 请求完成并获取流水单号 const [response] = await Promise.all([ page.waitForResponse(async res => { return res.url().includes('/bill') && res.status() === 200; }), page.getByRole('button', { name: /结\s算/ }).click(), ]); const responseBody = await response.json(); billNo = responseBody?.content?.billNo; expect(billNo).not.toBeNull(); }); await test.step('进入流水-对账流水', async () => { // 进入流水模块 await homeNavigation.gotoModule('流水'); await page.locator('.ant-dropdown-link', { hasText: '营业记录' }).click(); await Promise.all([ await page.getByRole('menuitem', { name: '对账流水' }).click(), await page.waitForResponse( response => response.url().includes('/payment_flow') && response.status() === 200, ), ]); // 拿取现金列的列数 let index: number; const thLocator = page.locator('.m-table__header tr').last().locator('th'); const headers = await thLocator.allInnerTexts(); index = headers.findIndex(headerText => headerText.includes('支付方式')); if (index !== -1) { console.log(`"支付方式" 列是第 ${index + 1} 列`); } else { throw new Error('没有找到支付方式列'); } const paymentArray = ( await page .locator('.main-table-body_tr ', { hasText: billNo }) .locator(`td:nth-child(${index + 1})`) .allInnerTexts() ).map(i => i.trim()); console.log(paymentArray); expect.soft(paymentArray).toContain('银联'); expect.soft(paymentArray).toContain('现金'); expect.soft(paymentArray).toContain('支付宝'); expect.soft(paymentArray).toContain('微信'); expect.soft(paymentArray).toContain('微信'); expect(paymentArray).not.toContain('欠款'); }); }); }); test.describe('日结单', () => { /** * 获取员工列的各项数据 * @param {import('@playwright/test').Page} page * @param {number} index 员工列 * @param {{ 'name': string, 'lastPrice': number, 'price': number }[]} data 员工数据 */ async function getColumnPrice(page, index, data) { const columnName = ['现金', '消耗', '划卡', '客数', '客次', '拓客', '留客', '项目数', '卖品', '总耗业绩']; for (const name of columnName) { const price = await page .locator('.m-table__body-wrapper tr', { hasText: name }) .locator('td') .nth(index) .locator('.hlknum') .innerText(); console.log(`${name} -- ${price}`); const item = data.find(item => item.name === name); if (!item) { throw new Error(`没有找到 ${name} 数据`); } item.lastPrice = item.price; item.price = price.trim() === '--' ? 0 : Number(price); } } test('根据条件搜索日结单', async ({ page, homeNavigation }) => { const employee_1 = staffData.firstStore.firstSector.employee_2; const employee_2 = staffData.firstStore.secondSector.employee_2; const firstSectorName = staffData.firstStore.firstSector.name; const secondSectorName = staffData.firstStore.secondSector.name; await test.step('进入日结单模块', async () => { await homeNavigation.gotoModule('流水'); await page.getByText('日结单').first().click(); await expect(page.getByText('查看更多汇总数据')).toBeVisible(); await expect(page.getByRole('cell', { name: employee_1.name })).toBeVisible(); await expect(page.getByRole('cell', { name: employee_2.name })).toBeVisible(); }); await test.step('切换到一部门查看员工A', async () => { await page.locator('.shop-picker-store > .icon > svg').click(); await page.getByLabel(firstSectorName).check(); await page.getByRole('button', { name: /保\s存/ }).click(); await expect(page.getByRole('cell', { name: employee_1.name })).toBeVisible(); await expect(page.getByRole('cell', { name: employee_2.name })).not.toBeVisible(); }); await test.step('切换到二部门查看员工B', async () => { await page.locator('.shop-picker-store > .icon > svg').click(); await page.getByLabel(secondSectorName).check(); await page.getByRole('button', { name: /保\s存/ }).click(); await expect(page.getByRole('cell', { name: employee_1.name })).not.toBeVisible(); await expect(page.getByRole('cell', { name: employee_2.name })).toBeVisible(); }); }); test('查询日结单明细', async ({ page, homeNavigation, createCustomer }) => { // 使用的员工 const employee = staffData.firstStore.firstSector.employee_1; const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: 980 }; // 当前员工的日结单数据初始值 const employeeData = [ { name: '现金', price: 0, lastPrice: 0 }, { name: '消耗', price: 0, lastPrice: 0, }, { name: '划卡', price: 0, lastPrice: 0 }, { name: '客数', price: 0, lastPrice: 0, }, { name: '客次', price: 0, lastPrice: 0 }, { name: '拓客', price: 0, lastPrice: 0, }, { name: '留客', price: 0, lastPrice: 0 }, { name: '项目数', price: 0, lastPrice: 0, }, { name: '卖品', price: 0, lastPrice: 0 }, { name: '总耗业绩', price: 0, lastPrice: 0 }, ]; const customer = createCustomer; // 拿取员工列的列数 let index; await test.step('进入流水-日结单,获取员工数据', async () => { await homeNavigation.gotoModule('流水'); await page.locator('.top_tab').getByText('日结单').first().click(); await page .getByRole('row', { name: '现金', exact: true, }) .locator('div') .nth(1) .waitFor(); const thLocator = page.locator('.m-table__header-wrapper tr').last().locator('th'); await thLocator.last().waitFor(); const headers = await thLocator.allInnerTexts(); index = headers.findIndex(headerText => { return headerText.includes(employee.name) && headerText.includes(String(employee.id)); }); if (index !== -1) { console.log(`"${employee.name}" 列是第 ${index + 1} 列`); } else { throw new Error(`没有找到${employee.name}, ${employee.id}列`); } // 获取员工各个行的值 await getColumnPrice(page, index, employeeData); }); await test.step('开单结算,选择员工', async () => { await homeNavigation.gotoModule('收银'); await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(customer.phone); await page.locator('.ant-input-suffix', { hasText: '搜索' }).click(); await page.locator('.member_list .phone', { hasText: customer.phone }).click(); // 购买并消费项目A await page.locator('.project_list .number', { hasText: project.no }).click(); await page.locator('#shoppingCart .commodity_list li').first().click(); // 购买项目选择员工A await page.locator('.buy_item').first().locator('.buy_staff').getByRole('button').click(); await page .locator('.check_row', { hasText: employee.name, }) .getByRole('checkbox') .check(); await page.getByRole('button', { name: /确\s认/ }).click(); // 消耗项目选择员工A await page.locator('.use_item').first().locator('.use_staff').getByRole('button').click(); await page .locator('.check_row', { hasText: employee.name, }) .getByRole('checkbox') .check(); await page.getByRole('button', { name: /确\s认/ }).click(); await page.locator('.pay_btn', { hasText: /^结\s算$/ }).click(); // 去使用现金结算 await page.getByLabel('推送消费提醒').uncheck(); await page.getByLabel('结算签字').uncheck(); await page.locator('.left .paymentInfoItem', { hasText: '现金' }).click(); await page.getByRole('button', { name: /结\s算/ }).click(); }); await test.step('进入流水-日结单,比较员工数据变化', async () => { await homeNavigation.gotoModule('流水'); await page.locator('.top_tab').getByText('日结单').first().click(); await page .getByRole('row', { name: '现金', exact: true, }) .locator('div') .nth(1) .waitFor(); await getColumnPrice(page, index, employeeData); // 购买0.5 const checks = [ { name: '现金', expected: project.price * 0.5 }, { name: '消耗', expected: project.price * 0.6, }, { name: '客数', expected: 1 }, { name: '客次', expected: 1 }, { name: '拓客', expected: 1, }, { name: '留客', expected: 0 }, { name: '项目数', expected: 1 }, { name: '卖品', expected: 0, }, { name: '总耗业绩', expected: project.price * 0.6 }, ]; checks.forEach((check, index) => { const item = employeeData.find(i => i.name === check.name); if (!item) { throw new Error(`没有找到 ${check.name} 数据`); } if (index < checks.length - 1) { expect.soft(parseFloat((item.price - item.lastPrice).toFixed(2))).toBe(check.expected); } else { expect(parseFloat((item.price - item.lastPrice).toFixed(2))).toBe(check.expected); } }); }); }); });