优化代码

This commit is contained in:
LingandRX 2024-12-29 21:51:02 +08:00
parent 8c910d9086
commit 28430d0c9b
13 changed files with 211 additions and 230 deletions

View File

@ -2,6 +2,7 @@
const { defineConfig, devices } = require('@playwright/test'); const { defineConfig, devices } = require('@playwright/test');
// import dotenv from "dotenv"; // import dotenv from "dotenv";
import path from 'path'; import path from 'path';
import { firstAccount, secondAccount } from './tests/common/auth';
/** /**
* Read environment variables from file. * Read environment variables from file.
@ -15,9 +16,9 @@ require('dotenv').config({ path: path.resolve(__dirname, '.env') });
// ".env" + `${process.env.NODE_ENV ? "." + process.env.NODE_ENV : ""}` // ".env" + `${process.env.NODE_ENV ? "." + process.env.NODE_ENV : ""}`
// ), // ),
// }); // });
const authPath = '.auth/user.json';
const firstAuthFile = '.auth/admin_first.json'; const firstAuthFile = firstAccount.authFile;
const secondAuthFile = '.auth/admin_second.json'; const secondAuthFile = secondAccount.authFile;
/** /**
* @see https://playwright.dev/docs/test-configuration * @see https://playwright.dev/docs/test-configuration
*/ */

52
tests/common/auth.ts Normal file
View File

@ -0,0 +1,52 @@
import { getEnvVar } from '@/utils/envUtils';
import { readFileSync } from 'fs';
const authConfig = {
authFilePath: '.auth/',
indexedDBFilePath: '.auth/',
};
class AuthAccount {
account: string;
password: string;
authFile: string;
indexedDBFile: string;
constructor(account: string, password: string, authFile: string, indexedDBFile: string) {
this.account = account;
this.password = password;
this.authFile = authFile;
this.indexedDBFile = indexedDBFile;
}
static loadIndexedDBFile(account: string, accountArray: AuthAccount[]) {
try {
for (const item of accountArray) {
if (item.account === account) {
return JSON.parse(readFileSync(item.indexedDBFile, 'utf-8'));
}
}
} catch (e) {
throw new Error('indexedDB文件读取失败');
}
throw new Error('未找到该账户');
}
}
const firstAccount = new AuthAccount(
getEnvVar('boss_account'),
getEnvVar('boss_password'),
`${authConfig.authFilePath}${getEnvVar('boss_account')}.json`,
`${authConfig.indexedDBFilePath}${getEnvVar('boss_account')}_indexedDB.json`,
);
const secondAccount = new AuthAccount(
getEnvVar('boss_account_2'),
getEnvVar('boss_password_2'),
`${authConfig.authFilePath}${getEnvVar('boss_account_2')}.json`,
`${authConfig.indexedDBFilePath}${getEnvVar('boss_account_2')}_indexedDB.json`,
);
export { firstAccount, secondAccount, AuthAccount };

View File

