Commit 3de2b78454d612cd5e47ed22e598a6916cc8f439

Authored by 赵旭婷
2 parents 486d0cf6 4675d8dc

Merge branch 'xfbhzxt'

.gitignore
... ... @@ -11,4 +11,7 @@ node_modules/
11 11 /allure-results/
12 12 /plans/
13 13 /reports/
14   -/screenshots/
15 14 \ No newline at end of file
  15 +/screenshots/
  16 +/CLAUDE.md
  17 +/docs/
  18 +/.worktrees/
16 19 \ No newline at end of file
... ...
fixtures/testFixture.ts
1 1 import { test as base, Page } from '@playwright/test';
2 2 import { LoginPage } from '../pages/loginPage';
3 3 import { ProductPage } from '../pages/productPage';
  4 +import { ProductCategoryPage } from '../pages/productCategoryPage';
4 5 import { SalePage } from '../pages/salePage';
5 6 import { ConsignmentPage } from '../pages/consignmentPage';
6 7 import { CustomerPage } from '../pages/customerPage';
  8 +import { PurchasePage } from '../pages/purchasePage';
7 9  
8 10 /**
9 11 * 页面对象夹具类型定义
... ... @@ -20,6 +22,11 @@ export type PageFixtures = {
20 22 productPage: ProductPage;
21 23  
22 24 /**
  25 + * 商品分类页面
  26 + */
  27 + // productCategoryPage: ProductCategoryPage;
  28 +
  29 + /**
23 30 * 销售开单页面
24 31 */
25 32 salePage: SalePage;
... ... @@ -33,6 +40,11 @@ export type PageFixtures = {
33 40 * 客户管理页面
34 41 */
35 42 customerPage: CustomerPage;
  43 +
  44 + /**
  45 + * 采购入库页面
  46 + */
  47 + purchasePage: PurchasePage;
36 48 };
37 49  
38 50 /**
... ... @@ -49,6 +61,11 @@ export const test = base.extend<PageFixtures>({
49 61 await use(new ProductPage(page));
50 62 },
51 63  
  64 + // // 商品分类页面
  65 + // productCategoryPage: async ({ page }, use) => {
  66 + // await use(new ProductCategoryPage(page));
  67 + // },
  68 +
52 69 // 销售开单页面
53 70 salePage: async ({ page }, use) => {
54 71 await use(new SalePage(page));
... ... @@ -63,6 +80,11 @@ export const test = base.extend<PageFixtures>({
63 80 customerPage: async ({ page }, use) => {
64 81 await use(new CustomerPage(page));
65 82 },
  83 +
  84 + // 采购入库页面
  85 + purchasePage: async ({ page }, use) => {
  86 + await use(new PurchasePage(page));
  87 + },
66 88 });
67 89  
68 90 /**
... ...
pages/accountPage.ts 0 → 100644
  1 +import { Page, Locator, expect } from '@playwright/test';
  2 +import { BasePage } from './basePage';
  3 +
  4 +/**
  5 + * 账目管理页面选择器
  6 + */
  7 +const selectors = {
  8 + // 导航
  9 + moreMenu: 'text=更多 >',
  10 + accountMenu: 'text=账目管理',
  11 +
  12 + // 操作按钮
  13 + addButton: 'text=新增',
  14 + saveButton: 'text=保存',
  15 +
  16 + // 表单字段
  17 + accountNameInput: 'uni-input:has-text("科目名称") input',
  18 + remarkTextarea: 'uni-scroll-view textarea',
  19 +
  20 + // 收支类型单选
  21 + incomeRadio: '.nut-radio-group > uni-view:nth-child(1) > .nut-icon',
  22 + expenseRadio: '.nut-radio-group > uni-view:nth-child(2) > .nut-icon',
  23 +};
  24 +
  25 +/**
  26 + * 账目管理页面类
  27 + * 处理账目管理相关操作
  28 + */
  29 +export class AccountPage extends BasePage {
  30 + // 导航定位器
  31 + readonly moreMenu: Locator;
  32 + readonly accountMenu: Locator;
  33 +
  34 + // 操作按钮
  35 + readonly addButton: Locator;
  36 + readonly saveButton: Locator;
  37 + readonly editButton: Locator;
  38 +
  39 + // 表单字段
  40 + readonly accountNameInput: Locator;
  41 + readonly remarkTextarea: Locator;
  42 +
  43 + // 收支类型
  44 + readonly incomeRadio: Locator;
  45 + readonly expenseRadio: Locator;
  46 +
  47 + constructor(page: Page) {
  48 + super(page);
  49 +
  50 + this.moreMenu = page.getByText('更多 >');
  51 + this.accountMenu = page.getByText('账目管理').first();
  52 + this.addButton = page.getByText('新增', { exact: true });
  53 + this.saveButton = page.locator('uni-button.execute-btn').filter({ hasText: '保存' });
  54 + this.editButton = page.locator('uni-button').filter({ hasText: '修改' });
  55 + this.accountNameInput = page.locator('uni-input').filter({ hasText: '科目名称' }).getByRole('textbox');
  56 + this.remarkTextarea = page.locator('uni-scroll-view textarea');
  57 + this.incomeRadio = page.locator('.nut-radio-group > uni-view:nth-child(1) > .nut-icon');
  58 + this.expenseRadio = page.locator('.nut-radio-group > uni-view:nth-child(2) > .nut-icon');
  59 + }
  60 +
  61 + /**
  62 + * 进入账目管理页面
  63 + */
  64 + async openAccountManagement(): Promise<void> {
  65 + await this.navigate('/');
  66 + await this.moreMenu.click();
  67 + await this.page.waitForTimeout(500);
  68 + await this.accountMenu.click();
  69 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  70 + }
  71 +
  72 + /**
  73 + * 点击新增按钮
  74 + */
  75 + async clickAddButton(): Promise<void> {
  76 + await this.addButton.click();
  77 + await this.page.waitForTimeout(500);
  78 + }
  79 +
  80 + /**
  81 + * 填写账目名称
  82 + * @param name 账目名称
  83 + */
  84 + async fillAccountName(name: string): Promise<void> {
  85 + await this.accountNameInput.click();
  86 + await this.accountNameInput.fill(name);
  87 + }
  88 +
  89 + /**
  90 + * 选择收支类型
  91 + * @param type '收入' 或 '支出'
  92 + */
  93 + async selectIncomeType(type: '收入' | '支出'): Promise<void> {
  94 + if (type === '支出') {
  95 + await this.expenseRadio.click();
  96 + }
  97 + // 收入是默认选项,无需选择
  98 + }
  99 +
  100 + /**
  101 + * 填写备注
  102 + * @param remark 备注内容
  103 + */
  104 + async fillRemark(remark: string): Promise<void> {
  105 + await this.remarkTextarea.click();
  106 + await this.remarkTextarea.fill(remark);
  107 + }
  108 +
  109 + /**
  110 + * 保存账目
  111 + */
  112 + async saveAccount(): Promise<void> {
  113 + await this.saveButton.click();
  114 + await this.page.waitForTimeout(1000);
  115 + }
  116 +
  117 + /**
  118 + * 搜索账目
  119 + * @param name 账目名称
  120 + */
  121 + async searchAccount(name: string): Promise<void> {
  122 + await this.page.locator('uni-input').filter({ hasText: '账目名称' }).getByRole('textbox').click();
  123 + await this.page.locator('uni-input').filter({ hasText: '账目名称' }).getByRole('textbox').fill(name);
  124 + await this.page.waitForTimeout(1000);
  125 + }
  126 +
  127 + /**
  128 + * 验证账目是否存在
  129 + * @param name 账目名称
  130 + * @returns 是否存在
  131 + */
  132 + async verifyAccountExists(name: string): Promise<boolean> {
  133 + const isVisible = await this.page.getByText(name, { exact: true }).isVisible().catch(() => false);
  134 + return isVisible;
  135 + }
  136 +
  137 + /**
  138 + * 删除账目
  139 + * @param name 账目名称
  140 + */
  141 + async deleteAccount(name: string): Promise<void> {
  142 + // 搜索账目
  143 + await this.searchAccount(name);
  144 + await this.page.waitForTimeout(500);
  145 + // 点击删除按钮
  146 + await this.page.getByText('删除').click();
  147 + await this.page.waitForTimeout(500);
  148 + // 确认删除
  149 + await this.page.getByText('确定').click();
  150 + await this.page.waitForTimeout(1000);
  151 + }
  152 +
  153 + /**
  154 + * 验证账目删除成功
  155 + * @param name 账目名称
  156 + * @returns 是否删除成功
  157 + */
  158 + async verifyAccountDeleted(name: string): Promise<boolean> {
  159 + await this.searchAccount(name);
  160 + await this.page.waitForTimeout(500);
  161 + const isVisible = await this.page.getByText(name, { exact: true }).isVisible().catch(() => false);
  162 + return !isVisible;
  163 + }
  164 +
  165 + /**
  166 + * 点击修改按钮
  167 + */
  168 + async clickEditButton(): Promise<void> {
  169 + await this.editButton.click();
  170 + await this.page.waitForTimeout(500);
  171 + }
  172 +
  173 + /**
  174 + * 修改账目完整流程
  175 + * @param originalName 原账目名称
  176 + * @param newName 新账目名称
  177 + * @param incomeType 收支类型(收入/支出)
  178 + * @param remark 备注
  179 + */
  180 + async updateAccount(originalName: string, newName: string, incomeType?: '收入' | '支出', remark?: string): Promise<void> {
  181 + // 搜索原账目
  182 + await this.searchAccount(originalName);
  183 + await this.page.waitForTimeout(500);
  184 + // 点击修改按钮
  185 + await this.clickEditButton();
  186 + // 修改账目名称
  187 + await this.accountNameInput.click();
  188 + await this.accountNameInput.fill(newName);
  189 + // 修改收支类型
  190 + if (incomeType === '支出') {
  191 + await this.expenseRadio.click();
  192 + }
  193 + // 修改备注
  194 + if (remark) {
  195 + await this.remarkTextarea.click();
  196 + await this.remarkTextarea.fill(remark);
  197 + }
  198 + // 保存
  199 + await this.saveAccount();
  200 + }
  201 +
  202 + /**
  203 + * 清除搜索框
  204 + */
  205 + async clearSearchBox(): Promise<void> {
  206 + await this.page.locator('.nut-icon.nutui-iconfont.nut-icon-circle-close').click();
  207 + await this.page.waitForTimeout(500);
  208 + }
  209 +
  210 + /**
  211 + * 新增账目完整流程
  212 + * @param accountName 账目名称
  213 + * @param incomeType 收支类型(默认'收入')
  214 + * @param remark 备注
  215 + */
  216 + async createAccount(accountName: string, incomeType: '收入' | '支出' = '收入', remark?: string): Promise<void> {
  217 + await this.openAccountManagement();
  218 + await this.clickAddButton();
  219 + await this.fillAccountName(accountName);
  220 + await this.selectIncomeType(incomeType);
  221 + if (remark) {
  222 + await this.fillRemark(remark);
  223 + }
  224 + await this.saveAccount();
  225 + }
  226 +
  227 + /**
  228 + * 验证账目创建成功
  229 + * @param accountName 账目名称
  230 + * @returns 是否创建成功
  231 + */
  232 + async verifyAccountCreated(accountName: string): Promise<boolean> {
  233 + await this.searchAccount(accountName);
  234 + return await this.verifyAccountExists(accountName);
  235 + }
  236 +}
... ...
pages/customerGroupPage.ts 0 → 100644
  1 +import { Page, Locator, expect } from '@playwright/test';
  2 +import { BasePage } from './basePage';
  3 +
  4 +/**
  5 + * 客户分组页面选择器
  6 + */
  7 +const selectors = {
  8 + // 导航
  9 + moreMenu: 'text=更多 >',
  10 + customerGroupMenu: 'text=客户分组',
  11 +
  12 + // 操作按钮
  13 + newButton: 'text=新建',
  14 + confirmButton: 'text=确定',
  15 + saveButton: 'text=保存',
  16 + deleteButton: 'text=删除',
  17 + editButton: 'text=编辑',
  18 +
  19 + // 表单字段
  20 + groupNameInput: 'text=分组名称*',
  21 + sortInput: 'textbox',
  22 +
  23 + // 搜索
  24 + searchInput: 'textbox',
  25 + clearSearchButton: '.nut-searchbar__search-icon',
  26 +};
  27 +
  28 +/**
  29 + * 客户分组页面类
  30 + * 处理客户分组相关操作
  31 + */
  32 +export class CustomerGroupPage extends BasePage {
  33 + // 导航定位器
  34 + readonly moreMenu: Locator;
  35 + readonly customerGroupMenu: Locator;
  36 +
  37 + // 操作按钮
  38 + readonly newButton: Locator;
  39 + readonly confirmButton: Locator;
  40 + readonly saveButton: Locator;
  41 + readonly deleteButton: Locator;
  42 + readonly editButton: Locator;
  43 +
  44 + // 表单字段
  45 + readonly groupNameInput: Locator;
  46 + readonly sortInput: Locator;
  47 +
  48 + // 搜索
  49 + readonly searchInput: Locator;
  50 + readonly clearSearchButton: Locator;
  51 +
  52 + constructor(page: Page) {
  53 + super(page);
  54 +
  55 + this.moreMenu = page.getByText('更多 >');
  56 + this.customerGroupMenu = page.getByText('客户分组').first();
  57 + this.newButton = page.getByText('新建');
  58 + this.confirmButton = page.getByText('确定');
  59 + this.saveButton = page.getByText('保存');
  60 + this.deleteButton = page.getByText('删除');
  61 + this.editButton = page.getByText('编辑');
  62 + this.groupNameInput = page.locator('uni-view').filter({ hasText: /^分组名称\*$/ }).first();
  63 + this.sortInput = page.getByRole('textbox').nth(2);
  64 + this.searchInput = page.getByRole('textbox');
  65 + this.clearSearchButton = page.locator('.nut-searchbar__search-icon');
  66 + }
  67 +
  68 + /**
  69 + * 进入客户分组页面
  70 + */
  71 + async openCustomerGroupManagement(): Promise<void> {
  72 + await this.navigate('/');
  73 + await this.moreMenu.click();
  74 + await this.page.waitForTimeout(500);
  75 + await this.customerGroupMenu.click();
  76 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  77 + }
  78 +
  79 + /**
  80 + * 点击新建按钮
  81 + */
  82 + async clickNewButton(): Promise<void> {
  83 + await this.newButton.click();
  84 + await this.page.waitForTimeout(500);
  85 + }
  86 +
  87 + /**
  88 + * 填写分组名称
  89 + * @param name 分组名称
  90 + */
  91 + async fillGroupName(name: string): Promise<void> {
  92 + await this.page.getByRole('textbox').nth(1).click();
  93 + await this.page.getByRole('textbox').nth(1).fill(name);
  94 + }
  95 +
  96 + /**
  97 + * 填写排序号
  98 + * @param sort 排序号
  99 + */
  100 + async fillSortNumber(sort: string): Promise<void> {
  101 + await this.page.getByRole('textbox').nth(2).click();
  102 + await this.page.getByRole('textbox').nth(2).fill(sort);
  103 + }
  104 +
  105 + /**
  106 + * 点击确定按钮
  107 + */
  108 + async clickConfirm(): Promise<void> {
  109 + await this.confirmButton.click();
  110 + await this.page.waitForTimeout(1000);
  111 + }
  112 +
  113 + /**
  114 + * 搜索分组
  115 + * @param name 分组名称
  116 + */
  117 + async searchGroup(name: string): Promise<void> {
  118 + await this.searchInput.click();
  119 + await this.searchInput.fill(name);
  120 + await this.page.waitForTimeout(500);
  121 + }
  122 +
  123 + /**
  124 + * 点击清除搜索按钮
  125 + */
  126 + async clearSearch(): Promise<void> {
  127 + await this.clearSearchButton.click();
  128 + await this.page.waitForTimeout(2000);
  129 + }
  130 +
  131 + /**
  132 + * 点击编辑按钮
  133 + */
  134 + async clickEdit(): Promise<void> {
  135 + await this.editButton.click();
  136 + await this.page.waitForTimeout(500);
  137 + }
  138 +
  139 + /**
  140 + * 点击删除按钮
  141 + */
  142 + async clickDelete(): Promise<void> {
  143 + await this.deleteButton.click();
  144 + await this.page.waitForTimeout(500);
  145 + }
  146 +
  147 + /**
  148 + * 验证分组是否存在(验证前先搜索)
  149 + * @param name 分组名称
  150 + * @returns 是否存在
  151 + */
  152 + async verifyGroupExists(name: string): Promise<boolean> {
  153 + await this.searchGroup(name);
  154 + await this.page.waitForTimeout(500);
  155 + const count = await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${name}$`) }).count();
  156 + return count > 0;
  157 + }
  158 +
  159 + /**
  160 + * 验证分组是否不存在
  161 + * @param name 分组名称
  162 + * @returns 是否不存在
  163 + */
  164 + async verifyGroupNotExists(name: string): Promise<boolean> {
  165 + const count = await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${name}$`) }).count();
  166 + return count === 0;
  167 + }
  168 +
  169 + /**
  170 + * 新增客户分组完整流程
  171 + * @param groupName 分组名称
  172 + * @param sortNumber 排序号(可选)
  173 + */
  174 + async createGroup(groupName: string, sortNumber?: string): Promise<void> {
  175 + await this.openCustomerGroupManagement();
  176 + await this.clickNewButton();
  177 + await this.fillGroupName(groupName);
  178 + if (sortNumber) {
  179 + await this.fillSortNumber(sortNumber);
  180 + }
  181 + await this.clickConfirm();
  182 + }
  183 +
  184 + /**
  185 + * 修改客户分组完整流程
  186 + * @param originalName 原分组名称
  187 + * @param newName 新分组名称
  188 + * @param newSortNumber 新排序号(可选)
  189 + */
  190 + async updateGroup(originalName: string, newName: string, newSortNumber?: string): Promise<void> {
  191 + await this.searchGroup(originalName);
  192 + await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${originalName}$`) }).first().click();
  193 + await this.page.waitForTimeout(500);
  194 + await this.clickEdit();
  195 + // 清空分组名称并填写新名称
  196 + await this.page.getByRole('textbox').nth(1).click();
  197 + await this.page.locator('.nut-input__clear-icon').first().click();
  198 + await this.page.getByRole('textbox').nth(1).click();
  199 + await this.page.getByRole('textbox').nth(1).fill(newName);
  200 + if (newSortNumber) {
  201 + await this.page.getByRole('textbox').nth(2).click();
  202 + await this.page.getByRole('textbox').nth(2).fill(newSortNumber);
  203 + }
  204 + await this.clickConfirm();
  205 + }
  206 +
  207 + /**
  208 + * 删除客户分组完整流程
  209 + * @param groupName 分组名称
  210 + */
  211 + async deleteGroup(groupName: string): Promise<void> {
  212 + await this.searchGroup(groupName);
  213 + await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${groupName}$`) }).first().click();
  214 + await this.page.waitForTimeout(500);
  215 + await this.clickDelete();
  216 + await this.page.getByText('确定', { exact: true }).click();
  217 + await this.page.waitForTimeout(1000);
  218 + await this.clearSearch();
  219 + }
  220 +}
