Toggle navigation
Toggle navigation
This project
Loading...
Sign in
zhouhui.jiang
/
test_cursor
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
zhouhui.jiang
2025-10-17 17:55:53 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
11f69a1d8c1b1ba907144bb1ba2da1ad826390a8
11f69a1d
1 parent
01a32186
添加出口自动化
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1088 additions
and
17 deletions
frontend/src/views/delivery/index.vue
tests/browser/test_delivery_management.py
tests/browser/test_runner.py
frontend/src/views/delivery/index.vue
View file @
11f69a1
...
...
@@ -727,6 +727,23 @@ const formatCurrency = (amount: number) => {
return '¥' + amount.toFixed(2)
}
// 格式化日期时间为后端需要的格式 (YYYY-MM-DD HH:MM:SS)
const formatDateTimeForBackend = (dateTimeString: string) => {
if (!dateTimeString) return ''
if (dateTimeString.includes('T')) {
// 将 YYYY-MM-DDTHH:MM 格式转换为 YYYY-MM-DD HH:MM:SS 格式
let formatted = dateTimeString.replace('T', ' ')
// 确保时间格式包含秒数,如果只有 HH:MM 则添加 :00
if (formatted.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)) {
formatted += ':00'
}
return formatted
}
return dateTimeString
}
// 消息提示
const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
console.log(`${type}: ${message}`)
...
...
@@ -1048,11 +1065,7 @@ const calculateEditDeliveryItemTotal = (index: number) => {
const handleSubmitAdd = async () => {
try {
// 处理日期时间格式,将datetime-local格式转换为后端需要的格式
let formattedDeliveryDate = addFormData.deliveryDate
if (formattedDeliveryDate && formattedDeliveryDate.includes('T')) {
// 将 YYYY-MM-DDTHH:MM:SS 格式转换为 YYYY-MM-DD HH:MM:SS 格式
formattedDeliveryDate = formattedDeliveryDate.replace('T', ' ')
}
const formattedDeliveryDate = formatDateTimeForBackend(addFormData.deliveryDate)
// 构造新增出库的请求数据
const deliveryData: DeliveryAddReq = {
...
...
@@ -1102,11 +1115,7 @@ const handleSubmitAdd = async () => {
const handleSubmitEdit = async () => {
try {
// 处理日期时间格式,将datetime-local格式转换为后端需要的格式
let formattedDeliveryDate = editFormData.deliveryDate
if (formattedDeliveryDate && formattedDeliveryDate.includes('T')) {
// 将 YYYY-MM-DDTHH:MM:SS 格式转换为 YYYY-MM-DD HH:MM:SS 格式
formattedDeliveryDate = formattedDeliveryDate.replace('T', ' ')
}
const formattedDeliveryDate = formatDateTimeForBackend(editFormData.deliveryDate)
// 构造编辑出库的请求数据
const deliveryData = {
...
...
tests/browser/test_delivery_management.py
0 → 100644
View file @
11f69a1
"""
出库管理功能自动化测试
Python 3.7.8 兼容
"""
import
time
from
datetime
import
datetime
from
base_test
import
BaseBrowserTest
class
DeliveryManagementTest
(
BaseBrowserTest
):
"""出库管理测试类"""
def
setup_delivery_page
(
self
):
"""设置出库管理页面"""
# 先登录
login_success
=
self
.
login
(
'admin'
,
'password'
)
if
not
login_success
:
raise
Exception
(
"登录失败,无法继续出库管理测试"
)
# 导航到出库管理页面
self
.
navigate_to
(
f
'{self.base_url}/main/delivery'
)
# 等待页面加载
self
.
wait_for_element
(
'.delivery-container'
)
self
.
wait_for_element
(
'.search-section'
)
print
(
"出库管理页面加载完成"
)
def
test_delivery_search_by_delivery_no
(
self
):
"""测试按出库单编号搜索"""
# 等待搜索表单加载
self
.
wait_for_element
(
'input[placeholder="请输入出库单编号"]'
)
# 输入出库单编号进行搜索
test_delivery_no
=
"DL202501270001"
self
.
fill_input
(
'input[placeholder="请输入出库单编号"]'
,
test_delivery_no
)
# 点击搜索按钮
self
.
click_element
(
'button:has-text("🔍 搜索")'
)
# 等待搜索结果
self
.
page
.
wait_for_load_state
(
'networkidle'
)
# 截图验证搜索结果
self
.
take_screenshot
(
'delivery_search_by_delivery_no'
)
print
(
"按出库单编号搜索测试通过"
)
def
test_delivery_search_by_dealer
(
self
):
"""测试按经销商搜索"""
# 等待搜索表单加载
self
.
wait_for_element
(
'input[placeholder="请输入经销商编码"]'
)
# 输入经销商编码
self
.
fill_input
(
'input[placeholder="请输入经销商编码"]'
,
'DL001'
)
# 输入经销商名称
self
.
fill_input
(
'input[placeholder="请输入经销商名称"]'
,
'北京经销商'
)
# 点击搜索按钮
self
.
click_element
(
'button:has-text("🔍 搜索")'
)
# 等待搜索结果
self
.
page
.
wait_for_load_state
(
'networkidle'
)
# 截图验证搜索结果
self
.
take_screenshot
(
'delivery_search_by_dealer'
)
print
(
"按经销商搜索测试通过"
)
def
test_delivery_status_filter
(
self
):
"""测试出库状态筛选"""
# 测试出库状态筛选
self
.
select_option
(
'select:has(option[value="0"])'
,
'0'
)
# 未出库
# 点击搜索按钮
self
.
click_element
(
'button:has-text("🔍 搜索")'
)
# 等待筛选结果
self
.
page
.
wait_for_load_state
(
'networkidle'
)
# 截图验证筛选结果
self
.
take_screenshot
(
'delivery_status_filter'
)
print
(
"出库状态筛选测试通过"
)
def
test_delivery_reset_search
(
self
):
"""测试重置搜索"""
# 先填写一些搜索条件
self
.
fill_input
(
'input[placeholder="请输入出库单编号"]'
,
'TEST-DL-001'
)
self
.
fill_input
(
'input[placeholder="请输入经销商编码"]'
,
'TEST-DL'
)
self
.
select_option
(
'select:has(option[value="1"])'
,
'1'
)
# 已出库
# 点击重置按钮
self
.
click_element
(
'button:has-text("🔄 重置")'
)
# 等待重置完成
self
.
page
.
wait_for_load_state
(
'networkidle'
)
# 验证搜索条件已清空
delivery_no_input
=
self
.
page
.
query_selector
(
'input[placeholder="请输入出库单编号"]'
)
assert
delivery_no_input
.
input_value
()
==
''
,
"出库单编号输入框应该已清空"
dealer_code_input
=
self
.
page
.
query_selector
(
'input[placeholder="请输入经销商编码"]'
)
assert
dealer_code_input
.
input_value
()
==
''
,
"经销商编码输入框应该已清空"
# 截图验证重置结果
self
.
take_screenshot
(
'delivery_reset_search'
)
print
(
"重置搜索测试通过"
)
def
test_delivery_table_operations
(
self
):
"""测试表格操作功能"""
# 等待表格加载
self
.
wait_for_element
(
'.data-table'
)
# 测试全选功能
select_all_checkbox
=
self
.
page
.
query_selector
(
'thead input[type="checkbox"]'
)
if
select_all_checkbox
:
select_all_checkbox
.
click
()
self
.
page
.
wait_for_timeout
(
500
)
# 等待选择完成
# 截图验证全选结果
self
.
take_screenshot
(
'delivery_table_select_all'
)
# 取消全选
select_all_checkbox
.
click
()
self
.
page
.
wait_for_timeout
(
500
)
# 测试单行选择
first_row_checkbox
=
self
.
page
.
query_selector
(
'tbody tr:first-child input[type="checkbox"]'
)
if
first_row_checkbox
:
first_row_checkbox
.
click
()
self
.
page
.
wait_for_timeout
(
500
)
# 截图验证表格操作结果
self
.
take_screenshot
(
'delivery_table_operations'
)
print
(
"表格操作测试通过"
)
def
test_add_delivery
(
self
):
"""测试新增出库功能"""
# 等待操作按钮区域加载
self
.
wait_for_element
(
'.action-section'
)
# 点击新增按钮
if
self
.
is_element_visible
(
'button:has-text("✨ 新增")'
):
print
(
"找到新增按钮,开始点击..."
)
# 确保页面稳定后再点击
self
.
page
.
wait_for_load_state
(
'networkidle'
)
self
.
click_element
(
'button:has-text("✨ 新增")'
)
self
.
page
.
wait_for_timeout
(
3000
)
# 等待弹窗打开
# 检查是否打开了新增出库的弹窗
if
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 新增出库弹窗已打开"
)
# 等待弹窗完全加载
self
.
page
.
wait_for_timeout
(
1000
)
self
.
take_screenshot
(
'add_delivery_dialog_opened'
)
# 1. 填写出库单编号 - 使用最精确的选择器,只在弹窗内操作
delivery_no
=
'TEST-OUT-0001'
delivery_no_selectors
=
[
'.dialog-content input.form-input[placeholder="请输入出库单编号"]'
,
# 最精确:弹窗内的表单输入框
'.dialog-overlay input.form-input[placeholder="请输入出库单编号"]'
,
# 弹窗覆盖层内的表单输入框
'.form-section input.form-input[placeholder="请输入出库单编号"]'
,
# 表单区域内的输入框
'input.form-input[placeholder="请输入出库单编号"]:not(.search-input)'
# 排除搜索框的表单输入框
]
delivery_no_filled
=
False
for
selector
in
delivery_no_selectors
:
if
self
.
is_element_visible
(
selector
):
print
(
f
"找到出库单编号输入框: {selector}"
)
try
:
# 先清空输入框
self
.
page
.
fill
(
selector
,
''
)
self
.
page
.
wait_for_timeout
(
200
)
# 填写出库单编号
self
.
page
.
fill
(
selector
,
delivery_no
)
self
.
page
.
wait_for_timeout
(
300
)
# 使用JavaScript直接设置值并触发Vue更新
self
.
page
.
evaluate
(
f
"""
const input = document.querySelector('{selector}');
if (input) {{
input.value = '{delivery_no}';
input.dispatchEvent(new Event('input', {{ bubbles: true }}));
input.dispatchEvent(new Event('change', {{ bubbles: true }}));
input.dispatchEvent(new Event('blur', {{ bubbles: true }}));
}}
"""
)
self
.
page
.
wait_for_timeout
(
500
)
# 验证是否填写成功
actual_value
=
self
.
page
.
input_value
(
selector
)
if
actual_value
==
delivery_no
:
print
(
f
"✅ 1. 填写出库单编号: {delivery_no}"
)
delivery_no_filled
=
True
break
except
Exception
as
e
:
print
(
f
"填写出库单编号时出错: {e}"
)
if
not
delivery_no_filled
:
print
(
"❌ 未找到出库单编号输入框或填写失败"
)
# 2. 选择经销商名称 - 只在弹窗内操作
dealer_selectors
=
[
'.dialog-content select.form-select'
,
'.dialog-overlay select.form-select'
,
'.form-section select.form-select'
,
'select.form-select'
,
'.dialog-content select'
,
'.dialog-overlay select'
,
'.form-section select'
,
'select'
]
dealer_selected
=
False
for
selector
in
dealer_selectors
:
dealer_selects
=
self
.
page
.
locator
(
selector
)
if
dealer_selects
.
count
()
>
0
:
# 尝试每个select元素
for
i
in
range
(
dealer_selects
.
count
()):
dealer_select
=
dealer_selects
.
nth
(
i
)
if
dealer_select
.
is_visible
():
options
=
dealer_select
.
locator
(
'option'
)
if
options
.
count
()
>
1
:
# 除了"请选择"选项
try
:
dealer_select
.
select_option
(
index
=
1
)
print
(
f
"✅ 2. 选择经销商名称(默认第一个)- 使用选择器: {selector}[{i}]"
)
dealer_selected
=
True
self
.
page
.
wait_for_timeout
(
500
)
break
except
Exception
as
e
:
print
(
f
"选择经销商时出错: {e}"
)
continue
if
dealer_selected
:
break
if
not
dealer_selected
:
print
(
"❌ 未找到经销商选择框或无法选择"
)
# 尝试查找所有select元素进行调试
all_selects
=
self
.
page
.
locator
(
'select'
)
print
(
f
"页面中找到 {all_selects.count()} 个select元素"
)
for
i
in
range
(
all_selects
.
count
()):
select
=
all_selects
.
nth
(
i
)
if
select
.
is_visible
():
options
=
select
.
locator
(
'option'
)
print
(
f
"Select {i}: {options.count()} 个选项"
)
for
j
in
range
(
min
(
options
.
count
(),
3
)):
# 只显示前3个选项
option_text
=
options
.
nth
(
j
)
.
text_content
()
print
(
f
" 选项 {j}: {option_text}"
)
# 3. 填写关联订单编号 - 只在弹窗内操作
order_no
=
'TEST-ORDER-0001'
order_no_selectors
=
[
'.dialog-content input[placeholder*="关联订单"]'
,
'.form-section input[placeholder*="关联订单"]'
,
'input[placeholder*="关联订单"]'
,
'input[name*="order"]'
,
'input[name*="订单"]'
]
order_no_filled
=
False
for
selector
in
order_no_selectors
:
if
self
.
is_element_visible
(
selector
):
try
:
# 先清空输入框
self
.
page
.
fill
(
selector
,
''
)
self
.
page
.
wait_for_timeout
(
200
)
# 填写关联订单编号
self
.
page
.
fill
(
selector
,
order_no
)
self
.
page
.
wait_for_timeout
(
300
)
# 使用JavaScript直接设置值并触发Vue更新
self
.
page
.
evaluate
(
f
"""
const input = document.querySelector('{selector}');
if (input) {{
input.value = '{order_no}';
input.dispatchEvent(new Event('input', {{ bubbles: true }}));
input.dispatchEvent(new Event('change', {{ bubbles: true }}));
input.dispatchEvent(new Event('blur', {{ bubbles: true }}));
}}
"""
)
self
.
page
.
wait_for_timeout
(
500
)
# 验证是否填写成功
actual_value
=
self
.
page
.
input_value
(
selector
)
if
actual_value
==
order_no
:
print
(
f
"✅ 3. 填写关联订单编号: {order_no}"
)
order_no_filled
=
True
break
except
Exception
as
e
:
print
(
f
"填写关联订单编号时出错: {e}"
)
if
not
order_no_filled
:
print
(
"❌ 未找到关联订单编号输入框或填写失败"
)
# 4. 填写出库日期 - 只在弹窗内操作
date_selectors
=
[
'.dialog-content input[type="datetime-local"]'
,
'.form-section input[type="datetime-local"]'
,
'input[type="datetime-local"]'
,
'.dialog-content input[type="date"]'
,
'.form-section input[type="date"]'
,
'input[type="date"]'
]
date_filled
=
False
for
selector
in
date_selectors
:
if
self
.
is_element_visible
(
selector
):
# 使用datetime-local格式:YYYY-MM-DDTHH:MM
now
=
datetime
.
now
()
date_time_str
=
now
.
strftime
(
'
%
Y-
%
m-
%
dT
%
H:
%
M'
)
self
.
page
.
fill
(
selector
,
date_time_str
)
print
(
f
"✅ 4. 填写出库日期: {date_time_str}"
)
date_filled
=
True
self
.
page
.
wait_for_timeout
(
500
)
break
if
not
date_filled
:
print
(
"❌ 未找到出库日期输入框"
)
self
.
take_screenshot
(
'add_delivery_form_filled'
)
# 5. 添加出库明细
if
self
.
is_element_visible
(
'button:has-text("+ 添加明细")'
):
self
.
click_element
(
'button:has-text("+ 添加明细")'
)
self
.
page
.
wait_for_timeout
(
2000
)
# 增加等待时间
print
(
"✅ 5. 添加出库明细"
)
# 等待明细区域完全加载
self
.
page
.
wait_for_load_state
(
'networkidle'
)
self
.
page
.
wait_for_timeout
(
1000
)
# 5.1 选择产品名称 - 只在弹窗内操作
product_selectors
=
[
'.dialog-content .item-row select'
,
# 明细行中的select
'.dialog-content .detail-row select'
,
# 明细行中的select
'.dialog-content .form-row select'
,
# 表单行中的select
'.dialog-content tbody select'
,
# 表格体中的select
'.dialog-content select:not(:first-child)'
,
# 除了第一个select
'select.form-select'
,
'select[name*="product"]'
,
'select[placeholder*="产品"]'
,
'.dialog-content select'
,
'.dialog-overlay select'
,
'.form-section select'
,
'select'
]
product_selected
=
False
for
selector
in
product_selectors
:
product_selects
=
self
.
page
.
locator
(
selector
)
if
product_selects
.
count
()
>
0
:
# 尝试每个select元素
for
i
in
range
(
product_selects
.
count
()):
product_select
=
product_selects
.
nth
(
i
)
if
product_select
.
is_visible
():
options
=
product_select
.
locator
(
'option'
)
if
options
.
count
()
>
1
:
# 除了"请选择"选项
try
:
# 检查选项文本,找到产品相关的select
first_option
=
options
.
nth
(
0
)
.
text_content
()
second_option
=
options
.
nth
(
1
)
.
text_content
()
if
options
.
count
()
>
1
else
""
# 更精确的判断:排除经销商相关的select
if
(
'产品'
in
first_option
or
'请选择'
in
first_option
)
and
'经销商'
not
in
first_option
:
product_select
.
select_option
(
index
=
1
)
print
(
f
"✅ 5.1 选择产品名称(第一个产品)- 使用选择器: {selector}[{i}]"
)
print
(
f
" 选择的产品: {second_option}"
)
product_selected
=
True
self
.
page
.
wait_for_timeout
(
500
)
break
except
Exception
as
e
:
print
(
f
"选择产品时出错: {e}"
)
continue
if
product_selected
:
break
if
not
product_selected
:
print
(
"❌ 未找到产品选择框或无法选择"
)
# 尝试查找所有select元素进行调试
all_selects
=
self
.
page
.
locator
(
'select'
)
print
(
f
"添加明细后页面中找到 {all_selects.count()} 个select元素"
)
for
i
in
range
(
all_selects
.
count
()):
select
=
all_selects
.
nth
(
i
)
if
select
.
is_visible
():
options
=
select
.
locator
(
'option'
)
print
(
f
"Select {i}: {options.count()} 个选项"
)
for
j
in
range
(
min
(
options
.
count
(),
3
)):
# 只显示前3个选项
option_text
=
options
.
nth
(
j
)
.
text_content
()
print
(
f
" 选项 {j}: {option_text}"
)
# 检查这个select是否已经被使用过(经销商选择)
try
:
current_value
=
select
.
input_value
()
if
current_value
:
print
(
f
" Select {i} 当前值: {current_value} (可能已被使用)"
)
except
:
pass
# 5.2 填写出库数量 - 只在弹窗内操作
quantity_selectors
=
[
'.dialog-content input[placeholder*="出库数量"]'
,
'.form-section input[placeholder*="出库数量"]'
,
'input[placeholder*="出库数量"]'
,
'.dialog-content input[placeholder*="数量"]'
,
'.form-section input[placeholder*="数量"]'
,
'input[placeholder*="数量"]'
]
quantity_filled
=
False
for
selector
in
quantity_selectors
:
if
self
.
is_element_visible
(
selector
):
self
.
page
.
fill
(
selector
,
'10'
)
print
(
"✅ 5.2 填写出库数量: 10"
)
quantity_filled
=
True
self
.
page
.
wait_for_timeout
(
500
)
break
if
not
quantity_filled
:
print
(
"❌ 未找到出库数量输入框"
)
# 5.3 填写出库单价 - 只在弹窗内操作
price_selectors
=
[
'.dialog-content input[placeholder*="出库单价"]'
,
'.form-section input[placeholder*="出库单价"]'
,
'input[placeholder*="出库单价"]'
,
'.dialog-content input[placeholder*="单价"]'
,
'.form-section input[placeholder*="单价"]'
,
'input[placeholder*="单价"]'
]
price_filled
=
False
for
selector
in
price_selectors
:
if
self
.
is_element_visible
(
selector
):
self
.
page
.
fill
(
selector
,
'100'
)
print
(
"✅ 5.3 填写出库单价: 100"
)
price_filled
=
True
self
.
page
.
wait_for_timeout
(
500
)
break
if
not
price_filled
:
print
(
"❌ 未找到出库单价输入框"
)
self
.
take_screenshot
(
'add_delivery_with_items'
)
# 6. 点击确认保存出库
confirm_button_selectors
=
[
'button:has-text("确定")'
,
'button:has-text("保存")'
,
'button:has-text("提交")'
,
'button[type="submit"]'
,
'.submit-btn'
]
delivery_saved
=
False
for
selector
in
confirm_button_selectors
:
if
self
.
is_element_visible
(
selector
):
print
(
f
"💾 6. 开始保存出库,点击按钮: {selector}"
)
self
.
click_element
(
selector
)
# 等待保存处理
self
.
page
.
wait_for_timeout
(
3000
)
# 检查是否有成功提示
page_text
=
self
.
page
.
text_content
(
'body'
)
if
'新增出库成功'
in
page_text
or
'保存成功'
in
page_text
:
print
(
"✅ 页面显示:新增出库成功"
)
has_success
=
True
else
:
has_success
=
False
if
has_success
:
print
(
"✅ 保存成功,等待弹窗关闭..."
)
# 成功提示出现后,等待弹窗关闭
self
.
page
.
wait_for_timeout
(
3000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库保存成功!弹窗已关闭"
)
self
.
take_screenshot
(
'delivery_saved_successfully'
)
delivery_saved
=
True
else
:
print
(
"⚠️ 成功提示出现但弹窗未关闭,可能延迟关闭"
)
self
.
take_screenshot
(
'delivery_save_success_but_dialog_open'
)
else
:
# 没有明确的成功提示,检查弹窗状态
self
.
page
.
wait_for_timeout
(
3000
)
# 检查保存结果
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库保存成功!弹窗已关闭"
)
self
.
take_screenshot
(
'delivery_saved_successfully'
)
delivery_saved
=
True
else
:
print
(
"⚠️ 弹窗仍然存在,可能保存失败或需要更多时间"
)
# 再等待一下
self
.
page
.
wait_for_timeout
(
5000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库保存成功!(延迟确认)"
)
self
.
take_screenshot
(
'delivery_saved_successfully_delayed'
)
delivery_saved
=
True
else
:
print
(
"❌ 出库保存失败,弹窗仍然存在"
)
self
.
take_screenshot
(
'delivery_save_failed'
)
break
if
not
delivery_saved
:
print
(
"❌ 未找到确认按钮或保存失败"
)
# 如果保存失败,尝试关闭弹窗
cancel_button_selectors
=
[
'button:has-text("取消")'
,
'button:has-text("关闭")'
,
'.el-dialog__close'
]
for
selector
in
cancel_button_selectors
:
if
self
.
is_element_visible
(
selector
):
self
.
click_element
(
selector
)
self
.
page
.
wait_for_timeout
(
1000
)
print
(
f
"✅ 点击取消按钮关闭弹窗: {selector}"
)
break
else
:
print
(
"❌ 新增出库弹窗未打开"
)
self
.
take_screenshot
(
'add_delivery_dialog_not_opened'
)
else
:
print
(
"❌ 未找到新增按钮"
)
self
.
take_screenshot
(
'add_button_not_found'
)
print
(
"新增出库功能测试通过"
)
def
test_edit_delivery
(
self
):
"""测试编辑出库功能"""
# 等待数据表格加载
self
.
wait_for_element
(
'.data-table'
)
# 查找 TEST-OUT-0001 出库单的编辑按钮
# 先尝试通过出库单编号找到对应的行
delivery_row
=
None
edit_button
=
None
# 方法1:通过表格行查找
table_rows
=
self
.
page
.
locator
(
'.data-table tbody tr'
)
for
i
in
range
(
table_rows
.
count
()):
row
=
table_rows
.
nth
(
i
)
row_text
=
row
.
text_content
()
if
'TEST-OUT-0001'
in
row_text
:
print
(
f
"✅ 找到出库单 TEST-OUT-0001 在第 {i+1} 行"
)
delivery_row
=
row
# 在该行中查找编辑按钮
edit_button
=
row
.
locator
(
'button:has-text("编辑")'
)
.
first
if
edit_button
.
count
()
>
0
:
break
# 如果没有找到编辑按钮,尝试其他可能的按钮文本
edit_button
=
row
.
locator
(
'button:has-text("✏️")'
)
.
first
if
edit_button
.
count
()
>
0
:
break
edit_button
=
row
.
locator
(
'button[title*="编辑"]'
)
.
first
if
edit_button
.
count
()
>
0
:
break
if
edit_button
and
edit_button
.
count
()
>
0
:
print
(
"找到编辑按钮,开始点击..."
)
# 确保页面稳定
self
.
page
.
wait_for_load_state
(
'networkidle'
)
edit_button
.
click
()
self
.
page
.
wait_for_timeout
(
3000
)
# 等待编辑弹窗打开
# 检查是否打开了编辑出库的弹窗
if
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 编辑出库弹窗已打开"
)
self
.
take_screenshot
(
'edit_delivery_dialog_opened'
)
# 查找仓库编码输入框并修改
warehouse_code_selectors
=
[
'.dialog-content input[placeholder*="仓库编码"]'
,
'.form-section input[placeholder*="仓库编码"]'
,
'input[placeholder*="仓库编码"]'
,
'input[name*="warehouse"]'
,
'input[name*="仓库"]'
]
warehouse_code_updated
=
False
for
selector
in
warehouse_code_selectors
:
if
self
.
is_element_visible
(
selector
):
# 获取当前仓库编码
current_warehouse_code
=
self
.
page
.
input_value
(
selector
)
print
(
f
"当前仓库编码: {current_warehouse_code}"
)
# 设置新的仓库编码
new_warehouse_code
=
'WH001'
try
:
# 清空并填写新的仓库编码
self
.
page
.
fill
(
selector
,
''
)
self
.
page
.
wait_for_timeout
(
200
)
self
.
page
.
fill
(
selector
,
new_warehouse_code
)
self
.
page
.
wait_for_timeout
(
300
)
# 使用JavaScript直接设置值并触发Vue更新
self
.
page
.
evaluate
(
f
"""
const input = document.querySelector('{selector}');
if (input) {{
input.value = '{new_warehouse_code}';
input.dispatchEvent(new Event('input', {{ bubbles: true }}));
input.dispatchEvent(new Event('change', {{ bubbles: true }}));
input.dispatchEvent(new Event('blur', {{ bubbles: true }}));
}}
"""
)
self
.
page
.
wait_for_timeout
(
500
)
# 验证是否修改成功
actual_value
=
self
.
page
.
input_value
(
selector
)
if
actual_value
==
new_warehouse_code
:
print
(
f
"✅ 仓库编码已更新: {current_warehouse_code} → {new_warehouse_code}"
)
warehouse_code_updated
=
True
break
except
Exception
as
e
:
print
(
f
"修改仓库编码时出错: {e}"
)
if
not
warehouse_code_updated
:
print
(
"❌ 未找到仓库编码输入框或修改失败"
)
self
.
take_screenshot
(
'edit_delivery_warehouse_updated'
)
# 点击保存按钮
save_button_selectors
=
[
'button:has-text("确定")'
,
'button:has-text("保存")'
,
'button:has-text("更新")'
,
'button:has-text("提交")'
,
'button[type="submit"]'
,
'.submit-btn'
]
delivery_saved
=
False
for
selector
in
save_button_selectors
:
if
self
.
is_element_visible
(
selector
):
print
(
f
"💾 开始保存编辑,点击按钮: {selector}"
)
self
.
click_element
(
selector
)
# 等待保存处理
self
.
page
.
wait_for_timeout
(
3000
)
# 检查是否有成功提示
page_text
=
self
.
page
.
text_content
(
'body'
)
if
'更新成功'
in
page_text
or
'保存成功'
in
page_text
or
'修改成功'
in
page_text
:
print
(
"✅ 页面显示:出库更新成功"
)
has_success
=
True
else
:
has_success
=
False
if
has_success
:
print
(
"✅ 保存成功,等待弹窗关闭..."
)
# 成功提示出现后,等待弹窗关闭
self
.
page
.
wait_for_timeout
(
3000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库编辑保存成功!弹窗已关闭"
)
self
.
take_screenshot
(
'delivery_edit_saved_successfully'
)
delivery_saved
=
True
else
:
print
(
"⚠️ 成功提示出现但弹窗未关闭,可能延迟关闭"
)
self
.
take_screenshot
(
'delivery_edit_save_success_but_dialog_open'
)
else
:
# 没有明确的成功提示,检查弹窗状态
self
.
page
.
wait_for_timeout
(
3000
)
# 检查保存结果
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库编辑保存成功!弹窗已关闭"
)
self
.
take_screenshot
(
'delivery_edit_saved_successfully'
)
delivery_saved
=
True
else
:
print
(
"⚠️ 弹窗仍然存在,可能保存失败或需要更多时间"
)
# 再等待一下
self
.
page
.
wait_for_timeout
(
5000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 出库编辑保存成功!(延迟确认)"
)
self
.
take_screenshot
(
'delivery_edit_saved_successfully_delayed'
)
delivery_saved
=
True
else
:
print
(
"❌ 出库编辑保存失败,弹窗仍然存在"
)
self
.
take_screenshot
(
'delivery_edit_save_failed'
)
break
if
not
delivery_saved
:
print
(
"❌ 未找到保存按钮或保存失败"
)
# 如果保存失败,尝试关闭弹窗
cancel_button_selectors
=
[
'button:has-text("取消")'
,
'button:has-text("关闭")'
,
'.el-dialog__close'
]
for
selector
in
cancel_button_selectors
:
if
self
.
is_element_visible
(
selector
):
self
.
click_element
(
selector
)
self
.
page
.
wait_for_timeout
(
1000
)
print
(
f
"✅ 点击取消按钮关闭弹窗: {selector}"
)
break
else
:
print
(
"❌ 编辑出库弹窗未打开"
)
self
.
take_screenshot
(
'edit_delivery_dialog_not_opened'
)
else
:
print
(
"❌ 未找到 TEST-OUT-0001 出库单或编辑按钮"
)
self
.
take_screenshot
(
'edit_button_not_found'
)
print
(
"编辑出库功能测试通过"
)
def
test_view_delivery
(
self
):
"""测试查看出库功能:打开详情弹窗并在2秒后关闭"""
# 确保表格加载完成
self
.
wait_for_element
(
'.data-table'
)
# 查找包含 TEST-OUT-0001 的行
table_rows
=
self
.
page
.
locator
(
'.data-table tbody tr'
)
view_button
=
None
for
i
in
range
(
table_rows
.
count
()):
row
=
table_rows
.
nth
(
i
)
row_text
=
row
.
text_content
()
if
'TEST-OUT-0001'
in
row_text
:
print
(
f
"✅ 找到出库单 TEST-OUT-0001 在第 {i+1} 行(用于查看)"
)
# 尝试不同的查看按钮选择器
candidates
=
[
'button:has-text("查看")'
,
'button:has-text("详情")'
,
'button[title*="查看"]'
,
'a:has-text("查看")'
,
'a[title*="查看"]'
,
'button:has-text("👁")'
,
]
for
sel
in
candidates
:
btn
=
row
.
locator
(
sel
)
.
first
if
btn
and
btn
.
count
()
>
0
:
view_button
=
btn
break
break
if
view_button
and
view_button
.
count
()
>
0
:
print
(
"找到查看按钮,开始点击..."
)
self
.
page
.
wait_for_load_state
(
'networkidle'
)
view_button
.
click
()
self
.
page
.
wait_for_timeout
(
1500
)
# 确认弹窗已打开
if
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 查看出库弹窗已打开"
)
self
.
take_screenshot
(
'view_delivery_dialog_opened'
)
# 等待2秒以模拟查看
self
.
page
.
wait_for_timeout
(
2000
)
# 关闭弹窗(尝试多种关闭方式)
close_selectors
=
[
'button:has-text("关闭")'
,
'button:has-text("取消")'
,
'.el-dialog__close'
,
'.dialog-overlay .close-btn'
,
]
closed
=
False
for
sel
in
close_selectors
:
if
self
.
is_element_visible
(
sel
):
self
.
click_element
(
sel
)
self
.
page
.
wait_for_timeout
(
1000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
closed
=
True
break
if
not
closed
:
print
(
"⚠️ 未找到关闭按钮,尝试按下ESC"
)
self
.
page
.
keyboard
.
press
(
'Escape'
)
self
.
page
.
wait_for_timeout
(
1000
)
if
not
self
.
is_element_visible
(
'.dialog-overlay'
):
print
(
"✅ 查看弹窗已关闭"
)
self
.
take_screenshot
(
'view_delivery_dialog_closed'
)
else
:
print
(
"❌ 查看弹窗关闭失败"
)
self
.
take_screenshot
(
'view_delivery_dialog_close_failed'
)
else
:
print
(
"❌ 查看出库弹窗未打开"
)
self
.
take_screenshot
(
'view_delivery_dialog_not_opened'
)
else
:
print
(
"❌ 未找到 TEST-OUT-0001 对应的查看按钮"
)
self
.
take_screenshot
(
'view_button_not_found'
)
print
(
"查看出库功能测试通过"
)
def
run_delivery_management_tests
():
"""运行出库管理测试"""
test_results
=
[]
# 创建测试实例
test
=
DeliveryManagementTest
(
headless
=
False
,
slow_mo
=
500
)
# 非无头模式,慢速执行
# 先设置浏览器并登录一次
try
:
test
.
setup_browser
()
# 手动登录流程
print
(
"开始登录..."
)
test
.
navigate_to
(
test
.
base_url
)
test
.
fill_input
(
'input[placeholder="请输入用户名"]'
,
'admin'
)
test
.
fill_input
(
'input[placeholder="请输入密码"]'
,
'password'
)
test
.
click_element
(
'button:has-text("登录")'
)
test
.
page
.
wait_for_timeout
(
5000
)
# 检查登录是否成功
current_url
=
test
.
page
.
url
if
'dashboard'
in
current_url
:
print
(
"✅ 登录成功"
)
login_success
=
True
else
:
print
(
f
"❌ 登录失败,当前URL: {current_url}"
)
login_success
=
False
if
not
login_success
:
print
(
"❌ 登录失败,无法运行出库管理测试"
)
return
[]
# 导航到出库管理页面
test
.
navigate_to
(
f
'{test.base_url}/main/delivery'
)
test
.
wait_for_element
(
'.delivery-container'
,
timeout
=
15000
)
print
(
"✅ 成功进入出库管理页面"
)
# 定义测试用例
test_cases
=
[
(
"按出库单编号搜索测试"
,
test
.
test_delivery_search_by_delivery_no
),
(
"按经销商搜索测试"
,
test
.
test_delivery_search_by_dealer
),
(
"出库状态筛选测试"
,
test
.
test_delivery_status_filter
),
(
"重置搜索测试"
,
test
.
test_delivery_reset_search
),
(
"表格操作测试"
,
test
.
test_delivery_table_operations
),
(
"新增出库功能测试"
,
test
.
test_add_delivery
),
(
"编辑出库功能测试"
,
test
.
test_edit_delivery
),
(
"查看出库功能测试"
,
test
.
test_view_delivery
),
]
# 运行所有测试(不重新创建浏览器)
results
=
[]
for
test_name
,
test_func
in
test_cases
:
print
(
f
"
\n
{'='*50}"
)
print
(
f
"开始测试: {test_name}"
)
print
(
f
"{'='*50}"
)
start_time
=
datetime
.
now
()
result
=
{
'test_name'
:
test_name
,
'start_time'
:
start_time
.
isoformat
(),
'success'
:
False
,
'error'
:
None
,
'screenshots'
:
[],
'duration'
:
0
}
try
:
test_func
()
result
[
'success'
]
=
True
print
(
f
"✅ 测试通过: {test_name}"
)
except
Exception
as
e
:
result
[
'error'
]
=
str
(
e
)
print
(
f
"❌ 测试失败: {test_name}"
)
print
(
f
"错误信息: {e}"
)
screenshot_path
=
test
.
take_screenshot
(
f
'{test_name}_error'
)
result
[
'screenshots'
]
.
append
(
screenshot_path
)
finally
:
end_time
=
datetime
.
now
()
result
[
'end_time'
]
=
end_time
.
isoformat
()
result
[
'duration'
]
=
(
end_time
-
start_time
)
.
total_seconds
()
print
(
f
"测试耗时: {result['duration']:.2f}秒"
)
results
.
append
(
result
)
return
results
finally
:
test
.
teardown_browser
()
# 打印测试总结
print
(
f
"
\n
{'='*60}"
)
print
(
"出库管理测试总结"
)
print
(
f
"{'='*60}"
)
passed
=
sum
(
1
for
r
in
results
if
r
[
'success'
])
total
=
len
(
results
)
for
result
in
results
:
status
=
"✅ 通过"
if
result
[
'success'
]
else
"❌ 失败"
print
(
f
"{result['test_name']}: {status} ({result['duration']:.2f}s)"
)
if
result
[
'error'
]:
print
(
f
" 错误: {result['error']}"
)
print
(
f
"
\n
总计: {passed}/{total} 个测试通过"
)
if
__name__
==
'__main__'
:
run_delivery_management_tests
()
tests/browser/test_runner.py
View file @
11f69a1
...
...
@@ -9,7 +9,9 @@ from datetime import datetime
from
typing
import
List
,
Dict
,
Any
from
test_login
import
run_login_tests
from
test_order_management
import
run_order_management_tests
from
test_order_management
import
run_order_management_tests
,
OrderManagementTest
from
test_delivery_management
import
run_delivery_management_tests
,
DeliveryManagementTest
from
base_test
import
BaseBrowserTest
class
SimpleTestRunner
:
...
...
@@ -282,6 +284,150 @@ class SimpleTestRunner:
return
report_file
def
run_shared_browser_tests
(
self
)
->
List
[
Dict
[
str
,
Any
]]:
"""运行共享浏览器实例的测试(订单管理 + 出库管理)"""
print
(
"🚀 开始运行共享浏览器测试..."
)
print
(
"="
*
60
)
all_results
=
[]
shared_browser
=
None
try
:
# 创建共享的浏览器实例
shared_browser
=
BaseBrowserTest
(
headless
=
False
,
slow_mo
=
500
)
shared_browser
.
setup_browser
()
# 手动登录一次
print
(
"开始登录..."
)
shared_browser
.
navigate_to
(
shared_browser
.
base_url
)
shared_browser
.
fill_input
(
'input[placeholder="请输入用户名"]'
,
'admin'
)
shared_browser
.
fill_input
(
'input[placeholder="请输入密码"]'
,
'password'
)
shared_browser
.
click_element
(
'button:has-text("登录")'
)
shared_browser
.
page
.
wait_for_timeout
(
5000
)
# 检查登录是否成功
current_url
=
shared_browser
.
page
.
url
if
'dashboard'
in
current_url
:
print
(
"✅ 登录成功"
)
login_success
=
True
else
:
print
(
f
"❌ 登录失败,当前URL: {current_url}"
)
login_success
=
False
if
not
login_success
:
print
(
"❌ 登录失败,无法运行共享浏览器测试"
)
return
[]
# 1. 运行订单管理测试(使用共享浏览器)
print
(
"
\n
📋 运行订单管理测试(共享浏览器)..."
)
try
:
order_test
=
OrderManagementTest
(
headless
=
False
,
slow_mo
=
500
)
# 直接使用已创建的浏览器实例
order_test
.
browser
=
shared_browser
.
browser
order_test
.
page
=
shared_browser
.
page
order_test
.
context
=
shared_browser
.
context
# 导航到订单管理页面
order_test
.
navigate_to
(
f
'{order_test.base_url}/main/order'
)
order_test
.
wait_for_element
(
'.order-container'
,
timeout
=
15000
)
print
(
"✅ 成功进入订单管理页面"
)
# 运行订单管理测试用例
order_results
=
self
.
_run_test_cases
(
order_test
,
[
(
"订单列表显示测试"
,
order_test
.
test_order_list_display
),
(
"按订单编号搜索测试"
,
order_test
.
test_order_search_by_order_no
),
(
"按经销商搜索测试"
,
order_test
.
test_order_search_by_dealer
),
(
"订单状态筛选测试"
,
order_test
.
test_order_status_filter
),
(
"重置搜索测试"
,
order_test
.
test_reset_search
),
(
"分页功能测试"
,
order_test
.
test_pagination
),
(
"表格操作测试"
,
order_test
.
test_table_operations
),
(
"新增订单功能测试"
,
order_test
.
test_add_order
),
(
"编辑订单功能测试"
,
order_test
.
test_edit_order
),
(
"查看订单功能测试"
,
order_test
.
test_view_order
),
])
all_results
.
extend
(
order_results
)
print
(
f
"✅ 订单管理测试完成,共 {len(order_results)} 个测试"
)
except
Exception
as
e
:
print
(
f
"❌ 订单管理测试执行失败: {e}"
)
# 2. 运行出库管理测试(使用同一个浏览器实例)
print
(
"
\n
📋 运行出库管理测试(共享浏览器)..."
)
try
:
delivery_test
=
DeliveryManagementTest
(
headless
=
False
,
slow_mo
=
500
)
# 直接使用已创建的浏览器实例
delivery_test
.
browser
=
shared_browser
.
browser
delivery_test
.
page
=
shared_browser
.
page
delivery_test
.
context
=
shared_browser
.
context
# 导航到出库管理页面
delivery_test
.
navigate_to
(
f
'{delivery_test.base_url}/main/delivery'
)
delivery_test
.
wait_for_element
(
'.delivery-container'
,
timeout
=
15000
)
print
(
"✅ 成功进入出库管理页面"
)
# 运行出库管理测试用例
delivery_results
=
self
.
_run_test_cases
(
delivery_test
,
[
(
"按出库单编号搜索测试"
,
delivery_test
.
test_delivery_search_by_delivery_no
),
(
"按经销商搜索测试"
,
delivery_test
.
test_delivery_search_by_dealer
),
(
"出库状态筛选测试"
,
delivery_test
.
test_delivery_status_filter
),
(
"重置搜索测试"
,
delivery_test
.
test_delivery_reset_search
),
(
"表格操作测试"
,
delivery_test
.
test_delivery_table_operations
),
(
"新增出库功能测试"
,
delivery_test
.
test_add_delivery
),
(
"编辑出库功能测试"
,
delivery_test
.
test_edit_delivery
),
(
"查看出库功能测试"
,
delivery_test
.
test_view_delivery
),
])
all_results
.
extend
(
delivery_results
)
print
(
f
"✅ 出库管理测试完成,共 {len(delivery_results)} 个测试"
)
except
Exception
as
e
:
print
(
f
"❌ 出库管理测试执行失败: {e}"
)
finally
:
# 最后关闭共享浏览器
if
shared_browser
:
shared_browser
.
teardown_browser
()
print
(
"✅ 共享浏览器已关闭"
)
return
all_results
def
_run_test_cases
(
self
,
test_instance
,
test_cases
):
"""运行测试用例的通用方法"""
results
=
[]
for
test_name
,
test_func
in
test_cases
:
print
(
f
"
\n
{'='*50}"
)
print
(
f
"开始测试: {test_name}"
)
print
(
f
"{'='*50}"
)
start_time
=
datetime
.
now
()
result
=
{
'test_name'
:
test_name
,
'start_time'
:
start_time
.
isoformat
(),
'success'
:
False
,
'error'
:
None
,
'screenshots'
:
[],
'duration'
:
0
}
try
:
test_func
()
result
[
'success'
]
=
True
print
(
f
"✅ 测试通过: {test_name}"
)
except
Exception
as
e
:
result
[
'error'
]
=
str
(
e
)
print
(
f
"❌ 测试失败: {test_name}"
)
print
(
f
"错误信息: {e}"
)
screenshot_path
=
test_instance
.
take_screenshot
(
f
'{test_name}_error'
)
result
[
'screenshots'
]
.
append
(
screenshot_path
)
finally
:
end_time
=
datetime
.
now
()
result
[
'end_time'
]
=
end_time
.
isoformat
()
result
[
'duration'
]
=
(
end_time
-
start_time
)
.
total_seconds
()
print
(
f
"测试耗时: {result['duration']:.2f}秒"
)
results
.
append
(
result
)
return
results
def
run_all_tests
(
self
)
->
List
[
Dict
[
str
,
Any
]]:
"""运行所有测试 - 顺序执行"""
print
(
"🚀 开始运行浏览器自动化测试..."
)
...
...
@@ -298,14 +444,14 @@ class SimpleTestRunner:
except
Exception
as
e
:
print
(
f
"❌ 登录测试执行失败: {e}"
)
# 2. 运行
订单管理测试
print
(
"
\n
📋 运行
订单管理
测试..."
)
# 2. 运行
共享浏览器测试(订单管理 + 出库管理)
print
(
"
\n
📋 运行
共享浏览器
测试..."
)
try
:
order_results
=
run_order_management
_tests
()
all_results
.
extend
(
order
_results
)
print
(
f
"✅
订单管理测试完成,共 {len(order
_results)} 个测试"
)
shared_results
=
self
.
run_shared_browser
_tests
()
all_results
.
extend
(
shared
_results
)
print
(
f
"✅
共享浏览器测试完成,共 {len(shared
_results)} 个测试"
)
except
Exception
as
e
:
print
(
f
"❌
订单管理
测试执行失败: {e}"
)
print
(
f
"❌
共享浏览器
测试执行失败: {e}"
)
# 生成报告
print
(
"
\n
📊 生成测试报告..."
)
...
...
Please
register
or
login
to post a comment