Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,6 @@ jobs:
UPSTASH_REDIS_REST_TOKEN: dummy-token
RESEND_API_KEY: dummy-resend-key
TRIGGER_SECRET_KEY: dummy-trigger-key
STRIPE_SECRET_KEY: sk_test_dummy_key_for_ci_builds
STRIPE_WEBHOOK_SECRET: whsec_test_dummy_webhook_secret

- name: Start server
run: |
Expand Down Expand Up @@ -282,8 +280,6 @@ jobs:
UPSTASH_REDIS_REST_TOKEN: dummy-token
RESEND_API_KEY: dummy-resend-key
TRIGGER_SECRET_KEY: dummy-trigger-key
STRIPE_SECRET_KEY: sk_test_dummy_key_for_ci_builds
STRIPE_WEBHOOK_SECRET: whsec_test_dummy_webhook_secret

- name: Run E2E tests
run: |
Expand All @@ -305,8 +301,6 @@ jobs:
UPSTASH_REDIS_REST_TOKEN: dummy-token
RESEND_API_KEY: dummy-resend-key
TRIGGER_SECRET_KEY: dummy-trigger-key
STRIPE_SECRET_KEY: sk_test_dummy_key_for_ci_builds
STRIPE_WEBHOOK_SECRET: whsec_test_dummy_webhook_secret

- name: Generate test summary
if: always()
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/quick-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ jobs:
UPSTASH_REDIS_REST_TOKEN: dummy-token
RESEND_API_KEY: dummy-resend-key
TRIGGER_SECRET_KEY: dummy-trigger-key
STRIPE_SECRET_KEY: sk_test_dummy_key_for_ci_builds
STRIPE_WEBHOOK_SECRET: whsec_test_dummy_webhook_secret

- name: Post test results to PR
if: failure() && github.event_name == 'pull_request'
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/test-quick.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,3 @@ jobs:
UPSTASH_REDIS_REST_TOKEN: dummy-token
RESEND_API_KEY: dummy-resend-key
TRIGGER_SECRET_KEY: dummy-trigger-key
STRIPE_SECRET_KEY: sk_test_dummy_key_for_ci_builds
STRIPE_WEBHOOK_SECRET: whsec_test_dummy_webhook_secret
5 changes: 0 additions & 5 deletions apps/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ RESEND_API_KEY="" # Resend Dashboard -> API Keys
UPSTASH_REDIS_REST_URL="" # Upstash Console -> Redis -> Create Database
UPSTASH_REDIS_REST_TOKEN="" # Found in the same database details page

# Payment Processing
# Stripe (https://dashboard.stripe.com/apikeys)
STRIPE_SECRET_KEY="" # Stripe Dashboard -> Developers -> API keys
STRIPE_WEBHOOK_SECRET="" # Stripe Dashboard -> Developers -> Webhooks

# File Storage
# Upload Thing (https://uploadthing.com/dashboard)
UPLOADTHING_TOKEN="" # Upload Thing Dashboard -> API Keys
Expand Down
79 changes: 31 additions & 48 deletions apps/app/e2e/simple-auth.spec.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,53 @@
import { expect, test } from '@playwright/test';
import { authenticateTestUser } from './utils/auth-helpers';