... ...
pages/customerPage.ts
... ... @@ -182,17 +182,20 @@ export class CustomerPage extends BasePage {
182 182 }
183 183  
184 184 /**
185   - * 选择客户分组(选择"普通客户"
  185 + * 选择客户分组(随机选择
186 186 */
187 187 async selectCustomerGroup(): Promise<void> {
188 188 // 点击客户分组选择器
189 189 await this.customerGroupPicker.click();
190   -
  190 +
191 191 // 等待弹窗出现
192 192 await this.page.waitForTimeout(500);
193 193  
194   - // 选择"普通客户"(使用 nth(4) 和 force: true 绕过遮罩层)
195   - await this.page.locator('uni-view').filter({ hasText: /^普通客户$/ }).nth(4).click({ force: true });
  194 + // 随机选择一个客户分组
  195 + const items = this.page.locator('.zp-paging-container-content .default-item');
  196 + const count = await items.count();
  197 + const randomIndex = Math.floor(Math.random() * count);
  198 + await items.nth(randomIndex).click({ force: true });
196 199 }
197 200  
198 201 /**
... ...
pages/employeePage.ts 0 → 100644
  1 +import { Page, Locator, expect } from '@playwright/test';
  2 +import { BasePage } from './basePage';
  3 +
  4 +/**
  5 + * 员工管理页面类
  6 + * 处理员工管理相关操作
  7 + */
  8 +export class EmployeePage extends BasePage {
  9 + // 导航定位器
  10 + readonly employeeMenu: Locator;
  11 +
  12 + // 操作按钮
  13 + readonly newEmployeeButton: Locator;
  14 + readonly manualAddButton: Locator;
  15 + readonly confirmButton: Locator;
  16 +
  17 + // 表单字段
  18 + readonly employeeNameInput: Locator;
  19 + readonly phoneInput: Locator;
  20 + readonly remarkInput: Locator;
  21 + readonly positionInput: Locator;
  22 +
  23 + // 岗位列表
  24 + readonly positionOption: Locator;
  25 +
  26 + constructor(page: Page) {
  27 + super(page);
  28 +
  29 + this.employeeMenu = page.getByText('员工管理').first();
  30 + this.newEmployeeButton = page.getByText('新建员工');
  31 + this.manualAddButton = page.getByText('手动添加');
  32 + this.confirmButton = page.getByText('确定');
  33 + this.employeeNameInput = page.locator('uni-input').filter({ hasText: '请输入员工姓名' }).getByRole('textbox');
  34 + this.phoneInput = page.getByRole('spinbutton');
  35 + this.remarkInput = page.locator('uni-input').filter({ hasText: '请输入备注信息' }).getByRole('textbox');
  36 + this.positionInput = page.locator('.nut-cell__link').first();
  37 + this.positionOption = page.locator('uni-view').filter({ hasText: /^岗位名称$/ }).first();
  38 + }
  39 +
  40 + /**
  41 + * 进入员工管理页面
  42 + */
  43 + async openEmployeeManagement(): Promise<void> {
  44 + await this.navigate('/');
  45 + await this.employeeMenu.click();
  46 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  47 + }
  48 +
  49 + /**
  50 + * 点击新建员工按钮
  51 + */
  52 + async clickNewEmployee(): Promise<void> {
  53 + await this.newEmployeeButton.click();
  54 + await this.page.waitForTimeout(500);
  55 + }
  56 +
  57 + /**
  58 + * 点击手动添加
  59 + */
  60 + async clickManualAdd(): Promise<void> {
  61 + await this.manualAddButton.click();
  62 + await this.page.waitForTimeout(500);
  63 + }
  64 +
  65 + /**
  66 + * 填写员工姓名
  67 + * @param name 员工姓名
  68 + */
  69 + async fillEmployeeName(name: string): Promise<void> {
  70 + await this.employeeNameInput.click();
  71 + await this.employeeNameInput.fill(name);
  72 + }
  73 +
  74 + /**
  75 + * 填写联系电话
  76 + * @param phone 联系电话
  77 + */
  78 + async fillPhone(phone: string): Promise<void> {
  79 + await this.phoneInput.click();
  80 + await this.phoneInput.fill(phone);
  81 + }
  82 +
  83 + /**
  84 + * 填写备注
  85 + * @param remark 备注信息
  86 + */
  87 + async fillRemark(remark: string): Promise<void> {
  88 + await this.remarkInput.click();
  89 + await this.remarkInput.fill(remark);
  90 + }
  91 +
  92 + /**
  93 + * 选择岗位
  94 + * @param position 岗位名称(可选,不传则随机选择)
  95 + */
  96 + async selectPosition(position?: string): Promise<void> {
  97 + // 点击岗位名称输入框打开下拉列表
  98 + await this.page.locator('uni-view:nth-child(6) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask').click();
  99 + await this.page.waitForTimeout(500);
  100 +
  101 + if (position) {
  102 + // 选择指定岗位
  103 + await this.page.locator('uni-view').filter({ hasText: position }).first().click();
  104 + } else {
  105 + // 随机选择岗位 - 获取所有岗位选项并随机选择一个
  106 + const options = this.page.locator('uni-view').filter({ hasText: /^销售员$|^仓库管理员$/ });
  107 + const count = await options.count();
  108 + if (count > 0) {
  109 + const randomIndex = Math.floor(Math.random() * count);
  110 + await options.nth(randomIndex).click();
  111 + }
  112 + }
  113 + await this.page.waitForTimeout(300);
  114 + }
  115 +
  116 + /**
  117 + * 点击确定按钮
  118 + */
  119 + async clickConfirm(): Promise<void> {
  120 + await this.confirmButton.click();
  121 + await this.page.waitForTimeout(1000);
  122 + }
  123 +
  124 + /**
  125 + * 搜索员工
  126 + * @param name 员工姓名
  127 + */
  128 + async searchEmployee(name: string): Promise<void> {
  129 + await this.page.getByRole('textbox').click();
  130 + await this.page.getByRole('textbox').fill(name);
  131 + await this.page.waitForTimeout(1000);
  132 + }
  133 +
  134 + /**
  135 + * 验证员工是否存在
  136 + * @param name 员工姓名
  137 + * @returns 是否存在
  138 + */
  139 + async verifyEmployeeExists(name: string): Promise<boolean> {
  140 + const isVisible = await this.page.getByText(name).first().isVisible().catch(() => false);
  141 + return isVisible;
  142 + }
  143 +
  144 + /**
  145 + * 新增员工完整流程
  146 + * @param name 员工姓名
  147 + * @param phone 联系电话
  148 + * @param remark 备注
  149 + * @param position 岗位(可选)
  150 + */
  151 + async createEmployee(name: string, phone: string, remark: string, position?: string): Promise<void> {
  152 + await this.openEmployeeManagement();
  153 + await this.clickNewEmployee();
  154 + await this.clickManualAdd();
  155 + await this.fillEmployeeName(name);
  156 + await this.fillPhone(phone);
  157 + await this.fillRemark(remark);
  158 + await this.selectPosition(position);
  159 + await this.clickConfirm();
  160 + }
  161 +
  162 + /**
  163 + * 点击修改按钮
  164 + */
  165 + async clickEditButton(): Promise<void> {
  166 + await this.page.locator('uni-button.execute-plain-btn').getByText('修改').click();
  167 + await this.page.waitForTimeout(500);
  168 + }
  169 +
  170 + /**
  171 + * 填写备注(修改页面)
  172 + * @param remark 备注信息
  173 + */
  174 + async fillRemarkForEdit(remark: string): Promise<void> {
  175 + await this.page.locator('uni-input').filter({ hasText: '请输入备注信息' }).getByRole('textbox').click();
  176 + await this.page.locator('uni-input').filter({ hasText: '请输入备注信息' }).getByRole('textbox').fill(remark);
  177 + }
  178 +
  179 + /**
  180 + * 点击状态开关
  181 + */
  182 + async clickStatusSwitch(): Promise<void> {
  183 + await this.page.locator('uni-view:nth-child(5) > .nut-cell__value > .nut-form-item__body__slots > .nut-switch > .nut-switch-button').click();
  184 + await this.page.waitForTimeout(300);
  185 + }
  186 +
  187 + /**
  188 + * 选择岗位(修改页面)
  189 + * @param position 岗位名称
  190 + */
  191 + async selectPositionForEdit(position: string): Promise<void> {
  192 + // 点击岗位名称输入框打开下拉列表(使用与新增相同的selector)
  193 + await this.page.locator('uni-view:nth-child(6) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask').click();
  194 + await this.page.waitForTimeout(500);
  195 + // 选择岗位
  196 + await this.page.locator('uni-view').filter({ hasText: position }).first().click();
  197 + await this.page.waitForTimeout(300);
  198 + }
  199 +
  200 + /**
  201 + * 验证员工备注是否包含指定内容
  202 + * @param expectedRemark 期望的备注内容
  203 + * @returns 是否包含
  204 + */
  205 + async verifyEmployeeRemarkContains(expectedRemark: string): Promise<boolean> {
  206 + const remarkElement = this.page.getByText(expectedRemark).first();
  207 + const isVisible = await remarkElement.isVisible({ timeout: 3000 }).catch(() => false);
  208 + return isVisible;
  209 + }
  210 +
  211 + /**
  212 + * 点击解绑按钮
  213 + */
  214 + async clickUnbindButton(): Promise<void> {
  215 + await this.page.locator('uni-button.warning-plain-btn').getByText('解绑').click();
  216 + await this.page.waitForTimeout(500);
  217 + }
  218 +
  219 + /**
  220 + * 确认解绑
  221 + */
  222 + async confirmUnbind(): Promise<void> {
  223 + await this.page.getByText('确定', { exact: true }).click();
  224 + await this.page.waitForTimeout(1000);
  225 + }
  226 +
  227 + /**
  228 + * 验证员工不存在
  229 + * @param name 员工姓名
  230 + * @returns 是否不存在
  231 + */
  232 + async verifyEmployeeNotExists(name: string): Promise<boolean> {
  233 + const employeeElement = this.page.getByText(name).first();
  234 + const isHidden = await employeeElement.isHidden({ timeout: 3000 }).catch(() => true);
  235 + return isHidden;
  236 + }
  237 +}
