diff --git a/ui/tests/base-page.ts b/ui/tests/base-page.ts index e45da93970..a31f84c7ad 100644 --- a/ui/tests/base-page.ts +++ b/ui/tests/base-page.ts @@ -17,7 +17,7 @@ export abstract class BasePage { // Common locators that most pages share this.title = page.locator("h1, h2, [role='heading']").first(); - this.loadingIndicator = page.getByText("Loading"); + this.loadingIndicator = page.getByRole("status", { name: "Loading" }); this.themeToggle = page.getByLabel("Toggle theme"); } diff --git a/ui/tests/page-objects/home-page.ts b/ui/tests/page-objects/home-page.ts deleted file mode 100644 index 077591eb4e..0000000000 --- a/ui/tests/page-objects/home-page.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Page, Locator, expect } from "@playwright/test"; - -export class HomePage { - readonly page: Page; - - // Main content elements - readonly mainContent: Locator; - readonly breadcrumbs: Locator; - readonly overviewHeading: Locator; - - // Navigation elements - readonly navigationMenu: Locator; - readonly userMenu: Locator; - readonly signOutButton: Locator; - - // Dashboard elements - readonly dashboardCards: Locator; - readonly overviewSection: Locator; - - // UI elements - readonly themeToggle: Locator; - readonly logo: Locator; - - constructor(page: Page) { - this.page = page; - - // Main content elements - this.mainContent = page.locator("main"); - this.breadcrumbs = page.getByLabel("Breadcrumbs"); - this.overviewHeading = page.getByRole("heading", { name: "Overview", exact: true }); - - // Navigation elements - this.navigationMenu = page.locator("nav"); - this.userMenu = page.getByRole("button", { name: /user menu/i }); - this.signOutButton = page.getByRole("button", { name: "Sign out" }); - - // Dashboard elements - this.dashboardCards = page.locator('[data-testid="dashboard-card"]'); - this.overviewSection = page.locator('[data-testid="overview-section"]'); - - // UI elements - this.themeToggle = page.getByLabel("Toggle theme"); - this.logo = page.locator('svg[width="300"]'); - } - - // Navigation methods - async goto(): Promise { - await this.page.goto("/"); - await this.waitForPageLoad(); - } - - async waitForPageLoad(): Promise { - await this.page.waitForLoadState("networkidle"); - } - - // Verification methods - async verifyPageLoaded(): Promise { - await expect(this.page).toHaveURL("/"); - await expect(this.mainContent).toBeVisible(); - await expect(this.overviewHeading).toBeVisible(); - await this.waitForPageLoad(); - } - - async verifyBreadcrumbs(): Promise { - await expect(this.breadcrumbs).toBeVisible(); - await expect(this.overviewHeading).toBeVisible(); - } - - async verifyMainContent(): Promise { - await expect(this.mainContent).toBeVisible(); - } - - // Navigation methods - async navigateToOverview(): Promise { - await this.overviewHeading.click(); - } - - async openUserMenu(): Promise { - await this.userMenu.click(); - } - - async signOut(): Promise { - await this.openUserMenu(); - await this.signOutButton.click(); - } - - // Dashboard methods - async verifyDashboardCards(): Promise { - await expect(this.dashboardCards.first()).toBeVisible(); - } - - async verifyOverviewSection(): Promise { - await expect(this.overviewSection).toBeVisible(); - } - - // Utility methods - async refresh(): Promise { - await this.page.reload(); - await this.waitForPageLoad(); - } - - async goBack(): Promise { - await this.page.goBack(); - await this.waitForPageLoad(); - } - - // Accessibility methods - async verifyKeyboardNavigation(): Promise { - // Test tab navigation through main elements - await this.page.keyboard.press("Tab"); - await expect(this.themeToggle).toBeFocused(); - } - - // Wait methods - async waitForRedirect(expectedUrl: string): Promise { - await this.page.waitForURL(expectedUrl); - } - - async waitForContentLoad(): Promise { - await this.page.waitForFunction(() => { - const main = document.querySelector("main"); - return main && main.offsetHeight > 0; - }); - } -} diff --git a/ui/tests/page-objects/sign-in-page.ts b/ui/tests/page-objects/sign-in-page.ts deleted file mode 100644 index 2c426606c7..0000000000 --- a/ui/tests/page-objects/sign-in-page.ts +++ /dev/null @@ -1,316 +0,0 @@ -import { Page, Locator, expect } from "@playwright/test"; -import { HomePage } from "./home-page"; - -export interface SignInCredentials { - email: string; - password: string; -} - -export interface SocialAuthConfig { - googleEnabled: boolean; - githubEnabled: boolean; -} - -export class SignInPage { - readonly page: Page; - readonly homePage: HomePage; - - // Form elements - readonly emailInput: Locator; - readonly passwordInput: Locator; - readonly loginButton: Locator; - readonly form: Locator; - - // Social authentication buttons - readonly googleButton: Locator; - readonly githubButton: Locator; - readonly samlButton: Locator; - - // Navigation elements - readonly signUpLink: Locator; - readonly backButton: Locator; - - // UI elements - readonly title: Locator; - readonly logo: Locator; - readonly themeToggle: Locator; - - // Error messages - readonly errorMessages: Locator; - readonly loadingIndicator: Locator; - - // SAML specific elements - readonly samlModeTitle: Locator; - readonly samlEmailInput: Locator; - - constructor(page: Page) { - this.page = page; - this.homePage = new HomePage(page); - - // Form elements - this.emailInput = page.getByLabel("Email"); - this.passwordInput = page.getByLabel("Password"); - this.loginButton = page.getByRole("button", { name: "Log in" }); - this.form = page.locator("form"); - - // Social authentication buttons - this.googleButton = page.getByText("Continue with Google"); - this.githubButton = page.getByText("Continue with Github"); - this.samlButton = page.getByText("Continue with SAML SSO"); - - // Navigation elements - this.signUpLink = page.getByRole("link", { name: "Sign up" }); - this.backButton = page.getByText("Back"); - - // UI elements - this.title = page.getByText("Sign in", { exact: true }); - this.logo = page.locator('svg[width="300"]'); - this.themeToggle = page.getByLabel("Toggle theme"); - - // Error messages - this.errorMessages = page.locator('[role="alert"], .error-message, [data-testid="error"]'); - this.loadingIndicator = page.getByText("Loading"); - - // SAML specific elements - this.samlModeTitle = page.getByText("Sign in with SAML SSO"); - this.samlEmailInput = page.getByLabel("Email"); - } - - // Navigation methods - async goto(): Promise { - await this.page.goto("/sign-in"); - await this.waitForPageLoad(); - } - - async waitForPageLoad(): Promise { - await this.page.waitForLoadState("networkidle"); - } - - // Form interaction methods - async fillEmail(email: string): Promise { - await this.emailInput.fill(email); - } - - async fillPassword(password: string): Promise { - await this.passwordInput.fill(password); - } - - async fillCredentials(credentials: SignInCredentials): Promise { - await this.fillEmail(credentials.email); - await this.fillPassword(credentials.password); - } - - async submitForm(): Promise { - await this.loginButton.click(); - } - - async login(credentials: SignInCredentials): Promise { - await this.fillCredentials(credentials); - await this.submitForm(); - } - - // Social authentication methods - async clickGoogleAuth(): Promise { - await this.googleButton.click(); - } - - async clickGithubAuth(): Promise { - await this.githubButton.click(); - } - - async clickSamlAuth(): Promise { - await this.samlButton.click(); - } - - // SAML SSO methods - async toggleSamlMode(): Promise { - await this.clickSamlAuth(); - } - - async goBackFromSaml(): Promise { - await this.backButton.click(); - } - - async fillSamlEmail(email: string): Promise { - await this.samlEmailInput.fill(email); - } - - async submitSamlForm(): Promise { - await this.submitForm(); - } - - // Navigation methods - async goToSignUp(): Promise { - await this.signUpLink.click(); - } - - // Validation and assertion methods - async verifyPageLoaded(): Promise { - await expect(this.page).toHaveTitle(/Prowler/); - await expect(this.logo).toBeVisible(); - await expect(this.title).toBeVisible(); - await this.waitForPageLoad(); - } - - async verifyFormElements(): Promise { - await expect(this.emailInput).toBeVisible(); - await expect(this.passwordInput).toBeVisible(); - await expect(this.loginButton).toBeVisible(); - } - - async verifySocialButtons(config: SocialAuthConfig): Promise { - if (config.googleEnabled) { - await expect(this.googleButton).toBeVisible(); - } - if (config.githubEnabled) { - await expect(this.githubButton).toBeVisible(); - } - await expect(this.samlButton).toBeVisible(); - } - - async verifyNavigationLinks(): Promise { - await expect(this.page.getByText("Need to create an account?")).toBeVisible(); - await expect(this.signUpLink).toBeVisible(); - } - - async verifySuccessfulLogin(): Promise { - await this.homePage.verifyPageLoaded(); - } - - async verifyLoginError(errorMessage: string = "Invalid email or password"): Promise { - await expect(this.page.getByText(errorMessage).first()).toBeVisible(); - await expect(this.page).toHaveURL("/sign-in"); - } - - async verifySamlModeActive(): Promise { - await expect(this.samlModeTitle).toBeVisible(); - await expect(this.passwordInput).not.toBeVisible(); - await expect(this.backButton).toBeVisible(); - } - - async verifyNormalModeActive(): Promise { - await expect(this.title).toBeVisible(); - await expect(this.passwordInput).toBeVisible(); - } - - async verifyLoadingState(): Promise { - await expect(this.loginButton).toHaveAttribute("aria-disabled", "true"); - await expect(this.loadingIndicator).toBeVisible(); - } - - async verifyFormValidation(): Promise { - // Check for common validation messages - const emailError = this.page.getByText("Please enter a valid email address."); - const passwordError = this.page.getByText("Password is required."); - - // At least one validation error should be visible - await expect(emailError.or(passwordError)).toBeVisible(); - } - - // Accessibility methods - async verifyKeyboardNavigation(): Promise { - // Test tab navigation through form elements - await this.page.keyboard.press("Tab"); // Theme toggle - await this.page.keyboard.press("Tab"); // Email field - await expect(this.emailInput).toBeFocused(); - - await this.page.keyboard.press("Tab"); // Password field - await expect(this.passwordInput).toBeFocused(); - - await this.page.keyboard.press("Tab"); // Show password button - await this.page.keyboard.press("Tab"); // Login button - await expect(this.loginButton).toBeFocused(); - } - - async verifyAriaLabels(): Promise { - await expect(this.page.getByRole("textbox", { name: "Email" })).toBeVisible(); - await expect(this.page.getByRole("textbox", { name: "Password" })).toBeVisible(); - await expect(this.page.getByRole("button", { name: "Log in" })).toBeVisible(); - } - - // Utility methods - async clearForm(): Promise { - await this.emailInput.clear(); - await this.passwordInput.clear(); - } - - async isFormValid(): Promise { - const emailValue = await this.emailInput.inputValue(); - const passwordValue = await this.passwordInput.inputValue(); - return emailValue.length > 0 && passwordValue.length > 0; - } - - async getFormErrors(): Promise { - const errorElements = await this.errorMessages.all(); - const errors: string[] = []; - - for (const element of errorElements) { - const text = await element.textContent(); - if (text) { - errors.push(text.trim()); - } - } - - return errors; - } - - // Browser interaction methods - async refresh(): Promise { - await this.page.reload(); - await this.waitForPageLoad(); - } - - async goBack(): Promise { - await this.page.goBack(); - await this.waitForPageLoad(); - } - - // Session management methods - async logout(): Promise { - await this.homePage.signOut(); - } - - async verifyLogoutSuccess(): Promise { - await expect(this.page).toHaveURL("/sign-in"); - await expect(this.title).toBeVisible(); - } - - // Advanced interaction methods - async fillFormWithValidation(credentials: SignInCredentials): Promise { - // Fill email first and check for validation - await this.fillEmail(credentials.email); - await this.page.keyboard.press("Tab"); // Trigger validation - - // Fill password - await this.fillPassword(credentials.password); - } - - async submitFormWithEnterKey(): Promise { - await this.passwordInput.press("Enter"); - } - - async submitFormWithButtonClick(): Promise { - await this.submitForm(); - } - - // Error handling methods - async handleSamlError(): Promise { - const samlError = this.page.getByText("SAML Authentication Error"); - if (await samlError.isVisible()) { - // Handle SAML error if present - console.log("SAML authentication error detected"); - } - } - - // Wait methods - async waitForFormSubmission(): Promise { - await this.page.waitForFunction(() => { - const button = document.querySelector('button[aria-disabled="true"]'); - return button === null; - }); - } - - async waitForRedirect(expectedUrl: string): Promise { - await this.page.waitForURL(expectedUrl); - } -} diff --git a/ui/tests/sign-in/sign-in-page.ts b/ui/tests/sign-in/sign-in-page.ts index 8fcf1a3a04..5c426779b6 100644 --- a/ui/tests/sign-in/sign-in-page.ts +++ b/ui/tests/sign-in/sign-in-page.ts @@ -51,13 +51,13 @@ export class SignInPage extends BasePage { this.form = page.locator("form"); // Social authentication buttons - this.googleButton = page.getByText("Continue with Google"); - this.githubButton = page.getByText("Continue with Github"); - this.samlButton = page.getByText("Continue with SAML SSO"); + this.googleButton = page.getByRole("button", { name: "Continue with Google" }); + this.githubButton = page.getByRole("button", { name: "Continue with Github" }); + this.samlButton = page.getByRole("button", { name: "Continue with SAML SSO" }); // Navigation elements this.signUpLink = page.getByRole("link", { name: "Sign up" }); - this.backButton = page.getByText("Back"); + this.backButton = page.getByRole("button", { name: "Back" }); // UI elements this.logo = page.locator('svg[width="300"]'); @@ -66,7 +66,7 @@ export class SignInPage extends BasePage { this.errorMessages = page.locator('[role="alert"], .error-message, [data-testid="error"]'); // SAML specific elements - this.samlModeTitle = page.getByText("Sign in with SAML SSO"); + this.samlModeTitle = page.getByRole("heading", { name: "Sign in with SAML SSO" }); this.samlEmailInput = page.getByLabel("Email"); } @@ -137,7 +137,7 @@ export class SignInPage extends BasePage { async verifyPageLoaded(): Promise { await expect(this.page).toHaveTitle(/Prowler/); await expect(this.logo).toBeVisible(); - await expect(this.page.getByText("Sign in", { exact: true })).toBeVisible(); + await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible(); } async verifyFormElements(): Promise { @@ -157,7 +157,7 @@ export class SignInPage extends BasePage { } async verifyNavigationLinks(): Promise { - await expect(this.page.getByText("Need to create an account?")).toBeVisible(); + await expect(this.page.getByRole('link', { name: /Need to create an account\?/i })).toBeVisible(); await expect(this.signUpLink).toBeVisible(); } @@ -166,7 +166,7 @@ export class SignInPage extends BasePage { } async verifyLoginError(errorMessage: string = "Invalid email or password"): Promise { - await expect(this.page.getByText(errorMessage).first()).toBeVisible(); + await expect(this.page.getByRole("alert", { name: errorMessage })).toBeVisible(); await expect(this.page).toHaveURL("/sign-in"); } @@ -177,7 +177,7 @@ export class SignInPage extends BasePage { } async verifyNormalModeActive(): Promise { - await expect(this.page.getByText("Sign in", { exact: true })).toBeVisible(); + await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible(); await expect(this.passwordInput).toBeVisible(); } @@ -188,8 +188,8 @@ export class SignInPage extends BasePage { async verifyFormValidation(): Promise { // Check for common validation messages - const emailError = this.page.getByText("Please enter a valid email address."); - const passwordError = this.page.getByText("Password is required."); + const emailError = this.page.getByRole("alert", { name: "Please enter a valid email address." }); + const passwordError = this.page.getByRole("alert", { name: "Password is required." }); // At least one validation error should be visible await expect(emailError.or(passwordError)).toBeVisible(); @@ -237,7 +237,7 @@ export class SignInPage extends BasePage { async verifyLogoutSuccess(): Promise { await expect(this.page).toHaveURL("/sign-in"); - await expect(this.page.getByText("Sign in", { exact: true })).toBeVisible(); + await expect(this.page.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible(); } // Advanced interaction methods @@ -260,7 +260,7 @@ export class SignInPage extends BasePage { // Error handling methods async handleSamlError(): Promise { - const samlError = this.page.getByText("SAML Authentication Error"); + const samlError = this.page.getByRole("alert", { name: "SAML Authentication Error" }); if (await samlError.isVisible()) { // Handle SAML error if present console.log("SAML authentication error detected"); diff --git a/ui/tests/sign-up/sign-up-page.ts b/ui/tests/sign-up/sign-up-page.ts index 077f250e86..52027279d3 100644 --- a/ui/tests/sign-up/sign-up-page.ts +++ b/ui/tests/sign-up/sign-up-page.ts @@ -39,7 +39,7 @@ export class SignUpPage extends BasePage { this.submitButton = page.getByRole("button", { name: "Sign up" }); this.loginLink = page.getByRole("link", { name: "Log in" }); - this.termsCheckbox = page.getByText("I agree with the"); + this.termsCheckbox = page.getByRole("checkbox", { name: /I agree with the/i }); } async goto(): Promise { @@ -47,7 +47,7 @@ export class SignUpPage extends BasePage { } async verifyPageLoaded(): Promise { - await expect(this.page.getByText("Sign up", { exact: true })).toBeVisible(); + await expect(this.page.getByRole("heading", { name: "Sign up" })).toBeVisible(); await expect(this.emailInput).toBeVisible(); await expect(this.submitButton).toBeVisible(); await this.waitForPageLoad();