From 8bda3fc24441f148c22b166d585a4bbe89449385 Mon Sep 17 00:00:00 2001 From: LingandRX Date: Thu, 7 Nov 2024 21:44:08 +0800 Subject: [PATCH] init --- .github/workflows/playwright.yml | 27 ++++ package.json | 2 +- tests/hlk/demo.spec.js | 119 ++++++++--------- tests/{ => hlk}/setup/hlk.setup.js | 2 +- tests/{ => mgj}/setup/mgj.setup.js | 2 +- tests/zhb/demo.spec.js | 202 ----------------------------- tests/{ => zhb}/setup/zhb.setup.js | 22 +--- 7 files changed, 85 insertions(+), 291 deletions(-) create mode 100644 .github/workflows/playwright.yml rename tests/{ => hlk}/setup/hlk.setup.js (92%) rename tests/{ => mgj}/setup/mgj.setup.js (90%) delete mode 100644 tests/zhb/demo.spec.js rename tests/{ => zhb}/setup/zhb.setup.js (50%) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..314778d --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run tests repeat 3 + run: npx playwright test:repeat + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/package.json b/package.json index a809c7a..a8ce400 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "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/", - "zhb_csv-5": "npx playwright test ./tests/zhb/csv-demo.spec.js:13 --repeat-each=5", + "test:repeat": "npx playwright test ./tests/zhb ./tests/mgj ./tests/hlk --repeat-each=3", "ui": "npx playwright test --ui", "pwi": "npm ci && npx playwright install", "pwu": "npx playwright install --with-deps" diff --git a/tests/hlk/demo.spec.js b/tests/hlk/demo.spec.js index 4460366..d0e178d 100644 --- a/tests/hlk/demo.spec.js +++ b/tests/hlk/demo.spec.js @@ -2,70 +2,59 @@ const { test, expect } = require('./fixtures/common'); const { Customer } = require('./pom/customer'); const { faker } = require('@faker-js/faker/locale/zh_CN'); -for (let i = 0; i < 3; 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 }); +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); - 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.waitForTimeout(2000); - await h5Page.reload(); - 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').nth(2).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(); - }); + await touchCustomerPage.createCustomer(customer); + /** @type string*/ + 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(); + const element = h5Page.locator('.coupon_content'); + const boundingBox = await element.boundingBox(); + + if (boundingBox) { + const x = boundingBox.x + boundingBox.width / 2; // 元素水平中心 + const y = boundingBox.y - 10; // 元素上侧空白区域 + + await h5Page.mouse.click(x, y); + } + 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').nth(2).click(); + await h5Page.getByText('立即购买').click(); + await h5Page.locator('uni-text').filter({ hasText: '佘山二店' }).click(); + await h5Page.getByText('确认').click(); + await h5Page.getByText('哎哟代理卡').click(); + await expect(h5Page.getByText('确认支付')).toBeEnabled(); + await h5Page.getByText('确认支付').click(); + await expect(h5Page.getByText('支付成功').first()).toBeVisible(); + await expect(h5Page.getByText('查看订单')).toBeVisible(); + }); +}); diff --git a/tests/setup/hlk.setup.js b/tests/hlk/setup/hlk.setup.js similarity index 92% rename from tests/setup/hlk.setup.js rename to tests/hlk/setup/hlk.setup.js index 648db33..8a8de7c 100644 --- a/tests/setup/hlk.setup.js +++ b/tests/hlk/setup/hlk.setup.js @@ -1,6 +1,6 @@ const { test: setup, expect } = require('@playwright/test'); const path = require('path'); -const hlkAuthFile = path.join(__dirname, '../../.auth/hlk_admin.json'); +const hlkAuthFile = path.join(process.cwd(), '.auth', 'hlk_admin.json'); setup('hlk总部管理员登录', async ({ page, baseURL }) => { const $account = page.getByRole('textbox', { name: '请输入您的手机号码' }); diff --git a/tests/setup/mgj.setup.js b/tests/mgj/setup/mgj.setup.js similarity index 90% rename from tests/setup/mgj.setup.js rename to tests/mgj/setup/mgj.setup.js index 26d860e..36c89d9 100644 --- a/tests/setup/mgj.setup.js +++ b/tests/mgj/setup/mgj.setup.js @@ -1,6 +1,6 @@ const { test: setup, expect } = require('@playwright/test'); const path = require('path'); -const mgjAuthFile = path.join(__dirname, '../../.auth/mgj_admin.json'); +const mgjAuthFile = path.join(process.cwd(), '.auth', 'mgj_admin.json'); setup('mgj管理员登录', async ({ page, baseURL }) => { const account = process.env.MGJ_ACCOUNT; diff --git a/tests/zhb/demo.spec.js b/tests/zhb/demo.spec.js deleted file mode 100644 index 4ce59b8..0000000 --- a/tests/zhb/demo.spec.js +++ /dev/null @@ -1,202 +0,0 @@ -const { faker } = require('@faker-js/faker'); -const { test, expect } = require('./fixture/common'); -const { Customer } = require('./pom/customerPage'); - -test(`demo`, async ({ zhbPage, customerPage }, workerInfo) => { - const $area = zhbPage - .locator('.area') - .filter({ has: zhbPage.locator('.area-name', { hasText: '二楼' }) }); - const $$room = $area.locator('.room-list .room'); - - const customer = new Customer(); - await test.step('创建顾客', async () => { - await zhbPage.locator('#tab_main li').filter({ hasText: '顾客' }).click(); - 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('空房') }) - .nth(workerInfo.workerIndex % 3); - useRoomName = await $emptyRoom.locator('.roomName').innerText(); - expect(useRoomName).not.toBeNull(); - await $emptyRoom.click(); - await expect(async () => { - if (await zhbPage.locator('.close > .iconfont').first().isVisible()) { - await zhbPage.locator('.close > .iconfont').first().click(); - } - await zhbPage.getByRole('button', { name: '选择顾客' }).click({ timeout: 3000 }); - 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 - .locator('.goods-content-item') - .nth(faker.number.int({ min: 0, max: 14 })) - .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({ timeout: 30_000 }); - 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 zhbPage.locator('#page_footBathPay').getByText('立即返回').click(); - }); - - 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('空房'); - }); -}); - -test.skip('h5 demo', async ({ - zhbAdminPage, - // zhbPage, - h5Page, - h5LoginPage, - // customerPage -}) => { - // const customer = new Customer(); - // await test.step('创建顾客', async () => { - // await zhbPage.locator('#tab_main li').filter({ hasText: '顾客' }).click(); - // await customerPage.createCustomer(customer); - // }); - - // await test.step('搜索顾客进行开卡', async () => { - // await zhbPage.getByRole('textbox', { name: '输入会员手机号或姓名搜索' }).fill(customer.phone); - // await zhbPage.locator('#page_member svg').click(); - // await zhbPage - // .locator('.searchresultBox tbody tr') - // .first() - // .locator('#page_member') - // .getByText('详情') - // .click(); - // await expect(zhbPage.getByText('会员卡 点击卡片即可用卡进行结算哦~')).toBeVisible(); - // await zhbPage - // .locator('p') - // .filter({ hasText: '会员卡 点击卡片即可用卡进行结算哦~' }) - // .locator('span') - // .nth(2) - // .click(); - // await zhbPage.locator('#page_memberCard').getByText('5000元储值卡', { exact: true }).click(); - - // await expect(zhbPage.locator('#page_memberCard .cashierBox tbody tr').first()).toBeVisible(); - // await zhbPage - // .locator('#page_memberCard') - // .getByText(/结\s算/) - // .click(); - - // await expect(async () => { - // const $signature = zhbPage.locator('#page_pay').getByText('结算签字'); - // await $signature.click(); - // await expect($signature).not.toHaveClass(/checked/, { timeout: 2000 }); - // }).toPass(); - - // await expect(async () => { - // const $cashBtn = zhbPage.locator('#page_pay .pay_cash'); - // await $cashBtn.click(); - // await expect(zhbPage.locator('#page_pay .pay_cash.selected')).toBeVisible(); - // await zhbPage - // .locator('#page_pay span') - // .filter({ hasText: /结\s算/ }) - // .click(); - // await expect(zhbPage.locator('#payConfirm').getByText('立即返回')).not.toBeVisible({ - // timeout: 2000, - // }); - // }).toPass(); - // }); - - const $smsCodeInput = h5Page.getByRole('textbox', { name: '验证码', exact: true }); - await test.step('获取手机验证码', async () => { - const graphCode = await h5LoginPage.sendSmsCode('17770898274'); - await h5Page.getByRole('textbox', { name: '请输入图形验证码' }).fill(graphCode); - await h5Page.locator('#setup_loginForm').getByText('获取验证码').nth(1).first().tap(); - await expect(h5Page.getByText('重新发送')).toBeVisible(); - }); - - let smsCode; - await test.step('获取短信验证码', async () => { - await zhbAdminPage - .locator('a') - .filter({ hasText: /^管理$/ }) - .click(); - await zhbAdminPage.locator('.menu-item').getByText('短信发送记录', { exact: true }).click(); - const $smsCodeTr = zhbAdminPage - .locator('#routerView iframe') - .contentFrame() - .locator('.table-responsive tbody tr') - .filter({ hasText: '17770898274' }) - .first() - .locator('td') - .nth(2); - await expect($smsCodeTr).toBeVisible(); - const text = await $smsCodeTr.innerText(); - smsCode = text.match(/验证码是(\d{4})/)[1]; - }); - expect(smsCode).not.toBeUndefined(); - - await test.step('使用短信验证码登录h5', async () => { - await h5Page.getByRole('textbox', { name: '验证码', exact: true }).fill(smsCode); - await h5Page.locator('#setup_loginForm span').first().tap(); - await h5Page.getByText('绑定', { exact: true }).tap(); - await h5Page.waitForTimeout(5000); - }); -}); diff --git a/tests/setup/zhb.setup.js b/tests/zhb/setup/zhb.setup.js similarity index 50% rename from tests/setup/zhb.setup.js rename to tests/zhb/setup/zhb.setup.js index 82deda5..30d11c0 100644 --- a/tests/setup/zhb.setup.js +++ b/tests/zhb/setup/zhb.setup.js @@ -1,7 +1,6 @@ const { test: setup, expect } = require('@playwright/test'); const path = require('path'); -const zhbAuthFile = '.auth/zhb.json'; -const zhbAdminAuthFile = '.auth/zhb_admin.json'; +const zhbAuthFile = path.join(process.cwd(), '.auth', 'zhb.json'); setup('zhb总部管理员登录', async ({ page, baseURL }) => { const account = process.env.ZHB_ACCOUNT; @@ -19,22 +18,3 @@ setup('zhb总部管理员登录', async ({ page, baseURL }) => { await expect(page.locator('#tab_main li').filter({ hasText: '营业' })).toBeVisible(); await page.context().storageState({ path: zhbAuthFile }); }); - -// setup('zhb管理页面登录', async ({ page }) => { -// const account = process.env.ZHB_ACCOUNT; -// const password = process.env.ZHB_PASSWORD; -// const baseURL = process.env.ZHB_ADMIN_URL; - -// await page.goto(baseURL); - -// await page.getByPlaceholder('账户').fill(account); -// await page.getByPlaceholder('密码').fill(password); -// await page.getByRole('button', { name: '登 录' }).click(); -// await expect(page.locator('a').filter({ hasText: /^收银$/ })).toBeVisible(); -// await page -// .locator('a') -// .filter({ hasText: /^顾客$/ }) -// .click(); -// await expect(page.getByText('顾客概要分析')).toBeVisible(); -// await page.context().storageState({ path: zhbAdminAuthFile }); -// });