-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Cypress Auth Test to POM and Improve Test Stability #9024
base: develop
Are you sure you want to change the base?
Changes from all commits
80a8168
18443a0
8730d9a
b01c10b
052a052
7315053
1960971
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import LoginPage from "pageobject/Login/LoginPage"; | ||
const loginPage = new LoginPage(); | ||
|
||
describe("Authorisation/Authentication", () => { | ||
beforeEach(() => { | ||
cy.awaitUrl("/", true); | ||
}); | ||
|
||
it("Try login as admin with correct password", () => { | ||
loginPage.loginManuallyAsDistrictAdmin(); | ||
loginPage.interceptFacilityReq(); | ||
loginPage.verifyFacilityReq(); | ||
loginPage.ensureLoggedIn(); | ||
loginPage.clickSignOutBtn(); | ||
loginPage.verifyLoginPageUrl(); | ||
}); | ||
|
||
it("Try login as admin with incorrect password", () => { | ||
loginPage.interceptLoginReq(); | ||
loginPage.loginManuallyAsDistrictAdmin(false); | ||
loginPage.verifyLoginReq(); | ||
cy.verifyNotification("No active account found with the given credentials"); | ||
}); | ||
}); | ||
|
||
describe("Forgot Password", () => { | ||
const userName = "dummy_user_1"; | ||
beforeEach(() => { | ||
cy.awaitUrl("/", true); | ||
cy.verifyAndClickElement("#forgot-pass-btn", "Forgot password?"); | ||
}); | ||
|
||
it("Send Password Reset Link", () => { | ||
loginPage.fillUserNameInForgotPasswordForm(userName); | ||
loginPage.interceptResetLinkReq(); | ||
loginPage.clickSendResetLinkBtn(); | ||
loginPage.verifyResetLinkReq(); | ||
cy.verifyNotification("Password Reset Email Sent"); | ||
}); | ||
|
||
it("Go to Login page", () => { | ||
loginPage.clickBackButton(); | ||
loginPage.verifyLoginPageUrl(); | ||
}); | ||
}); | ||
Comment on lines
+26
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification 🛠️ Refactor suggestion Based on the analysis of the codebase, I can now provide the final response: Consider using fixtures for test data and add validation coverage The review comment is valid as the codebase shows:
However, the suggested implementation needs adjustment:
+// cypress/fixtures/auth.json
+{
+ "forgotPassword": {
+ "validUser": "dummy_user_1",
+ "invalidUser": "nonexistent@user",
+ "invalidFormat": "user@"
+ }
+}
describe("Forgot Password", () => {
+ beforeEach(() => {
+ cy.fixture('auth.json').as('authData');
+ cy.awaitUrl("/", true);
+ cy.verifyAndClickElement("#forgot-pass-btn", "Forgot password?");
+ });
- const userName = "dummy_user_1";
-
it("Send Password Reset Link", () => {
- loginPage.fillUserNameInForgotPasswordForm(userName);
+ cy.get('@authData').then((authData) => {
+ loginPage.fillUserNameInForgotPasswordForm(authData.forgotPassword.validUser);
+ cy.get('input[name="username"]')
+ .should('have.value', authData.forgotPassword.validUser)
+ .and('be.valid');
+ cy.get('button[type="submit"]').should('be.enabled');
+ loginPage.interceptResetLinkReq();
+ loginPage.clickSendResetLinkBtn();
+ loginPage.verifyResetLinkReq();
+ cy.verifyNotification("Password Reset Email Sent");
+ });
});
+ it("Show error for invalid username format", () => {
+ cy.get('@authData').then((authData) => {
+ loginPage.fillUserNameInForgotPasswordForm(authData.forgotPassword.invalidFormat);
+ cy.get('input[name="username"]').should('have.class', 'error');
+ cy.get('button[type="submit"]').should('be.disabled');
+ });
+ }); 🔗 Analysis chainMove test data to fixtures and add input validation. Consider these improvements:
+import testData from '../fixtures/testData.json';
+
describe("Forgot Password", () => {
- const userName = "dummy_user_1";
beforeEach(() => {
cy.awaitUrl("/", true);
cy.verifyAndClickElement("#forgot-pass-btn", "Forgot password?");
});
it("Send Password Reset Link", () => {
- loginPage.fillUserNameInForgotPasswordForm(userName);
+ loginPage.fillUserNameInForgotPasswordForm(testData.users.testUser.username);
+ // Validate input field
+ cy.get('input[name="username"]').should('have.value', testData.users.testUser.username);
+ // Verify button is enabled
+ cy.get('button[type="submit"]').should('be.enabled');
loginPage.interceptResetLinkReq();
loginPage.clickSendResetLinkBtn();
loginPage.verifyResetLinkReq();
cy.verifyNotification("Password Reset Email Sent");
}); Also, consider adding negative test cases:
Let me check the implementation of these methods and validation patterns in other test files to make a more informed decision. Let me check for existing test data patterns and negative test cases to make a complete assessment. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check if similar validation exists in other test files
rg -l "fillUserNameInForgotPasswordForm|verifyNotification" --type ts
Length of output: 1074 Script: #!/bin/bash
# Check implementation of fillUserNameInForgotPasswordForm and related validations
rg "fillUserNameInForgotPasswordForm" -A 5 --type ts
# Check if test data is used from fixtures in other tests
rg "fixtures.*\.json" --type ts
# Check for input validation patterns in other tests
ast-grep --pattern 'cy.get($SELECTOR).should("have.value", $_)'
Length of output: 1589 Script: #!/bin/bash
# Check for existing test data patterns
rg "const \w+ = ['\"].*['\"]" cypress/e2e/ --type ts
# Check for negative test cases and validation patterns
rg "it\(.*invalid.*|it\(.*error.*" -A 5 cypress/e2e/ --type ts
# Check for existing fixture usage patterns
fd fixtures.ts$ -x cat {}
Length of output: 21386 |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,16 +13,20 @@ class LoginPage { | |||||||||||||
cy.loginByApi("staffdev", "Coronasafe@123"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
loginManuallyAsDistrictAdmin(): void { | ||||||||||||||
loginManuallyAsDistrictAdmin(isCorrectCredentials: boolean = true): void { | ||||||||||||||
cy.get("input[id='username']").type("devdistrictadmin"); | ||||||||||||||
cy.get("input[id='password']").type("Coronasafe@123"); | ||||||||||||||
cy.get("button").contains("Login").click(); | ||||||||||||||
if (isCorrectCredentials) { | ||||||||||||||
cy.get("input[id='password']").type("Coronasafe@123"); | ||||||||||||||
} else { | ||||||||||||||
cy.get("input[id='password']").type("Corona"); | ||||||||||||||
} | ||||||||||||||
cy.submitButton("Login"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
loginManuallyAsNurse(): void { | ||||||||||||||
cy.get("input[id='username']").click().type("dummynurse1"); | ||||||||||||||
cy.get("input[id='password']").click().type("Coronasafe@123"); | ||||||||||||||
cy.get("button").contains("Login").click(); | ||||||||||||||
cy.submitButton("Login"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
login(username: string, password: string): void { | ||||||||||||||
|
@@ -34,6 +38,50 @@ class LoginPage { | |||||||||||||
cy.get("#sign-out-button").scrollIntoView(); | ||||||||||||||
cy.get("#sign-out-button").contains("Sign Out").should("exist"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
clickSignOutBtn(): void { | ||||||||||||||
cy.verifyAndClickElement("#sign-out-button", "Sign Out"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
fillUserNameInForgotPasswordForm(userName: string): void { | ||||||||||||||
cy.get("#forgot_username").type(userName); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
clickSendResetLinkBtn(): void { | ||||||||||||||
cy.verifyAndClickElement("#send-reset-link-btn", "Send Reset Link"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
verifyLoginPageUrl(): void { | ||||||||||||||
cy.url().should("include", "/"); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+54
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance URL verification specificity. The current URL verification is too broad. Consider checking for a more specific path. - verifyLoginPageUrl(): void {
- cy.url().should("include", "/");
+ verifyLoginPageUrl(): void {
+ cy.url().should("include", "/login");
} 📝 Committable suggestion
Suggested change
|
||||||||||||||
|
||||||||||||||
clickBackButton(): void { | ||||||||||||||
cy.verifyAndClickElement("#back-to-login-btn", "Back to login"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
interceptFacilityReq(): void { | ||||||||||||||
cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
verifyFacilityReq(): void { | ||||||||||||||
cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
interceptLoginReq(): void { | ||||||||||||||
cy.intercept("POST", "**/api/v1/auth/login").as("userLogin"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
verifyLoginReq(): void { | ||||||||||||||
cy.wait("@userLogin").its("response.statusCode").should("eq", 401); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
interceptResetLinkReq(): void { | ||||||||||||||
cy.intercept("POST", "**/api/v1/password_reset").as("resetLink"); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
verifyResetLinkReq(): void { | ||||||||||||||
cy.wait("@resetLink").its("response.statusCode").should("eq", 200); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+62
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance API interception methods for better flexibility and type safety. The API interception methods could be improved in several ways:
Example implementation: // Constants
const API_ENDPOINTS = {
FACILITY: '**/api/v1/facility/**',
LOGIN: '**/api/v1/auth/login',
PASSWORD_RESET: '**/api/v1/password_reset'
} as const;
const HTTP_STATUS = {
OK: 200,
UNAUTHORIZED: 401
} as const;
// Enhanced methods
interface ApiResponse {
statusCode: number;
body?: unknown;
}
interceptLoginReq(response?: Partial<ApiResponse>): this {
cy.intercept('POST', API_ENDPOINTS.LOGIN, response).as('userLogin');
return this;
}
verifyLoginReq(expectedStatus: number = HTTP_STATUS.UNAUTHORIZED): this {
cy.wait('@userLogin')
.its('response.statusCode')
.should('eq', expectedStatus);
return this;
} |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
export default LoginPage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance test stability with retry and error handling.
While the test structure is good, consider these improvements for better stability: