@@ -1529,3 +1529,157 @@ def test_user_sees_complex_product_sections_with_extra_fields_preserved(self, mo
15291529 assert product ["Custom Field 1" ] == "Custom Value 1"
15301530 assert product ["Custom Field 2" ] == "Custom Value 2"
15311531 assert product ["Custom Field 3" ] == "Custom Value 3"
1532+
1533+ def test_user_sees_notes_field_always_last_in_component (self , model_instance ) -> None :
1534+ """
1535+ User sees the Notes field displayed at the bottom of every component editor section.
1536+
1537+ GIVEN: A component where "Notes" appears before other fields
1538+ WHEN: The system processes the component data
1539+ THEN: "Notes" is moved to be the very last field in that component
1540+ AND: All other field values and their order are preserved
1541+ """
1542+ # Arrange: Component with Notes appearing before connection data
1543+ existing_components = {
1544+ "Telemetry" : {
1545+ "Notes" : "SiK radio on Telem1" ,
1546+ "Product" : {"Manufacturer" : "RFDesign" , "Model" : "RFD900x" },
1547+ "FC Connection" : {"Type" : "SERIAL1" , "Protocol" : "MAVLink2" },
1548+ }
1549+ }
1550+
1551+ # Act
1552+ result = model_instance ._reorder_components (existing_components )
1553+
1554+ # Assert: Notes is last
1555+ fields = list (result ["Telemetry" ].keys ())
1556+ assert fields [- 1 ] == "Notes" , "Notes should be the last field in the component"
1557+ assert result ["Telemetry" ]["Notes" ] == "SiK radio on Telem1"
1558+ # Other fields are preserved in their original relative order
1559+ assert fields .index ("Product" ) < fields .index ("FC Connection" )
1560+
1561+ def test_user_sees_notes_field_last_when_already_at_end (self , model_instance ) -> None :
1562+ """
1563+ User sees Notes remain last when it is already the last field.
1564+
1565+ GIVEN: A component where "Notes" is already the last field
1566+ WHEN: The system processes the component data
1567+ THEN: "Notes" stays last and all other fields are untouched
1568+ """
1569+ # Arrange: Notes already last
1570+ existing_components = {
1571+ "GNSS Receiver" : {
1572+ "Product" : {"Manufacturer" : "u-blox" , "Model" : "NEO-M8N" },
1573+ "FC Connection" : {"Type" : "SERIAL3" , "Protocol" : "AUTO" },
1574+ "Notes" : "Primary GPS" ,
1575+ }
1576+ }
1577+
1578+ # Act
1579+ result = model_instance ._reorder_components (existing_components )
1580+
1581+ # Assert
1582+ fields = list (result ["GNSS Receiver" ].keys ())
1583+ assert fields [- 1 ] == "Notes"
1584+ assert result ["GNSS Receiver" ]["Notes" ] == "Primary GPS"
1585+
1586+ def test_user_sees_components_without_notes_field_unaffected (self , model_instance ) -> None :
1587+ """
1588+ User sees components that have no Notes field remain completely unaffected by Notes ordering.
1589+
1590+ GIVEN: Components with no Notes field
1591+ WHEN: The system processes the component data
1592+ THEN: All component fields are preserved without modification
1593+ """
1594+ # Arrange
1595+ existing_components = {
1596+ "Motors" : {
1597+ "Product" : {"Manufacturer" : "T-Motor" , "Model" : "F60 Pro IV" },
1598+ "Specifications" : {"Poles" : 14 },
1599+ },
1600+ "Propellers" : {
1601+ "Product" : {"Manufacturer" : "HQProp" , "Model" : "5.1x4.6" },
1602+ "Specifications" : {"Diameter_inches" : 5.1 },
1603+ },
1604+ }
1605+
1606+ # Act
1607+ result = model_instance ._reorder_components (existing_components )
1608+
1609+ # Assert: No Notes key injected, all fields unchanged
1610+ assert "Notes" not in result ["Motors" ]
1611+ assert "Notes" not in result ["Propellers" ]
1612+ assert list (result ["Motors" ].keys ()) == ["Product" , "Specifications" ]
1613+ assert list (result ["Propellers" ].keys ()) == ["Product" , "Specifications" ]
1614+
1615+ def test_user_sees_notes_last_across_all_components_simultaneously (self , model_instance ) -> None :
1616+ """
1617+ User sees Notes consistently last in every component when multiple components are processed together.
1618+
1619+ GIVEN: Multiple components each with Notes in different positions
1620+ WHEN: The system processes all component data at once
1621+ THEN: Notes is the last field in every component that has it
1622+ AND: Components without Notes are unaffected
1623+ """
1624+ # Arrange: Multiple components with Notes in varying positions
1625+ existing_components = {
1626+ "Flight Controller" : {
1627+ "Notes" : "flight controller notes" ,
1628+ "Product" : {"Manufacturer" : "Holybro" , "Model" : "Pixhawk 6C" },
1629+ "Firmware" : {"Type" : "ArduCopter" , "Version" : "4.6.x" },
1630+ },
1631+ "Battery" : {
1632+ "Product" : {"Manufacturer" : "Tattu" },
1633+ "Notes" : "battery notes" ,
1634+ "Specifications" : {"Chemistry" : "Lipo" },
1635+ },
1636+ "ESC" : {
1637+ "Product" : {"Manufacturer" : "Mamba" },
1638+ "FC->ESC Connection" : {"Type" : "Main Out" , "Protocol" : "DShot600" },
1639+ "ESC->FC Telemetry" : {"Type" : "Main Out" , "Protocol" : "BDShot" },
1640+ "Notes" : "esc notes" ,
1641+ },
1642+ "Motors" : {
1643+ "Specifications" : {"Poles" : 14 },
1644+ # No Notes field
1645+ },
1646+ }
1647+
1648+ # Act
1649+ result = model_instance ._reorder_components (existing_components )
1650+
1651+ # Assert: Notes is last in every component that has it
1652+ for component_name in ["Flight Controller" , "Battery" , "ESC" ]:
1653+ fields = list (result [component_name ].keys ())
1654+ assert fields [- 1 ] == "Notes" , f"Notes should be last in { component_name } , got: { fields } "
1655+
1656+ # Motors has no Notes and should be unaffected
1657+ assert "Notes" not in result ["Motors" ]
1658+
1659+ def test_system_moves_notes_last_during_update_json_structure (self , model_instance ) -> None :
1660+ """
1661+ System moves Notes to be the last field during a full update_json_structure call.
1662+
1663+ GIVEN: A stored vehicle_components.json where Notes appears before connection data
1664+ WHEN: update_json_structure is called on load
1665+ THEN: Notes is the last field in the affected component after migration
1666+ """
1667+ # Arrange: Inject raw data with Notes in the middle
1668+ model_instance ._data = {
1669+ "Format version" : 1 ,
1670+ "Components" : {
1671+ "RC Receiver" : {
1672+ "Notes" : "FrSky receiver" ,
1673+ "Product" : {"Manufacturer" : "FrSky" , "Model" : "R-XSR" },
1674+ "FC Connection" : {"Type" : "RCin/SBUS" , "Protocol" : "SBUS" },
1675+ }
1676+ },
1677+ }
1678+
1679+ # Act
1680+ model_instance .update_json_structure ()
1681+
1682+ # Assert
1683+ rc_fields = list (model_instance ._data ["Components" ]["RC Receiver" ].keys ())
1684+ assert rc_fields [- 1 ] == "Notes" , f"Notes should be last after update_json_structure, got: { rc_fields } "
1685+ assert model_instance ._data ["Components" ]["RC Receiver" ]["Notes" ] == "FrSky receiver"
0 commit comments