add playwright test for nextcloud

This commit is contained in:
ibizaman 2025-01-30 21:40:10 +01:00 committed by Pierre Penninckx
parent d13ed1c0ef
commit c280b57bf5
4 changed files with 79 additions and 53 deletions

View file

@ -91,6 +91,10 @@ let
if response['auth_query'] != "rd=${proto_fqdn}/": if response['auth_query'] != "rd=${proto_fqdn}/":
raise Exception(f"auth query should be rd=${proto_fqdn}/ but is {response['auth_query']}") raise Exception(f"auth query should be rd=${proto_fqdn}/ but is {response['auth_query']}")
'') '')
+ (let
script = extraScript args;
in
lib.optionalString (script != "") script)
+ (optionalString (hasAttr "test" nodes.client && hasAttr "login" nodes.client.test) '' + (optionalString (hasAttr "test" nodes.client && hasAttr "login" nodes.client.test) ''
with subtest("Login"): with subtest("Login"):
code, logs = client.execute("login_playwright firefox") code, logs = client.execute("login_playwright firefox")
@ -99,10 +103,6 @@ let
if code != 0: if code != 0:
raise Exception("login_playwright did not succeed") raise Exception("login_playwright did not succeed")
'') '')
+ (let
script = extraScript args;
in
lib.optionalString (script != "") script)
); );
backupScript = args: (accessScript args).override { backupScript = args: (accessScript args).override {
@ -153,15 +153,15 @@ in
cfg = config.test.login; cfg = config.test.login;
in { in {
options.test.login = { options.test.login = {
usernameFieldLabel = mkOption { usernameFieldLabelRegex = mkOption {
type = str; type = str;
default = "username"; default = "username";
}; };
passwordFieldLabel = mkOption { passwordFieldLabelRegex = mkOption {
type = str; type = str;
default = "password"; default = "password";
}; };
loginButtonName = mkOption { loginButtonNameRegex = mkOption {
type = str; type = str;
default = "login"; default = "login";
}; };
@ -234,6 +234,7 @@ in
print(f"Testing for user {u['username']} and password {u['password']}") print(f"Testing for user {u['username']} and password {u['password']}")
context = browser.new_context(ignore_https_errors=True) context = browser.new_context(ignore_https_errors=True)
context.set_default_navigation_timeout(2 * 60 * 1000)
context.tracing.start(screenshots=True, snapshots=True, sources=True) context.tracing.start(screenshots=True, snapshots=True, sources=True)
try: try:
page = context.new_page() page = context.new_page()
@ -241,14 +242,14 @@ in
page.goto(testCfg['startUrl']) page.goto(testCfg['startUrl'])
if u['username'] is not None: if u['username'] is not None:
print(f"Filling field {testCfg['usernameFieldLabel']} with {u['username']}") print(f"Filling field {testCfg['usernameFieldLabelRegex']} with {u['username']}")
page.get_by_label(testCfg['usernameFieldLabel']).fill(u['username']) page.get_by_label(re.compile(testCfg['usernameFieldLabelRegex'])).fill(u['username'])
if u['password'] is not None: if u['password'] is not None:
print(f"Filling field {testCfg['passwordFieldLabel']} with {u['password']}") print(f"Filling field {testCfg['passwordFieldLabelRegex']} with {u['password']}")
page.get_by_label(testCfg['passwordFieldLabel']).fill(u['password']) page.get_by_label(re.compile(testCfg['passwordFieldLabelRegex'])).fill(u['password'])
print(f"Clicking button {testCfg['loginButtonName']}") print(f"Clicking button {testCfg['loginButtonNameRegex']}")
page.get_by_role("button", name=testCfg['loginButtonName']).click() page.get_by_role("button", name=re.compile(testCfg['loginButtonNameRegex'])).click()
for line in u['nextPageExpect']: for line in u['nextPageExpect']:
print(f"Running: {line}") print(f"Running: {line}")

View file

@ -108,8 +108,8 @@ let
}; };
test.login = { test.login = {
passwordFieldLabel = "Password"; passwordFieldLabelRegex = "Password";
loginButtonName = "Login"; loginButtonNameRegex = "Login";
testLoginWith = [ testLoginWith = [
{ password = "deluge"; nextPageExpect = [ { password = "deluge"; nextPageExpect = [
"expect(page.get_by_role('button', name='Login')).not_to_be_visible()" "expect(page.get_by_role('button', name='Login')).not_to_be_visible()"

View file

@ -80,9 +80,9 @@ let
test.login = { test.login = {
startUrl = "http://${config.test.fqdn}/user/login"; startUrl = "http://${config.test.fqdn}/user/login";
usernameFieldLabel = "Username or email address"; usernameFieldLabelRegex = "Username or email address";
passwordFieldLabel = "Password"; passwordFieldLabelRegex = "Password";
loginButtonName = "Sign In"; loginButtonNameRegex = "Sign In";
testLoginWith = [ testLoginWith = [
{ username = "theadmin"; password = adminPassword + "oops"; nextPageExpect = [ { username = "theadmin"; password = adminPassword + "oops"; nextPageExpect = [
"expect(page.get_by_text('Username or password is incorrect.')).to_be_visible()" "expect(page.get_by_text('Username or password is incorrect.')).to_be_visible()"

View file

@ -21,20 +21,9 @@ let
def find_in_logs(unit, text): def find_in_logs(unit, text):
return server.systemctl("status {}".format(unit))[1].find(text) != -1 return server.systemctl("status {}".format(unit))[1].find(text) != -1
with subtest("cron job succeeds"): with subtest("cron job starts"):
# This calls blocks until the service is done. # This call does not block until the service is done.
server.systemctl("start nextcloud-cron.service") server.succeed("systemctl start nextcloud-cron.service&")
# If the service failed, then we're not happy.
status = "active"
while status == "active":
status = server.get_unit_info("nextcloud-cron")["ActiveState"]
time.sleep(5)
if status != "inactive":
raise Exception("Cron job did not finish correctly")
if not find_in_logs("nextcloud-cron", "nextcloud-cron.service: Deactivated successfully."):
raise Exception("Nextcloud cron job did not finish successfully.")
with subtest("fails with incorrect authentication"): with subtest("fails with incorrect authentication"):
client.fail( client.fail(
@ -106,10 +95,27 @@ let
) )
if content != "hello\n": if content != "hello\n":
raise Exception("Got incorrect content for file, expected 'hello\n' but got:\n{}".format(content)) raise Exception("Got incorrect content for file, expected 'hello\n' but got:\n{}".format(content))
with subtest("cron job succeeds"):
# If the service failed, then we're not happy.
status = "active"
while status == "active":
status = server.get_unit_info("nextcloud-cron")["ActiveState"]
time.sleep(5)
if status != "inactive":
raise Exception("Cron job did not finish correctly")
if not find_in_logs("nextcloud-cron", "nextcloud-cron.service: Deactivated successfully."):
raise Exception("Nextcloud cron job did not finish successfully.")
''; '';
}; };
basic = { config, ... }: { basic = { config, ... }: {
imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
];
test = { test = {
subdomain = "n"; subdomain = "n";
}; };
@ -136,6 +142,36 @@ let
}; };
}; };
clientLogin = { config, ... }: {
imports = [
testLib.baseModule
testLib.clientLoginModule
];
virtualisation.memorySize = 4096;
test = {
subdomain = "n";
};
test.login = {
startUrl = "http://${config.test.fqdn}";
usernameFieldLabelRegex = "[Uu]sername";
passwordFieldLabelRegex = "^ *[Pp]assword";
loginButtonNameRegex = "[Ll]og [Ii]n";
testLoginWith = [
{ username = adminUser; password = adminPass; nextPageExpect = [
"expect(page.get_by_text('Wrong username or password')).not_to_be_visible()"
"expect(page.get_by_role('button', name=re.compile('[Ll]og [Ii]n'))).not_to_be_visible()"
"expect(page).to_have_title(re.compile('Dashboard'))"
]; }
# Failure is after so we're not throttled too much.
{ username = adminUser; password = adminPass + "oops"; nextPageExpect = [
"expect(page.get_by_text('Wrong username or password')).to_be_visible()"
]; }
];
};
};
https = { config, ...}: { https = { config, ...}: {
shb.nextcloud = { shb.nextcloud = {
ssl = config.shb.certs.certs.selfsigned.n; ssl = config.shb.certs.certs.selfsigned.n;
@ -234,10 +270,13 @@ in
basic = pkgs.testers.runNixOSTest { basic = pkgs.testers.runNixOSTest {
name = "nextcloud_basic"; name = "nextcloud_basic";
nodes.client = {
imports = [
clientLogin
];
};
nodes.server = { nodes.server = {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
basic basic
]; ];
}; };
@ -252,8 +291,6 @@ in
nodes.server = { config, ... }: { nodes.server = { config, ... }: {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
basic basic
(testLib.backup config.shb.nextcloud.backup) (testLib.backup config.shb.nextcloud.backup)
]; ];
@ -269,10 +306,8 @@ in
nodes.server = { nodes.server = {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
testLib.certs
basic basic
testLib.certs
https https
]; ];
}; };
@ -288,10 +323,8 @@ in
nodes.server = { nodes.server = {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
testLib.certs
basic basic
testLib.certs
https https
previewgenerator previewgenerator
]; ];
@ -307,10 +340,8 @@ in
nodes.server = { nodes.server = {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
testLib.certs
basic basic
testLib.certs
https https
externalstorage externalstorage
]; ];
@ -326,10 +357,8 @@ in
nodes.server = { config, ... }: { nodes.server = { config, ... }: {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
testLib.certs
basic basic
testLib.certs
https https
testLib.ldap testLib.ldap
ldap ldap
@ -346,10 +375,8 @@ in
nodes.server = { config, ... }: { nodes.server = { config, ... }: {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
testLib.certs
basic basic
testLib.certs
https https
testLib.ldap testLib.ldap
ldap ldap
@ -368,8 +395,6 @@ in
nodes.server = { config, ... }: { nodes.server = { config, ... }: {
imports = [ imports = [
testLib.baseModule
../../modules/services/nextcloud-server.nix
basic basic
prometheus prometheus
]; ];