划分美管加、慧来客、智荟宝项目

This commit is contained in:
wenpeng 2024-10-18 14:36:06 +08:00
parent 228c806846
commit 4bca759adb
35 changed files with 659 additions and 611 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,111 +0,0 @@
{
"cookies": [
{
"name": "realParentShopId",
"value": "1063590",
"domain": "vip1.meiguanjia.net",
"path": "/shair/",
"expires": 1763607851.35647,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "v",
"value": "mgj",
"domain": "vip1.meiguanjia.net",
"path": "/shair/",
"expires": 1763607851.356543,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "JSESSIONID",
"value": "6F73A614C651DB7F8F1F9EC08A03658D",
"domain": "vip1.meiguanjia.net",
"path": "/shair",
"expires": -1,
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
},
{
"name": "shopId",
"value": "1063590",
"domain": ".meiguanjia.net",
"path": "/shair",
"expires": -1,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "userId",
"value": "1424506",
"domain": ".meiguanjia.net",
"path": "/shair",
"expires": -1,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "username",
"value": "%E5%B0%8F%E7%BE%8E%E7%BE%8E%E4%B8%9A",
"domain": "vip1.meiguanjia.net",
"path": "/shair",
"expires": 1729652651,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "token",
"value": "de064679-b32a-4617-9135-ca1289f568ae",
"domain": ".meiguanjia.net",
"path": "/shair",
"expires": -1,
"httpOnly": false,
"secure": false,
"sameSite": "Lax"
},
{
"name": "io",
"value": "x4_ZagMh7LMUnzzFGbNQ",
"domain": "socketlogin.meiguanjia.net",
"path": "/",
"expires": -1,
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
}
],
"origins": [
{
"origin": "https://vip1.meiguanjia.net",
"localStorage": [
{
"name": "CLUSTER",
"value": "{\"cluster\":\"vip\",\"existence\":true,\"shopName\":\"小美美业3\",\"list\":[{\"id\":1424506,\"parentShopId\":1063590,\"shopId\":1063590,\"barberId\":null,\"barberName\":null,\"cardTypeId\":null,\"schemeIds\":null,\"cardtypeid\":null,\"softgenre\":null,\"token\":null,\"period\":null,\"startDatetime\":null,\"endDatetime\":null,\"fromHis\":0,\"lastUpdateTime\":null,\"operateStr\":null,\"powerStr\":null,\"descriptionFlag\":0,\"itemIdStr\":null,\"username\":null,\"openid\":null,\"role\":\"3\",\"name\":\"11\",\"roleStr\":null,\"type\":null,\"shopName\":\" \",\"shopIds\":null,\"qyWechatUserid\":null,\"status\":null,\"auditStatus\":null,\"mobile\":\"18900000001\",\"hqName\":\"小美美业3\",\"invaliddate\":1819512000000,\"shopStatus\":\"1\",\"mobileFlag\":false}],\"url\":\"https://vip1.meiguanjia.net/\"}"
},
{
"name": "SHAIR_NEW_MODE",
"value": "false"
},
{
"name": "renewalLastQueryTime",
"value": "1729047852587"
},
{
"name": "themeConfig",
"value": "1063590"
},
{
"name": "mgjversion",
"value": "3"
}
]
}
]
}

File diff suppressed because one or more lines are too long

4
.env
View File

@ -6,7 +6,9 @@ SMSCODE=1660
ZHB_ACCOUNT=18571458300
ZHB_PASSWORD=1
ZHB_BASE_URL="https://shengyibao.meiguanjia.net/shair/components/shair-web/base-system/index.html#"
# ZHB_BASE_URL="https://shengyibao.meiguanjia.net/shair/components/shair-web/base-system/index.html#"
ZHB_BASE_URL="https://shengyibao.meiguanjia.net/young/"
MGJ_ACCOUNT=小美美业
MGJ_PASSWORD=1

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ node_modules/
/playwright-report/
/blob-report/
/playwright/.cache/
.auth/

24
package-lock.json generated
View File

