anchor.diff:              return 200 'It does not work!';
             '';
           };
  -
  -      environment.systemPackages = with pkgs; [
  -        xdotool
  -        firefox
  -        chromium
  -        qutebrowser
  -        midori
  -      ];
       };
   
  -  testScript = ''
  -    from typing import Tuple
  -    def execute_as(user: str, cmd: str) -> Tuple[int, str]:
  -        """
  -        Run a shell command as a specific user.
  -        """
  -        return machine.execute(f"sudo -u {user} {cmd}")
  +  curlTest = makeTest {
  +    name = "custom-ca-curl";
  +    meta.maintainers = with lib.maintainers; [ rnhmjoj ];
  +    nodes.machine = { ... }: webserverConfig;
  +    testScript = ''
  +        with subtest("Good certificate is trusted in curl"):
  +            machine.wait_for_unit("nginx")
  +            machine.wait_for_open_port(443)
  +            machine.succeed("curl -fv https://good.example.com")
  +
  +        with subtest("Unknown CA is untrusted in curl"):
  +            machine.fail("curl -fv https://bad.example.com")
  +    '';
  +  };
  +
  +  mkBrowserTest = browser: testParams: makeTest {
  +    name = "custom-ca-${browser}";
  +    meta.maintainers = with lib.maintainers; [ rnhmjoj ];
  +
  +    enableOCR = true;
  +
  +    nodes.machine = { pkgs, ... }:
  +      { imports =
  +          [ ./common/user-account.nix
  +            ./common/x11.nix
  +            webserverConfig
  +          ];
  +
  +        # chromium-based browsers refuse to run as root
  +        test-support.displayManager.auto.user = "alice";
  +
  +        # browsers may hang with the default memory
  +        virtualisation.memorySize = 600;
  +
  +        environment.systemPackages = [ pkgs.xdotool pkgs.${browser} ];
  +      };
  +
  +    testScript = ''
  +      from typing import Tuple
  +      def execute_as(user: str, cmd: str) -> Tuple[int, str]:
  +          """
  +          Run a shell command as a specific user.
  +          """
  +          return machine.execute(f"sudo -u {user} {cmd}")
   
   
  -    def wait_for_window_as(user: str, cls: str) -> None:
  -        """
  -        Wait until a X11 window of a given user appears.
  -        """
  +      def wait_for_window_as(user: str, cls: str) -> None:
  +          """
  +          Wait until a X11 window of a given user appears.
  +          """
   
  -        def window_is_visible(last_try: bool) -> bool:
  -            ret, stdout = execute_as(user, f"xdotool search --onlyvisible --class {cls}")
  -            if last_try:
  -                machine.log(f"Last chance to match {cls} on the window list")
  -            return ret == 0
  +          def window_is_visible(last_try: bool) -> bool:
  +              ret, stdout = execute_as(user, f"xdotool search --onlyvisible --class {cls}")
  +              if last_try:
  +                  machine.log(f"Last chance to match {cls} on the window list")
  +              return ret == 0
   
  -        with machine.nested("Waiting for a window to appear"):
  -            retry(window_is_visible)
  +          with machine.nested("Waiting for a window to appear"):
  +              retry(window_is_visible)
   
   
  -    machine.start()
  +      machine.start()
  +      machine.wait_for_x()
   
  -    with subtest("Good certificate is trusted in curl"):
  -        machine.wait_for_unit("nginx")
  -        machine.wait_for_open_port(443)
  -        machine.succeed("curl -fv https://good.example.com")
  +      command = "${browser} ${testParams.args or ""}"
  +      with subtest("Good certificate is trusted in ${browser}"):
  +          execute_as(
  +              "alice", f"{command} https://good.example.com >&2 &"
  +          )
  +          wait_for_window_as("alice", "${browser}")
  +          machine.wait_for_text("It works!")
  +          machine.screenshot("good${browser}")
  +          execute_as("alice", "xdotool key ctrl+w")  # close tab
   
  -    with subtest("Unknown CA is untrusted in curl"):
  -        machine.fail("curl -fv https://bad.example.com")
  +      with subtest("Unknown CA is untrusted in ${browser}"):
  +          execute_as("alice", f"{command} https://bad.example.com >&2 &")
  +          machine.wait_for_text("${testParams.error}")
  +          machine.screenshot("bad${browser}")
  +    '';
  +  };
   
  -    browsers = {
  -      "firefox": "Security Risk",
  -      "chromium": "not private",
  -      "qutebrowser -T": "Certificate error",
  -      "midori": "Security"
  -    }
  +in
   
  -    machine.wait_for_x()
  -    for command, error in browsers.items():
  -        browser = command.split()[0]
  -        with subtest("Good certificate is trusted in " + browser):
  -            execute_as(
  -                "alice", f"{command} https://good.example.com >&2 &"
  -            )
  -            wait_for_window_as("alice", browser)
  -            machine.wait_for_text("It works!")
  -            machine.screenshot("good" + browser)
  -            execute_as("alice", "xdotool key ctrl+w")  # close tab
  -
  -        with subtest("Unknown CA is untrusted in " + browser):
  -            execute_as("alice", f"{command} https://bad.example.com >&2 &")
  -            machine.wait_for_text(error)
  -            machine.screenshot("bad" + browser)
  -            machine.succeed("pkill -f " + browser)
  -  '';
  -})
  +{
  +  curl = curlTest;
  +} // pkgs.lib.mapAttrs mkBrowserTest {
  +  firefox = { error = "Security Risk"; };
  +  chromium = { error = "not private"; };
  +  qutebrowser = { args = "-T"; error = "Certificate error"; };
  +  midori = { error = "Security"; };
  +}
  
positive.code: # Checks that `security.pki` options are working in curl and the main browser
  # engines: Gecko (via Firefox), Chromium, QtWebEngine (Falkon) and WebKitGTK
  # (via Midori). The test checks that certificates issued by a custom trusted
  # CA are accepted but those from an unknown CA are rejected.
  
  import ./make-test-python.nix ({ pkgs, lib, ... }:
  
  let
    makeCert = { caName, domain }: pkgs.runCommand "example-cert"
  
negative.code:       domain = "bad.example.com";
      };
  
    webserverConfig =
      { networking.hosts."127.0.0.1" = [ "good.example.com" "bad.example.com" ];
        security.pki.certificateFiles = [ "${example-good-cert}/ca.crt" ];
  
        services.nginx.enable = true;
  