Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 15 additions & 57 deletions .github/workflows/validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,21 @@ jobs:
if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-latest
timeout-minutes: 45
concurrency:
group: saucelabs-testbench # Global queue for SauceLabs tests only
cancel-in-progress: false
services:
selenium:
image: selenium/standalone-chrome:latest
ports:
- 4444:4444
options: >-
--shm-size=2g
--add-host=host.docker.internal:host-gateway
--health-cmd "curl -fsS http://localhost:4444/wd/hub/status"
--health-interval=5s
--health-timeout=10s
--health-retries=30
--health-start-period=10s
strategy:
max-parallel: 1 # Only one JUnit version at a time to stay within SauceLabs limit
fail-fast: false
matrix:
include:
- name: JUnit 4
Expand Down Expand Up @@ -180,63 +190,13 @@ jobs:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}

- name: Set up Sauce Labs tunnel
uses: saucelabs/sauce-connect-action@v3.0.0
with:
username: ${{ secrets.SAUCE_USERNAME }}
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
tunnelName: ${{ github.run_id }}-${{ github.run_number }}
region: us-west-1
retryTimeout: 300
proxyLocalhost: allow

- name: Wait for Sauce Labs tunnel to be ready
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
SAUCE_TUNNEL_ID: ${{ github.run_id }}-${{ github.run_number }}
run: |
# sauce-connect-action returns once the local process is up, but the tunnel
# can still be propagating on the Sauce side — poll the REST API until it
# actually shows as running before launching 25 parallel sessions.
# ?full=1 returns tunnel objects (not just IDs). sauce-connect-action@v3 uses
# Sauce Connect 5 which stores the name in `tunnel_name`; fall back to
# `tunnel_identifier` for SC4-era responses just in case.
URL="https://api.us-west-1.saucelabs.com/rest/v1/${SAUCE_USERNAME}/tunnels?full=1&filter=running"
echo "Probing Sauce Labs for tunnel ${SAUCE_TUNNEL_ID}..."
LAST_RESPONSE=""
for i in $(seq 1 30); do
if RESPONSE=$(curl -sSf -u "${SAUCE_USERNAME}:${SAUCE_ACCESS_KEY}" "$URL"); then
LAST_RESPONSE="$RESPONSE"
if echo "$RESPONSE" | jq -e --arg id "$SAUCE_TUNNEL_ID" \
'any(.[]; (.tunnel_name // .tunnel_identifier) == $id and (.status // "running") == "running")' > /dev/null; then
echo "Tunnel ${SAUCE_TUNNEL_ID} is running (attempt $i)."
# Grace period to let the tunnel fully stabilize before 25 parallel sessions hit it
sleep 10
exit 0
fi
echo "Tunnel not listed yet (attempt $i/30), sleeping 5s..."
else
echo "Sauce API request failed (attempt $i/30), sleeping 5s..." >&2
fi
sleep 5
done
echo "Tunnel ${SAUCE_TUNNEL_ID} did not become ready in time" >&2
echo "Last response from Sauce API:" >&2
echo "$LAST_RESPONSE" | jq '.' >&2 || echo "$LAST_RESPONSE" >&2
exit 1

- name: Set TB License
run: |
TB_LICENSE=${{secrets.TB_LICENSE}}
mkdir -p ~/.vaadin/
echo '{"username":"'`echo $TB_LICENSE | cut -d / -f1`'","proKey":"'`echo $TB_LICENSE | cut -d / -f2`'"}' > ~/.vaadin/proKey

- name: Run Integration Tests
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
SAUCE_TUNNEL_ID: ${{ github.run_id }}-${{ github.run_number }}
run: |
mvn verify \
-pl ${{ matrix.module }} -am \
Expand All @@ -245,10 +205,8 @@ jobs:
-Dsystem.com.vaadin.testbench.Parameters.testsInParallel=5 \
-Dsystem.com.vaadin.testbench.Parameters.maxAttempts=2 \
-Dcom.vaadin.testbench.Parameters.hubHostname=localhost \
-Dsauce.tunnelId=${SAUCE_TUNNEL_ID} \
-Ddeployment.hostname=host.docker.internal \
-Dfailsafe.forkCount=5 \
-Dsystem.sauce.user=${SAUCE_USERNAME} \
-Dsystem.sauce.sauceAccessKey=${SAUCE_ACCESS_KEY} \
-B

- name: Upload error screenshots
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