@ -10,7 +10,7 @@
"license": "ISC",
"devDependencies": {
"@faker-js/faker": "^9.0.3",
"@playwright/test": "^1.48.0",
"@playwright/test": "^1.48.1",
"@types/node": "^22.7.5",
"dotenv": "^16.4.5"
}
@ -33,13 +33,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.48.0",
"resolved": "https://registry.npmmirror.com/@playwright/test/-/test-1.48.0.tgz",
"integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==",
"version": "1.48.1",
"resolved": "https://registry.npmmirror.com/@playwright/test/-/test-1.48.1.tgz",
"integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.48.0"
"playwright": "1.48.1"
},
"bin": {
"playwright": "cli.js"
@ -87,13 +87,13 @@
}
},
"node_modules/playwright": {
"version": "1.48.0",
"resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.48.0.tgz",
"integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==",
"version": "1.48.1",
"resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.48.1.tgz",
"integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.48.0"
"playwright-core": "1.48.1"
},
"bin": {
"playwright": "cli.js"
@ -106,9 +106,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.48.0",
"resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.48.0.tgz",
"integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==",
"version": "1.48.1",
"resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.48.1.tgz",
"integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@ -1,19 +1,21 @@
{
"name": "playwright-demo",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"codegen": "npx playwright codegen",
"name": "playwright-demo",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"hlk_codegen": "npx playwright codegen https://hlk.meiguanjia.net/#/",
"mgj_codegen": "npx playwright codegen https://vip1.meiguanjia.net/shair/?v=mgj",
"zhb_codegen": "npx playwright codegen https://shengyibao.meiguanjia.net/young/",
"ui": "npx playwright test --ui"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@faker-js/faker": "^9.0.3",
"@playwright/test": "^1.48.0",
"@types/node": "^22.7.5",
"dotenv": "^16.4.5"
}
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@faker-js/faker": "^9.0.3",
"@playwright/test": "^1.48.1",
"@types/node": "^22.7.5",
"dotenv": "^16.4.5"
}
}

View File

@ -5,8 +5,9 @@ const path = require('path');
// Read from ".env" file.
dotenv.config({ path: path.resolve(__dirname, '.env') });
const adminAuthFile = path.join(__dirname, '../.auth/admin.json');
const zhbAuthFile = path.join(__dirname, '../.auth/zhb_admin.json');
const hlkAuthFile = path.join(__dirname, '.auth/hlk_admin.json');
const zhbAuthFile = path.join(__dirname, '.auth/zhb_admin.json');
const mgjAuthFile = path.join(__dirname, '.auth/mgj_admin.json');
/**
* Read environment variables from file.
@ -18,79 +19,74 @@ const zhbAuthFile = path.join(__dirname, '../.auth/zhb_admin.json');
* @see https://playwright.dev/docs/test-configuration
*/
module.exports = defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL,
timezoneId: 'Asia/Shanghai',
locale: 'zh-CN',
geolocation: { longitude: 114.24, latitude: 22.73 },
permissions: ['geolocation'],
extraHTTPHeaders: {
'Accept-Language': 'zh-CN,zh;q=0.9',
},
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL,
timezoneId: 'Asia/Shanghai',
locale: 'zh-CN',
geolocation: { longitude: 114.24, latitude: 22.73 },
permissions: ['geolocation'],
extraHTTPHeaders: {
'Accept-Language': 'zh-CN,zh;q=0.9',
},
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{ name: 'hlk_setup', testMatch: /.*\.setup\.js/ },
{ name: 'zhb_setup', testMatch: /.*\.setup\.js/ },
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: adminAuthFile },
dependencies: ['hlk_setup'],
},
/* Configure projects for major browsers */
projects: [
{ name: 'hlk_setup', use: { baseURL: process.env.BASE_URL }, testMatch: /hlk\.setup\.js/ },
{ name: 'zhb_setup', use: { baseURL: process.env.ZHB_BASE_URL }, testMatch: /zhb\.setup\.js/ },
{ name: 'mgj_setup', use: { baseURL: process.env.MGJ_BASE_URL }, testMatch: /mgj\.setup\.js/ },
{
name: 'chromium',
use: {
baseURL: process.env.BASE_URL,
...devices['Desktop Chrome'],
storageState: hlkAuthFile,
},
testMatch: '**/tests/hlk/**.spec.js',
dependencies: ['hlk_setup'],
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'], storageState: adminAuthFile },
dependencies: ['hlk_setup'],
},
{
name: 'chromium',
use: {
baseURL: process.env.ZHB_BASE_URL,
...devices['Desktop Chrome'],
storageState: zhbAuthFile,
},
testMatch: '**/tests/zhb/**.spec.js',
dependencies: ['zhb_setup'],
},
{
name: 'chromium',
use: {
baseURL: process.env.MGJ_BASE_URL,
...devices['Desktop Chrome'],
storageState: mgjAuthFile,
},
testMatch: '**/tests/mgj/**.spec.js',
dependencies: ['mgj_setup'],
},
],
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: adminAuthFile },
dependencies: ['zhb_setup'],
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});

View File

