mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-17 11:19:15 +00:00
Merge pull request #6252 from MashaKashirina/bug6251
bug6251: selenium test impl
This commit is contained in:
+108
-5
@@ -1,7 +1,14 @@
|
||||
package com.sap.sailing.selenium.pages.adminconsole.connectors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.StaleElementReferenceException;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
|
||||
import com.sap.sailing.selenium.core.BySeleniumId;
|
||||
import com.sap.sailing.selenium.core.FindBy;
|
||||
@@ -18,23 +25,30 @@ public class SmartphoneTrackingEventManagementPanelPO extends PageArea {
|
||||
|
||||
private static final String ACTION_COMPETITOR_REGISTRATIONS = "ACTION_COMPETITOR_REGISTRATIONS";
|
||||
private static final String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES";
|
||||
|
||||
private static final String ACTION_DENOTE_FOR_RACELOG_TRACKING = "ACTION_DENOTE_FOR_RACELOG_TRACKING";
|
||||
private static final String ACTION_START_TRACKING = "ACTION_START_TRACKING";
|
||||
private static final String ACTION_STOP_TRACKING = "ACTION_STOP_TRACKING";
|
||||
private static final String ACTION_REMOVE_DENOTATION = "ACTION_REMOVE_DENOTATION";
|
||||
|
||||
@FindBy(how = BySeleniumId.class, using = "AvailableLeaderboardsTable")
|
||||
private WebElement leaderboardTable;
|
||||
|
||||
|
||||
@FindBy(how = BySeleniumId.class, using = "RaceColumnTable")
|
||||
private WebElement raceColumnTableWrapper;
|
||||
|
||||
|
||||
@FindBy(how = BySeleniumId.class, using = "TrackedRacesListComposite")
|
||||
private WebElement trackedRacesListComposite;
|
||||
|
||||
|
||||
@FindBy(how = BySeleniumId.class, using = "LeaderboardRefreshButton")
|
||||
private WebElement leaderboardRefreshButton;
|
||||
|
||||
@FindBy(how = BySeleniumId.class, using = "StartTrackingButton")
|
||||
private WebElement startTrackingButton;
|
||||
|
||||
public SmartphoneTrackingEventManagementPanelPO(WebDriver driver, WebElement element) {
|
||||
super(driver, element);
|
||||
}
|
||||
|
||||
|
||||
public CellTablePO<DataEntryPO> getLeaderboardTable() {
|
||||
return new GenericCellTablePO<>(this.driver, this.leaderboardTable, DataEntryPO.class);
|
||||
}
|
||||
@@ -61,4 +75,93 @@ public class SmartphoneTrackingEventManagementPanelPO extends PageArea {
|
||||
aLeaderboard.clickActionImage(ACTION_MAP_DEVICES);
|
||||
return this.waitForPO(MapDevicesDialogPO::new, "regattaLogTrackingDeviceMappingsDialog");
|
||||
}
|
||||
|
||||
public void denoteLeaderboardForRaceLogTracking(DataEntryPO aLeaderboard) {
|
||||
aLeaderboard.clickActionImage(ACTION_DENOTE_FOR_RACELOG_TRACKING);
|
||||
findElementBySeleniumId(this.driver, "OkButton").click();
|
||||
waitForAjaxRequests();
|
||||
}
|
||||
|
||||
public void startTrackingForRace(DataEntryPO aRaceRow) {
|
||||
aRaceRow.clickActionImage(ACTION_START_TRACKING);
|
||||
waitForAjaxRequests();
|
||||
}
|
||||
|
||||
public void stopTrackingForRace(DataEntryPO aRaceRow) {
|
||||
aRaceRow.clickActionImage(ACTION_STOP_TRACKING);
|
||||
waitForAjaxRequests();
|
||||
}
|
||||
|
||||
public void clickStartTrackingButton() {
|
||||
this.startTrackingButton.click();
|
||||
waitForAjaxRequests();
|
||||
}
|
||||
|
||||
public void removeDenotationForRace(DataEntryPO aRaceRow) {
|
||||
aRaceRow.clickActionImage(ACTION_REMOVE_DENOTATION);
|
||||
waitForAjaxRequests();
|
||||
}
|
||||
|
||||
// Waits for a leaderboard row with the given name to be stably present and returns it.
|
||||
// Retries on stale/transient DOM state caused by GWT re-renders after Ajax.
|
||||
public DataEntryPO waitForLeaderboardEntry(final String leaderboardName) {
|
||||
return waitForTableEntry(getLeaderboardTable(), "Name", leaderboardName);
|
||||
}
|
||||
|
||||
// Waits for a race row with the given name to be stably present and returns it.
|
||||
// Retries on stale/transient DOM state caused by GWT re-renders after Ajax.
|
||||
public DataEntryPO waitForRaceRow(final String raceName) {
|
||||
return waitForTableEntry(getRaceColumnTableWrapper().getRaceColumnTable(), "Race", raceName);
|
||||
}
|
||||
|
||||
// Selects exactly the named rows in the race table, deselecting all others.
|
||||
// Retries one row at a time to handle GWT re-renders between clicks.
|
||||
public void selectRaceRowsByName(String... raceNames) {
|
||||
final List<String> namesToSelect = Arrays.asList(raceNames);
|
||||
boolean changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
try {
|
||||
final CellTablePO<DataEntryPO> raceTable = getRaceColumnTableWrapper().getRaceColumnTable();
|
||||
for (final DataEntryPO entry : raceTable.getEntries()) {
|
||||
final boolean shouldBeSelected = namesToSelect.contains(entry.getColumnContent("Race"));
|
||||
final boolean isSelected = entry.isSelected();
|
||||
if (shouldBeSelected != isSelected) {
|
||||
final WebElement checkboxDiv = entry.getWebElement().findElement(By.xpath("./td[1]/div"));
|
||||
scrollToView(checkboxDiv);
|
||||
new Actions(this.driver).moveToElement(checkboxDiv).click().build().perform();
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (StaleElementReferenceException e) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DataEntryPO waitForTableEntry(final CellTablePO<DataEntryPO> table, final String column, final String value) {
|
||||
final List<DataEntryPO> result = new ArrayList<>();
|
||||
waitUntil(() -> {
|
||||
boolean found = false;
|
||||
try {
|
||||
for (final DataEntryPO entry : table.getEntries()) {
|
||||
if (value.equals(entry.getColumnContent(column))) {
|
||||
result.clear();
|
||||
result.add(entry);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (StaleElementReferenceException e) {
|
||||
found = false;
|
||||
} catch (RuntimeException e) {
|
||||
// createDataEntry throws RuntimeException when the table is mid-re-render
|
||||
// (transient row visible that cannot be reflected into a DataEntryPO); retry
|
||||
found = false;
|
||||
}
|
||||
return found;
|
||||
});
|
||||
return result.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.sap.sailing.selenium.test.adminconsole;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import com.sap.sailing.selenium.core.SeleniumTestCase;
|
||||
import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
|
||||
import com.sap.sailing.selenium.pages.adminconsole.connectors.SmartphoneTrackingEventManagementPanelPO;
|
||||
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaListCompositePO.RegattaDescriptor;
|
||||
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaStructureManagementPanelPO;
|
||||
import com.sap.sailing.selenium.test.AbstractSeleniumTest;
|
||||
|
||||
/**
|
||||
* Selenium test for bug6113 + bug6251: verifies the start/stop tracking cycle for Race Log /
|
||||
* Smartphone Tracking. Runs two full denote → start → stop → undenote cycles to ensure that
|
||||
* button states reset correctly after the first cycle (bug6251). Tests both global and local start/stop tracking buttons.
|
||||
*/
|
||||
public class TestStartStopTrackingCycleForRaceLog extends AbstractSeleniumTest {
|
||||
private static final String REGATTA_NAME = "TestRegatta6113";
|
||||
private static final String BOAT_CLASS = "Laser";
|
||||
private static final String R1 = "R1";
|
||||
private static final String R2 = "R2";
|
||||
private static final String R3 = "R3";
|
||||
|
||||
private RegattaDescriptor regatta;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
this.regatta = new RegattaDescriptor(REGATTA_NAME, BOAT_CLASS);
|
||||
clearState(getContextRoot());
|
||||
super.setUp();
|
||||
configureRegattaAndLeaderboard();
|
||||
}
|
||||
|
||||
@SeleniumTestCase
|
||||
public void testStartStopTrackingCycle() {
|
||||
final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
|
||||
final SmartphoneTrackingEventManagementPanelPO smartphonePanel = adminConsole.goToSmartphoneTrackingPanel();
|
||||
smartphonePanel.getLeaderboardTable().selectEntry(smartphonePanel.waitForLeaderboardEntry(this.regatta.toString()));
|
||||
performTrackingCycle(smartphonePanel);
|
||||
performTrackingCycle(smartphonePanel);
|
||||
}
|
||||
|
||||
private void configureRegattaAndLeaderboard() {
|
||||
final AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
|
||||
final RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
|
||||
regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ true);
|
||||
}
|
||||
|
||||
private void performTrackingCycle(final SmartphoneTrackingEventManagementPanelPO smartphonePanel) {
|
||||
// denote all races for tracking, re-fetching the leaderboard entry each cycle
|
||||
smartphonePanel.denoteLeaderboardForRaceLogTracking(smartphonePanel.waitForLeaderboardEntry(this.regatta.toString()));
|
||||
// start R1 via local button, R2+R3 via global toggle button
|
||||
smartphonePanel.startTrackingForRace(smartphonePanel.waitForRaceRow(R1));
|
||||
smartphonePanel.selectRaceRowsByName(R2, R3);
|
||||
smartphonePanel.clickStartTrackingButton();
|
||||
// stop R3 via local button, R1+R2 via global toggle button
|
||||
smartphonePanel.stopTrackingForRace(smartphonePanel.waitForRaceRow(R3));
|
||||
smartphonePanel.selectRaceRowsByName(R1, R2);
|
||||
smartphonePanel.clickStartTrackingButton();
|
||||
// undenote all races to reset state for next cycle
|
||||
smartphonePanel.removeDenotationForRace(smartphonePanel.waitForRaceRow(R1));
|
||||
smartphonePanel.removeDenotationForRace(smartphonePanel.waitForRaceRow(R2));
|
||||
smartphonePanel.removeDenotationForRace(smartphonePanel.waitForRaceRow(R3));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user