// @ts-check import { faker } from '@faker-js/faker/locale/zh_CN'; import { test, expect } from '@/fixtures/boss_common.js'; import path from 'path'; import fs from 'fs'; import { staffData } from '@/fixtures/staff.js'; import { HomeNavigation } from '@/pages/homeNavigationPage.js'; import { AppointmentPage } from '@/pages/appointmentPage.js'; import { AppointmentOperation } from '@/pages/appointmentPage.js'; test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); const customer = createCustomer; const employee = staffData.firstStore.firstSector.employee_1; // 当前可预约时间定位器 const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); // 顾客预约定位器 const $customerAppointment = page .locator('.a_userInfo') .filter({ hasText: customer.phone }) .filter({ hasText: customer.username }); await test.step('进行未指定预约', async () => { await homeNavigation.gotoModule('预约'); // 将时间盒子滚动到当前窗口可见 await page.locator('.timeLine_Start').scrollIntoViewIfNeeded(); await $time.scrollIntoViewIfNeeded(); // 打开未指定预约单元格 await appointmentPage.openAppointmentCell('未指定'); // 新建预约 await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); // 选择顾客A await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); // 确认新建预约 await page.getByRole('button', { name: '确认新建' }).click(); // 判断新建预约成功 await expect($customerAppointment).toBeVisible(); await appointmentPage.openAppointmentDetail(customer.phone); await expect(page.getByText('未到店', { exact: true })).toBeVisible(); await appointmentPage.closeAppointmentDetail(); }); await test.step('取消未指定预约', async () => { // 打开未指定预约单元格 await appointmentPage.openAppointmentDetail(customer.phone); await page.getByRole('button', { name: '取消预约' }).click(); // 确认取消预约 await Promise.all([ page.getByRole('button', { name: /确\s认/ }).click(), page.waitForResponse(response => response.url().includes('/reservation_num') && response.status() === 200), ]); // 判断取消预约成功 await expect($customerAppointment).not.toBeVisible(); }); await test.step('指定员工进行预约', async () => { await $time.scrollIntoViewIfNeeded(); // 打开预约选择窗口 await expect(async () => { await appointmentPage.openAppointmentCell(employee.name); await expect(page.locator('.popup_content', { hasText: '选择操作' })).toBeVisible({ timeout: 2000 }); }).toPass(); // 新建预约 await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); // 在预约窗口确认顾客信息 await expect(page.locator('.newAppointmentContent .content .phone')).toContainText(customer.phone); await expect( page.locator('.content .right .right_ul_li', { hasText: employee.name, }), ).toBeVisible(); // 判断新建预约成功 await page.getByRole('button', { name: '确认新建' }).click(); await expect($customerAppointment).toBeVisible(); await appointmentPage.openAppointmentDetail(customer.phone); await expect(page.getByText('未到店', { exact: true })).toBeVisible(); await appointmentPage.closeAppointmentDetail(); }); await test.step('取消预约', async () => { await page.locator('.a_userInfo', { hasText: customer.username }).first().locator('.user_name_info').click(); await page.getByRole('button', { name: '取消预约' }).click(); await page.getByRole('button', { name: /确\s认/ }).click(); await expect($customerAppointment).not.toBeVisible(); }); }); test('占用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { // 获取当前可预约时间 let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); // 创建顾客 const customer = createCustomer; // 员工王芳 const employee = staffData.firstStore.firstSector.employee_3; // 占用单元格备注 const remark = '占用预约单元格' + faker.string.alpha(2); // 当前可预约时间定位器 const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); // 员工定位器 const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); // 顾客预约定位器 const $customerAppointment = page .locator('.a_userInfo') .filter({ hasText: customer.phone }) .filter({ hasText: customer.username }); await test.step('占用单元格', async () => { // 进入预约模块 await homeNavigation.gotoModule('预约'); // 窗口滚动到员工、预约时间可见 await $time.scrollIntoViewIfNeeded(); await $employee.scrollIntoViewIfNeeded(); // 长按目标位置 await appointmentPage.openAppointmentCell(employee.name); // 进行占用单元格 await Promise.all([ appointmentPage.operationAppointment(AppointmentOperation.ADD_OCCUPY), page.waitForResponse(response => response.url().includes('/reservation') && response.status() === 200), ]); // 占用成功 await expect(page.locator('.ant-message', { hasText: '占用成功' })).toBeVisible(); }); await test.step('备注占用单元格', async () => { // 特殊等待,页面内容已变化,但是去操作时,变为上一个状态,只能等待再去操作 await page.waitForTimeout(2000); // 打开占用操作窗口 await page.locator('.a_userInfo .occupy', { hasText: '占用' }).last().click(); // 进入添加备注 await appointmentPage.operationAppointment(AppointmentOperation.ADD_REMARK); // 进行添加备注,确认添加备注成功 await page.getByPlaceholder('请输入备注').fill(remark); await Promise.all([ page .locator('.popup_content') .getByRole('button', { name: /确\s认/ }) .click(), page.waitForResponse(response => response.url().includes('/reservation_num') && response.status() === 200), ]); // 判断备注后的单元格存在 await expect(page.locator('.a_userInfo .occupy', { hasText: remark })).toBeVisible(); }); await test.step('取消占用单元格', async () => { // 打开占用操作窗口 await page.locator('.a_userInfo .occupy', { hasText: remark }).click(); // 取消占用 await appointmentPage.operationAppointment(AppointmentOperation.CANCEL_OCCUPY); await expect(async () => { const confirm = page.locator('.popup_content').getByRole('button', { name: /确\s认/ }); await confirm.click(); await expect(confirm).not.toBeVisible(); }).toPass(); // 判断取消占用成功 await expect(page.locator('.ant-message', { hasText: '取消占用成功' })).toBeVisible(); await expect(page.locator('.a_userInfo .occupy', { hasText: remark })).not.toBeVisible(); }); await test.step('取消占用后,能够进行预约', async () => { // 重新选择员工王芳,进行预约单元格 await $employee.scrollIntoViewIfNeeded(); await $time.scrollIntoViewIfNeeded(); // 打开预约单元格 await appointmentPage.openAppointmentCell(employee.name); // 选择顾客去创建预约 await expect(async () => { await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); await page.getByText('选择会员').waitFor({ timeout: 2000 }); }).toPass(); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); // 确认进入新建预约页面 await expect( page.locator('.content .right .right_ul_li', { hasText: employee.name, }), ).toBeVisible(); await expect(page.locator('.newAppointmentContent .header_title')).toContainText('新建预约'); await expect(page.locator('.newAppointmentContent .content .phone')).toContainText(customer.phone); // 新建后,确认新建成功 await page.getByRole('button', { name: '确认新建' }).click(); await expect($customerAppointment).toBeVisible(); }); }); test.describe('预约状态', () => { test('预约-挂单-结算', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage, billSet }) => { // 员工4 const employee = staffData.firstStore.firstSector.employee_4; // 使用的项目 const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: 980 }; // 获取当前可预约时间 const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); // 当前可预约时间定位器 const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); // 员工定位器 const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); // 创建顾客 const customer = createCustomer; // 获取设置的预约状态 let appointmentStatusSetting; await test.step('获取预约状态', async () => { await homeNavigation.gotoModule('预约'); appointmentStatusSetting = await appointmentPage.getAppointmentStatusSetting(); }); await test.step('进行创建预约', async () => { await $time.scrollIntoViewIfNeeded(); await $employee.scrollIntoViewIfNeeded(); // 打开预约单元格,进行预约 await appointmentPage.openAppointmentCell(employee.name, undefined, 10); await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); await expect.soft(page.locator('.newAppointmentContent .header_title')).toContainText('新建预约'); await expect(page.locator('.newAppointmentContent .content .phone')).toContainText(customer.phone); await page.getByRole('button', { name: '确认新建' }).click(); await expect(page.locator('.ant-message', { hasText: '预约成功' })).toBeVisible(); }); await test.step('判断预约,状态为未到店', async () => { const { customerColor, customerStatus } = await appointmentPage.getCustomerAppointmentStatus(customer); // 进入未到店状态 expect.soft(customerColor).toEqual(appointmentStatusSetting.NORMAL.color); expect(customerStatus).toEqual(appointmentStatusSetting.NORMAL.name); }); await test.step('进行挂单,状态为已开单', async () => { await page .locator('.a_userInfo', { hasText: customer.username }) .first() .locator('.user_name_info') .click(); // 预约进行挂单 await page.getByRole('button', { name: '到店开单' }).click(); await page .locator('.project_list') .filter({ hasText: project.name }) .filter({ hasText: project.no }) .click(); await page.locator('.hold_bill', { hasText: '挂单' }).click(); await expect(page.locator('.ant-message', { hasText: '挂单成功' })).toBeVisible(); await homeNavigation.gotoModule('预约'); // 获取当前预约的状态 const { customerColor, customerStatus } = await appointmentPage.getCustomerAppointmentStatus(customer); // 进入未到店状态 expect.soft(customerColor).toEqual(appointmentStatusSetting.BILL.color); expect(customerStatus).toEqual(appointmentStatusSetting.BILL.name); }); await test.step('进行结算,状态为已结算', async () => { await page .locator('.a_userInfo', { hasText: customer.username }) .first() .locator('.user_name_info') .click(); 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('.paytype .paymentInfoItem', { hasText: '现金' }).click(); // 结算 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(); const billNo = responseBody?.content?.billNo; expect(billNo).not.toBeNull(); billSet.add(billNo); // 处理结算后的弹窗 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 homeNavigation.gotoModule('预约'); }); // 进入预约模块 await homeNavigation.gotoModule('预约'); // 获取当前创建预约的状态 const { customerColor, customerStatus } = await appointmentPage.getCustomerAppointmentStatus(customer); // 进入未到店状态 expect.soft(customerColor).toEqual(appointmentStatusSetting.SETTLED.color); expect(customerStatus).toEqual(appointmentStatusSetting.SETTLED.name); }); }); test.skip('预约-过期', async ({ page, createCustomer, appointmentPage, customerPage }) => { // 获取预约时间 let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); // 创建顾客 const customer = createCustomer; // 员工赵军 const employee = staffData.firstStore.firstSector.employee_5; // 预约时间定位器 const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); // 员工定位器 const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); await test.step('选择顾客进行预约', async () => { await $employee.scrollIntoViewIfNeeded(); await $time.scrollIntoViewIfNeeded(); await appointmentPage.openAppointmentCell(employee.name); await expect(async () => { await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); await expect(page.getByText('选择会员')).toBeVisible(); }).toPass(); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); await expect(page.locator('.newAppointmentContent .header_title')).toContainText('新建预约'); await expect(page.locator('.newAppointmentContent .content .phone')).toContainText(customer.phone); await page.getByRole('button', { name: '确认新建' }).click(); await expect(page.locator('.ant-message', { hasText: '预约成功' })).toBeVisible(); }); }); }); test.describe.skip('预约设置', () => { test('看板设置', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); const customer = createCustomer; const employee = staffData.firstStore.firstSector.employee_6; let switchStatus; // let nowNormalColor; // 当前模块颜色 let nowAppointmentColor; //当前预约颜色 const customerRemarkLocator = page .locator('.appointment_content', { hasText: customer.username }) .locator('.remark_info', { hasText: '测试看板设置' }); const normal = page.locator('.herder_right ul > li', { hasText: '未到店' }); const merchantRemarkLocator = page.locator('li').filter({ hasText: '商家备注' }); const $time = page.locator('.times_table td'); const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); await test.step('创建预约', async () => { await homeNavigation.gotoModule('预约'); await $time.filter({ hasText: setAppointmentTime }).scrollIntoViewIfNeeded(); await $employee.scrollIntoViewIfNeeded(); await appointmentPage.openAppointmentCell(employee.name); await page.locator('.popup_content', { hasText: '选择操作' }).waitFor(); await expect(async () => { await appointmentPage.operationAppointment(AppointmentOperation.ADD_APPOINT); await page.getByText('选择会员').waitFor({ timeout: 2000 }); }).toPass(); await customerPage.searchCustomer(customer.phone); await customerPage.selectSearchCustomer(customer.phone); await expect(page.locator('.newAppointmentContent .header_title')).toContainText('新建预约'); await expect(page.locator('.newAppointmentContent .content .phone')).toContainText(customer.phone); await page.getByRole('button', { name: '确认新建' }).click(); await expect(page.locator('.ant-message', { hasText: '预约成功' })).toBeVisible(); }); await test.step('给顾客预约添加备注', async () => { await page .locator('.a_userInfo', { hasText: customer.username }) .first() .locator('.user_name_info') .click(); await page.locator('.my_remark .write').click(); await page.getByPlaceholder('请输入1-100个字符备注内容').fill('测试看板设置'); await page.getByRole('button', { name: /确\s认/ }).click(); await page.locator('.info_center .close').click(); await expect(customerRemarkLocator).toBeVisible(); }); await test.step('修改商家备注为关闭、修改未到店的颜色', async () => { await page.getByRole('button', { name: /设\s置/ }).click(); await page.getByRole('menuitem', { name: '看板设置' }).click(); let switchStatus = page.locator('li').filter({ hasText: '商家备注' }).getByRole('switch'); await expect(switchStatus).toHaveText('开'); await page.locator('li').filter({ hasText: '商家备注' }).getByRole('switch').click(); await expect(switchStatus).toHaveText('关'); // 修改未到店的颜色 await page .locator('li') .filter({ hasText: /^未到店$/ }) .locator('div') .nth(3) .click(); // rgb(0, 0, 0) // D42525 await page.getByLabel('r', { exact: true }).fill('0'); await page.getByLabel('g', { exact: true }).fill('0'); await page.getByLabel('b', { exact: true }).fill('0'); await page.getByText('看板信息').click(); await page.locator('.close > svg > use').first().click(); // 获取预约状态Color nowNormalColor = await normal.locator('.status').evaluate(e => { return window.getComputedStyle(e).backgroundColor; }); // 获取当前创建预约的状态 nowAppointmentColor = await page .locator('.a_userInfo', { hasText: customer.username }) .first() .locator('.appointment') .evaluate(e => { return window.getComputedStyle(e).backgroundColor; }); // 判断商家备注被关闭 await expect(customerRemarkLocator).not.toBeVisible(); // 判断未到店颜色被修改 expect.soft(nowNormalColor).toEqual('rgb(0, 0, 0)'); // 判断预约颜色被修改 expect(nowAppointmentColor).toEqual('rgb(0, 0, 0)'); }); await test.step('恢复默认值', async () => { // 恢复默认 await page.getByRole('button', { name: /设\s置/ }).click(); await page.getByRole('menuitem', { name: '看板设置' }).click(); switchStatus = merchantRemarkLocator.getByRole('switch'); await expect(switchStatus).toHaveText('关'); await merchantRemarkLocator.getByRole('switch').click(); switchStatus = merchantRemarkLocator.getByRole('switch'); await expect(switchStatus).toHaveText('开'); await page .locator('li') .filter({ hasText: /^未到店$/ }) .locator('span') .nth(1) .click(); // await page.getByLabel('hex', { exact: true }).fill("D42525"); await page.getByLabel('r', { exact: true }).fill('212'); await page.getByLabel('g', { exact: true }).fill('37'); await page.getByLabel('b', { exact: true }).fill('37'); await page.getByText('状态颜色').click(); await page.locator('.m_sliding_bg').click(); // 获取预约状态Color nowNormalColor = await normal.locator('.status').evaluate(e => { return window.getComputedStyle(e).backgroundColor; }); expect(nowNormalColor).toEqual('rgb(212, 37, 37)'); }); }); test('导出图片', async ({ page, homeNavigation }) => { await homeNavigation.gotoModule('预约'); await page.locator('.herder_right').getByRole('button').nth(1).click(); const downloadPromise = page.waitForEvent('download'); await page.getByRole('menuitem', { name: '导出图片' }).click(); const download = await downloadPromise; // 获取下载的文件名 const fileName = download.suggestedFilename(); // 指定保存下载文件的路径 const downloadPath = path.join(__dirname, `@/imgs/${fileName}`); // 保存下载的文件 await download.saveAs(downloadPath); // 验证文件是否成功保存 const fileExists = fs.existsSync(downloadPath); expect(fileExists).toBeTruthy(); }); test('视图调整', async ({ page, homeNavigation, appointmentPage }) => { const cellBox = { height: 0, lastHeight: 0, width: 0, lastWidth: 0 }; // 单元格 const employee = staffData.firstStore.firstSector.employee_1; // 员工张伟 let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); // 预约时间 // 获取当前单元格的宽高 const $time = page.locator('.times_table td', { hasText: setAppointmentTime }); const $employee = page.locator('.room_table .tr .name_th', { hasText: employee.name }).first(); await homeNavigation.gotoModule('预约'); await $time.scrollIntoViewIfNeeded(); await $employee.scrollIntoViewIfNeeded(); let timeBox = await $time.boundingBox(); let employeeBox = await $employee.boundingBox(); cellBox.lastHeight = timeBox != null ? timeBox.height : 0; cellBox.lastWidth = employeeBox != null ? employeeBox.width : 0; await page.locator('.herder_right').getByRole('button').nth(1).click(); await page.getByRole('menuitem', { name: '视图调整' }).click(); // 打开了自定义的宽高 await expect(page.locator('.set_w_h_box .set_w_h_title', { hasText: '自定义预约表格宽高' })).toBeVisible(); const setHeightLocator = page.locator('.set_w_h_content', { hasText: '高度' }).getByRole('slider'); const setWidthLocator = page.locator('.set_w_h_content', { hasText: '宽度' }).getByRole('slider'); // 获取可以移动最大和最小的宽度和高度 const heightValueMax = Number(await setHeightLocator.getAttribute('aria-valuemax')); const heightValueMin = Number(await setHeightLocator.getAttribute('aria-valuemin')); const widthValueMax = Number(await setWidthLocator.getAttribute('aria-valuemax')); const widthValueMin = Number(await setWidthLocator.getAttribute('aria-valuemin')); const heightDistance = heightValueMax - heightValueMin; const widthDistance = widthValueMax - widthValueMin; const setHeightBox = await setHeightLocator.boundingBox(); const setWidthBox = await setWidthLocator.boundingBox(); if (setHeightBox == null || setWidthBox == null) { throw new Error('获取设置高度和宽度的定位器失败'); } await test.step('设置高度', async () => { await $time.scrollIntoViewIfNeeded(); // 设置单元格高度 await page.mouse.move(setHeightBox.x + setHeightBox.width / 2, setHeightBox.y + setHeightBox.height / 2); await page.mouse.down(); await page.mouse.move( setHeightBox.x + setHeightBox.width / 2 + heightDistance, setHeightBox.y + setHeightBox.height / 2, ); await page.mouse.down(); timeBox = await $time.boundingBox(); cellBox.height = timeBox != null ? timeBox.height : 0; expect(cellBox.height - cellBox.lastHeight).toEqual(heightDistance); }); await test.step('设置宽度', async () => { // 设置单元格宽度 await page.mouse.move(setWidthBox.x + setWidthBox.width / 2, setWidthBox.y + setWidthBox.height / 2); await page.mouse.move(setWidthBox.x, setWidthBox.y); await page.mouse.down(); await page.mouse.move( setWidthBox.x + setWidthBox.width / 2 + widthDistance, setWidthBox.y + setWidthBox.height / 2, ); await page.mouse.down(); await $employee.scrollIntoViewIfNeeded(); employeeBox = await $employee.boundingBox(); cellBox.width = employeeBox?.width ?? 0; expect(cellBox.width - cellBox.lastWidth).toEqual(widthDistance); }); // 恢复默认视图设置 await page.locator('.resetting').click(); }); }); test.afterEach(async ({ homeNavigation, wasteBookBusinessRecordPage, billSet }) => { // 撤回预约开的单 await homeNavigation.gotoModule('流水'); await wasteBookBusinessRecordPage.revokeOrder(billSet); }); // 清除当前时间之后所有的预约和占用 test.afterAll(async ({ browser, baseURL }) => { const page = await browser.newPage(); await page.goto(baseURL ?? ''); const homeNavigation = new HomeNavigation(page); const appointmentPage = new AppointmentPage(page); await homeNavigation.gotoModule('预约'); await expect(page.getByText('张伟')).toBeVisible(); const $$userInfo = page.locator('.a_userInfo'); // 获取顾客占用预约单元格信息 const $$appointmentBox = $$userInfo.locator('.appointment_content'); const names = await $$appointmentBox.locator('.name').allInnerTexts(); const userPhones = await $$appointmentBox.locator('.user_phone').allInnerTexts(); const userInfoData = names.map((name, index) => { return { name, phone: userPhones[index], }; }); // 取消预约占用 // 获取占用框的数量 let occupyBoxCount = await $$userInfo.locator('.occupy').count(); while (occupyBoxCount > 0) { // 每次都使用 first() 获取第一个占用框 const $occupyBox = $$userInfo.locator('.occupy').first(); await $occupyBox.click(); await page .locator('.popup_content .class_content') .getByText(/^取消占用$/) .click(); try { // 等待请求成功,确认按钮和响应并行处理 await Promise.all([ page.waitForResponse(res => res.url().includes('/reservation') && res.status() === 200, { timeout: 5000, }), // 添加超时 page.getByRole('button', { name: /确\s认/ }).click(), ]); } catch (error) { console.error('取消预约时出现错误: ', error); } await page.waitForTimeout(500); occupyBoxCount = await $$userInfo.locator('.occupy').count(); } // 取消有取消预约按钮的预约 for (const user of userInfoData) { const { name } = user; const $appointmentBox = $$appointmentBox.filter({ has: page.getByText(name) }).locator('.user_name_info'); const appointCount = await $appointmentBox.count(); if (appointCount === 1 && (await appointmentPage.elementCenterInViewport($appointmentBox.first()))) { await $appointmentBox.first().click(); await appointmentPage.cancelAppoint(); } } await page.close(); });