@ -1,54 +0,0 @@
const { test: setup, expect } = require('@playwright/test');
const path = require('path');
const authFile = path.join(__dirname, '../.auth/admin.json');
const zhbAuthFile = path.join(__dirname, '../.auth/zhb_admin.json');
const mgjAuthFile = path.join(__dirname, '../.auth/mgj_admin.json');
setup('hlk总部管理员登录', async ({ page, baseURL }) => {
const $account = page.getByRole('textbox', { name: '请输入您的手机号码' });
const $password = page.getByRole('textbox', { name: '请输入登录密码' });
const $loginBtn = page.getByRole('button', { name: /登\s录/ });
const account = process.env.ACCOUNT;
const password = process.env.PASSWORD;
await page.goto(baseURL);
await $account.fill(account);
const phoneStatsIcon = page.locator('.ant-row', { has: $account }).locator('.pass_svg');
await $password.fill(password);
await page.getByLabel('请同意慧来客隐私政策和用户协议').check();
await expect(phoneStatsIcon).toBeVisible();
await $loginBtn.click();
await expect(page.getByRole('button', { name: /开\s单/ })).toBeVisible();
await page.context().storageState({ path: authFile });
});
setup('zhb总部管理员登录', async ({ page }) => {
const baseURL = process.env.ZHB_BASE_URL;
const account = process.env.ZHB_ACCOUNT;
const password = process.env.ZHB_PASSWORD;
const $account = page.getByPlaceholder('账户');
const $password = page.getByPlaceholder('密码');
const $loginBtn = page.getByRole('button', { name: /登\s录/ });
await page.goto(baseURL);
await $account.fill(account);
await $password.fill(password);
await $loginBtn.click();
await expect(page.getByRole('heading', { name: '收银' })).toBeVisible();
await page.context().storageState({ path: zhbAuthFile });
});
setup('mgj管理员登录', async ({ page }) => {
const baseURL = process.env.MGJ_BASE_URL;
const account = process.env.MGJ_ACCOUNT;
const password = process.env.MGJ_PASSWORD;
await page.goto(baseURL);
await page.getByPlaceholder('请输入您的用户名').fill(account);
await page.getByPlaceholder('请输入密码').fill(password);
await page.getByText('登录', { exact: true }).click();
await expect(page.getByRole('link', { name: '管理层' })).toBeVisible();
await page.context().storageState({ path: mgjAuthFile });
});

View File

@ -1,73 +0,0 @@
const { test, expect } = require('./fixtures/common');
const { Customer } = require('./customer');
const { faker } = require('@faker-js/faker/locale/zh_CN');
// test('has title', async ({ page }) => {
// await page.goto('https://playwright.dev/');
// // Expect a title "to contain" a substring.
// await expect(page).toHaveTitle(/Playwright/);
// });
// test('get started link', async ({ page }) => {
// await page.goto('https://playwright.dev/');
// // Click the get started link.
// await page.getByRole('link', { name: 'Get started' }).click();
// // Expects page to have a heading with the name of Installation.
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
// });
test('登录touch和h5创建顾客购买会员卡使用顾客账号到h5商城购买货品', async ({
touchPage,
h5Page,
touchCustomerPage,
h5LoginPage,
}) => {
const phone = faker.helpers.fromRegExp(/1[3-9][0-1]{9}/);
const customer = new Customer(1, 1, { phone: phone });
await touchCustomerPage.createCustomer(customer);
await test.step('在touch页面购买会员卡', async () => {
await touchPage.getByText('去开单').click();
await touchPage.locator('.more > .icon > svg').click();
await touchPage.getByText('去开卡').click();
const $firstCard = touchPage.locator('.memberCard_box').first();
await $firstCard.click();
const cardName = await $firstCard.locator('.card_name').innerText();
await touchPage.getByRole('button', { name: '去结算' }).click();
await touchPage.locator('.row').filter({ hasText: '总额' }).locator('.touchIcon').click();
await touchPage.getByPlaceholder('请输入内容').fill('1000');
await touchPage.locator('div').filter({ hasText: /^789$/ }).getByRole('button').nth(3).click();
await touchPage.locator('.paymentInfoItem', { hasText: '现金' }).click();
await touchPage.getByText('推送消费提醒').click();
await touchPage.getByLabel('结算签字').uncheck();
await touchPage.getByRole('button', { name: /结\s算/ }).click();
await touchPage.getByRole('button', { name: /跳\s过/ }).click();
});
await test.step('登录h5并且商城页面使用会员卡进行购买', async () => {
await h5LoginPage.login(customer.phone);
await h5Page.locator('.singIn_content > .iconfont').click();
await h5Page.locator('.get_btn').click();
await h5Page.locator('.back').click();
await h5Page.locator('.bar_item', { hasText: '商城' }).click();
await expect(h5Page.locator('.title', { hasText: '商城' })).toBeVisible();
await h5Page.locator('.li span', { hasText: '全部' }).click();
await h5Page.locator('.p-item').first().click();
await h5Page.waitForLoadState();
await h5Page.getByText('立即购买').click();
await h5Page
.locator('.goodsClassBoxMain .footerBug_true_all')
.filter({ hasText: '确定' })
.click();
await h5Page.locator('.mgj-picker-head .mgj-picker-btn').filter({ hasText: '确认' }).click();
await expect(h5Page.locator('.pay-way .form_row', { hasText: cardName })).toHaveClass(
/click-able/
);
await expect(h5Page.getByText('确认支付')).toBeEnabled();
await h5Page.getByText('确认支付').click();
await expect(h5Page.getByText('支付成功').first()).toBeVisible();
await expect(h5Page.getByText('查看订单')).toBeVisible();
});
});

