mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
test(ui): enhance Playwright test setups for user authentication (#8881)
Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com>
This commit is contained in:
3
ui/.gitignore
vendored
3
ui/.gitignore
vendored
@@ -33,4 +33,5 @@ yarn-error.log*
|
|||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
playwright/.auth
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
"format:check": "./node_modules/.bin/prettier --check ./app",
|
"format:check": "./node_modules/.bin/prettier --check ./app",
|
||||||
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
|
"format:write": "./node_modules/.bin/prettier --config .prettierrc.json --write ./app",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test --project=chromium",
|
||||||
"test:e2e:ui": "playwright test --ui",
|
"test:e2e:ui": "playwright test --project=chromium --ui",
|
||||||
"test:e2e:debug": "playwright test --debug",
|
"test:e2e:debug": "playwright test --project=chromium --debug",
|
||||||
"test:e2e:headed": "playwright test --headed",
|
"test:e2e:headed": "playwright test --project=chromium --headed",
|
||||||
"test:e2e:report": "playwright show-report",
|
"test:e2e:report": "playwright show-report",
|
||||||
"test:e2e:install": "playwright install"
|
"test:e2e:install": "playwright install"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,72 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
|
// ===========================================
|
||||||
|
// Authentication Setup Projects
|
||||||
|
// ===========================================
|
||||||
|
// These projects handle user authentication for different permission levels
|
||||||
|
// Each setup creates authenticated state files that can be reused by test suites
|
||||||
|
|
||||||
|
// Admin user authentication setup
|
||||||
|
// Creates authenticated state for admin users with full system permissions
|
||||||
|
{
|
||||||
|
name: "admin.auth.setup",
|
||||||
|
testMatch: "admin.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Scans management user authentication setup
|
||||||
|
// Creates authenticated state for users with scan management permissions
|
||||||
|
{
|
||||||
|
name: "manage-scans.auth.setup",
|
||||||
|
testMatch: "manage-scans.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Integrations management user authentication setup
|
||||||
|
// Creates authenticated state for users with integration management permissions
|
||||||
|
{
|
||||||
|
name: "manage-integrations.auth.setup",
|
||||||
|
testMatch: "manage-integrations.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Account management user authentication setup
|
||||||
|
// Creates authenticated state for users with account management permissions
|
||||||
|
{
|
||||||
|
name: "manage-account.auth.setup",
|
||||||
|
testMatch: "manage-account.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cloud providers management user authentication setup
|
||||||
|
// Creates authenticated state for users with cloud provider management permissions
|
||||||
|
{
|
||||||
|
name: "manage-cloud-providers.auth.setup",
|
||||||
|
testMatch: "manage-cloud-providers.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Unlimited visibility user authentication setup
|
||||||
|
// Creates authenticated state for users with unlimited visibility permissions
|
||||||
|
{
|
||||||
|
name: "unlimited-visibility.auth.setup",
|
||||||
|
testMatch: "unlimited-visibility.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Invite and manage users authentication setup
|
||||||
|
// Creates authenticated state for users with user invitation and management permissions
|
||||||
|
{
|
||||||
|
name: "invite-and-manage-users.auth.setup",
|
||||||
|
testMatch: "invite-and-manage-users.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// All authentication setups combined
|
||||||
|
// Runs all authentication setup files to create all user states
|
||||||
|
{
|
||||||
|
name: "all.auth.setup",
|
||||||
|
testMatch: "**/*.auth.setup.ts",
|
||||||
|
},
|
||||||
|
|
||||||
|
// ===========================================
|
||||||
|
// Test Suite Projects
|
||||||
|
// ===========================================
|
||||||
|
// These projects run the actual test suites
|
||||||
{
|
{
|
||||||
name: "chromium",
|
name: "chromium",
|
||||||
use: { ...devices["Desktop Chrome"] },
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Page, expect } from "@playwright/test";
|
import { Page, expect } from "@playwright/test";
|
||||||
|
import { SignInPage, SignInCredentials } from "./page-objects/sign-in-page";
|
||||||
|
|
||||||
export const ERROR_MESSAGES = {
|
export const ERROR_MESSAGES = {
|
||||||
INVALID_CREDENTIALS: "Invalid email or password",
|
INVALID_CREDENTIALS: "Invalid email or password",
|
||||||
@@ -138,6 +139,29 @@ export async function verifyDashboardRoute(page: Page) {
|
|||||||
await expect(page).toHaveURL("/");
|
await expect(page).toHaveURL("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function authenticateAndSaveState(
|
||||||
|
page: Page,
|
||||||
|
email: string,
|
||||||
|
password: string,
|
||||||
|
storagePath: string,
|
||||||
|
) {
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new Error('Email and password are required for authentication and save state');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SignInPage instance
|
||||||
|
const signInPage = new SignInPage(page);
|
||||||
|
const credentials: SignInCredentials = { email, password };
|
||||||
|
|
||||||
|
// Perform authentication steps using Page Object Model
|
||||||
|
await signInPage.goto();
|
||||||
|
await signInPage.login(credentials);
|
||||||
|
await signInPage.verifySuccessfulLogin();
|
||||||
|
|
||||||
|
// Save authentication state
|
||||||
|
await page.context().storageState({ path: storagePath });
|
||||||
|
}
|
||||||
|
|
||||||
export async function getSession(page: Page) {
|
export async function getSession(page: Page) {
|
||||||
const response = await page.request.get("/api/auth/session");
|
const response = await page.request.get("/api/auth/session");
|
||||||
return response.json();
|
return response.json();
|
||||||
|
|||||||
125
ui/tests/page-objects/home-page.ts
Normal file
125
ui/tests/page-objects/home-page.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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<void> {
|
||||||
|
await this.page.goto("/");
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForPageLoad(): Promise<void> {
|
||||||
|
await this.page.waitForLoadState("networkidle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification methods
|
||||||
|
async verifyPageLoaded(): Promise<void> {
|
||||||
|
await expect(this.page).toHaveURL("/");
|
||||||
|
await expect(this.mainContent).toBeVisible();
|
||||||
|
await expect(this.overviewHeading).toBeVisible();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyBreadcrumbs(): Promise<void> {
|
||||||
|
await expect(this.breadcrumbs).toBeVisible();
|
||||||
|
await expect(this.overviewHeading).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyMainContent(): Promise<void> {
|
||||||
|
await expect(this.mainContent).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation methods
|
||||||
|
async navigateToOverview(): Promise<void> {
|
||||||
|
await this.overviewHeading.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async openUserMenu(): Promise<void> {
|
||||||
|
await this.userMenu.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async signOut(): Promise<void> {
|
||||||
|
await this.openUserMenu();
|
||||||
|
await this.signOutButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dashboard methods
|
||||||
|
async verifyDashboardCards(): Promise<void> {
|
||||||
|
await expect(this.dashboardCards.first()).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyOverviewSection(): Promise<void> {
|
||||||
|
await expect(this.overviewSection).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility methods
|
||||||
|
async refresh(): Promise<void> {
|
||||||
|
await this.page.reload();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async goBack(): Promise<void> {
|
||||||
|
await this.page.goBack();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessibility methods
|
||||||
|
async verifyKeyboardNavigation(): Promise<void> {
|
||||||
|
// Test tab navigation through main elements
|
||||||
|
await this.page.keyboard.press("Tab");
|
||||||
|
await expect(this.themeToggle).toBeFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait methods
|
||||||
|
async waitForRedirect(expectedUrl: string): Promise<void> {
|
||||||
|
await this.page.waitForURL(expectedUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForContentLoad(): Promise<void> {
|
||||||
|
await this.page.waitForFunction(() => {
|
||||||
|
const main = document.querySelector("main");
|
||||||
|
return main && main.offsetHeight > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
316
ui/tests/page-objects/sign-in-page.ts
Normal file
316
ui/tests/page-objects/sign-in-page.ts
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
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<void> {
|
||||||
|
await this.page.goto("/sign-in");
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForPageLoad(): Promise<void> {
|
||||||
|
await this.page.waitForLoadState("networkidle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form interaction methods
|
||||||
|
async fillEmail(email: string): Promise<void> {
|
||||||
|
await this.emailInput.fill(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillPassword(password: string): Promise<void> {
|
||||||
|
await this.passwordInput.fill(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillCredentials(credentials: SignInCredentials): Promise<void> {
|
||||||
|
await this.fillEmail(credentials.email);
|
||||||
|
await this.fillPassword(credentials.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitForm(): Promise<void> {
|
||||||
|
await this.loginButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(credentials: SignInCredentials): Promise<void> {
|
||||||
|
await this.fillCredentials(credentials);
|
||||||
|
await this.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Social authentication methods
|
||||||
|
async clickGoogleAuth(): Promise<void> {
|
||||||
|
await this.googleButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickGithubAuth(): Promise<void> {
|
||||||
|
await this.githubButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickSamlAuth(): Promise<void> {
|
||||||
|
await this.samlButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAML SSO methods
|
||||||
|
async toggleSamlMode(): Promise<void> {
|
||||||
|
await this.clickSamlAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
async goBackFromSaml(): Promise<void> {
|
||||||
|
await this.backButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillSamlEmail(email: string): Promise<void> {
|
||||||
|
await this.samlEmailInput.fill(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitSamlForm(): Promise<void> {
|
||||||
|
await this.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation methods
|
||||||
|
async goToSignUp(): Promise<void> {
|
||||||
|
await this.signUpLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation and assertion methods
|
||||||
|
async verifyPageLoaded(): Promise<void> {
|
||||||
|
await expect(this.page).toHaveTitle(/Prowler/);
|
||||||
|
await expect(this.logo).toBeVisible();
|
||||||
|
await expect(this.title).toBeVisible();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyFormElements(): Promise<void> {
|
||||||
|
await expect(this.emailInput).toBeVisible();
|
||||||
|
await expect(this.passwordInput).toBeVisible();
|
||||||
|
await expect(this.loginButton).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifySocialButtons(config: SocialAuthConfig): Promise<void> {
|
||||||
|
if (config.googleEnabled) {
|
||||||
|
await expect(this.googleButton).toBeVisible();
|
||||||
|
}
|
||||||
|
if (config.githubEnabled) {
|
||||||
|
await expect(this.githubButton).toBeVisible();
|
||||||
|
}
|
||||||
|
await expect(this.samlButton).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyNavigationLinks(): Promise<void> {
|
||||||
|
await expect(this.page.getByText("Need to create an account?")).toBeVisible();
|
||||||
|
await expect(this.signUpLink).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifySuccessfulLogin(): Promise<void> {
|
||||||
|
await this.homePage.verifyPageLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyLoginError(errorMessage: string = "Invalid email or password"): Promise<void> {
|
||||||
|
await expect(this.page.getByText(errorMessage).first()).toBeVisible();
|
||||||
|
await expect(this.page).toHaveURL("/sign-in");
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifySamlModeActive(): Promise<void> {
|
||||||
|
await expect(this.samlModeTitle).toBeVisible();
|
||||||
|
await expect(this.passwordInput).not.toBeVisible();
|
||||||
|
await expect(this.backButton).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyNormalModeActive(): Promise<void> {
|
||||||
|
await expect(this.title).toBeVisible();
|
||||||
|
await expect(this.passwordInput).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyLoadingState(): Promise<void> {
|
||||||
|
await expect(this.loginButton).toHaveAttribute("aria-disabled", "true");
|
||||||
|
await expect(this.loadingIndicator).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyFormValidation(): Promise<void> {
|
||||||
|
// 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<void> {
|
||||||
|
// 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<void> {
|
||||||
|
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<void> {
|
||||||
|
await this.emailInput.clear();
|
||||||
|
await this.passwordInput.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
async isFormValid(): Promise<boolean> {
|
||||||
|
const emailValue = await this.emailInput.inputValue();
|
||||||
|
const passwordValue = await this.passwordInput.inputValue();
|
||||||
|
return emailValue.length > 0 && passwordValue.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFormErrors(): Promise<string[]> {
|
||||||
|
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<void> {
|
||||||
|
await this.page.reload();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
async goBack(): Promise<void> {
|
||||||
|
await this.page.goBack();
|
||||||
|
await this.waitForPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session management methods
|
||||||
|
async logout(): Promise<void> {
|
||||||
|
await this.homePage.signOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyLogoutSuccess(): Promise<void> {
|
||||||
|
await expect(this.page).toHaveURL("/sign-in");
|
||||||
|
await expect(this.title).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced interaction methods
|
||||||
|
async fillFormWithValidation(credentials: SignInCredentials): Promise<void> {
|
||||||
|
// 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<void> {
|
||||||
|
await this.passwordInput.press("Enter");
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitFormWithButtonClick(): Promise<void> {
|
||||||
|
await this.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling methods
|
||||||
|
async handleSamlError(): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
await this.page.waitForFunction(() => {
|
||||||
|
const button = document.querySelector('button[aria-disabled="true"]');
|
||||||
|
return button === null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForRedirect(expectedUrl: string): Promise<void> {
|
||||||
|
await this.page.waitForURL(expectedUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
ui/tests/setups/admin.auth.setup.ts
Normal file
15
ui/tests/setups/admin.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authAdminSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const adminUserFile = 'playwright/.auth/admin_user.json';
|
||||||
|
|
||||||
|
authAdminSetup('authenticate as admin e2e user', async ({ page }) => {
|
||||||
|
const adminEmail = process.env.E2E_ADMIN_USER;
|
||||||
|
const adminPassword = process.env.E2E_ADMIN_PASSWORD;
|
||||||
|
|
||||||
|
if (!adminEmail || !adminPassword) {
|
||||||
|
throw new Error('E2E_ADMIN_USER and E2E_ADMIN_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, adminEmail, adminPassword, adminUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/invite-and-manage-users.auth.setup.ts
Normal file
15
ui/tests/setups/invite-and-manage-users.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authInviteAndManageUsersSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const inviteAndManageUsersUserFile = 'playwright/.auth/invite_and_manage_users_user.json';
|
||||||
|
|
||||||
|
authInviteAndManageUsersSetup('authenticate as invite and manage users e2e user', async ({ page }) => {
|
||||||
|
const inviteAndManageUsersEmail = process.env.E2E_INVITE_AND_MANAGE_USERS_USER;
|
||||||
|
const inviteAndManageUsersPassword = process.env.E2E_INVITE_AND_MANAGE_USERS_PASSWORD;
|
||||||
|
|
||||||
|
if (!inviteAndManageUsersEmail || !inviteAndManageUsersPassword) {
|
||||||
|
throw new Error('E2E_INVITE_AND_MANAGE_USERS_USER and E2E_INVITE_AND_MANAGE_USERS_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, inviteAndManageUsersEmail, inviteAndManageUsersPassword, inviteAndManageUsersUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/manage-account.auth.setup.ts
Normal file
15
ui/tests/setups/manage-account.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authManageAccountSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const manageAccountUserFile = 'playwright/.auth/manage_account_user.json';
|
||||||
|
|
||||||
|
authManageAccountSetup('authenticate as manage account e2e user', async ({ page }) => {
|
||||||
|
const accountEmail = process.env.E2E_MANAGE_ACCOUNT_USER;
|
||||||
|
const accountPassword = process.env.E2E_MANAGE_ACCOUNT_PASSWORD;
|
||||||
|
|
||||||
|
if (!accountEmail || !accountPassword) {
|
||||||
|
throw new Error('E2E_MANAGE_ACCOUNT_USER and E2E_MANAGE_ACCOUNT_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, accountEmail, accountPassword, manageAccountUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/manage-cloud-providers.auth.setup.ts
Normal file
15
ui/tests/setups/manage-cloud-providers.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authManageCloudProvidersSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const manageCloudProvidersUserFile = 'playwright/.auth/manage_cloud_providers_user.json';
|
||||||
|
|
||||||
|
authManageCloudProvidersSetup('authenticate as manage cloud providers e2e user', async ({ page }) => {
|
||||||
|
const cloudProvidersEmail = process.env.E2E_MANAGE_CLOUD_PROVIDERS_USER;
|
||||||
|
const cloudProvidersPassword = process.env.E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD;
|
||||||
|
|
||||||
|
if (!cloudProvidersEmail || !cloudProvidersPassword) {
|
||||||
|
throw new Error('E2E_MANAGE_CLOUD_PROVIDERS_USER and E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, cloudProvidersEmail, cloudProvidersPassword, manageCloudProvidersUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/manage-integrations.auth.setup.ts
Normal file
15
ui/tests/setups/manage-integrations.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authManageIntegrationsSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const manageIntegrationsUserFile = 'playwright/.auth/manage_integrations_user.json';
|
||||||
|
|
||||||
|
authManageIntegrationsSetup('authenticate as integrations e2e user', async ({ page }) => {
|
||||||
|
const integrationsEmail = process.env.E2E_MANAGE_INTEGRATIONS_USER;
|
||||||
|
const integrationsPassword = process.env.E2E_MANAGE_INTEGRATIONS_PASSWORD;
|
||||||
|
|
||||||
|
if (!integrationsEmail || !integrationsPassword) {
|
||||||
|
throw new Error('E2E_MANAGE_INTEGRATIONS_USER and E2E_MANAGE_INTEGRATIONS_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, integrationsEmail, integrationsPassword, manageIntegrationsUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/manage-scans.auth.setup.ts
Normal file
15
ui/tests/setups/manage-scans.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authManageScansSetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const manageScansUserFile = 'playwright/.auth/manage_scans_user.json';
|
||||||
|
|
||||||
|
authManageScansSetup('authenticate as scans e2e user', async ({ page }) => {
|
||||||
|
const scansEmail = process.env.E2E_MANAGE_SCANS_USER;
|
||||||
|
const scansPassword = process.env.E2E_MANAGE_SCANS_PASSWORD;
|
||||||
|
|
||||||
|
if (!scansEmail || !scansPassword) {
|
||||||
|
throw new Error('E2E_MANAGE_SCANS_USER and E2E_MANAGE_SCANS_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, scansEmail, scansPassword, manageScansUserFile);
|
||||||
|
});
|
||||||
15
ui/tests/setups/unlimited-visibility.auth.setup.ts
Normal file
15
ui/tests/setups/unlimited-visibility.auth.setup.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { test as authUnlimitedVisibilitySetup } from '@playwright/test';
|
||||||
|
import { authenticateAndSaveState } from '@/tests/helpers';
|
||||||
|
|
||||||
|
const unlimitedVisibilityUserFile = 'playwright/.auth/unlimited_visibility_user.json';
|
||||||
|
|
||||||
|
authUnlimitedVisibilitySetup('authenticate as unlimited visibility e2e user', async ({ page }) => {
|
||||||
|
const unlimitedVisibilityEmail = process.env.E2E_UNLIMITED_VISIBILITY_USER;
|
||||||
|
const unlimitedVisibilityPassword = process.env.E2E_UNLIMITED_VISIBILITY_PASSWORD;
|
||||||
|
|
||||||
|
if (!unlimitedVisibilityEmail || !unlimitedVisibilityPassword) {
|
||||||
|
throw new Error('E2E_UNLIMITED_VISIBILITY_USER and E2E_UNLIMITED_VISIBILITY_PASSWORD environment variables are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticateAndSaveState(page, unlimitedVisibilityEmail, unlimitedVisibilityPassword, unlimitedVisibilityUserFile);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user