Form Submission Example
This example demonstrates how to find form elements, fill them out, and submit forms using WebDriverBiDi.NET.
Overview
This example shows:
- Locating form elements with CSS selectors
- Clicking elements to focus them
- Sending keyboard input
- Submitting forms
- Waiting for navigation after submission
Complete Example
string webSocketUrl = "ws://localhost:9515/session/YOUR-SESSION-ID";
BiDiDriver driver = new BiDiDriver(TimeSpan.FromSeconds(30));
try
{
await driver.StartAsync(webSocketUrl);
Console.WriteLine("Connected to browser");
// Subscribe to navigation events to track form submission
SubscribeCommandParameters subscribe =
new SubscribeCommandParameters(driver.BrowsingContext.OnNavigationStarted.EventName);
subscribe.Events.Add(driver.BrowsingContext.OnLoad.EventName);
await driver.Session.SubscribeAsync(subscribe);
// Get browsing context
GetTreeCommandResult tree = await driver.BrowsingContext.GetTreeAsync(
new GetTreeCommandParameters());
string contextId = tree.ContextTree[0].BrowsingContextId;
// Navigate to form page
Console.WriteLine("Navigating to form...");
NavigateCommandParameters navParams = new NavigateCommandParameters(
contextId,
"https://httpbin.org/forms/post")
{
Wait = ReadinessState.Complete
};
await driver.BrowsingContext.NavigateAsync(navParams);
// Find the customer name input field
Console.WriteLine("Finding form fields...");
string findInputScript = "document.querySelector('input[name=\"custname\"]')";
EvaluateCommandParameters evalParams = new EvaluateCommandParameters(
findInputScript,
new ContextTarget(contextId),
true);
EvaluateResult inputResult = await driver.Script.EvaluateAsync(evalParams);
if (inputResult is EvaluateResultSuccess inputSuccess &&
inputSuccess.Result.TryAs(out NodeRemoteValue? inputElement))
{
Console.WriteLine($"Found input element: {inputElement.SharedId}");
// Click the input to focus it
Console.WriteLine("Clicking input field...");
PerformActionsCommandParameters clickParams = new PerformActionsCommandParameters(contextId);
PointerSourceActions clickParamsMouse = new PointerSourceActions
{
Parameters = new PointerParameters { PointerType = PointerType.Mouse },
};
clickParamsMouse.Actions.Add(new PointerMoveAction
{
X = 0,
Y = 0,
Origin = Origin.Element(inputElement.ToSharedReference()),
});
clickParamsMouse.Actions.Add(new PointerDownAction(0));
clickParamsMouse.Actions.Add(new PointerUpAction(0));
clickParams.Actions.Add(clickParamsMouse);
await driver.Input.PerformActionsAsync(clickParams);
// Type the customer name
Console.WriteLine("Typing customer name...");
string customerName = "John Doe";
PerformActionsCommandParameters typeParams = new PerformActionsCommandParameters(contextId);
KeySourceActions typeParamsKeyboard = new KeySourceActions();
foreach (char c in customerName)
{
typeParamsKeyboard.Actions.Add(new KeyDownAction(c.ToString()));
typeParamsKeyboard.Actions.Add(new KeyUpAction(c.ToString()));
}
typeParams.Actions.Add(typeParamsKeyboard);
await driver.Input.PerformActionsAsync(typeParams);
// Find and fill the telephone number field
Console.WriteLine("Filling telephone field...");
string findPhoneScript = "document.querySelector('input[name=\"custtel\"]')";
EvaluateResult phoneResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
findPhoneScript,
new ContextTarget(contextId),
true));
if (phoneResult is EvaluateResultSuccess phoneSuccess &&
phoneSuccess.Result.TryAs(out NodeRemoteValue? phoneElement))
{
// Click phone field
PerformActionsCommandParameters clickPhoneParams = new PerformActionsCommandParameters(contextId);
PointerSourceActions clickPhoneParamsMouse = new PointerSourceActions
{
Parameters = new PointerParameters { PointerType = PointerType.Mouse },
};
clickPhoneParamsMouse.Actions.Add(new PointerMoveAction
{
X = 0,
Y = 0,
Origin = Origin.Element(phoneElement.ToSharedReference()),
});
clickPhoneParamsMouse.Actions.Add(new PointerDownAction(0));
clickPhoneParamsMouse.Actions.Add(new PointerUpAction(0));
clickPhoneParams.Actions.Add(clickPhoneParamsMouse);
await driver.Input.PerformActionsAsync(clickPhoneParams);
// Type phone number
string phoneNumber = "555-1234";
PerformActionsCommandParameters typePhoneParams = new PerformActionsCommandParameters(contextId);
KeySourceActions typePhoneParamsKeyboard = new KeySourceActions();
foreach (char c in phoneNumber)
{
typePhoneParamsKeyboard.Actions.Add(new KeyDownAction(c.ToString()));
typePhoneParamsKeyboard.Actions.Add(new KeyUpAction(c.ToString()));
}
typePhoneParams.Actions.Add(typePhoneParamsKeyboard);
await driver.Input.PerformActionsAsync(typePhoneParams);
}
// Find and click the submit button
Console.WriteLine("Finding submit button...");
string findButtonScript = "document.querySelector('button[type=\"submit\"]')";
EvaluateResult buttonResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
findButtonScript,
new ContextTarget(contextId),
true));
if (buttonResult is EvaluateResultSuccess buttonSuccess &&
buttonSuccess.Result.TryAs(out NodeRemoteValue? buttonElement))
{
Console.WriteLine($"Found submit button: {buttonElement.SharedId}");
// Set up navigation observer
EventObserver<NavigationEventArgs> navObserver =
driver.BrowsingContext.OnLoad.AddObserver((e) =>
{
Console.WriteLine($"Navigation complete to: {e.Url}");
});
navObserver.StartCapturingTasks();
// Click submit button
Console.WriteLine("Clicking submit button...");
PerformActionsCommandParameters clickSubmitParams = new PerformActionsCommandParameters(contextId);
PointerSourceActions clickSubmitParamsMouse = new PointerSourceActions
{
Parameters = new PointerParameters { PointerType = PointerType.Mouse },
};
clickSubmitParamsMouse.Actions.Add(new PointerMoveAction
{
X = 0,
Y = 0,
Origin = Origin.Element(buttonElement.ToSharedReference()),
});
clickSubmitParamsMouse.Actions.Add(new PointerDownAction(0));
clickSubmitParamsMouse.Actions.Add(new PointerUpAction(0));
clickSubmitParams.Actions.Add(clickSubmitParamsMouse);
await driver.Input.PerformActionsAsync(clickSubmitParams);
// Wait for navigation to complete
Task[] navTasks = await navObserver.WaitForCapturedTasksAsync(1, TimeSpan.FromSeconds(10));
bool navCompleted = navTasks.Length == 1;
navObserver.StopCapturingTasks();
if (navCompleted)
{
Console.WriteLine("✓ Form submitted successfully!");
// Verify we're on the results page
EvaluateResult urlResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"window.location.href",
new ContextTarget(contextId),
true));
if (urlResult is EvaluateResultSuccess urlSuccess &&
urlSuccess.Result is StringRemoteValue urlValue)
{
string currentUrl = urlValue.Value ?? "No URL";
Console.WriteLine($"Current URL: {currentUrl}");
}
}
else
{
Console.WriteLine("✗ Navigation timeout after form submission");
}
navObserver.Unobserve();
}
}
else if (inputResult is EvaluateResultException inputException)
{
Console.WriteLine($"Error finding input: {inputException.ExceptionDetails.Text}");
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
catch (WebDriverBiDiException ex)
{
Console.WriteLine($"WebDriver BiDi Error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
await driver.StopAsync();
Console.WriteLine("Disconnected from browser");
}
The example is a complete program: it creates the driver, connects with driver.StartAsync(webSocketUrl), performs the submission, and stops the driver. Replace webSocketUrl with your browser's WebSocket URL.
Step-by-Step Breakdown
1. Subscribe to Navigation Events
SubscribeCommandParameters subscribe = new SubscribeCommandParameters(
[
driver.BrowsingContext.OnNavigationStarted.EventName,
driver.BrowsingContext.OnLoad.EventName,
]
);
await driver.Session.SubscribeAsync(subscribe);
This allows us to track when the form submission causes navigation.
2. Find Form Elements
string findInputScript = "document.querySelector('input[name=\"custname\"]')";
EvaluateResult inputResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(findInputScript, target, true));
Use JavaScript to find elements. You can also use LocateNodesAsync with CSS selectors.
3. Click to Focus
PerformActionsCommandParameters clickParams = new PerformActionsCommandParameters(contextId);
PointerSourceActions mouseSource = new PointerSourceActions
{
Parameters = new PointerParameters { PointerType = PointerType.Mouse },
};
mouseSource.Actions.Add(new PointerMoveAction
{
X = 0,
Y = 0,
Origin = Origin.Element(inputElement.ToSharedReference()),
});
mouseSource.Actions.Add(new PointerDownAction(0));
mouseSource.Actions.Add(new PointerUpAction(0));
clickParams.Actions.Add(mouseSource);
await driver.Input.PerformActionsAsync(clickParams);
Clicking the input field focuses it for keyboard input.
4. Send Keyboard Input
KeySourceActions keyboard = new KeySourceActions();
foreach (char c in text)
{
keyboard.Actions.Add(new KeyDownAction(c.ToString()));
keyboard.Actions.Add(new KeyUpAction(c.ToString()));
}
PerformActionsCommandParameters typeParams = new PerformActionsCommandParameters(contextId);
typeParams.Actions.Add(keyboard);
await driver.Input.PerformActionsAsync(typeParams);
Each character requires a key down and key up action.
5. Wait for Form Submission
EventObserver<NavigationEventArgs> navObserver =
driver.BrowsingContext.OnLoad.AddObserver((e) => { });
navObserver.StartCapturingTasks();
// Click submit button...
Task[] tasks = await navObserver.WaitForCapturedTasksAsync(1, TimeSpan.FromSeconds(10));
bool navCompleted = tasks.Length == 1;
navObserver.StopCapturingTasks();
Use an event observer to wait for the navigation to complete.
Alternative Approach: Using LocateNodes
Instead of JavaScript evaluation, you can use the BrowsingContext module's LocateNodesAsync:
// Find input field using CSS selector
LocateNodesCommandParameters locateParams = new LocateNodesCommandParameters(
contextId,
new CssLocator("input[name='custname']"));
LocateNodesCommandResult locateResult =
await driver.BrowsingContext.LocateNodesAsync(locateParams);
if (locateResult.Nodes.Count > 0)
{
RemoteValue inputElement = locateResult.Nodes[0];
// Continue with clicking and typing...
}
Alternative Approach: Submitting with Enter Key
Instead of clicking the submit button, you can press Enter:
// After typing in the last field, press Enter. Special keys are sent using their
// WebDriver key code; "\uE007" is Enter.
KeySourceActions keyboard = new KeySourceActions();
keyboard.Actions.Add(new KeyDownAction("\uE007"));
keyboard.Actions.Add(new KeyUpAction("\uE007"));
PerformActionsCommandParameters submitParams = new PerformActionsCommandParameters(contextId);
submitParams.Actions.Add(keyboard);
await driver.Input.PerformActionsAsync(submitParams);
Alternative Approach: Direct JavaScript Submission
For simple scenarios, you can submit the form directly with JavaScript:
// Find and fill fields...
// Submit form with JavaScript
string submitScript = "document.querySelector('form').submit()";
await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(submitScript, target, false));
Note: This bypasses validation and doesn't trigger the same events as a user clicking submit.
Handling Different Input Types
Checkbox
Target target = new ContextTarget(contextId);
// Find checkbox
EvaluateResult checkboxResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"document.querySelector('input[type=\"checkbox\"]')",
target,
true));
if (checkboxResult is EvaluateResultSuccess checkboxSuccess &&
checkboxSuccess.Result is NodeRemoteValue checkboxElement)
{
// Click to toggle
PerformActionsCommandParameters clickParams = new PerformActionsCommandParameters(contextId);
PointerSourceActions clickParamsMouse = new PointerSourceActions
{
Parameters = new PointerParameters { PointerType = PointerType.Mouse },
};
clickParamsMouse.Actions.Add(new PointerMoveAction
{
X = 0,
Y = 0,
Origin = Origin.Element(checkboxElement.ToSharedReference()),
});
clickParamsMouse.Actions.Add(new PointerDownAction(0));
clickParamsMouse.Actions.Add(new PointerUpAction(0));
clickParams.Actions.Add(clickParamsMouse);
await driver.Input.PerformActionsAsync(clickParams);
}
Radio Button
// Find and click specific radio button
EvaluateResult radioResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"document.querySelector('input[type=\"radio\"][value=\"option1\"]')",
target,
true));
// Click similar to checkbox
Select Dropdown
// Set dropdown value with JavaScript
string setSelectScript = @"
const select = document.querySelector('select[name=""delivery""]');
select.value = 'express';
select.dispatchEvent(new Event('change', { bubbles: true }));
";
await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(setSelectScript, target, false));
Textarea
// Same as text input - click and type
string longText = "This is a longer message\nthat spans multiple lines.";
// Note: '\n' creates a new line
KeySourceActions keyboard = new KeySourceActions();
foreach (char c in longText)
{
if (c == '\n')
{
keyboard.Actions.Add(new KeyDownAction("\uE007"));
keyboard.Actions.Add(new KeyUpAction("\uE007"));
}
else
{
keyboard.Actions.Add(new KeyDownAction(c.ToString()));
keyboard.Actions.Add(new KeyUpAction(c.ToString()));
}
}
PerformActionsCommandParameters typeParams = new PerformActionsCommandParameters(contextId);
typeParams.Actions.Add(keyboard);
await driver.Input.PerformActionsAsync(typeParams);
Note: \n creates a new line using the Enter key (Unicode U+E007).
Verification After Submission
After form submission, verify the results:
// Check URL changed
EvaluateResult urlResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"window.location.href",
target,
true));
string currentUrl = urlResult.As<EvaluateResultSuccess>().Result.As<StringRemoteValue>().Value;
if (currentUrl.Contains("/success"))
{
Console.WriteLine("Form submitted successfully!");
}
// Check for success message
EvaluateResult messageResult = await driver.Script.EvaluateAsync(
new EvaluateCommandParameters(
"document.querySelector('.success-message')?.textContent",
target,
true));
if (messageResult is EvaluateResultSuccess messageSuccess &&
messageSuccess.Result is StringRemoteValue messageValue)
{
string? message = messageValue.Value;
Console.WriteLine($"Success message: {message}");
}
Best Practices
- Wait for elements: Ensure elements exist before interacting
- Focus before typing: Always click/focus input fields before sending keys
- Use event observers: Track navigation after form submission
- Handle errors: Check for
EvaluateResultExceptionwhen finding elements - Verify submission: Check URL or page content after submission
- Release actions: Call
ReleaseActionsAsyncbetween form interactions in tests
Common Issues
Text Not Appearing
Problem: Text typed but doesn't appear in field.
Solution: Ensure the field is focused by clicking it first.
Form Not Submitting
Problem: Click submit button but nothing happens.
Solution:
- Verify the button is the correct element
- Check for JavaScript errors in console
- Ensure form validation passes
Navigation Timeout
Problem: Form submits but navigation times out.
Solution:
- Increase timeout duration
- Check if form submission is AJAX (no navigation)
- Look for error messages on the page
Next Steps
- Input Module: Complete input simulation guide
- Script Module: JavaScript execution patterns
- Common Scenarios: More examples