View File

@ -1,51 +0,0 @@
const { test: base, expect, devices } = require('@playwright/test');
const path = require('path');
const authFile = path.resolve(__dirname, '../../.auth/admin.json');
const zhbAuthFile = path.join(__dirname, '../.auth/zhb_admin.json');
const test = base.extend({
/**
* @type {import('@playwright/test').Page}
*/
touchPage: async ({ browser, baseURL }, use) => {
const context = await browser.newContext({
storageState: authFile,
});
const page = await context.newPage();
await page.goto(baseURL);
await use(page);
await page.close();
await context.close();
},
/**
* @type {import('@playwright/test').Page}
*/
h5Page: async ({ browser }, use) => {
const iPhone = devices['iPhone 11'];
const context = await browser.newContext({
...iPhone,
storageState: undefined,
});
const page = await context.newPage();
await page.goto(process.env.H5_BASE_URL);
await use(page);
},
/**
* @type {import('@playwright/test').Page}
*/
zhbPage: async ({ browser, baseURL }, use) => {
const context = await browser.newContext({
storageState: zhbAuthFile,
});
const page = await context.newPage();
await page.goto(baseURL);
await use(page);
await page.close();
await context.close();
},
});
module.exports = {
test,
expect,
};

67
tests/hlk/demo.spec.js Normal file
View File

@ -0,0 +1,67 @@
const { test, expect } = require('./fixtures/common');
const { Customer } = require('./pom/hlk/customer');
const { faker } = require('@faker-js/faker/locale/zh_CN');
for (let i = 0; i < 10; i++) {
test(`登录touch和h5创建顾客购买会员卡使用顾客账号到h5商城购买货品${i}`, async ({
touchPage,
h5Page,
touchCustomerPage,
h5LoginPage,
}) => {
const phone = faker.helpers.fromRegExp(/1[3-9][0-1]{9}/);
const customer = new Customer(1, 1, { phone: phone });
await touchCustomerPage.createCustomer(customer);
let cardName;
await test.step('在touch页面购买会员卡', async () => {
await touchPage.getByText('去开单').click();
await touchPage.locator('.more > .icon > svg').click();
await touchPage.getByText('去开卡').click();
const $firstCard = touchPage.locator('.memberCard_box').first();
await $firstCard.click();
cardName = await $firstCard.locator('.card_name').innerText();
await touchPage.getByRole('button', { name: '去结算' }).click();
await touchPage.locator('.row').filter({ hasText: '总额' }).locator('.touchIcon').click();
await touchPage.getByPlaceholder('请输入内容').fill('1000');
await touchPage
.locator('div')
.filter({ hasText: /^789$/ })
.getByRole('button')
.nth(3)
.click();
await touchPage.locator('.paymentInfoItem', { hasText: '现金' }).click();
await touchPage.getByText('推送消费提醒').click();
await touchPage.getByLabel('结算签字').uncheck();
await touchPage.getByRole('button', { name: /结\s算/ }).click();
await touchPage.getByRole('button', { name: /跳\s过/ }).click();
});
await test.step('登录h5并且商城页面使用会员卡进行购买', async () => {
await h5LoginPage.login(customer.phone);
await h5Page.locator('.singIn_content > .iconfont').click();
await h5Page.locator('.get_btn').click();
await h5Page.locator('.back').click();
await h5Page.locator('.bar_item', { hasText: '商城' }).click();
await expect(h5Page.locator('.title', { hasText: '商城' })).toBeVisible();
await h5Page.locator('.li span', { hasText: '全部' }).click();
await h5Page.locator('.p-item').first().click();
await h5Page.waitForLoadState();
await h5Page.getByText('立即购买').click();
await h5Page
.locator('.goodsClassBoxMain .footerBug_true_all')
.filter({ hasText: '确定' })
.click();
await h5Page.locator('.mgj-picker-head .mgj-picker-btn').filter({ hasText: '确认' }).click();
await expect(
h5Page
.locator('.pay-way .form_row', { hasText: cardName })
.locator('.radio_btn .uni-radio-input')
).toHaveClass(/uni-radio-input-checked/);
await expect(h5Page.getByText('确认支付')).toBeEnabled();
await h5Page.getByText('确认支付').click();
await expect(h5Page.getByText('支付成功').first()).toBeVisible();
await expect(h5Page.getByText('查看订单')).toBeVisible();
});
});
}

