diff --git a/test/common.nix b/test/common.nix index 7a90853..9692a25 100644 --- a/test/common.nix +++ b/test/common.nix @@ -1,7 +1,7 @@ { pkgs, lib }: let - inherit (lib) mkOption; - inherit (lib.types) str; + inherit (lib) hasAttr mkOption optionalString; + inherit (lib.types) bool listOf nullOr submodule str; baseImports = { imports = [ @@ -90,8 +90,11 @@ let raise Exception(f"auth host should be auth.${cfg.domain} but is {response['auth_host']}") if response['auth_query'] != "rd=${proto_fqdn}/": raise Exception(f"auth query should be rd=${proto_fqdn}/ but is {response['auth_query']}") - '' - ) + '') + + (optionalString (hasAttr "test" nodes.client && hasAttr "login" nodes.client.test) '' + with subtest("Login"): + print(client.succeed("login_playwright firefox")) + '') + (let script = extraScript args; in @@ -142,6 +145,113 @@ in }; }; + clientLoginModule = { config, pkgs, ... }: let + cfg = config.test.login; + in { + options.test.login = { + usernameFieldLabel = mkOption { + type = str; + default = "username"; + }; + passwordFieldLabel = mkOption { + type = str; + default = "password"; + }; + loginButtonName = mkOption { + type = str; + default = "login"; + }; + testLoginWith = mkOption { + type = listOf (submodule { + options = { + username = mkOption { + type = nullOr str; + default = null; + }; + password = mkOption { + type = nullOr str; + default = null; + }; + nextPageExpect = mkOption { + type = listOf str; + }; + }; + }); + }; + startUrl = mkOption { + type = str; + default = "http://${config.test.fqdn}"; + }; + }; + config = { + networking.hosts = { + "192.168.1.2" = [ config.test.fqdn ]; + }; + + environment.variables = { + PLAYWRIGHT_BROWSERS_PATH = pkgs.playwright-driver.browsers; + }; + + environment.systemPackages = [ + (pkgs.writers.writePython3Bin "login_playwright" + { + libraries = [ pkgs.python3Packages.playwright ]; + flakeIgnore = [ "F401" "E501" ]; + } + (let + testCfg = pkgs.writeText "users.json" (builtins.toJSON cfg); + in '' + # import re + import json + import sys + from playwright.sync_api import expect + from playwright.sync_api import sync_playwright + + + browsers = { + "chromium": ["--headless", "--disable-gpu"], + "firefox": [], + "webkit": [] + } + if len(sys.argv) != 2 or sys.argv[1] not in browsers.keys(): + print(f"usage: {sys.argv[0]} [{'|'.join(browsers.keys())}]") + sys.exit(1) + browser_name = sys.argv[1] + browser_args = browsers.get(browser_name) + print(f"Running test on {browser_name} {' '.join(browser_args)}") + + with open("${testCfg}") as f: + testCfg = json.load(f) + + with sync_playwright() as p: + browser = getattr(p, browser_name).launch(args=browser_args) + + for u in testCfg["testLoginWith"]: + print(f"Testing for user {u['username']} and password {u['password']}") + + context = browser.new_context(ignore_https_errors=True) + context.tracing.start(screenshots=True, snapshots=True, sources=True) + page = context.new_page() + page.goto(testCfg['startUrl']) + + page.screenshot(path="example.png") + if u['username'] is not None: + page.get_by_label(testCfg['usernameFieldLabel']).fill(u['username']) + if u['password'] is not None: + page.get_by_label(testCfg['passwordFieldLabel']).fill(u['password']) + page.get_by_role("button", name=testCfg['loginButtonName']).click() + for line in u['nextPageExpect']: + eval(line) + + context.tracing.stop(path="trace.zip") + + browser.close() + '') + ) + ]; + }; + }; + backup = backupOption: { config, ... }: { imports = [ ../modules/blocks/restic.nix diff --git a/test/services/deluge.nix b/test/services/deluge.nix index 79a5a44..a10350f 100644 --- a/test/services/deluge.nix +++ b/test/services/deluge.nix @@ -68,6 +68,12 @@ let ''; basic = { config, ... }: { + imports = [ + testLib.baseModule + ../../modules/blocks/hardcodedsecret.nix + ../../modules/services/deluge.nix + ]; + test = { subdomain = "d"; }; @@ -92,6 +98,31 @@ let }; }; + clientLogin = { config, ... }: { + imports = [ + testLib.baseModule + testLib.clientLoginModule + ]; + test = { + subdomain = "d"; + }; + + test.login = { + passwordFieldLabel = "Password"; + loginButtonName = "Login"; + testLoginWith = [ + { password = "deluge"; nextPageExpect = [ + "expect(page.get_by_role('button', name='Login')).not_to_be_visible()" + "expect(page.get_by_text('Login Failed')).not_to_be_visible()" + ]; } + { password = "other"; nextPageExpect = [ + "expect(page.get_by_role('button', name='Login')).to_be_visible()" + "expect(page.get_by_text('Login Failed')).to_be_visible()" + ]; } + ]; + }; + }; + prometheus = { config, ... }: { shb.deluge = { prometheusScraperPassword.result = config.shb.hardcodedsecret."scraper".result; @@ -118,11 +149,13 @@ in basic = pkgs.testers.runNixOSTest { name = "deluge_basic"; + nodes.client = { + imports = [ + clientLogin + ]; + }; nodes.server = { imports = [ - testLib.baseModule - ../../modules/blocks/hardcodedsecret.nix - ../../modules/services/deluge.nix basic ]; }; @@ -137,9 +170,6 @@ in nodes.server = { config, ... }: { imports = [ - testLib.baseModule - ../../modules/blocks/hardcodedsecret.nix - ../../modules/services/deluge.nix basic (testLib.backup config.shb.deluge.backup) ]; @@ -155,11 +185,8 @@ in nodes.server = { imports = [ - testLib.baseModule - ../../modules/blocks/hardcodedsecret.nix - ../../modules/services/deluge.nix - testLib.certs basic + testLib.certs https ]; }; @@ -174,11 +201,8 @@ in nodes.server = { config, ... }: { imports = [ - testLib.baseModule - ../../modules/blocks/hardcodedsecret.nix - ../../modules/services/deluge.nix - testLib.certs basic + testLib.certs https testLib.ldap (testLib.sso config.shb.certs.certs.selfsigned.n) @@ -198,11 +222,8 @@ in nodes.server = { imports = [ - testLib.baseModule - ../../modules/blocks/hardcodedsecret.nix - ../../modules/services/deluge.nix - testLib.certs basic + testLib.certs https prometheus ];