test('simple auth flow', async ({ page, context, browserName }) => {
// Create a test user and authenticate
const testEmail = `test-${Date.now()}@example.com`;

console.log(`[${browserName}] Starting test with email: ${testEmail}`);

const response = await context.request.post('http://localhost:3000/api/auth/test-login', {
data: {
email: testEmail,
name: 'Test User',
},
timeout: 30000, // 30 second timeout
const testEmail = `test-${Date.now()}-${Math.random().toString(36).substring(7)}-${browserName}@example.com`;

// Authenticate user
await authenticateTestUser(page, {
email: testEmail,
name: 'Test User',
skipOrg: false,
hasAccess: true,
});

// Add debugging for all browsers
if (!response.ok()) {
console.error(`[${browserName}] Test login failed:`, {
status: response.status(),
statusText: response.statusText(),
});
try {
const body = await response.text();
console.error(`[${browserName}] Response body:`, body);
} catch (e) {
console.error(`[${browserName}] Could not read response body`);
}
}

expect(response.ok()).toBeTruthy();
const data = await response.json();
expect(data.success).toBe(true);
expect(data.user).toBeDefined();
expect(data.user.email).toBe(testEmail);
expect(data.user.emailVerified).toBe(true);

// Verify session cookie was set
const cookies = await context.cookies();
const sessionCookie = cookies.find((c) => c.name === 'better-auth.session_token');
expect(sessionCookie).toBeDefined();
expect(sessionCookie?.httpOnly).toBe(true);

// Navigate to auth page - should be redirected since we're authenticated
await page.goto('http://localhost:3000/auth', { waitUntil: 'domcontentloaded' });
// Navigate to root first to let the user settle into their authenticated state
await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000); // Wait for all redirects to complete

const afterRootUrl = page.url();
console.log('URL after navigating to root:', afterRootUrl);

// Now navigate to auth page - should be redirected since we're authenticated
await page.goto('/auth', { waitUntil: 'domcontentloaded' });

// Wait for the redirect to happen
// Since we know we should be redirected, wait for URL change
let retries = 0;
const maxRetries = 10;
// Wait for redirect away from auth
await page.waitForURL((url) => !url.toString().includes('/auth'), { timeout: 5000 });

while (page.url().includes('/auth') && retries < maxRetries) {
await page.waitForTimeout(500);
retries++;
// If we're on root, wait for the subsequent redirect to final destination
if (new URL(page.url()).pathname === '/') {
console.log('On root route, waiting for final redirect...');
await page.waitForURL((url) => new URL(url).pathname !== '/', { timeout: 5000 });
}

const currentUrl = page.url();
console.log('Final URL after auth redirect:', currentUrl);

// Verify we're redirected to an authenticated route
expect(currentUrl).not.toContain('/auth');

// Common authenticated routes include /setup, /dashboard, /upgrade, or organization-specific routes
// User should be on one of these meaningful authenticated routes
const isAuthenticatedRoute =
currentUrl.includes('/setup') ||
currentUrl.includes('/dashboard') ||
currentUrl.includes('/upgrade') ||
currentUrl.includes('/org_');
currentUrl.includes('/setup') || // Setup flow
currentUrl.match(/\/org_[a-zA-Z0-9]+\//) !== null || // Organization pages
currentUrl.includes('/upgrade') || // Upgrade page
currentUrl.includes('/no-access') || // No access page
currentUrl.includes('/onboarding'); // Onboarding flow

expect(isAuthenticatedRoute).toBeTruthy();
});
58 changes: 38 additions & 20 deletions apps/app/e2e/tests/middleware-onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@ test.describe('Middleware Onboarding Behavior', () => {
});

// Try to access organization page
await page.goto('/');

// Should NOT be redirected to onboarding
await page.waitForTimeout(2000); // Give time for any redirects
expect(page.url()).not.toContain('/onboarding');

// Should be on an authenticated page (org page, frameworks, etc)
const isOnOrgPage =
page.url().includes('/org_') ||
page.url().includes('/frameworks') ||
page.url().includes('/setup');
expect(isOnOrgPage).toBeTruthy();
await page.goto('/', { waitUntil: 'domcontentloaded' });

// Wait for all redirects to complete
await page.waitForTimeout(3000); // Just wait for redirects to settle

const currentUrl = page.url();
console.log('Current URL after navigation:', currentUrl);

expect(currentUrl).not.toContain('/onboarding');

// Should be on an authenticated page - could be org page or setup (if activeOrgId not set)
const isOnValidPage =
currentUrl.match(/\/org_[a-zA-Z0-9]+\//) !== null || // Organization routes
currentUrl.match(/\/setup\/[a-zA-Z0-9]+/) !== null || // Dynamic setup URLs
currentUrl.includes('/upgrade'); // Upgrade page

expect(isOnValidPage).toBeTruthy();
});

test('user without org is redirected to setup', async ({ page }) => {
Expand All @@ -43,19 +48,32 @@ test.describe('Middleware Onboarding Behavior', () => {
});

// Navigate to root
await page.goto('/');
await page.goto('/', { waitUntil: 'domcontentloaded' });

// Should be redirected to setup
await page.waitForURL(/\/setup\/[a-zA-Z0-9]+/, { timeout: 10000 });
expect(page.url()).toContain('/setup/');
// Wait for redirects
await page.waitForTimeout(2000);

const currentUrl = page.url();
console.log('User without org redirected to:', currentUrl);

// Should be redirected to setup (with dynamic ID)
expect(currentUrl).toMatch(/\/setup\/[a-zA-Z0-9]+/);
});

test('unauthenticated user is redirected to auth', async ({ page }) => {
// Try to access protected route without auth
await page.goto('/org_123/frameworks');
// Try to access protected route without auth, expecting redirect
const response = await page.goto('/org_123/frameworks', {
waitUntil: 'commit', // Just wait for navigation to start, not complete
});

// Wait a bit for redirect
await page.waitForTimeout(1000);

// Check final URL
const currentUrl = page.url();
console.log('Unauthenticated user redirected to:', currentUrl);

// Should be redirected to auth
await page.waitForURL(/\/auth/, { timeout: 5000 });
expect(page.url()).toContain('/auth');
expect(currentUrl).toContain('/auth');
});
});
6 changes: 4 additions & 2 deletions apps/app/e2e/tests/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { fillFormField, generateTestData, waitForURL } from '../utils/helpers';
// Increase test timeout for complex flows
test.describe.configure({ timeout: 60000 });

test.describe('Onboarding Flow', () => {
// DEPRECATED: This test file is for the old full onboarding flow.
// The new split onboarding flow is tested in split-onboarding.spec.ts
test.describe.skip('Onboarding Flow (DEPRECATED)', () => {
test.beforeEach(async ({ page }) => {
// Clear any existing auth state
await clearAuth(page);
Expand Down Expand Up @@ -388,7 +390,7 @@ test.describe('Onboarding Flow', () => {
});
});

test.describe('Setup Page Components', () => {
test.describe.skip('Setup Page Components (DEPRECATED)', () => {
test.beforeEach(async ({ page }) => {
const testData = generateTestData();

Expand Down
Loading
Loading