diff --git a/src/modules/search/application/useCases/queries/GetSimilarUrlsForUrlUseCase.ts b/src/modules/search/application/useCases/queries/GetSimilarUrlsForUrlUseCase.ts index 014cf147..e925e7dd 100644 --- a/src/modules/search/application/useCases/queries/GetSimilarUrlsForUrlUseCase.ts +++ b/src/modules/search/application/useCases/queries/GetSimilarUrlsForUrlUseCase.ts @@ -61,7 +61,6 @@ export class GetSimilarUrlsForUrlUseCase } // Find similar URLs - console.log('Finding similar URLs for:', urlResult.value.value); const similarUrlsResult = await this.searchService.findSimilarUrls( urlResult.value, { @@ -70,7 +69,6 @@ export class GetSimilarUrlsForUrlUseCase callingUserId: query.callingUserId, }, ); - console.log('Similar URLs result:', similarUrlsResult); if (similarUrlsResult.isErr()) { return err( diff --git a/src/modules/user/tests/e2e/OAuthSignInFlow.e2e.test.ts b/src/modules/user/tests/e2e/OAuthSignInFlow.e2e.test.ts deleted file mode 100644 index 4e6bcd41..00000000 --- a/src/modules/user/tests/e2e/OAuthSignInFlow.e2e.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { chromium, Browser, Page } from 'playwright'; -import express from 'express'; -import { Server } from 'http'; -import path from 'path'; -import dotenv from 'dotenv'; -import { config } from 'dotenv'; -import { AtProtoOAuthProcessor } from '../../../atproto/infrastructure/services/AtProtoOAuthProcessor'; -import { InitiateOAuthSignInUseCase } from '../../application/use-cases/InitiateOAuthSignInUseCase'; -import { CompleteOAuthSignInUseCase } from '../../application/use-cases/CompleteOAuthSignInUseCase'; -import { OAuthClientFactory } from '../../infrastructure/services/OAuthClientFactory'; -import { InMemoryUserRepository } from '../infrastructure/InMemoryUserRepository'; -import { - JwtTokenService, - UserAuthenticationService, -} from '../../infrastructure'; -import { InMemoryTokenRepository } from '../infrastructure/InMemoryTokenRepository'; - -// Load environment variables -dotenv.config(); -// Load test environment variables -config({ path: '.env.test' }); - -// Get test credentials from environment -const TEST_HANDLE = process.env.TEST_BLUESKY_HANDLE; -const TEST_PASSWORD = process.env.TEST_BLUESKY_PASSWORD; - -if (!TEST_HANDLE || !TEST_PASSWORD) { - console.error( - 'TEST_BLUESKY_HANDLE and TEST_BLUESKY_PASSWORD must be set in .env.test', - ); - process.exit(1); -} - -describe('OAuth Sign-In Flow', () => { - let browser: Browser; - let page: Page; - let server: Server; - const PORT = 3001; - const BASE_URL = `http://127.0.0.1:${PORT}`; - - // This test requires manual interaction - // Set a longer timeout for manual testing - jest.setTimeout(5 * 60 * 1000); // 5 minutes - let oauthTokens: any = null; - - beforeAll(async () => { - // Create OAuth client using the factory with in-memory stores - const oauthClient = OAuthClientFactory.createInMemoryClient( - `${BASE_URL}/api/user`, - 'Annos Test App', - ); - - // Create OAuth processor with the client - const oauthProcessor = new AtProtoOAuthProcessor(oauthClient); - - // Create in-memory services - const userRepository = InMemoryUserRepository.getInstance(); - const tokenRepository = InMemoryTokenRepository.getInstance(); - const tokenService = new JwtTokenService(tokenRepository, 'test-secret'); - const userAuthService = new UserAuthenticationService(userRepository); - - // Create use cases - const initiateOAuthSignInUseCase = new InitiateOAuthSignInUseCase( - oauthProcessor, - ); - - const completeOAuthSignInUseCase = new CompleteOAuthSignInUseCase( - oauthProcessor, - tokenService, - userRepository, - userAuthService, - ); - - // Start a simple express server to serve our test page - const app = express(); - app.use(express.static(path.join(__dirname, 'public'))); - - // Real API endpoints using our actual implementations - app.get('/api/user/login', async (req, res) => { - const handle = req.query.handle; - - const result = await initiateOAuthSignInUseCase.execute({ - handle: handle as string | undefined, - }); - - if (result.isErr()) { - res.status(400).json({ error: result.error }); - } - - res.json({ authUrl: result.unwrap().authUrl }); - }); - - // Store tokens globally so we can access them in the test - - app.get('/api/user/oauth/callback', async (req, res) => { - const { code, state, iss } = req.query; - - if (!code || !state || !iss) { - res.status(400).json({ error: 'Missing required parameters' }); - return; - } - - // Use the CompleteOAuthSignInUseCase to process the callback - const result = await completeOAuthSignInUseCase.execute({ - code: code as string, - state: state as string, - iss: iss as string, - }); - - if (result.isErr()) { - res.status(400).json({ error: result.error }); - return; - } - - // Store tokens for test verification - oauthTokens = result.unwrap(); - - // Return the tokens - res.json({ - message: 'Authentication successful', - tokens: oauthTokens, - testComplete: true, - }); - }); - - server = app.listen(PORT); - - // Launch browser - browser = await chromium.launch({ headless: false }); - page = await browser.newPage(); - }); - - afterAll(async () => { - await browser.close(); - server.close(); - }); - - it('should complete the OAuth sign-in flow automatically', async () => { - // Navigate to our test page - await page.goto(`${BASE_URL}/oauth-test.html`); - - // Enter the Bluesky handle from environment variables - await page.fill('#handle-input', TEST_HANDLE); - - // Click the login button - await page.click('#login-button'); - - // Wait for navigation to the Bluesky OAuth page - await page.waitForNavigation(); - - // We should now be on the Bluesky login page - // Wait for the login form to be fully loaded - await page.waitForLoadState('networkidle'); - - // The page is a React app, so we need to wait for elements to be available - // Only wait for password field since the identifier is pre-filled - await page.waitForSelector('input[type="password"]', { timeout: 10000 }); - - // Enter password (identifier is already filled) - await page.fill('input[type="password"]', TEST_PASSWORD); - - // Click the login/authorize button - using a more specific selector - // In React apps, sometimes we need to wait a bit after filling inputs - await page.waitForTimeout(1000); - - // Find and click the submit button - try { - // Take a screenshot before clicking to help with debugging - await page.screenshot({ path: 'before-login-click.png' }); - - // Try different possible selectors for the submit button - const buttonSelectors = [ - 'button[type="submit"]', - 'button:has-text("Sign in")', - 'button:has-text("Continue")', - 'button:has-text("Authorize")', - 'button.primary', - 'button[aria-label="Sign in"]', - 'form button', - // More specific selectors based on the HTML structure - 'div > button', - 'form > div > button', - ]; - - let buttonClicked = false; - for (const selector of buttonSelectors) { - const button = await page.$(selector); - if (button) { - // Check if button is visible and enabled - const isVisible = await button.isVisible(); - if (isVisible) { - await button.click(); - console.log(`Clicked button with selector: ${selector}`); - buttonClicked = true; - break; - } - } - } - - if (!buttonClicked) { - console.log( - 'No button found with standard selectors, trying JavaScript click', - ); - // Try clicking any button that looks like a submit button using JavaScript - await page.evaluate(() => { - // @ts-ignore - const buttons = Array.from(document.querySelectorAll('button')); - const loginButton = buttons.find( - (button: any) => - button.innerText.includes('Sign in') || - button.innerText.includes('Continue') || - button.innerText.includes('Log in') || - button.innerText.includes('Authorize'), - ); - // @ts-ignore - if (loginButton) loginButton.click(); - }); - } - } catch (error) { - console.error('Failed to find or click the submit button:', error); - // Take a screenshot to debug - await page.screenshot({ path: 'login-page-debug.png' }); - throw error; - } - - // Wait for redirect back to our callback page - // Use a more robust approach to handle potential redirects - try { - // Wait for any navigation to complete - await page.waitForNavigation({ timeout: 1000 }).catch((e) => { - console.log('Initial navigation timeout, continuing...'); - }); - - // Take a screenshot after login attempt - await page.screenshot({ path: 'after-login-click.png' }); - - // Check if we're on the right page - const currentUrl = page.url(); - console.log(`Current page URL: ${currentUrl}`); - - // If we're not on our callback page yet, we might need to authorize - if (!currentUrl.includes('127.0.0.1:3001')) { - console.log( - 'Not yet redirected to callback, looking for authorize button...', - ); - - // Wait a shorter time for page transitions - await page.waitForTimeout(500); - - // Take another screenshot to see the current state - await page.screenshot({ path: 'authorize-page.png' }); - - // Wait for the authorization page to fully load, but with shorter timeouts - await page.waitForLoadState('networkidle', { timeout: 5000 }); - await page.waitForTimeout(1000); // Give React app time to render - - // Try to find and click an authorize button if present - const authorizeSelectors = [ - 'button:has-text("Authorize")', - 'button:has-text("Allow")', - 'button:has-text("Continue")', - 'button.authorize', - 'button[aria-label="Authorize"]', - // More generic selectors - 'button.primary', - 'form button', - ]; - - let buttonClicked = false; - for (const selector of authorizeSelectors) { - try { - const button = await page.$(selector); - if (button && (await button.isVisible())) { - await button.click(); - console.log( - `Clicked authorize button with selector: ${selector}`, - ); - buttonClicked = true; - await page.waitForNavigation({ timeout: 1000 }).catch((e) => { - console.log('Navigation after authorize click timed out'); - }); - break; - } - } catch (e) { - console.log(`Error with selector ${selector}:`, e); - } - } - - if (!buttonClicked) { - console.log( - 'No standard authorize button found, trying JavaScript click', - ); - // Try clicking any button that looks like an authorize button using JavaScript - await page.evaluate(() => { - // @ts-ignore - const buttons = Array.from(document.querySelectorAll('button')); - const authorizeButton = buttons.find( - (button: any) => - button.innerText.includes('Authorize') || - button.innerText.includes('Allow') || - button.innerText.includes('Continue'), - ); - // @ts-ignore - if (authorizeButton) authorizeButton.click(); - }); - - await page.waitForNavigation({ timeout: 1000 }).catch((e) => { - console.log('Navigation after JS authorize click timed out'); - }); - } - } - } catch (error) { - console.error('Navigation error:', error); - // Take a screenshot to debug - await page.screenshot({ path: 'navigation-debug.png' }); - throw error; - } - - // Wait for the test-complete element to appear, which signals the callback was processed - console.log('Waiting for OAuth flow to complete...'); - - try { - // Wait for the test-complete element that's added when the callback is successful - await page.waitForSelector('#test-complete', { timeout: 10000 }); - console.log('Test complete element found, OAuth flow completed!'); - - // Take a final screenshot - await page.screenshot({ path: 'final-callback-page.png' }); - - // Close the browser early since we're done - await browser.close(); - - // Verify that we received tokens in the global variable - expect(oauthTokens).toBeTruthy(); - expect(oauthTokens).toHaveProperty('accessToken'); - expect(oauthTokens).toHaveProperty('refreshToken'); - - console.log('OAuth flow completed successfully!'); - } catch (error) { - console.error('Error waiting for test completion:', error); - await page.screenshot({ path: 'test-completion-error.png' }); - throw error; - } - }); -}); diff --git a/src/modules/user/tests/e2e/public/oauth-test.html b/src/modules/user/tests/e2e/public/oauth-test.html deleted file mode 100644 index e19f8422..00000000 --- a/src/modules/user/tests/e2e/public/oauth-test.html +++ /dev/null @@ -1,142 +0,0 @@ - - -
- - -