From 8cb2decb5130724a3b2305f4ea65fb418d3ecff5 Mon Sep 17 00:00:00 2001 From: downtownallday Date: Mon, 18 Dec 2023 15:00:08 -0500 Subject: [PATCH] Update QA tests for Nextcloud 28 --- tests/lib/all.sh | 7 +- .../python/browser/NcContactsAutomation.py | 4 +- .../lib/python/browser/NextcloudAutomation.py | 32 ++++- tests/lib/webdav.sh | 133 ++++++++++++++++++ tests/suites/nextcloud/contacts.py | 3 + tests/suites/remote-nextcloud.sh | 14 +- tests/suites/roundcube.sh | 16 ++- 7 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 tests/lib/webdav.sh diff --git a/tests/lib/all.sh b/tests/lib/all.sh index dc80ffb3..c744f68c 100644 --- a/tests/lib/all.sh +++ b/tests/lib/all.sh @@ -22,8 +22,9 @@ D=$(dirname "$BASH_SOURCE") . "$D/rest.sh" || exit 4 . "$D/system.sh" || exit 5 . "$D/carddav.sh" || exit 6 +. "$D/webdav.sh" || exit 7 -. "$D/populate.sh" || exit 7 -. "$D/installed-state.sh" || exit 8 -. "$D/totp.sh" || exit 9 +. "$D/populate.sh" || exit 8 +. "$D/installed-state.sh" || exit 9 +. "$D/totp.sh" || exit 10 diff --git a/tests/lib/python/browser/NcContactsAutomation.py b/tests/lib/python/browser/NcContactsAutomation.py index 7ba80630..386f257a 100644 --- a/tests/lib/python/browser/NcContactsAutomation.py +++ b/tests/lib/python/browser/NcContactsAutomation.py @@ -31,7 +31,9 @@ class NcContactsAutomation(object): fullname = el.find_el('.line-one,.option__lineone').content().strip() email = el.find_el('.line-two,.option__linetwo').content().strip() d.say_verbose('contact: "%s" <%s>', fullname, email) - if fullname.lower() == "%s %s" % (contact['givenname'].lower(), contact['surname'].lower()) and email.lower() == contact['email'].lower(): + # NC 28: email not present in html + ignore_email = True if email == '' else False + if fullname.lower() == "%s %s" % (contact['givenname'].lower(), contact['surname'].lower()) and ( ignore_email or email.lower() == contact['email'].lower() ): found = True el.click() break diff --git a/tests/lib/python/browser/NextcloudAutomation.py b/tests/lib/python/browser/NextcloudAutomation.py index 4c31b96c..1633610f 100644 --- a/tests/lib/python/browser/NextcloudAutomation.py +++ b/tests/lib/python/browser/NextcloudAutomation.py @@ -9,6 +9,8 @@ from selenium.common.exceptions import ( NoSuchElementException, + ElementNotInteractableException, + ElementClickInterceptedException, ) from .NcContactsAutomation import NcContactsAutomation @@ -32,18 +34,35 @@ class NextcloudAutomation(object): if not submit: submit = d.find_el('#submit-wrapper') # nc<25 submit.click() - def logout(self): + def click_avatar(self): d = self.d - d.say("Logout of Nextcloud") + d.say("click avatar") el = d.find_el('#user-menu > a', throws=False) + if not el: + el = d.find_el('#user-menu > button', throws=False) + if not el: # nc < 26 d.find_el('#settings .avatardiv').click() - d.find_el('[data-id="logout"] a').click() else: # nc >= 26 el.click() - d.find_el('#logout > a').click() + d.wait_tick(1) + + def logout(self): + d = self.d + d.say("Logout of Nextcloud") + self.click_avatar() + + el = d.find_el('[data-id="logout"] a', throws=False) # nc < 26 + if not el: + # nc >= 26 + el = d.find_el('#logout > a', throws=False) + + if el: + el.click() + else: + raise NoSuchElementException('could not find logout link') def open_contacts(self): d = self.d @@ -81,5 +100,8 @@ class NextcloudAutomation(object): firstrunwiz = d.find_el('#firstrunwizard', throws=False, quiet=True) if firstrunwiz and firstrunwiz.is_displayed(): d.say_verbose("closing first run wizard") - d.find_el('#firstrunwizard span.close-icon').click() + # ElementNotInteractableException + # d.find_el('#firstrunwizard span.close-icon').click() + d.execute_script('document.querySelector("#firstrunwizard span.close-icon").click()') + d.wait_tick(1) diff --git a/tests/lib/webdav.sh b/tests/lib/webdav.sh new file mode 100644 index 00000000..0e8b93f2 --- /dev/null +++ b/tests/lib/webdav.sh @@ -0,0 +1,133 @@ +##### +##### This file is part of Mail-in-a-Box-LDAP which is released under the +##### terms of the GNU Affero General Public License as published by the +##### Free Software Foundation, either version 3 of the License, or (at +##### your option) any later version. See file LICENSE or go to +##### https://github.com/downtownallday/mailinabox-ldap for full license +##### details. +##### + +# +# requires: +# system packages: [ curl ] +# scripts: [ color-output.sh, misc.sh, locations.sh, carddav.sh ] +# + +webdav_url() { + local user="$1" + local path="${2:-/}" + # nextcloud_url is defined in carddav.sh + local nc_url="$(nextcloud_url)" + echo "${nc_url%/}/remote.php/dav/files/$user${path%/}" +} + +webdav_rest() { + # issue a WebDAV rest call to Nextcloud + # + # eg: webdav_rest PROPFIND / qa@abc.com Test_1234 + # + # The function will set the following global variables regardless + # of exit code: + # REST_HTTP_CODE + # REST_OUTPUT + # REST_ERROR + # REST_ERROR_BRIEF + # + # Return values: + # 0 indicates success (curl returned 0 or a code deemed to be + # successful and HTTP status is >=200 but <300) + # 1 curl returned with non-zero code that indicates and error + # 2 the response status was <200 or >= 300 + # + # Debug messages are sent to stderr + # + local verb="$1" + local uri="$2" + local auth_user="$3" + local auth_pass="$4" + shift; shift; shift; shift # remaining arguments are data + + local url + case "$uri" in + http* ) + url="$uri" + ;; + * ) + url="$(webdav_url "$auth_user")${uri#/}" + ;; + esac + + local data=() + local item output onlydata="false" + + for item; do + case "$item" in + -- ) + onlydata="true" + ;; + --* ) + # curl argument + if $onlydata; then + data+=("--data" "$item"); + else + data+=("$item") + fi + ;; + * ) + onlydata="true" + data+=("--data" "$item"); + ;; + esac + done + + local ct='text/xml; charset="utf-8"' + local tmp1="/tmp/curl.$$.tmp" + + echo "spawn: curl -w \"%{http_code}\" -X $verb -H 'Content-Type: $ct' --user \"${auth_user}:xxx\" ${data[@]} \"$url\"" 1>&2 + output=$(curl -s -S -w "%{http_code}" -X $verb -H "Content-Type: $ct" --user "${auth_user}:${auth_pass}" "${data[@]}" "$url" 2>$tmp1) + local code=$? + + # http status is last 3 characters of output, extract it + REST_HTTP_CODE=$(awk '{S=substr($0,length($0)-2)} END {print S}' <<<"$output") + REST_OUTPUT=$(awk 'BEGIN{L=""}{ if(L!="") print L; L=$0 } END { print substr(L,1,length(L)-3) }' <<<"$output") + REST_ERROR="" + REST_ERROR_BRIEF="" + [ -z "$REST_HTTP_CODE" ] && REST_HTTP_CODE="000" + + if [ $code -ne 0 -o \ + $REST_HTTP_CODE -lt 200 -o \ + $REST_HTTP_CODE -ge 300 ] + then + if [ $code -ne 0 -a "$REST_HTTP_CODE" == "000" ]; then + REST_ERROR="exit code $code" + REST_ERROR_BRIEF="$REST_ERROR" + else + REST_ERROR="REST status $REST_HTTP_CODE: $REST_OUTPUT" + REST_ERROR_BRIEF=$(python3 -c "import xml.etree.ElementTree as ET; print(ET.fromstring(r'''$REST_OUTPUT''').find('s:message',{'s':'http://sabredav.org/ns'}).text)" 2>/dev/null) + if [ -z "$REST_ERROR_BRIEF" ]; then + REST_ERROR_BRIEF="$REST_ERROR" + else + REST_ERROR_BRIEF="$REST_HTTP_CODE: $REST_ERROR_BRIEF" + fi + if [ $code -ne 0 ]; then + REST_ERROR_BRIEF="exit code $code: $REST_ERROR_BRIEF" + REST_ERROR="exit code $code: $REST_ERROR" + fi + fi + + if [ -s $tmp1 ]; then + REST_ERROR="$REST_ERROR: $(cat $tmp1)" + REST_ERROR_BRIEF="$REST_ERROR_BRIEF: $(cat $tmp1)" + fi + rm -f $tmp1 + + echo "${F_DANGER}$REST_ERROR${F_RESET}" 1>&2 + [ $code -ne 0 ] && return 1 + return 2 + fi + + echo "CURL succeded, HTTP status $REST_HTTP_CODE" 1>&2 + echo "$output" 1>&2 + rm -f $tmp1 + return 0 +} diff --git a/tests/suites/nextcloud/contacts.py b/tests/suites/nextcloud/contacts.py index beef1fc8..ddf82b41 100644 --- a/tests/suites/nextcloud/contacts.py +++ b/tests/suites/nextcloud/contacts.py @@ -65,6 +65,9 @@ try: contacts.click_contact(contact) contacts.wait_contact_loaded() contacts.delete_current_contact() + + elif op=='nop': + pass else: raise ValueError('Invalid operation: %s' % op) diff --git a/tests/suites/remote-nextcloud.sh b/tests/suites/remote-nextcloud.sh index 0eff2ef3..69995aa7 100644 --- a/tests/suites/remote-nextcloud.sh +++ b/tests/suites/remote-nextcloud.sh @@ -86,7 +86,19 @@ test_nextcloud_contacts() { # create local user alice mgmt_assert_create_user "$alice" "$alice_pw" - + # NC 28: make sure user is initialized in nextcloud by logging + # them in and opening the contacts app using ui + # automation. otherwise, any access to contacts will fail with 404 + # "Addressbook with name 'contacts' could not be found". logging + # in, by say, using a WebDAV PROPFIND does not work. + record "Initialize $alice in nextcloud" + assert_browser_test \ + nextcloud/contacts.py \ + "nop" \ + "$alice" \ + "$alice_pw" \ + "" "" "" + # # 1. create contact in Nextcloud - ensure it is available in Roundcube # diff --git a/tests/suites/roundcube.sh b/tests/suites/roundcube.sh index 05caecff..d8f2a62b 100644 --- a/tests/suites/roundcube.sh +++ b/tests/suites/roundcube.sh @@ -71,7 +71,21 @@ test_create_contact() { # generate an email address - the contact's email must be # unique or it can't be created local contact_email="bob_bacon$(generate_uuid | awk -F- '{print $1 }')@example.com" - + + # NC 28: make sure user is initialized in nextcloud by logging + # them in and opening the contacts app using ui + # automation. otherwise, any access to contacts will fail with + # 404 "Addressbook with name 'contacts' could not be + # found". logging in, by say, using a WebDAV PROPFIND does not + # work. + record "Initialize $alice in nextcloud" + assert_browser_test \ + nextcloud/contacts.py \ + "nop" \ + "$alice" \ + "$alice_pw" \ + "" "" "" + # create a contact using roundcube's ui record "[create contact in Roundcube]" if assert_browser_test \