## 🚀 Feature Proposal

Add support for the WebAuthn Automation API, a standard WebDriver extension that allows developers to create virtual authenticators to test websites using WebAuthn.

https://w3c.github.io/webauthn/#sctn-automation

_note: the above link may not work until https://github.com/w3c/webauthn/pull/1340 is merged_

## Motivation

At the moment there is no practical way to write e2e tests for pages using WebAuthn. Since this is a standard WebDriver API, the cost of implementing a layer on java is low.

## Example

For a page like this

```html
<button id="signup">Sign up with webauthn</button>
<span id="result"></span>
```

```javascript
document.getElmentById("signup").addEventListener("click", () => {
  navigator.credentials.create({...}).then(credentials => {
    document.getElementById("result").innerText = "Success!";
  })
});
```

A test could be written like so:

```java
// HasVirtualAuthenticator is an interface that augments a RemoteWebDriver.
HasVirtualAuthenticator virtualAuthenticatorManager = ((HasVirtualAuthenticator) driver);
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
VirtualAuthenticator authenticator =
    virtualAuthenticatorManager.addVirtualAuthenticator(options);
driver.findElement(By.id("signup")).click();
WebElement result = driver.findElement(By.id("result"));
new WebDriverWait(driver, 60, 1000).until(
    driver -> !result.getText().equals(""));
assertThat(result.getText()).isEqualTo("Success!");
```

## Implementation

We can implement the AddVirtualAuthenticator and RemoveVirtualAuthenticator methods first since they're the most useful and follow up with the others in subsequent PRs.
String SWITCH_TO_NEW_WINDOW = "newWindow";
  static CommandPayload SWITCH_TO_NEW_WINDOW(WindowType typeHint) {
    return new CommandPayload(SWITCH_TO_NEW_WINDOW, ImmutableMap.of("type", typeHint.toString()));
  }
  String SWITCH_TO_CONTEXT = "switchToContext";
  String SWITCH_TO_FRAME = "switchToFrame";
  static CommandPayload SWITCH_TO_FRAME(Object frame) {
    return new CommandPayload(SWITCH_TO_FRAME, Collections.singletonMap("id", frame));
  }
  String SWITCH_TO_PARENT_FRAME = "switchToParentFrame";
  String GET_ACTIVE_ELEMENT = "getActiveElement";
  String GET_CURRENT_URL = "getCurrentUrl";
  String GET_PAGE_SOURCE = "getPageSource";
  String GET_TITLE = "getTitle";
  String EXECUTE_SCRIPT = "executeScript";
  static CommandPayload EXECUTE_SCRIPT(String script, List<Object> args) {
    return new CommandPayload(EXECUTE_SCRIPT, ImmutableMap.of("script", script, "args", args));
  }
  String EXECUTE_ASYNC_SCRIPT = "executeAsyncScript";
  static CommandPayload EXECUTE_ASYNC_SCRIPT(String script, List<Object> args) {
    return new CommandPayload(EXECUTE_ASYNC_SCRIPT, ImmutableMap.of("script", script, "args", args));
  }
  String GET_ELEMENT_TEXT = "getElementText";
  static CommandPayload GET_ELEMENT_TEXT(String id) {
    return new CommandPayload(GET_ELEMENT_TEXT, ImmutableMap.of("id", id));
  }
  String GET_ELEMENT_TAG_NAME = "getElementTagName";
  static CommandPayload GET_ELEMENT_TAG_NAME(String id) {
    return new CommandPayload(GET_ELEMENT_TAG_NAME, ImmutableMap.of("id", id));
  }