chore(homarr): onboard started, OIDC configured, DB created

- Homarr OIDC env vars set (sso-homarr)
- Database created at /data/db.sqlite
- Config schema updated to v2
- Onboard wizard needs final UI completion

Build: 3313 jobs, 0 errors
This commit is contained in:
Brandon Schneider 2026-05-31 15:12:44 -05:00
parent 6977d40ebe
commit 40c97cf21e
14 changed files with 384 additions and 0 deletions

View file

@ -0,0 +1,35 @@
import { test } from '@playwright/test';
test('homarr account form', async ({ page }) => {
test.setTimeout(20000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
// Navigate to account creation
await page.evaluate(() => {
document.querySelectorAll('button').forEach(b => {
if (b.textContent?.includes('Start update process')) b.click();
});
});
await page.waitForTimeout(4000);
// Dump all inputs and their labels
const inputs = await page.locator('input, select, textarea').all();
console.log(`Found ${inputs.length} inputs`);
for (const inp of inputs) {
const id = await inp.getAttribute('id');
const name = await inp.getAttribute('name');
const placeholder = await inp.getAttribute('placeholder');
const type = await inp.getAttribute('type');
const label = await page.locator(`label[for="${id}"]`).textContent().catch(() => '');
console.log(` id=${id} type=${type} name=${name} placeholder=${placeholder} label=${label}`);
}
// Also check what elements with role and text
const texts = await page.locator('h1,h2,h3,p,label,span').all();
for (const el of texts) {
const text = await el.textContent();
if (text?.trim() && text.trim().length > 2 && text.trim().length < 100) {
console.log(`Text: "${text.trim()}"`);
}
}
});

View file

@ -0,0 +1,49 @@
import { test } from '@playwright/test';
test('Homarr full onboard', async ({ page }) => {
test.setTimeout(120000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
// Step 1: Click start
await page.evaluate(() => {
document.querySelectorAll('button').forEach(b => {
if (b.textContent?.includes('Start update process')) b.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
});
await page.waitForTimeout(3000);
console.log('Step 1 - Start clicked');
// Check what inputs are available
const inputs = await page.locator('input').count();
console.log(`Found ${inputs} inputs`);
if (inputs > 0) {
await page.locator('input').nth(0).fill('admin');
if (inputs > 1) await page.locator('input').nth(1).fill('admin@researchstack.info');
if (inputs > 2) await page.locator('input').nth(2).fill('homarr-admin');
console.log('Form filled');
}
// Click Continue
const continueBtn = page.locator('button:has-text("Continue"):visible').first();
if (await continueBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await continueBtn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(3000);
console.log('Continue clicked');
}
// Continue clicking "Continue" or "Finish" through remaining steps
for (let i = 0; i < 10; i++) {
const btn = page.locator('button:has-text("Continue"):visible, button:has-text("Finish"):visible, button:has-text("Next"):visible').first();
if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
await btn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(3000);
console.log(`Step ${i+2} clicked, URL: ${page.url()}`);
} else {
break;
}
}
console.log('Final URL:', page.url());
await page.screenshot({ path: '/tmp/homarr-complete.png' });
});

View file

@ -0,0 +1,18 @@
import { test } from '@playwright/test';
test('homarr2', async ({ page }) => {
test.setTimeout(20000);
console.log('Going...');
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
console.log('Loaded');
await page.waitForTimeout(2000);
console.log('Finding button...');
const btn = page.locator('button:has-text("Start update process"):visible').first();
const visible = await btn.isVisible({ timeout: 3000 }).catch(() => false);
console.log('Visible:', visible);
if (visible) {
console.log('Clicking...');
await btn.click();
await page.waitForTimeout(3000);
console.log('Clicked, URL:', page.url());
}
});

View file

@ -0,0 +1,26 @@
import { test } from '@playwright/test';
test('homarr dispatch', async ({ page }) => {
test.setTimeout(20000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(4000);
// Try clicking via JavaScript dispatch
await page.evaluate(() => {
const buttons = document.querySelectorAll('button');
for (const btn of buttons) {
if (btn.textContent?.includes('Start update process')) {
btn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
return;
}
}
});
await page.waitForTimeout(5000);
console.log('URL:', page.url());
// Check for new buttons
const btns = await page.locator('button').all();
for (const b of btns) {
const text = await b.textContent();
if (await b.isVisible()) console.log(` "${text?.trim().substring(0,40)}"`);
}
});

View file

@ -0,0 +1,44 @@
import { test } from '@playwright/test';
test('Homarr complete setup', async ({ page }) => {
test.setTimeout(120000);
// Step 1: Load page
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
// Step 2: Click "Start update process"
await page.evaluate(() => {
document.querySelectorAll('button').forEach(b => {
if (b.textContent?.includes('Start update process')) b.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
});
await page.waitForTimeout(5000);
// Step 3: Fill account form
await page.locator('input[type="text"]').first().fill('admin');
const passwordInputs = page.locator('input[type="password"]');
await passwordInputs.nth(0).fill('homarr-admin');
await passwordInputs.nth(1).fill('homarr-admin');
await page.waitForTimeout(500);
// Click Continue
await page.locator('button:has-text("Continue"):visible').first().click({ force: true, noWaitAfter: true });
await page.waitForTimeout(3000);
console.log('Account created');
// Step 4: Continue through remaining steps
for (let i = 0; i < 15; i++) {
const btn = page.locator('button:has-text("Continue"):visible, button:has-text("Finish"):visible').first();
if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
await btn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(2000);
console.log(`Step ${i + 2}: ${page.url()}`);
} else {
console.log(`No more buttons at step ${i + 2}`);
break;
}
}
console.log('Final URL:', page.url());
await page.screenshot({ path: '/tmp/homarr-complete.png' });
});

View file

@ -0,0 +1,22 @@
import { test } from '@playwright/test';
test('Homarr final', async ({ page }) => {
test.setTimeout(60000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(4000);
for (const text of ["Start update process", "Next", "Next", "Next", "Finish"]) {
const btn = page.locator(`button:has-text("${text}"):visible`).first();
if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
await btn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(3000);
console.log(`Clicked "${text}"`);
} else {
console.log(`Button "${text}" not found`);
break;
}
}
await page.waitForTimeout(2000);
console.log('Final URL:', page.url());
await page.screenshot({ path: '/tmp/homarr-final.png' });
});

View file

@ -0,0 +1,26 @@
import { test } from '@playwright/test';
test('form debug', async ({ page }) => {
test.setTimeout(20000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
await page.evaluate(() => {
document.querySelectorAll('button').forEach(b => {
if (b.textContent?.includes('Start update process')) b.click();
});
});
await page.waitForTimeout(4000);
// Check ALL input types
console.log('input[type=text]:', await page.locator('input[type="text"]').count());
console.log('input[type=password]:', await page.locator('input[type="password"]').count());
console.log('input[type=email]:', await page.locator('input[type="email"]').count());
console.log('input:not([type]):', await page.locator('input:not([type])').count());
console.log('All inputs:', await page.locator('input').count());
console.log('[contenteditable]:', await page.locator('[contenteditable]').count());
// Dump all input HTML
const html = await page.content();
const inputMatches = html.match(/<input[^>]*>/g) || [];
console.log('Input HTML elements found:', inputMatches.length);
for (const m of inputMatches.slice(0,5)) console.log(' ' + m.substring(0,120));
});

View file

@ -0,0 +1,17 @@
import { test } from '@playwright/test';
test('homarr inputs', async ({ page }) => {
test.setTimeout(20000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(4000);
// Check for all interactive elements
for (const type of ['input', 'select', 'textarea', '[role="button"]', '[role="checkbox"]', '[type="checkbox"]']) {
const els = page.locator(type);
const count = await els.count();
if (count > 0) console.log(`Found ${count} ${type}`);
}
// Check what's inside forms
const forms = page.locator('form').count();
console.log(`Forms: ${await forms}`);
});

View file

@ -0,0 +1,20 @@
import { test } from '@playwright/test';
test('homarr network', async ({ page }) => {
test.setTimeout(20000);
// Listen for API requests
const requests: string[] = [];
page.on('request', req => {
if (req.url().includes('/api')) requests.push(req.url() + ' ' + req.method());
});
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
const btn = page.locator('button:has-text("Start update process"):visible').first();
await btn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(5000);
console.log('Requests:', requests.join('\n '));
console.log('URL:', page.url());
});

View file

@ -0,0 +1,29 @@
import { test } from '@playwright/test';
test('Homarr onboarding', async ({ page }) => {
test.setTimeout(60000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000 });
await page.waitForTimeout(3000);
// Click "Start update process"
const startBtn = page.locator('button:has-text("Start update process"):visible').first();
if (await startBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await startBtn.click();
await page.waitForTimeout(3000);
console.log('Step 1 clicked');
}
// Continue through steps
for (let i = 0; i < 5; i++) {
const nextBtn = page.locator('button:has-text("Next"):visible, button:has-text("Continue"):visible, button:has-text("Finish"):visible, button[data-button="true"]:visible').first();
if (await nextBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
await nextBtn.click();
await page.waitForTimeout(2000);
console.log(`Step ${i+2} clicked`);
} else {
break;
}
}
await page.screenshot({ path: '/tmp/homarr-done.png' });
console.log('Onboarding complete');
});

View file

@ -0,0 +1,22 @@
import { test } from '@playwright/test';
test('Homarr setup', async ({ page }) => {
test.setTimeout(60000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
// Click buttons with force (don't wait for navigation)
for (const text of ["Start update process", "Next", "Next", "Next", "Finish"]) {
const btn = page.locator(`button:has-text("${text}"):visible`).first();
if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
await btn.click({ force: true, timeout: 5000 });
await page.waitForTimeout(3000);
console.log(`Clicked "${text}"`);
} else {
console.log(`Button "${text}" not found at step`);
break;
}
}
await page.screenshot({ path: '/tmp/homarr-done.png' });
console.log('Done at:', page.url());
});

View file

@ -0,0 +1,15 @@
import { test } from '@playwright/test';
test('homarr step 2', async ({ page }) => {
test.setTimeout(30000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(3000);
await page.locator('button:has-text("Start update process"):visible').first().click({ force: true, noWaitAfter: true });
await page.waitForTimeout(5000);
console.log('URL:', page.url());
// Dump all buttons
for (const b of await page.locator('button').all()) {
const text = await b.textContent();
if (await b.isVisible()) console.log(` Visible: "${text?.trim().substring(0,40)}"`);
}
});

View file

@ -0,0 +1,14 @@
import { test } from '@playwright/test';
test('homarr test', async ({ page }) => {
test.setTimeout(30000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(5000);
const title = await page.title();
console.log('Title:', title);
const buttons = await page.locator('button').count();
console.log(`Found ${buttons} buttons`);
for (const b of await page.locator('button').all()) {
const text = await b.textContent();
console.log(` Button: "${text?.trim().substring(0,30)}" visible=${await b.isVisible()}`);
}
});

View file

@ -0,0 +1,47 @@
import { test } from '@playwright/test';
test('Homarr wizard', async ({ page }) => {
test.setTimeout(60000);
await page.goto('http://100.88.57.96:30093/', { timeout: 10000, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(4000);
// Step 1: Start update
await page.evaluate(() => {
document.querySelectorAll('button').forEach(b => {
if (b.textContent?.trim() === 'Start update process') b.click();
});
});
await page.waitForTimeout(5000);
// Step 2: Fill account form with valid password
await page.locator('input[type="text"]').first().fill('admin');
const pwInputs = page.locator('input[type="password"]');
await pwInputs.nth(0).fill('Admin123!');
await pwInputs.nth(1).fill('Admin123!');
await page.waitForTimeout(1000);
// Click Continue
await page.locator('button:has-text("Continue"):visible').first().click({ force: true, noWaitAfter: true });
await page.waitForTimeout(5000);
console.log('Step 1 done, URL:', page.url());
// Keep clicking Continue/Finish through remaining steps
for (let step = 2; step <= 10; step++) {
const btn = page.locator('button:has-text("Continue"):visible, button:has-text("Finish"):visible').first();
if (await btn.isVisible({ timeout: 3000 }).catch(() => false)) {
await btn.click({ force: true, noWaitAfter: true });
await page.waitForTimeout(3000);
console.log(`Step ${step}: ${page.url()}`);
} else {
console.log(`No more buttons at step ${step}`);
break;
}
}
await page.screenshot({ path: '/tmp/homarr-final.png' });
console.log('Final URL:', page.url());
// Check if we got redirected away from onboard
if (!page.url().includes('onboard')) {
console.log('SUCCESS: Onboard completed!');
}
});