View File

@ -0,0 +1,38 @@
const { test: base, expect, devices } = require('@playwright/test');
const authFile = '.auth/hlk_admin.json';
const test = base.extend({
/**
* @type {import('@playwright/test').Page}
*/
touchPage: async ({ browser, baseURL }, use) => {
const context = await browser.newContext({
storageState: authFile,
});
const page = await context.newPage();
await page.goto(baseURL);
await use(page);
await page.close();
await context.close();
},
/**
* @type {import('@playwright/test').Page}
*/
h5Page: async ({ browser }, use) => {
const iPhone = devices['iPhone 11'];
const context = await browser.newContext({
...iPhone,
storageState: undefined,
});
const page = await context.newPage();
await page.goto(process.env.H5_BASE_URL);
await use(page);
},
});
module.exports = {
test,
expect,
};

View File

@ -1,5 +1,5 @@
const { test: base, expect } = require('./base');
const { H5LoginPage } = require('../pages/h5LoginPage');
const { H5LoginPage } = require('../pom/hlk/h5LoginPage');
const test = base.extend({
/**

View File

@ -1,5 +1,5 @@
const { test: base, expect } = require('./base');
const { CustomerPage } = require('../pages/customerPage');
const { CustomerPage } = require('../pom/hlk/customerPage');
const test = base.extend({
/**

View File

@ -1,6 +1,6 @@
//@ts-check
const { expect } = require('@playwright/test');
const { Customer } = require('../customer');
const { Customer } = require('./customer');
class CustomerPage {
/**

97
tests/mgj/demo.spec.js Normal file
View File

@ -0,0 +1,97 @@
const { test, expect } = require('./fixture/common');
const { Customer } = require('./pom/customerPage');
test('demo', async ({ zhbPage, customerPage }) => {
const $area = zhbPage
.locator('.area')
.filter({ has: zhbPage.locator('.area-name', { hasText: '二楼' }) });
const $$room = $area.locator('.room-list .room');
await zhbPage.locator('#tab_main li').filter({ hasText: '顾客' }).click();
const customer = new Customer();
await customerPage.createCustomer(customer);
let useRoomName;
await test.step('购买商品', async () => {
await zhbPage.locator('#tab_main li').filter({ hasText: '营业' }).click();
const $emptyRoom = $$room.filter({ has: zhbPage.getByText('空房') }).first();
useRoomName = await $emptyRoom.locator('.roomName').innerText();
expect(useRoomName).not.toBeNull();
await $emptyRoom.click();
await expect(async () => {
await zhbPage.getByRole('button', { name: '选择顾客' }).click();
await expect(zhbPage.locator('#page_searchMember').getByText('创建会员')).toBeVisible();
}).toPass();
await zhbPage
.getByRole('textbox', { name: '输入会员手机号或姓名或卡号搜索' })
.fill(customer.phone, { delay: 100 });
await zhbPage.locator('#page_searchMember svg').click();
const $customerTr = zhbPage
.locator('.list-warp')
.filter({ has: zhbPage.locator('.name', { hasText: customer.name }) });
await $customerTr.locator('.list-body').first().click();
await zhbPage.getByText('项目开单').click();
await expect(zhbPage.locator('#page_roomDetail').getByText('服务项目')).toBeVisible();
await zhbPage.getByText('选择', { exact: true }).nth(1).click();
await expect(zhbPage.locator('#serviceSelector').getByText('项目选择')).toBeVisible();
await zhbPage.getByText('臻品足疗 (10101)').click();
await zhbPage.locator('#serviceSelector').getByText('确认').click();
await zhbPage
.locator('div')
.filter({ hasText: /^匠星足浴$/ })
.locator('span')
.first()
.click();
await zhbPage.getByRole('button', { name: '完成开单' }).click();
await expect(zhbPage.getByRole('button', { name: '结账' })).toBeVisible();
await zhbPage.getByRole('button', { name: '结账' }).click();
await zhbPage.locator('#page_footBathPay').getByText('结算签字').click();
await expect(async () => {
await zhbPage.locator('#page_footBathPay li').filter({ hasText: '现金' }).click();
await expect(zhbPage.locator('#page_footBathPay li').filter({ hasText: '现金' })).toHaveClass(
/selected/
);
await zhbPage
.locator('#page_footBathPay span')
.filter({ hasText: /结\s算/ })
.click();
await expect(zhbPage.getByText('顾客满意度点评')).toBeVisible({ timeout: 2000 });
}).toPass();
await zhbPage.getByText('不想评价').click();
await expect(zhbPage.locator('#page_footBathPay').getByText('立即返回')).not.toBeVisible();
});
await test.step('起钟下钟,清理房间', async () => {
const $cleanRoom = $$room.filter({
has: zhbPage.locator('.roomName', { hasText: new RegExp(`^${useRoomName}$`) }),
});
await expect($cleanRoom).toContainText('已结清');
await $cleanRoom.click();
await zhbPage.getByText('技师操作').click();
await zhbPage.getByText('起钟', { exact: true }).click();
await zhbPage.getByText('起钟成功!').click();
await zhbPage.getByText('技师操作').click();
await zhbPage.getByText('下钟', { exact: true }).click();
await zhbPage
.locator('div')
.filter({ hasText: /^确认返回$/ })
.locator('div')
.first()
.click();
await zhbPage.getByRole('button', { name: '不需要' }).click();
await expect($cleanRoom).toContainText('打扫');
await $cleanRoom.click();
await zhbPage
.locator('div')
.filter({ hasText: /^确定取消$/ })
.locator('div')
.first()
.click();
await expect($cleanRoom).toContainText('空房');
});
});

25
tests/mgj/fixture/base.js Normal file
View File

@ -0,0 +1,25 @@
const { test: base, expect } = require('@playwright/test');
const authFile = '.auth/mgj_admin.json';
const test = base.extend({
/**
* @type {import('@playwright/test').Page}
*/
mgjPage: async ({ browser, baseURL }, use) => {
const context = await browser.newContext({
storageState: authFile,
});
const page = await context.newPage();
await page.goto(baseURL);
console.log(baseURL);
await use(page);
await page.close();
await context.close();
},
});
module.exports = {
test,
expect,
};

