The Core Issue
Using hardcoded delays (WebUI.delay) or standard element waits (WebUI.waitForElementPresent) fails in this scenario because they operate on absolute timers. They do not actually listen to the state of your application or wait for human interaction to complete.
The Industry-Standard Solution
To gracefully handle manual intervention during automated test execution (a practice often used in semi-automated testing or debugging environments), you should leverage Conditional Synchronization Hooks.
Instead of waiting for a set amount of time, we configure Katalon to poll the browser until a specific post-login element appears (like a “Dashboard” header) or until the 2FA input field disappears from the DOM.
However, the cleanest and most robust method for manual overrides is to use Java’s JOptionPane. This pops up a native UI dialog box that completely halts the Katalon execution thread. The automation will sit paused indefinitely until you type the OTP into the popup and hit “OK”, at which point Katalon injects your input into the browser and proceeds.
The Code Implementation
Since this is a reusable utility that you’ll likely need across multiple login test cases, the best practice in Katalon Studio is to wrap this logic inside a Custom Keyword.
Step 1: Create the Custom Keyword
In Katalon Studio, navigate to Keywords (in the Object Repository tree), create a new Package (e.g., com.utils), and create a new Keyword class named ManualTestingHelper. Paste the following code:
Groovy
package com.utils
import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.testobject.TestObject
import javax.swing.JOptionPane
public class ManualTestingHelper {
/**
* Pauses the test execution and displays a popup dialog for the user to input the OTP.
* It then automatically types that OTP into the specified test object.
* * @param to The Test Object representing the OTP input field on the web page.
*/
@Keyword
def handleManualOTP(TestObject to) {
// 1. Bring up a native Java dialog box that pauses the script execution thread
String userOTP = JOptionPane.showInputDialog(null, "The automation is paused.\nPlease enter the OTP from your device:", "Manual 2FA Verification", JOptionPane.QUESTION_MESSAGE)
// 2. Check if the user didn't cancel or leave it empty
if (userOTP != null && !userOTP.isEmpty()) {
// 3. Wait for the browser field to be ready, then send the keys
WebUI.waitForElementVisible(to, 10)
WebUI.setText(to, userOTP)
} else {
throw new com.kms.katalon.core.exception.StepErrorException("Test aborted or empty OTP provided in the manual popup.")
}
}
}
Step 2: How to call it in your Test Case (Script View)
In your actual Test Case script, replace your old delay logic with a call to this custom keyword. Pass it your OTP text box test object:
Groovy
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
WebUI.openBrowser('https://example.com/login')
// ... navigate and enter username/password ...
WebUI.click(findTestObject('Object Repository/Login_Page/btn_Submit'))
// --- 2FA PAGE REACHED ---
// Call your custom keyword to pause the test and handle the manual input
CustomKeywords.'com.utils.ManualTestingHelper.handleManualOTP'(findTestObject('Object Repository/Login_Page/input_OTP_Field'))
// Continue with the rest of your automation seamlessly
WebUI.click(findTestObject('Object Repository/Login_Page/btn_VerifyOTP'))
WebUI.waitForPageLoad(10)
Why this is better:
-
Flake-Free: It completely eliminates timing out because the Java JOptionPane will hold the execution state indefinitely until a human interacts with it.
-
Separation of Concerns: Your test scripts stay clean, while the logic for handling manual overrides is safely abstracted into your Keywords library.
Did you want to explore how to fully automate this via an API or database query to bypass the manual step entirely down the road?