vendor_bill_editable_totals/tests/run_view_tests.py
2025-11-21 18:02:20 +07:00

313 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Standalone test runner for view configuration unit tests.
This allows running tests without the full Odoo test framework.
Tests verify:
- price_subtotal field is editable in vendor bill form
- price_total field is editable in vendor bill form
- Fields are readonly in non-vendor-bill contexts
Requirements tested: 1.1, 2.1, 5.2
"""
import sys
import os
from lxml import etree
def test_view_xml_structure():
"""
Test that the view XML file has the correct structure.
Requirement 5.2: Use proper view inheritance
"""
view_path = os.path.join(
os.path.dirname(__file__),
'../views/account_move_views.xml'
)
if not os.path.exists(view_path):
return False, f"View file not found at {view_path}"
try:
tree = etree.parse(view_path)
root = tree.getroot()
# Check that it's an Odoo XML file
if root.tag != 'odoo':
return False, f"Root tag should be 'odoo', got '{root.tag}'"
# Find the record element
records = root.xpath("//record[@id='view_move_form_editable_totals']")
if not records:
return False, "Could not find record with id 'view_move_form_editable_totals'"
record = records[0]
# Check model
if record.get('model') != 'ir.ui.view':
return False, f"Record model should be 'ir.ui.view', got '{record.get('model')}'"
# Check inherit_id field
inherit_fields = record.xpath(".//field[@name='inherit_id']")
if not inherit_fields:
return False, "Missing inherit_id field"
inherit_ref = inherit_fields[0].get('ref')
if inherit_ref != 'account.view_move_form':
return False, f"inherit_id should reference 'account.view_move_form', got '{inherit_ref}'"
return True, "View XML structure is correct"
except Exception as e:
return False, f"Error parsing XML: {e}"
def test_price_subtotal_field_editable():
"""
Test that price_subtotal field is made editable in the view.
Requirement 1.1: price_subtotal field should be editable in vendor bill form
"""
view_path = os.path.join(
os.path.dirname(__file__),
'../views/account_move_views.xml'
)
try:
tree = etree.parse(view_path)
root = tree.getroot()
# Find the xpath that modifies price_subtotal
xpath_elements = root.xpath(
"//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_subtotal']\"]"
)
if not xpath_elements:
return False, "XPath for price_subtotal field not found in view"
xpath_element = xpath_elements[0]
# Check that readonly attribute is set to 0
readonly_attrs = xpath_element.xpath(".//attribute[@name='readonly']")
if not readonly_attrs:
return False, "Readonly attribute not set for price_subtotal"
if readonly_attrs[0].text != '0':
return False, f"price_subtotal readonly should be '0', got '{readonly_attrs[0].text}'"
# Check that attrs attribute exists for conditional readonly
attrs_elements = xpath_element.xpath(".//attribute[@name='attrs']")
if not attrs_elements:
return False, "Attrs attribute not set for price_subtotal"
attrs_text = attrs_elements[0].text
if 'readonly' not in attrs_text:
return False, "Attrs should contain readonly condition"
if 'draft' not in attrs_text:
return False, "Attrs should reference draft state"
return True, "price_subtotal field is correctly configured as editable"
except Exception as e:
return False, f"Error checking price_subtotal field: {e}"
def test_price_total_field_editable():
"""
Test that price_total field is made editable in the view.
Requirement 2.1: price_total field should be editable in vendor bill form
"""
view_path = os.path.join(
os.path.dirname(__file__),
'../views/account_move_views.xml'
)
try:
tree = etree.parse(view_path)
root = tree.getroot()
# Find the xpath that modifies price_total
xpath_elements = root.xpath(
"//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_total']\"]"
)
if not xpath_elements:
return False, "XPath for price_total field not found in view"
xpath_element = xpath_elements[0]
# Check that readonly attribute is set to 0
readonly_attrs = xpath_element.xpath(".//attribute[@name='readonly']")
if not readonly_attrs:
return False, "Readonly attribute not set for price_total"
if readonly_attrs[0].text != '0':
return False, f"price_total readonly should be '0', got '{readonly_attrs[0].text}'"
# Check that attrs attribute exists for conditional readonly
attrs_elements = xpath_element.xpath(".//attribute[@name='attrs']")
if not attrs_elements:
return False, "Attrs attribute not set for price_total"
attrs_text = attrs_elements[0].text
if 'readonly' not in attrs_text:
return False, "Attrs should contain readonly condition"
if 'draft' not in attrs_text:
return False, "Attrs should reference draft state"
return True, "price_total field is correctly configured as editable"
except Exception as e:
return False, f"Error checking price_total field: {e}"
def test_fields_readonly_conditions():
"""
Test that fields have attrs to make them readonly when state is not draft.
Requirement 5.2: Fields should be readonly in non-draft states
"""
view_path = os.path.join(
os.path.dirname(__file__),
'../views/account_move_views.xml'
)
try:
tree = etree.parse(view_path)
root = tree.getroot()
# Check price_subtotal attrs
xpath_subtotal = root.xpath(
"//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_subtotal']\"]"
)
if not xpath_subtotal:
return False, "price_subtotal xpath not found"
attrs_subtotal = xpath_subtotal[0].xpath(".//attribute[@name='attrs']")
if not attrs_subtotal:
return False, "Attrs not set for price_subtotal"
attrs_text = attrs_subtotal[0].text
if 'readonly' not in attrs_text:
return False, "price_subtotal attrs should contain readonly condition"
if 'parent.state' not in attrs_text:
return False, "price_subtotal attrs should reference parent.state"
# Check price_total attrs
xpath_total = root.xpath(
"//xpath[@expr=\"//field[@name='invoice_line_ids']/tree//field[@name='price_total']\"]"
)
if not xpath_total:
return False, "price_total xpath not found"
attrs_total = xpath_total[0].xpath(".//attribute[@name='attrs']")
if not attrs_total:
return False, "Attrs not set for price_total"
attrs_text = attrs_total[0].text
if 'readonly' not in attrs_text:
return False, "price_total attrs should contain readonly condition"
if 'parent.state' not in attrs_text:
return False, "price_total attrs should reference parent.state"
return True, "Both fields have correct readonly conditions based on state"
except Exception as e:
return False, f"Error checking readonly conditions: {e}"
def test_view_inheritance_pattern():
"""
Test that the view follows Odoo's inheritance best practices.
Requirement 5.2: Use proper view inheritance
"""
view_path = os.path.join(
os.path.dirname(__file__),
'../views/account_move_views.xml'
)
try:
tree = etree.parse(view_path)
root = tree.getroot()
# Check that we're using xpath for modifications
xpaths = root.xpath("//xpath")
if len(xpaths) < 2:
return False, f"Expected at least 2 xpath elements, found {len(xpaths)}"
# Check that we're using position="attributes" for field modifications
for xpath in xpaths:
position = xpath.get('position')
if position != 'attributes':
return False, f"XPath should use position='attributes', got '{position}'"
# Check that we're using attribute elements
attributes = root.xpath("//attribute")
if len(attributes) < 4: # At least 2 fields × 2 attributes each
return False, f"Expected at least 4 attribute elements, found {len(attributes)}"
return True, "View follows proper inheritance patterns"
except Exception as e:
return False, f"Error checking inheritance pattern: {e}"
if __name__ == '__main__':
print("Running View Configuration Unit Tests for vendor-bill-editable-totals")
print("=" * 70)
tests = [
("View XML structure", test_view_xml_structure),
("price_subtotal field editable", test_price_subtotal_field_editable),
("price_total field editable", test_price_total_field_editable),
("Fields readonly conditions", test_fields_readonly_conditions),
("View inheritance pattern", test_view_inheritance_pattern),
]
passed = 0
failed = 0
for test_name, test_func in tests:
print(f"\nTest: {test_name}")
print("-" * 70)
try:
success, message = test_func()
if success:
print(f"✓ PASSED: {message}")
passed += 1
else:
print(f"✗ FAILED: {message}")
failed += 1
except Exception as e:
print(f"✗ FAILED with exception: {e}")
import traceback
traceback.print_exc()
failed += 1
print("\n" + "=" * 70)
print(f"Results: {passed} passed, {failed} failed out of {len(tests)} tests")
if failed == 0:
print("✓ All view configuration tests passed!")
sys.exit(0)
else:
print("✗ Some view configuration tests failed!")
sys.exit(1)