View File

@ -0,0 +1,6 @@
const { mergeTests } = require('@playwright/test');
const { test: customerTest } = require('./customerFixture');
export const test = mergeTests(customerTest);
export { expect } from '@playwright/test';

View File

@ -0,0 +1,12 @@
import { test as base } from './base';
import { CustomerPage } from '../pom/customerPage';
export const test = base.extend({
/**
* @type {CustomerPage}
*/
customerPage: async ({ zhbPage }, use) => {
const customerPage = new CustomerPage(zhbPage);
await use(customerPage);
},
});

View File

@ -0,0 +1,54 @@
const { faker } = require('@faker-js/faker/locale/zh_CN');
const { expect } = require('@playwright/test');
export class Customer {
constructor({
name = faker.person.fullName(),
phone = faker.helpers.fromRegExp(/1[3-9][0-9]{6}/),
} = {}) {
this.name = name;
this.phone = phone;
}
}
export class CustomerPage {
/**
* @param {import('@playwright/test').Page} page
*/
constructor(page) {
this.page = page;
}
/**
* 创建顾客
* @param {Customer} customer
*/
createCustomer = async (customer) => {
console.log(customer.name);
console.log(customer.phone);
await expect(this.page.locator('#page_member').getByText('新增顾客档案')).toBeVisible();
await this.page.locator('#page_member').getByText('新增顾客档案').click();
await this.page.getByPlaceholder('请输入会员姓名').fill(customer.name);
await this.page.getByPlaceholder('请输入手机号码').click();
await expect(this.page.getByText('请输入数字')).toBeVisible();
await this.page.getByPlaceholder('在此输入').click();
await this.page.keyboard.type(customer.phone, { delay: 100 });
// await this.page.getByPlaceholder('在此输入').type(customer.phone, { delay: 100 });
await this.page.locator('#maskBoard').getByText('确认').click();
await expect(this.page.getByPlaceholder('请输入手机号码')).toHaveValue(customer.phone, {
timeout: 2000,
});
await this.page
.locator('div')
.filter({ hasText: /^员工带客$/ })
.locator('span')
.first()
.click();
await this.page.getByText('创建', { exact: true }).click();
await expect(this.page.getByText('用户资料创建成功!')).toBeVisible();
await this.page.getByText('以后再说').click();
await expect(this.page.getByText('以后再说')).not.toBeVisible();
};
}

22
tests/setup/hlk.setup.js Normal file
View File

