327 lines
11 KiB
JavaScript
327 lines
11 KiB
JavaScript
//@ts-check
|
|
const { expect } = require('@playwright/test');
|
|
const { Customer } = require('./customer');
|
|
|
|
class CustomerPage {
|
|
/**
|
|
* @param {import("@playwright/test").Page} page
|
|
*/
|
|
constructor(page) {
|
|
this.page = page;
|
|
this.$module = this.page.locator('.left_box .link_item').filter({ hasText: /顾客/ });
|
|
this.$tabItem = this.page.locator('.top_tab .tab_item');
|
|
this.$summary = this.$tabItem.filter({ hasText: '顾客概要' });
|
|
this.$distribution = this.$tabItem.filter({ hasText: '顾客分配' });
|
|
this.$dynamic = this.$tabItem.filter({ hasText: '顾客动态' });
|
|
this.$analysis = this.$tabItem.filter({ hasText: '顾客分析' });
|
|
this.$serviceLog = this.$tabItem.filter({ hasText: '服务日志' });
|
|
this.$register = this.page.locator('.regmeber_warp', { hasText: '创建会员' });
|
|
this.firstStore = {
|
|
firstDepartment: { no: 1, name: '美容部' },
|
|
secondDepartment: { no: 2, name: '医美部' },
|
|
};
|
|
this.secondStore = {
|
|
firstDepartment: { no: 1, name: '美容部' },
|
|
};
|
|
this.source = [
|
|
'邀客',
|
|
'员工带客',
|
|
'美团',
|
|
'大众点评',
|
|
'客带客',
|
|
'上门客人',
|
|
'百度糯米',
|
|
'支付宝',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 跳转子页面,并且等待页面接口加载完成
|
|
* @param {import("@playwright/test").Locator} locator
|
|
* @param {string[]} apiList
|
|
*/
|
|
gotoSubPage = async (locator, apiList) => {
|
|
await expect(async () => {
|
|
if (!(await locator.getAttribute('class'))?.includes('active')) {
|
|
await locator.click();
|
|
}
|
|
await expect(locator).toHaveClass(/active/);
|
|
}).toPass({ timeout: 30000 });
|
|
|
|
await Promise.all(
|
|
apiList.map((api) =>
|
|
this.page.waitForResponse((res) => res.url().includes(api) && res.status() === 200)
|
|
)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 跳转到顾客概要
|
|
*/
|
|
gotoSummary = async () => {
|
|
await this.gotoSubPage(this.$summary, ['summary', 'todo']);
|
|
};
|
|
|
|
/**
|
|
* 跳转到顾客分配
|
|
*/
|
|
gotoDistribution = async () => {
|
|
await this.gotoSubPage(this.$distribution, ['search_new', 'distribution']);
|
|
};
|
|
|
|
/**
|
|
* 跳转到顾客动态
|
|
*/
|
|
gotoDynamic = async () => {
|
|
await this.gotoSubPage(this.$dynamic, ['daily_action']);
|
|
};
|
|
|
|
/**
|
|
* 跳转到顾客分析
|
|
*/
|
|
gotoAnalysis = async () => {
|
|
await this.gotoSubPage(this.$analysis, ['analysis']);
|
|
};
|
|
|
|
/**
|
|
* 跳转到服务日志
|
|
*/
|
|
gotoServiceLog = async () => {
|
|
await this.gotoSubPage(this.$serviceLog, ['service_log']);
|
|
};
|
|
|
|
/**
|
|
* 搜索顾客
|
|
* @param {Customer} customer
|
|
*/
|
|
searchCustomer = async (customer) => {
|
|
const searchLocator = this.page.locator('.search_normal');
|
|
const searchInput = this.page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索');
|
|
await searchInput.fill(customer.phone);
|
|
await searchLocator.filter({ has: searchInput }).getByText('搜索', { exact: true }).click();
|
|
const customerLocator = this.page
|
|
.locator('.member_list .member_list_li')
|
|
.filter({ hasText: customer.username });
|
|
await customerLocator.click();
|
|
await expect(customerLocator).not.toBeVisible();
|
|
};
|
|
|
|
/**
|
|
* 打开顾客详情页面
|
|
* @param {Customer} customer
|
|
*/
|
|
openCustomerDetails = async (customer) => {
|
|
const $username = this.page.getByText(customer.username).last();
|
|
await $username.click();
|
|
const infoBox = this.page.locator('.member_info_box');
|
|
|
|
await Promise.all([
|
|
expect(infoBox.getByText(customer.username)).toBeVisible(),
|
|
this.page.waitForFunction(() => {
|
|
return document.readyState === 'complete';
|
|
}),
|
|
]);
|
|
};
|
|
|
|
/**
|
|
* 关闭顾客详情页面
|
|
*/
|
|
closeCustomerDetails = async () => {
|
|
const closeButton = this.page.locator('.member_info_box .close_icons > svg');
|
|
await closeButton.click();
|
|
await expect(closeButton).not.toBeVisible();
|
|
};
|
|
|
|
/**
|
|
* 创建顾客
|
|
* @param {Customer} customer 顾客
|
|
* @returns {Promise<void>}
|
|
*/
|
|
createCustomer = async (customer) => {
|
|
await expect(async () => {
|
|
await this.$module.click({ clickCount: 1 });
|
|
await expect(this.$module).toHaveClass(/active/, { timeout: 2000 });
|
|
}).toPass({ timeout: 30000 });
|
|
|
|
// 选择门店
|
|
await this.page.locator('.search_store > div').click();
|
|
await this.page.getByText('部门', { exact: true }).waitFor();
|
|
await this.page.locator('.shopSelect_box .shopSelect_shop_content').click();
|
|
await this.page
|
|
.locator('.com_picker .label')
|
|
.nth(customer.store - 1)
|
|
.click();
|
|
await this.page.getByRole('button', { name: /保.*存/ }).click();
|
|
|
|
await this.page.getByRole('button', { name: '新增顾客' }).click();
|
|
await this.$register.getByPlaceholder('请输入姓名').fill(customer.username);
|
|
await this.$register.getByPlaceholder('请输入会员手机号').fill(customer.phone);
|
|
await this.$register.getByPlaceholder('请输入会员手机号').click();
|
|
|
|
const checkPhoneLocator = this.$register
|
|
.locator('.ant-form-item', { hasText: '手机号' })
|
|
.locator('.ant-form-explain');
|
|
if (await checkPhoneLocator.isVisible()) {
|
|
const phoneStr = await checkPhoneLocator.innerText();
|
|
if (phoneStr.includes('非法手机号码') || phoneStr.includes('请输入会员手机号')) {
|
|
throw new Error(`手机号码:${customer.phone}不正确`);
|
|
}
|
|
}
|
|
|
|
// 输入档案号
|
|
if (customer.archive) {
|
|
await this.$register.getByPlaceholder('请输入12位以内的数字或字母').fill(customer.archive);
|
|
}
|
|
|
|
// 选择部门
|
|
await this.$register.locator('#register_departmentNo').getByRole('combobox').click();
|
|
if (customer.store === 1) {
|
|
if (customer.department === 1) {
|
|
await this.page.getByRole('option', { name: this.firstStore.firstDepartment.name }).click();
|
|
} else if (customer.department === 2) {
|
|
await this.page
|
|
.getByRole('option', { name: this.firstStore.secondDepartment.name })
|
|
.click();
|
|
}
|
|
} else if (customer.store === 2) {
|
|
if (customer.department === 1) {
|
|
await this.page
|
|
.getByRole('option', { name: this.secondStore.firstDepartment.name })
|
|
.click();
|
|
} else {
|
|
throw new Error(`部门:${customer.department}不存在`);
|
|
}
|
|
} else {
|
|
throw new Error(`门店:${customer.store}不存在`);
|
|
}
|
|
|
|
// 选择员工
|
|
|
|
// 选择性别
|
|
if (customer.gender) {
|
|
if (customer.gender === 0) {
|
|
await this.$register.locator('label').filter({ hasText: '女性' }).click();
|
|
} else if (customer.gender === 1) {
|
|
await this.$register.locator('label').filter({ hasText: '男性' }).click();
|
|
}
|
|
}
|
|
|
|
// 选择生日
|
|
const birthday = customer.birthday;
|
|
const birthdayLocator = this.$register.locator('.ant-form-item', { hasText: '生日' });
|
|
if (birthday) {
|
|
const { year, month, day } = birthday;
|
|
if (year) {
|
|
await birthdayLocator.getByText('年份').click();
|
|
await this.page.getByRole('option', { name: `${year}` }).click();
|
|
await expect(this.page.getByRole('option', { name: `${year}` })).not.toBeVisible();
|
|
}
|
|
if (month && day) {
|
|
await birthdayLocator.getByText('日期').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('button', { name: /确\s认/ }).click();
|
|
} else {
|
|
throw new Error(`month:${month}, day:${day}其中一个为空`);
|
|
}
|
|
}
|
|
|
|
// 选择顾客来源
|
|
// await this.$register.getByLabel(this.source[customer.source]).click();
|
|
|
|
await this.$register.getByText('请选择顾客来源').click();
|
|
await this.page.getByRole('option', { name: this.source[customer.source] }).first().click();
|
|
|
|
// 选择备注
|
|
if (customer.remark) {
|
|
await this.$register.getByPlaceholder('请输入1-100个字符备注内容').fill(customer.remark);
|
|
}
|
|
|
|
const [response] = await Promise.all([
|
|
this.page.waitForResponse(
|
|
async (res) => res.url().includes('/invalid_check') && (await res.json()).code === 'SUCCESS'
|
|
),
|
|
this.page.getByRole('button', { name: '确认创建' }).click(),
|
|
]);
|
|
const responseBody = await response.json();
|
|
|
|
const phoneStatus = responseBody?.content?.status;
|
|
if (phoneStatus) {
|
|
await this.page
|
|
.getByText('系统查询到当前手机号被建档后转为无效客,是否要恢复无效客?')
|
|
.waitFor();
|
|
await this.page.getByRole('button', { name: '重新建档' }).click();
|
|
// 检查弹窗信息
|
|
const popupWindow = this.page.locator('.ant-message');
|
|
await popupWindow.waitFor();
|
|
const popupContent = (await popupWindow.innerText()).trim();
|
|
if (popupContent.includes('该手机号码已经被使用')) {
|
|
throw new Error(`该手机号码:${customer.phone}已经被使用`);
|
|
}
|
|
}
|
|
|
|
await this.page.locator('.person_content').waitFor();
|
|
};
|
|
|
|
/**
|
|
* 设置顾客为无效客
|
|
* @param {Customer} customer 顾客
|
|
* @returns {Promise<void>}
|
|
*/
|
|
setInvalidCustomer = async (customer) => {
|
|
await this.page.goto(process.env.BASE_URL || '', { waitUntil: 'load' });
|
|
const moduleLocator = this.$module;
|
|
const activeLocator = moduleLocator.locator('.active_arrow');
|
|
await expect(async () => {
|
|
if (!(await activeLocator.isVisible())) {
|
|
await moduleLocator.click({ clickCount: 1 });
|
|
}
|
|
await expect(activeLocator).toBeVisible({ timeout: 2_000 });
|
|
}).toPass({ timeout: 30_000 });
|
|
|
|
// 根据手机号进行搜索,进入顾客详情页面
|
|
await this.page.getByPlaceholder('姓名(拼音首字)、手机号、档案号搜索').fill(customer.phone);
|
|
await this.page.locator('.ant-input-suffix .search_btn', { hasText: '搜索' }).click();
|
|
await this.page.locator('.custom_content', { hasText: customer.phone }).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.getByRole('menuitem', { name: '设为无效客' }).click();
|
|
const [response] = await Promise.all([
|
|
this.page.waitForResponse(
|
|
(response) =>
|
|
response.url().includes('/customer') && response.request().method() === 'PATCH'
|
|
),
|
|
this.page.getByRole('button', { name: /确\s认/ }).click(),
|
|
]);
|
|
|
|
const responseBody = await response.json();
|
|
const code = responseBody?.code;
|
|
expect(code).toBe('SUCCESS');
|
|
};
|
|
|
|
/**
|
|
* 批量创建顾客
|
|
* @param {Array<Customer>} customerArray
|
|
*/
|
|
createMoreCustomer = async (customerArray) => {
|
|
for (const customer of customerArray) {
|
|
await this.createCustomer(customer);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 批量设置无效客
|
|
* @param {Array<Customer>} customerArray
|
|
*/
|
|
setMoreInvalidCustomer = async (customerArray) => {
|
|
for (const customer of customerArray) {
|
|
await this.setInvalidCustomer(customer);
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = { CustomerPage };
|