diff --git a/4-Infrastructure/k3s-flake/tests/homarr-verify.spec.ts b/4-Infrastructure/k3s-flake/tests/homarr-verify.spec.ts new file mode 100644 index 00000000..db93b605 --- /dev/null +++ b/4-Infrastructure/k3s-flake/tests/homarr-verify.spec.ts @@ -0,0 +1,116 @@ +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' }); + } + } + + // Land on Homarr. If we see a "Login" link, click it to authenticate to Homarr itself + const loginLink = page.locator('a[href="/auth/login"], a:has-text("Login")').first(); + if (await loginLink.isVisible({ timeout: 5000 }).catch(() => false)) { + console.log('Homarr login link visible. Clicking it to authenticate NextAuth...'); + await loginLink.click(); + await page.waitForTimeout(6000); + + // If Authentik consent/authorization screen appears after clicking login + if (page.url().includes('consent') || page.url().includes('authorize') || page.url().includes('application/o/authorize')) { + console.log('Consent/Authorization screen detected after Homarr login. 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); + } + } + } + + 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); + } +});