From 2fb0cf9b8a13141db22e70c78aadb173be96b00b Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 08:33:27 -0500 Subject: [PATCH 01/14] Add a query filter for machine elements that match a specific catalog id. --- .../beans/ItemDomainMachineDesignFacade.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java index 6fb841a8e..64708c158 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java @@ -7,6 +7,7 @@ import gov.anl.aps.cdb.common.exceptions.CdbException; import gov.anl.aps.cdb.portal.constants.EntityTypeName; import gov.anl.aps.cdb.portal.constants.ItemDomainName; +import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainCatalog; import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainMachineDesign; import gov.anl.aps.cdb.portal.model.db.entities.ItemElement; import gov.anl.aps.cdb.portal.model.db.entities.ListTbl; @@ -83,6 +84,32 @@ public List searchIOCItems(String searchString) { .getResultList(); } + public List findByAssignedCatalog( + ItemDomainCatalog catalog, String nameFilter) { + if (catalog == null || catalog.getId() == null) { + return java.util.Collections.emptyList(); + } + String query = "SELECT DISTINCT machine FROM ItemDomainMachineDesign machine " + + "JOIN machine.fullItemElementList element " + + "JOIN element.containedItem2 assignment " + + "WHERE element.name IS NULL " + + "AND element.derivedFromItemElement IS NULL " + + "AND (assignment.id = :catalogId " + + "OR assignment.derivedFromItem.id = :catalogId)"; + boolean filterByName = nameFilter != null && !nameFilter.isBlank(); + if (filterByName) { + query += " AND LOWER(machine.name) LIKE :nameFilter"; + } + javax.persistence.TypedQuery typedQuery + = em.createQuery(query, ItemDomainMachineDesign.class) + .setParameter("catalogId", catalog.getId()); + if (filterByName) { + String namePattern = convertWildcards(nameFilter.toLowerCase()); + typedQuery.setParameter("nameFilter", "%" + namePattern + "%"); + } + return typedQuery.getResultList(); + } + public List getMachineDesignTemplates() { return findByDomainAndEntityType( ItemDomainName.machineDesign.getValue(), From 7aded1761391345c33eb7e014c00128a99ff3ccd Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 08:37:23 -0500 Subject: [PATCH 02/14] Add ability to build a tree from a results list. --- .../ItemDomainMachineDesignBaseTreeNode.java | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNode.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNode.java index 00adc7ef4..c6e4eb707 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNode.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNode.java @@ -41,6 +41,7 @@ public abstract class ItemDomainMachineDesignBaseTreeNode rawFilterResults; private List filterResults; private Boolean filterAllNodes; + private boolean includeAllTopLevelResults; protected ItemDomainMachineDesignBaseTreeNode(ItemElement element, MachineNodeConfiguration config, ItemDomainMachineDesignBaseTreeNode parent, boolean setTypeForLevel) { super(element, config, parent); @@ -299,34 +300,49 @@ public void filterChangeEvent(String onComplete) { public void finishFiltering() { if (rawFilterResults != null) { - getChildren().clear(); - - AtomicInteger relevantResults = new AtomicInteger(0); - filterResults = new ArrayList<>(); - // Passed as array to force pass by reference. - Integer[] displayedNodes = new Integer[1]; - displayedNodes[0] = 0; - for (ItemDomainMachineDesign item : rawFilterResults) { - Set additionalParents = new HashSet<>(); - List processParents = new ArrayList<>(); - - ItemDomainMachineDesignBaseTreeNode createTreeFromFilter = createTreeFromFilter(item, true, displayedNodes, additionalParents, null); - processAdditionalFilterParentResult(item, additionalParents, processParents, relevantResults, displayedNodes); - - if (createTreeFromFilter != null) { - relevantResults.incrementAndGet(); - filterResults.add(item); - } - } + buildResultTree(rawFilterResults, true); + } + } - Integer maxSearchNodes = config.getMaxSearchNodes(); - if (displayedNodes[0] > maxSearchNodes) { - clearFilterResults(); - SessionUtility.addErrorMessage("Too many results rows (" + displayedNodes[0] + ")", "Too many results to display. Please provide a more specific search criteria.", true); - } else { - SessionUtility.addInfoMessage("Done", "Showing " + relevantResults + " relevant results.", true); + public void buildResultTree(List results) { + includeAllTopLevelResults = true; + try { + buildResultTree(results, false); + } finally { + includeAllTopLevelResults = false; + } + } + + private void buildResultTree(List results, boolean displayMessages) { + getChildren().clear(); + + AtomicInteger relevantResults = new AtomicInteger(0); + filterResults = new ArrayList<>(); + // Passed as array to force pass by reference. + Integer[] displayedNodes = new Integer[]{0}; + for (ItemDomainMachineDesign item : results) { + Set additionalParents = new HashSet<>(); + List processParents = new ArrayList<>(); + + ItemDomainMachineDesignBaseTreeNode resultNode + = createTreeFromFilter(item, true, displayedNodes, additionalParents, null); + processAdditionalFilterParentResult(item, additionalParents, processParents, + relevantResults, displayedNodes); + + if (resultNode != null) { + relevantResults.incrementAndGet(); + filterResults.add(item); } } + + Integer maxSearchNodes = config.getMaxSearchNodes(); + if (displayMessages && displayedNodes[0] > maxSearchNodes) { + clearFilterResults(); + SessionUtility.addErrorMessage("Too many results rows (" + displayedNodes[0] + ")", + "Too many results to display. Please provide a more specific search criteria.", true); + } else if (displayMessages) { + SessionUtility.addInfoMessage("Done", "Showing " + relevantResults + " relevant results.", true); + } } private void processAdditionalFilterParentResult(ItemDomainMachineDesign item, Set additionalParents, List processParents, AtomicInteger relevantResults, Integer[] displayedNodes) { @@ -436,6 +452,9 @@ private ItemDomainMachineDesignBaseTreeNode createTreeFromFilter(ItemDomainMachi } private boolean isFilterTopLevelItem(ItemDomainMachineDesign item) { + if (includeAllTopLevelResults) { + return topLevelItems.contains(item); + } for (ItemDomainMachineDesign topItem : topLevelItems) { if (topItem.equals(item)) { return topItem.isFilterMachineNode(); From a1b52e30579d60f0d36ad529df2939cce8976192 Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 08:38:03 -0500 Subject: [PATCH 03/14] Expose the new options to be passed into the query. --- .../ItemDomainInventoryController.java | 54 +++++++++++++++++++ .../InventoryMachineAssignmentSelection.java | 20 +++++++ 2 files changed, 74 insertions(+) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java index e99c5cf1c..953366f59 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java @@ -21,8 +21,10 @@ import gov.anl.aps.cdb.portal.model.ItemDomainInventoryLazyDataModel; import gov.anl.aps.cdb.portal.model.ItemDomainMachineDesignTreeNode; import gov.anl.aps.cdb.portal.model.db.beans.ItemDomainInventoryFacade; +import gov.anl.aps.cdb.portal.model.db.beans.ItemDomainMachineDesignFacade; import gov.anl.aps.cdb.portal.model.db.entities.EntityType; import gov.anl.aps.cdb.portal.model.db.entities.Item; +import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainCatalog; import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainInventory; import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainMachineDesign; import gov.anl.aps.cdb.portal.model.db.entities.ItemElement; @@ -64,6 +66,9 @@ public class ItemDomainInventoryController extends ItemDomainInventoryBaseContro @EJB private ItemDomainInventoryFacade itemDomainInventoryFacade; + @EJB + private ItemDomainMachineDesignFacade itemDomainMachineDesignFacade; + private final InventoryMachineAssignmentSelection machineAssignmentSelection = new InventoryMachineAssignmentSelection(); @@ -88,6 +93,33 @@ public void setSelectedMachineAssignmentNode(ItemDomainMachineDesignTreeNode nod machineAssignmentSelection.setReplacementConfirmation(false); } + public boolean isMachineAssignmentMatchingCatalogOnly() { + return machineAssignmentSelection.isMatchingCatalogOnly(); + } + + public void setMachineAssignmentMatchingCatalogOnly(boolean matchingCatalogOnly) { + machineAssignmentSelection.setMatchingCatalogOnly(matchingCatalogOnly); + } + + public String getMachineAssignmentNameFilter() { + return machineAssignmentSelection.getMachineNameFilter(); + } + + public void setMachineAssignmentNameFilter(String nameFilter) { + machineAssignmentSelection.setMachineNameFilter(nameFilter); + } + + public void updateMachineAssignmentCatalogFilter() { + ItemDomainMachineDesign selectedMachine = machineAssignmentSelection.getSelectedMachine(); + ItemDomainMachineDesignTreeNode tree + = ItemDomainMachineDesignController.getInstance().loadMachineDesignRootTreeNode(); + if (machineAssignmentSelection.isMatchingCatalogOnly()) { + tree.buildResultTree(getMachinesMatchingCurrentInventoryCatalog()); + } + machineAssignmentSelection.setTree(tree); + machineAssignmentSelection.selectMachineInTree(selectedMachine); + } + public boolean isMachineAssignmentInstalled() { return machineAssignmentSelection.isInstalled(); } @@ -288,6 +320,28 @@ protected Item getCatalogItem(Item item) { return item instanceof ItemDomainInventory ? item.getDerivedFromItem() : item; } + protected List getMachinesMatchingCurrentInventoryCatalog() { + Item catalog = getCurrentInventoryCatalog(); + if (!(catalog instanceof ItemDomainCatalog)) { + return new ArrayList<>(); + } + return filterMachinesByCatalog( + itemDomainMachineDesignFacade.findByAssignedCatalog( + (ItemDomainCatalog) catalog, machineAssignmentSelection.getMachineNameFilter()), + catalog); + } + + static List filterMachinesByCatalog( + List machines, Item catalog) { + List matches = new ArrayList<>(); + for (ItemDomainMachineDesign machine : machines) { + if (catalog.equals(machine.getCatalogItem())) { + matches.add(machine); + } + } + return matches; + } + protected boolean requiresCatalogReplacementConfirmation(ItemDomainMachineDesign machine) { Item assignment = machine.getAssignedItem(); if (assignment == null || assignment.equals(getCurrent())) { diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java index feead7ac6..468fea85c 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java @@ -10,6 +10,8 @@ public class InventoryMachineAssignmentSelection extends MachineDesignTreeSelection { private boolean installed = true; + private boolean matchingCatalogOnly; + private String machineNameFilter = ""; private boolean replacementConfirmation; private boolean destinationPrepared; private boolean sourceReleased; @@ -24,6 +26,22 @@ public void setInstalled(boolean installed) { this.installed = installed; } + public boolean isMatchingCatalogOnly() { + return matchingCatalogOnly; + } + + public void setMatchingCatalogOnly(boolean matchingCatalogOnly) { + this.matchingCatalogOnly = matchingCatalogOnly; + } + + public String getMachineNameFilter() { + return machineNameFilter; + } + + public void setMachineNameFilter(String machineNameFilter) { + this.machineNameFilter = machineNameFilter == null ? "" : machineNameFilter; + } + public boolean isReplacementConfirmation() { return replacementConfirmation; } @@ -75,6 +93,8 @@ public void resetReplacementProgress() { public void reset() { super.reset(); installed = true; + matchingCatalogOnly = false; + machineNameFilter = ""; replacementConfirmation = false; resetReplacementProgress(); } From 962054a28ad0c65eba12106a7fb972237e75981a Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 08:38:38 -0500 Subject: [PATCH 04/14] Expose the filtering in the UI. --- ...DomainInventoryMachineAssignmentTree.xhtml | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/java/CdbWebPortal/web/views/itemDomainInventory/private/itemDomainInventoryMachineAssignmentTree.xhtml b/src/java/CdbWebPortal/web/views/itemDomainInventory/private/itemDomainInventoryMachineAssignmentTree.xhtml index 44c252a6b..f66b5f012 100644 --- a/src/java/CdbWebPortal/web/views/itemDomainInventory/private/itemDomainInventoryMachineAssignmentTree.xhtml +++ b/src/java/CdbWebPortal/web/views/itemDomainInventory/private/itemDomainInventoryMachineAssignmentTree.xhtml @@ -6,15 +6,48 @@ See LICENSE file. - - - - - - - - - - + +
+ + + + + + + + + + +
+ + + + + + + + + + + + +
From 985f6f7107076ec8aeaca8d45451dce766b20acd Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 08:55:06 -0500 Subject: [PATCH 05/14] handle filtered tree inventory specifc selection --- .../controllers/ItemDomainInventoryController.java | 9 +++++++-- .../InventoryMachineAssignmentSelection.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java index 953366f59..cdef25c10 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java @@ -116,8 +116,13 @@ public void updateMachineAssignmentCatalogFilter() { if (machineAssignmentSelection.isMatchingCatalogOnly()) { tree.buildResultTree(getMachinesMatchingCurrentInventoryCatalog()); } - machineAssignmentSelection.setTree(tree); - machineAssignmentSelection.selectMachineInTree(selectedMachine); + machineAssignmentSelection.setAssignmentTree(tree); + if (selectedMachine != null) { + ItemDomainMachineDesignTreeNode selectedNode + = (ItemDomainMachineDesignTreeNode) ItemDomainMachineDesignController + .expandToSpecificMachineDesignItem(tree, selectedMachine); + machineAssignmentSelection.setSelectedNode(selectedNode); + } } public boolean isMachineAssignmentInstalled() { diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java index 468fea85c..4efefa634 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/view/objects/InventoryMachineAssignmentSelection.java @@ -4,11 +4,14 @@ */ package gov.anl.aps.cdb.portal.view.objects; +import gov.anl.aps.cdb.portal.model.ItemDomainMachineDesignTreeNode; + /** * Inventory-specific state layered on reusable machine-tree selection. */ public class InventoryMachineAssignmentSelection extends MachineDesignTreeSelection { + private ItemDomainMachineDesignTreeNode assignmentTree; private boolean installed = true; private boolean matchingCatalogOnly; private String machineNameFilter = ""; @@ -18,6 +21,16 @@ public class InventoryMachineAssignmentSelection extends MachineDesignTreeSelect private boolean inventoryAssigned; private String replacementError; + @Override + public ItemDomainMachineDesignTreeNode getTree() { + return assignmentTree == null ? super.getTree() : assignmentTree; + } + + public void setAssignmentTree(ItemDomainMachineDesignTreeNode assignmentTree) { + this.assignmentTree = assignmentTree; + setSelectedNode(null); + } + public boolean isInstalled() { return installed; } @@ -92,6 +105,7 @@ public void resetReplacementProgress() { @Override public void reset() { super.reset(); + assignmentTree = null; installed = true; matchingCatalogOnly = false; machineNameFilter = ""; From 1107708145b4f31d3499c87f2da58b662005af65 Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 09:01:02 -0500 Subject: [PATCH 06/14] add missing overrides --- .../gov/anl/aps/cdb/portal/utilities/SessionUtility.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/utilities/SessionUtility.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/utilities/SessionUtility.java index b5bff2942..a7867e158 100644 --- a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/utilities/SessionUtility.java +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/utilities/SessionUtility.java @@ -44,6 +44,10 @@ public static void addErrorMessage(String summary, String detail) { errorMessage = detail; } + public static void addErrorMessage(String summary, String detail, boolean isAjax) { + addErrorMessage(summary, detail); + } + public static void addWarningMessage(String summary, String detail) { } @@ -51,6 +55,10 @@ public static void addInfoMessage(String summary, String detail) { infoMessage = detail; } + public static void addInfoMessage(String summary, String detail, boolean isAjax) { + addInfoMessage(summary, detail); + } + public static void executeRemoteCommand(String command) { remoteCommand = command; } From 299d1738ef30f3dac96473f0fb7ff2edf1c3e21c Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 09:01:23 -0500 Subject: [PATCH 07/14] Add unit tests to test the machine design tree nodes. --- ...emDomainMachineDesignBaseTreeNodeTest.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java new file mode 100644 index 000000000..f6fd86e25 --- /dev/null +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) UChicago Argonne, LLC. All rights reserved. + * See LICENSE file. + */ +package gov.anl.aps.cdb.portal.model; + +import gov.anl.aps.cdb.portal.constants.ItemDomainName; +import gov.anl.aps.cdb.portal.model.db.entities.Domain; +import gov.anl.aps.cdb.portal.model.db.entities.ItemDomainMachineDesign; +import gov.anl.aps.cdb.portal.model.db.entities.ItemElement; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ItemDomainMachineDesignBaseTreeNodeTest { + + @Test + public void normalFilteringStillRespectsDisabledTopLevelMachines() throws Exception { + ItemDomainMachineDesign top = machine(1, "Top"); + ItemDomainMachineDesign child = childMachine(2, "Child", top); + ItemDomainMachineDesignTreeNode root = root(top); + setRawFilterResults(root, Collections.singletonList(child)); + + root.finishFiltering(); + + assertTrue(root.getChildren().isEmpty()); + assertTrue(root.getFilterResults().isEmpty()); + } + + @Test + public void suppliedResultsIgnoreNormalTopLevelSearchSelection() { + ItemDomainMachineDesign top = machine(1, "Top"); + ItemDomainMachineDesign child = childMachine(2, "Child", top); + ItemDomainMachineDesignTreeNode root = root(top); + + root.buildResultTree(Collections.singletonList(child)); + + assertEquals(1, root.getChildren().size()); + ItemDomainMachineDesignTreeNode topNode + = (ItemDomainMachineDesignTreeNode) root.getChildren().get(0); + assertSame(top, containedMachine(topNode)); + assertEquals(1, topNode.getChildren().size()); + assertSame(child, containedMachine( + (ItemDomainMachineDesignTreeNode) topNode.getChildren().get(0))); + assertEquals(Collections.singletonList(child), root.getFilterResults()); + } + + @Test + public void suppliedResultsDeduplicateSharedAncestors() { + ItemDomainMachineDesign top = machine(1, "Top"); + ItemDomainMachineDesign first = childMachine(2, "First", top); + ItemDomainMachineDesign second = childMachine(3, "Second", top); + ItemDomainMachineDesignTreeNode root = root(top); + + root.buildResultTree(Arrays.asList(first, second)); + + assertEquals(1, root.getChildren().size()); + ItemDomainMachineDesignTreeNode topNode + = (ItemDomainMachineDesignTreeNode) root.getChildren().get(0); + assertEquals(2, topNode.getChildren().size()); + assertEquals(Arrays.asList(first, second), root.getFilterResults()); + } + + @Test + public void emptySuppliedResultsProduceEmptyTree() { + ItemDomainMachineDesignTreeNode root = root(machine(1, "Top")); + + root.buildResultTree(Collections.emptyList()); + + assertTrue(root.getChildren().isEmpty()); + assertTrue(root.getFilterResults().isEmpty()); + } + + @Test + public void suppliedResultModeDoesNotAffectLaterNormalFiltering() throws Exception { + ItemDomainMachineDesign top = machine(1, "Top"); + ItemDomainMachineDesign child = childMachine(2, "Child", top); + ItemDomainMachineDesignTreeNode root = root(top); + root.buildResultTree(Collections.singletonList(child)); + setRawFilterResults(root, Collections.singletonList(child)); + + root.finishFiltering(); + + assertTrue(root.getChildren().isEmpty()); + } + + private static ItemDomainMachineDesignTreeNode root(ItemDomainMachineDesign... topItems) { + return new ItemDomainMachineDesignTreeNode( + new ArrayList<>(Arrays.asList(topItems)), + new Domain(ItemDomainName.MACHINE_DESIGN_ID, ItemDomainName.machineDesign.getValue()), + null, + null); + } + + private static ItemDomainMachineDesign machine(int id, String name) { + ItemDomainMachineDesign machine = new ItemDomainMachineDesign(); + machine.setId(id); + machine.setName(name); + machine.setDomain(new Domain(ItemDomainName.MACHINE_DESIGN_ID, + ItemDomainName.machineDesign.getValue())); + machine.setItemElementMemberList(new ArrayList<>()); + + ItemElement self = new ItemElement(); + self.setParentItem(machine); + self.setSortOrder((float) id); + machine.setFullItemElementList(Collections.singletonList(self)); + return machine; + } + + private static ItemDomainMachineDesign childMachine( + int id, String name, ItemDomainMachineDesign parent) { + ItemDomainMachineDesign child = machine(id, name); + ItemElement membership = new ItemElement(); + membership.setParentItem(parent); + membership.setContainedItem(child); + membership.setSortOrder((float) id); + child.setItemElementMemberList(Collections.singletonList(membership)); + return child; + } + + private static ItemDomainMachineDesign containedMachine( + ItemDomainMachineDesignTreeNode node) { + return (ItemDomainMachineDesign) ((ItemElement) node.getData()).getContainedItem(); + } + + private static void setRawFilterResults(ItemDomainMachineDesignTreeNode root, + List results) throws Exception { + Field field = ItemDomainMachineDesignBaseTreeNode.class + .getDeclaredField("rawFilterResults"); + field.setAccessible(true); + field.set(root, results); + } +} From cfe979e20042e168a2fae7ec0e04009ccd6f57f3 Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 09:01:31 -0500 Subject: [PATCH 08/14] Add functionality to test new functionality. --- .../ItemDomainInventoryControllerTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java index 420f8e64d..6a50c151b 100644 --- a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java @@ -17,6 +17,7 @@ import gov.anl.aps.cdb.portal.utilities.SessionUtility; import gov.anl.aps.cdb.portal.view.objects.InventoryMachineAssignmentSelection; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import org.junit.Before; import org.junit.Test; @@ -186,10 +187,32 @@ public void movingInventoryReleasesSourceThenAssignsDestination() { assertTrue(controller.finished); } + @Test + public void filtersMachinesByDirectOrInventoryCatalogAssignment() { + ItemDomainMachineDesign directCatalog = machine(); + directCatalog.setAssignedItem(catalog); + ItemDomainMachineDesign sameCatalogInventory = machine(); + sameCatalogInventory.setAssignedItem(inventory(catalog)); + ItemDomainMachineDesign otherCatalogInventory = machine(); + otherCatalogInventory.setAssignedItem(inventory(catalog(2))); + ItemDomainMachineDesign unassigned = machine(); + + java.util.List matches + = ItemDomainInventoryController.filterMachinesByCatalog( + Arrays.asList(directCatalog, sameCatalogInventory, + otherCatalogInventory, unassigned), catalog); + + assertEquals(Arrays.asList(directCatalog, sameCatalogInventory), matches); + } + @Test public void tracksInstalledAndReplacementProgressAndResetsState() { InventoryMachineAssignmentSelection selection = controller.getMachineAssignmentSelection(); + assertFalse(controller.isMachineAssignmentMatchingCatalogOnly()); + assertEquals("", controller.getMachineAssignmentNameFilter()); controller.setMachineAssignmentInstalled(false); + controller.setMachineAssignmentMatchingCatalogOnly(true); + controller.setMachineAssignmentNameFilter("sector"); controller.setMachineAssignmentReplacementConfirmation(true); selection.setDestinationPrepared(true); selection.setSourceReleased(true); @@ -204,6 +227,8 @@ public void tracksInstalledAndReplacementProgressAndResetsState() { controller.resetMachineAssignmentState(); assertTrue(controller.isMachineAssignmentInstalled()); + assertFalse(controller.isMachineAssignmentMatchingCatalogOnly()); + assertEquals("", controller.getMachineAssignmentNameFilter()); assertNull(controller.getSelectedMachineAssignmentNode()); assertFalse(selection.isDestinationPrepared()); assertFalse(selection.isSourceReleased()); From 3aaec0fa8c55d59379b465845624116fcf313422 Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 09:17:21 -0500 Subject: [PATCH 09/14] Resolve test --- .../portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java index f6fd86e25..b534f42fc 100644 --- a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/model/ItemDomainMachineDesignBaseTreeNodeTest.java @@ -21,6 +21,7 @@ public class ItemDomainMachineDesignBaseTreeNodeTest { @Test public void normalFilteringStillRespectsDisabledTopLevelMachines() throws Exception { ItemDomainMachineDesign top = machine(1, "Top"); + top.updateFilterMachineNode(false); ItemDomainMachineDesign child = childMachine(2, "Child", top); ItemDomainMachineDesignTreeNode root = root(top); setRawFilterResults(root, Collections.singletonList(child)); @@ -78,6 +79,7 @@ public void emptySuppliedResultsProduceEmptyTree() { @Test public void suppliedResultModeDoesNotAffectLaterNormalFiltering() throws Exception { ItemDomainMachineDesign top = machine(1, "Top"); + top.updateFilterMachineNode(false); ItemDomainMachineDesign child = childMachine(2, "Child", top); ItemDomainMachineDesignTreeNode root = root(top); root.buildResultTree(Collections.singletonList(child)); From d9d6e1bd4adddd86d1adbc21d056eb8f7e761c75 Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 10:08:31 -0500 Subject: [PATCH 10/14] Add standardized way of putting together a wildcard search string. --- .../aps/cdb/common/utilities/StringUtility.java | 16 ++++++++++++++++ .../utilities/CdbEntityControllerUtility.java | 14 +------------- .../cdb/common/utilities/StringUtilityTest.java | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/common/utilities/StringUtility.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/common/utilities/StringUtility.java index edc64f7ae..4bae7a31b 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/common/utilities/StringUtility.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/common/utilities/StringUtility.java @@ -18,6 +18,22 @@ public static boolean isEmailAddressValid(String emailAddress){ return emailPattern.matcher(emailAddress).matches(); } + public static Pattern buildWildcardSearchPattern(String searchString, boolean caseInsensitive) { + StringBuilder regex = new StringBuilder(); + for (int i = 0; i < searchString.length(); i++) { + char character = searchString.charAt(i); + if (character == '*') { + regex.append(".*"); + } else if (character == '?') { + regex.append('.'); + } else { + regex.append(Pattern.quote(String.valueOf(character))); + } + } + int flags = caseInsensitive ? Pattern.CASE_INSENSITIVE : 0; + return Pattern.compile(regex.toString(), flags); + } + /** * Verify that two char sequences are the same. * diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/utilities/CdbEntityControllerUtility.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/utilities/CdbEntityControllerUtility.java index cf7dfff81..21c171c37 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/utilities/CdbEntityControllerUtility.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/utilities/CdbEntityControllerUtility.java @@ -414,19 +414,7 @@ public LinkedList performEntitySearch(String searchString, boolean * @return compiled search pattern */ protected Pattern buildSearchPattern(String searchString, boolean caseInsensitive) { - String patternString; - if (searchString.contains("?") || searchString.contains("*")) { - patternString = searchString.replace("*", ".*"); - patternString = patternString.replace("?", "."); - } else { - patternString = Pattern.quote(searchString); - } - - if (caseInsensitive) { - return Pattern.compile(patternString, Pattern.CASE_INSENSITIVE); - } else { - return Pattern.compile(patternString); - } + return StringUtility.buildWildcardSearchPattern(searchString, caseInsensitive); } public PropertyValue preparePropertyTypeValueAdd(EntityType cdbDomainEntity, PropertyType propertyType) { diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/common/utilities/StringUtilityTest.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/common/utilities/StringUtilityTest.java index 8f63a36c0..491379589 100644 --- a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/common/utilities/StringUtilityTest.java +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/common/utilities/StringUtilityTest.java @@ -49,6 +49,20 @@ public void testIsEmailAddressValid() { assertEquals(expResult, result); } + @Test + public void testBuildWildcardSearchPattern() { + assertTrue(StringUtility.buildWildcardSearchPattern("S01*H1", true) + .matcher("s01-RACK-H1").find()); + assertTrue(StringUtility.buildWildcardSearchPattern("Q?", false) + .matcher("S01-Q1:PS").find()); + assertFalse(StringUtility.buildWildcardSearchPattern("Q?", false) + .matcher("S01-q1:PS").find()); + assertTrue(StringUtility.buildWildcardSearchPattern("A.B", false) + .matcher("X-A.B-Y").find()); + assertFalse(StringUtility.buildWildcardSearchPattern("A.B", false) + .matcher("X-AxB-Y").find()); + } + /** * Test of equals method, of class StringUtility. */ From 6ade229865349c122aa3ccdbfa8737d9477696bb Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 10:09:19 -0500 Subject: [PATCH 11/14] Remove name filtering now done on client side --- .../beans/ItemDomainMachineDesignFacade.java | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java index 64708c158..b0aad9907 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/model/db/beans/ItemDomainMachineDesignFacade.java @@ -84,30 +84,21 @@ public List searchIOCItems(String searchString) { .getResultList(); } - public List findByAssignedCatalog( - ItemDomainCatalog catalog, String nameFilter) { + public List findByAssignedCatalog(ItemDomainCatalog catalog) { if (catalog == null || catalog.getId() == null) { return java.util.Collections.emptyList(); } - String query = "SELECT DISTINCT machine FROM ItemDomainMachineDesign machine " + return em.createQuery( + "SELECT DISTINCT machine FROM ItemDomainMachineDesign machine " + "JOIN machine.fullItemElementList element " + "JOIN element.containedItem2 assignment " + "WHERE element.name IS NULL " + "AND element.derivedFromItemElement IS NULL " + "AND (assignment.id = :catalogId " - + "OR assignment.derivedFromItem.id = :catalogId)"; - boolean filterByName = nameFilter != null && !nameFilter.isBlank(); - if (filterByName) { - query += " AND LOWER(machine.name) LIKE :nameFilter"; - } - javax.persistence.TypedQuery typedQuery - = em.createQuery(query, ItemDomainMachineDesign.class) - .setParameter("catalogId", catalog.getId()); - if (filterByName) { - String namePattern = convertWildcards(nameFilter.toLowerCase()); - typedQuery.setParameter("nameFilter", "%" + namePattern + "%"); - } - return typedQuery.getResultList(); + + "OR assignment.derivedFromItem.id = :catalogId)", + ItemDomainMachineDesign.class) + .setParameter("catalogId", catalog.getId()) + .getResultList(); } public List getMachineDesignTemplates() { From 82cf16b3b2df7ac50dd0762c767fbf315d1c86dc Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 10:09:43 -0500 Subject: [PATCH 12/14] Search the result hierarchy for catalog matching results. --- .../ItemDomainInventoryController.java | 49 ++++++++++++++++--- .../ItemDomainInventoryControllerTest.java | 48 +++++++++++++++++- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java index cdef25c10..7a9e94f29 100644 --- a/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java +++ b/src/java/CdbWebPortal/src/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryController.java @@ -6,6 +6,7 @@ import gov.anl.aps.cdb.common.constants.CdbRole; import gov.anl.aps.cdb.common.exceptions.CdbException; +import gov.anl.aps.cdb.common.utilities.StringUtility; import gov.anl.aps.cdb.portal.constants.ItemDomainName; import gov.anl.aps.cdb.portal.import_export.import_.helpers.ImportHelperInventory; import gov.anl.aps.cdb.portal.controllers.extensions.ItemCreateWizardController; @@ -36,7 +37,10 @@ import gov.anl.aps.cdb.portal.view.objects.DomainImportExportInfo; import gov.anl.aps.cdb.portal.view.objects.ImportExportFormatInfo; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; import javax.ejb.EJB; import javax.enterprise.context.SessionScoped; import javax.inject.Named; @@ -330,23 +334,54 @@ protected List getMachinesMatchingCurrentInventoryCatal if (!(catalog instanceof ItemDomainCatalog)) { return new ArrayList<>(); } - return filterMachinesByCatalog( - itemDomainMachineDesignFacade.findByAssignedCatalog( - (ItemDomainCatalog) catalog, machineAssignmentSelection.getMachineNameFilter()), - catalog); + return filterMachinesByCatalogAndHierarchyName( + itemDomainMachineDesignFacade.findByAssignedCatalog((ItemDomainCatalog) catalog), + catalog, machineAssignmentSelection.getMachineNameFilter()); } - static List filterMachinesByCatalog( - List machines, Item catalog) { + static List filterMachinesByCatalogAndHierarchyName( + List machines, Item catalog, String nameFilter) { + Pattern namePattern = createWildcardPattern(nameFilter); List matches = new ArrayList<>(); for (ItemDomainMachineDesign machine : machines) { - if (catalog.equals(machine.getCatalogItem())) { + if (catalog.equals(machine.getCatalogItem()) + && hierarchyNameMatches(machine, namePattern)) { matches.add(machine); } } return matches; } + private static boolean hierarchyNameMatches( + ItemDomainMachineDesign machine, Pattern namePattern) { + if (namePattern == null) { + return true; + } + Set visitedIds = new HashSet<>(); + Set visitedItems = new HashSet<>(); + ItemDomainMachineDesign current = machine; + while (current != null) { + Integer id = current.getId(); + if ((id != null && !visitedIds.add(id)) + || (id == null && !visitedItems.add(current))) { + break; + } + String name = current.getName(); + if (name != null && namePattern.matcher(name).find()) { + return true; + } + current = current.getParentMachineDesign(); + } + return false; + } + + private static Pattern createWildcardPattern(String nameFilter) { + if (nameFilter == null || nameFilter.isBlank()) { + return null; + } + return StringUtility.buildWildcardSearchPattern(nameFilter, true); + } + protected boolean requiresCatalogReplacementConfirmation(ItemDomainMachineDesign machine) { Item assignment = machine.getAssignedItem(); if (assignment == null || assignment.equals(getCurrent())) { diff --git a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java index 6a50c151b..cacc73801 100644 --- a/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java +++ b/tools/developer_tools/code_testing/CdbWebPortalTest/src/test/java/gov/anl/aps/cdb/portal/controllers/ItemDomainInventoryControllerTest.java @@ -198,13 +198,42 @@ public void filtersMachinesByDirectOrInventoryCatalogAssignment() { ItemDomainMachineDesign unassigned = machine(); java.util.List matches - = ItemDomainInventoryController.filterMachinesByCatalog( + = ItemDomainInventoryController.filterMachinesByCatalogAndHierarchyName( Arrays.asList(directCatalog, sameCatalogInventory, - otherCatalogInventory, unassigned), catalog); + otherCatalogInventory, unassigned), catalog, ""); assertEquals(Arrays.asList(directCatalog, sameCatalogInventory), matches); } + @Test + public void filtersCatalogMatchesByMachineOrAncestorName() { + ItemDomainMachineDesign top = machine("S01 Area H1"); + ItemDomainMachineDesign housing = childMachine("Rack", top); + ItemDomainMachineDesign matchingLeaf = childMachine("Power Supply", housing); + matchingLeaf.setAssignedItem(catalog); + ItemDomainMachineDesign excludedLeaf = childMachine("Other Supply", machine("S02 Area")); + excludedLeaf.setAssignedItem(catalog); + ItemDomainMachineDesign wrongCatalog = childMachine("S01 H1 Wrong", top); + wrongCatalog.setAssignedItem(catalog(2)); + + java.util.List matches + = ItemDomainInventoryController.filterMachinesByCatalogAndHierarchyName( + Arrays.asList(matchingLeaf, excludedLeaf, wrongCatalog), + catalog, "S01*H1"); + + assertEquals(Collections.singletonList(matchingLeaf), matches); + } + + @Test + public void catalogMatchNameFilterMatchesLeafAndQuestionWildcard() { + ItemDomainMachineDesign leaf = machine("S01-Q1 Supply"); + leaf.setAssignedItem(catalog); + + assertEquals(Collections.singletonList(leaf), + ItemDomainInventoryController.filterMachinesByCatalogAndHierarchyName( + Collections.singletonList(leaf), catalog, "s01-q?")); + } + @Test public void tracksInstalledAndReplacementProgressAndResetsState() { InventoryMachineAssignmentSelection selection = controller.getMachineAssignmentSelection(); @@ -265,15 +294,30 @@ private static ItemDomainInventory inventory(ItemDomainCatalog catalogItem) { } private ItemDomainMachineDesign machine() { + return machine(null); + } + + private ItemDomainMachineDesign machine(String name) { ItemDomainMachineDesign machine = new ItemDomainMachineDesign(); machine.setId(nextMachineId++); + machine.setName(name); ItemElement selfElement = new ItemElement(); selfElement.setParentItem(machine); machine.setFullItemElementList(Collections.singletonList(selfElement)); setOwner(machine, SessionUtility.getUser()); + machine.setItemElementMemberList(new ArrayList<>()); return machine; } + private ItemDomainMachineDesign childMachine(String name, ItemDomainMachineDesign parent) { + ItemDomainMachineDesign child = machine(name); + ItemElement membership = new ItemElement(); + membership.setParentItem(parent); + membership.setContainedItem(child); + child.setItemElementMemberList(Collections.singletonList(membership)); + return child; + } + private ItemDomainMachineDesign house(ItemDomainInventory item, boolean installed) { ItemDomainMachineDesign machine = machine(); machine.setAssignedItem(item); From 5e94d79789d1d02aa0899adae92aa09518863fcd Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 10:19:45 -0500 Subject: [PATCH 13/14] add selenium test to validate invalid credentials. --- .../PythonSeleniumTest/tests/test_portal.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/developer_tools/portal_testing/PythonSeleniumTest/tests/test_portal.py b/tools/developer_tools/portal_testing/PythonSeleniumTest/tests/test_portal.py index fd890bfff..950ff1e98 100644 --- a/tools/developer_tools/portal_testing/PythonSeleniumTest/tests/test_portal.py +++ b/tools/developer_tools/portal_testing/PythonSeleniumTest/tests/test_portal.py @@ -5,8 +5,11 @@ See LICENSE file. """ +from selenium.webdriver.common.by import By + from cdbSeleniumModules.catalog import Catalog from cdbSeleniumModules.inventory import Inventory +from cdbSeleniumModules.portal import LOGIN_BUTTON_ID, LOGOUT_BUTTON_ID from tests.base import CdbPortalTestCase @@ -17,6 +20,22 @@ def setUp(self): self.catalog = Catalog(self.driver) self.inventory = Inventory(self.driver) + def test_invalid_login_credentials(self): + self.portal.logout() + self.portal._click_on_id(LOGIN_BUTTON_ID) + + self.portal._type_in_id('loginForm:username', 'cdb') + self.portal._type_in_id('loginForm:password', 'invalid-password') + self.portal._click_on_id('loginForm:loginButton') + + self.assertIn( + 'Username/password combination could not be verified.', + self.portal._wait_for_visible_xpath( + "//*[contains(@class, 'ui-growl-message') " + "and contains(., 'Invalid Credentials')]").text) + self.assertTrue(self.portal._wait_for_id(LOGIN_BUTTON_ID).is_displayed()) + self.assertFalse(self.driver.find_elements(By.ID, LOGOUT_BUTTON_ID)) + def test_login_and_logout(self): self.catalog.navigate_to_catalog_list() self.portal.logout() From adef829bebe9d0620f975179f968b6d12cffc89d Mon Sep 17 00:00:00 2001 From: Dariusz Jarosz Date: Wed, 28 Oct 2026 10:34:23 -0500 Subject: [PATCH 14/14] Add dedicated data for the inventory assignment test --- db/sql/test/populate_entity_info.sql | 11 ++++++++++- db/sql/test/populate_item.sql | 8 +++++++- db/sql/test/populate_item_element.sql | 11 ++++++++++- db/sql/test/populate_item_item_project.sql | 8 +++++++- .../cdbSeleniumModules/inventory.py | 14 +++++++++----- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/db/sql/test/populate_entity_info.sql b/db/sql/test/populate_entity_info.sql index 0de8ce640..cfc1add89 100644 --- a/db/sql/test/populate_entity_info.sql +++ b/db/sql/test/populate_entity_info.sql @@ -188,6 +188,15 @@ INSERT INTO `entity_info` VALUES (186,1,1,1,'2025-04-02 08:43:31',1,'2025-04-02 08:43:31',1,NULL,NULL), (187,1,1,1,'2026-10-27 10:43:43',1,'2026-10-27 10:44:07',1,NULL,NULL), (188,1,1,1,'2026-10-27 10:44:00',1,'2026-10-27 10:44:00',1,NULL,NULL), -(189,1,1,1,'2026-10-27 10:44:00',1,'2026-10-27 10:44:19',1,NULL,NULL); +(189,1,1,1,'2026-10-27 10:44:00',1,'2026-10-27 10:44:19',1,NULL,NULL), +(190,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(191,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(192,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(193,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(194,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(195,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(196,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(197,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL), +(198,1,1,1,'2026-10-28 09:00:00',1,'2026-10-28 09:00:00',1,NULL,NULL); /*!40000 ALTER TABLE `entity_info` ENABLE KEYS */; UNLOCK TABLES; diff --git a/db/sql/test/populate_item.sql b/db/sql/test/populate_item.sql index bdd687ec2..47620dc1f 100644 --- a/db/sql/test/populate_item.sql +++ b/db/sql/test/populate_item.sql @@ -128,6 +128,12 @@ INSERT INTO `item` VALUES (126,6,'Item with Ports 3',NULL,NULL,NULL,NULL), (127,2,'Item With Connectors 3',NULL,'','',NULL), (128,6,'Another Top Level ',NULL,NULL,NULL,NULL), -(129,6,'Inventory Assignment Test Destination',NULL,NULL,NULL,NULL); +(129,6,'Inventory Assignment Test Destination',NULL,NULL,NULL,NULL), +(130,3,'Machine Assignment Test Inventory',1,NULL,NULL,NULL), +(131,6,'Machine Assignment Test Source',NULL,NULL,NULL,NULL), +(132,6,'Machine Assignment Test Component 1',NULL,NULL,NULL,NULL), +(133,6,'Machine Assignment Test Component 2',NULL,NULL,NULL,NULL), +(134,6,'Machine Assignment Replacement Root',NULL,NULL,NULL,NULL), +(135,6,'Machine Assignment Replacement Destination',NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `item` ENABLE KEYS */; UNLOCK TABLES; diff --git a/db/sql/test/populate_item_element.sql b/db/sql/test/populate_item_element.sql index 1cf2d91c5..42ca500c1 100644 --- a/db/sql/test/populate_item_element.sql +++ b/db/sql/test/populate_item_element.sql @@ -188,6 +188,15 @@ INSERT INTO `item_element` VALUES (186,NULL,127,NULL,NULL,NULL,NULL,0,1,'',NULL,186), (187,NULL,128,NULL,NULL,NULL,NULL,0,1,'',NULL,187), (188,'E1',128,129,NULL,NULL,NULL,0,1,NULL,0.00,188), -(189,NULL,129,NULL,40,NULL,NULL,0,1,'',NULL,189); +(189,NULL,129,NULL,40,NULL,NULL,0,1,'',NULL,189), +(190,NULL,130,NULL,NULL,NULL,NULL,0,1,'',NULL,190), +(191,NULL,131,NULL,NULL,NULL,NULL,0,1,'',NULL,191), +(192,NULL,132,NULL,1,NULL,NULL,0,1,'',NULL,192), +(193,'E1',131,132,NULL,NULL,NULL,0,1,NULL,0.00,193), +(194,NULL,133,NULL,1,NULL,NULL,0,1,'',NULL,194), +(195,'E2',131,133,NULL,NULL,NULL,0,1,NULL,1.00,195), +(196,NULL,134,NULL,NULL,NULL,NULL,0,1,'',NULL,196), +(197,NULL,135,NULL,40,NULL,NULL,0,1,'',NULL,197), +(198,'E1',134,135,NULL,NULL,NULL,0,1,NULL,0.00,198); /*!40000 ALTER TABLE `item_element` ENABLE KEYS */; UNLOCK TABLES; diff --git a/db/sql/test/populate_item_item_project.sql b/db/sql/test/populate_item_item_project.sql index d4e081c2f..d6f698247 100644 --- a/db/sql/test/populate_item_item_project.sql +++ b/db/sql/test/populate_item_item_project.sql @@ -120,6 +120,12 @@ INSERT INTO `item_item_project` VALUES (126,3), (127,3), (128,3), -(129,3); +(129,3), +(130,3), +(131,3), +(132,3), +(133,3), +(134,3), +(135,3); /*!40000 ALTER TABLE `item_item_project` ENABLE KEYS */; UNLOCK TABLES; diff --git a/tools/developer_tools/portal_testing/PythonSeleniumTest/cdbSeleniumModules/inventory.py b/tools/developer_tools/portal_testing/PythonSeleniumTest/cdbSeleniumModules/inventory.py index bde6b77f9..048359763 100644 --- a/tools/developer_tools/portal_testing/PythonSeleniumTest/cdbSeleniumModules/inventory.py +++ b/tools/developer_tools/portal_testing/PythonSeleniumTest/cdbSeleniumModules/inventory.py @@ -22,10 +22,14 @@ class Inventory(ItemBase): ASSIGNMENT_TREE_ID = VIEW_FORM_NAME + ':itemMachineDesignListDataTableinventoryMachineAssignment' REPLACEMENT_DIALOG_ID = VIEW_FORM_NAME + ':machineAssignmentReplacementConfirmDialog' - TEST_MACHINE = ['Test Machine'] - TEST_MACHINE_COMPONENT_1 = ['Test Machine', 'Component 1'] - TEST_MACHINE_COMPONENT_2 = ['Test Machine', 'Another Needed Component'] - TEST_REPLACEMENT_DESTINATION = ['Another Top Level', 'Inventory Assignment Test Destination'] + TEST_INVENTORY_ID = 130 + TEST_MACHINE_COMPONENT_1 = [ + 'Machine Assignment Test Source', 'Machine Assignment Test Component 1'] + TEST_MACHINE_COMPONENT_2 = [ + 'Machine Assignment Test Source', 'Machine Assignment Test Component 2'] + TEST_REPLACEMENT_DESTINATION = [ + 'Machine Assignment Replacement Root', + 'Machine Assignment Replacement Destination'] def navigate_to_inventory_list(self): self._navigate_to_dropdown('inventoryDropdownButton', 'componentInstanceButton', '%s/list' % self.VIEW_BASE_NAME) @@ -51,7 +55,7 @@ def test_inventory_detail_page(self, test): def test_machine_assignment_acceptance(self, test): """Acceptance coverage for inventory-side machine assignment.""" - self._open_inventory_by_id(41) + self._open_inventory_by_id(self.TEST_INVENTORY_ID) # Selection and Installed/Planned state are available from Housing edit. self._open_machine_assignment()