优化代码
This commit is contained in:
parent
8c910d9086
commit
28430d0c9b
@ -2,6 +2,7 @@
|
||||
const { defineConfig, devices } = require('@playwright/test');
|
||||
// import dotenv from "dotenv";
|
||||
import path from 'path';
|
||||
import { firstAccount, secondAccount } from './tests/common/auth';
|
||||
|
||||
/**
|
||||
* 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 : ""}`
|
||||
// ),
|
||||
// });
|
||||
const authPath = '.auth/user.json';
|
||||
const firstAuthFile = '.auth/admin_first.json';
|
||||
const secondAuthFile = '.auth/admin_second.json';
|
||||
|
||||
const firstAuthFile = firstAccount.authFile;
|
||||
const secondAuthFile = secondAccount.authFile;
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
|
||||
52
tests/common/auth.ts
Normal file
52
tests/common/auth.ts
Normal 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 };
|
||||
@ -1,7 +1,7 @@
|
||||
//@ts-check
|
||||
// 默认为生产
|
||||
let nodeEnv = process.env.NODE_ENV || "production";
|
||||
nodeEnv = nodeEnv === "staging" ? "production" : nodeEnv;
|
||||
let nodeEnv = process.env.NODE_ENV || 'production';
|
||||
nodeEnv = nodeEnv === 'staging' ? 'production' : nodeEnv;
|
||||
|
||||
/**
|
||||
* - 员工数据 门店 部门 员工 姓名
|
||||
@ -14,163 +14,163 @@ nodeEnv = nodeEnv === "staging" ? "production" : nodeEnv;
|
||||
let staffData = {
|
||||
firstStore: {
|
||||
firstSector: {
|
||||
name: "美容部",
|
||||
name: '美容部',
|
||||
employee_1: {
|
||||
name: "张伟",
|
||||
phone: "13812345678",
|
||||
name: '张伟',
|
||||
phone: '13812345678',
|
||||
id: { production: 3, test: 1 },
|
||||
},
|
||||
employee_2: {
|
||||
name: "李娜",
|
||||
phone: "13987654321",
|
||||
name: '李娜',
|
||||
phone: '13987654321',
|
||||
id: { production: 4, test: 2 },
|
||||
},
|
||||
employee_3: {
|
||||
name: "王芳",
|
||||
phone: "13723456789",
|
||||
name: '王芳',
|
||||
phone: '13723456789',
|
||||
id: { production: 5, test: 3 },
|
||||
},
|
||||
employee_4: {
|
||||
name: "陈刚",
|
||||
phone: "13698765432",
|
||||
name: '陈刚',
|
||||
phone: '13698765432',
|
||||
id: { production: 6, test: 4 },
|
||||
},
|
||||
employee_5: {
|
||||
name: "赵军",
|
||||
phone: "13512349876",
|
||||
name: '赵军',
|
||||
phone: '13512349876',
|
||||
id: { production: 7, test: 5 },
|
||||
},
|
||||
employee_6: {
|
||||
name: "刘强",
|
||||
phone: "13498761234",
|
||||
name: '刘强',
|
||||
phone: '13498761234',
|
||||
id: { production: 8, test: 6 },
|
||||
},
|
||||
employee_7: {
|
||||
name: "周萍",
|
||||
phone: "13365432109",
|
||||
name: '周萍',
|
||||
phone: '13365432109',
|
||||
id: { production: 9, test: 7 },
|
||||
},
|
||||
employee_8: {
|
||||
name: "吴浩",
|
||||
phone: "13287654329",
|
||||
name: '吴浩',
|
||||
phone: '13287654329',
|
||||
id: { production: 10, test: 8 },
|
||||
},
|
||||
employee_9: {
|
||||
name: "徐亮",
|
||||
phone: "13123459876",
|
||||
name: '徐亮',
|
||||
phone: '13123459876',
|
||||
id: { production: 11, test: 9 },
|
||||
},
|
||||
employee_10: {
|
||||
name: "杨雪",
|
||||
phone: "13098761234",
|
||||
name: '杨雪',
|
||||
phone: '13098761234',
|
||||
id: { production: 12, test: 10 },
|
||||
},
|
||||
},
|
||||
secondSector: {
|
||||
name: "医美部",
|
||||
name: '医美部',
|
||||
employee_1: {
|
||||
name: "赵伟",
|
||||
phone: "13923456789",
|
||||
name: '赵伟',
|
||||
phone: '13923456789',
|
||||
id: { production: 13, test: 11 },
|
||||
},
|
||||
employee_2: {
|
||||
name: "钱丽",
|
||||
phone: "13898765432",
|
||||
name: '钱丽',
|
||||
phone: '13898765432',
|
||||
id: { production: 14, test: 12 },
|
||||
},
|
||||
employee_3: {
|
||||
name: "孙峰",
|
||||
phone: "13712349876",
|
||||
name: '孙峰',
|
||||
phone: '13712349876',
|
||||
id: { production: 15, test: 13 },
|
||||
},
|
||||
employee_4: {
|
||||
name: "李涛",
|
||||
phone: "13687654321",
|
||||
name: '李涛',
|
||||
phone: '13687654321',
|
||||
id: { production: 16, test: 14 },
|
||||
},
|
||||
employee_5: {
|
||||
name: "周慧",
|
||||
phone: "13598761234",
|
||||
name: '周慧',
|
||||
phone: '13598761234',
|
||||
id: { production: 17, test: 15 },
|
||||
},
|
||||
employee_6: {
|
||||
name: "吴凯",
|
||||
phone: "13465432109",
|
||||
name: '吴凯',
|
||||
phone: '13465432109',
|
||||
id: { production: 18, test: 16 },
|
||||
},
|
||||
employee_7: {
|
||||
name: "郑翔",
|
||||
phone: "13387654329",
|
||||
name: '郑翔',
|
||||
phone: '13387654329',
|
||||
id: { production: 19, test: 17 },
|
||||
},
|
||||
employee_8: {
|
||||
name: "冯敏",
|
||||
phone: "13223459876",
|
||||
name: '冯敏',
|
||||
phone: '13223459876',
|
||||
id: { production: 20, test: 18 },
|
||||
},
|
||||
employee_9: {
|
||||
name: "朱强",
|
||||
phone: "13198761234",
|
||||
name: '朱强',
|
||||
phone: '13198761234',
|
||||
id: { production: 21, test: 19 },
|
||||
},
|
||||
employee_10: {
|
||||
name: "何平",
|
||||
phone: "13065432198",
|
||||
name: '何平',
|
||||
phone: '13065432198',
|
||||
id: { production: 22, test: 20 },
|
||||
},
|
||||
},
|
||||
},
|
||||
secondStore: {
|
||||
name: "美容部",
|
||||
name: '美容部',
|
||||
firstSector: {
|
||||
employee_1: {
|
||||
name: "张凯",
|
||||
phone: "13865432198",
|
||||
name: '张凯',
|
||||
phone: '13865432198',
|
||||
id: { production: 1, test: 1 },
|
||||
},
|
||||
employee_2: {
|
||||
name: "李军",
|
||||
phone: "13923459876",
|
||||
name: '李军',
|
||||
phone: '13923459876',
|
||||
id: { production: 2, test: 2 },
|
||||
},
|
||||
employee_3: {
|
||||
name: "王涛",
|
||||
phone: "13798761234",
|
||||
name: '王涛',
|
||||
phone: '13798761234',
|
||||
id: { production: 3, test: 3 },
|
||||
},
|
||||
employee_4: {
|
||||
name: "陈敏",
|
||||
phone: "13654321987",
|
||||
name: '陈敏',
|
||||
phone: '13654321987',
|
||||
id: { production: 4, test: 4 },
|
||||
},
|
||||
employee_5: {
|
||||
name: "赵峰",
|
||||
phone: "13523456789",
|
||||
name: '赵峰',
|
||||
phone: '13523456789',
|
||||
id: { production: 5, test: 5 },
|
||||
},
|
||||
employee_6: {
|
||||
name: "刘丽",
|
||||
phone: "13487654321",
|
||||
name: '刘丽',
|
||||
phone: '13487654321',
|
||||
id: { production: 6, test: 6 },
|
||||
},
|
||||
employee_7: {
|
||||
name: "周亮",
|
||||
phone: "13398765432",
|
||||
name: '周亮',
|
||||
phone: '13398765432',
|
||||
id: { production: 7, test: 7 },
|
||||
},
|
||||
employee_8: {
|
||||
name: "吴平",
|
||||
phone: "13212349876",
|
||||
name: '吴平',
|
||||
phone: '13212349876',
|
||||
id: { production: 8, test: 8 },
|
||||
},
|
||||
employee_9: {
|
||||
name: "徐浩",
|
||||
phone: "13165432109",
|
||||
name: '徐浩',
|
||||
phone: '13165432109',
|
||||
id: { production: 9, test: 9 },
|
||||
},
|
||||
employee_10: {
|
||||
name: "孙杰",
|
||||
phone: "13087654329",
|
||||
name: '孙杰',
|
||||
phone: '13087654329',
|
||||
id: { production: 10, test: 10 },
|
||||
},
|
||||
},
|
||||
@ -200,7 +200,7 @@ function init(staffData, nodeEnv) {
|
||||
const store = updatedData[storeKey];
|
||||
Object.keys(store).forEach(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
|
||||
}
|
||||
});
|
||||
31
tests/fixtures/baseFixture.ts
vendored
31
tests/fixtures/baseFixture.ts
vendored
@ -1,7 +1,7 @@
|
||||
import { test as base, expect } from '@playwright/test';
|
||||
import { HomeNavigation } from '@/pages/homeNavigationPage';
|
||||
import { writeIndexedDB } from '@/utils/indexedDBUtils';
|
||||
import { readFileSync } from 'fs';
|
||||
import { firstAccount, secondAccount, AuthAccount } from '@/common/auth';
|
||||
|
||||
type MyFixture = {
|
||||
homeNavigation: HomeNavigation;
|
||||
@ -17,7 +17,6 @@ export const test = base.extend<MyFixture>({
|
||||
|
||||
if (!baseURL) throw new Error('baseURL is required');
|
||||
await page.goto(baseURL);
|
||||
// await page.getByRole('button', { name: /开\s单/ }).waitFor();
|
||||
|
||||
const mobileObject = await page.evaluate(() => {
|
||||
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');
|
||||
}
|
||||
const mobileString = JSON.parse(mobileObject);
|
||||
const bossAccount = process.env.boss_account || '';
|
||||
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');
|
||||
}
|
||||
const data = AuthAccount.loadIndexedDBFile(mobileString.val, [firstAccount, secondAccount]);
|
||||
|
||||
await page.evaluate(
|
||||
async ({ fnString, jsonData }) => {
|
||||
// 在浏览器上下文中动态创建函数
|
||||
async ({ fnString, data }) => {
|
||||
const writeIndexedDBFn = new Function('return ' + fnString)();
|
||||
|
||||
// 调用函数并传递参数
|
||||
return await writeIndexedDBFn(jsonData);
|
||||
return await writeIndexedDBFn(data);
|
||||
},
|
||||
{ fnString: writeIndexedDB.toString(), jsonData },
|
||||
{ fnString: writeIndexedDB.toString(), data },
|
||||
);
|
||||
// await page.reload();
|
||||
|
||||
await page.waitForFunction(
|
||||
async () => {
|
||||
const databases = await indexedDB.databases(); // 获取所有数据库
|
||||
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 use(page);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { expect, type Page, type Locator } from '@playwright/test';
|
||||
import { Customer } from '@/utils/customer';
|
||||
import { getNextAppointmentTime } from '@/utils/timeUtils';
|
||||
|
||||
/**
|
||||
* 预约状态颜色
|
||||
@ -88,22 +89,6 @@ export class AppointmentPage {
|
||||
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) => {
|
||||
const currentAppointment = time ? this.getAppointmentTimesAvailable(time) : this.getAppointmentTimesAvailable();
|
||||
const currentAppointment = time ? getNextAppointmentTime(time) : getNextAppointmentTime();
|
||||
|
||||
const $currentTime = this.$$time.filter({ hasText: currentAppointment });
|
||||
const currentTimeBoundingBox = await $currentTime.boundingBox();
|
||||
|
||||
@ -1,37 +1,21 @@
|
||||
import { test as setup } from '@playwright/test';
|
||||
import { readIndexedDB } from '@/utils/indexedDBUtils';
|
||||
import fs from 'fs';
|
||||
import { firstAccount, secondAccount } from '@/common/auth';
|
||||
|
||||
const firstAuthFile = '.auth/admin_first.json';
|
||||
const secondAuthFile = '.auth/admin_second.json';
|
||||
|
||||
const regex = /^https?:\/\/(www\.)?hlk\.meiguanjia\.net\/?(#\s)?$/;
|
||||
const regex = /^https?:\/\/(?:www\.)?hlk\.meiguanjia\.net\/$/;
|
||||
|
||||
/** @param personalizedPages 营销-个性化页面*/
|
||||
const personalizedPages = '/#/marketing/brand/personalized';
|
||||
|
||||
class testAccount {
|
||||
constructor(account: string, password: string, path: string) {
|
||||
this.account = account;
|
||||
this.password = password;
|
||||
this.path = path;
|
||||
}
|
||||
const testAccountArray = [firstAccount, secondAccount];
|
||||
|
||||
account: string;
|
||||
password: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const allAccounts = [
|
||||
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;
|
||||
for (let testAccount of testAccountArray) {
|
||||
setup(`租户${testAccount.account}总部管理员登录`, async ({ page, baseURL }) => {
|
||||
const account = testAccount.account;
|
||||
const password = testAccount.password;
|
||||
const authFile = testAccount.authFile;
|
||||
const indexedDBFile = testAccount.indexedDBFile;
|
||||
|
||||
const $phonePassIcon = page
|
||||
.locator('div', { has: page.getByRole('textbox', { name: '请输入您的手机号码' }) })
|
||||
@ -64,7 +48,7 @@ for (let a of allAccounts) {
|
||||
const readIndexedDBFn = new Function('return ' + readIndexedDBFnString)();
|
||||
return await readIndexedDBFn();
|
||||
}, readIndexedDB.toString());
|
||||
fs.writeFileSync('.auth/admin_first_indexeddb.json', JSON.stringify(result));
|
||||
await page.context().storageState({ path: savePath });
|
||||
fs.writeFileSync(indexedDBFile, JSON.stringify(result));
|
||||
await page.context().storageState({ path: authFile });
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { test as setup, expect } from "@playwright/test";
|
||||
const authFileArray = [".auth/user_1.json", ".auth/user_2.json"];
|
||||
import { test as setup, expect } from '@playwright/test';
|
||||
import { getEnvVar } from '@/utils/envUtils';
|
||||
|
||||
const authFileArray = ['.auth/user_1.json', '.auth/user_2.json'];
|
||||
const employee = [
|
||||
{
|
||||
phone: process.env.staff1_account,
|
||||
@ -10,21 +12,21 @@ const employee = [
|
||||
password: process.env.staff2_password,
|
||||
},
|
||||
];
|
||||
setup.describe("员工登录", () => {
|
||||
setup.describe('员工登录', () => {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
setup(`门店员工登录_${i}`, async ({ page }) => {
|
||||
const baseUrl = process.env.BASE_URL;
|
||||
const $phone = page.getByRole("textbox", { name: "请输入您的手机号码" });
|
||||
const $password = page.getByRole("textbox", { name: "请输入登录密码" });
|
||||
const $login = page.getByRole("button", { name: /登\s录/ });
|
||||
const baseUrl = getEnvVar('BASE_URL');
|
||||
const $phone = page.getByRole('textbox', { name: '请输入您的手机号码' });
|
||||
const $password = page.getByRole('textbox', { name: '请输入登录密码' });
|
||||
const $login = page.getByRole('button', { name: /登\s录/ });
|
||||
await page.goto(baseUrl);
|
||||
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 $password.fill(employee[i].password);
|
||||
await page.getByLabel("请同意慧来客隐私政策和用户协议").check();
|
||||
await page.getByLabel('请同意慧来客隐私政策和用户协议').check();
|
||||
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] });
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,24 +1,19 @@
|
||||
// @ts-check
|
||||
import { faker } from '@faker-js/faker/locale/zh_CN';
|
||||
import { test, expect } from '@/fixtures/boss_common.js';
|
||||
import { staffData } from '@/fixtures/staff.js';
|
||||
import { HomeNavigation } from '@/pages/homeNavigationPage.js';
|
||||
import { AppointmentPage } from '@/pages/appointmentPage.js';
|
||||
import { AppointmentOperation } from '@/pages/appointmentPage.js';
|
||||
import { staffData } from '@/common/staff';
|
||||
import { AppointmentOperation } from '@/pages/appointmentPage';
|
||||
import { getNextAppointmentTime } from '@/utils/timeUtils';
|
||||
|
||||
test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => {
|
||||
const employee = staffData.firstStore.firstSector.employee_1;
|
||||
const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable();
|
||||
|
||||
const appointmentTime = getNextAppointmentTime();
|
||||
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
|
||||
.locator('.a_userInfo')
|
||||
.filter({ hasText: customer.phone })
|
||||
.filter({ hasText: customer.username });
|
||||
const $customerAppointment = page.locator('.a_userInfo').filter({ hasText: customer.phone }).filter({ hasText: customer.username });
|
||||
|
||||
await test.step('进行未指定预约', async () => {
|
||||
await homeNavigation.gotoModule('预约');
|
||||
@ -96,7 +91,7 @@ test('使用预约单元格', async ({ page, homeNavigation, createCustomer, app
|
||||
|
||||
test('占用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => {
|
||||
// 获取当前可预约时间
|
||||
let setAppointmentTime = appointmentPage.getAppointmentTimesAvailable();
|
||||
let appointmentTime = getNextAppointmentTime();
|
||||
|
||||
// 创建顾客
|
||||
const customer = createCustomer;
|
||||
@ -106,7 +101,7 @@ test('占用预约单元格', async ({ page, homeNavigation, createCustomer, app
|
||||
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 });
|
||||
// 顾客预约定位器
|
||||
@ -202,18 +197,15 @@ test('占用预约单元格', async ({ page, homeNavigation, createCustomer, app
|
||||
|
||||
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 appointmentTime = getNextAppointmentTime();
|
||||
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 customer = createCustomer;
|
||||
|
||||
// 获取设置的预约状态
|
||||
let appointmentStatusSetting;
|
||||
@ -311,74 +303,29 @@ test.describe('预约状态', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
test('测试预约操作', async ({ page, homeNavigation }) => {
|
||||
await homeNavigation.gotoModule('预约');
|
||||
await expect(page.getByText('张伟')).toBeVisible();
|
||||
const $$userInfo = page.locator('.a_userInfo');
|
||||
// 获取顾客占用预约单元格信息
|
||||
const $$appointmentBox = $$userInfo.locator('.appointment_content');
|
||||
await page.getByRole('button', { name: /设\s置/ }).waitFor();
|
||||
await page.waitForTimeout(3000);
|
||||
// await page.mouse.wheel(0, 500);
|
||||
|
||||
const names = await $$appointmentBox.locator('.name').allInnerTexts();
|
||||
const userPhones = await $$appointmentBox.locator('.user_phone').allInnerTexts();
|
||||
const $table = page.locator('.content_table');
|
||||
const tableBox = await $table.boundingBox();
|
||||
if (tableBox) {
|
||||
const startX = tableBox.x + tableBox.width / 2; // 元素中心点 X 坐标
|
||||
const startY = tableBox.y + tableBox.height / 2; // 元素中心点 Y 坐标
|
||||
|
||||
// 移动鼠标到起始位置
|
||||
await page.mouse.move(startX, startY);
|
||||
|
||||
// 按下鼠标左键
|
||||
await page.mouse.down();
|
||||
|
||||
// 向左拖动 (X 坐标减小,Y 坐标保持不变)
|
||||
await page.mouse.move(startX - 400, startY, { steps: 10 });
|
||||
|
||||
// 松开鼠标左键
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
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 { Customer } from '@/utils/customer';
|
||||
import { KeepOnlyNumbers } from '@/utils/utils.js';
|
||||
|
||||
@ -4,7 +4,7 @@ import { test, expect } from '@/fixtures/boss_common.js';
|
||||
import path from 'path';
|
||||
import { decodeQR } from '@/utils/utils.js';
|
||||
import { Customer } from '@/utils/customer';
|
||||
import { staffData } from '@/fixtures/staff.js';
|
||||
import { staffData } from '@/common/staff';
|
||||
import { HomeNavigation } from '@/pages/homeNavigationPage.js';
|
||||
import fs from 'fs';
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
@ -6,7 +6,7 @@ import { KeepOnlyNumbers } from '@/utils/utils.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Employees, ProjectName } from '@/fixtures/userconfig.js';
|
||||
import { staffData } from '@/fixtures/staff.js';
|
||||
import { staffData } from '@/common/staff';
|
||||
|
||||
test.describe('营业记录', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
|
||||
7
tests/utils/envUtils.ts
Normal file
7
tests/utils/envUtils.ts
Normal 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
14
tests/utils/timeUtils.ts
Normal 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 };
|
||||
Reference in New Issue
Block a user