diff --git a/Common/pom.xml b/Common/pom.xml
index 6b76a6b8..d49a9fe4 100644
--- a/Common/pom.xml
+++ b/Common/pom.xml
@@ -5,7 +5,7 @@
com.ingingenious-playwright
- 2.3.1
+ 2.4Common
diff --git a/Datalib/pom.xml b/Datalib/pom.xml
index 0ce83e3c..d414f53e 100644
--- a/Datalib/pom.xml
+++ b/Datalib/pom.xml
@@ -4,7 +4,7 @@
com.ingingenious-playwright
- 2.3.1
+ 2.4ingenious-datalibjar
diff --git a/Datalib/src/main/java/com/ing/datalib/component/Project.java b/Datalib/src/main/java/com/ing/datalib/component/Project.java
index 18ec1bfc..1f099678 100644
--- a/Datalib/src/main/java/com/ing/datalib/component/Project.java
+++ b/Datalib/src/main/java/com/ing/datalib/component/Project.java
@@ -11,9 +11,12 @@
import com.ing.datalib.util.data.FileScanner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.ing.datalib.or.web.WebOR.ORScope;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.Set;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -23,8 +26,21 @@
import javax.swing.table.TableModel;
/**
+ * Represents an automation project and acts as the central entry point for loading, managing,
+ * and persisting project data from disk.
+ *
+ * A {@code Project} encapsulates the project’s filesystem location and name, and maintains the
+ * in-memory model of core assets such as scenarios (TestPlan), releases/test sets (TestLab),
+ * environment test data, project settings, and the {@link ObjectRepository}. It supports loading
+ * and reloading from disk, saving all managed components, and producing table models for UI
+ * components via {@code getTableModelFor(...)}.
+ *
*
- *
+ *
+ * The class also provides refactoring utilities that propagate renames across scenarios, releases,
+ * and test data (e.g., scenario/test case renames, page/object reference updates, and test data
+ * renames), including scope-aware refactoring for Object Repository references where applicable.
+ *
*/
public class Project {
@@ -211,7 +227,9 @@ public Boolean rename(String newName) {
}
}
getObjectRepository().getWebOR().setName(newName);
+ getObjectRepository().getWebSharedOR().setName(newName);
getObjectRepository().getMobileOR().setName(newName);
+ getObjectRepository().getMobileSharedOR().setName(newName);
return true;
}
return false;
@@ -395,7 +413,7 @@ public void refactorTestCaseScenario(String testCaseName, String oldScenarioName
.ifPresent(scn -> scn.setName(newScenarioName));
});
}
-
+
public void refactorObjectName(String pageName, String oldName, String newName) {
for (Scenario scenario : scenarios) {
scenario.refactorObjectName(pageName, oldName, newName);
@@ -408,12 +426,57 @@ public void refactorObjectName(String oldpageName, String oldObjName, String new
}
}
+ /**
+ * Renames an object reference on the given page for the specified OR scope across the project,
+ * by delegating to all scenarios.
+ *
+ * @param scope OR scope to match (e.g., shared vs project)
+ * @param pageName page (screen) name containing the object reference
+ * @param oldName existing object name to replace
+ * @param newName new object name to apply
+ */
+ public void refactorObjectName(ORScope scope, String pageName, String oldName, String newName) {
+ for (Scenario scenario : scenarios) {
+ scenario.refactorObjectName(scope, pageName, oldName, newName);
+ }
+ }
+
public void refactorPageName(String oldPageName, String newPageName) {
for (Scenario scenario : scenarios) {
scenario.refactorPageName(oldPageName, newPageName);
}
}
+ /**
+ * Refactors (renames) a page reference across the project for a given Object Repository scope.
+ *
+ * In addition to delegating the rename for the raw page names, this method also renames
+ * scope-qualified page names using the convention:
+ *
+ *
{@code "[Shared] " + pageName} when scope is {@code ORScope.SHARED}
+ *
{@code "[Project] " + pageName} otherwise
+ *
+ * For each {@link Scenario}, it applies both:
+ * {@code scenario.refactorPageName(oldPageName, newPageName)} and
+ * {@code scenario.refactorPageName(oldScoped, newScoped)}.
+ *
+ *
+ * @param scope the Object Repository scope used to derive the scoped page name prefix
+ * @param oldPageName the original page name to be replaced
+ * @param newPageName the new page name to apply
+ *
+ * @implNote This method performs two refactors per scenario: one for the plain page name and one
+ * for the derived scoped form (e.g., {@code "[Shared] Login"}).
+ */
+ public void refactorPageName(ORScope scope, String oldPageName, String newPageName) {
+ String oldScoped = scope == ORScope.SHARED ? "[Shared] " + oldPageName : "[Project] " + oldPageName;
+ String newScoped = scope == ORScope.SHARED ? "[Shared] " + newPageName : "[Project] " + newPageName;
+ for (Scenario scenario : scenarios) {
+ scenario.refactorPageName(oldPageName, newPageName);
+ scenario.refactorPageName(oldScoped, newScoped);
+ }
+ }
+
public void refactorTestData(String oldTDName, String newTDName) {
for (Scenario scenario : scenarios) {
scenario.refactorTestData(oldTDName, newTDName);
@@ -434,6 +497,23 @@ public List getImpactedObjectTestCases(String pageName, String objectN
return impactedTestCases;
}
+ public List getImpactedObjectTestCases(ORScope scope, String pageName, String objectName) {
+ Set impacted = new LinkedHashSet<>();
+ String scopedPageName = null;
+ if (scope != null) {
+ scopedPageName = (scope == ORScope.SHARED)
+ ? "[Shared] " + pageName
+ : "[Project] " + pageName;
+ }
+ for (Scenario scenario : scenarios) {
+ impacted.addAll(scenario.getImpactedObjectTestCases(pageName, objectName));
+ if (scopedPageName != null) {
+ impacted.addAll(scenario.getImpactedObjectTestCases(scopedPageName, objectName));
+ }
+ }
+ return new ArrayList<>(impacted);
+ }
+
public List getImpactedTestCaseTestCases(String scenarioName, String testCaseName) {
List impactedTestCases = new ArrayList<>();
for (Scenario scenario : scenarios) {
@@ -520,5 +600,4 @@ private static DataItem fromTS(TestSet ts) {
}
}
}
-
-}
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/component/Scenario.java b/Datalib/src/main/java/com/ing/datalib/component/Scenario.java
index 25adef32..2e9a36be 100644
--- a/Datalib/src/main/java/com/ing/datalib/component/Scenario.java
+++ b/Datalib/src/main/java/com/ing/datalib/component/Scenario.java
@@ -2,13 +2,25 @@
package com.ing.datalib.component;
import com.ing.datalib.component.utils.FileUtils;
+import com.ing.datalib.or.web.WebOR.ORScope;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
+ * Represents a scenario within a project’s TestPlan and serves as a container for related test cases.
+ *
+ * A {@code Scenario} is backed by a filesystem folder under {@code /TestPlan/},
+ * automatically loads its {@link TestCase} CSV files on construction, and exposes methods for adding,
+ * removing, loading, and saving test cases.
+ *
*
- *
+ *
+ * The class also implements {@code DataModel} table-model behavior for UI consumption by presenting
+ * a scenario-level view of non-reusable test cases, and provides refactoring and impact-analysis helpers
+ * that delegate to contained test cases (e.g., object/page/test data reference updates and impacted test
+ * case discovery).
+ *
*/
public class Scenario extends DataModel {
@@ -279,6 +291,21 @@ public void refactorObjectName(String oldpageName, String oldObjName, String new
}
}
+ /**
+ * Renames an object reference on the given page for the specified OR scope within this scenario,
+ * by delegating to all test cases.
+ *
+ * @param scope OR scope to match (e.g., shared vs project)
+ * @param pageName page (screen) name containing the object reference
+ * @param oldName existing object name to replace
+ * @param newName new object name to apply
+ */
+ public void refactorObjectName(ORScope scope, String pageName, String oldName, String newName) {
+ for (TestCase testCase : testCases) {
+ testCase.refactorObjectName(scope, pageName, oldName, newName);
+ }
+ }
+
public void refactorPageName(String oldPageName, String newPageName) {
for (TestCase testCase : testCases) {
testCase.refactorPageName(oldPageName, newPageName);
@@ -350,5 +377,4 @@ public Boolean delete() {
}
return false;
}
-
-}
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/component/TestCase.java b/Datalib/src/main/java/com/ing/datalib/component/TestCase.java
index c0332c6e..a22eb8a9 100644
--- a/Datalib/src/main/java/com/ing/datalib/component/TestCase.java
+++ b/Datalib/src/main/java/com/ing/datalib/component/TestCase.java
@@ -4,6 +4,7 @@
import com.ing.datalib.component.TestStep.HEADERS;
import com.ing.datalib.component.utils.FileUtils;
import com.ing.datalib.component.utils.SaveListener;
+import com.ing.datalib.or.web.WebOR.ORScope;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
@@ -18,8 +19,21 @@
import org.apache.commons.csv.CSVRecord;
/**
+ * Represents a test case composed of ordered {@link TestStep} entries and implements a table model
+ * suitable for direct editing in UI components.
+ *
+ * A {@code TestCase} belongs to a {@link Scenario}, loads and persists its steps from/to a CSV file,
+ * and supports common editing operations such as inserting, removing, moving, replicating steps,
+ * clearing values, toggling comments/breakpoints, and bulk removal. Save state is tracked and propagated
+ * via a {@link SaveListener}.
+ *
*
- *
+ *
+ * The class also supports creating and managing reusable test cases (represented as “Execute” steps),
+ * provides utilities for refactoring references (scenario/test case reuse links, object/page names,
+ * test data and columns—including scope-aware OR references), and can report impact when a given object,
+ * reusable, or test data reference is used.
+ *
*/
public class TestCase extends DataModel {
@@ -557,7 +571,11 @@ public void refactorObjectName(String pageName, String oldName, String newName)
Boolean clearOnExit = getTestSteps().isEmpty();
loadTableModel();
for (TestStep testStep : testSteps) {
- if (testStep.getReference().equals(pageName) && testStep.getObject().equals(oldName)) {
+ String ref = Objects.toString(testStep.getReference(), "");
+ String obj = Objects.toString(testStep.getObject(), "");
+ String normalizedRef = normalizePageRef(ref);
+
+ if (normalizedRef.equals(pageName) && obj.equals(oldName)) {
testStep.setObject(newName);
}
}
@@ -566,22 +584,85 @@ public void refactorObjectName(String pageName, String oldName, String newName)
getTestSteps().clear();
}
}
-
+
public void refactorObjectName(String oldpageName, String oldObjName, String newPageName, String newObjName) {
Boolean clearOnExit = getTestSteps().isEmpty();
loadTableModel();
+
for (TestStep testStep : testSteps) {
- if (testStep.getReference().equals(oldpageName) && testStep.getObject().equals(oldObjName)) {
+ String ref = normalizePageRef(Objects.toString(testStep.getReference(), ""));
+ String obj = Objects.toString(testStep.getObject(), "");
+ if (ref.equals(oldpageName) && obj.equals(oldObjName)) {
testStep.setObject(newObjName);
testStep.setReference(newPageName);
}
}
+
if (clearOnExit) {
save();
getTestSteps().clear();
}
}
+ /**
+ * Renames an object reference on the given page within this test case, restricted to the specified OR scope.
+ * A step matches when its reference has the expected scope prefix and its normalized page name equals {@code pageName}.
+ *
+ * @param scope OR scope to match (e.g., {@code PROJECT} or {@code SHARED})
+ * @param pageName page (screen) name (without scope prefix) to match
+ * @param oldName existing object name to replace
+ * @param newName new object name to apply
+ */
+ public void refactorObjectName(ORScope scope, String pageName, String oldName, String newName) {
+ Boolean clearOnExit = getTestSteps().isEmpty();
+ loadTableModel();
+ for (TestStep testStep : testSteps) {
+ String refRaw = Objects.toString(testStep.getReference(), "");
+ String obj = Objects.toString(testStep.getObject(), "");
+ boolean scopedMatch = matchesScope(refRaw, scope) && normalizePageRef(refRaw).equals(pageName);
+ if (scopedMatch && obj.equals(oldName)) {
+ testStep.setObject(newName);
+ }
+ }
+ if (clearOnExit) {
+ save();
+ getTestSteps().clear();
+ }
+ }
+
+ /**
+ * Checks whether a reference string is explicitly scoped for the given OR scope.
+ * Returns {@code true} only when {@code ref} starts with the expected scope prefix
+ * (e.g., {@code "[Project] "} or {@code "[Shared] "}); otherwise returns {@code false}.
+ *
+ * @param ref raw reference value (may be {@code null})
+ * @param scope scope to match against
+ * @return {@code true} if {@code ref} begins with the prefix for {@code scope}; {@code false} otherwise
+ */
+ private boolean matchesScope(String ref, ORScope scope) {
+ if (ref == null) return false;
+ ref = ref.trim();
+ if (scope == ORScope.PROJECT) return ref.startsWith("[Project] ");
+ if (scope == ORScope.SHARED) return ref.startsWith("[Shared] ");
+ return false;
+ }
+
+ /**
+ * Normalizes a page reference by removing known scope prefixes.
+ * Trims the input and strips {@code "[Project] "} or {@code "[Shared] "} when present;
+ * otherwise returns the trimmed reference. Returns an empty string when {@code ref} is {@code null}.
+ *
+ * @param ref raw reference value (may be {@code null})
+ * @return normalized page name without scope prefix (never {@code null})
+ */
+ private String normalizePageRef(String ref) {
+ if (ref == null) return "";
+ ref = ref.trim();
+ if (ref.startsWith("[Project] ")) return ref.substring("[Project] ".length()).trim();
+ if (ref.startsWith("[Shared] ")) return ref.substring("[Shared] ".length()).trim();
+ return ref;
+ }
+
public void refactorPageName(String oldPageName, String newPageName) {
Boolean clearOnExit = getTestSteps().isEmpty();
loadTableModel();
@@ -662,5 +743,4 @@ public Boolean rename(String newName) {
}
return false;
}
-
-}
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/component/TestStep.java b/Datalib/src/main/java/com/ing/datalib/component/TestStep.java
index 96adb007..b72f3031 100644
--- a/Datalib/src/main/java/com/ing/datalib/component/TestStep.java
+++ b/Datalib/src/main/java/com/ing/datalib/component/TestStep.java
@@ -44,27 +44,31 @@ public static int size() {
}
- private final TestCase testcase;
+ private final TestCase testCase;
List stepDetails = Collections.synchronizedList(new ArrayList(HEADERS.values().length) {
@Override
public String set(int index, String element) {
String val = super.set(index, element);
- if (testcase != null && testcase.getTestSteps().contains(TestStep.this)) {
- testcase.fireTableCellUpdated(testcase.getTestSteps().indexOf(TestStep.this),
+ if (testCase != null && testCase.getTestSteps().contains(TestStep.this)) {
+ testCase.fireTableCellUpdated(testCase.getTestSteps().indexOf(TestStep.this),
index);
}
return val;
}
});
+
+ public List getStepDetails(){
+ return this.stepDetails;
+ }
public TestStep(TestCase testcase, CSVRecord record) {
- this.testcase = testcase;
+ this.testCase = testcase;
loadStep(record);
}
public TestStep(TestCase testcase) {
- this.testcase = testcase;
+ this.testCase = testcase;
loadEmptyStep();
}
@@ -131,11 +135,11 @@ public TestStep setDescription(String description) {
}
public Project getProject() {
- return testcase.getProject();
+ return testCase.getProject();
}
- public TestCase getTestcase() {
- return testcase;
+ public TestCase getTestCase() {
+ return testCase;
}
private void loadStep(CSVRecord record) {
diff --git a/Datalib/src/main/java/com/ing/datalib/or/ObjectRepository.java b/Datalib/src/main/java/com/ing/datalib/or/ObjectRepository.java
index 55c946fc..a4547c45 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/ObjectRepository.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/ObjectRepository.java
@@ -5,50 +5,116 @@
import com.ing.datalib.or.common.ORPageInf;
import com.ing.datalib.or.common.ObjectGroup;
import com.ing.datalib.or.mobile.MobileOR;
+import com.ing.datalib.or.mobile.MobileORObject;
+import com.ing.datalib.or.mobile.MobileORPage;
import com.ing.datalib.or.web.WebOR;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.ing.datalib.or.mobile.ResolvedMobileObject;
+import com.ing.datalib.or.web.ResolvedWebObject;
+import com.ing.datalib.or.web.WebOR.ORScope;
+import com.ing.datalib.or.web.WebORObject;
+import com.ing.datalib.or.web.WebORPage;
import java.io.File;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- *
- *
+ * Manages all Object Repository types (Web Project OR, Web Shared OR, Mobile OR)
+ * for a project. Handles loading, saving, renaming, lookup, copying of pages and
+ * objects, and resolving objects across project/shared scopes.
*/
public class ObjectRepository {
-
private static final XmlMapper XML_MAPPER = new XmlMapper();
+ private static final Logger LOG = Logger.getLogger(ObjectRepository.class.getName());
private final Project sProject;
+ private WebOR webSharedOR;
+ private WebOR webProjectOR;
+ private MobileOR mobileProjectOR;
+ private MobileOR mobileSharedOR;
+
+ private final Set sharedUsageProjects = new HashSet<>();
- private WebOR webOR;
- private MobileOR mobileOR;
-
+ /**
+ * Creates an ObjectRepository for the given project and loads all OR files
+ * (project WebOR, shared WebOR, and MobileOR), initializing defaults when missing.
+ *
+ * @param sProject the project owning this repository
+ */
public ObjectRepository(Project sProject) {
this.sProject = sProject;
init();
}
+ /**
+ * Loads OR files from disk (shared, project, mobile), updates names, sets scopes,
+ * and links them to this repository.
+ */
private void init() {
try {
- if (new File(getORLocation()).exists()) {
- webOR = XML_MAPPER.readValue(new File(getORLocation()), WebOR.class);
- webOR.setName(sProject.getName());
+ File sharedFile = new File(getSharedORLocation());
+ if (sharedFile.exists()) {
+ webSharedOR = XML_MAPPER.readValue(sharedFile, WebOR.class);
+ webSharedOR.setName("Shared Web Objects");
+ } else {
+ webSharedOR = new WebOR("Shared Web Objects");
+ }
+
+ File projFile = new File(getORLocation());
+ if (projFile.exists()) {
+ webProjectOR = XML_MAPPER.readValue(projFile, WebOR.class);
+ webProjectOR.setName(sProject.getName());
+ } else {
+ webProjectOR = new WebOR(sProject.getName());
+ }
+
+ File sharedmorFile = new File(getSharedMORLocation());
+ if (sharedmorFile.exists()) {
+ mobileSharedOR = XML_MAPPER.readValue(sharedmorFile, MobileOR.class);
+ mobileSharedOR.setName("Shared Mobile Objects");
} else {
- webOR = new WebOR(sProject.getName());
+ mobileSharedOR = new MobileOR("Shared Mobile Objects");
}
- if (new File(getMORLocation()).exists()) {
- mobileOR = XML_MAPPER.readValue(new File(getMORLocation()), MobileOR.class);
- mobileOR.setName(sProject.getName());
+
+ File morFile = new File(getMORLocation());
+ if (morFile.exists()) {
+ mobileProjectOR = XML_MAPPER.readValue(morFile, MobileOR.class);
+ mobileProjectOR.setName(sProject.getName());
} else {
- mobileOR = new MobileOR(sProject.getName());
+ mobileProjectOR = new MobileOR(sProject.getName());
}
- webOR.setObjectRepository(this);
- webOR.setSaved(true);
- mobileOR.setObjectRepository(this);
-
+ if (webSharedOR != null) {
+ webSharedOR.setObjectRepository(this);
+ webSharedOR.setSaved(true);
+ webSharedOR.setRepLocationOverride(getSharedORRepLocation());
+ webSharedOR.setScope(ORScope.SHARED);
+ }
+ if (webProjectOR != null) {
+ webProjectOR.setObjectRepository(this);
+ webProjectOR.setSaved(true);
+ webProjectOR.setScope(ORScope.PROJECT);
+ }
+ if (mobileSharedOR != null) {
+ mobileSharedOR.setObjectRepository(this);
+ mobileSharedOR.setSaved(true);
+ mobileSharedOR.setRepLocationOverride(getSharedMORRepLocation());
+ mobileSharedOR.setScope(MobileOR.ORScope.SHARED);
+
+ }
+ if (mobileProjectOR != null) {
+ mobileProjectOR.setObjectRepository(this);
+ mobileProjectOR.setSaved(true);
+ mobileProjectOR.setScope(MobileOR.ORScope.PROJECT);
+ }
+
+ LOG.log(Level.INFO, "Shared WebOR loaded: {0}", (webSharedOR != null));
+ LOG.log(Level.INFO, "Project WebOR loaded: {0}", (webProjectOR != null));
+ LOG.log(Level.INFO, "Shared MobileOR loaded: {0}", (mobileSharedOR != null));
+ LOG.log(Level.INFO, "Project MobileOR loaded: {0}", (mobileProjectOR != null));
} catch (IOException ex) {
Logger.getLogger(ObjectRepository.class.getName()).log(Level.SEVERE, null, ex);
}
@@ -57,71 +123,560 @@ private void init() {
public String getORLocation() {
return sProject.getLocation() + File.separator + "OR.object";
}
-
+ public String getSharedORLocation() {
+ return "Shared" + File.separator + "SharedWebObjects" + File.separator + "SharedOR.object";
+ }
public String getIORLocation() {
return sProject.getLocation() + File.separator + "IOR.object";
}
-
public String getMORLocation() {
return sProject.getLocation() + File.separator + "MOR.object";
}
-
+ public String getSharedMORLocation() {
+ return "Shared" + File.separator + "SharedMobileObjects" + File.separator + "SharedMOR.object";
+ }
public String getORRepLocation() {
return sProject.getLocation() + File.separator + "ObjectRepository";
}
-
+ public String getSharedORRepLocation() {
+ return "Shared" + File.separator + "SharedWebObjects" + File.separator + "SharedObjectRepository";
+ }
public String getIORRepLocation() {
return sProject.getLocation() + File.separator + "ImageObjectRepository";
}
-
public String getMORRepLocation() {
return sProject.getLocation() + File.separator + "MobileObjectRepository";
}
-
+ public String getSharedMORRepLocation() {
+ return "Shared" + File.separator + "SharedMobileObjects" + File.separator + "MobileObjectRepository";
+ }
public Project getsProject() {
return sProject;
}
-
public WebOR getWebOR() {
- return webOR;
+ return webProjectOR;
+ }
+ public WebOR getWebSharedOR() {
+ return webSharedOR;
}
-
public MobileOR getMobileOR() {
- return mobileOR;
+ return mobileProjectOR;
+ }
+ public MobileOR getMobileSharedOR() {
+ return mobileSharedOR;
}
-
-
+ /**
+ * Saves updated shared, project, and mobile ORs to disk.
+ * Also updates shared project usage metadata when required.
+ */
public void save() {
try {
- if (!webOR.isSaved()) {
- XML_MAPPER.writerWithDefaultPrettyPrinter().writeValue(new File(getORLocation()), webOR);
+ java.util.List existingProjects = (webSharedOR != null) ? webSharedOR.getProjects() : java.util.List.of();
+ java.util.LinkedHashSet mergedProjects = new java.util.LinkedHashSet<>();
+ if (existingProjects != null) mergedProjects.addAll(existingProjects);
+ mergedProjects.addAll(sharedUsageProjects);
+ boolean projectsChanged = false;
+ if (webSharedOR != null) {
+ java.util.ArrayList mergedList = new java.util.ArrayList<>(mergedProjects);
+ java.util.List current = webSharedOR.getProjects();
+ projectsChanged = (current == null) || !new java.util.LinkedHashSet<>(current).equals(mergedProjects);
+ if (projectsChanged) {
+ webSharedOR.setProjects(mergedList);
+ }
+ }
+ java.util.List mExisting = (mobileSharedOR != null) ? mobileSharedOR.getProjects() : java.util.List.of();
+ java.util.LinkedHashSet mMerged = new java.util.LinkedHashSet<>();
+ if (mExisting != null) mMerged.addAll(mExisting);
+ mMerged.addAll(sharedUsageProjects);
+ boolean mProjectsChanged = false;
+ if (mobileSharedOR != null) {
+ java.util.ArrayList mList = new java.util.ArrayList<>(mMerged);
+ java.util.List mCurrent = mobileSharedOR.getProjects();
+ mProjectsChanged = (mCurrent == null) || !new java.util.LinkedHashSet<>(mCurrent).equals(mMerged);
+ if (mProjectsChanged) {
+ mobileSharedOR.setProjects(mList);
+ }
+ }
+ if (webSharedOR != null && (!webSharedOR.isSaved() || projectsChanged)) {
+ XML_MAPPER.writerWithDefaultPrettyPrinter()
+ .writeValue(new File(getSharedORLocation()), webSharedOR);
+ webSharedOR.setSaved(true);
+ }
+ if (webProjectOR != null && !webProjectOR.isSaved()) {
+ XML_MAPPER.writerWithDefaultPrettyPrinter()
+ .writeValue(new File(getORLocation()), webProjectOR);
+ webProjectOR.setSaved(true);
+ }
+ if (mobileSharedOR != null && !mobileSharedOR.isSaved()) {
+ XML_MAPPER.writerWithDefaultPrettyPrinter()
+ .writeValue(new File(getSharedMORLocation()), mobileSharedOR);
+ mobileSharedOR.setSaved(true);
+ }
+ if (mobileProjectOR != null && !mobileProjectOR.isSaved()) {
+ XML_MAPPER.writerWithDefaultPrettyPrinter()
+ .writeValue(new File(getMORLocation()), mobileProjectOR);
+ mobileProjectOR.setSaved(true);
}
- XML_MAPPER.writerWithDefaultPrettyPrinter().writeValue(new File(getMORLocation()), mobileOR);
} catch (IOException ex) {
Logger.getLogger(ObjectRepository.class.getName()).log(Level.SEVERE, null, ex);
}
}
+ /**
+ * Checks whether the given object exists in either PROJECT or SHARED scope.
+ *
+ * @param pageName page containing the object
+ * @param objectName object name
+ * @return true if present in project or shared OR
+ */
public Boolean isObjectPresent(String pageName, String objectName) {
- Boolean present = false;
- if (webOR.getPageByName(pageName) != null) {
- present = webOR.getPageByName(pageName).getObjectGroupByName(objectName) != null;
+ return resolveWebObjectWithScope(pageName, objectName) != null;
+ }
+
+ public Boolean isMobileObjectPresent(String pageName, String objectName) {
+ return resolveMobileObjectWithScope(pageName, objectName) != null;
+ }
+
+ /**
+ * Renames an object (object group) within its parent page. Determines whether the object
+ * is in project or shared scope and triggers corresponding scenario refactor in Project.
+ *
+ * @param group object group containing the object
+ * @param newName new object name
+ */
+ public void renameObject(ObjectGroup group, String newName) {
+ if (group == null || newName == null || newName.isBlank()) return;
+ var parentPage = group.getParent();
+ if (parentPage == null) return;
+ String oldName = group.getName();
+ if (oldName.equals(newName)) return;
+ boolean inProject = (webProjectOR != null) &&
+ (webProjectOR.getPageByName(parentPage.getName()) == parentPage);
+ boolean inShared = !inProject && (webSharedOR != null) &&
+ (webSharedOR.getPageByName(parentPage.getName()) == parentPage);
+ if (inProject && webProjectOR != null) {
+ webProjectOR.setSaved(false);
+ sProject.refactorObjectName(WebOR.ORScope.PROJECT, parentPage.getName(), oldName, newName);
+ } else if (inShared && webSharedOR != null) {
+ webSharedOR.setSaved(false);
+ markSharedUsage();
+ sProject.refactorObjectName(WebOR.ORScope.SHARED, parentPage.getName(), oldName, newName);
+ } else {
+ sProject.refactorObjectName(parentPage.getName(), oldName, newName);
}
- if (!present) {
- if (mobileOR.getPageByName(pageName) != null) {
- present = mobileOR.getPageByName(pageName).getObjectGroupByName(objectName) != null;
+ }
+
+ /**
+ * Renames a page in project or shared OR, respecting scope rules and preventing collisions,
+ * then propagates refactor changes into Project.
+ *
+ * @param page page object reference
+ * @param newName new page name
+ */
+ public void renamePage(ORPageInf page, String newName) {
+ if (page == null || newName == null || newName.isBlank()) return;
+ String oldName = page.getName();
+ if (oldName.equals(newName)) return;
+ boolean renamed = false;
+ ORScope scopeRenamed = null;
+ if (webProjectOR != null) {
+ var p = webProjectOR.getPageByName(oldName);
+ if (p == page) {
+ var existsSameScope = webProjectOR.getPageByName(newName);
+ if (existsSameScope != null && existsSameScope != page) {
+ return;
+ }
+ p.setName(newName);
+ webProjectOR.setSaved(false);
+ renamed = true;
+ scopeRenamed = ORScope.PROJECT;
}
}
- return present;
+ if (!renamed && webSharedOR != null) {
+ var s = webSharedOR.getPageByName(oldName);
+ if (s == page) {
+ var existsSameScope = webSharedOR.getPageByName(newName);
+ if (existsSameScope != null && existsSameScope != page) {
+ return;
+ }
+ s.setName(newName);
+ webSharedOR.setSaved(false);
+ renamed = true;
+ scopeRenamed = ORScope.SHARED;
+ }
+ }
+ if (renamed) {
+ sProject.refactorPageName(scopeRenamed, oldName, newName);
+ } else {
+ sProject.refactorPageName(oldName, newName);
+ }
}
- public void renameObject(ObjectGroup group, String newName) {
- sProject.refactorObjectName(group.getParent().getName(), group.getName(), newName);
+ public void renamePage(com.ing.datalib.or.mobile.MobileORPage page, String newName) {
+ if (page == null || newName == null || newName.isBlank()) return;
+ String oldName = page.getName();
+ if (oldName.equals(newName)) return;
+ boolean renamed = false;
+ com.ing.datalib.or.mobile.MobileOR.ORScope mScope = null;
+ if (mobileProjectOR != null) {
+ var p = mobileProjectOR.getPageByName(oldName);
+ if (p == page) {
+ var existsSameScope = mobileProjectOR.getPageByName(newName);
+ if (existsSameScope != null && existsSameScope != page) return;
+ p.setName(newName);
+ mobileProjectOR.setSaved(false);
+ renamed = true;
+ mScope = com.ing.datalib.or.mobile.MobileOR.ORScope.PROJECT;
+ }
+ }
+ if (!renamed && mobileSharedOR != null) {
+ var s = mobileSharedOR.getPageByName(oldName);
+ if (s == page) {
+ var existsSameScope = mobileSharedOR.getPageByName(newName);
+ if (existsSameScope != null && existsSameScope != page) return;
+ s.setName(newName);
+ mobileSharedOR.setSaved(false);
+ renamed = true;
+ mScope = com.ing.datalib.or.mobile.MobileOR.ORScope.SHARED;
+ }
+ }
+ if (renamed) {
+ var webLikeScope = (mScope == com.ing.datalib.or.mobile.MobileOR.ORScope.PROJECT)
+ ? com.ing.datalib.or.web.WebOR.ORScope.PROJECT
+ : com.ing.datalib.or.web.WebOR.ORScope.SHARED;
+ sProject.refactorPageName(webLikeScope, oldName, newName);
+ } else {
+ sProject.refactorPageName(oldName, newName);
+ }
}
- public void renamePage(ORPageInf page, String newName) {
- sProject.refactorPageName(page.getName(), newName);
+ /**
+ * Resolves a WebOR object from a scoped PageRef and object name, returning a
+ * ResolvedWebObject containing scope, page, object name, and object group.
+ */
+ public ResolvedWebObject resolveWebObject(ResolvedWebObject.PageRef pageRef, String objectName) {
+ if (pageRef == null || objectName == null) return null;
+ if (pageRef.scope == WebOR.ORScope.PROJECT) {
+ var g = getFrom(webProjectOR, pageRef.name, objectName);
+ if (g != null) {
+ String actualPageName = g.getParent() != null ? g.getParent().getName() : pageRef.name;
+ return new ResolvedWebObject(WebOR.ORScope.PROJECT, actualPageName, objectName, g);
+ }
+ return null;
+ }
+ if (pageRef.scope == WebOR.ORScope.SHARED) {
+ var g = getFrom(webSharedOR, pageRef.name, objectName);
+ if (g != null) {
+ markSharedUsage();
+ String actualPageName = g.getParent() != null ? g.getParent().getName() : pageRef.name;
+ return new ResolvedWebObject(WebOR.ORScope.SHARED, actualPageName, objectName, g);
+ }
+ return null;
+ }
+ var proj = getFrom(webProjectOR, pageRef.name, objectName);
+ if (proj != null) {
+ String actualPageName = proj.getParent() != null ? proj.getParent().getName() : pageRef.name;
+ return new ResolvedWebObject(WebOR.ORScope.PROJECT, actualPageName, objectName, proj);
+ }
+ var shared = getFrom(webSharedOR, pageRef.name, objectName);
+ if (shared != null) {
+ markSharedUsage();
+ String actualPageName = shared.getParent() != null ? shared.getParent().getName() : pageRef.name;
+ return new ResolvedWebObject(WebOR.ORScope.SHARED, actualPageName, objectName, shared);
+ }
+ return null;
+ }
+
+ public ResolvedMobileObject resolveMobileObject(ResolvedMobileObject.PageRef pageRef, String objectName) {
+ if (pageRef == null || objectName == null) return null;
+ if (pageRef.scope == ORScope.PROJECT) {
+ var g = getFrom(mobileProjectOR, pageRef.name, objectName);
+ if (g != null) {
+ String actualPageName = g.getParent() != null ? g.getParent().getName() : pageRef.name;
+ return new ResolvedMobileObject(ORScope.PROJECT, actualPageName, objectName, g);
+ }
+ return null;
+ }
+ if (pageRef.scope == ORScope.SHARED) {
+ var g = getFrom(mobileSharedOR, pageRef.name, objectName);
+ if (g != null) {
+ markSharedUsage();
+ String actualPageName = g.getParent() != null ? g.getParent().getName() : pageRef.name;
+ return new ResolvedMobileObject(ORScope.SHARED, actualPageName, objectName, g);
+ }
+ return null;
+ }
+ return resolveMobileObjectWithScope(pageRef.name, objectName);
+ }
+
+ /**
+ * Resolves a WebOR object by searching project scope first, then shared scope.
+ *
+ * @param pageName page to search
+ * @param objectName object group name
+ * @return resolved WebOR object with scope metadata
+ */
+ public ResolvedWebObject resolveWebObjectWithScope(String pageName, String objectName) {
+ var proj = getFrom(webProjectOR, pageName, objectName);
+ if (proj != null) {
+ String actualPageName = proj.getParent() != null ? proj.getParent().getName() : pageName;
+ return new ResolvedWebObject(ORScope.PROJECT, actualPageName, objectName, proj);
+ }
+ var shared = getFrom(webSharedOR, pageName, objectName);
+ if (shared != null) {
+ markSharedUsage();
+ String actualPageName = shared.getParent() != null ? shared.getParent().getName() : pageName;
+ return new ResolvedWebObject(ORScope.SHARED, actualPageName, objectName, shared);
+ }
+ return null;
+ }
+
+ /**
+ * Resolves a MobileOR object by searching project scope first, then shared scope.
+ *
+ * @param pageName page to search
+ * @param objectName object group name
+ * @return resolved MobileOR object with scope metadata
+ */
+ public ResolvedMobileObject resolveMobileObjectWithScope(String pageName, String objectName) {
+ var proj = getFrom(mobileProjectOR, pageName, objectName);
+ if (proj != null) {
+ String actualPageName = proj.getParent() != null ? proj.getParent().getName() : pageName;
+ return new ResolvedMobileObject(ORScope.PROJECT, actualPageName, objectName, proj);
+ }
+ var shared = getFrom(mobileSharedOR, pageName, objectName);
+ if (shared != null) {
+ markSharedUsage();
+ String actualPageName = shared.getParent() != null ? shared.getParent().getName() : pageName;
+ return new ResolvedMobileObject(ORScope.SHARED, actualPageName, objectName, shared);
+ }
+ return null;
+ }
+
+ private ObjectGroup getFrom(WebOR or, String page, String obj) {
+ if (or == null) return null;
+ var p = or.getPageByName(page);
+ return (p == null) ? null : p.getObjectGroupByName(obj);
+ }
+
+ private ObjectGroup getFrom(MobileOR or, String page, String obj) {
+ if (or == null) return null;
+ MobileORPage p = or.getPageByName(page);
+ return (p == null) ? null : p.getObjectGroupByName(obj);
+ }
+
+ /**
+ * Deep-clones an object group and its objects into another page.
+ */
+ private ObjectGroup cloneGroupIntoPage(ObjectGroup originalGroup, WebORPage targetPage) {
+ ObjectGroup newGroup = new ObjectGroup<>(originalGroup.getName(), targetPage);
+ for (WebORObject obj : originalGroup.getObjects()) {
+ WebORObject cloned = new WebORObject();
+ cloned.setName(obj.getName());
+ cloned.setParent(newGroup);
+ obj.clone(cloned);
+ newGroup.getObjects().add(cloned);
+ }
+ return newGroup;
+ }
+
+ /**
+ * Generates a unique name by appending "(n)" when duplicates exist.
+ */
+ private String generateUniqueName(String baseName, java.util.function.Predicate exists) {
+ if (baseName == null || baseName.isBlank()) return baseName;
+ String candidate = baseName;
+ int counter = 1;
+ while (exists.test(candidate)) {
+ candidate = baseName + " (" + counter + ")";
+ counter++;
+ }
+ return candidate;
+ }
+
+ private String generateUniquePageName(WebOR or, String baseName) {
+ if (or == null) return baseName;
+ return generateUniqueName(baseName, name -> or.getPageByName(name) != null);
+ }
+
+ private String generateUniqueGroupName(WebORPage page, String baseName) {
+ if (page == null) return baseName;
+ return generateUniqueName(baseName, name -> page.getObjectGroupByName(name) != null);
+ }
+
+ /**
+ * Ensures a page exists in the given OR; creates one if missing.
+ */
+ private WebORPage getOrCreatePage(WebOR or, String pageName) {
+ if (or == null || pageName == null) return null;
+ WebORPage page = or.getPageByName(pageName);
+ return (page != null) ? page : or.addPage(pageName);
+ }
+
+ /**
+ * Copies all object groups from a source page to a target page.
+ */
+ private void copyAllGroups(WebORPage sourcePage, WebORPage targetPage) {
+ if (sourcePage == null || targetPage == null) return;
+ for (ObjectGroup originalGroup : sourcePage.getObjectGroups()) {
+ if (originalGroup == null) continue;
+ targetPage.getObjectGroups().add(cloneGroupIntoPage(originalGroup, targetPage));
+ }
+ }
+
+ private ObjectGroup cloneGroupIntoPage(ObjectGroup originalGroup, WebORPage targetPage, String newGroupName) {
+ ObjectGroup newGroup = new ObjectGroup<>(newGroupName, targetPage);
+ if (originalGroup.getObjects() != null && !originalGroup.getObjects().isEmpty()) {
+ WebORObject sourceObj = originalGroup.getObjects().get(0);
+ WebORObject cloned = new WebORObject();
+ cloned.setName(newGroupName);
+ cloned.setParent(newGroup);
+ sourceObj.clone(cloned);
+ newGroup.getObjects().add(cloned);
+ }
+ return newGroup;
+ }
+
+ /**
+ * Copies a project WebOR page into the shared OR using a unique name.
+ *
+ * @param sourcePageName project page
+ * @param targetPageName desired shared page name
+ * @return actual created page name
+ */
+ public String copyWebPage(String sourcePageName, String targetPageName) {
+ WebOR projectOR = getWebOR();
+ WebOR sharedOR = getWebSharedOR();
+ if (projectOR == null || sharedOR == null) {
+ return null;
+ }
+ WebORPage sourcePage = projectOR.getPageByName(sourcePageName);
+ if (sourcePage == null) {
+ return null;
+ }
+ String uniqueTargetName = generateUniquePageName(sharedOR, targetPageName);
+ WebORPage targetPage = getOrCreatePage(sharedOR, uniqueTargetName);
+ copyAllGroups(sourcePage, targetPage);
+ sharedOR.setSaved(false);
+ LOG.info(() -> "Copied Web Page '" + sourcePageName + "' to SHARED page '" + uniqueTargetName + "' successfully.");
+ return uniqueTargetName;
+ }
+
+ /**
+ * Copies a WebOR object into a shared page (creating the page if needed)
+ * using a unique object group name.
+ *
+ * @param source resolved web object
+ * @param targetPageName target page in shared OR
+ * @return new object name
+ */
+ public String copyWebObject(ResolvedWebObject source, String targetPageName) {
+ if (source == null) return null;
+ WebOR sharedOR = getWebSharedOR();
+ if (sharedOR == null) return null;
+ WebORPage targetPage = getOrCreatePage(sharedOR, targetPageName);
+ if (targetPage == null) return null;
+ ObjectGroup originalGroup = source.getGroup();
+ if (originalGroup == null) return null;
+ String baseName = originalGroup.getName();
+ String uniqueName = generateUniqueGroupName(targetPage, baseName);
+ ObjectGroup newGroup = cloneGroupIntoPage(originalGroup, targetPage, uniqueName);
+ targetPage.getObjectGroups().add(newGroup);
+ sharedOR.setSaved(false);
+ LOG.info(() -> "Copied Web Object '" + baseName + "' to SHARED as '" + uniqueName + "'");
+ return uniqueName;
+ }
+
+ /**
+ * Copies a project MobileOR page into the shared Mobile OR using a unique name.
+ * @param sourcePageName project page to copy from
+ * @param targetPageName desired shared page name (will uniquify if needed)
+ * @return actual created page name in shared OR, or null on failure
+ */
+ public String copyMobilePage(String sourcePageName, String targetPageName) {
+ MobileOR projectMOR = getMobileOR();
+ MobileOR sharedMOR = getMobileSharedOR();
+ if (projectMOR == null || sharedMOR == null) return null;
+ MobileORPage sourcePage = projectMOR.getPageByName(sourcePageName);
+ if (sourcePage == null) return null;
+ String uniqueTargetName = generateUniquePageName(sharedMOR, targetPageName);
+ MobileORPage targetPage = getOrCreateMobilePage(sharedMOR, uniqueTargetName);
+ copyAllMobileGroups(sourcePage, targetPage);
+ sharedMOR.setSaved(false);
+ LOG.info(() -> "Copied Mobile Page '" + sourcePageName
+ + "' to SHARED page '" + uniqueTargetName + "' successfully.");
+ return uniqueTargetName;
+ }
+
+ /**
+ * Copies a MobileOR object into a target shared Mobile page (creates page if needed)
+ * using a unique object group name.
+ * @param source resolved mobile object (from project OR)
+ * @param targetPageName target page name in shared Mobile OR
+ * @return new object name created in shared OR, or null on failure
+ */
+ public String copyMobileObject(ResolvedMobileObject source, String targetPageName) {
+ if (source == null) return null;
+ MobileOR sharedMOR = getMobileSharedOR();
+ if (sharedMOR == null) return null;
+ MobileORPage targetPage = getOrCreateMobilePage(sharedMOR, targetPageName);
+ if (targetPage == null) return null;
+ ObjectGroup originalGroup = source.getGroup();
+ if (originalGroup == null) return null;
+ String baseName = originalGroup.getName();
+ String uniqueName = generateUniqueMobileGroupName(targetPage, baseName);
+ ObjectGroup newGroup = cloneMobileGroupIntoPage(originalGroup, targetPage, uniqueName);
+ targetPage.getObjectGroups().add(newGroup);
+ sharedMOR.setSaved(false);
+ LOG.info(() -> "Copied Mobile Object '" + baseName + "' to SHARED as '" + uniqueName + "'");
+ return uniqueName;
}
-}
+ private String generateUniquePageName(MobileOR mor, String baseName) {
+ if (mor == null) return baseName;
+ return generateUniqueName(baseName, name -> mor.getPageByName(name) != null);
+ }
+
+ private String generateUniqueMobileGroupName(MobileORPage page, String baseName) {
+ if (page == null) return baseName;
+ return generateUniqueName(baseName, name -> page.getObjectGroupByName(name) != null);
+ }
+
+ private MobileORPage getOrCreateMobilePage(MobileOR mor, String pageName) {
+ if (mor == null || pageName == null) return null;
+ MobileORPage page = mor.getPageByName(pageName);
+ return (page != null) ? page : mor.addPage(pageName);
+ }
+
+ private void copyAllMobileGroups(MobileORPage sourcePage, MobileORPage targetPage) {
+ if (sourcePage == null || targetPage == null) return;
+ for (ObjectGroup originalGroup : sourcePage.getObjectGroups()) {
+ if (originalGroup == null) continue;
+ targetPage.getObjectGroups().add(cloneMobileGroupIntoPage(originalGroup, targetPage, originalGroup.getName()));
+ }
+ }
+
+ private ObjectGroup cloneMobileGroupIntoPage(ObjectGroup originalGroup, MobileORPage targetPage, String newGroupName) {
+ ObjectGroup newGroup = new ObjectGroup<>(newGroupName, targetPage);
+ if (originalGroup.getObjects() != null && !originalGroup.getObjects().isEmpty()) {
+ MobileORObject sourceObj = originalGroup.getObjects().get(0);
+ MobileORObject cloned = new MobileORObject();
+ cloned.setName(newGroupName);
+ cloned.setParent(newGroup);
+ sourceObj.clone(cloned);
+ newGroup.getObjects().add(cloned);
+ }
+ return newGroup;
+ }
+
+ /**
+ * Marks that the current project has used a shared object,
+ * updating shared OR metadata.
+ */
+ private void markSharedUsage() {
+ if (sProject != null && sProject.getName() != null && !sProject.getName().isBlank()) {
+ sharedUsageProjects.add(sProject.getName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/common/ObjectGroup.java b/Datalib/src/main/java/com/ing/datalib/or/common/ObjectGroup.java
index 9648a8a3..da60a75f 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/common/ObjectGroup.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/common/ObjectGroup.java
@@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.ing.datalib.or.web.WebORObject;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@@ -179,7 +180,7 @@ public TreePath getTreePath() {
public Boolean rename(String newName) {
if (getParent().getObjectGroupByName(newName) == null) {
if (FileUtils.renameFile(getRepLocation(), newName)) {
- getParent().getRoot().getObjectRepository().renameObject(this, newName);
+ getParent().getRoot().getObjectRepository().renameObject((ObjectGroup) this, newName);
setName(newName);
getParent().getRoot().setSaved(false);
return true;
diff --git a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileOR.java b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileOR.java
index 4d3a93c8..4f3c7e1e 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileOR.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileOR.java
@@ -17,6 +17,11 @@
import java.util.List;
import javax.swing.tree.TreeNode;
+/**
+ * Represents the Mobile Object Repository (MobileOR), containing pages and their objects,
+ * along with metadata such as scope, type, associated projects, and save state.
+ * Provides page management, tree navigation, sorting, and repository integration.
+ */
@JsonInclude(JsonInclude.Include.NON_NULL)
@JacksonXmlRootElement(localName = "Root")
public class MobileOR implements ORRootInf {
@@ -43,12 +48,22 @@ public class MobileOR implements ORRootInf {
@JacksonXmlProperty(isAttribute = true)
private String type;
+
+ @JacksonXmlProperty(isAttribute = true)
+ private ORScope scope = ORScope.PROJECT;
+
+ @JacksonXmlElementWrapper(localName = "projects")
+ @JacksonXmlProperty(localName = "project")
+ private List projects = new ArrayList<>();
@JsonIgnore
private ObjectRepository objectRepository;
@JsonIgnore
private Boolean saved = true;
+
+ @JsonIgnore
+ private String repLocationOverride;
public MobileOR() {
this.pages = new ArrayList<>();
@@ -120,6 +135,7 @@ public MobileORPage addPage() {
public MobileORPage addPage(String pageName) {
if (getPageByName(pageName) == null) {
MobileORPage page = new MobileORPage(pageName, this);
+ page.setSource(this.isShared() ? ORScope.SHARED : ORScope.PROJECT);
pages.add(page);
new File(page.getRepLocation()).mkdirs();
setSaved(false);
@@ -216,10 +232,17 @@ public TreeNode[] getPath() {
return new TreeNode[]{this};
}
+ @JsonIgnore
+ public void setRepLocationOverride(String path) {
+ this.repLocationOverride = path;
+ }
+
@JsonIgnore
@Override
public String getRepLocation() {
- return getObjectRepository().getMORRepLocation();
+ return repLocationOverride != null
+ ? repLocationOverride
+ : getObjectRepository().getMORRepLocation();
}
@JsonIgnore
@@ -227,4 +250,30 @@ public String getRepLocation() {
public void sort() {
ORUtils.sort(this);
}
-}
+
+ public enum ORScope {
+ PROJECT, SHARED
+ }
+
+ @JsonIgnore
+ public ORScope getScope() {
+ return scope;
+ }
+
+ public void setScope(ORScope scope) {
+ this.scope = scope;
+ }
+
+ @JsonIgnore
+ public boolean isShared() {
+ return scope == ORScope.SHARED;
+ }
+
+ public List getProjects() {
+ return projects;
+ }
+
+ public void setProjects(List projects) {
+ this.projects = (projects == null) ? new ArrayList<>() : projects;
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORObject.java b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORObject.java
index 6469bab6..3ec64033 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORObject.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORObject.java
@@ -22,6 +22,12 @@
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
+/**
+ * Represents a single mobile object inside a MobileOR page, containing a collection of
+ * OR attributes, frame information, and references to its parent object group.
+ * Supports attribute editing, table model operations, cloning, renaming,
+ * and object repository persistence updates.
+ */
public class MobileORObject extends UndoRedoModel implements ORObjectInf {
@JacksonXmlProperty(isAttribute = true, localName = "ref")
@@ -514,4 +520,4 @@ public void insertColumnAt(int colIndex, String colName, Object[] values) {
public void removeColumn(int colIndex) {
throw new UnsupportedOperationException("Not supported yet.");
}
-}
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORPage.java b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORPage.java
index 4b0877e8..f4d45059 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORPage.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/mobile/MobileORPage.java
@@ -9,6 +9,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.ing.datalib.or.mobile.MobileOR.ORScope;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@@ -17,6 +18,12 @@
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
+/**
+ * Represents a single mobile object inside a MobileOR page, containing a collection of
+ * OR attributes, frame information, and references to its parent object group.
+ * Supports attribute editing, table model operations, cloning, renaming,
+ * and object repository persistence updates.
+ */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MobileORPage implements ORPageInf {
@@ -32,6 +39,9 @@ public class MobileORPage implements ORPageInf {
@JsonIgnore
private MobileOR root;
+
+ @JacksonXmlProperty(isAttribute = true, localName = "source")
+ private ORScope source = ORScope.PROJECT;
public MobileORPage() {
this.objectGroups = new ArrayList<>();
@@ -259,4 +269,12 @@ public String getRepLocation() {
public void sort() {
ORUtils.sort(this);
}
-}
+
+ public ORScope getSource() {
+ return source;
+ }
+
+ public void setSource(ORScope source) {
+ this.source = source;
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/mobile/ResolvedMobileObject.java b/Datalib/src/main/java/com/ing/datalib/or/mobile/ResolvedMobileObject.java
new file mode 100644
index 00000000..688a4763
--- /dev/null
+++ b/Datalib/src/main/java/com/ing/datalib/or/mobile/ResolvedMobileObject.java
@@ -0,0 +1,112 @@
+package com.ing.datalib.or.mobile;
+
+import com.ing.datalib.or.common.ObjectGroup;
+import com.ing.datalib.or.web.WebOR.ORScope;
+
+/**
+ * Represents a resolved mobile object within the Object Repository, including its scope,
+ * page name, object name, and resolved object group.
+ *
+ */
+public class ResolvedMobileObject {
+
+ private final ORScope scope;
+ private final String pageName;
+ private final String objectName;
+ private final ObjectGroup group;
+
+ public ResolvedMobileObject(ORScope scope, String pageName, String objectName, ObjectGroup group) {
+ this.scope = scope;
+ this.pageName = pageName;
+ this.objectName = objectName;
+ this.group = group;
+ }
+
+ public ORScope getScope() {
+ return scope;
+ }
+
+ public String getPageName() {
+ return pageName;
+ }
+
+ public String getObjectName() {
+ return objectName;
+ }
+
+ public ObjectGroup getGroup() {
+ return group;
+ }
+
+ /**
+ * Returns the first resolved MobileORObject from the group, or null if none exist.
+ */
+ public MobileORObject getObject() {
+ return (group != null && !group.getObjects().isEmpty()) ? group.getObjects().get(0) : null;
+ }
+
+ public boolean isFromProject() {
+ return scope == ORScope.PROJECT;
+ }
+
+ public boolean isFromShared() {
+ return scope == ORScope.SHARED;
+ }
+
+ public boolean isPresent() {
+ return group != null && !group.getObjects().isEmpty();
+ }
+
+ public String debugString() {
+ return "ResolvedMobileObject{scope=" + scope
+ + ", page='" + pageName + '\''
+ + ", object='" + objectName + '\''
+ + ", objectCount=" + (group == null ? 0 : group.getObjects().size())
+ + '}';
+ }
+
+ /**
+ * Optional: reuse the same PageRef concept as web if you want scoped tokens like:
+ * "[Shared] Login" / "[Project] Home"
+ *
+ * If you already want Mobile page tokens to behave the same way, you can keep this.
+ */
+ public static final class PageRef {
+ public final String name;
+ public final ORScope scope;
+
+ public PageRef(String name, ORScope scope) {
+ this.name = name;
+ this.scope = scope;
+ }
+
+ public String qualified() {
+ if (scope == null) return name;
+ switch (scope) {
+ case PROJECT: return "[Project] " + name;
+ case SHARED: return "[Shared] " + name;
+ default: return name;
+ }
+ }
+
+ public static PageRef parse(String token) {
+ String s = token == null ? "" : token.trim();
+ if (s.isEmpty()) return new PageRef("", ORScope.PROJECT);
+
+ if (s.startsWith("[") && s.contains("]")) {
+ int end = s.indexOf(']');
+ String scopeText = s.substring(1, end).trim().toUpperCase();
+ String base = s.substring(end + 1).trim();
+ ORScope sc;
+ switch (scopeText) {
+ case "PROJECT": sc = ORScope.PROJECT; break;
+ case "SHARED": sc = ORScope.SHARED; break;
+ default: sc = ORScope.PROJECT;
+ }
+ return new PageRef(base, sc);
+ }
+
+ return new PageRef(s, ORScope.PROJECT);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/web/ResolvedWebObject.java b/Datalib/src/main/java/com/ing/datalib/or/web/ResolvedWebObject.java
new file mode 100644
index 00000000..158e9ef6
--- /dev/null
+++ b/Datalib/src/main/java/com/ing/datalib/or/web/ResolvedWebObject.java
@@ -0,0 +1,137 @@
+
+package com.ing.datalib.or.web;
+
+import com.ing.datalib.or.common.ObjectGroup;
+import com.ing.datalib.or.web.WebOR.ORScope;
+
+/**
+ * Represents a resolved web object within the Object Repository, including its scope,
+ * page name, object name, and resolved object group.
+ */
+public class ResolvedWebObject {
+ private final ORScope scope;
+ private final String pageName;
+ private final String objectName;
+ private final ObjectGroup group;
+
+ /**
+ * Creates a resolved web object record tying together scope, page name, object name,
+ * and the matched object group.
+ *
+ * @param scope OR scope (Project or Shared)
+ * @param pageName logical page name
+ * @param objectName name of the web object
+ * @param group group of matching WebORObject instances
+ */
+ public ResolvedWebObject(ORScope scope, String pageName, String objectName, ObjectGroup group) {
+ this.scope = scope;
+ this.pageName = pageName;
+ this.objectName = objectName;
+ this.group = group;
+ }
+
+ /**
+ * Represents a reference to a page along with its OR scope, and provides utilities
+ * for formatting and parsing scoped page tokens.
+ */
+ public static final class PageRef {
+ public final String name;
+ public final ORScope scope;
+
+ /**
+ * Creates a page reference with the given name and scope.
+ *
+ * @param name page name without prefix
+ * @param scope OR scope of the page
+ */
+ public PageRef(String name, ORScope scope) {
+ this.name = name;
+ this.scope = scope;
+ }
+
+ /**
+ * Returns the page name prefixed with its scope (e.g., "[Project] Login"),
+ * or the raw name if scope is null.
+ *
+ * @return fully qualified scoped page name
+ */
+ public String qualified() {
+ if (null == scope) {
+ return name;
+ }
+ else switch (scope) {
+ case PROJECT:
+ return "[Project] " + name;
+ case SHARED:
+ return "[Shared] " + name;
+ default:
+ return name;
+ }
+ }
+
+ /**
+ * Parses a scoped page token (e.g., "[Shared] Home") into a PageRef.
+ * Defaults to PROJECT scope when missing or unrecognized.
+ *
+ * @param token raw page reference text
+ * @return parsed PageRef instance
+ */
+ public static PageRef parse(String token) {
+ String s = token == null ? "" : token.trim();
+ if (s.isEmpty()) {
+ return new PageRef("", ORScope.PROJECT);
+ }
+ if (s.startsWith("[") && s.contains("]")) {
+ int end = s.indexOf(']');
+ String scopeText = s.substring(1, end).trim().toUpperCase();
+ String base = s.substring(end + 1).trim();
+ ORScope sc;
+ switch (scopeText) {
+ case "PROJECT":
+ sc = ORScope.PROJECT;
+ break;
+ case "SHARED":
+ sc = ORScope.SHARED;
+ break;
+ default:
+ sc = ORScope.PROJECT;
+ }
+
+ return new PageRef(base, sc);
+ }
+ return new PageRef(s, ORScope.PROJECT);
+ }
+ }
+
+ public ORScope getScope() { return scope; }
+ public String getPageName() { return pageName; }
+ public String getObjectName() { return objectName; }
+ public ObjectGroup getGroup() { return group; }
+
+ /**
+ * Returns the first resolved WebORObject from the group, or null if none exist.
+ *
+ * @return a resolved WebORObject or null
+ */
+ public WebORObject getObject() {
+ return (group != null && !group.getObjects().isEmpty()) ? group.getObjects().get(0) : null;
+ }
+
+ public boolean isFromProject() { return scope == ORScope.PROJECT; }
+ public boolean isFromShared() { return scope == ORScope.SHARED; }
+ public boolean isPresent() { return group != null && !group.getObjects().isEmpty(); }
+
+ /**
+ * Returns a debug-friendly string summarizing the scope, page, object name,
+ * and number of resolved objects.
+ *
+ * @return formatted debug information
+ */
+ public String debugString() {
+ return "ResolvedWebObject{scope=" + scope +
+ ", page='" + pageName + '\'' +
+ ", object='" + objectName + '\'' +
+ ", objectCount=" + (group == null ? 0 : group.getObjects().size()) +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/web/WebOR.java b/Datalib/src/main/java/com/ing/datalib/or/web/WebOR.java
index 1966fabd..6718b231 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/web/WebOR.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/web/WebOR.java
@@ -17,6 +17,11 @@
import java.util.List;
import javax.swing.tree.TreeNode;
+/**
+ * Represents the Web Object Repository (WebOR), containing pages and their objects,
+ * along with metadata such as scope, type, associated projects, and save state.
+ * Provides page management, tree navigation, sorting, and repository integration.
+ */
@JsonInclude(JsonInclude.Include.NON_NULL)
@JacksonXmlRootElement(localName = "Root")
public class WebOR implements ORRootInf {
@@ -43,12 +48,22 @@ public class WebOR implements ORRootInf {
@JacksonXmlProperty(isAttribute = true)
private String type;
+
+ @JacksonXmlProperty(isAttribute = true)
+ private ORScope scope = ORScope.PROJECT;
+
+ @JacksonXmlElementWrapper(localName = "projects")
+ @JacksonXmlProperty(localName = "project")
+ private List projects = new ArrayList<>();
@JsonIgnore
private ObjectRepository objectRepository;
@JsonIgnore
private Boolean saved = true;
+
+ @JsonIgnore
+ private String repLocationOverride;
public WebOR() {
this.pages = new ArrayList<>();
@@ -80,6 +95,9 @@ public void setPages(List pages) {
this.pages = pages;
for (WebORPage page : pages) {
page.setRoot(this);
+ if (page.getSource() == null || page.getSource().isBlank()) {
+ page.setSource(isShared() ? "SHARED" : "PROJECT");
+ }
}
}
@@ -131,6 +149,7 @@ public WebORPage addPage(String pageName) {
if (getPageByName(pageName) == null) {
WebORPage page = new WebORPage(pageName, this);
pages.add(page);
+ page.setSource(isShared() ? "SHARED" : "PROJECT");
new File(page.getRepLocation()).mkdirs();
setSaved(false);
return page;
@@ -226,10 +245,17 @@ public TreeNode[] getPath() {
return new TreeNode[]{this};
}
+ @JsonIgnore
+ public void setRepLocationOverride(String path) {
+ this.repLocationOverride = path;
+ }
+
@JsonIgnore
@Override
public String getRepLocation() {
- return getObjectRepository().getORRepLocation();
+ return repLocationOverride != null
+ ? repLocationOverride
+ : getObjectRepository().getORRepLocation();
}
@JsonIgnore
@@ -237,4 +263,30 @@ public String getRepLocation() {
public void sort() {
ORUtils.sort(this);
}
-}
+
+ public enum ORScope {
+ PROJECT, SHARED
+ }
+
+ @JsonIgnore
+ public ORScope getScope() {
+ return scope;
+ }
+
+ public void setScope(ORScope scope) {
+ this.scope = scope;
+ }
+
+ @JsonIgnore
+ public boolean isShared() {
+ return scope == ORScope.SHARED;
+ }
+
+ public List getProjects() {
+ return projects;
+ }
+
+ public void setProjects(List projects) {
+ this.projects = (projects == null) ? new ArrayList<>() : projects;
+ }
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/web/WebORObject.java b/Datalib/src/main/java/com/ing/datalib/or/web/WebORObject.java
index c9bd9af5..eebf033d 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/web/WebORObject.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/web/WebORObject.java
@@ -23,6 +23,12 @@
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
+/**
+ * Represents a single web object inside a WebOR page, containing a collection of
+ * OR attributes, frame information, and references to its parent object group.
+ * Supports attribute editing, table model operations, cloning, renaming,
+ * and object repository persistence updates.
+ */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class WebORObject extends UndoRedoModel implements ORObjectInf {
@@ -586,5 +592,4 @@ public void insertColumnAt(int colIndex, String colName, Object[] values) {
public void removeColumn(int colIndex) {
throw new UnsupportedOperationException("Not supported yet.");
}
-
-}
+}
\ No newline at end of file
diff --git a/Datalib/src/main/java/com/ing/datalib/or/web/WebORPage.java b/Datalib/src/main/java/com/ing/datalib/or/web/WebORPage.java
index 96991f00..43863de5 100644
--- a/Datalib/src/main/java/com/ing/datalib/or/web/WebORPage.java
+++ b/Datalib/src/main/java/com/ing/datalib/or/web/WebORPage.java
@@ -18,6 +18,11 @@
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
+/**
+ * Represents a page in the Web Object Repository (WebOR), containing object groups
+ * and metadata such as title, source, and its parent WebOR root. Supports object
+ * group management, tree navigation, renaming, and persistence utilities.
+ */
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties({"root"})
public class WebORPage implements ORPageInf {
@@ -34,6 +39,9 @@ public class WebORPage implements ORPageInf {
@JsonIgnore
private WebOR root;
+
+ @JacksonXmlProperty(isAttribute = true)
+ private String source;
public WebORPage() {
this.objectGroups = new ArrayList<>();
@@ -260,4 +268,7 @@ public String getRepLocation() {
public void sort() {
ORUtils.sort(this);
}
+
+ public String getSource() { return source; }
+ public void setSource(String source) { this.source = source; }
}
diff --git a/Datalib/src/main/java/com/ing/datalib/settings/DriverProperties.java b/Datalib/src/main/java/com/ing/datalib/settings/DriverProperties.java
index d858750c..e5705d8e 100644
--- a/Datalib/src/main/java/com/ing/datalib/settings/DriverProperties.java
+++ b/Datalib/src/main/java/com/ing/datalib/settings/DriverProperties.java
@@ -3,15 +3,15 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import com.ing.datalib.util.data.LinkedProperties;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
/**
@@ -195,7 +195,7 @@ private Properties setAPIProperties(Properties prop, String apiAlias) {
* This method initializes the following keys with default values:
*
+ * This method looks up the property httpClientRedirect inside the API configuration
+ * identified by {@code currLoadedAPIConfig}. If the property is not defined, the method returns the
+ * default value "NEVER".
+ *
+ *
+ * @return the redirect policy defined for the current API configuration, or "NEVER"
+ * if the property is missing
+ */
+ public String getHttpClientRedirect() {
+ return apiConfigFilePropMap.get(currLoadedAPIConfig).getProperty("httpClientRedirect", "NEVER");
+ }
+
//Setters for some specific properties.
//Commented out as these are not set programmatically but are extracted from
//configurations files.
diff --git a/Dist/pom.xml b/Dist/pom.xml
index eeb86385..385c8830 100644
--- a/Dist/pom.xml
+++ b/Dist/pom.xml
@@ -4,7 +4,7 @@
com.ingingenious-playwright
- 2.3.1
+ 2.4Distpom
@@ -14,6 +14,7 @@
org.apache.maven.pluginsmaven-dependency-plugin
+ 3.9.0copy-jar
diff --git a/Engine/pom.xml b/Engine/pom.xml
index dbde6ba6..6d3178e2 100644
--- a/Engine/pom.xml
+++ b/Engine/pom.xml
@@ -5,7 +5,7 @@
com.ingingenious-playwright
- 2.3.1
+ 2.4ingenious-enginejar
@@ -58,18 +58,60 @@
org.apache.poipoi${apache.poi.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ log4j
+ log4j
+
+ org.apache.poipoi-ooxml${apache.poi.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ log4j
+ log4j
+
+ org.apache.poipoi-ooxml-schemas${apache.poi.schemas.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ log4j
+ log4j
+
+
@@ -130,6 +172,17 @@
com.jayway.jsonpathjson-path${json-path.version}
+
+
+ net.minidev
+ json-smart
+
+
+
+
+ net.minidev
+ json-smart
+ 2.4.9
@@ -150,18 +203,6 @@
${java-string-similarity.version}
-
- org.apache.logging.log4j
- log4j-core
- ${log4j.version}
-
-
-
- org.apache.logging.log4j
- log4j-api
- ${log4j.version}
-
-
org.checkerframeworkchecker-qual
@@ -232,6 +273,17 @@
galen-core2.4.4jar
+
+
+ com.squareup.okhttp3
+ okhttp
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 5.0.0ru.yandex.qatools.ashot
@@ -239,11 +291,22 @@
1.5.4jar
+
+ org.yaml
+ snakeyaml
+ 2.0
+ com.github.javafakerjavafaker${javafaker.version}
-
+
+
+ org.yaml
+ snakeyaml
+
+
+
com.ibm.mq
@@ -257,6 +320,11 @@
${appium.version}jar
+
+ com.microsoft.sqlserver
+ mssql-jdbc
+ 12.10.0.jre11
+ com.mysqlmysql-connector-j
diff --git a/Engine/src/main/java/com/ing/engine/commands/browser/Command.java b/Engine/src/main/java/com/ing/engine/commands/browser/Command.java
index a5a2cdfe..52f9d0bb 100644
--- a/Engine/src/main/java/com/ing/engine/commands/browser/Command.java
+++ b/Engine/src/main/java/com/ing/engine/commands/browser/Command.java
@@ -35,15 +35,14 @@
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
-/** Kafka Imports
-import org.apache.kafka.common.header.Header;
-import org.apache.avro.Schema;
-import org.apache.avro.generic.GenericRecord;
-import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.apache.kafka.clients.consumer.KafkaConsumer;
-import org.apache.kafka.clients.producer.KafkaProducer;
-import org.apache.kafka.clients.producer.ProducerRecord;
-*/
+/** Kafka Imports */
+// import org.apache.kafka.common.header.Header;
+// import org.apache.avro.Schema;
+// import org.apache.avro.generic.GenericRecord;
+// import org.apache.kafka.clients.consumer.ConsumerRecord;
+// import org.apache.kafka.clients.consumer.KafkaConsumer;
+// import org.apache.kafka.clients.producer.KafkaProducer;
+// import org.apache.kafka.clients.producer.ProducerRecord;
public class Command {
@@ -89,6 +88,9 @@ public class Command {
static public Map before = new HashMap<>();
static public Map after = new HashMap<>();
static public Map duration = new HashMap<>();
+ static public HashMap headerMap = new HashMap<>();
+ static public Map> headerKeyValueMap = new HashMap<>();
+
public String key;
static public String basicAuthorization;
/**
@@ -143,46 +145,44 @@ public class Command {
/**
* *** Kafka Parameters ****
*/
-
- /** Kafka Parameters
- static public Map> kafkaHeaders = new HashMap<>();
- static public Map kafkaProducerTopic = new HashMap<>();
- static public Map kafkaConsumerTopic = new HashMap<>();
- static public Map kafkaConsumerGroupId = new HashMap<>();
- static public Map kafkaServers = new HashMap<>();
- static public Map kafkaSchemaRegistryURL = new HashMap<>();
- static public Map kafkaPartition = new HashMap<>();
- static public Map kafkaTimeStamp = new HashMap<>();
- static public Map kafkaKey = new HashMap<>();
- static public Map kafkaKeySerializer = new HashMap<>();
- static public Map kafkaKeyDeserializer = new HashMap<>();
- static public Map kafkaValue = new HashMap<>();
- static public Map kafkaValueSerializer = new HashMap<>();
- static public Map kafkaValueDeserializer = new HashMap<>();
- static public Map kafkaConsumerPollRetries = new HashMap<>();
- static public Map kafkaConsumerPollDuration = new HashMap<>();
- static public Map kafkaAvroSchema =new HashMap<>();
- static public Map> kafkaGenericRecord =new HashMap<>();
- static public Map kafkaGenericRecordValue =new HashMap<>();
- static public Map> kafkaAvroProducer =new HashMap<>();
- static public Map> kafkaConfigs = new HashMap<>();
- static public Map kafkaProducersslConfigs = new HashMap<>();
- static public Map kafkaConsumersslConfigs = new HashMap<>();
- static public Map kafkaAvroCompatibleMessage = new HashMap<>();
- static public Map kafkaConsumeRecordCount = new HashMap<>();
- static public Map kafkaConsumeRecordValue = new HashMap<>();
- static public Map kafkaSharedSecret = new HashMap<>();
- static public Map>> kafkaConsumerRecords = new HashMap<>();
- static public Map> kafkaConsumerPollRecord = new HashMap<>();
- static public Map kafkaRecordIdentifierValue = new HashMap<>();
- static public Map kafkaRecordIdentifierPath = new HashMap<>();
- static public Map kafkaConsumerMaxPollRecords = new HashMap<>();
- static public Map kafkaAutoRegisterSchemas = new HashMap<>();
- static public Map kafkaProducerRecord = new HashMap<>();
- static public Map kafkaConsumerRecord = new HashMap<>();
- static public Map kafkaProducer = new HashMap<>();
- static public Map kafkaConsumer = new HashMap<>();
- */
+ // static public Map> kafkaHeaders = new HashMap<>();
+ // static public Map kafkaProducerTopic = new HashMap<>();
+ // static public Map kafkaConsumerTopic = new HashMap<>();
+ // static public Map kafkaConsumerGroupId = new HashMap<>();
+ // static public Map kafkaServers = new HashMap<>();
+ // static public Map kafkaSchemaRegistryURL = new HashMap<>();
+ // static public Map kafkaPartition = new HashMap<>();
+ // static public Map kafkaTimeStamp = new HashMap<>();
+ // static public Map kafkaKey = new HashMap<>();
+ // static public Map kafkaKeySerializer = new HashMap<>();
+ // static public Map kafkaKeyDeserializer = new HashMap<>();
+ // static public Map kafkaValue = new HashMap<>();
+ // static public Map kafkaValueSerializer = new HashMap<>();
+ // static public Map kafkaValueDeserializer = new HashMap<>();
+ // static public Map kafkaConsumerPollRetries = new HashMap<>();
+ // static public Map kafkaConsumerPollDuration = new HashMap<>();
+ // static public Map kafkaAvroSchema =new HashMap<>();
+ // static public Map> kafkaGenericRecord =new HashMap<>();
+ // static public Map kafkaGenericRecordValue =new HashMap<>();
+ // static public Map> kafkaAvroProducer =new HashMap<>();
+ // static public Map> kafkaConfigs = new HashMap<>();
+ // static public Map kafkaProducersslConfigs = new HashMap<>();
+ // static public Map kafkaConsumersslConfigs = new HashMap<>();
+ // static public Map kafkaAvroCompatibleMessage = new HashMap<>();
+ // static public Map kafkaConsumeRecordCount = new HashMap<>();
+ // static public Map kafkaConsumeRecordValue = new HashMap<>();
+ // static public Map kafkaSharedSecret = new HashMap<>();
+ // static public Map>> kafkaConsumerRecords = new HashMap<>();
+ // static public Map> kafkaConsumerPollRecord = new HashMap<>();
+ // static public Map kafkaRecordIdentifierValue = new HashMap<>();
+ // static public Map kafkaRecordIdentifierPath = new HashMap<>();
+ // static public Map kafkaConsumerMaxPollRecords = new HashMap<>();
+ // static public Map kafkaAutoRegisterSchemas = new HashMap<>();
+ // static public Map kafkaProducerRecord = new HashMap<>();
+ // static public Map kafkaConsumerRecord = new HashMap<>();
+ // static public Map kafkaProducer = new HashMap<>();
+ // static public Map kafkaConsumer = new HashMap<>();
+ // static public Map>> kafkaRecordIdentifier = new HashMap<>();
public Command(CommandControl cc) {
Commander = cc;
diff --git a/Engine/src/main/java/com/ing/engine/commands/browser/DynamicObject.java b/Engine/src/main/java/com/ing/engine/commands/browser/DynamicObject.java
index 5bdf8069..0d0160be 100644
--- a/Engine/src/main/java/com/ing/engine/commands/browser/DynamicObject.java
+++ b/Engine/src/main/java/com/ing/engine/commands/browser/DynamicObject.java
@@ -3,16 +3,21 @@
import com.ing.engine.core.CommandControl;
import com.ing.engine.drivers.AutomationObject;
+import com.ing.engine.reporting.impl.html.bdd.Report;
+import com.ing.engine.reporting.util.RDS;
import com.ing.engine.support.Status;
+import com.ing.engine.support.Step;
import com.ing.engine.support.methodInf.Action;
import com.ing.engine.support.methodInf.InputType;
import com.ing.engine.support.methodInf.ObjectType;
+
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
*
- *
+ *
*/
public class DynamicObject extends Command {
@@ -74,4 +79,55 @@ private void setProperty(String key, String value) {
AutomationObject.dynamicValue.get(Reference).get(ObjectName).put(key, value);
}
}
+
+ @Action(object = ObjectType.PLAYWRIGHT, desc = "Set filter `Has Text` for the locator", input = InputType.YES, condition = InputType.NO)
+ public void setFilterHasText() {
+ if (!Data.isEmpty()) {
+ AutomationObject.locatorFiltersMap.computeIfAbsent(Reference+ObjectName, k -> new ArrayList<>()).add("setHasText: "+ Data);
+ String text = String.format("Setting Filter 'Has Text' with '%s' for Object [%s - %s]",
+ Data, Reference, ObjectName);
+ Report.updateTestLog(Action, text, Status.DONE);
+ } else {
+ Report.updateTestLog(Action, "Input should not be empty", Status.FAILNS);
+ }
+ }
+
+ @Action(object = ObjectType.PLAYWRIGHT, desc = "Set filter `Has Not Text` for the locator", input = InputType.YES, condition = InputType.NO)
+ public void setFilterHasNotText() {
+ if (!Data.isEmpty()) {
+ AutomationObject.locatorFiltersMap.computeIfAbsent(Reference+ObjectName, k -> new ArrayList<>()).add("setHasNotText: "+ Data);
+ String text = String.format("Setting Filter 'Has Not Text' with '%s' for Object [%s - %s]",
+ Data, Reference, ObjectName);
+ Report.updateTestLog(Action, text, Status.DONE);
+ } else {
+ Report.updateTestLog(Action, "Input should not be empty", Status.FAILNS);
+ }
+ }
+
+ @Action(object = ObjectType.PLAYWRIGHT, desc = "Set filter `Visible` for the locator", input = InputType.YES, condition = InputType.NO)
+ public void setFilterIsVisible() {
+ if (!Data.isEmpty()) {
+ AutomationObject.locatorFiltersMap.computeIfAbsent(Reference+ObjectName, k -> new ArrayList<>()).add("setVisible: "+ Data);
+ String text = String.format("Setting Filter 'Visible' with '%s' for Object [%s - %s]",
+ Data, Reference, ObjectName);
+ Report.updateTestLog(Action, text, Status.DONE);
+ } else {
+ Report.updateTestLog(Action, "Input should not be empty", Status.FAILNS);
+ }
+ }
+
+ @Action(object = ObjectType.PLAYWRIGHT, desc = "Set filter `Index` for the locator", input = InputType.YES, condition = InputType.NO)
+ public void setFilterIndex() {
+ if (!Data.isEmpty()) {
+ AutomationObject.locatorFiltersMap.computeIfAbsent(Reference+ObjectName, k -> new ArrayList<>()).add("setIndex: "+ Data);
+ String text = String.format("Setting Filter 'Index' with '%s' for Object [%s - %s]",
+ Data, Reference, ObjectName);
+ Report.updateTestLog(Action, text, Status.DONE);
+ } else {
+ Report.updateTestLog(Action, "Input should not be empty", Status.FAILNS);
+ }
+ }
+
+
+
}
diff --git a/Engine/src/main/java/com/ing/engine/commands/database/Database.java b/Engine/src/main/java/com/ing/engine/commands/database/Database.java
index a833af0d..bf240c92 100644
--- a/Engine/src/main/java/com/ing/engine/commands/database/Database.java
+++ b/Engine/src/main/java/com/ing/engine/commands/database/Database.java
@@ -12,15 +12,24 @@
import java.util.List;
/**
- *
- *
+ * Provides database-specific command implementations for executing queries, asserting results,
+ * storing values, and managing database connections. Extends General for common database utilities.
*/
public class Database extends General {
+ /**
+ * Constructs a Database command handler with the given command control.
+ *
+ * @param cc the command control context
+ */
public Database(CommandControl cc) {
super(cc);
}
+ /**
+ * Initiates the database connection using the input database name.
+ * Updates the test log with connection status and metadata.
+ */
@Action(object = ObjectType.DATABASE, desc = "Initiate the DB transaction", input = InputType.YES)
public void initDBConnection() {
try {
@@ -45,6 +54,9 @@ public void initDBConnection() {
}
}
+ /**
+ * Executes a SELECT query and updates the test log with the result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Execute the Query in []", input = InputType.YES)
public void executeSelectQuery() {
try {
@@ -56,13 +68,17 @@ public void executeSelectQuery() {
}
}
+ /**
+ * Executes a DML query (INSERT, UPDATE, DELETE) and updates the test log with the result and query used.
+ */
@Action(object = ObjectType.DATABASE, desc = "Execute the Query in []", input = InputType.YES)
public void executeDMLQuery() {
try {
- if (executeDML()) {
- Report.updateTestLog(Action, " Table updated by using " + Data, Status.PASSNS);
+ DMLResult result = executeDML();
+ if (result.success) {
+ Report.updateTestLog(Action, "Table updated by using query: " + result.query, Status.PASSNS);
} else {
- Report.updateTestLog(Action, " Table not updated by using " + Data, Status.FAILNS);
+ Report.updateTestLog(Action, "Table not updated by using query: " + result.query, Status.FAILNS);
}
} catch (SQLException ex) {
Report.updateTestLog(Action, "Error executing the SQL Query: " + ex.getMessage(),
@@ -70,6 +86,10 @@ public void executeDMLQuery() {
}
}
+ /**
+ * Asserts that the value in Data exists in the specified column (Condition) of the database.
+ * Updates the test log with the assertion result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Assert the value [] exist in the column [] ", input = InputType.YES, condition = InputType.YES)
public void assertDBResult() {
if (assertDB(Condition, Data)) {
@@ -79,6 +99,10 @@ public void assertDBResult() {
}
}
+ /**
+ * Stores the value from the specified DB column (Condition) in a global variable (Input).
+ * Updates the test log with the storage result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Store it in Global variable from the DB column [] ", input = InputType.YES, condition = InputType.YES)
public void storeValueInGlobalVariable() {
storeValue(Input, Condition, true);
@@ -89,6 +113,10 @@ public void storeValueInGlobalVariable() {
}
}
+ /**
+ * Stores the value from the specified DB column (Condition) in a local variable (Input).
+ * Updates the test log with the storage result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Store it in the variable from the DB column [] ", input = InputType.YES, condition = InputType.YES)
public void storeValueInVariable() {
storeValue(Input, Condition, false);
@@ -99,6 +127,10 @@ public void storeValueInVariable() {
}
}
+ /**
+ * Stores the value from the specified DB column (Condition) in the test data sheet (Input).
+ * Updates the test log with the storage result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Save DB value in Test Data Sheet", input = InputType.YES, condition = InputType.YES)
public void storeDBValueinDataSheet() {
try {
@@ -134,6 +166,9 @@ public void storeDBValueinDataSheet() {
}
}
+ /**
+ * Closes the database connection and updates the test log with the result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Close the DB Connection")
public void closeDBConnection() {
try {
@@ -148,6 +183,9 @@ public void closeDBConnection() {
}
}
+ /**
+ * Verifies table values against the test data sheet and updates the test log with the result.
+ */
@Action(object = ObjectType.DATABASE, desc = "Verify Table values with the Test Data sheet ", input = InputType.YES)
public void verifyWithDataSheet() {
String sheetName = Data;
@@ -171,8 +209,8 @@ public void verifyWithDataSheet() {
}
/**
- * Under the assumption that 1. User executed only SELECT Query 2. Returns a
- * column with one or more rows
+ * Stores the result of a SELECT query in runtime variable(s) based on the specified condition.
+ * Assumes the query returns one or more rows in a column.
*/
@Action(object = ObjectType.DATABASE, desc = "Query and save the result in variable(s) ", input = InputType.YES, condition = InputType.YES)
public void storeResultInVariable() {
@@ -205,7 +243,8 @@ public void storeResultInVariable() {
}
/**
- * Under the assumption that 1. User executed only SELECT Query
+ * Stores the result of a SELECT query in the datasheet based on the specified condition.
+ * Assumes the query returns one or more rows.
*/
@Action(object = ObjectType.DATABASE, desc = "Query and save the result in Datasheet ", input = InputType.YES, condition = InputType.YES)
public void storeResultInDataSheet() {
diff --git a/Engine/src/main/java/com/ing/engine/commands/database/General.java b/Engine/src/main/java/com/ing/engine/commands/database/General.java
index 546832e5..7eab1892 100644
--- a/Engine/src/main/java/com/ing/engine/commands/database/General.java
+++ b/Engine/src/main/java/com/ing/engine/commands/database/General.java
@@ -19,8 +19,9 @@
import java.util.regex.Pattern;
/**
- *
- *
+ * Provides database command utilities for executing SQL queries, managing connections,
+ * handling variable resolution, and storing results. This class is intended to be extended
+ * for specific database operations and supports both DML and SELECT queries.
*/
public class General extends Command {
@@ -39,10 +40,23 @@ public class General extends Command {
static final Pattern INPUTS = Pattern.compile("([^{]+?)(?=\\})");
static List colNames = new ArrayList<>();
+ /**
+ * Constructs a General database command handler with the given command control.
+ *
+ * @param cc the command control context
+ */
public General(CommandControl cc) {
super(cc);
}
+ /**
+ * Verifies and establishes a database connection using the specified database name.
+ *
+ * @param dbName the name or alias of the database
+ * @return true if the connection is established successfully, false otherwise
+ * @throws ClassNotFoundException if the database driver class is not found
+ * @throws SQLException if a database access error occurs
+ */
public boolean verifyDbConnection(String dbName) throws ClassNotFoundException, SQLException {
if (getDBFile(dbName).exists()) {
Properties dbDetails = getDBDetails(dbName);
@@ -95,32 +109,69 @@ private String resolveAllVariables(String str) {
return str;
}
+ /**
+ * Executes a SELECT SQL query after resolving variables and stores the result set.
+ *
+ * @throws SQLException if a database access error occurs
+ */
public void executeSelect() throws SQLException {
String query = Data;
query = handleDataSheetVariables(query);
- query = handleuserDefinedVariables(query);
+ query = handleUserDefinedVariables(query);
System.out.println("Query :" + query);
result = statement.executeQuery(query);
resultData = result.getMetaData();
populateColumnNames();
}
- public boolean executeDML() throws SQLException {
+ /**
+ * Represents the result of a DML operation, including success status and the executed query.
+ */
+ public static class DMLResult {
+ public final boolean success;
+ public final String query;
+ public DMLResult(boolean success, String query) {
+ this.success = success;
+ this.query = query;
+ }
+ }
+
+ /**
+ * Executes a DML SQL query (INSERT, UPDATE, DELETE) after resolving variables.
+ *
+ * @return a DMLResult containing the success status and the executed query
+ * @throws SQLException if a database access error occurs
+ */
+ public DMLResult executeDML() throws SQLException {
String query = Data;
- query = handleDataSheetVariables(query);
- query = handleuserDefinedVariables(query);
- System.out.println("Query :" + query);
- return (statement.executeUpdate(query) >= 0);
+ query = handleDataSheetVariables(query);
+ query = handleUserDefinedVariables(query);
+ System.out.println("Executing DML query: :" + query);
+ boolean result = (statement.executeUpdate(query) >= 0);
+ return new DMLResult(result, query);
}
+ /**
+ * Initializes the database connection, statement, and variable resolution.
+ *
+ * @param commit whether to use auto-commit mode
+ * @param timeout the query timeout in seconds
+ * @throws SQLException if a database access error occurs
+ */
private void initialize(Boolean commit,int timeout) throws SQLException {
colNames.clear();
dbconnection.setAutoCommit(commit);
- statement = dbconnection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
+ statement = dbconnection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
statement.setQueryTimeout(timeout);
resolveVars();
}
+ /**
+ * Closes the database connection, statement, and result set.
+ *
+ * @return true if all resources are closed successfully, false otherwise
+ * @throws SQLException if a database access error occurs
+ */
public boolean closeConnection() throws SQLException {
if (dbconnection != null && statement != null && result != null) {
dbconnection.close();
@@ -131,6 +182,13 @@ public boolean closeConnection() throws SQLException {
return true;
}
+ /**
+ * Asserts that a value exists in the specified column of the result set.
+ *
+ * @param columnName the column to check
+ * @param condition the value to assert
+ * @return true if the value exists, false otherwise
+ */
public boolean assertDB(String columnName, String condition) {
boolean isExist = false;
try {
@@ -152,6 +210,13 @@ public boolean assertDB(String columnName, String condition) {
return isExist;
}
+ /**
+ * Stores a value from the result set in a variable or global variable.
+ *
+ * @param input the variable name
+ * @param condition the column and row specification
+ * @param isGlobal true to store as a global variable, false for local
+ */
public void storeValue(String input, String condition, boolean isGlobal) {
String value;
int rowIndex = 1;
@@ -182,6 +247,9 @@ public void storeValue(String input, String condition, boolean isGlobal) {
}
}
+ /**
+ * Resolves variables in the Data string and replaces them with their values.
+ */
private void resolveVars() {
Matcher matcher = INPUTS.matcher(Data);
Set listMatches = new HashSet<>();
@@ -203,10 +271,21 @@ private void resolveVars() {
}
+ /**
+ * Retrieves database properties for the specified database name.
+ *
+ * @param dbName the database name or alias
+ * @return the database properties
+ */
public Properties getDBDetails(String dbName) {
return getDataBaseData(dbName);
}
+ /**
+ * Populates the column names from the result set metadata.
+ *
+ * @throws SQLException if a database access error occurs
+ */
private void populateColumnNames() throws SQLException {
int count = resultData.getColumnCount();
for (int index = 1; index <= count; index++) {
@@ -214,10 +293,22 @@ private void populateColumnNames() throws SQLException {
}
}
+ /**
+ * Gets the index of the specified column name in the column list.
+ *
+ * @param columnName the column name to search for
+ * @return the index of the column, or -1 if not found
+ */
public int getColumnIndex(String columnName) {
return colNames.indexOf(columnName);
}
+ /**
+ * Resolves datasheet variables in the query string.
+ *
+ * @param query the SQL query string
+ * @return the query with datasheet variables replaced
+ */
private String handleDataSheetVariables(String query) {
List sheetlist = Control.getCurrentProject().getTestData().getTestDataFor(Control.exe.runEnv())
.getTestDataNames();
@@ -237,7 +328,13 @@ private String handleDataSheetVariables(String query) {
return query;
}
- private String handleuserDefinedVariables(String query) {
+ /**
+ * Resolves user-defined variables in the query string.
+ *
+ * @param query the SQL query string
+ * @return the query with user-defined variables replaced
+ */
+ private String handleUserDefinedVariables(String query) {
Collection