... ...
pages/productCategoryPage.ts 0 → 100644
  1 +import { Page, Locator, expect } from '@playwright/test';
  2 +import { BasePage } from './basePage';
  3 +
  4 +/**
  5 + * 商品分类页面选择器
  6 + */
  7 +const selectors = {
  8 + // 导航
  9 + moreMenu: 'text=更多 >',
  10 + categoryMenu: 'text=商品分类',
  11 +
  12 + // 功能按钮
  13 + addCategoryButton: 'text=新增分类',
  14 + editCategoryButton: 'text=修改分类',
  15 + confirmButton: 'text=确定',
  16 + clearSearchButton: '.nut-searchbar__search-icon',
  17 +
  18 + // 表单
  19 + categoryNameInput: 'textbox',
  20 + categoryDescriptionInput: 'textbox',
  21 + categoryStatusSwitch: '.nut-switch > .nut-switch-button',
  22 +
  23 + // 搜索
  24 + searchInput: 'textbox',
  25 +
  26 + // 列表
  27 + categoryItem: (categoryName: string) => `span:has-text("${categoryName}")`,
  28 +};
  29 +
  30 +/**
  31 + * 商品分类页面类
  32 + * 处理商品分类相关操作
  33 + */
  34 +export class ProductCategoryPage extends BasePage {
  35 + // 导航定位器
  36 + readonly moreMenu: Locator;
  37 + readonly categoryMenu: Locator;
  38 +
  39 + // 功能按钮
  40 + readonly addCategoryButton: Locator;
  41 + readonly editCategoryButton: Locator;
  42 + readonly deleteCategoryButton: Locator;
  43 + readonly confirmButton: Locator;
  44 + readonly clearSearchButton: Locator;
  45 +
  46 + // 表单定位器
  47 + readonly categoryNameInput: Locator;
  48 + readonly categoryDescriptionInput: Locator;
  49 + readonly categoryStatusSwitch: Locator;
  50 +
  51 + // 搜索
  52 + readonly searchInput: Locator;
  53 +
  54 + constructor(page: Page) {
  55 + super(page);
  56 +
  57 + this.moreMenu = page.getByText('更多 >');
  58 + this.categoryMenu = page.getByText('商品分类').first();
  59 + this.addCategoryButton = page.getByText('新增分类');
  60 + this.editCategoryButton = page.getByText('修改分类');
  61 + this.deleteCategoryButton = page.getByText('删除分类');
  62 + this.confirmButton = page.getByText('确定', { exact: true });
  63 + this.clearSearchButton = page.locator('uni-view:nth-child(7) > .default-container > uni-view > .z-paging-content > .nut-searchbar > .nut-searchbar__search-input > .nut-searchbar__input-inner-icon > .nut-searchbar__search-icon > .nut-icon');
  64 + this.categoryNameInput = page.getByRole('textbox').nth(1);
  65 + this.categoryDescriptionInput = page.getByRole('textbox').nth(2);
  66 + this.categoryStatusSwitch = page.locator('.uni-scroll-view-content > uni-view:nth-child(3) > .nut-cell__value > .nut-form-item__body__slots > .nut-switch > .nut-switch-button');
  67 + this.searchInput = page.getByRole('textbox').first();
  68 + }
  69 +
  70 + /**
  71 + * 打开商品分类管理页面
  72 + * 完整流程:首页 -> 更多 -> 商品分类
  73 + */
  74 + async openProductCategoryManagement(): Promise<void> {
  75 + await this.navigate('/');
  76 + await this.moreMenu.click();
  77 + await this.page.waitForTimeout(500);
  78 + await this.categoryMenu.click();
  79 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  80 + }
  81 +
  82 + /**
  83 + * 点击新增分类按钮
  84 + */
  85 + async clickAddCategory(): Promise<void> {
  86 + await this.addCategoryButton.click();
  87 + }
  88 +
  89 + /**
  90 + * 输入分类名称
  91 + * @param categoryName 分类名称
  92 + */
  93 + async enterCategoryName(categoryName: string): Promise<void> {
  94 + await this.categoryNameInput.click();
  95 + await this.categoryNameInput.fill(categoryName);
  96 + }
  97 +
  98 + /**
  99 + * 输入分类描述
  100 + * @param description 分类描述
  101 + */
  102 + async enterCategoryDescription(description: string): Promise<void> {
  103 + await this.categoryDescriptionInput.click();
  104 + await this.categoryDescriptionInput.fill(description);
  105 + }
  106 +
  107 + /**
  108 + * 点击确定按钮
  109 + */
  110 + async clickConfirm(): Promise<void> {
  111 + await this.confirmButton.click();
  112 + }
  113 +
  114 + /**
  115 + * 搜索分类
  116 + * @param categoryName 分类名称
  117 + */
  118 + async searchCategory(categoryName: string): Promise<void> {
  119 + await this.searchInput.click();
  120 + await this.searchInput.fill(categoryName);
  121 + }
  122 +
  123 + /**
  124 + * 验证分类是否存在
  125 + * @param categoryName 分类名称
  126 + */
  127 + async expectCategoryVisible(categoryName: string): Promise<void> {
  128 + await expect(this.page.locator('span').filter({ hasText: categoryName })).toBeVisible();
  129 + }
  130 +
  131 + /**
  132 + * 创建新商品分类(完整流程)
  133 + * @param categoryName 分类名称
  134 + * @param description 分类描述
  135 + */
  136 + async createCategory(categoryName: string, description: string): Promise<void> {
  137 + await this.openProductCategoryManagement();
  138 + await this.clickAddCategory();
  139 + await this.enterCategoryName(categoryName);
  140 + await this.enterCategoryDescription(description);
  141 + await this.clickConfirm();
  142 + await this.searchCategory(categoryName);
  143 + await this.expectCategoryVisible(categoryName);
  144 + }
  145 +
  146 + /**
  147 + * 点击修改分类按钮
  148 + */
  149 + async clickEditCategory(): Promise<void> {
  150 + await this.editCategoryButton.click();
  151 + }
  152 +
  153 + /**
  154 + * 清除搜索框内容
  155 + */
  156 + async clearSearch(): Promise<void> {
  157 + await this.searchInput.click();
  158 + await this.searchInput.selectText();
  159 + await this.searchInput.fill('');
  160 + await this.page.waitForTimeout(300);
  161 + }
  162 +
  163 + /**
  164 + * 切换分类状态
  165 + */
  166 + async toggleCategoryStatus(): Promise<void> {
  167 + await this.categoryStatusSwitch.click();
  168 + }
  169 +
  170 + /**
  171 + * 修改商品分类(完整流程)
  172 + * @param oldCategoryName 原分类名称
  173 + * @param newCategoryName 新分类名称
  174 + * @param newDescription 新描述
  175 + * @param toggleStatus 是否切换状态
  176 + */
  177 + async updateCategory(oldCategoryName: string, newCategoryName: string, newDescription: string, toggleStatus: boolean = false): Promise<void> {
  178 + // 搜索原分类
  179 + await this.searchCategory(oldCategoryName);
  180 + await this.expectCategoryVisible(oldCategoryName);
  181 +
  182 + // 点击修改分类
  183 + await this.clickEditCategory();
  184 +
  185 + // 编辑分类信息
  186 + await this.enterCategoryName(newCategoryName);
  187 + await this.enterCategoryDescription(newDescription);
  188 +
  189 + // 切换状态(可选)
  190 + if (toggleStatus) {
  191 + await this.toggleCategoryStatus();
  192 + }
  193 +
  194 + // 点击保存
  195 + await this.clickConfirm();
  196 +
  197 + // 清除搜索框并搜索新分类名
  198 + await this.clearSearch();
  199 + await this.searchCategory(newCategoryName);
  200 + await this.expectCategoryVisible(newCategoryName);
  201 + }
  202 +
  203 + /**
  204 + * 点击删除分类按钮
  205 + */
  206 + async clickDeleteCategory(): Promise<void> {
  207 + await this.deleteCategoryButton.click();
  208 + }
  209 +
  210 + /**
  211 + * 验证分类已删除(不存在)
  212 + * @param categoryName 分类名称
  213 + */
  214 + async expectCategoryNotVisible(categoryName: string): Promise<void> {
  215 + await expect(this.page.locator('span').filter({ hasText: categoryName })).not.toBeVisible();
  216 + }
  217 +
  218 + /**
  219 + * 删除商品分类(完整流程)
  220 + * @param categoryName 分类名称
  221 + */
  222 + async deleteCategory(categoryName: string): Promise<void> {
  223 + // 搜索要删除的分类
  224 + await this.searchCategory(categoryName);
  225 + await this.expectCategoryVisible(categoryName);
  226 +
  227 + // 点击删除分类
  228 + await this.clickDeleteCategory();
  229 +
  230 + // 确认删除
  231 + await this.clickConfirm();
  232 +
  233 + // 等待删除完成
  234 + await this.page.waitForTimeout(1000);
  235 +
  236 + // 验证分类已删除
  237 + await this.expectCategoryNotVisible(categoryName);
  238 + }
  239 +}