@ -1,7 +1,7 @@
//@ts-check //@ts-check
// 默认为生产 // 默认为生产
let nodeEnv = process.env.NODE_ENV || "production"; let nodeEnv = process.env.NODE_ENV || 'production';
nodeEnv = nodeEnv === "staging" ? "production" : nodeEnv; nodeEnv = nodeEnv === 'staging' ? 'production' : nodeEnv;
/** /**
* - * -
@ -14,163 +14,163 @@ nodeEnv = nodeEnv === "staging" ? "production" : nodeEnv;
let staffData = { let staffData = {
firstStore: { firstStore: {
firstSector: { firstSector: {
name: "美容部", name: '美容部',
employee_1: { employee_1: {
name: "张伟", name: '张伟',
phone: "13812345678", phone: '13812345678',
id: { production: 3, test: 1 }, id: { production: 3, test: 1 },
}, },
employee_2: { employee_2: {
name: "李娜", name: '李娜',
phone: "13987654321", phone: '13987654321',
id: { production: 4, test: 2 }, id: { production: 4, test: 2 },
}, },
employee_3: { employee_3: {
name: "王芳", name: '王芳',
phone: "13723456789", phone: '13723456789',
id: { production: 5, test: 3 }, id: { production: 5, test: 3 },
}, },
employee_4: { employee_4: {
name: "陈刚", name: '陈刚',
phone: "13698765432", phone: '13698765432',
id: { production: 6, test: 4 }, id: { production: 6, test: 4 },
}, },
employee_5: { employee_5: {
name: "赵军", name: '赵军',
phone: "13512349876", phone: '13512349876',
id: { production: 7, test: 5 }, id: { production: 7, test: 5 },
}, },
employee_6: { employee_6: {
name: "刘强", name: '刘强',
phone: "13498761234", phone: '13498761234',
id: { production: 8, test: 6 }, id: { production: 8, test: 6 },
}, },
employee_7: { employee_7: {
name: "周萍", name: '周萍',
phone: "13365432109", phone: '13365432109',
id: { production: 9, test: 7 }, id: { production: 9, test: 7 },
}, },
employee_8: { employee_8: {
name: "吴浩", name: '吴浩',
phone: "13287654329", phone: '13287654329',
id: { production: 10, test: 8 }, id: { production: 10, test: 8 },
}, },
employee_9: { employee_9: {
name: "徐亮", name: '徐亮',
phone: "13123459876", phone: '13123459876',
id: { production: 11, test: 9 }, id: { production: 11, test: 9 },
}, },
employee_10: { employee_10: {
name: "杨雪", name: '杨雪',
phone: "13098761234", phone: '13098761234',
id: { production: 12, test: 10 }, id: { production: 12, test: 10 },
}, },
}, },
secondSector: { secondSector: {
name: "医美部", name: '医美部',
employee_1: { employee_1: {
name: "赵伟", name: '赵伟',
phone: "13923456789", phone: '13923456789',
id: { production: 13, test: 11 }, id: { production: 13, test: 11 },
}, },
employee_2: { employee_2: {
name: "钱丽", name: '钱丽',
phone: "13898765432", phone: '13898765432',
id: { production: 14, test: 12 }, id: { production: 14, test: 12 },
}, },
employee_3: { employee_3: {
name: "孙峰", name: '孙峰',
phone: "13712349876", phone: '13712349876',
id: { production: 15, test: 13 }, id: { production: 15, test: 13 },
}, },
employee_4: { employee_4: {
name: "李涛", name: '李涛',
phone: "13687654321", phone: '13687654321',
id: { production: 16, test: 14 }, id: { production: 16, test: 14 },
}, },
employee_5: { employee_5: {
name: "周慧", name: '周慧',
phone: "13598761234", phone: '13598761234',
id: { production: 17, test: 15 }, id: { production: 17, test: 15 },
}, },
employee_6: { employee_6: {
name: "吴凯", name: '吴凯',
phone: "13465432109", phone: '13465432109',
id: { production: 18, test: 16 }, id: { production: 18, test: 16 },
}, },
employee_7: { employee_7: {
name: "郑翔", name: '郑翔',
phone: "13387654329", phone: '13387654329',
id: { production: 19, test: 17 }, id: { production: 19, test: 17 },
}, },
employee_8: { employee_8: {
name: "冯敏", name: '冯敏',
phone: "13223459876", phone: '13223459876',
id: { production: 20, test: 18 }, id: { production: 20, test: 18 },
}, },
employee_9: { employee_9: {
name: "朱强", name: '朱强',
phone: "13198761234", phone: '13198761234',
id: { production: 21, test: 19 }, id: { production: 21, test: 19 },
}, },
employee_10: { employee_10: {
name: "何平", name: '何平',
phone: "13065432198", phone: '13065432198',
id: { production: 22, test: 20 }, id: { production: 22, test: 20 },
}, },
}, },
}, },
secondStore: { secondStore: {
name: "美容部", name: '美容部',
firstSector: { firstSector: {
employee_1: { employee_1: {
name: "张凯", name: '张凯',
phone: "13865432198", phone: '13865432198',
id: { production: 1, test: 1 }, id: { production: 1, test: 1 },
}, },
employee_2: { employee_2: {
name: "李军", name: '李军',
phone: "13923459876", phone: '13923459876',
id: { production: 2, test: 2 }, id: { production: 2, test: 2 },
}, },
employee_3: { employee_3: {
name: "王涛", name: '王涛',
phone: "13798761234", phone: '13798761234',
id: { production: 3, test: 3 }, id: { production: 3, test: 3 },
}, },
employee_4: { employee_4: {
name: "陈敏", name: '陈敏',
phone: "13654321987", phone: '13654321987',
id: { production: 4, test: 4 }, id: { production: 4, test: 4 },
}, },
employee_5: { employee_5: {
name: "赵峰", name: '赵峰',
phone: "13523456789", phone: '13523456789',
id: { production: 5, test: 5 }, id: { production: 5, test: 5 },
}, },
employee_6: { employee_6: {
name: "刘丽", name: '刘丽',
phone: "13487654321", phone: '13487654321',
id: { production: 6, test: 6 }, id: { production: 6, test: 6 },
}, },
employee_7: { employee_7: {
name: "周亮", name: '周亮',
phone: "13398765432", phone: '13398765432',
id: { production: 7, test: 7 }, id: { production: 7, test: 7 },
}, },
employee_8: { employee_8: {
name: "吴平", name: '吴平',
phone: "13212349876", phone: '13212349876',
id: { production: 8, test: 8 }, id: { production: 8, test: 8 },
}, },
employee_9: { employee_9: {
name: "徐浩", name: '徐浩',
phone: "13165432109", phone: '13165432109',
id: { production: 9, test: 9 }, id: { production: 9, test: 9 },
}, },
employee_10: { employee_10: {
name: "孙杰", name: '孙杰',
phone: "13087654329", phone: '13087654329',
id: { production: 10, test: 10 }, id: { production: 10, test: 10 },
}, },
}, },
@ -200,7 +200,7 @@ function init(staffData, nodeEnv) {
const store = updatedData[storeKey]; const store = updatedData[storeKey];
Object.keys(store).forEach(sectorKey => { Object.keys(store).forEach(sectorKey => {
const sector = store[sectorKey]; const sector = store[sectorKey];
if (typeof sector === "object" && sector !== null && sector.name) { if (typeof sector === 'object' && sector !== null && sector.name) {
updateIds(sector); // 更新每个 sector 的员工 id updateIds(sector); // 更新每个 sector 的员工 id
} }
}); });

View File

@ -1,7 +1,7 @@
import { test as base, expect } from '@playwright/test'; import { test as base, expect } from '@playwright/test';
import { HomeNavigation } from '@/pages/homeNavigationPage'; import { HomeNavigation } from '@/pages/homeNavigationPage';
import { writeIndexedDB } from '@/utils/indexedDBUtils'; import { writeIndexedDB } from '@/utils/indexedDBUtils';
import { readFileSync } from 'fs'; import { firstAccount, secondAccount, AuthAccount } from '@/common/auth';
type MyFixture = { type MyFixture = {
homeNavigation: HomeNavigation; homeNavigation: HomeNavigation;
@ -17,7 +17,6 @@ export const test = base.extend<MyFixture>({
if (!baseURL) throw new Error('baseURL is required'); if (!baseURL) throw new Error('baseURL is required');
await page.goto(baseURL); await page.goto(baseURL);
// await page.getByRole('button', { name: /开\s单/ }).waitFor();
const mobileObject = await page.evaluate(() => { const mobileObject = await page.evaluate(() => {
return window.localStorage.getItem('hlk_touch_mobile'); return window.localStorage.getItem('hlk_touch_mobile');
@ -26,36 +25,26 @@ export const test = base.extend<MyFixture>({
throw new Error('localStorage does not contain boss_account or boss_account2'); throw new Error('localStorage does not contain boss_account or boss_account2');
} }
const mobileString = JSON.parse(mobileObject); const mobileString = JSON.parse(mobileObject);
const bossAccount = process.env.boss_account || ''; const data = AuthAccount.loadIndexedDBFile(mobileString.val, [firstAccount, secondAccount]);
const bossAccount2 = process.env.boss_account2 || '';
let jsonData: [];
if (mobileString.val?.includes(bossAccount)) {
jsonData = JSON.parse(readFileSync('.auth/admin_first_indexeddb.json', 'utf-8'));
} else if (mobileString.val?.includes(bossAccount2)) {
jsonData = JSON.parse(readFileSync('.auth/admin_second_indexeddb.json', 'utf-8'));
} else {
throw new Error('localStorage does not contain boss_account or boss_account2');
}
await page.evaluate( await page.evaluate(
async ({ fnString, jsonData }) => { async ({ fnString, data }) => {
// 在浏览器上下文中动态创建函数
const writeIndexedDBFn = new Function('return ' + fnString)(); const writeIndexedDBFn = new Function('return ' + fnString)();
return await writeIndexedDBFn(data);
// 调用函数并传递参数
return await writeIndexedDBFn(jsonData);
}, },
{ fnString: writeIndexedDB.toString(), jsonData }, { fnString: writeIndexedDB.toString(), data },
); );
// await page.reload();
await page.waitForFunction( await page.waitForFunction(
async () => { async () => {
const databases = await indexedDB.databases(); // 获取所有数据库 const databases = await indexedDB.databases(); // 获取所有数据库
return databases.some(db => db.name === 'hlk_touch_test_init'); // 判断是否存在目标数据库 return databases.some(db => db.name === 'hlk_touch_test_init'); // 判断是否存在目标数据库
}, },
{ timeout: 30000 }, // 设置超时时间,默认为 30 秒 { timeout: 30000 },
); );
await page.reload();
await page.getByRole('button', { name: /开\s单/ }).waitFor(); await page.getByRole('button', { name: /开\s单/ }).waitFor();
await use(page); await use(page);

View File

@ -1,5 +1,6 @@
import { expect, type Page, type Locator } from '@playwright/test'; import { expect, type Page, type Locator } from '@playwright/test';
import { Customer } from '@/utils/customer'; import { Customer } from '@/utils/customer';
import { getNextAppointmentTime } from '@/utils/timeUtils';
/** /**
* *
@ -88,22 +89,6 @@ export class AppointmentPage {
await this.page.reload(); await this.page.reload();
}; };
/**
*
* - 8:00 --> 8:30
* - 8:30 --> 9:00
* - 8:50 --> 9:00
*/
getAppointmentTimesAvailable = (data = new Date()) => {
if (!(data instanceof Date)) {
throw new Error(`传入的${data}不是时间类型`);
}
const currentHour = data.getHours();
const nextTime = data.getMinutes() > 28 ? ':00' : ':30';
const hour = String(currentHour + (nextTime === ':00' ? 1 : 0)).padStart(2, '0');
return `${hour}${nextTime}`;
};
/** /**
* *
*/ */
@ -159,7 +144,7 @@ export class AppointmentPage {
* *
*/ */
openAppointmentCell = async (name: string, time?: Date, retry = 10) => { openAppointmentCell = async (name: string, time?: Date, retry = 10) => {
const currentAppointment = time ? this.getAppointmentTimesAvailable(time) : this.getAppointmentTimesAvailable(); const currentAppointment = time ? getNextAppointmentTime(time) : getNextAppointmentTime();
const $currentTime = this.$$time.filter({ hasText: currentAppointment }); const $currentTime = this.$$time.filter({ hasText: currentAppointment });
const currentTimeBoundingBox = await $currentTime.boundingBox(); const currentTimeBoundingBox = await $currentTime.boundingBox();

View File

@ -1,37 +1,21 @@
import { test as setup } from '@playwright/test'; import { test as setup } from '@playwright/test';
import { readIndexedDB } from '@/utils/indexedDBUtils'; import { readIndexedDB } from '@/utils/indexedDBUtils';
import fs from 'fs'; import fs from 'fs';
import { firstAccount, secondAccount } from '@/common/auth';
const firstAuthFile = '.auth/admin_first.json'; const regex = /^https?:\/\/(?:www\.)?hlk\.meiguanjia\.net\/$/;
const secondAuthFile = '.auth/admin_second.json';
const regex = /^https?:\/\/(www\.)?hlk\.meiguanjia\.net\/?(#\s)?$/;
/** @param personalizedPages 营销-个性化页面*/ /** @param personalizedPages 营销-个性化页面*/
const personalizedPages = '/#/marketing/brand/personalized'; const personalizedPages = '/#/marketing/brand/personalized';
class testAccount { const testAccountArray = [firstAccount, secondAccount];
constructor(account: string, password: string, path: string) {
this.account = account;
this.password = password;
this.path = path;
}
account: string; for (let testAccount of testAccountArray) {
password: string; setup(`租户${testAccount.account}总部管理员登录`, async ({ page, baseURL }) => {
path: string; const account = testAccount.account;
} const password = testAccount.password;
const authFile = testAccount.authFile;
const allAccounts = [ const indexedDBFile = testAccount.indexedDBFile;
new testAccount(process.env.boss_account!, process.env.boss_password!, firstAuthFile),
new testAccount(process.env.boss_account_2!, process.env.boss_password_2!, secondAuthFile),
];
for (let a of allAccounts) {
setup(`租户${a.account}总部管理员登录`, async ({ page, baseURL }) => {
const account = a.account;
const password = a.password;
const savePath = a.path;
const $phonePassIcon = page const $phonePassIcon = page
.locator('div', { has: page.getByRole('textbox', { name: '请输入您的手机号码' }) }) .locator('div', { has: page.getByRole('textbox', { name: '请输入您的手机号码' }) })
@ -64,7 +48,7 @@ for (let a of allAccounts) {
const readIndexedDBFn = new Function('return ' + readIndexedDBFnString)(); const readIndexedDBFn = new Function('return ' + readIndexedDBFnString)();
return await readIndexedDBFn(); return await readIndexedDBFn();
}, readIndexedDB.toString()); }, readIndexedDB.toString());
fs.writeFileSync('.auth/admin_first_indexeddb.json', JSON.stringify(result)); fs.writeFileSync(indexedDBFile, JSON.stringify(result));
await page.context().storageState({ path: savePath }); await page.context().storageState({ path: authFile });
}); });
} }

View File

@ -1,5 +1,7 @@
import { test as setup, expect } from "@playwright/test"; import { test as setup, expect } from '@playwright/test';
const authFileArray = [".auth/user_1.json", ".auth/user_2.json"]; import { getEnvVar } from '@/utils/envUtils';
const authFileArray = ['.auth/user_1.json', '.auth/user_2.json'];
const employee = [ const employee = [
{ {
phone: process.env.staff1_account, phone: process.env.staff1_account,
@ -10,21 +12,21 @@ const employee = [
password: process.env.staff2_password, password: process.env.staff2_password,
}, },
]; ];
setup.describe("员工登录", () => { setup.describe('员工登录', () => {
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
setup(`门店员工登录_${i}`, async ({ page }) => { setup(`门店员工登录_${i}`, async ({ page }) => {
const baseUrl = process.env.BASE_URL; const baseUrl = getEnvVar('BASE_URL');
const $phone = page.getByRole("textbox", { name: "请输入您的手机号码" }); const $phone = page.getByRole('textbox', { name: '请输入您的手机号码' });
const $password = page.getByRole("textbox", { name: "请输入登录密码" }); const $password = page.getByRole('textbox', { name: '请输入登录密码' });
const $login = page.getByRole("button", { name: /登\s录/ }); const $login = page.getByRole('button', { name: /登\s录/ });
await page.goto(baseUrl); await page.goto(baseUrl);
await $phone.fill(employee[i].phone); await $phone.fill(employee[i].phone);
const checkPhone = page.locator(".ant-row", { has: $phone }).locator(".pass_svg"); const checkPhone = page.locator('.ant-row', { has: $phone }).locator('.pass_svg');
await expect(checkPhone).toBeVisible(); await expect(checkPhone).toBeVisible();
await $password.fill(employee[i].password); await $password.fill(employee[i].password);
await page.getByLabel("请同意慧来客隐私政策和用户协议").check(); await page.getByLabel('请同意慧来客隐私政策和用户协议').check();
await $login.click(); await $login.click();
await expect(page.getByRole("button", { name: /开\s单/ })).toBeEnabled(); await expect(page.getByRole('button', { name: /开\s单/ })).toBeEnabled();
await page.context().storageState({ path: authFileArray[i] }); await page.context().storageState({ path: authFileArray[i] });
}); });
} }

View File

@ -1,24 +1,19 @@
// @ts-check // @ts-check
import { faker } from '@faker-js/faker/locale/zh_CN'; import { faker } from '@faker-js/faker/locale/zh_CN';
import { test, expect } from '@/fixtures/boss_common.js'; import { test, expect } from '@/fixtures/boss_common.js';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/common/staff';
import { HomeNavigation } from '@/pages/homeNavigationPage.js'; import { AppointmentOperation } from '@/pages/appointmentPage';
import { AppointmentPage } from '@/pages/appointmentPage.js'; import { getNextAppointmentTime } from '@/utils/timeUtils';
import { AppointmentOperation } from '@/pages/appointmentPage.js';
test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => {
const employee = staffData.firstStore.firstSector.employee_1; const employee = staffData.firstStore.firstSector.employee_1;
const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); const appointmentTime = getNextAppointmentTime();
const customer = createCustomer; const customer = createCustomer;
/** 当前可预约时间定位器 */ /** 当前可预约时间定位器 */
const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); const $time = page.locator('.times_table td').filter({ hasText: appointmentTime });
/** 顾客预约定位器 */ /** 顾客预约定位器 */
const $customerAppointment = page const $customerAppointment = page.locator('.a_userInfo').filter({ hasText: customer.phone }).filter({ hasText: customer.username });
.locator('.a_userInfo')
.filter({ hasText: customer.phone })
.filter({ hasText: customer.username });
await test.step('进行未指定预约', async () => { await test.step('进行未指定预约', async () => {
await homeNavigation.gotoModule('预约'); await homeNavigation.gotoModule('预约');
@ -96,7 +91,7 @@ test('使用预约单元格', async ({ page, homeNavigation, createCustomer, app
test('占用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { test('占用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => {
// 获取当前可预约时间 // 获取当前可预约时间
let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); let appointmentTime = getNextAppointmentTime();
// 创建顾客 // 创建顾客
const customer = createCustomer; const customer = createCustomer;
@ -106,7 +101,7 @@ test('占用预约单元格', async ({ page, homeNavigation, createCustomer, app
const remark = '占用预约单元格' + faker.string.alpha(2); const remark = '占用预约单元格' + faker.string.alpha(2);
// 当前可预约时间定位器 // 当前可预约时间定位器
const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); const $time = page.locator('.times_table td').filter({ hasText: appointmentTime });
// 员工定位器 // 员工定位器
const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name });
// 顾客预约定位器 // 顾客预约定位器
@ -202,18 +197,15 @@ test('占用预约单元格', async ({ page, homeNavigation, createCustomer, app
test.describe('预约状态', () => { test.describe('预约状态', () => {
test('预约-挂单-结算', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage, billSet }) => { test('预约-挂单-结算', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage, billSet }) => {
// 员工4
const employee = staffData.firstStore.firstSector.employee_4; const employee = staffData.firstStore.firstSector.employee_4;
// 使用的项目
const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: 980 }; const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: 980 };
// 获取当前可预约时间 const appointmentTime = getNextAppointmentTime();
const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable(); const customer = createCustomer;
// 当前可预约时间定位器 // 当前可预约时间定位器
const $time = page.locator('.times_table td').filter({ hasText: setAppointmentTime }); const $time = page.locator('.times_table td').filter({ hasText: appointmentTime });
// 员工定位器 // 员工定位器
const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name }); const $employee = page.locator('.room_table .tr .name_th').filter({ hasText: employee.name });
// 创建顾客
const customer = createCustomer;
// 获取设置的预约状态 // 获取设置的预约状态
let appointmentStatusSetting; let appointmentStatusSetting;
@ -311,74 +303,29 @@ test.describe('预约状态', () => {
}); });
}); });
test.afterEach(async ({ homeNavigation, wasteBookBusinessRecordPage, billSet }) => { test('测试预约操作', async ({ page, homeNavigation }) => {
// 撤回预约开的单
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 homeNavigation.gotoModule('预约');
await expect(page.getByText('张伟')).toBeVisible(); await page.getByRole('button', { name: /设\s置/ }).waitFor();
const $$userInfo = page.locator('.a_userInfo'); await page.waitForTimeout(3000);
// 获取顾客占用预约单元格信息 // await page.mouse.wheel(0, 500);
const $$appointmentBox = $$userInfo.locator('.appointment_content');
const names = await $$appointmentBox.locator('.name').allInnerTexts(); const $table = page.locator('.content_table');
const userPhones = await $$appointmentBox.locator('.user_phone').allInnerTexts(); const tableBox = await $table.boundingBox();
if (tableBox) {
const startX = tableBox.x + tableBox.width / 2; // 元素中心点 X 坐标
const startY = tableBox.y + tableBox.height / 2; // 元素中心点 Y 坐标
const userInfoData = names.map((name, index) => { // 移动鼠标到起始位置
return { await page.mouse.move(startX, startY);
name,
phone: userPhones[index],
};
});
// 取消预约占用 // 按下鼠标左键
// 获取占用框的数量 await page.mouse.down();
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 { // 向左拖动 (X 坐标减小Y 坐标保持不变)
// 等待请求成功,确认按钮和响应并行处理 await page.mouse.move(startX - 400, startY, { steps: 10 });
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(); await page.mouse.up();
} }
// 取消有取消预约按钮的预约 });
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();
});

View File

@ -1,6 +1,6 @@
// @ts-check // @ts-check
import { expect, test } from '@/fixtures/boss_common.js'; import { expect, test } from '@/fixtures/boss_common.js';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/common/staff';
import { Employees } from '@/fixtures/userconfig.js'; import { Employees } from '@/fixtures/userconfig.js';
import { Customer } from '@/utils/customer'; import { Customer } from '@/utils/customer';
import { KeepOnlyNumbers } from '@/utils/utils.js'; import { KeepOnlyNumbers } from '@/utils/utils.js';

View File

@ -4,7 +4,7 @@ import { test, expect } from '@/fixtures/boss_common.js';
import path from 'path'; import path from 'path';
import { decodeQR } from '@/utils/utils.js'; import { decodeQR } from '@/utils/utils.js';
import { Customer } from '@/utils/customer'; import { Customer } from '@/utils/customer';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/common/staff';
import { HomeNavigation } from '@/pages/homeNavigationPage.js'; import { HomeNavigation } from '@/pages/homeNavigationPage.js';
import fs from 'fs'; import fs from 'fs';
import { Page } from '@playwright/test'; import { Page } from '@playwright/test';

View File

@ -6,7 +6,7 @@ import { KeepOnlyNumbers } from '@/utils/utils.js';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { Employees, ProjectName } from '@/fixtures/userconfig.js'; import { Employees, ProjectName } from '@/fixtures/userconfig.js';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/common/staff';
test.describe('营业记录', () => { test.describe('营业记录', () => {
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {

7
tests/utils/envUtils.ts Normal file
View File

@ -0,0 +1,7 @@
export function getEnvVar(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(`Environment variable ${key} is not set`);
}
return value;
}

14
tests/utils/timeUtils.ts Normal file
View File

@ -0,0 +1,14 @@
/**
*
* - 8:00 --> 8:30
* - 8:30 --> 9:00
* - 8:50 --> 9:00
*/
function getNextAppointmentTime(date = new Date()) {
const currentHour = date.getHours();
const nextTime = date.getMinutes() > 28 ? ':00' : ':30';
const hour = String(currentHour + (nextTime === ':00' ? 1 : 0)).padStart(2, '0');
return `${hour}${nextTime}`;
}
export { getNextAppointmentTime };