createRegattaLeaderboard() returned as soon as the create dialog's OK was
pressed, without waiting for the newly created regatta leaderboard row to be
rendered into the leaderboards cell table. A subsequent getLeaderboardDetails()
call could then race the AJAX round-trip, find no matching row yet, and return
null, causing NullPointerExceptions downstream (e.g. in linkRace()).
Wait until the new row (named after the regatta, see RegattaDescriptor.toString())
has been rendered before returning, mirroring createFlexibleLeaderboard(). This
fixes the intermittent failures of TestColumnsInLeaderboard.testLeaderboardPanelRefresh
and TestSwitchingOffStartOfRaceInference; the other callers of
createRegattaLeaderboard() were verified to still pass.
Assisted-By: Claude Opus 4.8 (Claude Code)
The self-service checkbox in LocalServerManagementPanel fired its
config-write RPC with a plain AsyncCallback and signalled progress via an
"updating" DOM attribute. Under Selenium 4 the W3C click() returns before
the browser dispatches the checkbox's ValueChangeHandler, so LocalServerPO
could observe "updating != true" and proceed before the RPC had even been
fired, letting createEvent run before the server was in self-service mode.
Bring the panel into the harness's standard pending-Ajax mechanism: mark the
updateServerConfiguration RPC with a MarkedAsyncCallback on a dedicated
category, and replace the bespoke "updating" barrier in LocalServerPO with
the proven baseline-before-click + monotonic finished-call-counter idiom
(as in LeaderboardPage.refresh()). Because the finished-call count only ever
increases, this is immune both to the click-return gap and to a boolean flag
toggling back between polls.
Also encapsulate the PENDING_AJAX_CALLS JavaScript: the literal now lives
once in AjaxCallsExecuted, which exposes getNumberOfFinishedCalls(...);
AjaxCallsComplete reuses the shared constant, and PageObject delegates via
getNumberOfFinishedAjaxRequests(...). LocalServerPO no longer contains JS.
Assisted-By: Claude (claude-4.8-opus)
Drop the leftover TODO and commented-out NO_REGATTA / BMW_CUP_RACE_8
constants and the surrounding blank lines; no behavioral change.
Assisted-By: Claude (claude-4.8-opus)
TestStartStopTrackingCycleForRaceLog failed intermittently with
NoSuchElementException on ACTION_START_TRACKING: at click time the race
rows still showed the un-denoted action set, i.e. the leaderboard
denotation had not applied client-side yet.
Root cause: the leaderboard-level denoteForRaceLogTracking RPC in
SmartphoneTrackingEventManagementPanel used a plain AsyncCallback, so it
was invisible to the PENDING_AJAX_CALLS counter that only
MarkedAsyncCallback touches. waitForAjaxRequests() therefore returned
before the denote->refresh chain settled, and the test clicked against
the stale DOM. This was exposed by the Selenium 3.141 -> 4.13 upgrade,
not by a bug6305 functional change.
Two fixes:
1. Production (root): wrap the denote RPC callback in MarkedAsyncCallback,
matching the sibling RPCs in the same panel, so the pending-call
counter stays non-zero until the whole denote->refresh chain settles.
Makes waitForAjaxRequests() honest for this RPC for any test.
2. Test robustness: startTrackingForRace now takes the race name and
waits state-based for the ACTION_START_TRACKING image to appear
(re-fetching the row each poll to survive GWT re-renders) before
clicking, via a new DataEntryPO.hasActionImage helper, instead of
assuming row presence implies the action is available.
Verified: test passes on two independent local runs (98.9s and 275.5s),
both denote->start->stop->undenote cycles green.
Assisted-By: Claude (claude-4.8-opus)
The user-creation rate limit and animated ACL dialogs caused flaky
failures in TestNegativeAcls:
- The per-IP user-creation lock (server side) previously was only worked
around by a client-side Thread.sleep for the full locking duration.
Instead, when a UserManagementPanelPO has an AdminConsolePage, it now
clears the server-side lock through the "User Creation Abuse" blocklist
UI before creating a user. The legacy sleep-based throttle is retained
as a fallback when no AdminConsolePage is available.
- IpBlocklistPanelPO.unblockIP now polls a freshly refreshed table until
the unblocked row is gone, tying completion to a server round-trip. The
unlock and data-load GWT-RPCs in IPBlocklistTableWrapper are now wrapped
in MarkedAsyncCallback so waitForAjaxRequests() can observe them instead
of being a silent no-op.
- AclActionInputPO.addAction waits for the suggest box to be displayed and
enabled before typing, fixing an ElementNotInteractableException when the
denied-actions input is still settling after adding a user group.
Assisted-By: Claude Opus 4.8 (claude-opus-5)
The negative-ACL Selenium tests intermittently failed with
StaleElementReferenceException (masked as a generic RuntimeException) and
a NullPointerException when reading a freshly-created role/permission row
that had not yet rendered.
Root causes and fixes:
- Several user-management RPCs were fired through a plain AsyncCallback, so
they were invisible to the pending-Ajax counter and waitForAjaxRequests()
returned before their deferred CellTable repaint ran. Wrap them in
MarkedAsyncCallback: createRoleDefinition, getRolesAndPermissionsForUser,
getUserList, addPermissionForUser, removePermissionFromUser and
removeRoleFromUser.
- Pair those marks with waitForAjaxRequests() barriers on the test side after
add/delete clicks (WildcardPermissionPanelPO, UserRoleDefinitionPanelPO).
The ...AndExpectPermissionError() variants deliberately click directly and
wait for the alert instead, since the RPC fails rather than completing.
- Add RoleDefinitionsPanelPO.findRoleWhenPresent(): wait for Ajax, then poll
until the row is present before returning it, and use it from
TestNegativeAcls so a not-yet-rendered row no longer NPEs.
- Stop CellTablePO/GenericCellTablePO from masking staleness: narrow
createEntry's catch to the exceptions the reflective construction can
actually throw, unwrap InvocationTargetException(StaleElementReferenceException)
and rethrow it, and retry getEntries() a bounded number of times so a
redraw-during-read is tolerated rather than surfaced as an opaque failure.
- PageObject.waitUntil now ignores StaleElementReferenceException.
Assisted-By: Claude Opus 4.8 (Claude Code)
The test read the first device-configuration table row immediately after
creating a configuration, but the reload that populates that row was not
awaited, so a fast GeckoDriver could reach getEntries().get(0) before the
row existed and fail intermittently.
- Mark DeviceConfigurationListComposite.refreshTable()'s getDeviceConfigurations
RPC with MarkedAsyncCallback so the reload is counted by the Selenium AJAX
barrier (waitForAjaxRequests). Previously it used a bare AsyncCallback and
was invisible to the barrier.
- Use DataEntryDialogPO.pressOk() instead of clickOkButtonOrThrow() in the
test so the OK click is followed by waitForAjaxRequests().
- Guard getDeviceConfigurationDetails() to wait until the leaderboard table
is non-empty before reading row 0, protecting all callers against the race.
Assisted-By: Claude Opus 4.8
especially with the new Selenium driver, sending the click() event may
not wait synchronously until all event handlers on the element clicked
have fired and run to completion. This way, the "updating" DOM attribute
used to synchronize in this case may not have been touched. Now we wait
for the checkbox to really be ticked before continuing. Then we can be
sure that the "updating" flag has also been set.
Assisted-By: Opus 4.8
The Media tab replaces the event activity asynchronously, so wait for the new tab content before constructing its page object.
Assisted-By: GPT-5.6-sol
Wait for lazily loaded tab content instead of failing on transient absence, and sequence and track event, regatta, and leaderboard creation callbacks so dependent dialogs see persisted state.
Assisted-By: GPT-5.6-sol
Extract a reusable interactability wait so media URL entry waits for the animated dialog to settle before clearing the input. Track public-server configuration requests and explicitly handle the expected tenant warning during the affected login test.
Assisted-By: GPT-5.6-sol
this is intended to help with animated elements such as the race map
settings, or an animated error dialog's buttons.
Assisted-By: Claude Code + GPT 5.6 Sol
Selenium 3.x only speaks the legacy JSON Wire Protocol, which forces us to
pin old browser versions. Selenium 4 brings the W3C WebDriver protocol.
Since the reactor still runs on Java 8 and Selenium 4.14+ requires Java 11,
4.13.0 is the migration target: the last Java-8-compatible release.
org.openqa.selenium.osgi wrapper bundle:
- Drop the Selenium-3 client-combined fat jar and its transitives.
- Add the 12 Selenium 4.13.0 modules + selenium-chromium-driver and the
required transitive runtime deps (Netty async HTTP client, OpenTelemetry).
- Keep the legacy org.json jar (still consumed by the test bundle and
previously re-exported here); DevTools/CDP jars intentionally omitted
(the suite uses no CDP).
- Re-scope Export-Package to the 4.13.0 package layout; keep
Bundle-RequiredExecutionEnvironment JavaSE-1.8. Bump bundle to 4.13.0
and mirror the jar list in .classpath, build.properties and pom.xml.
com.sap.sailing.selenium.test source fixes:
- WebDriverWait(WebDriver, long) -> WebDriverWait(WebDriver, java.time.Duration).
- org.openqa.selenium.internal.WrapsElement -> org.openqa.selenium.WrapsElement.
- ElementNotSelectableException (removed) -> ElementNotInteractableException.
- Bump wrapper-bundle dependency to 4.13.0.
The full test bundle compiles cleanly against the new wrapper on Java 8.
Assisted-By: Claude (claude-4.8-opus)
Follow-up to a960398cd3, which inadvertently captured a half-edited state of
this file (the previous commit staged it while a local edit was in flight).
This restores the intended version: 'information'/'sites'/'Internet Explorer'
wording and the empty-line cleanup in the web-driver system-properties comment.
Comment/whitespace only; no test behavior change.
The Javadoc on PolarDataService.runWhenPolarLoadingFinishedFor (interface),
PolarDataMiner (impl) and the RacingEventServiceImpl.scheduleWindEstimationInstallation
caller all read as if the callback fired once the fixes belonging to the given
race had drained. In reality the loading pipeline (preFilteringProcessorForLoadedFixes)
is shared across all races and has no per-race completion tracking, so the
callback fires only on a global drain: when the fixes of every race ingested so
far have been processed.
The race parameter does not scope the wait; it only gates WHEN the callback may
attach to the shared drain, via two conditions that must both hold: (1) the
race's own fixes have been ingested (so the callback can't fire on a pipeline
that is merely momentarily idle before this race was queued), and (2)
markLoadingOfAllRacesToRestoreStarted() has been signalled (so an idle window
between two startup races' ingestion bursts isn't mistaken for a real drain).
This global-wait behavior is deliberate: we want the polar model to reflect all
loaded data before any maneuver-based wind estimation is installed, and we accept
the resulting startup sequentiality (an early race's estimator install waits
behind the whole loaded-fix backlog). Documentation only; no behavior change.
The method name is kept as-is.
when this exception is thrown, catch it and return null for the table
entry looked for; this is intended to cause the waitUntil method to try
again instead of failing with the exception
Locally, the lock duration was not long enough to last until the admin
logged in to unlock them. This bypassed the need to interact with a
dialog locally, and so the test was still succeeding even after an
interaction command for this dialog was removed. This duration was long
enough for the server however, and so the dialog demanding interaction
would appear and crash the test. The expected lock duration has been
extended by trigger +1 failed login attempts as well as the dialog
interact.
Popup dialog replaced with toast notification for success. Although the
reason behind this bug is yet not understood because the dialog should
have been accepted by the corresponding method in the PO, as it has done
before, replacing it with a toast notification should nevertheless
remove the possibility of it happening again.