This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
140 lines
4.6 KiB
TypeScript
140 lines
4.6 KiB
TypeScript
/**
|
|
* Authentication Persistence Test
|
|
*
|
|
* CRITICAL TEST: Ensures authentication persists across page reloads
|
|
*
|
|
* This test verifies that:
|
|
* 1. Cookies are properly set with domain=.lvh.me
|
|
* 2. Tokens persist in cookies after page reload
|
|
* 3. The API client correctly sends tokens with requests
|
|
* 4. Users remain logged in after refreshing the page
|
|
*
|
|
* DO NOT REMOVE OR DISABLE THIS TEST
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Authentication Persistence', () => {
|
|
test('should maintain authentication after page reload', async ({ page }) => {
|
|
// Navigate to platform subdomain
|
|
await page.goto('http://platform.lvh.me:5173/');
|
|
|
|
// Should see login page
|
|
await expect(page.locator('h2')).toContainText('Sign in to your account');
|
|
|
|
// Fill in credentials
|
|
await page.fill('input[name="username"]', 'poduck');
|
|
await page.fill('input[name="password"]', 'starry12');
|
|
|
|
// Click sign in
|
|
await page.click('button[type="submit"]');
|
|
|
|
// Wait for navigation/login to complete
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Check cookies are set
|
|
const cookies = await page.context().cookies();
|
|
console.log('Cookies after login:', cookies);
|
|
|
|
const accessToken = cookies.find(c => c.name === 'access_token');
|
|
const refreshToken = cookies.find(c => c.name === 'refresh_token');
|
|
|
|
console.log('Access token:', accessToken);
|
|
console.log('Refresh token:', refreshToken);
|
|
|
|
// Verify tokens exist
|
|
expect(accessToken).toBeTruthy();
|
|
expect(refreshToken).toBeTruthy();
|
|
|
|
// Should be logged in - check for platform dashboard or user content
|
|
const currentUrl = page.url();
|
|
console.log('Current URL after login:', currentUrl);
|
|
|
|
// Wait for any dashboard content to appear
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Take screenshot before reload
|
|
await page.screenshot({ path: 'tests/e2e/before-reload.png' });
|
|
|
|
// Get page content before reload
|
|
const contentBeforeReload = await page.content();
|
|
console.log('Page title before reload:', await page.title());
|
|
|
|
// Reload the page
|
|
console.log('Reloading page...');
|
|
|
|
// Listen to console logs
|
|
page.on('console', msg => {
|
|
console.log('BROWSER CONSOLE:', msg.type(), msg.text());
|
|
});
|
|
|
|
// Listen to network requests
|
|
page.on('request', request => {
|
|
if (request.url().includes('/api/auth')) {
|
|
console.log('REQUEST:', request.method(), request.url());
|
|
}
|
|
});
|
|
|
|
page.on('response', response => {
|
|
if (response.url().includes('/api/auth')) {
|
|
console.log('RESPONSE:', response.status(), response.url());
|
|
}
|
|
});
|
|
|
|
await page.reload();
|
|
|
|
// Wait for page to load
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Check cookies after reload
|
|
const cookiesAfterReload = await page.context().cookies();
|
|
console.log('Cookies after reload:', cookiesAfterReload);
|
|
|
|
const accessTokenAfterReload = cookiesAfterReload.find(c => c.name === 'access_token');
|
|
const refreshTokenAfterReload = cookiesAfterReload.find(c => c.name === 'refresh_token');
|
|
|
|
console.log('Access token after reload:', accessTokenAfterReload);
|
|
console.log('Refresh token after reload:', refreshTokenAfterReload);
|
|
|
|
// Take screenshot after reload
|
|
await page.screenshot({ path: 'tests/e2e/after-reload.png' });
|
|
|
|
// Get page content after reload
|
|
console.log('Page title after reload:', await page.title());
|
|
console.log('Current URL after reload:', page.url());
|
|
|
|
// Should still have tokens
|
|
expect(accessTokenAfterReload, 'Access token should persist after reload').toBeTruthy();
|
|
expect(refreshTokenAfterReload, 'Refresh token should persist after reload').toBeTruthy();
|
|
|
|
// Should NOT see login page
|
|
const hasLoginForm = await page.locator('input[name="username"]').count();
|
|
expect(hasLoginForm, 'Should not see login form after reload').toBe(0);
|
|
});
|
|
|
|
test('should inspect cookie storage mechanism', async ({ page }) => {
|
|
// Navigate to platform subdomain
|
|
await page.goto('http://platform.lvh.me:5173/');
|
|
|
|
// Execute script to test cookie utilities
|
|
const cookieTest = await page.evaluate(() => {
|
|
// @ts-ignore
|
|
const { setCookie, getCookie } = window;
|
|
|
|
// Test setting a cookie
|
|
document.cookie = 'test_cookie=test_value;path=/;domain=.lvh.me;SameSite=Lax';
|
|
|
|
// Read back
|
|
const allCookies = document.cookie;
|
|
|
|
return {
|
|
hostname: window.location.hostname,
|
|
allCookies,
|
|
testCookie: document.cookie.split(';').find(c => c.includes('test_cookie'))
|
|
};
|
|
});
|
|
|
|
console.log('Cookie test results:', cookieTest);
|
|
});
|
|
});
|