Enhance E2E Tests and Configuration (#358)
* Enhance E2E Tests and Configuration - Updated `.gitignore` to exclude `.auth/` directory for cleaner test environment. - Added `test:fast` script in `package.json` for faster Playwright test execution. - Configured Playwright to include a setup project for initializing tests. - Introduced `AUTH_STATES` utility for managing authentication states in tests. - Created `auth.setup.ts` for user authentication tests, ensuring proper login flows. - Refactored various test files to utilize the new authentication state management, improving test reliability and maintainability. - Adjusted team and user billing tests to streamline setup and enhance clarity. - Refactored some dialogs
This commit is contained in:
committed by
GitHub
parent
f157cc7f3e
commit
02e2502dcc
@@ -1,40 +1,23 @@
|
||||
import { Page, expect, selectors, test } from '@playwright/test';
|
||||
import { Page, expect, test } from '@playwright/test';
|
||||
|
||||
import { AuthPageObject } from '../authentication/auth.po';
|
||||
import { TeamAccountsPageObject } from '../team-accounts/team-accounts.po';
|
||||
|
||||
const MFA_KEY = 'NHOHJVGPO3R3LKVPRMNIYLCDMBHUM2SE';
|
||||
import { AUTH_STATES } from '../utils/auth-state';
|
||||
|
||||
test.describe('Admin Auth flow without MFA', () => {
|
||||
AuthPageObject.setupSession(AUTH_STATES.OWNER_USER);
|
||||
|
||||
test('will return a 404 for non-admin users', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
await page.goto('/auth/sign-in');
|
||||
|
||||
await auth.signIn({
|
||||
email: 'owner@makerkit.dev',
|
||||
password: 'testingpassword',
|
||||
});
|
||||
|
||||
await page.waitForURL('/home');
|
||||
|
||||
await page.goto('/admin');
|
||||
|
||||
expect(page.url()).toContain('/404');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Admin Auth flow with Super Admin but without MFA', () => {
|
||||
AuthPageObject.setupSession(AUTH_STATES.TEST_USER);
|
||||
|
||||
test('will redirect to 404 for admin users without MFA', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
await page.goto('/auth/sign-in');
|
||||
|
||||
await auth.signIn({
|
||||
email: 'test@makerkit.dev',
|
||||
password: 'testingpassword',
|
||||
});
|
||||
|
||||
await page.waitForURL('/home');
|
||||
|
||||
await page.goto('/admin');
|
||||
|
||||
expect(page.url()).toContain('/404');
|
||||
@@ -42,12 +25,13 @@ test.describe('Admin Auth flow without MFA', () => {
|
||||
});
|
||||
|
||||
test.describe('Admin', () => {
|
||||
// must be serial because OTP verification is not working in parallel
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
test.describe('Admin Dashboard', () => {
|
||||
AuthPageObject.setupSession(AUTH_STATES.SUPER_ADMIN);
|
||||
|
||||
test('displays all stat cards', async ({ page }) => {
|
||||
await goToAdmin(page);
|
||||
await page.goto('/admin');
|
||||
|
||||
// Check all stat cards are present
|
||||
await expect(page.getByRole('heading', { name: 'Users' })).toBeVisible();
|
||||
@@ -73,19 +57,14 @@ test.describe('Admin', () => {
|
||||
});
|
||||
|
||||
test.describe('Personal Account Management', () => {
|
||||
AuthPageObject.setupSession(AUTH_STATES.SUPER_ADMIN);
|
||||
|
||||
let testUserEmail: string;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
selectors.setTestIdAttribute('data-test');
|
||||
|
||||
// Create a new test user before each test
|
||||
testUserEmail = await createUser(page);
|
||||
|
||||
await goToAdmin(page);
|
||||
|
||||
// Navigate to the newly created user's account page
|
||||
// Note: We need to get the user's ID from the email - this might need adjustment
|
||||
// based on your URL structure
|
||||
await page.goto(`/admin/accounts`);
|
||||
|
||||
// use the email as the filter text
|
||||
@@ -99,6 +78,7 @@ test.describe('Admin', () => {
|
||||
await expect(page.getByText('Personal Account')).toBeVisible();
|
||||
await expect(page.getByTestId('admin-ban-account-button')).toBeVisible();
|
||||
await expect(page.getByTestId('admin-impersonate-button')).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByTestId('admin-delete-account-button'),
|
||||
).toBeVisible();
|
||||
@@ -106,6 +86,7 @@ test.describe('Admin', () => {
|
||||
|
||||
test('ban user flow', async ({ page }) => {
|
||||
await page.getByTestId('admin-ban-account-button').click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Ban User' }),
|
||||
).toBeVisible();
|
||||
@@ -189,29 +170,14 @@ test.describe('Admin', () => {
|
||||
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
await auth.signIn({
|
||||
await auth.loginAsUser({
|
||||
email: testUserEmail,
|
||||
password: 'testingpassword',
|
||||
});
|
||||
|
||||
await page.waitForURL('/home');
|
||||
});
|
||||
|
||||
test('impersonate user flow', async ({ page }) => {
|
||||
await page.getByTestId('admin-impersonate-button').click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Impersonate User' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM');
|
||||
await page.getByRole('button', { name: 'Impersonate User' }).click();
|
||||
|
||||
// Should redirect to home and be logged in as the user
|
||||
await page.waitForURL('/home');
|
||||
});
|
||||
|
||||
test('delete user flow', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
await page.getByTestId('admin-delete-account-button').click();
|
||||
|
||||
await expect(
|
||||
@@ -236,13 +202,10 @@ test.describe('Admin', () => {
|
||||
await page.waitForURL('/admin/accounts');
|
||||
|
||||
// Log out
|
||||
await page.context().clearCookies();
|
||||
await auth.signOut();
|
||||
await page.waitForURL('/');
|
||||
|
||||
// Verify user can't log in
|
||||
await page.goto('/auth/sign-in');
|
||||
|
||||
const auth = new AuthPageObject(page);
|
||||
await auth.goToSignIn();
|
||||
|
||||
await auth.signIn({
|
||||
email: testUserEmail,
|
||||
@@ -256,130 +219,116 @@ test.describe('Admin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Team Account Management', () => {
|
||||
test.skip(
|
||||
process.env.ENABLE_TEAM_ACCOUNT_TESTS !== 'true',
|
||||
'Team account tests are disabled',
|
||||
);
|
||||
test.describe('Impersonation', () => {
|
||||
test('can sign in as a user', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
let testUserEmail: string;
|
||||
let teamName: string;
|
||||
let slug: string;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
selectors.setTestIdAttribute('data-test');
|
||||
|
||||
// Create a new test user and team account
|
||||
testUserEmail = await createUser(page, {
|
||||
afterSignIn: async () => {
|
||||
teamName = `test-${Math.random().toString(36).substring(2, 15)}`;
|
||||
|
||||
const teamAccountPo = new TeamAccountsPageObject(page);
|
||||
const teamSlug = teamName.toLowerCase().replace(/ /g, '-');
|
||||
|
||||
slug = teamSlug;
|
||||
|
||||
await teamAccountPo.createTeam({
|
||||
teamName,
|
||||
slug,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await goToAdmin(page);
|
||||
await auth.loginAsSuperAdmin({});
|
||||
const filterText = await createUser(page);
|
||||
|
||||
await page.goto(`/admin/accounts`);
|
||||
|
||||
await filterAccounts(page, teamName);
|
||||
await selectAccount(page, teamName);
|
||||
});
|
||||
await filterAccounts(page, filterText);
|
||||
await selectAccount(page, filterText);
|
||||
|
||||
test('displays team account details', async ({ page }) => {
|
||||
await expect(page.getByText('Team Account')).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId('admin-delete-account-button'),
|
||||
).toBeVisible();
|
||||
});
|
||||
await page.getByTestId('admin-impersonate-button').click();
|
||||
|
||||
test('delete team account flow', async ({ page }) => {
|
||||
await page.getByTestId('admin-delete-account-button').click();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Delete Account' }),
|
||||
page.getByRole('heading', { name: 'Impersonate User' }),
|
||||
).toBeVisible();
|
||||
|
||||
// Try with invalid confirmation
|
||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'WRONG');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Delete Account' }),
|
||||
).toBeVisible(); // Dialog should still be open
|
||||
|
||||
// Confirm with correct text
|
||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'Impersonate User' }).click();
|
||||
|
||||
// Should redirect to admin dashboard after deletion
|
||||
await expect(page).toHaveURL('/admin/accounts');
|
||||
// Should redirect to home and be logged in as the user
|
||||
await page.waitForURL('/home');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function goToAdmin(page: Page) {
|
||||
const auth = new AuthPageObject(page);
|
||||
test.describe('Team Account Management', () => {
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
await page.goto('/auth/sign-in');
|
||||
test.skip(
|
||||
process.env.ENABLE_TEAM_ACCOUNT_TESTS !== 'true',
|
||||
'Team account tests are disabled',
|
||||
);
|
||||
|
||||
await auth.signIn({
|
||||
email: 'super-admin@makerkit.dev',
|
||||
password: 'testingpassword',
|
||||
let testUserEmail: string;
|
||||
let teamName: string;
|
||||
let slug: string;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
// Create a new test user and team account
|
||||
testUserEmail = await createUser(page);
|
||||
|
||||
teamName = `test-${Math.random().toString(36).substring(2, 15)}`;
|
||||
|
||||
await auth.loginAsUser({ email: testUserEmail });
|
||||
|
||||
const teamAccountPo = new TeamAccountsPageObject(page);
|
||||
const teamSlug = teamName.toLowerCase().replace(/ /g, '-');
|
||||
|
||||
slug = teamSlug;
|
||||
|
||||
await teamAccountPo.createTeam({
|
||||
teamName,
|
||||
slug,
|
||||
});
|
||||
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
await auth.signOut();
|
||||
await page.waitForURL('/');
|
||||
|
||||
await auth.loginAsSuperAdmin({});
|
||||
|
||||
await page.goto(`/admin/accounts`);
|
||||
|
||||
await filterAccounts(page, teamName);
|
||||
await selectAccount(page, teamName);
|
||||
});
|
||||
|
||||
await page.waitForURL('/auth/verify');
|
||||
await page.waitForTimeout(250);
|
||||
test('delete team account flow', async ({ page }) => {
|
||||
await expect(page.getByText('Team Account')).toBeVisible();
|
||||
|
||||
await expect(async () => {
|
||||
await auth.submitMFAVerification(MFA_KEY);
|
||||
await page.waitForURL('/home');
|
||||
}).toPass({
|
||||
intervals: [
|
||||
500, 2500, 5000, 7500, 10_000, 15_000, 20_000, 25_000, 30_000, 35_000,
|
||||
40_000, 45_000, 50_000,
|
||||
],
|
||||
await page.getByTestId('admin-delete-account-button').click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Delete Account' }),
|
||||
).toBeVisible();
|
||||
|
||||
// Try with invalid confirmation
|
||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'WRONG');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Delete Account' }),
|
||||
).toBeVisible(); // Dialog should still be open
|
||||
|
||||
// Confirm with correct text
|
||||
await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
// Should redirect to admin dashboard after deletion
|
||||
await expect(page).toHaveURL('/admin/accounts');
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('/admin');
|
||||
}
|
||||
|
||||
async function createUser(
|
||||
page: Page,
|
||||
params: {
|
||||
afterSignIn?: () => Promise<void>;
|
||||
} = {},
|
||||
) {
|
||||
async function createUser(page: Page) {
|
||||
const auth = new AuthPageObject(page);
|
||||
|
||||
const password = 'testingpassword';
|
||||
const email = auth.createRandomEmail();
|
||||
|
||||
// sign up
|
||||
await page.goto('/auth/sign-up');
|
||||
|
||||
await auth.signUp({
|
||||
// create user using bootstrap method
|
||||
await auth.bootstrapUser({
|
||||
email,
|
||||
password,
|
||||
repeatPassword: password,
|
||||
name: 'Test User',
|
||||
});
|
||||
|
||||
// confirm email
|
||||
await auth.visitConfirmEmailLink(email);
|
||||
|
||||
if (params.afterSignIn) {
|
||||
await params.afterSignIn();
|
||||
}
|
||||
|
||||
// sign out
|
||||
await auth.signOut();
|
||||
await page.waitForURL('/');
|
||||
|
||||
// return the email
|
||||
return email;
|
||||
}
|
||||
@@ -404,6 +353,6 @@ async function selectAccount(page: Page, email: string) {
|
||||
|
||||
await link.click();
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForURL(/\/admin\/accounts\/[^\/]+/);
|
||||
}).toPass();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user