@ -0,0 +1,22 @@
const { test: setup, expect } = require('@playwright/test');
const path = require('path');
const hlkAuthFile = path.join(__dirname, '../../.auth/hlk_admin.json');
setup('hlk总部管理员登录', async ({ page, baseURL }) => {
const $account = page.getByRole('textbox', { name: '请输入您的手机号码' });
const $password = page.getByRole('textbox', { name: '请输入登录密码' });
const $loginBtn = page.getByRole('button', { name: /登\s录/ });
const account = process.env.ACCOUNT;
const password = process.env.PASSWORD;
await page.goto(baseURL);
await $account.fill(account);
const phoneStatsIcon = page.locator('.ant-row', { has: $account }).locator('.pass_svg');
await $password.fill(password);
await page.getByLabel('请同意慧来客隐私政策和用户协议').check();
await expect(phoneStatsIcon).toBeVisible();
await $loginBtn.click();
await expect(page.getByRole('button', { name: /开\s单/ })).toBeVisible();
await page.context().storageState({ path: hlkAuthFile });
});

16
tests/setup/mgj.setup.js Normal file
View File

@ -0,0 +1,16 @@
const { test: setup, expect } = require('@playwright/test');
const path = require('path');
const mgjAuthFile = path.join(__dirname, '../../.auth/mgj_admin.json');
setup('mgj管理员登录', async ({ page,baseURL }) => {
const account = process.env.MGJ_ACCOUNT;
const password = process.env.MGJ_PASSWORD;
await page.goto(baseURL);
await page.getByPlaceholder('请输入您的用户名').fill(account);
await page.getByPlaceholder('请输入密码').fill(password);
await page.getByText('登录', { exact: true }).click();
await expect(page.getByRole('link', { name: '管理层' })).toBeVisible();
await page.context().storageState({ path: mgjAuthFile });
});

20
tests/setup/zhb.setup.js Normal file
View File

@ -0,0 +1,20 @@
const { test: setup, expect } = require('@playwright/test');
const path = require('path');
const zhbAuthFile = path.join(__dirname, '../../.auth/zhb_admin.json');
setup('zhb总部管理员登录', async ({ page, baseURL }) => {
const account = process.env.ZHB_ACCOUNT;
const password = process.env.ZHB_PASSWORD;
await page.goto(baseURL);
await page.getByText('账号登录').click();
await page.getByPlaceholder('用户名').fill(account);
await page.getByPlaceholder('密码', { exact: true }).fill(password);
await page.getByText('登录', { exact: true }).click();
await expect(page.getByText('演示一店')).toBeVisible();
await expect(page.getByText('演示二店')).toBeVisible();
await page.getByText('演示一店').click();
await expect(page.locator('#tab_main li').filter({ hasText: '营业' })).toBeVisible();
await page.context().storageState({ path: zhbAuthFile });
});

97
tests/zhb/demo.spec.js Normal file
View File

@ -0,0 +1,97 @@
const { test, expect } = require('./fixture/common');
const { Customer } = require('./pom/customerPage');
test('demo', async ({ zhbPage, customerPage }) => {
const $area = zhbPage
.locator('.area')
.filter({ has: zhbPage.locator('.area-name', { hasText: '二楼' }) });
const $$room = $area.locator('.room-list .room');
await zhbPage.locator('#tab_main li').filter({ hasText: '顾客' }).click();
const customer = new Customer();
await customerPage.createCustomer(customer);
let useRoomName;
await test.step('购买商品', async () => {
await zhbPage.locator('#tab_main li').filter({ hasText: '营业' }).click();
const $emptyRoom = $$room.filter({ has: zhbPage.getByText('空房') }).first();
useRoomName = await $emptyRoom.locator('.roomName').innerText();
expect(useRoomName).not.toBeNull();
await $emptyRoom.click();
await expect(async () => {
await zhbPage.getByRole('button', { name: '选择顾客' }).click();
await expect(zhbPage.locator('#page_searchMember').getByText('创建会员')).toBeVisible();
}).toPass();
await zhbPage
.getByRole('textbox', { name: '输入会员手机号或姓名或卡号搜索' })
.fill(customer.phone, { delay: 100 });
await zhbPage.locator('#page_searchMember svg').click();
const $customerTr = zhbPage
.locator('.list-warp')
.filter({ has: zhbPage.locator('.name', { hasText: customer.name }) });
await $customerTr.locator('.list-body').first().click();
await zhbPage.getByText('项目开单').click();
await expect(zhbPage.locator('#page_roomDetail').getByText('服务项目')).toBeVisible();
await zhbPage.getByText('选择', { exact: true }).nth(1).click();
await expect(zhbPage.locator('#serviceSelector').getByText('项目选择')).toBeVisible();
await zhbPage.getByText('臻品足疗 (10101)').click();
await zhbPage.locator('#serviceSelector').getByText('确认').click();
await zhbPage
.locator('div')
.filter({ hasText: /^匠星足浴$/ })
.locator('span')
.first()
.click();
await zhbPage.getByRole('button', { name: '完成开单' }).click();
await expect(zhbPage.getByRole('button', { name: '结账' })).toBeVisible();
await zhbPage.getByRole('button', { name: '结账' }).click();
await zhbPage.locator('#page_footBathPay').getByText('结算签字').click();
await expect(async () => {
await zhbPage.locator('#page_footBathPay li').filter({ hasText: '现金' }).click();
await expect(zhbPage.locator('#page_footBathPay li').filter({ hasText: '现金' })).toHaveClass(
/selected/
);
await zhbPage
.locator('#page_footBathPay span')
.filter({ hasText: /结\s算/ })
.click();
await expect(zhbPage.getByText('顾客满意度点评')).toBeVisible({ timeout: 2000 });
}).toPass();
await zhbPage.getByText('不想评价').click();
await expect(zhbPage.locator('#page_footBathPay').getByText('立即返回')).not.toBeVisible();
});
await test.step('起钟下钟,清理房间', async () => {
const $cleanRoom = $$room.filter({
has: zhbPage.locator('.roomName', { hasText: new RegExp(`^${useRoomName}$`) }),
});
await expect($cleanRoom).toContainText('已结清');
await $cleanRoom.click();
await zhbPage.getByText('技师操作').click();
await zhbPage.getByText('起钟', { exact: true }).click();
await zhbPage.getByText('起钟成功!').click();
await zhbPage.getByText('技师操作').click();
await zhbPage.getByText('下钟', { exact: true }).click();
await zhbPage
.locator('div')
.filter({ hasText: /^确认返回$/ })
.locator('div')
.first()
.click();
await zhbPage.getByRole('button', { name: '不需要' }).click();
await expect($cleanRoom).toContainText('打扫');
await $cleanRoom.click();
await zhbPage
.locator('div')
.filter({ hasText: /^确定取消$/ })
.locator('div')
.first()
.click();
await expect($cleanRoom).toContainText('空房');
});
});