... ...
pages/purchasePage.ts 0 → 100644
  1 +import { Page, Locator, expect } from '@playwright/test';
  2 +import { BasePage } from './basePage';
  3 +
  4 +/**
  5 + * 采购入库页面选择器
  6 + */
  7 +const selectors = {
  8 + // 导航
  9 + moreMenu: 'text=更多 >',
  10 + purchaseInMenu: 'text=采购入库',
  11 +
  12 + // 商品列表
  13 + goodsList: '.goods-list',
  14 + goodsItem: '.goods-item',
  15 + goodsName: '.goods-name span',
  16 +
  17 + // 商品选择
  18 + productItem: (productName: string) => `uni-view:hasText("/^${productName}$/")`,
  19 + unitBox: 'uni-view:hasText("/^箱$/")',
  20 + quantityInput: "getByText('1', { exact: true })",
  21 + priceInput: "getByText('1', { exact: true })",
  22 + doneButton: 'text=完成',
  23 +
  24 + // 供应商列表
  25 + supplierField: 'uni-view:nth-child(5) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask',
  26 + supplierList: '.zp-paging-container-content',
  27 + supplierItem: '.item-card',
  28 + supplierName: '.mainTitle span',
  29 + supplierOption: (supplierName: string) => `uni-view:hasText("/^${supplierName}$/")`,
  30 +
  31 + // 仓库列表
  32 + warehouseField: 'uni-view:nth-child(6) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask',
  33 + warehouseList: '.zp-paging-container-content',
  34 + warehouseItem: '.list-item',
  35 + warehouseName: '.name',
  36 + warehouseOption: 'text=东区普通仓库',
  37 +
  38 + // 草稿操作
  39 + saveDraftButton: 'text=存入草稿',
  40 + draftListButton: 'text=草稿单列表',
  41 +
  42 + // 草稿箱搜索
  43 + draftSearchInput: '.draft-list-wrapper > .z-paging-content > .nut-searchbar > .nut-searchbar__search-input > .nut-searchbar__input-inner > .nut-searchbar__input-form > span > .nut-searchbar__input-bar > .uni-input-wrapper > .uni-input-input',
  44 + draftItem: 'span',
  45 +};
  46 +
  47 +/**
  48 + * 采购入库页面类
  49 + * 处理采购入库草稿相关操作
  50 + */
  51 +export class PurchasePage extends BasePage {
  52 + // 导航定位器
  53 + readonly moreMenu: Locator;
  54 + readonly purchaseInMenu: Locator;
  55 +
  56 + // 商品列表
  57 + readonly goodsList: Locator;
  58 + readonly goodsItem: Locator;
  59 + readonly goodsName: Locator;
  60 +
  61 + // 商品选择
  62 + readonly quantityInput: Locator;
  63 + readonly priceInput: Locator;
  64 + readonly doneButton: Locator;
  65 +
  66 + // 供应商
  67 + readonly supplierField: Locator;
  68 + readonly supplierList: Locator;
  69 + readonly supplierItem: Locator;
  70 + readonly supplierName: Locator;
  71 +
  72 + // 仓库
  73 + readonly warehouseField: Locator;
  74 + readonly warehouseList: Locator;
  75 + readonly warehouseItem: Locator;
  76 + readonly warehouseName: Locator;
  77 +
  78 + // 草稿操作
  79 + readonly saveDraftButton: Locator;
  80 + readonly draftListButton: Locator;
  81 +
  82 + // 草稿箱搜索
  83 + readonly draftSearchInput: Locator;
  84 +
  85 + // 费用相关
  86 + readonly addExpenseButton: Locator;
  87 + readonly expenseItem: Locator;
  88 + readonly amountInput: Locator;
  89 + readonly costItem: Locator;
  90 + readonly settlementAccountField: Locator;
  91 + readonly settlementAccountOption: Locator;
  92 +
  93 + // 采购员
  94 + readonly purchaserField: Locator;
  95 + readonly purchaserItem: Locator;
  96 +
  97 + // 车牌号
  98 + readonly licensePlateInput: Locator;
  99 +
  100 + // 备注
  101 + readonly remarkInput: Locator;
  102 +
  103 + // 图片上传
  104 + readonly uploadImageButton: Locator;
  105 +
  106 + // 保存
  107 + readonly saveButton: Locator;
  108 +
  109 + // 付款
  110 + readonly payButton: Locator;
  111 + readonly settlementAccount: Locator;
  112 + readonly confirmDialogButton: Locator;
  113 +
  114 + // 采购列表
  115 + readonly purchaseListMenu: Locator;
  116 + readonly purchaseSearchInput: Locator;
  117 +
  118 + // 复制创建
  119 + readonly copyCreateButton: Locator;
  120 +
  121 + // 货款小计
  122 + readonly subtotalField: Locator;
  123 +
  124 + // 存储温度
  125 + readonly storageTemperatureInput: Locator;
  126 +
  127 + // 编辑修改
  128 + readonly editButton: Locator;
  129 +
  130 + // 入账日期
  131 + readonly entryDateField: Locator;
  132 +
  133 + // 批次别名
  134 + readonly batchAliasInput: Locator;
  135 + readonly batchAliasClearIcon: Locator;
  136 +
  137 + // 取消
  138 + readonly cancelButton: Locator;
  139 + readonly cancelConfirmButton: Locator;
  140 +
  141 + // 撤销
  142 + readonly revokeButton: Locator;
  143 +
  144 + constructor(page: Page) {
  145 + super(page);
  146 +
  147 + this.moreMenu = page.getByText('更多 >');
  148 + this.purchaseInMenu = page.getByText('采购入库').first();
  149 + this.goodsList = page.locator('.goods-list');
  150 + this.goodsItem = page.locator('.goods-item');
  151 + this.goodsName = page.locator('.goods-name span');
  152 + this.quantityInput = page.locator('[data-prop="num1"] .input');
  153 + this.priceInput = page.locator('[data-prop="unitPrice"] .input');
  154 + this.doneButton = page.getByText('完成');
  155 + this.supplierField = page.locator('uni-view:nth-child(5) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  156 + this.supplierList = page.locator('.zp-paging-container-content').first();
  157 + this.supplierItem = page.locator('.item-card');
  158 + this.supplierName = page.locator('.mainTitle span');
  159 + this.warehouseField = page.locator('uni-view:nth-child(6) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  160 + this.warehouseList = page.locator('.zp-paging-container-content').first();
  161 + this.warehouseItem = page.locator('.list-item');
  162 + this.warehouseName = page.locator('.name');
  163 + this.saveDraftButton = page.getByText('存入草稿', { exact: true });
  164 + this.draftListButton = page.getByText('草稿单列表', { exact: true });
  165 + this.draftSearchInput = page.locator('.draft-list-wrapper > .z-paging-content > .nut-searchbar > .nut-searchbar__search-input > .nut-searchbar__input-inner > .nut-searchbar__input-form > span > .nut-searchbar__input-bar > .uni-input-wrapper > .uni-input-input');
  166 +
  167 + // 费用相关
  168 + this.addExpenseButton = page.getByText('入库费用添加费用');
  169 + this.expenseItem = page.locator('.account-item');
  170 + this.amountInput = page.locator('.cost-item > uni-view:nth-child(3) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  171 + this.costItem = page.locator('.cost-item');
  172 + this.settlementAccountField = page.locator('.cost-item > uni-view:nth-child(6) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  173 + this.settlementAccountOption = page.locator('uni-view').filter({ hasText: /^现金$/ }).first();
  174 +
  175 + // 采购员
  176 + this.purchaserField = page.locator('uni-view:nth-child(10) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  177 + this.purchaserItem = page.locator('.list > .item');
  178 +
  179 + // 车牌号 - 使用nth-child(11)
  180 + this.licensePlateInput = page.locator('uni-view:nth-child(11) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  181 +
  182 + // 备注 - 使用nth-child(12)
  183 + this.remarkInput = page.locator('uni-view:nth-child(12) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__input > .uni-input-wrapper > .uni-input-input');
  184 +
  185 + // 图片上传
  186 + this.uploadImageButton = page.getByText('图片(最多上传9张)');
  187 +
  188 + // 保存
  189 + this.saveButton = page.getByText('保存', { exact: true });
  190 +
  191 + // 付款
  192 + this.payButton = page.getByText('付款', { exact: true });
  193 + this.settlementAccount = page.locator('uni-view').filter({ hasText: /^微信支付$/ }).first();
  194 + this.confirmDialogButton = page.getByText('确定').first();
  195 +
  196 + // 采购列表
  197 + this.purchaseListMenu = page.getByText('采购列表');
  198 + this.purchaseSearchInput = page.getByRole('textbox');
  199 +
  200 + // 复制创建
  201 + this.copyCreateButton = page.getByText('复制创建');
  202 +
  203 + // 货款小计
  204 + this.subtotalField = page.locator('uni-view:nth-child(2) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  205 +
  206 + // 存储温度
  207 + this.storageTemperatureInput = page.locator('uni-view:nth-child(9) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__input > .uni-input-wrapper > .uni-input-input');
  208 +
  209 + // 编辑修改
  210 + this.editButton = page.getByText('修改', { exact: true });
  211 +
  212 + // 入账日期
  213 + this.entryDateField = page.locator('uni-view:nth-child(7) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__mask');
  214 +
  215 + // 批次别名
  216 + this.batchAliasInput = page.locator('uni-view:nth-child(8) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__value > .nut-input__input > .uni-input-wrapper > .uni-input-input');
  217 + this.batchAliasClearIcon = page.locator('uni-view:nth-child(8) > .nut-cell__value > .nut-form-item__body__slots > .nut-input > .nut-input__clear > .nut-input__clear-icon');
  218 +
  219 + // 取消
  220 + this.cancelButton = page.getByText('取消', { exact: true });
  221 + this.cancelConfirmButton = page.getByText('确定', { exact: true });
  222 +
  223 + // 撤销
  224 + this.revokeButton = page.getByText('撤销', { exact: true });
  225 + }
  226 +
  227 + /**
  228 + * 进入采购入库页面
  229 + */
  230 + async openPurchaseInbound(): Promise<void> {
  231 + await this.navigate('/');
  232 + await this.moreMenu.click();
  233 + await this.page.waitForTimeout(500);
  234 + await this.purchaseInMenu.click();
  235 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  236 + }
  237 +
  238 + /**
  239 + * 从商品列表随机选择一个商品
  240 + * @returns 商品名称和索引
  241 + */
  242 + async getRandomProduct(): Promise<{ name: string; index: number }> {
  243 + // 等待商品列表加载
  244 + await this.goodsList.waitFor({ state: 'visible', timeout: 10000 });
  245 + await this.page.waitForTimeout(500);
  246 +
  247 + // 获取所有商品名称
  248 + const productNames = await this.goodsName.allTextContents();
  249 + if (productNames.length === 0) {
  250 + throw new Error('商品列表为空');
  251 + }
  252 +
  253 + // 随机选择一个商品
  254 + const randomIndex = Math.floor(Math.random() * productNames.length);
  255 + const selectedProduct = productNames[randomIndex];
  256 + console.log('随机选择的商品:', selectedProduct, '索引:', randomIndex);
  257 +
  258 + return { name: selectedProduct, index: randomIndex };
  259 + }
  260 +
  261 + /**
  262 + * 选择商品(从商品列表随机选择)
  263 + * @returns 商品名称
  264 + */
  265 + async selectRandomProduct(): Promise<string> {
  266 + const { name, index } = await this.getRandomProduct();
  267 + await this.goodsItem.nth(index).click();
  268 + await this.page.waitForTimeout(300);
  269 + return name;
  270 + }
  271 +
  272 + /**
  273 + * 选择指定商品
  274 + * @param productName 商品名称
  275 + */
  276 + async selectProduct(productName: string): Promise<void> {
  277 + await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${productName}$`) }).first().click();
  278 + }
  279 +
  280 + /**
  281 + * 点击数量输入框(通过data-prop属性定位)
  282 + */
  283 + async clickQuantityInput(): Promise<void> {
  284 + await this.quantityInput.click();
  285 + }
  286 +
  287 + /**
  288 + * 输入数量
  289 + * @param quantity 数量
  290 + */
  291 + async enterQuantity(quantity: string): Promise<void> {
  292 + // 点击数量输入框(通过data-prop属性定位)
  293 + await this.page.locator('[data-prop="num1"]').click();
  294 + // 点击键盘上的数字
  295 + await this.page.locator(`.number-keyboard uni-view[data-key="${quantity}"]`).click();
  296 + }
  297 +
  298 + /**
  299 + * 输入单价(点击单价输入框,然后点击键盘数字)
  300 + * @param price 单价
  301 + */
  302 + async enterPrice(price: string): Promise<void> {
  303 + // 点击单价输入框(通过data-prop属性定位,不依赖单位文本)
  304 + await this.page.locator('[data-prop="unitPrice"]').click();
  305 + // 点击键盘上的数字
  306 + await this.page.locator(`.number-keyboard uni-view[data-key="${price}"]`).click();
  307 + }
  308 +
  309 + /**
  310 + * 点击完成按钮
  311 + */
  312 + async clickDone(): Promise<void> {
  313 + await this.doneButton.click();
  314 + await this.page.waitForTimeout(1000);
  315 + }
  316 +
  317 + /**
  318 + * 从供应商列表随机选择供应商
  319 + * @returns 供应商名称
  320 + */
  321 + async getRandomSupplier(): Promise<string> {
  322 + await this.supplierField.click();
  323 + await this.page.waitForTimeout(1000);
  324 +
  325 + const supplierNames = await this.supplierName.allTextContents();
  326 + if (supplierNames.length === 0) {
  327 + throw new Error('供应商列表为空');
  328 + }
  329 +
  330 + const randomIndex = Math.floor(Math.random() * supplierNames.length);
  331 + const selectedSupplier = supplierNames[randomIndex];
  332 + console.log('随机选择的供应商:', selectedSupplier, '索引:', randomIndex);
  333 +
  334 + await this.supplierItem.nth(randomIndex).click();
  335 + await this.page.waitForTimeout(300);
  336 +
  337 + return selectedSupplier;
  338 + }
  339 +
  340 + /**
  341 + * 选择指定供应商
  342 + * @param supplierName 供应商名称
  343 + */
  344 + async selectSupplier(supplierName: string): Promise<void> {
  345 + await this.supplierField.click();
  346 + await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${supplierName}$`) }).nth(1).click();
  347 + }
  348 +
  349 + /**
  350 + * 从仓库列表随机选择仓库
  351 + * @returns 仓库名称
  352 + */
  353 + async getRandomWarehouse(): Promise<string> {
  354 + await this.warehouseField.click();
  355 + await this.page.waitForTimeout(1000);
  356 +
  357 + const warehouseNames = await this.warehouseName.allTextContents();
  358 + if (warehouseNames.length === 0) {
  359 + throw new Error('仓库列表为空');
  360 + }
  361 +
  362 + const randomIndex = Math.floor(Math.random() * warehouseNames.length);
  363 + const selectedWarehouse = warehouseNames[randomIndex];
  364 + console.log('随机选择的仓库:', selectedWarehouse, '索引:', randomIndex);
  365 +
  366 + await this.warehouseItem.nth(randomIndex).click();
  367 + await this.page.waitForTimeout(300);
  368 +
  369 + return selectedWarehouse;
  370 + }
  371 +
  372 + /**
  373 + * 选择指定仓库
  374 + * @param warehouseName 仓库名称
  375 + */
  376 + async selectWarehouse(warehouseName: string): Promise<void> {
  377 + await this.warehouseField.click();
  378 + await this.page.getByText(warehouseName).nth(2).click();
  379 + }
  380 +
  381 + /**
  382 + * 点击存入草稿
  383 + */
  384 + async clickSaveDraft(): Promise<void> {
  385 + await this.saveDraftButton.click();
  386 + }
  387 +
  388 + /**
  389 + * 点击草稿单列表
  390 + */
  391 + async clickDraftList(): Promise<void> {
  392 + await this.draftListButton.click();
  393 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  394 + }
  395 +
  396 + /**
  397 + * 在草稿箱搜索商品
  398 + * @param productName 商品名称
  399 + */
  400 + async searchInDraftList(productName: string): Promise<void> {
  401 + await this.draftSearchInput.click();
  402 + await this.draftSearchInput.fill(productName);
  403 + }
  404 +
  405 + /**
  406 + * 验证草稿列表中存在商品
  407 + * @param productName 商品名称
  408 + */
  409 + async expectDraftContainsProduct(productName: string): Promise<void> {
  410 + // 等待搜索结果加载
  411 + await this.page.waitForTimeout(500);
  412 + // 获取列表中第一个商品进行断言
  413 + const firstItem = this.page.locator('span').filter({ hasText: productName }).first();
  414 + await expect(firstItem).toBeVisible();
  415 + }
  416 +
  417 + /**
  418 + * 点击保存按钮
  419 + */
  420 + async clickSave(): Promise<void> {
  421 + await this.saveButton.click();
  422 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  423 + await this.page.waitForTimeout(2000);
  424 + }
  425 +
  426 + /**
  427 + * 点击草稿单列表第一个草稿进入编辑页
  428 + */
  429 + async clickDraftProduct(): Promise<void> {
  430 + await this.page.locator('.draft-card-list > uni-view').first().click();
  431 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  432 + await this.page.waitForTimeout(1000);
  433 + }
  434 +
  435 + /**
  436 + * 验证采购单状态为待支付
  437 + * @param productName 商品名称
  438 + */
  439 + async expectPurchaseOrderStatusPending(productName: string): Promise<void> {
  440 + const statusLabel = this.page.getByText('待支付').nth(1);
  441 + await expect(statusLabel).toBeVisible({ timeout: 10000 });
  442 + }
  443 +
  444 + /**
  445 + * 验证采购单状态为已支付
  446 + */
  447 + async expectPurchaseOrderStatusPaid(): Promise<void> {
  448 + const statusLabel = this.page.getByText('已支付').nth(1);
  449 + await expect(statusLabel).toBeVisible({ timeout: 10000 });
  450 + }
  451 +
  452 + /**
  453 + * 点击取消按钮
  454 + */
  455 + async clickCancelButton(): Promise<void> {
  456 + await this.cancelButton.click();
  457 + await this.page.waitForTimeout(500);
  458 + }
  459 +
  460 + /**
  461 + * 确认取消弹窗
  462 + */
  463 + async confirmCancelDialog(): Promise<void> {
  464 + await this.cancelConfirmButton.click();
  465 + await this.page.waitForTimeout(1000);
  466 + }
  467 +
  468 + /**
  469 + * 点击撤销按钮
  470 + */
  471 + async clickRevokeButton(): Promise<void> {
  472 + await this.revokeButton.click();
  473 + await this.page.waitForTimeout(500);
  474 + }
  475 +
  476 + /**
  477 + * 验证采购单状态为已取消
  478 + */
  479 + async expectPurchaseOrderStatusCancelled(): Promise<void> {
  480 + const statusLabel = this.page.locator('uni-text').filter({ hasText: '已取消' }).first();
  481 + await expect(statusLabel).toBeVisible({ timeout: 10000 });
  482 + }
  483 +
  484 + /**
  485 + * 验证采购单状态为已撤销
  486 + */
  487 + async expectPurchaseOrderStatusRevoked(): Promise<void> {
  488 + const statusLabel = this.page.locator('uni-text').filter({ hasText: '已撤销' }).first();
  489 + await expect(statusLabel).toBeVisible({ timeout: 10000 });
  490 + }
  491 +
  492 + /**
  493 + * 创建采购入库草稿(完整流程)
  494 + * @param productName 商品名称
  495 + * @param supplierName 供应商名称
  496 + * @param warehouseName 仓库名称
  497 + * @param quantity 数量
  498 + * @param price 单价
  499 + */
  500 + async createPurchaseDraft(productName: string, supplierName: string, warehouseName: string, quantity: string = '1', price: string = '1'): Promise<void> {
  501 + // 选择商品
  502 + await this.selectProduct(productName);
  503 +
  504 + // 输入数量
  505 + await this.enterQuantity(quantity);
  506 +
  507 + // 输入单价
  508 + await this.enterPrice(price);
  509 +
  510 + // 点击完成
  511 + await this.clickDone();
  512 +
  513 + // 选择供应商
  514 + await this.selectSupplier(supplierName);
  515 +
  516 + // 选择仓库
  517 + await this.selectWarehouse(warehouseName);
  518 +
  519 + // 存入草稿
  520 + await this.clickSaveDraft();
  521 +
  522 + // 打开草稿单列表
  523 + await this.clickDraftList();
  524 +
  525 + // 搜索商品
  526 + await this.searchInDraftList(productName);
  527 +
  528 + // 验证商品存在
  529 + await this.expectDraftContainsProduct(productName);
  530 + }
  531 +
  532 + /**
  533 + * 添加入库费用
  534 + * @param expenseIndex 费用项索引(默认0)
  535 + * @param amount 金额(默认'1')
  536 + */
  537 + async addExpense(expenseIndex: number = 0, amount: string = '1'): Promise<void> {
  538 + // 打开费用选择
  539 + await this.addExpenseButton.click();
  540 +
  541 + // 等待费用列表加载
  542 + await this.page.waitForTimeout(500);
  543 +
  544 + // 随机选择费用项
  545 + const expenseCount = await this.expenseItem.count();
  546 + const randomIndex = Math.floor(Math.random() * expenseCount);
  547 + await this.expenseItem.nth(randomIndex).click();
  548 + await this.page.getByText('确定').click();
  549 +
  550 + // 输入金额 - 点击金额输入框,然后点击键盘数字
  551 + await this.amountInput.click();
  552 + await this.page.waitForTimeout(300);
  553 + await this.page.getByText('1', { exact: true }).click();
  554 +
  555 + // 点击键盘上的完成按钮关闭键盘
  556 + await this.page.getByText('完成', { exact: true }).click();
  557 +
  558 + // 点击结算账户并选择现金
  559 + await this.settlementAccountField.click();
  560 + await this.page.waitForTimeout(300);
  561 + await this.settlementAccountOption.click();
  562 +
  563 + // 点击确定按钮
  564 + await this.page.getByText('确定').click();
  565 + }
  566 +
  567 + /**
  568 + * 选择采购员
  569 + * @param purchaserName 采购员名称
  570 + */
  571 + async selectPurchaser(purchaserName?: string): Promise<string> {
  572 + await this.purchaserField.click();
  573 + await this.page.waitForTimeout(500);
  574 +
  575 + // 如果没有指定采购员,随机选择
  576 + if (!purchaserName) {
  577 + const purchasers = await this.purchaserItem.locator('.name').allTextContents();
  578 + if (purchasers.length === 0) {
  579 + throw new Error('采购员列表为空');
  580 + }
  581 + const randomIndex = Math.floor(Math.random() * purchasers.length);
  582 + purchaserName = purchasers[randomIndex];
  583 + await this.purchaserItem.nth(randomIndex).click();
  584 + } else {
  585 + await this.page.locator('uni-view').filter({ hasText: new RegExp(`^${purchaserName}`) }).first().click();
  586 + }
  587 +
  588 + await this.page.waitForTimeout(300);
  589 + return purchaserName;
  590 + }
  591 +
  592 + /**
  593 + * 输入车牌号
  594 + * @param licensePlate 车牌号
  595 + */
  596 + async enterLicensePlate(licensePlate: string): Promise<void> {
  597 + await this.fillLicensePlate(licensePlate, this.licensePlateInput);
  598 + }
  599 +
  600 + /**
  601 + * 输入备注
  602 + * @param remark 备注内容
  603 + */
  604 + async enterRemark(remark: string): Promise<void> {
  605 + await this.remarkInput.click();
  606 + await this.remarkInput.fill(remark);
  607 + }
  608 +
  609 + /**
  610 + * 上传图片
  611 + * @param imagePath 图片路径
  612 + */
  613 + async uploadImage(imagePath: string): Promise<void> {
  614 + // 点击图片上传区域
  615 + await this.uploadImageButton.click();
  616 + await this.page.waitForTimeout(500);
  617 + // 点击上传按钮
  618 + await this.page.locator('span').filter({ hasText: '供应商*请选择供应商车牌号请选择车牌号入库日期*请选择入库日期入库仓库*请选择仓库批次别名*请输入批次别名备注请输入备注商品清单点击选择商品入库费用添加费用存储' }).locator('uni-button').click();
  619 + await this.page.waitForTimeout(300);
  620 + // 点击上传input
  621 + await this.page.locator('.nut-uploader__input').click();
  622 + // 使用 setInputFiles 方式上传
  623 + await this.page.locator('body').setInputFiles(imagePath);
  624 + }
  625 +
  626 + /**
  627 + * 点击付款按钮
  628 + * @param index 第几个付款按钮,默认0(表单页),列表页使用1
  629 + */
  630 + async clickPay(index: number = 0): Promise<void> {
  631 + await this.payButton.nth(index).click();
  632 + }
  633 +
  634 + /**
  635 + * 选择结算账户
  636 + * @param accountName 账户名称(默认微信支付)
  637 + */
  638 + async selectSettlementAccount(accountName: string = '微信支付'): Promise<void> {
  639 + await this.settlementAccount.filter({ hasText: new RegExp(`^${accountName}$`) }).click();
  640 + }
  641 +
  642 + /**
  643 + * 确认付款弹窗
  644 + */
  645 + async confirmPayment(): Promise<void> {
  646 + // 等待确认弹窗出现
  647 + await this.confirmDialogButton.waitFor({ state: 'visible', timeout: 10000 });
  648 + await this.confirmDialogButton.click();
  649 + await this.page.waitForTimeout(1000);
  650 + }
  651 +
  652 + /**
  653 + * 进入采购列表
  654 + */
  655 + async goToPurchaseList(): Promise<void> {
  656 + await this.purchaseListMenu.click();
  657 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  658 + await this.page.waitForTimeout(2000);
  659 + }
  660 +
  661 + /**
  662 + * 点击复制创建按钮
  663 + */
  664 + async clickCopyCreate(): Promise<void> {
  665 + await this.copyCreateButton.click();
  666 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  667 + await this.page.waitForTimeout(1000);
  668 + }
  669 +
  670 + /**
  671 + * 点击货款小计输入框(触发键盘)
  672 + */
  673 + async clickSubtotal(): Promise<void> {
  674 + await this.subtotalField.click();
  675 + await this.page.waitForTimeout(500);
  676 + }
  677 +
  678 + /**
  679 + * 输入货款小计金额(通过数字键盘)
  680 + * @param amount 金额
  681 + */
  682 + async enterSubtotalAmount(amount: string): Promise<void> {
  683 + await this.page.locator(`.number-keyboard uni-view[data-key="${amount}"]`).click();
  684 + await this.page.waitForTimeout(300);
  685 + await this.page.getByText('完成').click();
  686 + await this.page.waitForTimeout(500);
  687 + }
  688 +
  689 + /**
  690 + * 输入存储温度
  691 + * @param value 温度值
  692 + */
  693 + async enterStorageTemperature(value: string): Promise<void> {
  694 + await this.storageTemperatureInput.click();
  695 + await this.storageTemperatureInput.fill(value);
  696 + }
  697 +
  698 + /**
  699 + * 在采购列表搜索商品
  700 + * @param productName 商品名称
  701 + */
  702 + async searchInPurchaseList(productName: string): Promise<void> {
  703 + // 使用 filter 精确定位到商品名称搜索框(避免匹配到供应商搜索框)
  704 + const searchInput = this.page.locator('uni-input').filter({ hasText: '供应商/商品名称' }).locator('input[type="text"]');
  705 + await searchInput.waitFor({ state: 'visible', timeout: 10000 });
  706 + await searchInput.click();
  707 + await this.page.waitForTimeout(300);
  708 + await searchInput.fill(productName);
  709 + await this.page.waitForTimeout(500);
  710 + }
  711 +
  712 + /**
  713 + * 选择入账日期:打开日历 → 翻到上一月 → 选择15号
  714 + */
  715 + async selectEntryDate(): Promise<void> {
  716 + await this.entryDateField.click();
  717 + await this.page.waitForTimeout(500);
  718 + await this.page.locator('.uni-calendar__header-btn').first().click();
  719 + await this.page.waitForTimeout(300);
  720 + await this.page.locator('uni-view').filter({ hasText: /^15$/ }).nth(2).click();
  721 + await this.page.waitForTimeout(300);
  722 + }
  723 +
  724 + /**
  725 + * 清除并输入批次别名
  726 + * @param alias 批次别名
  727 + */
  728 + async clearAndEnterBatchAlias(alias: string): Promise<void> {
  729 + await this.batchAliasInput.click();
  730 + await this.page.waitForTimeout(300);
  731 + await this.batchAliasClearIcon.click();
  732 + await this.page.waitForTimeout(300);
  733 + await this.batchAliasInput.click();
  734 + await this.batchAliasInput.fill(alias);
  735 + }
  736 +
  737 + /**
  738 + * 清除采购列表搜索框内容
  739 + */
  740 + async clearSearchInPurchaseList(): Promise<void> {
  741 + const searchInput = this.page.locator('uni-input').filter({ hasText: '供应商/商品名称' }).locator('input[type="text"]');
  742 + await searchInput.clear();
  743 + await this.page.waitForTimeout(500);
  744 + }
  745 +
  746 + /**
  747 + * 点击采购列表的修改按钮进入编辑页
  748 + */
  749 + async clickEditButton(): Promise<void> {
  750 + await this.editButton.click();
  751 + await this.page.waitForLoadState('networkidle', { timeout: 30000 });
  752 + await this.page.waitForTimeout(1000);
  753 + }
  754 +
  755 + /**
  756 + * 验证采购列表中存在商品
  757 + * @param productName 商品名称
  758 + */
  759 + async expectPurchaseListContainsProduct(productName: string): Promise<void> {
  760 + // 虚拟列表中元素可能处于 hidden 状态,使用 toBeAttached 而非 toBeVisible
  761 + const firstItem = this.page.locator('.goods-item').filter({ hasText: productName }).first();
  762 + await expect(firstItem).toBeAttached();
  763 + }
  764 +
  765 + /**
  766 + * 创建采购入库付款单(完整流程)
  767 + * @param productName 商品名称
  768 + * @param supplierName 供应商名称
  769 + * @param warehouseName 仓库名称
  770 + * @param quantity 数量
  771 + * @param price 单价
  772 + * @param expenseAmount 费用金额
  773 + * @param purchaserName 采购员名称
  774 + * @param licensePlate 车牌号
  775 + * @param remark 备注
  776 + * @param imagePath 图片路径
  777 + */
  778 + async createPurchasePayment(
  779 + productName: string,
  780 + supplierName: string,
  781 + warehouseName: string,
  782 + quantity: string = '1',
  783 + price: string = '1',
  784 + expenseAmount: string = '1',
  785 + purchaserName?: string,
  786 + licensePlate?: string,
  787 + remark: string = '自动化测试',
  788 + imagePath?: string
  789 + ): Promise<void> {
  790 + // 选择商品
  791 + await this.selectProduct(productName);
  792 +
  793 + // 输入数量
  794 + await this.enterQuantity(quantity);
  795 +
  796 + // 输入单价
  797 + await this.enterPrice(price);
  798 +
  799 + // 点击完成
  800 + await this.clickDone();
  801 +
  802 + // 添加入库费用
  803 + await this.addExpense(0, expenseAmount);
  804 +
  805 + // 选择供应商
  806 + await this.selectSupplier(supplierName);
  807 +
  808 + // 选择仓库
  809 + await this.selectWarehouse(warehouseName);
  810 +
  811 + // 选择采购员
  812 + await this.selectPurchaser(purchaserName);
  813 +
  814 + // 输入车牌号
  815 + if (licensePlate) {
  816 + await this.enterLicensePlate(licensePlate);
  817 + }
  818 +
  819 + // 输入备注
  820 + await this.enterRemark(remark);
  821 +
  822 + // 上传图片
  823 + if (imagePath) {
  824 + await this.uploadImage(imagePath);
  825 + }
  826 +
  827 + // 点击付款
  828 + await this.clickPay();
  829 +
  830 + // 选择结算账户
  831 + await this.selectSettlementAccount('微信支付');
  832 +
  833 + // 确认付款
  834 + await this.confirmPayment();
  835 +
  836 + // 进入采购列表
  837 + await this.goToPurchaseList();
  838 +
  839 + // 搜索商品
  840 + await this.searchInPurchaseList(productName);
  841 +
  842 + // 验证商品存在
  843 + await this.expectPurchaseListContainsProduct(productName);
  844 + }
  845 +}
0 846 \ No newline at end of file
... ...
scripts/save-auth.ts
... ... @@ -7,7 +7,7 @@ dotenv.config({ path: path.resolve(__dirname, &#39;../.env&#39;) });
7 7  
8 8 const TEST_PHONE = process.env.phone;
9 9 const TEST_USER_NAME = process.env.TEST_USER_NAME;
10   -const BASE_URL = process.env.BASE_URL || 'https://erp-pad.test.gszdtop.com';
  10 +const BASE_URL = process.env.BASE_URL ;
11 11  
12 12 if (!TEST_PHONE || !TEST_USER_NAME) {
13 13 throw new Error('phone 和 TEST_USER_NAME 环境变量未设置');
... ...
tests/account.spec.ts
  1 +<<<<<<< HEAD
1 2 // import { test, expect } from '@playwright/test';
2 3 // import * as allure from 'allure-js-commons';
3 4  
... ... @@ -102,3 +103,166 @@
102 103 // });
103 104 // });
104 105 // });
  106 +=======
  107 +import { test, expect } from '@playwright/test';
  108 +import * as allure from 'allure-js-commons';
  109 +import { AccountPage } from '../pages/accountPage';
  110 +
  111 +/**
  112 + * 账目管理测试
  113 + */
  114 +test.describe('账目管理', () => {
  115 + // 使用已保存的认证状态
  116 + test.use({ storageState: 'auth.json' });
  117 +
  118 + // 强制测试串行执行
  119 + test.describe.configure({ mode: 'serial' });
  120 +
  121 + /**
  122 + * 生成随机账目名称(三个字+"费",带时间戳后缀防止重复)
  123 + */
  124 + function generateAccountName(): string {
  125 + const prefixes = ['管理', '维护', '看管', '保管', '仓储', '代理', '服务', '咨询', '技术', '运营'];
  126 + const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
  127 + const timestamp = Date.now().toString().slice(-4);
  128 + return `${prefix}费${timestamp}`;
  129 + }
  130 +
  131 + /**
  132 + * 生成随机收支类型
  133 + */
  134 + function generateIncomeType(): '收入' | '支出' {
  135 + return Math.random() > 0.5 ? '收入' : '支出';
  136 + }
  137 +
  138 + /**
  139 + * 生成包含"自动化"的备注
  140 + */
  141 + function generateRemark(): string {
  142 + const timestamp = Date.now().toString().slice(-6);
  143 + return `自动化测试备注${timestamp}`;
  144 + }
  145 +
  146 + test('新增账目', async ({ page }, testInfo) => {
  147 + const accountPage = new AccountPage(page);
  148 +
  149 + // 添加allure元素
  150 + await allure.epic('账目管理');
  151 + await allure.feature('账目信息');
  152 + await allure.story('新增账目');
  153 +
  154 + // 生成随机账目数据
  155 + const accountName = generateAccountName();
  156 + const incomeType = generateIncomeType();
  157 + const remark = generateRemark();
  158 +
  159 + console.log('账目名称:', accountName);
  160 + console.log('收支类型:', incomeType);
  161 + console.log('备注:', remark);
  162 +
  163 + // 步骤1:进入账目管理页面
  164 + await allure.step('进入账目管理页面', async () => {
  165 + await accountPage.openAccountManagement();
  166 + });
  167 +
  168 + // 步骤2:点击新增按钮
  169 + await allure.step('点击新增按钮', async () => {
  170 + await accountPage.clickAddButton();
  171 + });
  172 +
  173 + // 步骤3:填写账目表单
  174 + await allure.step('填写账目表单', async () => {
  175 + await accountPage.fillAccountName(accountName);
  176 + await accountPage.selectIncomeType(incomeType);
  177 + await accountPage.fillRemark(remark);
  178 + });
  179 +
  180 + // 步骤4:保存账目
  181 + await allure.step('保存账目', async () => {
  182 + await accountPage.saveAccount();
  183 + });
  184 +
  185 + // 步骤5:验证账目创建成功
  186 + await allure.step('验证账目创建成功', async () => {
  187 + await accountPage.searchAccount(accountName);
  188 + const isCreated = await accountPage.verifyAccountExists(accountName);
  189 + expect(isCreated).toBeTruthy();
  190 + });
  191 + });
  192 +
  193 + test('删除账目', async ({ page }, testInfo) => {
  194 + const accountPage = new AccountPage(page);
  195 +
  196 + // 添加allure元素
  197 + await allure.epic('账目管理');
  198 + await allure.feature('账目信息');
  199 + await allure.story('删除账目');
  200 +
  201 + // 生成随机账目数据
  202 + const accountName = generateAccountName();
  203 + console.log('待删除账目名称:', accountName);
  204 +
  205 + // 步骤1:新增账目
  206 + await allure.step('新增账目', async () => {
  207 + await accountPage.openAccountManagement();
  208 + await accountPage.clickAddButton();
  209 + await accountPage.fillAccountName(accountName);
  210 + await accountPage.saveAccount();
  211 + });
  212 +
  213 + // 步骤2:删除账目
  214 + await allure.step('删除账目', async () => {
  215 + await accountPage.deleteAccount(accountName);
  216 + });
  217 +
  218 + // 步骤3:验证账目删除成功
  219 + await allure.step('验证账目删除成功', async () => {
  220 + const isDeleted = await accountPage.verifyAccountDeleted(accountName);
  221 + expect(isDeleted).toBeTruthy();
  222 + });
  223 + });
  224 +
  225 + test('修改账目', async ({ page }, testInfo) => {
  226 + const accountPage = new AccountPage(page);
  227 +
  228 + // 添加allure元素
  229 + await allure.epic('账目管理');
  230 + await allure.feature('账目信息');
  231 + await allure.story('修改账目');
  232 +
  233 + // 生成随机账目数据
  234 + const originalName = generateAccountName();
  235 + const newName = generateAccountName();
  236 + const incomeType = generateIncomeType();
  237 + const remark = generateRemark();
  238 +
  239 + console.log('原账目名称:', originalName);
  240 + console.log('新账目名称:', newName);
  241 + console.log('收支类型:', incomeType);
  242 + console.log('备注:', remark);
  243 +
  244 + // 步骤1:新增账目
  245 + await allure.step('新增账目', async () => {
  246 + await accountPage.openAccountManagement();
  247 + await accountPage.clickAddButton();
  248 + await accountPage.fillAccountName(originalName);
  249 + await accountPage.saveAccount();
  250 + });
  251 +
  252 + // 步骤2:修改账目
  253 + await allure.step('修改账目', async () => {
  254 + await accountPage.updateAccount(originalName, newName, incomeType, remark);
  255 + });
  256 +
  257 + // 步骤3:验证账目修改成功
  258 + await allure.step('验证账目修改成功', async () => {
  259 + // 清除搜索框
  260 + await accountPage.clearSearchBox();
  261 + // 搜索修改后的账目名称
  262 + await accountPage.searchAccount(newName);
  263 + const isExists = await accountPage.verifyAccountExists(newName);
  264 + expect(isExists).toBeTruthy();
  265 + });
  266 + });
  267 +});
  268 +>>>>>>> xfbhzxt
... ...
tests/customerGroup.spec.ts
1 1 import { test, expect } from '@playwright/test';
2 2 import * as allure from 'allure-js-commons';
  3 +import { CustomerGroupPage } from '../pages/customerGroupPage';
3 4  
4 5 /**
5 6 * 客户分组测试
... ... @@ -32,6 +33,8 @@ test.describe(&#39;客户分组&#39;, () =&gt; {
32 33 }
33 34  
34 35 test('新增客户分组', async ({ page }, testInfo) => {
  36 + const customerGroupPage = new CustomerGroupPage(page);
  37 +
35 38 // 添加allure元素
36 39 await allure.epic('客户管理');
37 40 await allure.feature('客户分组');
... ... @@ -43,47 +46,34 @@ test.describe(&#39;客户分组&#39;, () =&gt; {
43 46  
44 47 // 步骤1:进入客户分组页面
45 48 await allure.step('进入客户分组页面', async () => {
46   - await page.goto('/');
47   - await page.waitForLoadState('networkidle', { timeout: 30000 });
48   - await page.getByText('更多 >').click();
49   - await page.waitForTimeout(500);
50   - await page.getByText('客户分组').first().click();
51   - await page.waitForLoadState('networkidle', { timeout: 30000 });
  49 + await customerGroupPage.openCustomerGroupManagement();
52 50 });
53 51  
54 52 // 步骤2:点击新建按钮
55 53 await allure.step('点击新建按钮', async () => {
56   - await page.getByText('新建').click();
57   - await page.waitForTimeout(500);
  54 + await customerGroupPage.clickNewButton();
58 55 });
59 56  
60 57 // 步骤3:填写分组信息
61 58 await allure.step('填写分组信息', async () => {
62   - // 填写分组名称
63   - await page.getByRole('textbox').nth(1).click();
64   - await page.getByRole('textbox').nth(1).fill(groupName);
65   -
66   - // 填写备注/排序
67   - await page.getByRole('textbox').nth(2).click();
68   - await page.getByRole('textbox').nth(2).fill('自动化测试分组');
  59 + await customerGroupPage.fillGroupName(groupName);
69 60 });
70 61  
71 62 // 步骤4:保存分组
72 63 await allure.step('保存分组', async () => {
73   - await page.getByText('确定').click();
74   - await page.waitForTimeout(1000);
  64 + await customerGroupPage.clickConfirm();
75 65 });
76 66  
77   - // 步骤5:验证分组创建成功 - 使用正则匹配检查页面是否出现新增的内容
  67 + // 步骤5:验证分组创建成功
78 68 await allure.step('验证分组创建成功', async () => {
79   - await page.waitForTimeout(1000); // 等待页面刷新
80   - // 使用正则匹配分组名称
81   - const isGroupVisible = await page.locator('uni-view').filter({ hasText: new RegExp(`^${groupName}$`) }).first().isVisible({ timeout: 5000 }).catch(() => false);
82   - expect(isGroupVisible).toBeTruthy();
  69 + const isExists = await customerGroupPage.verifyGroupExists(groupName);
  70 + expect(isExists).toBeTruthy();
83 71 });
84 72 });
85 73  
86 74 test('修改客户分组', async ({ page }, testInfo) => {
  75 + const customerGroupPage = new CustomerGroupPage(page);
  76 +
87 77 // 添加allure元素
88 78 await allure.epic('客户管理');
89 79 await allure.feature('客户分组');
... ... @@ -92,71 +82,30 @@ test.describe(&#39;客户分组&#39;, () =&gt; {
92 82 // 先生成一个分组用于修改
93 83 const originalGroupName = generateGroupName();
94 84 const newGroupName = generateGroupName();
  85 + const newSortNumber = generateSortNumber();
95 86 console.log('原分组名称:', originalGroupName);
96 87 console.log('新分组名称:', newGroupName);
97 88  
98   - // 步骤1:进入客户分组页面并创建分组
99   - await allure.step('进入客户分组页面并创建分组', async () => {
100   - await page.goto('/');
101   - await page.waitForLoadState('networkidle', { timeout: 30000 });
102   - await page.getByText('更多 >').click();
103   - await page.waitForTimeout(500);
104   - await page.getByText('客户分组').first().click();
105   - await page.waitForLoadState('networkidle', { timeout: 30000 });
106   -
107   - // 创建分组
108   - await page.getByText('新建').click();
109   - await page.waitForTimeout(500);
110   - // 点击分组名称输入框
111   - await page.locator('uni-view').filter({ hasText: /^分组名称\*$/ }).first().click();
112   - await page.getByRole('textbox').nth(1).fill(originalGroupName);
113   - await page.getByText('确定').click();
114   - await page.waitForTimeout(1000);
  89 + // 步骤1:先创建分组
  90 + await allure.step('创建待修改的分组', async () => {
  91 + await customerGroupPage.createGroup(originalGroupName);
115 92 });
116 93  
117   - // 步骤2:搜索并修改分组
118   - await allure.step('搜索并修改分组', async () => {
119   - // 在搜索框中输入原分组名称
120   - await page.getByRole('textbox').click();
121   - await page.getByRole('textbox').fill(originalGroupName);
122   -
123   - // 点击搜索结果中的分组
124   - await page.locator('uni-view').filter({ hasText: new RegExp(`^${originalGroupName}$`) }).first().click();
125   - await page.waitForTimeout(500);
126   -
127   - // 点击编辑按钮
128   - await page.getByText('编辑').click();
129   - await page.waitForTimeout(500);
130   -
131   - // 清空分组名称并填写新名称
132   - await page.getByRole('textbox').nth(1).click();
133   - await page.locator('.nut-input__clear-icon').first().click();
134   - await page.getByRole('textbox').nth(1).click();
135   - await page.getByRole('textbox').nth(1).fill(newGroupName);
136   -
137   - // 直接填写新排序号(新增时未填写,无需清除)
138   - const newSortNumber = generateSortNumber();
139   - await page.getByRole('textbox').nth(2).click();
140   - await page.getByRole('textbox').nth(2).fill(newSortNumber);
141   -
142   - await page.getByText('确定').click();
143   - await page.waitForTimeout(1000);
  94 + // 步骤2:修改分组
  95 + await allure.step('修改分组', async () => {
  96 + await customerGroupPage.updateGroup(originalGroupName, newGroupName, newSortNumber);
144 97 });
145 98  
146   - // 步骤3:验证分组修改成功 - 使用搜索验证修改后的内容
  99 + // 步骤3:验证分组修改成功
147 100 await allure.step('验证分组修改成功', async () => {
148   - // 在搜索框中输入新分组名称进行验证
149   - await page.waitForTimeout(500);
150   - await page.getByRole('textbox').click();
151   - await page.getByRole('textbox').fill(newGroupName);
152   - await page.waitForTimeout(500);
153   -
154   - const isNewGroupVisible = await page.locator('uni-view').filter({ hasText: new RegExp(`^${newGroupName}$`) }).first().isVisible({ timeout: 5000 }).catch(() => false);
155   - expect(isNewGroupVisible).toBeTruthy();
  101 + const isExists = await customerGroupPage.verifyGroupExists(newGroupName);
  102 + expect(isExists).toBeTruthy();
156 103 });
157 104 });
158 105  
159 106 test('删除客户分组', async ({ page }, testInfo) => {
  107 + const customerGroupPage = new CustomerGroupPage(page);
  108 +
160 109 // 添加allure元素
161 110 await allure.epic('客户管理');
162 111 await allure.feature('客户分组');
... ... @@ -166,53 +115,20 @@ test.describe(&#39;客户分组&#39;, () =&gt; {
166 115 const groupName = generateGroupName();
167 116 console.log('待删除分组名称:', groupName);
168 117  
169   - // 步骤1:进入客户分组页面并创建分组
170   - await allure.step('进入客户分组页面并创建分组', async () => {
171   - await page.goto('/');
172   - await page.waitForLoadState('networkidle', { timeout: 30000 });
173   - await page.getByText('更多 >').click();
174   - await page.waitForTimeout(500);
175   - await page.getByText('客户分组').first().click();
176   - await page.waitForLoadState('networkidle', { timeout: 30000 });
177   -
178   - // 创建分组
179   - await page.getByText('新建').click();
180   - await page.waitForTimeout(500);
181   - await page.getByRole('textbox').nth(1).click();
182   - await page.getByRole('textbox').nth(1).fill(groupName);
183   - await page.getByText('确定').click();
184   - await page.waitForTimeout(1000);
  118 + // 步骤1:先创建分组
  119 + await allure.step('创建待删除的分组', async () => {
  120 + await customerGroupPage.createGroup(groupName);
185 121 });
186 122  
187   - // 步骤2:搜索并删除分组
188   - await allure.step('搜索并删除分组', async () => {
189   - // 在搜索框中输入分组名称
190   - await page.getByRole('textbox').click();
191   - await page.getByRole('textbox').fill(groupName);
192   -
193   - // 搜索后自动选中,直接点击删除按钮
194   - await page.getByText('删除').click();
195   - await page.waitForTimeout(500);
196   -
197   - // 确认删除
198   - await page.getByText('确定', { exact: true }).click();
199   - // await page.waitForTimeout(1000);
200   -
201   - // 点击清除按钮
202   - await page.locator('.nut-searchbar__search-icon').click();
203   - // 清除后等待页面刷新
204   - await page.waitForTimeout(1000);
  123 + // 步骤2:删除分组
  124 + await allure.step('删除分组', async () => {
  125 + await customerGroupPage.deleteGroup(groupName);
205 126 });
206 127  
207 128 // 步骤3:验证分组删除成功
208 129 await allure.step('验证分组删除成功', async () => {
209   - // 搜索已删除的分组名称
210   - await page.getByRole('textbox').fill(groupName);
211   - await page.waitForTimeout(1000);
212   -
213   - // 验证搜索结果中是否还能找到该分组(找不到才是删除成功)
214   - const count = await page.locator('uni-view').filter({ hasText: new RegExp(`^${groupName}$`) }).count();
215   - expect(count).toBe(0);
  130 + const isNotExists = await customerGroupPage.verifyGroupNotExists(groupName);
  131 + expect(isNotExists).toBeTruthy();
216 132 });
217 133 });
218 134 });
... ...
tests/employee.spec.ts 0 → 100644
  1 +import { test, expect } from '@playwright/test';
  2 +import * as allure from 'allure-js-commons';
  3 +import { EmployeePage } from '../pages/employeePage';
  4 +import { generateCustomerName, generatePhoneNumber } from '../utils/dataGenerator';
  5 +
  6 +/**
  7 + * 员工管理测试
  8 + */
  9 +test.describe('员工管理', () => {
  10 + // 使用已保存的认证状态
  11 + test.use({ storageState: 'auth.json' });
  12 +
  13 + // 强制测试串行执行
  14 + test.describe.configure({ mode: 'serial' });
  15 +
  16 + /**
  17 + * 生成随机员工姓名(使用已有函数)
  18 + */
  19 + function generateEmployeeName(): string {
  20 + return generateCustomerName();
  21 + }
  22 +
  23 + /**
  24 + * 生成随机联系电话(使用已有函数)
  25 + */
  26 + function generatePhone(): string {
  27 + return generatePhoneNumber();
  28 + }
  29 +
  30 + /**
  31 + * 生成包含"自动化"的备注
  32 + */
  33 + function generateRemark(): string {
  34 + const timestamp = Date.now().toString().slice(-6);
  35 + return `自动化员工测试备注${timestamp}`;
  36 + }
  37 +
  38 + test('新增员工', async ({ page }, testInfo) => {
  39 + const employeePage = new EmployeePage(page);
  40 +
  41 + // 添加allure元素
  42 + await allure.epic('员工管理');
  43 + await allure.feature('员工信息');
  44 + await allure.story('新增员工');
  45 +
  46 + // 生成随机员工数据
  47 + const employeeName = generateEmployeeName();
  48 + const phone = generatePhone();
  49 + const remark = generateRemark();
  50 +
  51 + console.log('员工姓名:', employeeName);
  52 + console.log('联系电话:', phone);
  53 + console.log('备注:', remark);
  54 +
  55 + // 步骤1:进入员工管理页面
  56 + await allure.step('进入员工管理页面', async () => {
  57 + await employeePage.openEmployeeManagement();
  58 + });
  59 +
  60 + // 步骤2:点击新建员工按钮
  61 + await allure.step('点击新建员工按钮', async () => {
  62 + await employeePage.clickNewEmployee();
  63 + });
  64 +
  65 + // 步骤3:点击手动添加
  66 + await allure.step('点击手动添加', async () => {
  67 + await employeePage.clickManualAdd();
  68 + });
  69 +
  70 + // 步骤4:填写员工表单
  71 + await allure.step('填写员工表单', async () => {
  72 + await employeePage.fillEmployeeName(employeeName);
  73 + await employeePage.fillPhone(phone);
  74 + await employeePage.fillRemark(remark);
  75 + await employeePage.selectPosition(); // 不传参数,随机选择岗位
  76 + });
  77 +
  78 + // 步骤5:保存员工
  79 + await allure.step('保存员工', async () => {
  80 + await employeePage.clickConfirm();
  81 + });
  82 +
  83 + // 步骤6:验证员工创建成功
  84 + await allure.step('验证员工创建成功', async () => {
  85 + // 使用员工姓名搜索
  86 + await employeePage.searchEmployee(employeeName);
  87 + const isExists = await employeePage.verifyEmployeeExists(employeeName);
  88 + expect(isExists).toBeTruthy();
  89 + });
  90 + });
  91 +
  92 + test('修改员工', async ({ page }, testInfo) => {
  93 + const employeePage = new EmployeePage(page);
  94 +
  95 + // 添加allure元素
  96 + await allure.epic('员工管理');
  97 + await allure.feature('员工信息');
  98 + await allure.story('修改员工');
  99 +
  100 + // 先生成一个新员工用于修改测试
  101 + const employeeName = generateEmployeeName();
  102 + const phone = generatePhone();
  103 + const originalRemark = '新建员工备注';
  104 +
  105 + console.log('待修改员工姓名:', employeeName);
  106 + console.log('联系电话:', phone);
  107 +
  108 + // 步骤1:新增员工
  109 + await allure.step('新增员工', async () => {
  110 + await employeePage.openEmployeeManagement();
  111 + await employeePage.clickNewEmployee();
  112 + await employeePage.clickManualAdd();
  113 + await employeePage.fillEmployeeName(employeeName);
  114 + await employeePage.fillPhone(phone);
  115 + await employeePage.fillRemark(originalRemark);
  116 + await employeePage.selectPosition();
  117 + await employeePage.clickConfirm();
  118 + });
  119 +
  120 + // 步骤2:搜索员工
  121 + await allure.step('搜索员工', async () => {
  122 + await employeePage.searchEmployee(employeeName);
  123 + await employeePage.verifyEmployeeExists(employeeName);
  124 + });
  125 +
  126 + // 步骤3:点击修改按钮
  127 + await allure.step('点击修改按钮', async () => {
  128 + await employeePage.clickEditButton();
  129 + });
  130 +
  131 + // 步骤4:填写修改信息(姓名和电话不允许修改,只修改备注、状态、岗位)
  132 + await allure.step('填写修改信息', async () => {
  133 + const newRemark = '修改后的备注';
  134 + await employeePage.fillRemarkForEdit(newRemark);
  135 + await employeePage.clickStatusSwitch();
  136 + await employeePage.selectPositionForEdit('采购员');
  137 + });
  138 +
  139 + // 步骤5:保存修改
  140 + await allure.step('保存修改', async () => {
  141 + await employeePage.clickConfirm();
  142 + await employeePage.page.waitForTimeout(1000);
  143 + });
  144 +
  145 + // 步骤6:验证修改成功 - 搜索员工并检查备注是否包含"修改"
  146 + await allure.step('验证修改成功', async () => {
  147 + await employeePage.searchEmployee(employeeName);
  148 + const isSuccess = await employeePage.verifyEmployeeRemarkContains('修改');
  149 + expect(isSuccess).toBeTruthy();
  150 + });
  151 + });
  152 +
  153 + test('解绑员工', async ({ page }, testInfo) => {
  154 + const employeePage = new EmployeePage(page);
  155 +
  156 + // 添加allure元素
  157 + await allure.epic('员工管理');
  158 + await allure.feature('员工信息');
  159 + await allure.story('解绑员工');
  160 +
  161 + // 先生成一个新员工用于解绑测试
  162 + const employeeName = generateEmployeeName();
  163 + const phone = generatePhone();
  164 + const originalRemark = '解绑测试备注';
  165 +
  166 + console.log('待解绑员工姓名:', employeeName);
  167 + console.log('联系电话:', phone);
  168 +
  169 + // 步骤1:新增员工
  170 + await allure.step('新增员工', async () => {
  171 + await employeePage.openEmployeeManagement();
  172 + await employeePage.clickNewEmployee();
  173 + await employeePage.clickManualAdd();
  174 + await employeePage.fillEmployeeName(employeeName);
  175 + await employeePage.fillPhone(phone);
  176 + await employeePage.fillRemark(originalRemark);
  177 + await employeePage.selectPosition();
  178 + await employeePage.clickConfirm();
  179 + });
  180 +
  181 + // 步骤2:搜索员工
  182 + await allure.step('搜索员工', async () => {
  183 + await employeePage.searchEmployee(employeeName);
  184 + await employeePage.verifyEmployeeExists(employeeName);
  185 + });
  186 +
  187 + // 步骤3:点击解绑按钮
  188 + await allure.step('点击解绑按钮', async () => {
  189 + await employeePage.clickUnbindButton();
  190 + });
  191 +
  192 + // 步骤4:确认解绑
  193 + await allure.step('确认解绑', async () => {
  194 + await employeePage.confirmUnbind();
  195 + });
  196 +
  197 + // 步骤5:验证解绑成功 - 页面不再显示该员工
  198 + await allure.step('验证解绑成功', async () => {
  199 + const isNotExists = await employeePage.verifyEmployeeNotExists(employeeName);
  200 + expect(isNotExists).toBeTruthy();
  201 + });
  202 + });
  203 +});
0 204 \ No newline at end of file
... ...
tests/productCategory.spec.ts 0 → 100644
  1 +import { test, expect } from '@playwright/test';
  2 +import * as allure from 'allure-js-commons';
  3 +import { ProductCategoryPage } from '../pages/productCategoryPage';
  4 +
  5 +/**
  6 + * 商品分类测试
  7 + */
  8 +test.describe('商品分类', () => {
  9 + // 使用已保存的认证状态
  10 + test.use({ storageState: 'auth.json' });
  11 +
  12 + // 强制测试串行执行,避免并行测试之间的干扰
  13 + test.describe.configure({ mode: 'serial' });
  14 +
  15 + /**
  16 + * 生成随机分类名称
  17 + */
  18 + function generateCategoryName(): string {
  19 + const prefixes = ['畅销', '热卖', '新品', '精选', '推荐', '特惠', '优质', '普通'];
  20 + const timestamp = Date.now().toString().slice(-4);
  21 + return `${prefixes[Math.floor(Math.random() * prefixes.length)]}${timestamp}`;
  22 + }
  23 +
  24 + test('新增商品分类', async ({ page }, testInfo) => {
  25 + const productCategoryPage = new ProductCategoryPage(page);
  26 +
  27 + // 添加allure元素
  28 + await allure.epic('商品管理');
  29 + await allure.feature('商品分类');
  30 + await allure.story('新增商品分类');
  31 +
  32 + // 生成随机分类名称
  33 + const categoryName = generateCategoryName();
  34 + const categoryDescription = '畅销的商品';
  35 + console.log('新增分类名称:', categoryName);
  36 +
  37 + // 步骤1:进入商品分类页面
  38 + await allure.step('进入商品分类页面', async () => {
  39 + await productCategoryPage.openProductCategoryManagement();
  40 + });
  41 +
  42 + // 步骤2:点击新增分类按钮
  43 + await allure.step('点击新增分类按钮', async () => {
  44 + await productCategoryPage.clickAddCategory();
  45 + });
  46 +
  47 + // 步骤3:输入分类名称
  48 + await allure.step('输入分类名称', async () => {
  49 + await productCategoryPage.enterCategoryName(categoryName);
  50 + });
  51 +
  52 + // 步骤4:输入分类描述
  53 + await allure.step('输入分类描述', async () => {
  54 + await productCategoryPage.enterCategoryDescription(categoryDescription);
  55 + });
  56 +
  57 + // 步骤5:状态默认启用,点击确定
  58 + await allure.step('点击确定按钮', async () => {
  59 + await productCategoryPage.clickConfirm();
  60 + });
  61 +
  62 + // 步骤6:搜索新增的分类
  63 + await allure.step('搜索新增的分类', async () => {
  64 + await productCategoryPage.searchCategory(categoryName);
  65 + });
  66 +
  67 + // 步骤7:验证分类创建成功
  68 + await allure.step('验证分类创建成功', async () => {
  69 + await productCategoryPage.expectCategoryVisible(categoryName);
  70 + });
  71 + });
  72 +
  73 + test('修改商品分类', async ({ page }, testInfo) => {
  74 + const productCategoryPage = new ProductCategoryPage(page);
  75 +
  76 + // 添加allure元素
  77 + await allure.epic('商品管理');
  78 + await allure.feature('商品分类');
  79 + await allure.story('修改商品分类');
  80 +
  81 + // 先生成一个分类用于修改
  82 + const originalCategoryName = generateCategoryName();
  83 + const modifiedCategoryName = `修改${originalCategoryName}`;
  84 + const originalDescription = '原始描述';
  85 + const modifiedDescription = '修改后的描述';
  86 + console.log('原分类名称:', originalCategoryName);
  87 + console.log('修改后分类名称:', modifiedCategoryName);
  88 +
  89 + // 步骤1:先创建分类
  90 + await allure.step('创建待修改的分类', async () => {
  91 + await productCategoryPage.createCategory(originalCategoryName, originalDescription);
  92 + });
  93 +
  94 + // 步骤2:搜索分类并点击修改
  95 + await allure.step('搜索并点击修改分类', async () => {
  96 + await productCategoryPage.searchCategory(originalCategoryName);
  97 + await productCategoryPage.clickEditCategory();
  98 + });
  99 +
  100 + // 步骤3:修改分类名称
  101 + await allure.step('修改分类名称', async () => {
  102 + await productCategoryPage.enterCategoryName(modifiedCategoryName);
  103 + });
  104 +
  105 + // 步骤4:修改分类描述
  106 + await allure.step('修改分类描述', async () => {
  107 + await productCategoryPage.enterCategoryDescription(modifiedDescription);
  108 + });
  109 +
  110 + // 步骤5:切换状态(可选)
  111 + await allure.step('切换分类状态', async () => {
  112 + await productCategoryPage.toggleCategoryStatus();
  113 + });
  114 +
  115 + // 步骤6:点击确定保存
  116 + await allure.step('保存修改', async () => {
  117 + await productCategoryPage.clickConfirm();
  118 + });
  119 +
  120 + // 步骤7:清除搜索框并搜索修改后的分类
  121 + await allure.step('搜索修改后的分类', async () => {
  122 + await productCategoryPage.clearSearch();
  123 + await productCategoryPage.searchCategory(modifiedCategoryName);
  124 + });
  125 +
  126 + // 步骤8:验证分类修改成功
  127 + await allure.step('验证分类修改成功', async () => {
  128 + await productCategoryPage.expectCategoryVisible(modifiedCategoryName);
  129 + });
  130 + });
  131 +
  132 + test('删除商品分类', async ({ page }, testInfo) => {
  133 + const productCategoryPage = new ProductCategoryPage(page);
  134 +
  135 + // 添加allure元素
  136 + await allure.epic('商品管理');
  137 + await allure.feature('商品分类');
  138 + await allure.story('删除商品分类');
  139 +
  140 + // 先生成一个分类用于删除
  141 + const categoryName = generateCategoryName();
  142 + console.log('删除分类名称:', categoryName);
  143 +
  144 + // 步骤1:先创建分类
  145 + await allure.step('创建待删除的分类', async () => {
  146 + await productCategoryPage.createCategory(categoryName, '删除测试描述');
  147 + });
  148 +
  149 + // 步骤2:搜索分类
  150 + await allure.step('搜索待删除的分类', async () => {
  151 + await productCategoryPage.searchCategory(categoryName);
  152 + await productCategoryPage.expectCategoryVisible(categoryName);
  153 + });
  154 +
  155 + // 步骤3:点击删除分类
  156 + await allure.step('点击删除分类按钮', async () => {
  157 + await productCategoryPage.clickDeleteCategory();
  158 + });
  159 +
  160 + // 步骤4:确认删除
  161 + await allure.step('确认删除', async () => {
  162 + await productCategoryPage.clickConfirm();
  163 + });
  164 +
  165 + // 步骤5:验证分类已删除
  166 + await allure.step('验证分类已删除', async () => {
  167 + await productCategoryPage.expectCategoryNotVisible(categoryName);
  168 + });
  169 + });
  170 +});
... ...
tests/purchase.spec.ts 0 → 100644
  1 +import { test, expect } from '@playwright/test';
  2 +import * as allure from 'allure-js-commons';
  3 +import { PurchasePage } from '../pages/purchasePage';
  4 +
  5 +/**
  6 + * 采购入库测试
  7 + */
  8 +test.describe('采购入库', () => {
  9 + // 使用已保存的认证状态
  10 + test.use({ storageState: 'auth.json' });
  11 +
  12 + // 强制测试串行执行,避免并行测试之间的干扰
  13 + test.describe.configure({ mode: 'serial' });
  14 +
  15 + test('采购入库存草稿', async ({ page }, testInfo) => {
  16 + const purchasePage = new PurchasePage(page);
  17 +
  18 + // 添加allure元素
  19 + await allure.epic('仓储管理');
  20 + await allure.feature('采购入库');
  21 + await allure.story('采购入库存草稿');
  22 +
  23 + // 测试数据
  24 + let supplierName: string;
  25 + let warehouseName: string;
  26 +
  27 + let productName: string;
  28 +
  29 + // 步骤1:进入采购入库页面
  30 + await allure.step('进入采购入库页面', async () => {
  31 + await purchasePage.openPurchaseInbound();
  32 + });
  33 +
  34 + // 步骤2:随机选择商品
  35 + await allure.step('随机选择商品', async () => {
  36 + productName = await purchasePage.selectRandomProduct();
  37 + // console.log('商品:', productName);
  38 + });
  39 +
  40 + // 步骤3:输入数量
  41 + await allure.step('输入数量', async () => {
  42 + await purchasePage.enterQuantity('1');
  43 + });
  44 +
  45 + // 步骤5:输入单价
  46 + await allure.step('输入单价', async () => {
  47 + await purchasePage.enterPrice('2');
  48 + });
  49 +
  50 + // 步骤6:点击完成
  51 + await allure.step('点击完成', async () => {
  52 + await purchasePage.clickDone();
  53 + });
  54 +
  55 + // 步骤7:随机选择供应商
  56 + await allure.step('随机选择供应商', async () => {
  57 + supplierName = await purchasePage.getRandomSupplier();
  58 + // console.log('供应商:', supplierName);
  59 + });
  60 +
  61 + // 步骤8:随机选择仓库
  62 + await allure.step('随机选择仓库', async () => {
  63 + warehouseName = await purchasePage.getRandomWarehouse();
  64 + // console.log('仓库:', warehouseName);
  65 + });
  66 +
  67 + // 步骤9:存入草稿
  68 + await allure.step('存入草稿', async () => {
  69 + await purchasePage.clickSaveDraft();
  70 + });
  71 +
  72 + // 步骤10:打开草稿单列表
  73 + await allure.step('打开草稿单列表', async () => {
  74 + await purchasePage.clickDraftList();
  75 + });
  76 +
  77 + // 步骤11:搜索商品
  78 + await allure.step('搜索商品', async () => {
  79 + await purchasePage.searchInDraftList(productName!);
  80 + });
  81 +
  82 + // 步骤12:验证草稿中存在该商品
  83 + await allure.step('验证草稿中存在该商品', async () => {
  84 + await purchasePage.expectDraftContainsProduct(productName!);
  85 + });
  86 + });
  87 +
  88 + test('采购入库付款', async ({ page }, testInfo) => {
  89 + const purchasePage = new PurchasePage(page);
  90 +
  91 + // 添加allure元素
  92 + await allure.epic('仓储管理');
  93 + await allure.feature('采购入库');
  94 + await allure.story('采购入库付款');
  95 +
  96 + // 测试数据
  97 + let supplierName: string;
  98 + let warehouseName: string;
  99 + let productName: string;
  100 +
  101 + // 步骤1:进入采购入库页面
  102 + await allure.step('进入采购入库页面', async () => {
  103 + await purchasePage.openPurchaseInbound();
  104 + });
  105 +
  106 + // 步骤2:随机选择商品
  107 + await allure.step('随机选择商品', async () => {
  108 + productName = await purchasePage.selectRandomProduct();
  109 + // console.log('商品:', productName);
  110 + });
  111 +
  112 + // 步骤3:输入数量
  113 + await allure.step('输入数量', async () => {
  114 + await purchasePage.enterQuantity('1');
  115 + });
  116 +
  117 + // 步骤4:输入单价
  118 + await allure.step('输入单价', async () => {
  119 + await purchasePage.enterPrice('1');
  120 + });
  121 +
  122 + // 步骤5:点击完成
  123 + await allure.step('点击完成', async () => {
  124 + await purchasePage.clickDone();
  125 + });
  126 +
  127 + // 步骤6:添加入库费用
  128 + await allure.step('添加入库费用', async () => {
  129 + await purchasePage.addExpense(0, '1');
  130 + });
  131 +
  132 + // 步骤7:随机选择供应商
  133 + await allure.step('随机选择供应商', async () => {
  134 + supplierName = await purchasePage.getRandomSupplier();
  135 + // console.log('供应商:', supplierName);
  136 + });
  137 +
  138 + // 步骤8:随机选择仓库
  139 + await allure.step('随机选择仓库', async () => {
  140 + warehouseName = await purchasePage.getRandomWarehouse();
  141 + // console.log('仓库:', warehouseName);
  142 + });
  143 +
  144 + // 步骤9:选择采购员
  145 + await allure.step('选择采购员', async () => {
  146 + await purchasePage.selectPurchaser();
  147 + });
  148 +
  149 + // 步骤10:输入车牌号
  150 + await allure.step('输入车牌号', async () => {
  151 + await purchasePage.enterLicensePlate('渝YUNI99');
  152 + });
  153 +
  154 + // 步骤11:输入备注
  155 + await allure.step('输入备注', async () => {
  156 + await purchasePage.enterRemark('自动化测试');
  157 + });
  158 +
  159 + // 步骤12:上传图片(暂时跳过)
  160 + // await allure.step('上传图片', async () => {
  161 + // await purchasePage.uploadImage('test-data/img/苹果.jpg');
  162 + // });
  163 +
  164 + // 步骤13:点击付款
  165 + await allure.step('点击付款', async () => {
  166 + await purchasePage.clickPay();
  167 + });
  168 +
  169 + // 步骤14:选择结算账户
  170 + await allure.step('选择结算账户', async () => {
  171 + await purchasePage.selectSettlementAccount('微信支付');
  172 + });
  173 +
  174 + // 步骤15:确认付款
  175 + await allure.step('确认付款', async () => {
  176 + await purchasePage.confirmPayment();
  177 + });
  178 +
  179 + // 步骤16:进入采购列表
  180 + await allure.step('进入采购列表', async () => {
  181 + await purchasePage.goToPurchaseList();
  182 + });
  183 +
  184 + // 步骤17:搜索商品
  185 + await allure.step('搜索商品', async () => {
  186 + await purchasePage.searchInPurchaseList(productName);
  187 + });
  188 +
  189 + // 步骤18:验证采购列表中存在该商品
  190 + await allure.step('验证采购列表中存在该商品', async () => {
  191 + await purchasePage.expectPurchaseListContainsProduct(productName);
  192 + });
  193 + });
  194 +
  195 + test('采购入库草稿编辑', async ({ page }, testInfo) => {
  196 + const purchasePage = new PurchasePage(page);
  197 +
  198 + // 添加allure元素
  199 + await allure.epic('仓储管理');
  200 + await allure.feature('采购入库');
  201 + await allure.story('采购入库草稿编辑');
  202 +
  203 + // 测试数据
  204 + let supplierName: string;
  205 + let warehouseName: string;
  206 + let productName: string;
  207 +
  208 + // 步骤1:进入采购入库页面
  209 + await allure.step('进入采购入库页面', async () => {
  210 + await purchasePage.openPurchaseInbound();
  211 + });
  212 +
  213 + // 步骤2:随机选择商品
  214 + await allure.step('随机选择商品', async () => {
  215 + productName = await purchasePage.selectRandomProduct();
  216 + // console.log('商品:', productName);
  217 + });
  218 +
  219 + // 步骤3:输入数量
  220 + await allure.step('输入数量', async () => {
  221 + await purchasePage.enterQuantity('1');
  222 + });
  223 +
  224 + // 步骤4:输入单价
  225 + await allure.step('输入单价', async () => {
  226 + await purchasePage.enterPrice('1');
  227 + });
  228 +
  229 + // 步骤5:点击完成
  230 + await allure.step('点击完成', async () => {
  231 + await purchasePage.clickDone();
  232 + });
  233 +
  234 + // 步骤6:随机选择供应商
  235 + await allure.step('随机选择供应商', async () => {
  236 + supplierName = await purchasePage.getRandomSupplier();
  237 + // console.log('供应商:', supplierName);
  238 + });
  239 +
  240 + // 步骤7:随机选择仓库
  241 + await allure.step('随机选择仓库', async () => {
  242 + warehouseName = await purchasePage.getRandomWarehouse();
  243 + // console.log('仓库:', warehouseName);
  244 + });
  245 +
  246 + // 步骤8:存入草稿
  247 + await allure.step('存入草稿', async () => {
  248 + await purchasePage.clickSaveDraft();
  249 + await page.waitForTimeout(1000);
  250 + });
  251 +
  252 + // 步骤9:打开草稿单列表
  253 + await allure.step('打开草稿单列表', async () => {
  254 + await purchasePage.clickDraftList();
  255 + });
  256 +
  257 + // 步骤10:搜索商品
  258 + await allure.step('搜索商品', async () => {
  259 + await purchasePage.searchInDraftList(productName);
  260 + });
  261 +
  262 + // 步骤11:点击草稿单商品进入编辑页
  263 + await allure.step('点击草稿单商品进入编辑页', async () => {
  264 + await purchasePage.clickDraftProduct();
  265 + });
  266 +
  267 + // 步骤12:选择采购员
  268 + await allure.step('选择采购员', async () => {
  269 + await purchasePage.selectPurchaser();
  270 + });
  271 +
  272 + // 步骤13:添加入库费用
  273 + await allure.step('添加入库费用', async () => {
  274 + await purchasePage.addExpense(0, '1');
  275 + });
  276 +
  277 + // 步骤14:点击付款
  278 + await allure.step('点击付款', async () => {
  279 + await purchasePage.clickPay();
  280 + });
  281 +
  282 + // 步骤15:选择结算账户
  283 + await allure.step('选择结算账户', async () => {
  284 + await purchasePage.selectSettlementAccount('微信支付');
  285 + });
  286 +
  287 + // 步骤16:确认付款
  288 + await allure.step('确认付款', async () => {
  289 + await purchasePage.confirmPayment();
  290 + });
  291 +
  292 + // 步骤17:进入采购列表
  293 + await allure.step('进入采购列表', async () => {
  294 + await purchasePage.goToPurchaseList();
  295 + });
  296 +
  297 + // 步骤18:搜索商品
  298 + await allure.step('搜索商品', async () => {
  299 + await purchasePage.searchInPurchaseList(productName);
  300 + });
  301 +
  302 + // 步骤19:验证采购列表中存在该商品
  303 + await allure.step('验证采购列表中存在该商品', async () => {
  304 + await purchasePage.expectPurchaseListContainsProduct(productName);
  305 + });
  306 + });
  307 +
  308 + test('采购入库保存后付款', async ({ page }, testInfo) => {
  309 + const purchasePage = new PurchasePage(page);
  310 +
  311 + // 添加allure元素
  312 + await allure.epic('仓储管理');
  313 + await allure.feature('采购入库');
  314 + await allure.story('采购入库保存后付款');
  315 +
  316 + // 测试数据
  317 + let supplierName: string;
  318 + let warehouseName: string;
  319 + let productName: string;
  320 +
  321 + // 步骤1:进入采购入库页面
  322 + await allure.step('进入采购入库页面', async () => {
  323 + await purchasePage.openPurchaseInbound();
  324 + });
  325 +
  326 + // 步骤2:随机选择商品
  327 + await allure.step('随机选择商品', async () => {
  328 + productName = await purchasePage.selectRandomProduct();
  329 + console.log('商品:', productName);
  330 + });
  331 +
  332 + // 步骤3:输入数量
  333 + await allure.step('输入数量', async () => {
  334 + await purchasePage.enterQuantity('1');
  335 + });
  336 +
  337 + // 步骤4:输入单价
  338 + await allure.step('输入单价', async () => {
  339 + await purchasePage.enterPrice('1');
  340 + });
  341 +
  342 + // 步骤5:点击完成
  343 + await allure.step('点击完成', async () => {
  344 + await purchasePage.clickDone();
  345 + });
  346 +
  347 + // 步骤6:添加入库费用
  348 + await allure.step('添加入库费用', async () => {
  349 + await purchasePage.addExpense(0, '1');
  350 + });
  351 +
  352 + // 步骤7:随机选择供应商
  353 + await allure.step('随机选择供应商', async () => {
  354 + supplierName = await purchasePage.getRandomSupplier();
  355 + console.log('供应商:', supplierName);
  356 + });
  357 +
  358 + // 步骤8:随机选择仓库
  359 + await allure.step('随机选择仓库', async () => {
  360 + warehouseName = await purchasePage.getRandomWarehouse();
  361 + console.log('仓库:', warehouseName);
  362 + });
  363 +
  364 + // 步骤9:选择采购员
  365 + await allure.step('选择采购员', async () => {
  366 + await purchasePage.selectPurchaser();
  367 + });
  368 +
  369 + // 步骤10:输入车牌号
  370 + await allure.step('输入车牌号', async () => {
  371 + await purchasePage.enterLicensePlate('渝YUNI99');
  372 + });
  373 +
  374 + // 步骤11:输入备注
  375 + await allure.step('输入备注', async () => {
  376 + await purchasePage.enterRemark('自动化测试');
  377 + });
  378 +
  379 + // 步骤12:点击保存
  380 + await allure.step('点击保存', async () => {
  381 + await purchasePage.clickSave();
  382 + });
  383 +
  384 + // 步骤13:搜索商品
  385 + await allure.step('搜索商品', async () => {
  386 + await purchasePage.searchInPurchaseList(productName);
  387 + });
  388 +
  389 + // 步骤14:验证采购单状态为待支付
  390 + await allure.step('验证采购单状态为待支付', async () => {
  391 + await purchasePage.expectPurchaseOrderStatusPending(productName);
  392 + });
  393 +
  394 + // 步骤15:点击付款
  395 + await allure.step('点击付款', async () => {
  396 + await purchasePage.clickPay(1);
  397 + });
  398 +
  399 + // 步骤16:选择结算账户(支付成功,无确认弹窗)
  400 + await allure.step('选择结算账户', async () => {
  401 + await purchasePage.selectSettlementAccount('微信支付');
  402 + });
  403 +
  404 + // 步骤17:搜索商品
  405 + await allure.step('搜索商品', async () => {
  406 + await purchasePage.searchInPurchaseList(productName);
  407 + });
  408 +
  409 + // 步骤18:验证采购单状态为已支付
  410 + await allure.step('验证采购单状态为已支付', async () => {
  411 + await purchasePage.expectPurchaseOrderStatusPaid();
  412 + });
  413 + });
  414 +
  415 + test('采购单复制创建付款', async ({ page }, testInfo) => {
  416 + const purchasePage = new PurchasePage(page);
  417 +
  418 + // 添加allure元素
  419 + await allure.epic('仓储管理');
  420 + await allure.feature('采购入库');
  421 + await allure.story('采购单复制创建付款');
  422 +
  423 + // 测试数据
  424 + let supplierName: string;
  425 + let warehouseName: string;
  426 + let productName: string;
  427 +
  428 + // 步骤1:进入采购入库页面
  429 + await allure.step('进入采购入库页面', async () => {
  430 + await purchasePage.openPurchaseInbound();
  431 + });
  432 +
  433 + // 步骤2:随机选择商品
  434 + await allure.step('随机选择商品', async () => {
  435 + productName = await purchasePage.selectRandomProduct();
  436 + console.log('商品:', productName);
  437 + });
  438 +
  439 + // 步骤3:输入数量
  440 + await allure.step('输入数量', async () => {
  441 + await purchasePage.enterQuantity('1');
  442 + });
  443 +
  444 + // 步骤4:输入单价
  445 + await allure.step('输入单价', async () => {
  446 + await purchasePage.enterPrice('1');
  447 + });
  448 +
  449 + // 步骤5:点击完成
  450 + await allure.step('点击完成', async () => {
  451 + await purchasePage.clickDone();
  452 + });
  453 +
  454 + // 步骤6:随机选择供应商
  455 + await allure.step('随机选择供应商', async () => {
  456 + supplierName = await purchasePage.getRandomSupplier();
  457 + console.log('供应商:', supplierName);
  458 + });
  459 +
  460 + // 步骤7:随机选择仓库
  461 + await allure.step('随机选择仓库', async () => {
  462 + warehouseName = await purchasePage.getRandomWarehouse();
  463 + console.log('仓库:', warehouseName);
  464 + });
  465 +
  466 + // 步骤8:点击保存
  467 + await allure.step('点击保存', async () => {
  468 + await purchasePage.clickSave();
  469 + });
  470 +
  471 + // 步骤9:搜索商品
  472 + await allure.step('搜索商品', async () => {
  473 + await purchasePage.searchInPurchaseList(productName);
  474 + });
  475 +
  476 + // 步骤10:点击复制创建
  477 + await allure.step('点击复制创建', async () => {
  478 + await purchasePage.clickCopyCreate();
  479 + });
  480 +
  481 + // 步骤11:点击货款小计并输入
  482 + await allure.step('点击货款小计并输入金额', async () => {
  483 + await purchasePage.clickSubtotal();
  484 + await purchasePage.enterSubtotalAmount('1');
  485 + });
  486 +
  487 + // 步骤12:添加入库费用
  488 + await allure.step('添加入库费用', async () => {
  489 + await purchasePage.addExpense(0, '1');
  490 + });
  491 +
  492 + // 步骤13:输入存储温度
  493 + await allure.step('输入存储温度', async () => {
  494 + await purchasePage.enterStorageTemperature('6');
  495 + });
  496 +
  497 + // 步骤14:选择采购员
  498 + await allure.step('选择采购员', async () => {
  499 + await purchasePage.selectPurchaser();
  500 + });
  501 +
  502 + // 步骤15:输入车牌号
  503 + await allure.step('输入车牌号', async () => {
  504 + await purchasePage.enterLicensePlate('渝YUNI99');
  505 + });
  506 +
  507 + // 步骤16:输入备注
  508 + await allure.step('输入备注', async () => {
  509 + await purchasePage.enterRemark('1111111');
  510 + });
  511 +
  512 + // 步骤17:点击付款
  513 + await allure.step('点击付款', async () => {
  514 + await purchasePage.clickPay();
  515 + });
  516 +
  517 + // 步骤18:选择结算账户
  518 + await allure.step('选择结算账户', async () => {
  519 + await purchasePage.selectSettlementAccount('微信支付');
  520 + });
  521 +
  522 + // 步骤19:确认付款
  523 + await allure.step('确认付款', async () => {
  524 + await purchasePage.confirmPayment();
  525 + });
  526 +
  527 + // 步骤20:清除搜索结果并重新搜索
  528 + await allure.step('清除搜索结果并重新搜索', async () => {
  529 + await purchasePage.clearSearchInPurchaseList();
  530 + await purchasePage.searchInPurchaseList(productName);
  531 + });
  532 +
  533 + // 步骤21:验证采购单状态为已支付
  534 + await allure.step('验证采购单状态为已支付', async () => {
  535 + await purchasePage.expectPurchaseOrderStatusPaid();
  536 + });
  537 + });
  538 +
  539 + test('采购单编辑保存', async ({ page }, testInfo) => {
  540 + test.setTimeout(60000);
  541 + const purchasePage = new PurchasePage(page);
  542 +
  543 + // 添加allure元素
  544 + await allure.epic('仓储管理');
  545 + await allure.feature('采购入库');
  546 + await allure.story('采购单编辑保存');
  547 +
  548 + // 测试数据
  549 + let supplierName: string;
  550 + let warehouseName: string;
  551 + let productName: string;
  552 + let newSupplierName: string;
  553 + let newWarehouseName: string;
  554 +
  555 + // 步骤1:进入采购入库页面
  556 + await allure.step('进入采购入库页面', async () => {
  557 + await purchasePage.openPurchaseInbound();
  558 + });
  559 +
  560 + // 步骤2:随机选择商品
  561 + await allure.step('随机选择商品', async () => {
  562 + productName = await purchasePage.selectRandomProduct();
  563 + console.log('商品:', productName);
  564 + });
  565 +
  566 + // 步骤3:输入数量
  567 + await allure.step('输入数量', async () => {
  568 + await purchasePage.enterQuantity('1');
  569 + });
  570 +
  571 + // 步骤4:输入单价
  572 + await allure.step('输入单价', async () => {
  573 + await purchasePage.enterPrice('1');
  574 + });
  575 +
  576 + // 步骤5:点击完成
  577 + await allure.step('点击完成', async () => {
  578 + await purchasePage.clickDone();
  579 + });
  580 +
  581 + // 步骤6:随机选择供应商
  582 + await allure.step('随机选择供应商', async () => {
  583 + supplierName = await purchasePage.getRandomSupplier();
  584 + console.log('供应商:', supplierName);
  585 + });
  586 +
  587 + // 步骤7:随机选择仓库
  588 + await allure.step('随机选择仓库', async () => {
  589 + warehouseName = await purchasePage.getRandomWarehouse();
  590 + console.log('仓库:', warehouseName);
  591 + });
  592 +
  593 + // 步骤8:点击保存
  594 + await allure.step('点击保存', async () => {
  595 + await purchasePage.clickSave();
  596 + });
  597 +
  598 + // 步骤9:搜索商品
  599 + await allure.step('搜索商品', async () => {
  600 + await purchasePage.searchInPurchaseList(productName);
  601 + });
  602 +
  603 + // 步骤10:点击修改按钮进入编辑页
  604 + await allure.step('点击修改按钮', async () => {
  605 + await purchasePage.clickEditButton();
  606 + });
  607 +
  608 + // 步骤11:连续添加两个商品
  609 + await allure.step('添加第一个商品', async () => {
  610 + const name = await purchasePage.selectRandomProduct();
  611 + console.log('添加商品1:', name);
  612 + await purchasePage.enterQuantity('1');
  613 + await purchasePage.enterPrice('1');
  614 + await purchasePage.clickDone();
  615 + });
  616 +
  617 + await allure.step('添加第二个商品', async () => {
  618 + const name = await purchasePage.selectRandomProduct();
  619 + console.log('添加商品2:', name);
  620 + await purchasePage.enterQuantity('1');
  621 + await purchasePage.enterPrice('1');
  622 + await purchasePage.clickDone();
  623 + });
  624 +
  625 + // 步骤12:填写货款优惠
  626 + await allure.step('填写货款优惠', async () => {
  627 + await purchasePage.clickSubtotal();
  628 + await purchasePage.enterSubtotalAmount('1');
  629 + });
  630 +
  631 + // 步骤13:添加入库费用
  632 + await allure.step('添加入库费用', async () => {
  633 + await purchasePage.addExpense(0, '1');
  634 + });
  635 +
  636 + // 步骤14:换选供应商
  637 + await allure.step('换选供应商', async () => {
  638 + newSupplierName = await purchasePage.getRandomSupplier();
  639 + console.log('新供应商:', newSupplierName);
  640 + });
  641 +
  642 + // 步骤15:换选仓库
  643 + await allure.step('换选仓库', async () => {
  644 + newWarehouseName = await purchasePage.getRandomWarehouse();
  645 + console.log('新仓库:', newWarehouseName);
  646 + });
  647 +
  648 + // 步骤16:选择入账日期
  649 + await allure.step('选择入账日期', async () => {
  650 + await purchasePage.selectEntryDate();
  651 + });
  652 +
  653 + // 步骤17:清除并输入批次别名
  654 + await allure.step('输入批次别名', async () => {
  655 + await purchasePage.clearAndEnterBatchAlias('批次003');
  656 + });
  657 +
  658 + // 步骤18:点击保存
  659 + await allure.step('点击保存', async () => {
  660 + await purchasePage.clickSave();
  661 + });
  662 +
  663 + // 步骤19:按供应商搜索
  664 + await allure.step('按供应商搜索', async () => {
  665 + await purchasePage.searchInPurchaseList(newSupplierName);
  666 + });
  667 +
  668 + // 步骤20:验证采购列表中存在该商品
  669 + await allure.step('验证采购列表中存在该商品', async () => {
  670 + await purchasePage.expectPurchaseListContainsProduct(productName);
  671 + });
  672 + });
  673 +
  674 + test('采购单取消', async ({ page }, testInfo) => {
  675 + const purchasePage = new PurchasePage(page);
  676 +
  677 + // 添加allure元素
  678 + await allure.epic('仓储管理');
  679 + await allure.feature('采购入库');
  680 + await allure.story('采购单取消');
  681 +
  682 + // 测试数据
  683 + let supplierName: string;
  684 + let warehouseName: string;
  685 + let productName: string;
  686 +
  687 + // 步骤1:进入采购入库页面
  688 + await allure.step('进入采购入库页面', async () => {
  689 + await purchasePage.openPurchaseInbound();
  690 + });
  691 +
  692 + // 步骤2:随机选择商品
  693 + await allure.step('随机选择商品', async () => {
  694 + productName = await purchasePage.selectRandomProduct();
  695 + console.log('商品:', productName);
  696 + });
  697 +
  698 + // 步骤3:输入数量
  699 + await allure.step('输入数量', async () => {
  700 + await purchasePage.enterQuantity('1');
  701 + });
  702 +
  703 + // 步骤4:输入单价
  704 + await allure.step('输入单价', async () => {
  705 + await purchasePage.enterPrice('1');
  706 + });
  707 +
  708 + // 步骤5:点击完成
  709 + await allure.step('点击完成', async () => {
  710 + await purchasePage.clickDone();
  711 + });
  712 +
  713 + // 步骤6:随机选择供应商
  714 + await allure.step('随机选择供应商', async () => {
  715 + supplierName = await purchasePage.getRandomSupplier();
  716 + console.log('供应商:', supplierName);
  717 + });
  718 +
  719 + // 步骤7:随机选择仓库
  720 + await allure.step('随机选择仓库', async () => {
  721 + warehouseName = await purchasePage.getRandomWarehouse();
  722 + console.log('仓库:', warehouseName);
  723 + });
  724 +
  725 + // 步骤8:点击保存
  726 + await allure.step('点击保存', async () => {
  727 + await purchasePage.clickSave();
  728 + });
  729 +
  730 + // 步骤9:搜索商品
  731 + await allure.step('搜索商品', async () => {
  732 + await purchasePage.searchInPurchaseList(productName);
  733 + });
  734 +
  735 + // 步骤10:点击取消按钮
  736 + await allure.step('点击取消按钮', async () => {
  737 + await purchasePage.clickCancelButton();
  738 + });
  739 +
  740 + // 步骤11:确认取消弹窗
  741 + await allure.step('确认取消弹窗', async () => {
  742 + await purchasePage.confirmCancelDialog();
  743 + });
  744 +
  745 + // 步骤12:清除搜索框并重新搜索商品
  746 + await allure.step('清除搜索并重新搜索', async () => {
  747 + await purchasePage.clearSearchInPurchaseList();
  748 + await purchasePage.searchInPurchaseList(productName);
  749 + });
  750 +
  751 + // 步骤13:验证采购单状态为已取消
  752 + await allure.step('验证采购单状态为已取消', async () => {
  753 + await purchasePage.expectPurchaseOrderStatusCancelled();
  754 + });
  755 + });
  756 +
  757 + test('采购单撤销', async ({ page }, testInfo) => {
  758 + const purchasePage = new PurchasePage(page);
  759 +
  760 + // 添加allure元素
  761 + await allure.epic('仓储管理');
  762 + await allure.feature('采购入库');
  763 + await allure.story('采购单撤销');
  764 +
  765 + // 测试数据
  766 + let supplierName: string;
  767 + let warehouseName: string;
  768 + let productName: string;
  769 +
  770 + // 步骤1:进入采购入库页面
  771 + await allure.step('进入采购入库页面', async () => {
  772 + await purchasePage.openPurchaseInbound();
  773 + });
  774 +
  775 + // 步骤2:随机选择商品
  776 + await allure.step('随机选择商品', async () => {
  777 + productName = await purchasePage.selectRandomProduct();
  778 + console.log('商品:', productName);
  779 + });
  780 +
  781 + // 步骤3:输入数量
  782 + await allure.step('输入数量', async () => {
  783 + await purchasePage.enterQuantity('1');
  784 + });
  785 +
  786 + // 步骤4:输入单价
  787 + await allure.step('输入单价', async () => {
  788 + await purchasePage.enterPrice('1');
  789 + });
  790 +
  791 + // 步骤5:点击完成
  792 + await allure.step('点击完成', async () => {
  793 + await purchasePage.clickDone();
  794 + });
  795 +
  796 + // 步骤6:添加入库费用
  797 + await allure.step('添加入库费用', async () => {
  798 + await purchasePage.addExpense(0, '1');
  799 + });
  800 +
  801 + // 步骤7:随机选择供应商
  802 + await allure.step('随机选择供应商', async () => {
  803 + supplierName = await purchasePage.getRandomSupplier();
  804 + console.log('供应商:', supplierName);
  805 + });
  806 +
  807 + // 步骤8:随机选择仓库
  808 + await allure.step('随机选择仓库', async () => {
  809 + warehouseName = await purchasePage.getRandomWarehouse();
  810 + console.log('仓库:', warehouseName);
  811 + });
  812 +
  813 + // 步骤9:选择采购员
  814 + await allure.step('选择采购员', async () => {
  815 + await purchasePage.selectPurchaser();
  816 + });
  817 +
  818 + // 步骤10:输入车牌号
  819 + await allure.step('输入车牌号', async () => {
  820 + await purchasePage.enterLicensePlate('渝YUNI99');
  821 + });
  822 +
  823 + // 步骤11:输入备注
  824 + await allure.step('输入备注', async () => {
  825 + await purchasePage.enterRemark('自动化测试');
  826 + });
  827 +
  828 + // 步骤12:点击付款
  829 + await allure.step('点击付款', async () => {
  830 + await purchasePage.clickPay();
  831 + });
  832 +
  833 + // 步骤13:选择结算账户
  834 + await allure.step('选择结算账户', async () => {
  835 + await purchasePage.selectSettlementAccount('微信支付');
  836 + });
  837 +
  838 + // 步骤14:确认付款
  839 + await allure.step('确认付款', async () => {
  840 + await purchasePage.confirmPayment();
  841 + });
  842 +
  843 + // 步骤15:进入采购列表
  844 + await allure.step('进入采购列表', async () => {
  845 + await purchasePage.goToPurchaseList();
  846 + });
  847 +
  848 + // 步骤16:搜索商品
  849 + await allure.step('搜索商品', async () => {
  850 + await purchasePage.searchInPurchaseList(productName);
  851 + });
  852 +
  853 + // 步骤17:点击撤销按钮
  854 + await allure.step('点击撤销按钮', async () => {
  855 + await purchasePage.clickRevokeButton();
  856 + });
  857 +
  858 + // 步骤18:确认撤销弹窗
  859 + await allure.step('确认撤销弹窗', async () => {
  860 + await purchasePage.confirmCancelDialog();
  861 + });
  862 +
  863 + // 步骤19:清除搜索并重新搜索商品
  864 + await allure.step('清除搜索并重新搜索', async () => {
  865 + await purchasePage.clearSearchInPurchaseList();
  866 + await purchasePage.searchInPurchaseList(productName);
  867 + });
  868 +
  869 + // 步骤20:验证采购单状态为已撤销
  870 + await allure.step('验证采购单状态为已撤销', async () => {
  871 + await purchasePage.expectPurchaseOrderStatusRevoked();
  872 + });
  873 + });
  874 +});
0 875 \ No newline at end of file
... ...