优化代码

This commit is contained in:
LingandRX 2024-12-22 21:12:43 +08:00
parent cf67a1c95c
commit 8c910d9086
16 changed files with 233 additions and 270 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';
/** /**
* Read environment variables from file. * Read environment variables from file.
* https://github.com/motdotla/dotenv * https://github.com/motdotla/dotenv
@ -41,7 +42,8 @@ module.exports = defineConfig({
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : 2, workers: process.env.CI ? 1 : 2,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [['html'], ['list'], ['./my-awesome-reporter.ts']], // reporter: [['html'], ['list'], ['./my-awesome-reporter.ts']],
reporter: [['html'], ['list']],
// reporter: './my-awesome-reporter.ts', // reporter: './my-awesome-reporter.ts',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
@ -71,12 +73,12 @@ module.exports = defineConfig({
projects: [ projects: [
{ {
name: '总部管理员鉴权', name: '总部管理员鉴权',
testMatch: /.*boss_auth\.setup\.js/, testMatch: /.*boss_auth\.setup\.ts/,
use: { baseURL: process.env.BASE_URL }, use: { baseURL: process.env.BASE_URL },
}, },
{ {
name: '门店员工鉴权', name: '门店员工鉴权',
testMatch: /.*staff_auth\.setup\.js/, testMatch: /.*staff_auth\.setup\.ts/,
use: { baseURL: process.env.BASE_URL }, use: { baseURL: process.env.BASE_URL },
}, },
{ {
@ -88,7 +90,7 @@ module.exports = defineConfig({
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
isMobile: true, isMobile: true,
}, },
testMatch: /.*boss_.*\.spec\.js/, testMatch: /.*boss_.*\.spec\.ts/,
dependencies: ['总部管理员鉴权'], dependencies: ['总部管理员鉴权'],
}, },
{ {
@ -100,7 +102,7 @@ module.exports = defineConfig({
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
isMobile: true, isMobile: true,
}, },
testMatch: /.*boss_.*\.spec\.js/, testMatch: /.*boss_.*\.spec\.ts/,
dependencies: ['总部管理员鉴权'], dependencies: ['总部管理员鉴权'],
}, },
{ {
@ -111,7 +113,7 @@ module.exports = defineConfig({
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
isMobile: false, isMobile: false,
}, },
testMatch: /.*staff_.*\.spec\.js/, testMatch: /.*staff_.*\.spec\.ts/,
dependencies: ['门店员工鉴权'], dependencies: ['门店员工鉴权'],
}, },
{ {
@ -122,7 +124,7 @@ module.exports = defineConfig({
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
isMobile: false, isMobile: false,
}, },
testMatch: /.*staff_.*\.spec\.js/, testMatch: /.*staff_.*\.spec\.ts/,
dependencies: ['门店员工鉴权'], dependencies: ['门店员工鉴权'],
}, },
], ],

View File

@ -1,16 +1,8 @@
import { test as base } from '@playwright/test'; import { test as base, Page } from '@playwright/test';
import { CustomerPage, CustomerDetailsPage, CustomerAnalysisPage } from '@/pages/customer'; import { CustomerPage, CustomerDetailsPage, CustomerAnalysisPage } from '@/pages/customer';
import { Customer } from '@/utils/customer'; import { Customer } from '@/utils/customer';
type MyFixture = { type MyFixture = {
singleCustomerPage: {
createCustomer: (customer: Customer) => Promise<void>;
setInvalidCustomer: (customer: Customer) => Promise<void>;
};
moreCustomerPage: {
createMoreCustomer: (customer: Customer[]) => Promise<void>;
setMoreInvalidCustomer: (customer: Customer[]) => Promise<void>;
};
customerPage: CustomerPage; customerPage: CustomerPage;
customerDetailsPage: CustomerDetailsPage; customerDetailsPage: CustomerDetailsPage;
customerAnalysisPage: CustomerAnalysisPage; customerAnalysisPage: CustomerAnalysisPage;
@ -20,34 +12,50 @@ type MyFixture = {
createCustomCustomers: (customers: Customer[]) => Promise<void>; createCustomCustomers: (customers: Customer[]) => Promise<void>;
}; };
/**
*
*/
const navigateToCustomerSchema = async (page: Page, baseURL: string) => {
await page.goto(baseURL);
await page.getByRole('button', { name: /开\s单/ }).waitFor();
await page.goto('/#/member/member-schame');
};
/**
*
* @param customerPage { CustomerPage }
* @param customers { Customer | Customer[] }
*/
const setInvalidCustomers = async (customerPage: CustomerPage, customers: Customer[] | Customer) => {
try {
if (Array.isArray(customers)) {
await customerPage.setMoreInvalidCustomer(customers);
} else {
await customerPage.setInvalidCustomer(customers);
}
} catch (error) {
console.error('Failed to set invalid customers:', error);
throw error;
}
};
export const test = base.extend<MyFixture>({ export const test = base.extend<MyFixture>({
/** /**
* *
*/ */
createCustomer: async ({ page, baseURL }, use) => { createCustomer: async ({ page, baseURL }, use) => {
if (!baseURL) {
throw new Error('baseUrl is required');
}
const customerPage = new CustomerPage(page); const customerPage = new CustomerPage(page);
const customer = new Customer(1, 1); const customer = new Customer(1, 1);
await customerPage.createCustomer(customer); await customerPage.createCustomer(customer);
await page.goto(baseURL);
await use(customer); await use(customer);
await page.goto(baseURL); await navigateToCustomerSchema(page, baseURL!);
await page.getByRole('button', { name: /开\s单/ }).waitFor(); await setInvalidCustomers(customerPage, customer);
await page.goto('/#/member/member-schame');
await customerPage.setInvalidCustomer(customer);
}, },
/** /**
* *
*/ */
createCustomers: async ({ page, baseURL }, use): Promise<void> => { createCustomers: async ({ page, baseURL }, use): Promise<void> => {
if (!baseURL) {
throw new Error('baseUrl is required');
}
const customerPage = new CustomerPage(page); const customerPage = new CustomerPage(page);
let createdCustomers: Customer[] = []; let createdCustomers: Customer[] = [];
@ -70,56 +78,44 @@ export const test = base.extend<MyFixture>({
if (createdCustomers.length === 0) return; if (createdCustomers.length === 0) return;
// 测试结束后将客户设置为无效 await navigateToCustomerSchema(page, baseURL!);
await page.goto(baseURL); await setInvalidCustomers(customerPage, createdCustomers);
await page.getByRole('button', { name: /开\s单/ }).waitFor();
await page.goto('/#/member/member-schame');
await customerPage.setMoreInvalidCustomer(createdCustomers);
}, },
/** /**
* *
*/ */
createCustomCustomer: async ({ page, baseURL }, use) => { createCustomCustomer: async ({ page, baseURL }, use) => {
if (!baseURL) {
throw new Error('baseUrl is required');
}
const customerPage = new CustomerPage(page); const customerPage = new CustomerPage(page);
let tmp: Customer | undefined; let customizeCustomer: Customer | undefined;
const setCustomer = async (customer: Customer) => {
await customerPage.createCustomer(customer);
tmp = customer;
};
await use(setCustomer);
if (!tmp) return; const createCustomizeCustomer = async (customer: Customer) => {
await page.goto(baseURL); await customerPage.createCustomer(customer);
await page.getByRole('button', { name: /开\s单/ }).waitFor(); customizeCustomer = customer;
await page.goto('/#/member/member-schame'); };
await customerPage.setInvalidCustomer(tmp);
await use(createCustomizeCustomer);
if (!customizeCustomer) return;
await navigateToCustomerSchema(page, baseURL!);
await setInvalidCustomers(customerPage, customizeCustomer);
}, },
/** /**
* *
*/ */
createCustomCustomers: async ({ page, baseURL }, use) => { createCustomCustomers: async ({ page, baseURL }, use) => {
if (!baseURL) {
throw new Error('baseUrl is required');
}
const customerPage = new CustomerPage(page); const customerPage = new CustomerPage(page);
let tmp: Customer[] | undefined; let createCustomizeCustomerArray: Customer[] | undefined;
const setCustomers = async (customers: Customer[]) => { const setCustomers = async (customers: Customer[]) => {
await customerPage.createMoreCustomer(customers); await customerPage.createMoreCustomer(customers);
tmp = customers; createCustomizeCustomerArray = customers;
}; };
await use(setCustomers); await use(setCustomers);
if (!tmp) return; if (!createCustomizeCustomerArray) return;
await page.goto(baseURL); await navigateToCustomerSchema(page, baseURL!);
await page.getByRole('button', { name: /开\s单/ }).waitFor(); await setInvalidCustomers(customerPage, createCustomizeCustomerArray);
await page.goto('/#/member/member-schame');
await customerPage.setMoreInvalidCustomer(tmp);
}, },
/** /**

View File

@ -34,7 +34,6 @@ export class NumberInput {
/** /**
* *
* @param value
*/ */
async setValue(value: number): Promise<void> { async setValue(value: number): Promise<void> {
await this.inputLocator.fill(value.toString()); await this.inputLocator.fill(value.toString());

View File

@ -6,7 +6,6 @@ export class CustomerAnalysisPage {
/** /**
* *
* @param {import("@playwright/test").Page} page
*/ */
constructor(page: Page) { constructor(page: Page) {
this.page = page; this.page = page;

View File

@ -1,7 +1,6 @@
import { expect, type Locator, type Page } from '@playwright/test'; import { expect, type Locator, type Page } from '@playwright/test';
import { HomeNavigation } from '../homeNavigationPage.js'; import { Customer, employee } from '@/utils/customer';
import { Customer, employee } from '../../utils/customer'; import { waitSpecifyApiLoad } from '@/utils/utils';
import { waitSpecifyApiLoad } from '../../utils/utils.js';
type SubPage = { type SubPage = {
name: string; name: string;
@ -14,7 +13,6 @@ type SubPage = {
*/ */
export class CustomerPage { export class CustomerPage {
page: Page; page: Page;
private readonly homeNavigation: HomeNavigation;
private readonly subPages: SubPage[] = [ private readonly subPages: SubPage[] = [
{ name: '顾客概要', url: ['summary', 'todo'] }, { name: '顾客概要', url: ['summary', 'todo'] },
{ name: '顾客分配', url: ['search_new', 'distribution'] }, { name: '顾客分配', url: ['search_new', 'distribution'] },
@ -42,15 +40,14 @@ export class CustomerPage {
$register: Locator; $register: Locator;
$tabItem: Locator; $tabItem: Locator;
$searchInput: Locator; $searchInput: Locator;
$searchConfirm: Locator; $searchBtn: Locator;
constructor(page: Page) { constructor(page: Page) {
this.page = page; this.page = page;
this.$tabItem = this.page.locator('.top_tab .tab_item'); this.$tabItem = this.page.locator('.top_tab .tab_item');
this.$register = this.page.locator('.regmeber_warp', { hasText: '创建会员' }); this.$register = this.page.locator('.regmeber_warp', { hasText: '创建会员' });
this.$searchInput = this.page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索'); this.$searchInput = this.page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索');
this.$searchConfirm = this.page.getByText('搜索', { exact: true }); this.$searchBtn = this.page.getByText('搜索', { exact: true });
this.homeNavigation = new HomeNavigation(this.page);
} }
/** /**
@ -68,19 +65,19 @@ export class CustomerPage {
throw new Error(`子页面 ${subPageName} 不存在`); throw new Error(`子页面 ${subPageName} 不存在`);
} }
const $subPage = this.$tabItem.filter({ hasText: subPageName }); const $subPageTab = this.$tabItem.filter({ hasText: subPageName });
await $subPage.waitFor(); await $subPageTab.waitFor();
const classAttribute = await $subPage.getAttribute('class', { timeout: 5000 }); const classAttribute = await $subPageTab.getAttribute('class', { timeout: 5000 });
if (classAttribute && classAttribute.includes('active')) { if (classAttribute && classAttribute.includes('active')) {
return; return;
} }
await Promise.all([ await Promise.all([
expect(async () => { expect(async () => {
await $subPage.click(); await $subPageTab.click();
await expect($subPage).toHaveClass(/active/); await expect($subPageTab).toHaveClass(/active/);
}).toPass(), }).toPass(),
waitSpecifyApiLoad(this.page, subPage.url), waitSpecifyApiLoad(this.page, subPage.url),
]); ]);
@ -95,7 +92,7 @@ export class CustomerPage {
const $customerInfoCard = $$customerContent.filter({ hasText: text }).first(); const $customerInfoCard = $$customerContent.filter({ hasText: text }).first();
await this.$searchInput.fill(text); await this.$searchInput.fill(text);
await this.$searchConfirm.click(); await this.$searchBtn.click();
await $customerInfoCard.waitFor(); await $customerInfoCard.waitFor();
}; };
@ -136,14 +133,13 @@ export class CustomerPage {
* *
*/ */
closeCustomerDetail = async () => { closeCustomerDetail = async () => {
const closeButton = this.page.locator('.member_info_box .close_icons > svg'); const $closeBtn = this.page.locator('.member_info_box .close_icons > svg');
await closeButton.click(); await $closeBtn.click();
await closeButton.waitFor({ state: 'detached' }); await $closeBtn.waitFor({ state: 'detached' });
}; };
/** /**
* *
* @param {Customer} customer
*/ */
createCustomer = async (customer: Customer) => { createCustomer = async (customer: Customer) => {
const customerPage = '/#/member/member-schame'; const customerPage = '/#/member/member-schame';
@ -249,11 +245,22 @@ export class CustomerPage {
* @param gender * @param gender
*/ */
private readonly selectGender = async (gender: number) => { private readonly selectGender = async (gender: number) => {
// 定义性别选项的映射
const GENDER_OPTIONS = {
FEMALE: 0,
MALE: 1,
};
// 定义对应的文本
const GENDER_TEXTS = {
[GENDER_OPTIONS.FEMALE]: '女性',
[GENDER_OPTIONS.MALE]: '男性',
};
if (gender === 0) { if (gender === 0) {
await this.$register.locator('label').filter({ hasText: '女性' }).click(); return;
} else if (gender === 1) {
await this.$register.locator('label').filter({ hasText: '男性' }).click();
} }
await this.$register.locator('label').filter({ hasText: GENDER_TEXTS[gender] }).click();
}; };
/** /**
@ -264,23 +271,37 @@ export class CustomerPage {
* @param {number} birthday.day * @param {number} birthday.day
*/ */
private readonly selectBirthday = async (birthday: { year: number; month: number; day: number }) => { private readonly selectBirthday = async (birthday: { year: number; month: number; day: number }) => {
if (birthday) { if (!birthday) {
throw new Error('birthday参数为空');
}
const { year, month, day } = birthday; const { year, month, day } = birthday;
const birthdayLocator = this.$register.locator('.ant-form-item', { hasText: '生日' });
if (!Number.isInteger(year) || year < 1900 || year > new Date().getFullYear()) {
throw new Error(`年份 ${year} 不合法`);
}
if (!Number.isInteger(month) || month < 1 || month > 12) {
throw new Error(`月份 ${month} 不合法`);
}
if (!Number.isInteger(day) || day < 1 || day > 31) {
throw new Error(`日期 ${day} 不合法`);
}
const $birthday = this.$register.locator('.ant-form-item', { hasText: '生日' });
if (year) { if (year) {
await birthdayLocator.getByText('年份').click(); await $birthday.getByText('年份').click();
await this.page.getByRole('option', { name: `${year}` }).click(); await this.page.getByRole('option', { name: `${year}` }).click();
await expect(this.page.getByRole('option', { name: `${year}` })).not.toBeVisible(); await expect(this.page.getByRole('option', { name: `${year}` })).not.toBeVisible();
} }
if (month && day) { if (month && day) {
await birthdayLocator.getByText('日期').click(); await $birthday.getByText('日期').click();
await this.page.getByRole('option', { name: new RegExp(`^${month}\\s月$`) }).click(); await this.page.getByRole('option', { name: new RegExp(`^${month}\\s月$`) }).click();
await this.page.getByRole('option', { name: new RegExp(`^${day}\\s日$`) }).click(); await this.page.getByRole('option', { name: new RegExp(`^${day}\\s日$`) }).click();
await this.page.getByRole('button', { name: /确\s认/ }).click(); await this.page.getByRole('button', { name: /确\s认/ }).click();
} else { } else {
throw new Error(`month:${month}, day:${day}其中一个为空`); throw new Error(`month:${month}, day:${day}其中一个为空`);
} }
}
}; };
/** /**
@ -296,9 +317,8 @@ export class CustomerPage {
* @param {string} remark * @param {string} remark
*/ */
private readonly fillRemark = async (remark: string) => { private readonly fillRemark = async (remark: string) => {
if (remark) { if (!remark) return;
await this.$register.getByPlaceholder('请输入1-100个字符备注内容').fill(remark); await this.$register.getByPlaceholder('请输入1-100个字符备注内容').fill(remark);
}
}; };
/** /**
@ -315,7 +335,7 @@ export class CustomerPage {
const responseBody = await response.json(); const responseBody = await response.json();
const phoneStatus = responseBody?.content?.status; const phoneStatus = responseBody?.content?.status;
if (!phoneStatus) { if (phoneStatus === undefined || phoneStatus === null) {
return; return;
} }
await this.page.getByText('系统查询到当前手机号被建档后转为无效客,是否要恢复无效客?').waitFor(); await this.page.getByText('系统查询到当前手机号被建档后转为无效客,是否要恢复无效客?').waitFor();
@ -341,7 +361,6 @@ export class CustomerPage {
await this.page.locator('.m-table__fixed-left').getByText(customer.username).first().click(); await this.page.locator('.m-table__fixed-left').getByText(customer.username).first().click();
// 设置无效客 // 设置无效客
await this.page.locator('.person_content').waitFor();
await this.page.locator('.person_content .tag_box .more_icon svg').click(); await this.page.locator('.person_content .tag_box .more_icon svg').click();
await this.page.getByRole('menuitem', { name: '设为无效客' }).click(); await this.page.getByRole('menuitem', { name: '设为无效客' }).click();
const [response] = await Promise.all([ const [response] = await Promise.all([
@ -372,7 +391,7 @@ export class CustomerPage {
*/ */
setMoreInvalidCustomer = async (customerArray: Customer[]) => { setMoreInvalidCustomer = async (customerArray: Customer[]) => {
for (const customer of customerArray) { for (const customer of customerArray) {
this.page.reload(); await this.page.reload();
await this.setInvalidCustomer(customer); await this.setInvalidCustomer(customer);
} }
}; };

View File

@ -4,6 +4,7 @@ import { waitSpecifyApiLoad } from '@/utils/utils';
export class InventoryManagementPage { export class InventoryManagementPage {
page: Page; page: Page;
subPages: { name: string; url?: string[] }[]; subPages: { name: string; url?: string[] }[];
/** /**
* *
* @param page * @param page

View File

@ -4,9 +4,10 @@ import { waitSpecifyApiLoad } from '@/utils/utils';
export class TransferManagementPage { export class TransferManagementPage {
page: Page; page: Page;
subPages: { name: string; url?: string[] }[]; subPages: { name: string; url?: string[] }[];
/** /**
* - * -
* @param {import("@playwright/test").Page} page * @param {import('@playwright/test').Page} page
*/ */
constructor(page: Page) { constructor(page: Page) {
this.page = page; this.page = page;

View File

@ -11,20 +11,20 @@ const regex = /^https?:\/\/(www\.)?hlk\.meiguanjia\.net\/?(#\s)?$/;
const personalizedPages = '/#/marketing/brand/personalized'; const personalizedPages = '/#/marketing/brand/personalized';
class testAccount { class testAccount {
constructor(account, password, path) { constructor(account: string, password: string, path: string) {
this.account = account; this.account = account;
this.password = password; this.password = password;
this.path = path; this.path = path;
} }
account; account: string;
password; password: string;
path; path: string;
} }
const allAccounts = [ const allAccounts = [
new testAccount(process.env.boss_account, process.env.boss_password, firstAuthFile), new testAccount(process.env.boss_account!, process.env.boss_password!, firstAuthFile),
new testAccount(process.env.boss_account_2, process.env.boss_password_2, secondAuthFile), new testAccount(process.env.boss_account_2!, process.env.boss_password_2!, secondAuthFile),
]; ];
for (let a of allAccounts) { for (let a of allAccounts) {
@ -37,7 +37,7 @@ for (let a of allAccounts) {
.locator('div', { has: page.getByRole('textbox', { name: '请输入您的手机号码' }) }) .locator('div', { has: page.getByRole('textbox', { name: '请输入您的手机号码' }) })
.locator('.pass_svg'); .locator('.pass_svg');
await page.goto(baseURL); await page.goto(baseURL!);
await page.getByRole('textbox', { name: '请输入您的手机号码' }).fill(account); await page.getByRole('textbox', { name: '请输入您的手机号码' }).fill(account);
await page.getByRole('textbox', { name: '请输入登录密码' }).fill(password); await page.getByRole('textbox', { name: '请输入登录密码' }).fill(password);
await page.getByLabel('请同意慧来客隐私政策和用户协议').check(); await page.getByLabel('请同意慧来客隐私政策和用户协议').check();
@ -45,7 +45,7 @@ for (let a of allAccounts) {
await page.getByRole('button', { name: /登\s录/ }).click(); await page.getByRole('button', { name: /登\s录/ }).click();
await page.getByRole('button', { name: /开\s单/ }).waitFor(); await page.getByRole('button', { name: /开\s单/ }).waitFor();
if (regex.test(baseURL)) { if (regex.test(baseURL!)) {
await page.goto(personalizedPages); await page.goto(personalizedPages);
await page.getByRole('button', { name: '新增模块' }).waitFor(); await page.getByRole('button', { name: '新增模块' }).waitFor();
const $sideIframe = page.locator('.side iframe').contentFrame(); const $sideIframe = page.locator('.side iframe').contentFrame();

View File

@ -1,20 +1,20 @@
// @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 path from 'path';
import fs from 'fs';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/fixtures/staff.js';
import { HomeNavigation } from '@/pages/homeNavigationPage.js'; import { HomeNavigation } from '@/pages/homeNavigationPage.js';
import { AppointmentPage } from '@/pages/appointmentPage.js'; import { AppointmentPage } from '@/pages/appointmentPage.js';
import { AppointmentOperation } from '@/pages/appointmentPage.js'; import { AppointmentOperation } from '@/pages/appointmentPage.js';
test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => { test('使用预约单元格', async ({ page, homeNavigation, createCustomer, appointmentPage, customerPage }) => {
const setAppointmentTime = appointmentPage.getAppointmentTimesAvailable();
const customer = createCustomer;
const employee = staffData.firstStore.firstSector.employee_1; const employee = staffData.firstStore.firstSector.employee_1;
// 当前可预约时间定位器 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: setAppointmentTime });
// 顾客预约定位器 /** 顾客预约定位器 */
const $customerAppointment = page const $customerAppointment = page
.locator('.a_userInfo') .locator('.a_userInfo')
.filter({ hasText: customer.phone }) .filter({ hasText: customer.phone })
@ -309,36 +309,6 @@ test.describe('预约状态', () => {
expect(customerStatus).toEqual(appointmentStatusSetting.SETTLED.name); 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.afterEach(async ({ homeNavigation, wasteBookBusinessRecordPage, billSet }) => { test.afterEach(async ({ homeNavigation, wasteBookBusinessRecordPage, billSet }) => {

View File

@ -5,11 +5,12 @@ import { decodeQR, KeepOnlyNumbers } from '@/utils/utils.js';
import { faker } from '@faker-js/faker/locale/zh_CN'; import { faker } from '@faker-js/faker/locale/zh_CN';
import path from 'path'; import path from 'path';
import { AppointmentOperation } from '@/pages/appointmentPage.js'; import { AppointmentOperation } from '@/pages/appointmentPage.js';
import { Customer } from '@/utils/customer';
test.describe('挂单', () => { test.describe('挂单', () => {
let c; let c: Customer;
let username; let username: string;
let phone; let phone: string;
test.beforeEach('测试前新建会员', async ({ page, homeNavigation, createCustomer, customerPage }) => { test.beforeEach('测试前新建会员', async ({ page, homeNavigation, createCustomer, customerPage }) => {
// 创建顾客 // 创建顾客
c = createCustomer; c = createCustomer;
@ -423,8 +424,7 @@ test.describe('挂单', () => {
await page.locator('.qrcode').screenshot({ path: filePath }); await page.locator('.qrcode').screenshot({ path: filePath });
}).toPass(); }).toPass();
/**@type {string} 解析后的网址 */ let result: string = await decodeQR(filePath);
let result = await decodeQR(filePath);
console.log(result); console.log(result);
let url_1 = new URL(result); let url_1 = new URL(result);
@ -760,8 +760,7 @@ test.describe('收银-房态', () => {
const setAppointment = appointmentPage.getAppointmentTimesAvailable(); // 获取当前时间 const setAppointment = appointmentPage.getAppointmentTimesAvailable(); // 获取当前时间
const cede = page.locator('.left_table td'); const cede = page.locator('.left_table td');
// 找到当前时间的行数 // 找到当前时间的行数
/**@type {number} 找到当前时间的行数 */ const nowRowTime: number = await cede.allInnerTexts().then(text => {
const nowRowTime = await cede.allInnerTexts().then(text => {
return text.findIndex(item => item === setAppointment) + 2; return text.findIndex(item => item === setAppointment) + 2;
}); });
// 获取第几列没被占用 // 获取第几列没被占用
@ -822,9 +821,7 @@ test.describe('收银-房态', () => {
const setAppointment = appointmentPage.getAppointmentTimesAvailable(); // 获取当前时间 const setAppointment = appointmentPage.getAppointmentTimesAvailable(); // 获取当前时间
const cede = page.locator('.left_table td'); const cede = page.locator('.left_table td');
// 找到当前时间的行数 // 找到当前时间的行数
// const nowRowTime = await cede.allInnerTexts().then(text => text.indexOf(setAppointment) + 3); const nowRowTime: number = await cede.allInnerTexts().then(text => {
/**@type{number} 找到当前时间的行数 */
const nowRowTime = await cede.allInnerTexts().then(text => {
return text.findIndex(item => item === setAppointment) + 2; return text.findIndex(item => item === setAppointment) + 2;
}); });
// 获取第几列没被占用 // 获取第几列没被占用

View File

@ -97,7 +97,7 @@ test.describe('顾客通用', () => {
await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').click(); await page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').click();
await page.locator('.historyALertBox_title', { hasText: '搜索历史' }).waitFor(); await page.locator('.historyALertBox_title', { hasText: '搜索历史' }).waitFor();
// 判断搜索历史是否存在 // 判断搜索历史是否存在
await expect(page.locator('.historyMemberList_item')).toContainText([@.historySet].reverse()); await expect(page.locator('.historyMemberList_item')).toContainText([...historySet].reverse());
}); });
}); });
@ -1026,8 +1026,7 @@ test.describe('顾客详情', () => {
}); });
test('操作套餐', async ({ page, homeNavigation, customerPage, createCustomer }) => { test('操作套餐', async ({ page, homeNavigation, customerPage, createCustomer }) => {
/**@type {string} */ let billNo: string;
let billNo;
const date = new Date(); const date = new Date();
const currentYear = date.getFullYear(); const currentYear = date.getFullYear();
const currentMonth = date.getMonth() + 1; const currentMonth = date.getMonth() + 1;
@ -1166,8 +1165,7 @@ test.describe('顾客详情', () => {
await page.getByRole('button', { name: /确\s认/ }).click(); await page.getByRole('button', { name: /确\s认/ }).click();
}); });
/**@type {string} 当前单号 */ let billNo: string;
let billNo = '';
await test.step('购买项目并消耗,购买卖品并结算,结算使用优惠券、卡金、赠金、欠款、积分,拿取单号', async () => { await test.step('购买项目并消耗,购买卖品并结算,结算使用优惠券、卡金、赠金、欠款、积分,拿取单号', async () => {
// 左右两侧的支付方式定位器 // 左右两侧的支付方式定位器

View File

@ -361,8 +361,7 @@ test.describe('门店看板', () => {
// 收银结算button // 收银结算button
const leftPaymentInfoItem = page.locator('.left .paymentmain .paymentInfoItem'); const leftPaymentInfoItem = page.locator('.left .paymentmain .paymentInfoItem');
/**@type {Customer[]} */ let customers: Customer[] = [];
let customers = [];
await test.step('创建3个顾客', async () => { await test.step('创建3个顾客', async () => {
customers = await createCustomers(3); customers = await createCustomers(3);
}); });
@ -1261,8 +1260,7 @@ test.describe('精细目标', () => {
let firstStoreFirstSector = staffData.firstStore.firstSector; let firstStoreFirstSector = staffData.firstStore.firstSector;
let firstStoreSecondSector = staffData.firstStore.secondSector; let firstStoreSecondSector = staffData.firstStore.secondSector;
/**@type {Customer[]} */ let customers: Customer[] = [];
let customers = [];
await test.step('创建两个顾客', async () => { await test.step('创建两个顾客', async () => {
customers = await createCustomers(2); customers = await createCustomers(2);
}); });

View File

@ -3,7 +3,6 @@ import { faker } from '@faker-js/faker/locale/zh_CN';
import { expect, test } from '@/fixtures/boss_common.js'; import { expect, test } from '@/fixtures/boss_common.js';
import { CleanPunctuation, getListIndexForTargetElement, KeepOnlyNumbers } from '@/utils/utils.js'; import { CleanPunctuation, getListIndexForTargetElement, KeepOnlyNumbers } from '@/utils/utils.js';
import { ProjectName } from '@/fixtures/userconfig.js'; import { ProjectName } from '@/fixtures/userconfig.js';
import { Customer } from '@/utils/customer';
test.describe('出入库管理', () => { test.describe('出入库管理', () => {
test.describe('入库单', () => { test.describe('入库单', () => {
@ -11,8 +10,8 @@ test.describe('出入库管理', () => {
const remark = '入库' + faker.helpers.fromRegExp(/1[0-9]{4}/); const remark = '入库' + faker.helpers.fromRegExp(/1[0-9]{4}/);
const productA = ProjectName.Product.Product_3; const productA = ProjectName.Product.Product_3;
/**@type {number} 产品A余量 */ /** 产品A余量 */
let productASurplus; let productASurplus: number;
const quantity1 = 100; const quantity1 = 100;
const quantity2 = 5; const quantity2 = 5;
await test.step('获取入库前数据', async () => { await test.step('获取入库前数据', async () => {
@ -63,8 +62,8 @@ test.describe('出入库管理', () => {
await expect(page.getByRole('button', { name: '确认入库' })).not.toBeVisible(); await expect(page.getByRole('button', { name: '确认入库' })).not.toBeVisible();
}); });
/**@type {number} 入库单的索引 */ /** 入库单的索引 */
let billEntryIndex; let billEntryIndex: number;
await test.step('校验入库单', async () => { await test.step('校验入库单', async () => {
// 根据备注找出入库单在第几行 // 根据备注找出入库单在第几行
const codes = page.locator('.table-warp .m-table__body-wrapper .m-table__body tbody tr'); const codes = page.locator('.table-warp .m-table__body-wrapper .m-table__body tbody tr');
@ -898,8 +897,7 @@ test.describe('库存管理', () => {
const productA = ProjectName.Product.Product_2.name; const productA = ProjectName.Product.Product_2.name;
const productNum = ProjectName.Product.Product_2.num; const productNum = ProjectName.Product.Product_2.num;
/**@type {string} 单号*/ let billNo: string;
let billNo;
await test.step('出库', async () => { await test.step('出库', async () => {
// 点击库存 // 点击库存
await homeNavigation.gotoModule('库存'); await homeNavigation.gotoModule('库存');
@ -1261,8 +1259,7 @@ test.describe('库存管理', () => {
const customer = createCustomer; const customer = createCustomer;
/**@type {string} */ let billNo: string;
let billNo;
await test.step('进行开单购买卖品并寄存', async () => { await test.step('进行开单购买卖品并寄存', async () => {
await homeNavigation.gotoModule('收银'); await homeNavigation.gotoModule('收银');
await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByRole('button', { name: /开\s单/ }).click();
@ -1338,8 +1335,7 @@ test.describe('统计', async () => {
const quantity1 = 20; //数量 const quantity1 = 20; //数量
const UnitPrice = 10; //单价 const UnitPrice = 10; //单价
/**@type {string} 单号*/ let billNo: string;
let billNo;
let totality; let totality;
let GrossAmount; let GrossAmount;
@ -1839,8 +1835,7 @@ test.describe('统计', async () => {
const quantity1 = 20; //数量 const quantity1 = 20; //数量
const UnitPrice = 10; //单价 const UnitPrice = 10; //单价
/**@type {string} 单号*/ let billNo: string;
let billNo;
let totality = 0; let totality = 0;
await test.step('获取出库前总数', async () => { await test.step('获取出库前总数', async () => {
@ -1990,7 +1985,7 @@ test.describe('统计', async () => {
}).toPass({ timeout: 60000 }); }).toPass({ timeout: 60000 });
// 获取出库后总数 // 获取出库后总数
await expect(page.locator('.main-table-body_tr td').nth(5)).toBe(Number(totality) + Number(quantity1)); expect(page.locator('.main-table-body_tr td').nth(5)).toBe(Number(totality) + Number(quantity1));
// 点击总数进入流水 // 点击总数进入流水
await page.locator('.main-table-body_tr td').nth(5).click(); await page.locator('.main-table-body_tr td').nth(5).click();
@ -2683,8 +2678,7 @@ test.describe('统计', async () => {
.then(Number); .then(Number);
}); });
/**@type {string} 单号*/ let billNo: string;
let billNo;
await test.step('顾客开单购买3次项目并消耗3次项目选择产品配方3次5ml', async () => { await test.step('顾客开单购买3次项目并消耗3次项目选择产品配方3次5ml', async () => {
await homeNavigation.gotoModule('收银'); await homeNavigation.gotoModule('收银');
await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByRole('button', { name: /开\s单/ }).click();
@ -2806,7 +2800,7 @@ test.describe('调货管理', () => {
}; };
const remarkA = '调货管理' + faker.helpers.fromRegExp(/[a-d]{2}[0-9]{2}/); const remarkA = '调货管理' + faker.helpers.fromRegExp(/[a-d]{2}[0-9]{2}/);
const remarkB = '调货管理' + faker.helpers.fromRegExp(/[a-d]{2}[0-9]{2}/); const remarkB = '调货管理' + faker.helpers.fromRegExp(/[a-d]{2}[0-9]{2}/);
let billA, billB; let billA:string, billB:string;
let quantity = 0; // 数量 let quantity = 0; // 数量
let outQuantity = 0; // 调出数量 let outQuantity = 0; // 调出数量
@ -3310,8 +3304,7 @@ test.describe.serial('盘点', () => {
let ProductMargin; let ProductMargin;
// 获取单号的ID // 获取单号的ID
let billId; let billId;
/**@type {string} 单号*/ let billNo: string;
let billNo;
await test.step('记录各自批次数量', async () => { await test.step('记录各自批次数量', async () => {
// 点击库存 // 点击库存
@ -3537,8 +3530,7 @@ test.describe.serial('盘点', () => {
// 获取单号的ID // 获取单号的ID
let billId; let billId;
/**@type {string} 单号*/ let billNo: string;
let billNo;
await test.step('记录各自批次数量', async () => { await test.step('记录各自批次数量', async () => {
// 点击库存 // 点击库存

View File

@ -7,6 +7,7 @@ import { Customer } from '@/utils/customer';
import { staffData } from '@/fixtures/staff.js'; import { staffData } from '@/fixtures/staff.js';
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';
test.describe('商城', () => { test.describe('商城', () => {
test.describe('上架好物', () => { test.describe('上架好物', () => {
@ -1117,8 +1118,8 @@ test.describe('品牌', () => {
}); });
test('门店排序', async ({ page, homeNavigation }) => { test('门店排序', async ({ page, homeNavigation }) => {
let firstTrStoreName; let firstTrStoreName: string;
let secondTrStoreName; let secondTrStoreName: string;
await test.step('进入门店信息', async () => { await test.step('进入门店信息', async () => {
await homeNavigation.gotoModule('营销'); await homeNavigation.gotoModule('营销');
@ -1285,8 +1286,7 @@ test.describe('品牌', () => {
}); });
}); });
/**@type { import("@playwright/test").Page } 初始化页面*/ let initPage: Page;
let initPage;
test.beforeAll(async ({ browser, baseURL }) => { test.beforeAll(async ({ browser, baseURL }) => {
initPage = await browser.newPage(); initPage = await browser.newPage();
const homeNavigation = new HomeNavigation(initPage); const homeNavigation = new HomeNavigation(initPage);
@ -1336,7 +1336,7 @@ test.describe('品牌', () => {
test.describe('个性化', () => { test.describe('个性化', () => {
test('配色', async ({ page, homeNavigation }) => { test('配色', async ({ page, homeNavigation }) => {
let colorArray; let colorArray: string[];
await test.step('进入个性化装扮', async () => { await test.step('进入个性化装扮', async () => {
await homeNavigation.gotoModule('营销'); await homeNavigation.gotoModule('营销');
@ -1701,8 +1701,7 @@ test.describe('邀客系统', () => {
}); });
test('邀客管理', async ({ page, homeNavigation, tablePage, customerPage, createCustomers, marketingPage }) => { test('邀客管理', async ({ page, homeNavigation, tablePage, customerPage, createCustomers, marketingPage }) => {
/**@type {Customer[]} */ let customers: Customer[] = [];
let customers = [];
await test.step('创建顾客', async () => { await test.step('创建顾客', async () => {
customers = await createCustomers(2); customers = await createCustomers(2);
}); });

View File

@ -817,8 +817,7 @@ test.describe('对账流水', () => {
test('根据条件搜索对账流水', async ({ page, homeNavigation, createCustomer, wasteBookBusinessRecordPage }) => { test('根据条件搜索对账流水', async ({ page, homeNavigation, createCustomer, wasteBookBusinessRecordPage }) => {
const customer = createCustomer; const customer = createCustomer;
const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: '980' }; const project = { no: '100018', name: '苹果精萃护理', shortName: '精萃护理', price: '980' };
/**@type {string} 单号*/ let billNo: string;
let billNo;
await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => { await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => {
await homeNavigation.gotoModule('收银'); await homeNavigation.gotoModule('收银');
await page.getByRole('button', { name: /开\s单/ }).click(); await page.getByRole('button', { name: /开\s单/ }).click();
@ -930,8 +929,7 @@ test.describe('对账流水', () => {
const customer = createCustomer; const customer = createCustomer;
/**@type {string} 单号*/ let billNo:string;
let billNo;
await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => { await test.step('选择顾客开单,结算使用银联、支付宝、微信、欠款、现金', async () => {
await homeNavigation.gotoModule('收银'); await homeNavigation.gotoModule('收银');
@ -1000,7 +998,7 @@ test.describe('对账流水', () => {
]); ]);
// 拿取现金列的列数 // 拿取现金列的列数
let index; let index: number;
const thLocator = page.locator('.m-table__header tr').last().locator('th'); const thLocator = page.locator('.m-table__header tr').last().locator('th');
const headers = await thLocator.allInnerTexts(); const headers = await thLocator.allInnerTexts();
index = headers.findIndex(headerText => headerText.includes('支付方式')); index = headers.findIndex(headerText => headerText.includes('支付方式'));

View File

@ -1,56 +1,50 @@
export async function readIndexedDB() { export async function readIndexedDB() {
const databases = await indexedDB.databases(); const databases = await indexedDB.databases();
const allData: Array<{ databaseName: string; data: { key: IDBValidKey; value: any }[] }> = []; const allData = [];
for (const db of databases) { for (const db of databases) {
const databaseName = db.name!; const databaseName = db.name;
try { // 打开数据库
const dbInstance = await openDatabase(databaseName);
const transaction = dbInstance.transaction(dbInstance.objectStoreNames, 'readonly');
const objectStore = transaction.objectStore(dbInstance.objectStoreNames[0]);
const storeData = await getAllDataFromObjectStore(objectStore);
allData.push({
databaseName,
data: storeData,
});
dbInstance.close();
} catch (error) {
console.error(`Error reading database ${databaseName}:`, error);
}
}
return allData;
}
function openDatabase(databaseName: string): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(databaseName); const request = indexedDB.open(databaseName);
request.onsuccess = event => resolve(event.target.result); const databaseData = await new Promise((resolve, reject) => {
request.onerror = event => reject(event.target.error); request.onsuccess = (event) => {
}); const db = event.target.result;
} const transaction = db.transaction(db.objectStoreNames, 'readonly');
const objectStore = transaction.objectStore(db.objectStoreNames[0]);
function getAllDataFromObjectStore(objectStore: IDBObjectStore): Promise<Array<{ key: IDBValidKey; value: any }>> {
return new Promise((resolve, reject) => {
const storeData: Array<{ key: IDBValidKey; value: any }> = [];
const cursorRequest = objectStore.openCursor(); const cursorRequest = objectStore.openCursor();
cursorRequest.onsuccess = event => { const storeData = [];
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result; const cursor = event.target.result;
if (cursor) { if (cursor) {
storeData.push({ key: cursor.key, value: cursor.value }); storeData.push({ key: cursor.key, value: cursor.value });
cursor.continue(); cursor.continue();
} else { } else {
resolve(storeData); resolve(storeData); // 执行 resolve返回数据
} }
}; };
cursorRequest.onerror = event => reject(event.target.error); cursorRequest.onerror = (event) => {
reject(event.target.error); // 发生错误时 reject
};
};
request.onerror = (event) => {
reject(event.target.error); // 发生错误时 reject
};
}); });
// 将数据库数据存入 allData
allData.push({
databaseName,
data: databaseData,
});
}
return allData;
} }
export async function writeIndexedDB(jsonData) { export async function writeIndexedDB(jsonData) {