275 lines
8.6 KiB
Python
275 lines
8.6 KiB
Python
from pathlib import Path
|
|
|
|
from contract_flow_steps import (
|
|
ContractFlowContext,
|
|
begin_date_for,
|
|
check_customer_diligence,
|
|
read_contract_number,
|
|
sign_date_for,
|
|
_as_index_list,
|
|
_classify_dialog_text,
|
|
_click_toolbar_button,
|
|
_lui_btn_xpath,
|
|
_next_unused_goods_row,
|
|
_pick_contract_row,
|
|
_read_contract_number_on_page,
|
|
_resolve_contract_body_path,
|
|
_submit_error_from_body,
|
|
_submit_succeeded,
|
|
)
|
|
from errors import DiligenceError
|
|
import pytest
|
|
|
|
|
|
def _ctx(**overrides) -> ContractFlowContext:
|
|
data = dict(
|
|
contract_url="http://example",
|
|
username="u",
|
|
password="p",
|
|
supplier_name="示例",
|
|
order_date="2026-09-28",
|
|
source_type="",
|
|
)
|
|
data.update(overrides)
|
|
return ContractFlowContext(**data)
|
|
|
|
|
|
class _FakeBrowser:
|
|
def __init__(self, text: str, url: str = "", script_result: str = ""):
|
|
self._text = text
|
|
self._url = url
|
|
self._script_result = script_result
|
|
self.opened: list[str] = []
|
|
|
|
def read_body_text(self) -> str:
|
|
return self._text
|
|
|
|
def switch_to_default_content(self) -> None:
|
|
return None
|
|
|
|
def open_url(self, url: str) -> None:
|
|
self.opened.append(url)
|
|
self._url = url
|
|
|
|
def execute_script(self, script: str, *args):
|
|
if "_xform_docNumber" in script:
|
|
return self._script_result
|
|
if "location.href" in script:
|
|
return self._url
|
|
return ""
|
|
|
|
|
|
def test_sign_and_begin_fall_back_to_order_date():
|
|
ctx = _ctx()
|
|
assert sign_date_for(ctx) == "2026-09-28"
|
|
assert begin_date_for(ctx) == "2026-09-28"
|
|
|
|
|
|
def test_sign_and_begin_use_contract_dates_when_set():
|
|
ctx = _ctx(contract_sign_date="2026-10-01", contract_start_date="2026-10-02")
|
|
assert sign_date_for(ctx) == "2026-10-01"
|
|
assert begin_date_for(ctx) == "2026-10-02"
|
|
|
|
|
|
def test_diligence_raises_when_page_says_not_done():
|
|
with pytest.raises(DiligenceError, match="客户未尽调"):
|
|
check_customer_diligence(_FakeBrowser("客户未尽调,不能起草"), _ctx())
|
|
|
|
|
|
def test_diligence_passes_when_text_absent():
|
|
check_customer_diligence(_FakeBrowser("客户已尽调"), _ctx())
|
|
|
|
|
|
def test_same_product_keeps_own_row():
|
|
assert _next_unused_goods_row([0, 1, 2], []) == 0
|
|
assert _next_unused_goods_row([0, 1, 2], [0]) == 1
|
|
assert _next_unused_goods_row([0, 1, 2], [0, 1]) == 2
|
|
assert _next_unused_goods_row([0], [0]) == -1
|
|
assert _next_unused_goods_row([], [0]) == -1
|
|
|
|
|
|
def test_goods_row_indexes_from_script_string():
|
|
assert _as_index_list("0,1,2") == [0, 1, 2]
|
|
assert _as_index_list("") == []
|
|
assert _as_index_list(None) == []
|
|
assert _as_index_list([0, 1]) == [0, 1]
|
|
|
|
|
|
def test_pick_contract_row_fd_id_beats_latest():
|
|
rows = [
|
|
{"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"},
|
|
{
|
|
"no": "HT-OURS",
|
|
"title": "重庆亿昊药业有限公司采购协议20261002",
|
|
"customer": "DW0100046361",
|
|
"href": "kmAgreementApply.do?method=view&fdId=ours",
|
|
},
|
|
]
|
|
assert (
|
|
_pick_contract_row(
|
|
rows,
|
|
fd_id="ours",
|
|
title="重庆亿昊药业有限公司采购协议20261002",
|
|
customer_code="DW0100046361",
|
|
)
|
|
== "HT-OURS"
|
|
)
|
|
|
|
|
|
def test_pick_contract_row_title_and_customer():
|
|
rows = [
|
|
{"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"},
|
|
{
|
|
"no": "HT-OURS",
|
|
"title": "重庆亿昊药业有限公司采购协议20261002",
|
|
"customer": "DW0100046361",
|
|
"href": "fdId=ours",
|
|
},
|
|
]
|
|
assert (
|
|
_pick_contract_row(
|
|
rows,
|
|
title="重庆亿昊药业有限公司采购协议20261002",
|
|
customer_code="DW0100046361",
|
|
)
|
|
== "HT-OURS"
|
|
)
|
|
|
|
|
|
def test_pick_contract_row_does_not_take_first_unmatched():
|
|
rows = [{"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"}]
|
|
assert _pick_contract_row(rows, title="重庆亿昊", customer_code="DW0100046361") == ""
|
|
|
|
|
|
def test_submit_error_from_doc_missing_page():
|
|
body = "您的请求已提交,系统返回如下信息:\n操作失败!\n该文档不存在"
|
|
assert _submit_error_from_body(body) == "该文档不存在"
|
|
assert _submit_error_from_body("客户编码 提交") == ""
|
|
|
|
|
|
def test_submit_succeeded_on_success_toast():
|
|
browser = _FakeBrowser("您的操作已成功", url="kmAgreementApply.do?method=edit&fdId=1")
|
|
assert _submit_succeeded(browser) is True
|
|
|
|
|
|
def test_submit_succeeded_on_view_page():
|
|
browser = _FakeBrowser("合同详情", url="kmAgreementApply.do?method=view&fdId=1")
|
|
assert _submit_succeeded(browser) is True
|
|
|
|
|
|
def test_submit_not_succeeded_on_edit_or_add_page():
|
|
edit = _FakeBrowser("客户编码 暂存 提交", url="kmAgreementApply.do?method=edit&fdId=1")
|
|
add = _FakeBrowser("客户编码 暂存 提交", url="kmAgreementApply.do?method=add")
|
|
assert _submit_succeeded(edit) is False
|
|
assert _submit_succeeded(add) is False
|
|
|
|
|
|
def test_classify_dialog_text_kinds():
|
|
assert _classify_dialog_text("操作失败!该文档不存在") == "error"
|
|
assert _classify_dialog_text("您确定要提交此文档吗?") == "confirm"
|
|
assert _classify_dialog_text("您的操作已成功") == "success"
|
|
assert _classify_dialog_text("客户编码") == "none"
|
|
|
|
|
|
def test_lui_btn_xpath_is_exact_txt():
|
|
xpath = _lui_btn_xpath("提交")
|
|
assert "lui_widget_btn_txt" in xpath
|
|
assert "normalize-space()='提交'" in xpath
|
|
|
|
|
|
class _ClickBrowser(_FakeBrowser):
|
|
def __init__(self, script_result):
|
|
super().__init__("")
|
|
self.script_result = script_result
|
|
self.scripts: list[tuple] = []
|
|
self.native: list[list[str]] = []
|
|
self.first: list[list[str]] = []
|
|
|
|
def execute_script(self, script, *args):
|
|
self.scripts.append((script, args))
|
|
return self.script_result
|
|
|
|
def click_native(self, selectors):
|
|
self.native.append(list(selectors))
|
|
|
|
def click_first(self, selectors):
|
|
self.first.append(list(selectors))
|
|
|
|
|
|
def test_click_toolbar_button_targets_lui_btn_txt():
|
|
browser = _ClickBrowser({"ok": True, "via": "btn_txt", "cid": "lui-id-36"})
|
|
assert _click_toolbar_button(browser, "提交") is True
|
|
script, args = browser.scripts[0]
|
|
assert "lui_widget_btn_txt" in script
|
|
assert args[0] == "提交"
|
|
assert browser.native == []
|
|
assert browser.first == []
|
|
|
|
|
|
def test_click_toolbar_button_fallback_uses_exact_txt():
|
|
browser = _ClickBrowser({"ok": False, "texts": ["暂存"]})
|
|
assert _click_toolbar_button(browser, "提交") is True
|
|
joined = " ".join(browser.native[0])
|
|
assert "lui_widget_btn_txt" in joined
|
|
assert "normalize-space()='提交'" in joined
|
|
assert "text=提交" not in joined
|
|
assert "contains(text()" not in joined
|
|
|
|
|
|
def test_read_contract_number_from_xform_doc_number():
|
|
browser = _FakeBrowser(
|
|
"手机号 13559935238 合同编号",
|
|
script_result="035012026100835435",
|
|
)
|
|
assert _read_contract_number_on_page(browser) == "035012026100835435"
|
|
no = read_contract_number(browser, _ctx(), fd_id="abc")
|
|
assert no == "035012026100835435"
|
|
assert any("method=view" in url and "fdId=abc" in url for url in browser.opened)
|
|
assert all("我的合同" not in url for url in browser.opened)
|
|
|
|
|
|
def test_read_contract_number_requires_fd_id():
|
|
browser = _FakeBrowser("合同编号")
|
|
assert read_contract_number(browser, _ctx(), fd_id="") == ""
|
|
assert browser.opened == []
|
|
|
|
|
|
def test_read_contract_number_skips_placeholder_and_phone():
|
|
browser = _FakeBrowser(
|
|
"手机号\n13559935238\n提交时自动生成",
|
|
script_result="提交时自动生成",
|
|
)
|
|
assert _read_contract_number_on_page(browser) == ""
|
|
|
|
|
|
def test_pick_contract_row_skips_empty_number():
|
|
rows = [
|
|
{
|
|
"no": "",
|
|
"title": "重庆亿昊药业有限公司采购协议20261009",
|
|
"customer": "DW0100046361",
|
|
"href": "kmAgreementApply.do?method=view&fdId=ours",
|
|
}
|
|
]
|
|
assert (
|
|
_pick_contract_row(
|
|
rows,
|
|
fd_id="ours",
|
|
title="重庆亿昊药业有限公司采购协议20261009",
|
|
customer_code="DW0100046361",
|
|
)
|
|
== ""
|
|
)
|
|
|
|
|
|
def test_submit_without_file_uses_placeholder_body():
|
|
ctx = _ctx(submit_mode="submit", source_type="")
|
|
path = _resolve_contract_body_path(ctx)
|
|
assert Path(path).is_file()
|
|
assert Path(path).stat().st_size > 64
|
|
|
|
|
|
def test_dry_run_without_file_skips_body():
|
|
ctx = _ctx(submit_mode="dry_run", source_type="")
|
|
assert _resolve_contract_body_path(ctx) == ""
|