Table of Contents

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

  1. Wait for elements: Ensure elements exist before interacting
  2. Focus before typing: Always click/focus input fields before sending keys
  3. Use event observers: Track navigation after form submission
  4. Handle errors: Check for EvaluateResultException when finding elements
  5. Verify submission: Check URL or page content after submission
  6. Release actions: Call ReleaseActionsAsync between 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

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