From 36eb700c0c92a00ed4e7fb5efac9b1ea94373493 Mon Sep 17 00:00:00 2001 From: Mahbub Ul Alam Date: Wed, 27 Nov 2024 08:24:48 +0100 Subject: [PATCH 1/2] SS-1177 add e2e test checking that changes in the user profile are saved in the database (#256) Co-authored-by: akochari Source: https://scilifelab.atlassian.net/browse/SS-1177 This task is to add a new test to our e2e tests that would check whether provided new information on the Edit profile page is actually saved in the database (i.e. displays when the user goes to the user profile page after saving the changes). --- common/models.py | 8 +++++ cypress/e2e/setup-scripts/seed_login_user.py | 9 +++++ .../test-login-account-handling.cy.js | 33 +++++++++++++++++++ cypress/fixtures/users.json | 4 +++ static/js/form-helpers.js | 29 +++++++++------- 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/common/models.py b/common/models.py index e1019d878..9df2e296f 100644 --- a/common/models.py +++ b/common/models.py @@ -7,6 +7,12 @@ logger = get_logger(__name__) +class UserProfileManager(models.Manager): + def create_user_profile(self, user: User): + user_profile = self.create(user=user) + return user_profile + + class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) affiliation = models.CharField(max_length=100, blank=True) @@ -19,6 +25,8 @@ class UserProfile(models.Model): note = models.TextField(max_length=1000, blank=True) + objects = UserProfileManager() + def __str__(self): return f"{self.user.email}" diff --git a/cypress/e2e/setup-scripts/seed_login_user.py b/cypress/e2e/setup-scripts/seed_login_user.py index cbfe132e5..4e73a632e 100755 --- a/cypress/e2e/setup-scripts/seed_login_user.py +++ b/cypress/e2e/setup-scripts/seed_login_user.py @@ -6,6 +6,7 @@ from django.conf import settings from django.contrib.auth.models import User +from common.models import UserProfile from projects.models import Project cypress_path = os.path.join(settings.BASE_DIR, "cypress/fixtures") @@ -22,4 +23,12 @@ # Create the login user user = User.objects.create_user(username, email, pwd) + user.first_name = userdata["first_name"] + user.last_name = userdata["last_name"] user.save() + + # needed for editing profile information (see SS-1177) + user_profile = UserProfile.objects.create_user_profile(user) + user_profile.department = userdata["department"] + user_profile.affiliation = userdata["affiliation"] + user_profile.save() diff --git a/cypress/e2e/ui-tests/test-login-account-handling.cy.js b/cypress/e2e/ui-tests/test-login-account-handling.cy.js index 1312da8e3..e2259a4a8 100644 --- a/cypress/e2e/ui-tests/test-login-account-handling.cy.js +++ b/cypress/e2e/ui-tests/test-login-account-handling.cy.js @@ -60,6 +60,39 @@ describe("Test login, profile page view, password change, password reset", () => cy.get('div.col-8').should("contain", users.login_user.email) }) + it("can edit user profile information", () => { + + function editProfile(firstName, lastName, department) { + + cy.url().should("include", "edit-profile/") + + cy.get('#id_first_name').clear().type(firstName); + cy.get('#id_last_name').clear().type(lastName); + cy.get('#id_department').clear().type(department); + cy.get('#submit-id-save').click(); + + cy.contains(firstName).should('exist'); + cy.contains(lastName).should('exist'); + cy.contains(department).should('exist'); + } + + + cy.loginViaUI(users.login_user.email, users.login_user.password) + + cy.log("Editing and verifying userprofile by accessing it from the navbar") + cy.visit("/") + cy.get('button.btn-profile').click() + cy.get('li.btn-group').find('a').contains("Edit profile").click() + editProfile('changing first name', 'changing last name', 'changing department name'); + + cy.log("Editing and verifying userprofile by accessing it from the profile view") + cy.visit("/") + cy.get('button.btn-profile').click() + cy.get('li.btn-group').find('a').contains("My profile").click() + cy.get('button.btn-profile').contains('a', 'Edit').click(); + editProfile('changing first name again', 'changing last name again', 'changing department name again'); + }) + it("can change user password", () => { cy.loginViaUI(users.login_user.email, users.login_user.password) cy.visit("/") diff --git a/cypress/fixtures/users.json b/cypress/fixtures/users.json index f6319ecb7..660094cae 100644 --- a/cypress/fixtures/users.json +++ b/cypress/fixtures/users.json @@ -25,7 +25,11 @@ "username": "e2e_tests_deploy_app_user" }, "login_user": { + "affiliation": "uu", + "department": "e2e-tests-login-user-department-name", "email": "no-reply-login@scilifelab.uu.se", + "first_name": "e2e-tests-login-user-first-name", + "last_name": "e2e-tests-login-user-last-name", "password": "tesT12345@", "reset_password": "^x#^PbLnDY!{J0ju", "username": "e2e_tests_login_user" diff --git a/static/js/form-helpers.js b/static/js/form-helpers.js index 87dda2698..7dc0cb054 100644 --- a/static/js/form-helpers.js +++ b/static/js/form-helpers.js @@ -8,6 +8,7 @@ window.onload = (event) => { const domainRegex = /^(?:(?!\b(?:student|stud)\b\.)[A-Z0-9](?:[\.A-Z0-9-]{0,61}[A-Z0-9])?\.)*?(uu|lu|gu|su|umu|liu|ki|kth|chalmers|ltu|hhs|slu|kau|lth|lnu|oru|miun|mau|mdu|bth|fhs|gih|hb|du|hig|hh|hkr|his|hv|ju|sh)\.se$/i; function changeVisibility() { + let shouldHide = false; let match; @@ -30,23 +31,27 @@ window.onload = (event) => { department_label.classList.remove('required'); } - if (shouldHide) { - request_account_field.classList.add('hidden'); - } else { - request_account_field.classList.remove('hidden'); - request_account_label.classList.add('required'); + if (request_account_field){ // to prevent Uncaught TypeError for null value + if (shouldHide) { + request_account_field.classList.add('hidden'); + } else { + request_account_field.classList.remove('hidden'); + request_account_label.classList.add('required'); + } } } - // Temporarily disable transitions - request_account_field.style.transition = 'none'; + if (request_account_field){ // to prevent Uncaught TypeError for null value + // Temporarily disable transitions + request_account_field.style.transition = 'none'; - changeVisibility(); + changeVisibility(); - // Restore transitions after a short delay - setTimeout(() => { - request_account_field.style.transition = ''; - }, 50); + // Restore transitions after a short delay + setTimeout(() => { + request_account_field.style.transition = ''; + }, 50); + } email.addEventListener('input', changeVisibility); }; From 148aa2e51e5f301d05192c36c63c94e8e7c606e7 Mon Sep 17 00:00:00 2001 From: Mahbub Ul Alam Date: Wed, 27 Nov 2024 15:01:42 +0100 Subject: [PATCH 2/2] SS-1203-Add-e2e-test-for-making-sure-that-users-can-set-custom-start-URLs-for-their-apps (#257) Source: https://scilifelab.atlassian.net/browse/SS-1203 Creating E2E tests to ensure, a) when users create apps and set custom URLs, this URL is respected b) changing this URL works c) adding a custom URL to an already running app works d) giving invalid input in this field results in an error --- cypress/e2e/ui-tests/test-deploy-app.cy.js | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/cypress/e2e/ui-tests/test-deploy-app.cy.js b/cypress/e2e/ui-tests/test-deploy-app.cy.js index e8ed49682..040762fc1 100644 --- a/cypress/e2e/ui-tests/test-deploy-app.cy.js +++ b/cypress/e2e/ui-tests/test-deploy-app.cy.js @@ -80,6 +80,9 @@ describe("Test deploying app", () => { const createResources = Cypress.env('create_resources'); const app_type = "Custom App" const app_source_code_public = "https://doi.org/example" + const default_url_subpath = "default/url/subpath/" + const changed_default_url_subpath = "changed/subpath/" + const invalid_default_url_subpath = "€% / ()" let volume_display_text = "project-vol (" + project_name + ")" @@ -97,9 +100,15 @@ describe("Test deploying app", () => { cy.get('#id_port').clear().type(image_port) cy.get('#id_image').clear().type(image_name) cy.get('#id_path').clear().type(app_path) + cy.get('button.accordion-button.collapsed[data-bs-target="#advanced-settings"]').click(); // Go to Advanced settings + cy.get('#id_default_url_subpath').clear().type(default_url_subpath) // provide default_url_subpath cy.get('#submit-id-submit').contains('Submit').click() // check that the app was created verifyAppStatus(app_name_project, "Running", "project") + // check that the default URL subpath was created + cy.contains('a', app_name_project) + .should('have.attr', 'href') + .and('include', default_url_subpath); // check that the app is not visible under public apps cy.visit('/apps/') cy.get('h3').should('contain', 'Public applications and models') @@ -138,6 +147,8 @@ describe("Test deploying app", () => { cy.get('#id_image').clear().type(image_name) cy.get('#id_path').clear().type(app_path) cy.get('#id_volume').select(volume_display_text) + cy.get('button.accordion-button.collapsed[data-bs-target="#advanced-settings"]').click(); // Go to Advanced settings + cy.get('#id_default_url_subpath').clear().type(default_url_subpath) // provide default_url_subpath cy.get('#submit-id-submit').contains('Submit').click() verifyAppStatus(app_name_public, "Running", "public") @@ -147,6 +158,11 @@ describe("Test deploying app", () => { verifyAppStatus(app_name_public, "Running", "public") }) + // check that the default URL subpath was created + cy.contains('a', app_name_public) + .should('have.attr', 'href') + .and('include', default_url_subpath); + cy.visit("/apps") cy.get('h5.card-title').should('contain', app_name_public) cy.get('.card-text').find('p').should('contain', app_description) @@ -199,6 +215,9 @@ describe("Test deploying app", () => { cy.get('#id_image').clear().type(image_name_2) cy.get('#id_path').should('have.value', app_path) cy.get('#id_path').clear().type(app_path_2) + cy.get('button.accordion-button.collapsed[data-bs-target="#advanced-settings"]').click(); // Go to Advanced settings + cy.get('#id_default_url_subpath').should('have.value', default_url_subpath) // default_url_subpath should be same as before + cy.get('#id_default_url_subpath').clear().type(changed_default_url_subpath) // provide changed_default_url_subpath cy.get('#submit-id-submit').contains('Submit').click() // NB: it will get status "Running" but it won't work because the new port is incorrect @@ -209,6 +228,11 @@ describe("Test deploying app", () => { verifyAppStatus(app_name_public_2, "Running", "link") }) + // check that the default URL subpath was changed + cy.contains('a', app_name_public_2) + .should('have.attr', 'href') + .and('include', changed_default_url_subpath); + // Check that the changes were saved cy.visit("/projects/") cy.contains('.card-title', project_name).parents('.card-body').siblings('.card-footer').find('a:contains("Open")').first().click() @@ -221,6 +245,17 @@ describe("Test deploying app", () => { cy.get('#id_port').should('have.value', image_port_2) cy.get('#id_image').should('have.value', image_name_2) cy.get('#id_path').should('have.value', app_path_2) + cy.get('button.accordion-button.collapsed[data-bs-target="#advanced-settings"]').click(); // Go to Advanced settings + cy.get('#id_default_url_subpath').should('have.value', changed_default_url_subpath) // changed_url_subpath should be same as before + + // Make sure that giving invalid input in default_url_subpath field results in an error + cy.get('#id_default_url_subpath').clear().type(invalid_default_url_subpath) // provide invalid_default_url_subpath + cy.get('#submit-id-submit').contains('Submit').click() // this should trigger the error + + // check this invalid_default_url_subpath error was matched + cy.get('.client-validation-feedback.client-validation-invalid') + .should('exist') + .and('include.text', 'Your custom URL subpath is not valid, please correct it'); // Remove the created public app and verify that it is deleted from public apps page cy.logf("Now deleting the public app", Cypress.currentTest)