-
Notifications
You must be signed in to change notification settings - Fork 15
[CUS-12573] hides keyboard fastly in ios devices. #389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project | ||
| xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <groupId>com.testsigma.addons</groupId> | ||
| <artifactId>hide_keyboard</artifactId> | ||
| <version>1.0.0</version> | ||
| <packaging>jar</packaging> | ||
|
|
||
| <properties> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <maven.compiler.source>11</maven.compiler.source> | ||
| <maven.compiler.target>11</maven.compiler.target> | ||
| <testsigma.sdk.version>1.2.26_cloud</testsigma.sdk.version> | ||
| <junit.jupiter.version>5.12.1</junit.jupiter.version> | ||
| <testsigma.addon.maven.plugin>1.0.0</testsigma.addon.maven.plugin> | ||
| <maven.source.plugin.version>3.2.1</maven.source.plugin.version> | ||
| <lombok.version>1.18.30</lombok.version> | ||
| <testng.version>7.10.2</testng.version> | ||
| <selenium.version>4.33.0</selenium.version> | ||
| <appium.java.client.version>9.4.0</appium.java.client.version> | ||
| <jackson.version>2.13.0</jackson.version> | ||
| <maven.shade.plugin.version>3.2.4</maven.shade.plugin.version> | ||
|
|
||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>com.testsigma</groupId> | ||
| <artifactId>testsigma-java-sdk</artifactId> | ||
| <version>${testsigma.sdk.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.projectlombok</groupId> | ||
| <artifactId>lombok</artifactId> | ||
| <version>${lombok.version}</version> | ||
| <optional>true</optional> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-api</artifactId> | ||
| <version>${junit.jupiter.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.testng</groupId> | ||
| <artifactId>testng</artifactId> | ||
| <version>${testng.version}</version> | ||
| </dependency> | ||
| <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --> | ||
| <dependency> | ||
| <groupId>org.seleniumhq.selenium</groupId> | ||
| <artifactId>selenium-java</artifactId> | ||
| <version>${selenium.version}</version> | ||
| </dependency> | ||
| <!-- https://mvnrepository.com/artifact/io.appium/java-client --> | ||
| <dependency> | ||
| <groupId>io.appium</groupId> | ||
| <artifactId>java-client</artifactId> | ||
| <version>${appium.java.client.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.fasterxml.jackson.core</groupId> | ||
| <artifactId>jackson-annotations</artifactId> | ||
| <version>${jackson.version}</version> | ||
| </dependency> | ||
|
|
||
| </dependencies> | ||
| <build> | ||
| <finalName>hide_keyboard</finalName> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-shade-plugin</artifactId> | ||
| <version>${maven.shade.plugin.version}</version> | ||
| <executions> | ||
| <execution> | ||
| <phase>package</phase> | ||
| <goals> | ||
| <goal>shade</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-source-plugin</artifactId> | ||
| <version>${maven.source.plugin.version}</version> | ||
| <executions> | ||
| <execution> | ||
| <id>attach-sources</id> | ||
| <goals> | ||
| <goal>jar</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| package com.testsigma.addons.ios; | ||
|
|
||
| import com.testsigma.sdk.IOSAction; | ||
| import com.testsigma.sdk.Result; | ||
| import com.testsigma.sdk.annotation.Action; | ||
| import io.appium.java_client.AppiumBy; | ||
| import io.appium.java_client.AppiumDriver; | ||
| import io.appium.java_client.ios.IOSDriver; | ||
| import lombok.Data; | ||
| import org.openqa.selenium.By; | ||
| import org.openqa.selenium.Keys; | ||
| import org.openqa.selenium.Point; | ||
| import org.openqa.selenium.WebElement; | ||
| import org.openqa.selenium.interactions.Pause; | ||
| import org.openqa.selenium.interactions.PointerInput; | ||
| import org.openqa.selenium.interactions.Sequence; | ||
|
|
||
| import java.time.Duration; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| import static java.time.Duration.ofMillis; | ||
| import static org.openqa.selenium.interactions.PointerInput.Kind.TOUCH; | ||
| import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT; | ||
| import static org.openqa.selenium.interactions.PointerInput.Origin.viewport; | ||
|
|
||
| @Data | ||
| @Action(actionText = "Hide the iOS keyboard", | ||
| description = "Hides the on-screen keyboard on an iOS device.", | ||
| applicationType = com.testsigma.sdk.ApplicationType.IOS) | ||
| public class HideKeyboard extends IOSAction { | ||
|
|
||
| private static final String SUCCESS_MESSAGE = "Hide Keyboard executed successfully."; | ||
| private static final String FAILURE_MESSAGE = "Unable to hide keyboard. Please try executing \"Tap on element\" outside keyboard."; | ||
|
|
||
| @Override | ||
| public Result execute() { | ||
| logger.info("Attempting to hide the iOS keyboard"); | ||
| boolean keyboardShown = true; | ||
| for (int i = 0; i < 4; i++) { | ||
| long start = System.currentTimeMillis(); | ||
| if (i == 0) { | ||
| switchToActiveElementAndPressEnter(); | ||
| } else if (i == 1) { | ||
| hideKeyboardByTappingOutsideKeyboard(); | ||
| } else if (i == 2) { | ||
| clickOnReturnKeys(); | ||
| } else { | ||
| clickOnHideKeyBoardAccessibilityID(); | ||
| } | ||
| logger.info("Hiding keyboard using strategy " + i + " took " + (System.currentTimeMillis() - start) / 1000.0 + " seconds"); | ||
| if (!isKeyboardShown()) { | ||
| keyboardShown = false; | ||
| break; | ||
| } | ||
| } | ||
| if (!keyboardShown) { | ||
| setSuccessMessage(SUCCESS_MESSAGE); | ||
| return Result.SUCCESS; | ||
| } else { | ||
| setErrorMessage(FAILURE_MESSAGE); | ||
| return Result.FAILED; | ||
| } | ||
| } | ||
|
|
||
| private void switchToActiveElementAndPressEnter() { | ||
| try { | ||
| driver.switchTo().activeElement().sendKeys(Keys.RETURN); | ||
| logger.info("Hid keyboard by switching to active element and pressing Enter"); | ||
| } catch (Exception e) { | ||
| logger.info("Could not hide keyboard by switching to active element and pressing Enter: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void hideKeyboardByTappingOutsideKeyboard() { | ||
| String keyboardClassName = "XCUIElementTypeKeyboard"; | ||
| logger.info("Trying to hide keyboard by tapping above keyboard element (class: " + keyboardClassName + ")"); | ||
| try { | ||
| WebElement keyboard = driver.findElement(By.className(keyboardClassName)); | ||
| Point loc = keyboard.getLocation(); | ||
| PointerInput finger = new PointerInput(TOUCH, "finger"); | ||
| Sequence tap = new Sequence(finger, 1) | ||
| .addAction(finger.createPointerMove(ofMillis(0), viewport(), loc.getX() + 2, loc.getY() - 2)) | ||
| .addAction(finger.createPointerDown(LEFT.asArg())) | ||
| .addAction(new Pause(finger, ofMillis(1))) | ||
| .addAction(finger.createPointerUp(LEFT.asArg())); | ||
| ((AppiumDriver) driver).perform(Arrays.asList(tap)); | ||
| try { Thread.sleep(200); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } | ||
| logger.info("Tapped above keyboard successfully"); | ||
| } catch (Exception e) { | ||
| logger.info("Failed to hide keyboard by tapping above keyboard: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void clickOnReturnKeys() { | ||
| logger.info("Trying to hide keyboard by clicking Return/Done/Search/Next/Go keys"); | ||
| driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1)); | ||
| List.of("Return", "return", "done", "Done", "search", "Search", "Next", "next", "Go", "go").forEach(button -> { | ||
| try { | ||
| driver.findElement(By.xpath("//*[contains(@name, '" + button + "')]")).click(); | ||
| } catch (Exception e) { | ||
| logger.info("XPath click failed for key '" + button + "': " + e.getMessage()); | ||
| } | ||
| try { | ||
| driver.findElement(AppiumBy.name(button)).click(); | ||
| } catch (Exception e) { | ||
| logger.info("Name lookup failed for key '" + button + "': " + e.getMessage()); | ||
| } | ||
| }); | ||
| driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0)); | ||
| } | ||
|
|
||
| private void clickOnHideKeyBoardAccessibilityID() { | ||
| logger.info("Trying to hide keyboard via 'Hide keyboard' accessibility ID"); | ||
| try { | ||
| driver.findElement(AppiumBy.accessibilityId("Hide keyboard")).click(); | ||
| logger.info("Clicked 'Hide keyboard' accessibility ID successfully"); | ||
| } catch (Exception e) { | ||
| logger.info("Failed to click 'Hide keyboard' accessibility ID: " + e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private boolean isKeyboardShown() { | ||
| try { | ||
| driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1)); | ||
| boolean shown = ((IOSDriver) driver).isKeyboardShown(); | ||
| driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0)); | ||
| return shown; | ||
| } catch (Exception e) { | ||
| logger.info("Exception while checking keyboard visibility: " + e.getMessage()); | ||
| return false; | ||
| } | ||
|
Comment on lines
+123
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not treat keyboard-check exceptions as “keyboard hidden.” In Line 131, returning Suggested fix private boolean isKeyboardShown() {
try {
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1));
boolean shown = ((IOSDriver) driver).isKeyboardShown();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0));
return shown;
} catch (Exception e) {
logger.info("Exception while checking keyboard visibility: " + e.getMessage());
- return false;
+ return true; // fail-closed: don't report hidden when check failed
}
}🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1 @@ | ||||||
| testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNDBkMjhiNS0xNmUwLThlNmYtOWQ0ZS05MjYxMGNiZTcyYzciLCJ1bmlxdWVJZCI6IjYxODUiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiNDMifQ.Ktf9Ejac32ZAAKDwJFa9NerBc-xymkf_msZ0sr2C-o7gQ6tI1F8UVdfUYHwjGDY2P3R_4_7JZ4dIQ-lnL5Xceg | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the committed API key from source control before merge. Line 1 exposes a real credential in plaintext. For a PUBLIC addon, this is a release blocker and a credential-compromise risk. Replace this with runtime secret injection (env var / secure vault / CI secret), rotate this key immediately, and purge it from git history. Suggested secure change-testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNDBkMjhiNS0xNmUwLThlNmYtOWQ0ZS05MjYxMGNiZTcyYzciLCJ1bmlxdWVJZCI6IjYxODUiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiNDMifQ.Ktf9Ejac32ZAAKDwJFa9NerBc-xymkf_msZ0sr2C-o7gQ6tI1F8UVdfUYHwjGDY2P3R_4_7JZ4dIQ-lnL5Xceg
+testsigma-sdk.api.key=${TESTSIGMA_SDK_API_KEY}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore implicit wait safely; avoid leaking driver timeout state.
Lines 110 and 127 unconditionally set implicit wait to
0. If another timeout was configured before this action, it gets clobbered and can affect later steps.Suggested fix pattern
Also applies to: 125-127
🤖 Prompt for AI Agents