fix: Fix multiple bugs with new authentication flow
This commit is contained in:
153
e2e/tests/auth/login-return-url.spec.ts
Normal file
153
e2e/tests/auth/login-return-url.spec.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { test, expect } from '../../fixtures/multi-client';
|
||||
import { LoginPage } from '../../pages/login.page';
|
||||
import { RegisterPage } from '../../pages/register.page';
|
||||
|
||||
interface TestUser {
|
||||
username: string;
|
||||
displayName: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
test.describe('Login returnUrl handling', () => {
|
||||
test.describe.configure({ timeout: 120_000 });
|
||||
|
||||
test('unwraps nested login returnUrl chains after successful login', async ({ createClient }) => {
|
||||
const client = await createClient();
|
||||
const { page } = client;
|
||||
const suffix = uniqueName('nested-return');
|
||||
const user: TestUser = {
|
||||
username: `user_${suffix}`,
|
||||
displayName: 'Return Url User',
|
||||
password: 'TestPass123!'
|
||||
};
|
||||
|
||||
await test.step('Create an account', async () => {
|
||||
const registerPage = new RegisterPage(page);
|
||||
|
||||
await registerPage.goto();
|
||||
await registerPage.register(user.username, user.displayName, user.password);
|
||||
await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
|
||||
});
|
||||
|
||||
await test.step('Log out and open a deeply nested login returnUrl', async () => {
|
||||
await logout(page);
|
||||
|
||||
const nestedReturnUrl = '/login?returnUrl=%2Flogin%3FreturnUrl%3D%252Fservers';
|
||||
|
||||
await page.goto(`/login?returnUrl=${encodeURIComponent(nestedReturnUrl)}`, {
|
||||
waitUntil: 'domcontentloaded'
|
||||
});
|
||||
|
||||
await expect(page.locator('#login-username')).toBeVisible({ timeout: 15_000 });
|
||||
});
|
||||
|
||||
await test.step('Login lands on the original destination instead of looping on /login', async () => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await loginPage.login(user.username, user.password);
|
||||
await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
|
||||
await expect(page).not.toHaveURL(/returnUrl=.*login/);
|
||||
});
|
||||
});
|
||||
|
||||
test('redirects unauthenticated /servers visits to login and returns there after login', async ({ createClient }) => {
|
||||
const client = await createClient();
|
||||
const { page } = client;
|
||||
const suffix = uniqueName('servers-return');
|
||||
const user: TestUser = {
|
||||
username: `user_${suffix}`,
|
||||
displayName: 'Servers Return User',
|
||||
password: 'TestPass123!'
|
||||
};
|
||||
|
||||
await test.step('Create an account and log out', async () => {
|
||||
const registerPage = new RegisterPage(page);
|
||||
|
||||
await registerPage.goto();
|
||||
await registerPage.register(user.username, user.displayName, user.password);
|
||||
await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
|
||||
await logout(page);
|
||||
});
|
||||
|
||||
await test.step('Visiting /servers sends the user to a single-level login returnUrl', async () => {
|
||||
await page.goto('/servers', { waitUntil: 'domcontentloaded' });
|
||||
await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
|
||||
await expect(page).toHaveURL(/returnUrl=%2Fservers/);
|
||||
await expect(page).not.toHaveURL(/returnUrl=.*login/);
|
||||
});
|
||||
|
||||
await test.step('Logging in returns to /servers', async () => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await loginPage.login(user.username, user.password);
|
||||
await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
|
||||
await expect(page.locator('app-server-browser')).toBeVisible({ timeout: 15_000 });
|
||||
});
|
||||
});
|
||||
|
||||
test('lets a returning user log back in after an expired session redirect', async ({ createClient }) => {
|
||||
const client = await createClient();
|
||||
const { page } = client;
|
||||
const suffix = uniqueName('expired-session');
|
||||
const user: TestUser = {
|
||||
username: `user_${suffix}`,
|
||||
displayName: 'Expired Session User',
|
||||
password: 'TestPass123!'
|
||||
};
|
||||
|
||||
await test.step('Create an account', async () => {
|
||||
const registerPage = new RegisterPage(page);
|
||||
|
||||
await registerPage.goto();
|
||||
await registerPage.register(user.username, user.displayName, user.password);
|
||||
await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });
|
||||
});
|
||||
|
||||
await test.step('Simulate an expired session while keeping the persisted user id', async () => {
|
||||
await page.evaluate(() => {
|
||||
const storageKey = 'metoyou.authTokens';
|
||||
const raw = localStorage.getItem(storageKey);
|
||||
|
||||
if (!raw) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(raw) as Record<string, { token: string; expiresAt: number }>;
|
||||
const expiredStore = Object.fromEntries(
|
||||
Object.entries(parsed).map(([url, entry]) => [url, { ...entry, expiresAt: 0 }])
|
||||
);
|
||||
|
||||
localStorage.setItem(storageKey, JSON.stringify(expiredStore));
|
||||
});
|
||||
|
||||
await page.goto('/servers', { waitUntil: 'domcontentloaded' });
|
||||
await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
|
||||
await expect(page).toHaveURL(/returnUrl=%2Fservers/);
|
||||
await expect(page).not.toHaveURL(/returnUrl=.*login/);
|
||||
});
|
||||
|
||||
await test.step('The user can authenticate again and reach /servers', async () => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await loginPage.login(user.username, user.password);
|
||||
await expect(page).toHaveURL(/\/servers/, { timeout: 15_000 });
|
||||
await expect(page.locator('app-server-browser')).toBeVisible({ timeout: 15_000 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function logout(page: import('@playwright/test').Page): Promise<void> {
|
||||
const menuButton = page.getByRole('button', { name: 'Menu' });
|
||||
const logoutButton = page.getByRole('button', { name: 'Logout' });
|
||||
|
||||
await expect(menuButton).toBeVisible({ timeout: 10_000 });
|
||||
await menuButton.click();
|
||||
await expect(logoutButton).toBeVisible({ timeout: 10_000 });
|
||||
await logoutButton.click();
|
||||
await expect(page).toHaveURL(/\/login/, { timeout: 15_000 });
|
||||
}
|
||||
|
||||
function uniqueName(prefix: string): string {
|
||||
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36)
|
||||
.slice(2, 8)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user