import { test, expect } from '@playwright/test'; test('Verify Homarr configured apps via SSO', async ({ page }) => { test.setTimeout(60000); console.log('Navigating to Homarr via domain...'); await page.goto('https://researchstack.info/', { timeout: 20000, waitUntil: 'domcontentloaded' }); await page.waitForTimeout(5000); console.log('Current URL:', page.url()); await page.screenshot({ path: '/tmp/homarr-verify-1.png' }); // Check if we are on the Authentik login page if (page.url().includes('auth.researchstack.info')) { console.log('Authentik login detected. Step 1: Entering username...'); // Fill username const usernameInput = page.locator('input[name="username"], input[type="text"]').first(); await usernameInput.fill('allaun'); // Click submit for username const submitBtn1 = page.locator('button:has-text("Continue"), button:has-text("Log in"), button[type="submit"]').first(); await submitBtn1.click(); await page.waitForTimeout(3000); console.log('Step 2: Entering password...'); // Fill password const passwordInput = page.locator('input[name="password"], input[type="password"]').first(); await passwordInput.fill('Silverkitten14'); // Click submit for password const submitBtn2 = page.locator('button:has-text("Continue"), button:has-text("Log in"), button[type="submit"]').first(); await submitBtn2.click(); await page.waitForTimeout(6000); console.log('After login URL:', page.url()); await page.screenshot({ path: '/tmp/homarr-verify-2.png' }); } // Handle Authentik consent/authorization screen if present if (page.url().includes('consent') || page.url().includes('authorize')) { console.log('Consent/Authorization screen detected. Clicking continue...'); const continueBtn = page.locator('button:has-text("Continue"), input[type="submit"], button[type="submit"]').first(); if (await continueBtn.isVisible()) { await continueBtn.click(); await page.waitForTimeout(6000); console.log('After consent URL:', page.url()); await page.screenshot({ path: '/tmp/homarr-verify-3.png' }); } } // Force NextAuth OIDC authentication by navigating to the login URL console.log('Navigating directly to /auth/login to trigger OIDC session sync...'); await page.goto('https://researchstack.info/auth/login'); await page.waitForTimeout(6000); // If Authentik consent/authorization screen appears after navigating to login if (page.url().includes('consent') || page.url().includes('authorize') || page.url().includes('application/o/authorize')) { console.log('Consent/Authorization screen detected. Clicking continue...'); const continueBtn = page.locator('button:has-text("Continue"), input[type="submit"], button[type="submit"]').first(); if (await continueBtn.isVisible({ timeout: 5000 }).catch(() => false)) { await continueBtn.click(); await page.waitForTimeout(6000); } } // Navigate back to the home board console.log('Navigating back to home board...'); await page.goto('https://researchstack.info/'); await page.waitForTimeout(6000); const title = await page.title(); console.log('Final Page Title:', title); console.log('Final URL:', page.url()); await page.screenshot({ path: '/tmp/homarr-verify-final.png' }); // Extract all text content const content = await page.evaluate(() => document.body.innerText); console.log('Page text snapshot:'); console.log(content.slice(0, 1000)); // Find all links (a tags) and their href/text const links = await page.evaluate(() => { return Array.from(document.querySelectorAll('a')).map(a => ({ text: a.textContent?.trim(), href: a.getAttribute('href') })); }); console.log('Links found in DOM:', JSON.stringify(links, null, 2)); // Check if any element has class grid-layout or board const classes = await page.evaluate(() => { return Array.from(document.querySelectorAll('div')).slice(0, 50).map(div => ({ class: div.getAttribute('class'), id: div.getAttribute('id') })).filter(x => x.class || x.id); }); console.log('Top divs found:', JSON.stringify(classes, null, 2)); // Verify that our subdomains/apps are visible on the dashboard const expectedApps = [ 'Authentik', 'Navidrome', 'Audiobookshelf', 'Uptime Kuma', 'Credential Vault', 'Chat (Hermes)', 'Music Files', 'Music Player' ]; console.log('Verifying expected apps presence:'); for (const app of expectedApps) { const isPresent = content.includes(app); console.log(`- ${app}: ${isPresent ? 'PRESENT' : 'MISSING'}`); expect(isPresent).toBe(true); } });