This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 9ef561d723 Fix the Hop Web selenium failures in the transform dialog 
sweep (#8900)
9ef561d723 is described below

commit 9ef561d723e973998cd60918eaf154331989fd2f
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Oct 14 09:50:36 2026 +0200

    Fix the Hop Web selenium failures in the transform dialog sweep (#8900)
---
 .../combinationlookup/CombinationLookupDialog.java | 11 +++++-
 .../hop/web/it/pages/ExecutionResultsPanel.java    | 32 +++++++++++------
 web-tests/src/test/resources/transforms.csv        | 41 +++++++++++++++++-----
 3 files changed, 63 insertions(+), 21 deletions(-)

diff --git 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
index 2b3c800d74..4ce0eaf780 100644
--- 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
+++ 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
@@ -659,8 +659,17 @@ public class CombinationLookupDialog extends 
BaseTransformDialog {
     //
     String[] fieldNames = ConstUi.sortFieldNames(inputFields);
     ciKey[1].setComboValues(fieldNames);
+    // Called from the background thread that reads the input fields: touch 
the widget on the UI
+    // thread only, or SWT throws "Invalid thread access".
     if (wHashFieldInStream != null && !wHashFieldInStream.isDisposed()) {
-      wHashFieldInStream.setItems(fieldNames);
+      wHashFieldInStream
+          .getDisplay()
+          .asyncExec(
+              () -> {
+                if (!wHashFieldInStream.isDisposed()) {
+                  wHashFieldInStream.setItems(fieldNames);
+                }
+              });
     }
   }
 
diff --git 
a/web-tests/src/test/java/org/apache/hop/web/it/pages/ExecutionResultsPanel.java
 
b/web-tests/src/test/java/org/apache/hop/web/it/pages/ExecutionResultsPanel.java
index 754f7a74b1..5b515b6e33 100644
--- 
a/web-tests/src/test/java/org/apache/hop/web/it/pages/ExecutionResultsPanel.java
+++ 
b/web-tests/src/test/java/org/apache/hop/web/it/pages/ExecutionResultsPanel.java
@@ -43,27 +43,37 @@ public class ExecutionResultsPanel {
    * that has both those names and rows under it - the table is nested several 
containers deep and
    * every one of them "contains" the header text.
    *
-   * <p>A row is recognised by having exactly two children per column: a table 
row lays out as an
-   * alternating run of spacer and cell elements, which is also why only the 
odd children are read.
-   * The cells cannot simply be filtered on having text, because a metric that 
is genuinely empty
-   * would drop out and shift every column after it.
+   * <p>A row is a run of absolutely positioned leaf elements: one background 
per column, plus one
+   * text element for every cell that has ever held a value. The text elements 
cannot be read in
+   * document order. RAP creates a cell's text element the first time the cell 
gets a value and
+   * appends it to the row, so a metric that was still empty on the first 
paint ("Duration") ends up
+   * after "Status" and shifts every column read by position. Each text 
element is put in the column
+   * whose header starts at or left of it instead, and a cell that never held 
a value stays empty.
    */
   private static final String METRICS =
-      "const candidates=[...document.querySelectorAll('div')].filter("
+      "const left=e=>parseFloat(e.style.left)||0;"
+          + "const candidates=[...document.querySelectorAll('div')].filter("
           + "d=>d.textContent.includes('Transform 
Name')&&d.textContent.includes('Written (rows)'));"
           + "for(let i=candidates.length-1;i>=0;i--){"
           + "const table=candidates[i];"
           + "const 
head=[...table.children].find(c=>c.textContent.includes('Transform Name'));"
           + "if(!head)continue;"
-          + "const 
headers=[...head.children].map(c=>c.textContent.trim()).filter(t=>t.length>0);"
-          + "if(headers.length<"
+          + "const 
columns=[...head.children].filter(c=>c.textContent.trim().length>0)"
+          + 
".map(c=>({name:c.textContent.trim(),left:left(c)})).sort((a,b)=>a.left-b.left);"
+          + "if(columns.length<"
           + MIN_COLUMNS
           + ")continue;"
-          + "const rows=[...table.querySelectorAll('div')].filter(d=>"
-          + 
"d.children.length===headers.length*2&&[...d.children].every(c=>c.children.length===0));"
+          // The grid also paints empty filler rows below the last transform: 
skip those.
+          + "const rows=[...table.querySelectorAll('div')].filter(d=>d!==head"
+          + 
"&&d.children.length>=columns.length&&d.textContent.trim().length>0"
+          + "&&[...d.children].every(c=>c.children.length===0));"
           + "if(rows.length===0)continue;"
-          + "return {headers:headers,"
-          + 
"rows:rows.map(r=>[...r.children].filter((c,n)=>n%2===1).map(c=>c.textContent.trim()))};}"
+          + "return {headers:columns.map(c=>c.name),rows:rows.map(r=>{"
+          + "const cells=columns.map(()=>'');"
+          + "for(const c of r.children){const 
text=c.textContent.trim();if(!text)continue;"
+          + "let n=-1;columns.forEach((col,k)=>{if(col.left<=left(c))n=k;});"
+          + "if(n>=0)cells[n]=text;}"
+          + "return cells;})};}"
           + "return {headers:[],rows:[]};";
 
   private final WebDriver driver;
diff --git a/web-tests/src/test/resources/transforms.csv 
b/web-tests/src/test/resources/transforms.csv
index c030c12d93..67edc12457 100644
--- a/web-tests/src/test/resources/transforms.csv
+++ b/web-tests/src/test/resources/transforms.csv
@@ -47,6 +47,9 @@ Azure Event Hubs listener
 Azure Event Hubs writer
 Beam Bigtable input
 Beam Bigtable output
+Beam Debezium input
+Beam Elasticsearch input
+Beam Elasticsearch output
 Beam file input
 Beam file output
 Beam GCP Pub/Sub: publish
@@ -55,6 +58,12 @@ Beam Kafka consume
 Beam Kafka produce
 Beam Kinesis consume
 Beam Kinesis produce
+Beam MQTT input
+Beam MQTT output
+Beam partition
+Beam Snowflake input
+Beam Snowflake output
+Beam Splunk output
 Beam timestamp
 Beam window
 BigQuery input
@@ -79,6 +88,8 @@ Copy rows to result
 CrateDB bulk loader
 Credit card validator
 CSV file input
+Cypher builder
+Cypher output
 Data grid
 Data set input
 Data set output
@@ -88,6 +99,7 @@ Data validator
 Database impact input
 Database join
 Database lookup
+Database value validation
 DDL
 De-serialize from file
 Delay row
@@ -100,6 +112,7 @@ Dummy (do nothing)
 Dynamic SQL row
 EDI to XML
 Email messages input
+Embed text
 Enhanced JSON output
 ETL metadata injection
 Execute a process
@@ -107,12 +120,14 @@ Execute row SQL script
 Execute SQL script
 Execute unit tests
 Execution information
+Extract graph
 Fake data
 File exists
 File metadata
 Filter rows
 Formula
 Fuzzy match
+Generate graph CSVs
 Generate random value
 Generate rows
 Generate Snowflake ID
@@ -120,8 +135,9 @@ Get data from XML
 Get file names
 Get files from result
 Get files rows count
+Get graph logging info
+Get graph schema
 Get JDBC metadata
-Get Neo4j logging info
 Get records from stream (deprecated)
 Get rows from result
 Get server status
@@ -129,9 +145,13 @@ Get subfolder names
 Get system info
 Get table names
 Get variables
+Git Input
 Google Analytics 4
 Google Sheets input
 Google Sheets output
+Graph output
+Graph query
+Graph vector search
 Group by
 HTML to text
 HTTP client
@@ -150,6 +170,8 @@ JSON normalize input
 JSON output
 Kafka consumer
 Kafka producer
+Lake table input
+Lake table output
 Language model chat
 LDAP input
 LDAP output
@@ -157,6 +179,7 @@ Load file content in memory
 Mail
 Mapping input
 Mapping output
+Mask fields
 Memory group by
 Merge join
 Merge rows (diff)
@@ -173,24 +196,22 @@ MS SQL Server bulk loader
 Multi Mapping
 Multiway merge join
 MySQL bulk loader
-Neo4j Cypher
-Neo4j Cypher builder
-Neo4j generate CSVs
-Neo4j graph output
 Neo4j import
-Neo4j output
-Neo4j split graph
 Null if
 Number range
 OData input
 Oracle bulk loader
+Output transform metrics
 Parquet file input
 Parquet file output
 PGP decrypt stream
 PGP encrypt stream
+pgvector search
+pgvector upsert
 Pipeline executor
 Pipeline logging
 Pipeline probe
+Plugin Catalog
 PostgreSQL bulk loader
 Process files
 Properties input
@@ -232,13 +253,12 @@ Sorted merge
 Sorted schema merge
 Spark file input
 Spark file output
-Spark lake table input
 Spark lake table maintenance
 Spark lake table merge
-Spark lake table output
 Spark SQL
 Split field to rows
 Split fields
+Split graph
 Splunk input
 SQL file output
 SSTable output
@@ -248,6 +268,7 @@ Stream lookup
 Stream schema merge
 String operations
 Strings cut
+Structured extract
 Surefire report output
 Switch / case
 Synchronize after merge
@@ -256,6 +277,7 @@ Table exists
 Table input
 Table output
 Teradata FastLoad bulk loader
+Text chunker
 Text file input
 Text file output
 Token replacement
@@ -268,6 +290,7 @@ Value mapper
 vCard input
 vCard output
 Vertica bulk loader
+Watch Files
 Web services lookup
 Workflow executor
 Workflow logging

Reply via email to