mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
+16




![github-actions[bot] <github-actions[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)




Mega Mind
GitHub
YellowSnnowmann
Steven Enamakel
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Cyrus Gray
Horst1993
Cursor
James Gentes
Sam
Sami Rusani
oxoxDev
Muhammad Ismail
Claude Fable 5
nb213
binyangzhu000-sudo
Steven Enamakel
CodeGhost21
sanil-23
M3gA-Mind
oxoxDev
mysma-9403
mwakidenis
NgoQuocViet2001
viet.ngo
Maciej Myszkiewicz
2e5b5e7b23
Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: Horst1993 <horst.w@gmicloud.ai> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: James Gentes <jgentes@users.noreply.github.com> Co-authored-by: Sam <samrusani@users.noreply.github.com> Co-authored-by: Sami Rusani <14844597+samrusani@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Muhammad Ismail <78064250+myi1@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: nb213 <binyangzhu000@gmail.com> Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: sanil-23 <sanil@tinyhumans.ai> Co-authored-by: M3gA-Mind <elvin@mahadao.com> Co-authored-by: oxoxDev <oxoxdev@users.noreply.github.com> Co-authored-by: mysma-9403 <64923976+mysma-9403@users.noreply.github.com> Co-authored-by: mwakidenis <mwakidenice@gmail.com> Co-authored-by: NgoQuocViet2001 <123613986+NgoQuocViet2001@users.noreply.github.com> Co-authored-by: viet.ngo <viet.ngo@sotatek.com> Co-authored-by: Maciej Myszkiewicz <mmyszkiewicz@bwcoders.com>
177 lines
6.8 KiB
TypeScript
177 lines
6.8 KiB
TypeScript
import { expect, type Page, test } from '@playwright/test';
|
|
|
|
import {
|
|
bootAuthenticatedPage,
|
|
callCoreRpc,
|
|
dismissWalkthroughIfPresent,
|
|
waitForAppReady,
|
|
} from '../helpers/core-rpc';
|
|
|
|
async function openSettings(page: Page, userId: string, hash: string): Promise<void> {
|
|
await bootAuthenticatedPage(page, userId, hash);
|
|
await waitForAppReady(page);
|
|
await dismissWalkthroughIfPresent(page);
|
|
}
|
|
|
|
async function themeState(
|
|
page: Page
|
|
): Promise<{ mode?: string; tabBarLabels?: string; agentMessageViewMode?: string }> {
|
|
return page.evaluate(() => {
|
|
const store = (
|
|
window as unknown as {
|
|
__OPENHUMAN_STORE__?: {
|
|
getState?: () => {
|
|
theme?: { mode?: string; tabBarLabels?: string; agentMessageViewMode?: string };
|
|
};
|
|
};
|
|
}
|
|
).__OPENHUMAN_STORE__;
|
|
return store?.getState?.().theme ?? {};
|
|
});
|
|
}
|
|
|
|
async function persistedThemeState(
|
|
page: Page
|
|
): Promise<{ mode?: string; tabBarLabels?: string; agentMessageViewMode?: string }> {
|
|
return page.evaluate(() => {
|
|
const raw = localStorage.getItem('persist:theme');
|
|
if (!raw) return {};
|
|
try {
|
|
const parsed = JSON.parse(raw) as Record<string, string>;
|
|
return {
|
|
mode: parsed.mode ? JSON.parse(parsed.mode) : undefined,
|
|
tabBarLabels: parsed.tabBarLabels ? JSON.parse(parsed.tabBarLabels) : undefined,
|
|
agentMessageViewMode: parsed.agentMessageViewMode
|
|
? JSON.parse(parsed.agentMessageViewMode)
|
|
: undefined,
|
|
};
|
|
} catch {
|
|
return {};
|
|
}
|
|
});
|
|
}
|
|
|
|
function unwrap<T>(value: T | { result: T }): T {
|
|
if (value && typeof value === 'object' && 'result' in value) {
|
|
return (value as { result: T }).result;
|
|
}
|
|
return value as T;
|
|
}
|
|
|
|
test.describe('Settings leaf workflows', () => {
|
|
test('appearance theme, tab bar, and chat rendering preferences persist in app state', async ({
|
|
page,
|
|
}) => {
|
|
await openSettings(page, 'pw-settings-appearance', '/settings/appearance');
|
|
|
|
// Panel title dropped in the PanelPage migration; the theme radios confirm
|
|
// the Appearance panel mounted.
|
|
await expect(page.getByRole('radio', { name: /Dark/ })).toBeVisible();
|
|
await page.getByRole('radio', { name: /Dark/ }).click();
|
|
const labelSwitch = page.getByRole('switch', { name: /Always show labels/ });
|
|
if ((await labelSwitch.getAttribute('aria-checked')) !== 'true') {
|
|
await labelSwitch.click();
|
|
}
|
|
const assistantTextSwitch = page.getByRole('switch', { name: /Plain assistant responses/ });
|
|
if ((await assistantTextSwitch.getAttribute('aria-checked')) !== 'true') {
|
|
await assistantTextSwitch.click();
|
|
}
|
|
|
|
await expect
|
|
.poll(() => themeState(page))
|
|
.toMatchObject({ mode: 'dark', tabBarLabels: 'always', agentMessageViewMode: 'text' });
|
|
await expect
|
|
.poll(() => persistedThemeState(page))
|
|
.toMatchObject({ mode: 'dark', tabBarLabels: 'always', agentMessageViewMode: 'text' });
|
|
|
|
await page.reload();
|
|
await waitForAppReady(page);
|
|
await expect
|
|
.poll(() => themeState(page))
|
|
.toMatchObject({ mode: 'dark', tabBarLabels: 'always', agentMessageViewMode: 'text' });
|
|
});
|
|
|
|
test('embeddings custom endpoint setup writes provider, model, and dimensions', async ({
|
|
page,
|
|
}) => {
|
|
await openSettings(page, 'pw-settings-embeddings', '/settings/embeddings');
|
|
|
|
// Panel title dropped in the PanelPage migration; the provider radios confirm
|
|
// the Embeddings panel mounted.
|
|
await expect(page.getByRole('radio', { name: /Custom/i })).toBeVisible();
|
|
await page.getByRole('radio', { name: /Custom/i }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: /Set up/i })).toBeVisible();
|
|
await page
|
|
.getByPlaceholder('https://your-endpoint.com/v1')
|
|
.fill('http://127.0.0.1:18473/openai/v1');
|
|
// Use a `text-embedding-3-*` model so the save-time verification probe sends
|
|
// `dimensions: 64` — the mock backend (scripts/mock-api) echoes that length,
|
|
// so the live test embed verifies and the config can be persisted.
|
|
await page.getByPlaceholder('text-embedding-3-small').fill('text-embedding-3-small');
|
|
await page.getByPlaceholder('1024').fill('64');
|
|
await page.getByRole('button', { name: 'Save & switch' }).click();
|
|
|
|
const wipe = page.getByRole('button', { name: 'Wipe & apply' });
|
|
await expect(wipe).toBeVisible({ timeout: 15_000 });
|
|
await wipe.click();
|
|
|
|
await expect(page.getByText('Saved.')).toBeVisible({ timeout: 15_000 });
|
|
await expect
|
|
.poll(async () => {
|
|
const raw = await callCoreRpc<any>('openhuman.embeddings_get_settings', {});
|
|
const settings =
|
|
unwrap<{ provider?: string; model?: string; dimensions?: number }>(raw) ?? {};
|
|
return {
|
|
provider: settings.provider,
|
|
model: settings.model,
|
|
dimensions: settings.dimensions,
|
|
};
|
|
})
|
|
.toEqual({
|
|
provider: 'custom:http://127.0.0.1:18473/openai/v1',
|
|
model: 'text-embedding-3-small',
|
|
dimensions: 64,
|
|
});
|
|
});
|
|
|
|
test('agents/new creates a custom agent that appears in the registry', async ({ page }) => {
|
|
const agentId = `pw-researcher-${Date.now()}`;
|
|
await openSettings(page, 'pw-settings-agent-new', '/settings/agents/new');
|
|
|
|
// Page title dropped in the PanelPage migration; the Name field confirms the
|
|
// agent editor mounted.
|
|
await expect(page.getByRole('textbox', { name: 'Name' })).toBeVisible();
|
|
await page.getByRole('textbox', { name: 'Name' }).fill('Playwright Researcher');
|
|
await page.getByRole('textbox', { name: 'ID', exact: true }).fill(agentId);
|
|
await page.getByLabel('Description').fill('Validates settings agent authoring in E2E.');
|
|
await page.getByLabel('Model (optional)').selectOption('hint:reasoning');
|
|
await page
|
|
.getByLabel('System prompt (optional)')
|
|
.fill('Prefer concise citations and explain uncertainty.');
|
|
await page.getByRole('button', { name: 'Add tools' }).click();
|
|
await page.getByRole('button', { name: /Allow all tools/ }).click();
|
|
await page.getByRole('button', { name: 'Done', exact: true }).click();
|
|
await page.getByRole('button', { name: 'Create agent' }).click();
|
|
|
|
await expect(page).toHaveURL(/#\/settings\/agents$/);
|
|
const agent = await callCoreRpc<{
|
|
agent?: { id: string; model?: string; tool_allowlist?: string[] };
|
|
}>('openhuman.agent_registry_get', { id: agentId });
|
|
expect(agent.agent).toMatchObject({
|
|
id: agentId,
|
|
model: 'hint:reasoning',
|
|
tool_allowlist: ['*'],
|
|
});
|
|
});
|
|
|
|
test('retired task sources route lands on Connections', async ({ page }) => {
|
|
await openSettings(page, 'pw-settings-task-sources', '/settings/task-sources');
|
|
|
|
await expect
|
|
.poll(async () => page.evaluate(() => window.location.hash))
|
|
.toContain('/connections');
|
|
await expect(page.getByRole('button', { name: 'Connections' }).first()).toBeVisible();
|
|
});
|
|
});
|