Skip to content

Commit

Permalink
Fix input fields not disabled correctly on login page and improve tes…
Browse files Browse the repository at this point in the history
…ts (#1794)

* Fix input fields not disabled correctly on login page

* Improve frontend tests for login page

* Update changelog
  • Loading branch information
Sabr1n4W authored Jan 20, 2025
1 parent 8a722f8 commit 93503f9
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Real-time input validation on create superuser command ([#1651])
- Error handling in room statistics ([#1535], [#1600])
- Error handling in room attendance ([#1535], [#1600])
- Improve frontend tests for login page ([#1794])

### Fixed

- Wrong error message shown for 422 errors when verifying email ([#1744], [#1758])
- Broken banner link style 'warning' ([#1759], [#1760])
- Inconsistent select/multiselect loading states ([#1772])
- Input fields not disabled correctly on login page ([#1791], [#1794])

## [v4.2.0] - 2025-01-06

Expand Down Expand Up @@ -344,6 +346,8 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#1759]: https://github.com/THM-Health/PILOS/issues/1759
[#1760]: https://github.com/THM-Health/PILOS/pull/1760
[#1772]: https://github.com/THM-Health/PILOS/pull/1772
[#1791]: https://github.com/THM-Health/PILOS/issues/1791
[#1794]: https://github.com/THM-Health/PILOS/pull/1794
[unreleased]: https://github.com/THM-Health/PILOS/compare/v4.2.0...develop
[v3.0.0]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.0
[v3.0.1]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.1
Expand Down
6 changes: 4 additions & 2 deletions resources/js/components/LoginTabLdap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
<div data-test="login-tab-ldap">
<h1 class="p-card-title">{{ props.title }}</h1>
<form @submit.prevent="submit">
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2" data-test="username-field">
<label :for="`${props.id}-username`">{{ props.usernameLabel }}</label>
<InputText
:id="`${props.id}-username`"
v-model="username"
:disabled="props.loading"
type="text"
autocomplete="username"
:placeholder="props.usernameLabel"
Expand All @@ -24,7 +25,7 @@
<FormError :errors="props.errors?.username" />
</div>

<div class="mt-6 flex flex-col gap-2">
<div class="mt-6 flex flex-col gap-2" data-test="password-field">
<label :for="`${props.id}-password`">{{ props.passwordLabel }}</label>
<Password
v-model="password"
Expand All @@ -34,6 +35,7 @@
toggle-mask
required
fluid
:disabled="props.loading"
:placeholder="props.passwordLabel"
:state="
props.errors !== null &&
Expand Down
6 changes: 4 additions & 2 deletions resources/js/components/LoginTabLocal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
<div data-test="login-tab-local">
<h1 class="p-card-title">{{ props.title }}</h1>
<form @submit.prevent="submit">
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2" data-test="email-field">
<label :for="`${props.id}-email`">{{ props.emailLabel }}</label>
<InputText
:id="`${props.id}-email`"
v-model="email"
type="text"
:disabled="props.loading"
autocomplete="email"
:placeholder="props.emailLabel"
aria-describedby="email-help-block"
Expand All @@ -21,7 +22,7 @@
<FormError :errors="props.errors?.email" />
</div>

<div class="mt-6 flex flex-col gap-2">
<div class="mt-6 flex flex-col gap-2" data-test="password-field">
<label :for="`${props.id}-password`">{{ props.passwordLabel }}</label>
<Password
v-model="password"
Expand All @@ -31,6 +32,7 @@
toggle-mask
required
fluid
:disabled="props.loading"
:placeholder="props.passwordLabel"
aria-describedby="password-help-block"
:state="
Expand Down
213 changes: 202 additions & 11 deletions tests/Frontend/e2e/Login.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ describe("Login", function () {

// Check if ldap login tab is shown correctly and click on login button
cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get("#ldap-username").type("user");
cy.get("#ldap-password").type("password");
cy.get('[data-test="username-field"]')
.should("be.visible")
.and("include.text", "auth.ldap.username")
.within(() => {
cy.get("#ldap-username").should("have.value", "").type("user");
});

cy.get('[data-test="password-field"]')
.should("be.visible")
.and("include.text", "auth.password")
.within(() => {
cy.get("#ldap-password").should("have.value", "").type("password");
});

// Intercept requests that will be needed to show the room index page (needed to check redirect)
cy.intercept("GET", "api/v1/currentUser", {
Expand All @@ -47,6 +58,10 @@ describe("Login", function () {
cy.interceptRoomIndexRequests();

cy.get("button").should("have.text", "auth.login").click();
// Check loading
cy.get("#ldap-username").should("be.disabled");
cy.get("#ldap-password").should("be.disabled");

// Check if button is disabled after being clicked and loading and send response
cy.get("button")
.should("be.disabled")
Expand Down Expand Up @@ -108,7 +123,7 @@ describe("Login", function () {
}).as("loginRequest");

// Visit page that can only be visited by logged in users
cy.visit("admin");
cy.visit("/admin");

// Check redirect to the login page
cy.url().should("include", "/login?redirect=/admin");
Expand Down Expand Up @@ -169,10 +184,23 @@ describe("Login", function () {

cy.visit("/login");

// Check if ldap login tab is shown correctly and click on login button
// Check if local login tab is shown correctly and click on login button
cy.get('[data-test="login-tab-local"]').within(() => {
cy.get("#local-email").type("[email protected]");
cy.get("#local-password").type("password");
cy.get('[data-test="email-field"]')
.should("be.visible")
.and("include.text", "app.email")
.within(() => {
cy.get("#local-email")
.should("have.value", "")
.type("[email protected]");
});

cy.get('[data-test="password-field"]')
.should("be.visible")
.and("include.text", "auth.password")
.within(() => {
cy.get("#local-password").should("have.value", "").type("password");
});

// Intercept requests that will be needed to show the room index page (needed to check redirect)
cy.intercept("GET", "api/v1/currentUser", {
Expand All @@ -183,6 +211,11 @@ describe("Login", function () {
cy.get('[data-test="login-button"]')
.should("have.text", "auth.login")
.click();

// Check loading
cy.get("#local-email").should("be.disabled");
cy.get("#local-password").should("be.disabled");

// Check if button is disabled after being clicked and loading and send response
cy.get('[data-test="login-button"]')
.should("be.disabled")
Expand Down Expand Up @@ -243,7 +276,7 @@ describe("Login", function () {
}).as("loginRequest");

// Visit page that can only be visited by logged in users
cy.visit("admin");
cy.visit("/admin");

// Check redirect to the login page
cy.url().should("include", "/login?redirect=/admin");
Expand Down Expand Up @@ -271,7 +304,7 @@ describe("Login", function () {
cy.url().should("include", "/admin").and("not.include", "/login");
});

it("login errors", function () {
it("local login errors", function () {
// Intercept config request to only show local login tab
cy.fixture("config.json").then((config) => {
config.data.auth.local = true;
Expand Down Expand Up @@ -303,7 +336,35 @@ describe("Login", function () {
cy.wait("@loginRequest");

// Check if error gets displayed
cy.contains("Password or Email wrong!").should("be.visible");
cy.get('[data-test="email-field"]')
.should("be.visible")
.and("include.text", "Password or Email wrong!");

// Check with different 422 error
cy.intercept("POST", "api/v1/login/local", {
statusCode: 422,
body: {
errors: {
password: ["The Password field is required."],
},
},
}).as("loginRequest");

cy.get('[data-test="login-tab-local"]').within(() => {
cy.get('[data-test="login-button"]').click();
});

cy.wait("@loginRequest");

// Check that previous error message is hidden
cy.get('[data-test="email-field"]')
.should("be.visible")
.and("not.include.text", "Password or Email wrong!");

// Check if error gets displayed
cy.get('[data-test="password-field"]')
.should("be.visible")
.and("include.text", "The Password field is required.");

// Error for to many login requests gets displayed
cy.intercept("POST", "api/v1/login/local", {
Expand All @@ -325,8 +386,9 @@ describe("Login", function () {
cy.contains("Too many logins. Please try again later!").should(
"be.visible",
);
// Check that 422 error message is hidden
// Check that 422 error messages are hidden
cy.contains("Password or Email wrong!").should("not.exist");
cy.contains("The Password field is required.").should("not.exist");

// Other api errors
cy.intercept("POST", "api/v1/login/local", {
Expand Down Expand Up @@ -362,6 +424,135 @@ describe("Login", function () {
cy.url().should("not.include", "/login");
});

it("ldap login errors", function () {
// Intercept config request to only show ldap login tab
cy.fixture("config.json").then((config) => {
config.data.auth.ldap = true;

cy.intercept("GET", "api/v1/config", {
statusCode: 200,
body: config,
});
});
// Intercept csrf-cookie request to set defined cookie that can be checked later
cy.intercept("GET", "/sanctum/csrf-cookie", {
statusCode: 200,
headers: {
"Set-Cookie": "XSRF-TOKEN=test-csrf; Path=/",
},
}).as("cookieRequest");

// Unprocessable entity error
cy.intercept("POST", "api/v1/login/ldap", {
statusCode: 422,
body: {
errors: {
username: ["These credentials do not match our records."],
},
},
}).as("loginRequest");

cy.visit("/login");

cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get("#ldap-username").type("user");
cy.get("#ldap-password").type("password");
cy.get('[data-test="login-button"]').click();
});

cy.wait("@loginRequest");

// Check if error gets displayed
cy.get('[data-test="username-field"]')
.should("be.visible")
.and("include.text", "These credentials do not match our records.");

// Check with different error
cy.intercept("POST", "api/v1/login/ldap", {
statusCode: 422,
body: {
errors: {
password: ["The Password field is required."],
},
},
}).as("loginRequest");

cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get('[data-test="login-button"]').click();
});

cy.wait("@loginRequest");

// Check that previous error message is hidden
cy.get('[data-test="username-field"]')
.should("be.visible")
.and("not.include.text", "These credentials do not match our records.");

// Check if error gets displayed
cy.get('[data-test="password-field"]')
.should("be.visible")
.and("include.text", "The Password field is required.");

// Error for to many login requests gets displayed
cy.intercept("POST", "api/v1/login/ldap", {
statusCode: 429,
body: {
errors: {
username: ["Too many logins. Please try again later!"],
},
},
}).as("loginRequest");

cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get('[data-test="login-button"]').click();
});

cy.wait("@loginRequest");

// Check if error gets displayed
cy.contains("Too many logins. Please try again later!").should(
"be.visible",
);
// Check that 422 error messages are hidden
cy.contains("These credentials do not match our records.").should(
"not.exist",
);
cy.contains("The Password field is required.").should("not.exist");

// Other api errors
cy.intercept("POST", "api/v1/login/ldap", {
statusCode: 500,
}).as("loginRequest");

cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get('[data-test="login-button"]').click();
});

cy.wait("@loginRequest");

// Check that other error messages are hidden
cy.contains("Too many logins. Please try again later!").should("not.exist");

// Check that error message is shown
cy.checkToastMessage([
"app.flash.server_error.empty_message",
'app.flash.server_error.error_code_{"statusCode":500}',
]);

// Intercept login request with different error
cy.intercept("POST", "api/v1/login/ldap", {
statusCode: 420,
}).as("loginRequest");

cy.get('[data-test="login-tab-ldap"]').within(() => {
cy.get('[data-test="login-button"]').click();
});
cy.wait("@loginRequest");

cy.checkToastMessage("app.flash.guests_only");
cy.url().should("not.include", "/login");
});

it("visit login page with already logged in user", function () {
cy.intercept("GET", "api/v1/currentUser", { fixture: "currentUser.json" });
cy.interceptRoomIndexRequests();
Expand Down Expand Up @@ -429,7 +620,7 @@ describe("Login", function () {
});

// Visit page that can only be visited by logged in users
cy.visit("admin");
cy.visit("/admin");

// Check redirect to the login page
cy.url().should("include", "/login?redirect=/admin");
Expand Down

0 comments on commit 93503f9

Please sign in to comment.