Expand Down Expand Up @@ -62,15 +61,11 @@ public Capabilities getCapabilities() {

@BrowserConfiguration
public List<DesiredCapabilities> getBrowserConfiguration() {
List<DesiredCapabilities> caps;
if (getDriver() instanceof RemoteWebDriver) {
caps = Arrays.asList(BrowserUtil.firefox(), BrowserUtil.chrome(),
BrowserUtil.safari(), BrowserUtil.edge());
} else {
caps = Collections.singletonList(BrowserUtil.chrome());
return Arrays.asList(BrowserUtil.firefox(), BrowserUtil.chrome(),
BrowserUtil.edge());
}
caps.forEach(des -> des.setPlatform(Platform.WIN10));
return caps;
return Collections.singletonList(BrowserUtil.chrome());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,48 @@
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import com.vaadin.testbench.Parameters;
import com.vaadin.testbench.parallel.BrowserUtil;
import com.vaadin.testbench.parallel.SauceLabsIntegration;

/**
* Example of how to use SeleniumJupiter together with TestBench 9+ features.
* Example of how to use SeleniumJupiter against a remote grid together with
* TestBench 9+ features. The grid is either Sauce Labs (when configured) or a
* Selenium hub selected via the {@code hubHostname} parameter (e.g. the
* {@code selenium/standalone-chrome} container used in CI).
*
* @author Vaadin Ltd
*/
public abstract class AbstractSeleniumSauceTB9Test
extends AbstractSeleniumTB9Test {

@DriverUrl
String url = SauceLabsIntegration.getHubUrl();
String url = getHubUrl();

@DriverCapabilities
DesiredCapabilities capabilities = new DesiredCapabilities();
{
capabilities.merge(BrowserUtil.chrome());
capabilities.setPlatform(Platform.WIN10);
SauceLabsIntegration.setDesiredCapabilities(capabilities);
}
DesiredCapabilities capabilities = createCapabilities();

@BeforeEach
public void setDriver(RemoteWebDriver driver) {
super.setDriver(driver);
}

private static String getHubUrl() {
if (SauceLabsIntegration.isConfiguredForSauceLabs()) {
return SauceLabsIntegration.getHubUrl();
}
return String.format("http://%s:%d/wd/hub", Parameters.getHubHostname(),
Parameters.getHubPort());
}

private static DesiredCapabilities createCapabilities() {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.merge(BrowserUtil.chrome());
if (SauceLabsIntegration.isConfiguredForSauceLabs()) {
capabilities.setPlatform(Platform.WIN10);
SauceLabsIntegration.setDesiredCapabilities(capabilities);
}
return capabilities;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.vaadin.flow.component.Component;
import com.vaadin.testbench.AbstractBrowserDriverTestBase;
import com.vaadin.testbench.Parameters;
import com.vaadin.testbench.parallel.SauceLabsIntegration;

/**
Expand Down Expand Up @@ -135,4 +136,18 @@ protected int getDeploymentPort() {
public static boolean isConfiguredForSauceLabs() {
return SauceLabsIntegration.isConfiguredForSauceLabs();
}

/**
* Whether the tests are configured to run against a remote grid/hub, either
* a Selenium hub (e.g. the {@code selenium/standalone-chrome} container in
* CI, selected via the {@code hubHostname} parameter) or Sauce Labs.
* <p>
* Used to gate the SeleniumJupiter example tests: the local-driver variant
* only makes sense when no remote grid is configured, while the hub variant
* requires one.
*/
public static boolean isConfiguredForHub() {
return Parameters.getHubHostname() != null
|| SauceLabsIntegration.isConfiguredForSauceLabs();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import com.vaadin.flow.component.Component;
import com.vaadin.testUI.PageObjectView;

@EnabledIf("isConfiguredForSauceLabs")
@EnabledIf("isConfiguredForHub")
public class SeleniumHubPageObjectIT extends AbstractSeleniumSauceTB9Test {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import com.vaadin.flow.component.Component;
import com.vaadin.testUI.PageObjectView;

@DisabledIf("isConfiguredForSauceLabs")
@DisabledIf("isConfiguredForHub")
public class SeleniumLocalPageObjectIT extends AbstractSeleniumChromeTB9Test {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public class TB9TestBrowserFactory extends DefaultBrowserFactory {
@Override
public DesiredCapabilities create(Browser browser, String version,
Platform platform) {
if (browser != Browser.SAFARI) {
platform = Platform.WIN10;
}
DesiredCapabilities desiredCapabilities = super.create(browser, version,
platform);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public abstract class AbstractTB6Test extends ParallelTest {
@BrowserConfiguration
public List<DesiredCapabilities> getBrowserConfiguration() {
return Arrays.asList(BrowserUtil.firefox(), BrowserUtil.chrome(),
BrowserUtil.safari(), BrowserUtil.edge());
BrowserUtil.edge());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public class TB6TestBrowserFactory extends DefaultBrowserFactory {
@Override
public DesiredCapabilities create(Browser browser, String version,
Platform platform) {
if (browser != Browser.SAFARI) {
platform = Platform.WIN10;
}
DesiredCapabilities desiredCapabilities = super.create(browser, version,
platform);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
package com.vaadin.tests.elements;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.openqa.selenium.By;

import com.vaadin.testUI.ElementQueryView;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.tests.AbstractTB6Test;

@Ignore("Viewport resize does not work")
public class ElementScreenCompareIT extends AbstractTB6Test {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,20 @@ public void scrollIntoViewBringsTargetIntoViewport() {
TestBenchElement target = $(TestBenchElement.class)
.id("target-element");
target.scrollIntoView(Map.of("block", "nearest", "inline", "nearest"));
Long left = (Long) executeScript(
"return Math.round(arguments[0].getBoundingClientRect().left)",
target);
Long viewportWidth = (Long) executeScript("return window.innerWidth");
Assert.assertTrue(
"Target element left (" + left
+ ") should be within viewport width (" + viewportWidth
+ ") after scrollIntoView",
left >= 0 && left < viewportWidth);
// The grid-like container compensates for the scroll by applying a
// transform to the sticky tbody on the next animation frame (mimicking
// vaadin-grid's virtualizer), so the target only reaches its final
// position one frame after scrollIntoView returns. Poll until it
// settles rather than reading synchronously, which would otherwise race
// the rAF callback on fast (local) drivers.
waitUntil(driver -> {
Long left = (Long) executeScript(
"return Math.round(arguments[0].getBoundingClientRect().left)",
target);
Long viewportWidth = (Long) executeScript(
"return window.innerWidth");
return left >= 0 && left < viewportWidth;
});
}

@Test
Expand Down
Loading