sync: update from private repo (2336949a)
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
oss-sync
2026-09-17 11:29:31 +00:00
parent 28f74c4e37
commit 57702b4f34
2139 changed files with 321270 additions and 17494 deletions
+123
View File
@@ -0,0 +1,123 @@
// Local full-App browser check against built assets, with synthetic API data only.
import assert from 'node:assert/strict';
import { once } from 'node:events';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import express from 'express';
import { chromium } from 'playwright';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const timestamp = new Date().toISOString();
const space = { id: 'browser-space', kind: 'personal', title: 'Browser Workspace', description: '', ownerId: 'browser-test', visibility: 'private', status: 'open', myRole: 'owner', createdAt: timestamp, updatedAt: timestamp };
const task = { id: 1, title: 'Inspector fixture', body: 'SECRET_BODY', pieceName: 'chat', profile: 'default', outputFormat: 'text', askPolicy: 'auto', priority: 'normal', state: 'succeeded', ownerId: 'browser-test', visibility: 'private', spaceId: space.id, createdAt: timestamp, updatedAt: timestamp, latestJob: null, subtasks: [], tags: [], allowedTools: [] };
const app = express();
app.get('/api/auth/me', (req, res) => res.json({ id: 'browser-test', name: 'Browser Test', email: '[email protected]', role: req.headers.cookie?.includes('nonadmin=1') ? 'user' : 'admin' }));
app.get('/api/setup/status', (_req, res) => res.json({ needsSetup: false, authActive: true }));
app.get('/api/config', (_req, res) => res.json({ config: { configVersion: 2, tools: {}, storage: {}, llm: { workers: [] } }, overriddenByEnv: {} }));
app.get('/api/local/spaces', (_req, res) => res.json([space]));
app.get('/api/local/spaces/:id', (_req, res) => res.json(space));
app.get('/api/local/spaces/:id/members', (_req, res) => res.json([{ userId: 'browser-test', name: 'Browser Test', role: 'owner', isOwner: true }]));
app.get('/api/local/tasks', (_req, res) => res.json({ tasks: [task] }));
app.get('/api/local/tasks/:id', (_req, res) => res.json({ task }));
app.get('/api/local/tasks/:id/overview-metrics', (_req, res) => res.json({ metrics: { collecting: false } }));
app.get('/api/local/tasks/:id/comments', (_req, res) => res.json({ comments: [] }));
app.get('/api/local/tasks/:id/movement-history', (_req, res) => res.json({ events: [] }));
app.get('/api/local/tasks/:id/executions', (_req, res) => res.json({ executions: [], active: false }));
app.get(['/api/local/tasks/:id/files', '/api/local/spaces/:id/files'], (_req, res) => res.json({ basePath: '', path: '', entries: [], truncated: false }));
app.get('/api/local/tasks/:id/console/status', (_req, res) => res.json({ active: false }));
app.get('/api/pieces', (_req, res) => res.json({ pieces: [] }));
app.get('/api/pieces/:name', (req, res) => res.json({ piece: { name: req.params.name, description: '', movements: [], max_movements: 1, initial_movement: '' }, source: 'builtin' }));
app.get('/api/transcription/config', (_req, res) => res.json({ configured: false, finalizeTimeoutSeconds: 180 }));
app.get('/api/transcription/recordings', (_req, res) => res.json({ recordings: [] }));
app.get('/api/users/me/pets', (_req, res) => res.json({ pets: [], settings: { enabled: false, activePetId: null, size: 48, position: 'bottom-right', workerPets: {} } }));
app.get('/api/notifications/capabilities', (_req, res) => res.json({}));
app.use('/api', (_req, res) => res.status(404).json({ error: 'Not included in the local browser fixture' }));
app.use('/ui', express.static(resolve(root, 'ui/dist')));
app.get('/ui/*', (_req, res) => res.sendFile(resolve(root, 'ui/dist/index.html')));
const server = app.listen(0, '127.0.0.1');
await once(server, 'listening');
const base = `http://127.0.0.1:${server.address().port}`;
let browser, page;
const errors = [];
try {
browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
await context.addInitScript(() => {
localStorage.setItem('maestro.lang', 'ja');
Object.defineProperty(navigator, 'clipboard', { configurable: true, value: { writeText: async () => { throw new Error('Synthetic clipboard denial'); } } });
});
page = await context.newPage();
page.setDefaultTimeout(15000);
page.on('pageerror', error => errors.push(error.message));
await page.goto(`${base}/ui/?space=browser-space&chat=1&tab=transcription&secret=SECRET_QUERY`);
const input = page.locator('[data-ui-id="transcription.title"]');
await input.waitFor();
await input.fill('SECRET_INPUT');
await page.evaluate(() => history.replaceState(null, '', location.href + '&secret=SECRET_QUERY'));
const activate = async () => {
await page.getByTestId('user-account-menu-trigger').click();
await page.getByRole('button', { name: 'UI調査モード', exact: true }).click();
await page.getByRole('region', { name: 'UI調査', exact: true }).waitFor();
};
await activate();
await input.click();
const panel = page.getByRole('region', { name: 'UI調査', exact: true });
await panel.locator('textarea:not([readonly])').fill('入力欄を広くしてください');
const report = await panel.locator('textarea[readonly]').inputValue();
assert.match(report, /ui\/src\/components\/transcription\/TranscriptionPage\.tsx:\d+:\d+/);
assert.match(report, /transcription\.title/);
assert.match(report, /コンポーネント: TranscriptionPage/);
assert.match(report, /入力欄を広くしてください/);
assert(!/SECRET_INPUT|SECRET_QUERY|SECRET_BODY|browser-test|\/home\//.test(report), 'report contains metadata and explicit wish only');
await panel.getByRole('button', { name: '修正依頼をコピー' }).click();
await panel.getByRole('status').filter({ hasText: '手動でコピー' }).waitFor();
await input.focus();
await page.keyboard.press('Control+k');
assert.equal(await page.getByRole('dialog').count(), 0, 'command palette stays closed during inspection');
await page.screenshot({ path: '/tmp/aao-ui-inspector-wide.png', fullPage: true });
await page.setViewportSize({ width: 390, height: 844 });
await panel.locator('textarea:not([readonly])').fill('狭い画面でも入力できる');
const bounds = await panel.boundingBox();
assert(bounds && bounds.x >= 0 && bounds.x + bounds.width <= 390, 'inspector fits narrow viewport');
await page.screenshot({ path: '/tmp/aao-ui-inspector-narrow.png', fullPage: true });
await page.keyboard.press('Escape');
await panel.waitFor({ state: 'hidden' });
await input.click();
await input.press('Control+a');
await input.pressSequentially('restored');
assert.equal(await input.inputValue(), 'restored');
// Radix disables outside pointer events and traps focus. Keep this regression mandatory.
{
await page.setViewportSize({ width: 1440, height: 1000 });
await page.getByRole('button', { name: '+ 新規', exact: true }).first().click();
await page.getByTestId('space-title-input').waitFor();
// The admin shortcut is reachable even when the modal covers the account menu.
await page.keyboard.press('Control+Alt+i');
await page.getByTestId('space-title-input').click();
const modalWish = page.locator('section[data-ui-inspector-control] textarea:not([readonly])');
await modalWish.click({ timeout: 5000 });
assert(await page.getByTestId('space-title-input').isVisible(), 'inspector panel must not dismiss the inspected Radix modal');
await modalWish.press('Control+a');
await modalWish.pressSequentially('modal input works');
assert.equal(await modalWish.inputValue(), 'modal input works');
assert(await modalWish.evaluate(element => document.activeElement === element), 'inspector retains keyboard focus outside the Radix modal');
await page.screenshot({ path: '/tmp/aao-ui-inspector-modal.png', fullPage: true });
await page.keyboard.press('Escape');
await page.keyboard.press('Escape');
}
await context.addCookies([{ name: 'nonadmin', value: '1', url: base }]);
await page.reload();
await page.getByTestId('user-account-menu-trigger').click();
assert.equal(await page.getByRole('button', { name: 'UI調査モード', exact: true }).count(), 0, 'non-admin has no inspector entry');
await page.keyboard.press('Control+Alt+i');
assert.equal(await page.locator('section[data-ui-inspector-control]').count(), 0, 'non-admin cannot activate the inspector shortcut');
assert.equal(errors.length, 0, errors.join('\n'));
console.log(JSON.stringify({ passed: true, checks: ['admin activation', 'source/component/id', 'private values excluded', 'clipboard fallback', 'keyboard shortcut blocked', '390px panel', 'Escape and normal editing', 'Radix modal preserved with real keyboard input', 'non-admin entry absent'], screenshots: ['/tmp/aao-ui-inspector-wide.png', '/tmp/aao-ui-inspector-narrow.png', '/tmp/aao-ui-inspector-modal.png'] }));
} catch (error) {
console.error(JSON.stringify({ url: page?.url(), errors, body: await page?.locator('body').innerText().catch(() => '') }));
await page?.screenshot({ path: '/tmp/aao-ui-inspector-failure.png', fullPage: true }).catch(() => {});
throw error;
} finally {
await browser?.close();
server.closeAllConnections();
server.close();
}