## 🚀 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 name = aliases.getOrDefault(command.getName(), command.getName());
    CommandSpec spec = nameToSpec.get(name);
    if (spec == null) {
      throw new UnsupportedCommandException(command.getName());
    }
    Map<String, ?> parameters = amendParameters(command.getName(), command.getParameters());
    String uri = buildUri(name, command.getSessionId(), parameters, spec);
    HttpRequest request = new HttpRequest(spec.method, uri);
    if (HttpMethod.POST == spec.method) {
      String content = json.toJson(parameters);
      byte[] data = content.getBytes(UTF_8);
      request.setHeader(CONTENT_LENGTH, String.valueOf(data.length));
      request.setHeader(CONTENT_TYPE, JSON_UTF_8.toString());
      request.setContent(bytes(data));
    }
    if (HttpMethod.GET == spec.method) {
      request.setHeader(CACHE_CONTROL, "no-cache");
    }
    return request;
  }
  protected abstract Map<String,?> amendParameters(String name, Map<String, ?> parameters);
  @Override
  public Command decode(final HttpRequest encodedCommand) {
    final String path = Strings.isNullOrEmpty(encodedCommand.getUri())
                        ? "/" : encodedCommand.getUri();
    final ImmutableList<String> parts = ImmutableList.copyOf(PATH_SPLITTER.split(path));
    int minPathLength = Integer.MAX_VALUE;
    CommandSpec spec = null;
    String name = null;
    for (Map.Entry<String, CommandSpec> nameValue : nameToSpec.entrySet()) {