From 34f54a96cfbf3a80657528c45b5d9fb314baafa6 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 23 Nov 2020 14:24:42 +0100 Subject: [PATCH] tests: update e2e tests for SPA --- .github/FUNDING.yml | 2 +- tests/e2e/test_flows_enroll.py | 45 ++++++-------------------- tests/e2e/test_flows_login.py | 6 ++-- tests/e2e/test_flows_otp.py | 49 ++++++++++++----------------- tests/e2e/test_flows_stage_setup.py | 14 ++++++--- tests/e2e/test_source_oauth.py | 24 +++++--------- tests/e2e/test_source_saml.py | 9 ++---- tests/e2e/utils.py | 17 ++++++++++ 8 files changed, 69 insertions(+), 97 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 133f36b27..178ae1513 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ["https://www.paypal.me/beryju"] +github: [BeryJu] diff --git a/tests/e2e/test_flows_enroll.py b/tests/e2e/test_flows_enroll.py index 9261eb3b1..3c38d8935 100644 --- a/tests/e2e/test_flows_enroll.py +++ b/tests/e2e/test_flows_enroll.py @@ -8,6 +8,7 @@ from docker.types import Healthcheck from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec +from passbook.core.models import User from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding from passbook.stages.email.models import EmailStage, EmailTemplates from passbook.stages.identification.models import IdentificationStage @@ -100,25 +101,13 @@ class TestFlowsEnroll(SeleniumTestCase): self.driver.find_element(By.ID, "id_email").send_keys("foo@bar.baz") self.driver.find_element(By.CSS_SELECTOR, ".pf-c-button").click() - self.wait.until(ec.presence_of_element_located((By.LINK_TEXT, "foo"))) - self.driver.find_element(By.LINK_TEXT, "foo").click() + self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pb-sidebar"))) + self.driver.get(self.shell_url("passbook_core:user-settings")) - self.wait_for_url(self.url("passbook_core:user-settings")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - "foo", - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo" - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_name").get_attribute("value"), - "some name", - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_email").get_attribute("value"), - "foo@bar.baz", - ) + user = User.objects.get(username="foo") + self.assertEqual(user.username, "foo") + self.assertEqual(user.name, "some name") + self.assertEqual(user.email, "foo@bar.baz") @retry() @override_settings(EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend") @@ -207,21 +196,7 @@ class TestFlowsEnroll(SeleniumTestCase): self.driver.switch_to.window(self.driver.window_handles[0]) # We're now logged in - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) - self.driver.find_element(By.ID, "user-settings").click() + self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pb-sidebar"))) + self.driver.get(self.shell_url("passbook_core:user-settings")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - "foo", - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo" - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_name").get_attribute("value"), - "some name", - ) - self.assertEqual( - self.driver.find_element(By.ID, "id_email").get_attribute("value"), - "foo@bar.baz", - ) + self.assert_user(User.objects.get(username="foo")) diff --git a/tests/e2e/test_flows_login.py b/tests/e2e/test_flows_login.py index 01d439d9d..c7085bb4e 100644 --- a/tests/e2e/test_flows_login.py +++ b/tests/e2e/test_flows_login.py @@ -21,7 +21,5 @@ class TestFlowsLogin(SeleniumTestCase): self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER) self.driver.find_element(By.ID, "id_password").send_keys(USER().username) self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - USER().username, - ) + self.wait_for_url(self.shell_url("passbook_core:overview")) + self.assert_user(USER()) diff --git a/tests/e2e/test_flows_otp.py b/tests/e2e/test_flows_otp.py index 46675d2be..7890c31d5 100644 --- a/tests/e2e/test_flows_otp.py +++ b/tests/e2e/test_flows_otp.py @@ -13,6 +13,8 @@ from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as ec from passbook.flows.models import Flow, FlowStageBinding +from passbook.stages.otp_static.models import OTPStaticStage +from passbook.stages.otp_time.models import OTPTimeStage from passbook.stages.otp_validate.models import OTPValidateStage from tests.e2e.utils import USER, SeleniumTestCase, retry @@ -47,11 +49,8 @@ class TestFlowsOTP(SeleniumTestCase): totp = TOTP(device.bin_key, device.step, device.t0, device.digits, device.drift) self.driver.find_element(By.ID, "id_code").send_keys(totp.token()) self.driver.find_element(By.ID, "id_code").send_keys(Keys.ENTER) - self.wait_for_url(self.url("passbook_core:overview")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - USER().username, - ) + self.wait_for_url(self.shell_url("passbook_core:overview")) + self.assert_user(USER()) @retry() def test_otp_totp_setup(self): @@ -64,23 +63,19 @@ class TestFlowsOTP(SeleniumTestCase): self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER) self.driver.find_element(By.ID, "id_password").send_keys(USER().username) self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - USER().username, + self.wait_for_url(self.shell_url("passbook_core:overview")) + self.assert_user(USER()) + + self.driver.get( + self.url( + "passbook_flows:configure", + stage_uuid=OTPTimeStage.objects.first().stage_uuid, + ) ) - self.driver.find_element(By.CSS_SELECTOR, ".pf-c-page__header").click() - self.driver.get(self.url("passbook_core:user-settings")) - - self.driver.find_element(By.LINK_TEXT, "Time-based OTP").click() - # Remember the current URL as we should end up back here destination_url = self.driver.current_url - self.driver.find_element( - By.CSS_SELECTOR, ".pf-c-card__body a.pf-c-button" - ).click() - self.wait.until(ec.presence_of_element_located((By.ID, "qr"))) otp_uri = self.driver.find_element(By.ID, "qr").get_attribute("data-otpuri") @@ -111,23 +106,19 @@ class TestFlowsOTP(SeleniumTestCase): self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER) self.driver.find_element(By.ID, "id_password").send_keys(USER().username) self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - USER().username, + self.wait_for_url(self.shell_url("passbook_core:overview")) + self.assert_user(USER()) + + self.driver.get( + self.url( + "passbook_flows:configure", + stage_uuid=OTPStaticStage.objects.first().stage_uuid, + ) ) - self.driver.find_element(By.CSS_SELECTOR, ".pf-c-page__header").click() - self.driver.find_element(By.ID, "user-settings").click() - self.wait_for_url(self.url("passbook_core:user-settings")) - - self.driver.find_element(By.LINK_TEXT, "Static OTP").click() - # Remember the current URL as we should end up back here destination_url = self.driver.current_url - self.driver.find_element( - By.CSS_SELECTOR, ".pf-c-card__body a.pf-c-button" - ).click() token = self.driver.find_element( By.CSS_SELECTOR, ".pb-otp-tokens li:nth-child(1)" ).text diff --git a/tests/e2e/test_flows_stage_setup.py b/tests/e2e/test_flows_stage_setup.py index efa14aa74..351e150f8 100644 --- a/tests/e2e/test_flows_stage_setup.py +++ b/tests/e2e/test_flows_stage_setup.py @@ -38,16 +38,20 @@ class TestFlowsStageSetup(SeleniumTestCase): self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER) self.driver.find_element(By.ID, "id_password").send_keys(USER().username) self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) - self.driver.find_element(By.CSS_SELECTOR, ".pf-c-page__header").click() - self.driver.find_element(By.ID, "user-settings").click() - self.wait_for_url(self.url("passbook_core:user-settings")) - self.driver.find_element(By.LINK_TEXT, "Change password").click() + self.wait_for_url(self.shell_url("passbook_core:overview")) + + self.driver.get( + self.url( + "passbook_flows:configure", + stage_uuid=PasswordStage.objects.first().stage_uuid, + ) + ) self.driver.find_element(By.ID, "id_password").send_keys(new_password) self.driver.find_element(By.ID, "id_password_repeat").click() self.driver.find_element(By.ID, "id_password_repeat").send_keys(new_password) self.driver.find_element(By.CSS_SELECTOR, ".pf-c-button").click() - self.wait_for_url(self.url("passbook_core:user-settings")) + self.wait_for_url(self.shell_url("passbook_core:overview")) # Because USER() is cached, we need to get the user manually here user = User.objects.get(username=USER().username) self.assertTrue(user.check_password(new_password)) diff --git a/tests/e2e/test_source_oauth.py b/tests/e2e/test_source_oauth.py index 18eea1bbb..1653915af 100644 --- a/tests/e2e/test_source_oauth.py +++ b/tests/e2e/test_source_oauth.py @@ -140,14 +140,10 @@ class TestSourceOAuth2(SeleniumTestCase): self.driver.find_element(By.NAME, "username").send_keys("foo") self.driver.find_element(By.NAME, "username").send_keys(Keys.ENTER) - # Wait until we've loaded the user info page - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + # Wait until we've logged in + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - "foo", - ) self.assertEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo" ) @@ -202,7 +198,7 @@ class TestSourceOAuth2(SeleniumTestCase): """test OAuth Source With With OIDC (enroll and authenticate again)""" self.test_oauth_enroll() # We're logged in at the end of this, log out and re-login - self.driver.find_element(By.ID, "logout").click() + self.driver.get(self.url("passbook_flows:default-invalidation")) self.wait.until( ec.presence_of_element_located( @@ -226,13 +222,10 @@ class TestSourceOAuth2(SeleniumTestCase): ) self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click() - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + # Wait until we've logged in + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - "foo", - ) self.assertEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo" ) @@ -322,13 +315,10 @@ class TestSourceOAuth1(SeleniumTestCase): # Wait until we've loaded the user info page sleep(2) - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + # Wait until we've logged in + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) - self.assertEqual( - self.driver.find_element(By.ID, "user-settings").text, - "example-user", - ) self.assertEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "example-user", diff --git a/tests/e2e/test_source_saml.py b/tests/e2e/test_source_saml.py index 38c564dce..7b531e7fb 100644 --- a/tests/e2e/test_source_saml.py +++ b/tests/e2e/test_source_saml.py @@ -133,11 +133,10 @@ class TestSourceSAML(SeleniumTestCase): self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER) # Wait until we're logged in - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) # Wait until we've loaded the user info page - self.wait.until(ec.presence_of_element_located((By.ID, "id_username"))) self.assertNotEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "" ) @@ -185,11 +184,10 @@ class TestSourceSAML(SeleniumTestCase): self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER) # Wait until we're logged in - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) # Wait until we've loaded the user info page - self.wait.until(ec.presence_of_element_located((By.ID, "id_username"))) self.assertNotEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "" ) @@ -235,11 +233,10 @@ class TestSourceSAML(SeleniumTestCase): self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER) # Wait until we're logged in - self.wait.until(ec.presence_of_element_located((By.ID, "user-settings"))) + self.wait_for_url(self.shell_url("passbook_core:overview")) self.driver.get(self.url("passbook_core:user-settings")) # Wait until we've loaded the user info page - self.wait.until(ec.presence_of_element_located((By.ID, "id_username"))) self.assertNotEqual( self.driver.find_element(By.ID, "id_username").get_attribute("value"), "" ) diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 289d9242a..41ad96a36 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -1,4 +1,5 @@ """passbook e2e testing utilities""" +import json from functools import wraps from glob import glob from importlib.util import module_from_spec, spec_from_file_location @@ -17,11 +18,13 @@ from docker import DockerClient, from_env from docker.models.containers import Container from selenium import webdriver from selenium.common.exceptions import NoSuchElementException, TimeoutException +from selenium.webdriver.common.by import By from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.support.ui import WebDriverWait from structlog import get_logger +from passbook.core.api.users import UserSerializer from passbook.core.models import User @@ -100,6 +103,20 @@ class SeleniumTestCase(StaticLiveServerTestCase): """reverse `view` with `**kwargs` into full URL using live_server_url""" return self.live_server_url + reverse(view, kwargs=kwargs) + def shell_url(self, view, **kwargs) -> str: + """same as self.url() but show URL in shell""" + return f"{self.live_server_url}/#{reverse(view, kwargs=kwargs)}" + + def assert_user(self, expected_user: User): + """Check users/me API and assert it matches expected_user""" + self.driver.get(self.url("passbook_api:user-me") + "?format=json") + user_json = self.driver.find_element(By.CSS_SELECTOR, "pre").text + user = UserSerializer(data=json.loads(user_json)) + user.is_valid() + self.assertEqual(user["username"].value, expected_user.username) + self.assertEqual(user["name"].value, expected_user.name) + self.assertEqual(user["email"].value, expected_user.email) + def apply_default_data(self): """apply objects created by migrations after tables have been truncated""" # Find all migration files