25
tests/zhb/fixture/base.js Normal file
View File

@ -0,0 +1,25 @@
const { test: base, expect } = require('@playwright/test');
const authFile = '.auth/zhb_admin.json';
const test = base.extend({
/**
* @type {import('@playwright/test').Page}
*/
zhbPage: async ({ browser, baseURL }, use) => {
const context = await browser.newContext({
storageState: authFile,
});
const page = await context.newPage();
await page.goto(baseURL);
console.log(baseURL);
await use(page);
await page.close();
await context.close();
},
});
module.exports = {
test,
expect,
};

View File

@ -0,0 +1,6 @@
const { mergeTests } = require('@playwright/test');
const { test: customerTest } = require('./customerFixture');
export const test = mergeTests(customerTest);
export { expect } from '@playwright/test';

View File

@ -0,0 +1,12 @@
import { test as base } from './base';
import { CustomerPage } from '../pom/customerPage';
export const test = base.extend({
/**
* @type {CustomerPage}
*/
customerPage: async ({ zhbPage }, use) => {
const customerPage = new CustomerPage(zhbPage);
await use(customerPage);
},
});

View File

@ -0,0 +1,54 @@
const { faker } = require('@faker-js/faker/locale/zh_CN');
const { expect } = require('@playwright/test');
export class Customer {
constructor({
name = faker.person.fullName(),
phone = faker.helpers.fromRegExp(/1[3-9][0-9]{6}/),
} = {}) {
this.name = name;
this.phone = phone;
}
}
export class CustomerPage {
/**
* @param {import('@playwright/test').Page} page
*/
constructor(page) {
this.page = page;
}
/**
* 创建顾客
* @param {Customer} customer
*/
createCustomer = async (customer) => {
console.log(customer.name);
console.log(customer.phone);
await expect(this.page.locator('#page_member').getByText('新增顾客档案')).toBeVisible();
await this.page.locator('#page_member').getByText('新增顾客档案').click();
await this.page.getByPlaceholder('请输入会员姓名').fill(customer.name);
await this.page.getByPlaceholder('请输入手机号码').click();
await expect(this.page.getByText('请输入数字')).toBeVisible();
await this.page.getByPlaceholder('在此输入').click();
await this.page.keyboard.type(customer.phone, { delay: 100 });
// await this.page.getByPlaceholder('在此输入').type(customer.phone, { delay: 100 });
await this.page.locator('#maskBoard').getByText('确认').click();
await expect(this.page.getByPlaceholder('请输入手机号码')).toHaveValue(customer.phone, {
timeout: 2000,
});
await this.page
.locator('div')
.filter({ hasText: /^员工带客$/ })
.locator('span')
.first()
.click();
await this.page.getByText('创建', { exact: true }).click();
await expect(this.page.getByText('用户资料创建成功!')).toBeVisible();
await this.page.getByText('以后再说').click();
await expect(this.page.getByText('以后再说')).not.toBeVisible();
};
}