mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
f1a0b60c6c
* chore: unify product name display to OpenDesign repo-wide Rename the user-facing product name from "Open Design" to "OpenDesign" across documentation, web/desktop UI strings, i18n locales, prompts, plugin/skill/design-system content, changelogs, and specs, matching the landing-page rename already in flight. Deliberately excluded so installed-app identity stays untouched: - packages/release PRODUCT_NAME and everything derived from it (app bundle names, installer/registry identity, updater feeds) - tools/pack, tools/release, apps/packaged, landing page (PR #6983) - mocks/ recorded traces and LICENSE - test fixtures and comments that encode real on-disk install paths (Open Design.app, Program Files\Open Design, AppData paths) Validated with pnpm guard, pnpm typecheck, and targeted web/desktop/host test runs over the renamed strings. * fix(web): keep two-letter author monogram for CamelCase brand names authorInitials split words on whitespace only, so the renamed single-token author name "OpenDesign" produced a one-letter monogram. Treat lower-to-upper CamelCase boundaries as word breaks so the avatar fallback stays "OD". * fix(release): restore spaced Windows source artifact names in win.ps1 tools/pack still derives builder outputs from the unchanged PRODUCT_NAME ("Open Design"), so the asset script must look for the spaced filenames. Matches the mac/mac-intel/linux asset scripts, which were already kept on the original names. --------- Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { execFile } from 'node:child_process';
|
|
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import { promisify } from 'node:util';
|
|
|
|
import { expect, test } from '@/playwright/suite';
|
|
import type { Page } from '@playwright/test';
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const STORAGE_KEY = 'open-design:config';
|
|
|
|
test.describe.configure({ timeout: 45_000 });
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.addInitScript((key) => {
|
|
window.localStorage.setItem(
|
|
key,
|
|
JSON.stringify({
|
|
mode: 'daemon',
|
|
apiKey: '',
|
|
baseUrl: 'https://api.anthropic.com',
|
|
model: 'claude-sonnet-4-5',
|
|
agentId: 'mock',
|
|
skillId: null,
|
|
designSystemId: null,
|
|
onboardingCompleted: true,
|
|
agentModels: {},
|
|
privacyDecisionAt: 1,
|
|
telemetry: { metrics: false, content: false, artifactManifest: false },
|
|
}),
|
|
);
|
|
}, STORAGE_KEY);
|
|
});
|
|
|
|
test('[P1] diagnostics export zip includes the primary daemon, web, and desktop logs', async ({ page }) => {
|
|
await gotoEntryHome(page);
|
|
|
|
const response = await page.request.get('/api/diagnostics/export');
|
|
expect(response.ok(), await response.text()).toBeTruthy();
|
|
expect(response.headers()['content-type']).toContain('application/zip');
|
|
|
|
const tmpRoot = await mkdtemp(path.join(tmpdir(), 'od-diagnostics-e2e-'));
|
|
try {
|
|
const zipPath = path.join(tmpRoot, 'diagnostics.zip');
|
|
await writeFile(zipPath, Buffer.from(await response.body()));
|
|
|
|
const names = await unzipList(zipPath);
|
|
expect(names).toEqual(expect.arrayContaining([
|
|
'summary/manifest.json',
|
|
'logs/daemon/latest.log',
|
|
'logs/web/latest.log',
|
|
'logs/desktop/latest.log',
|
|
]));
|
|
|
|
const manifest = JSON.parse(await unzipRead(zipPath, 'summary/manifest.json')) as {
|
|
files?: Array<{ name?: string; missing?: boolean }>;
|
|
};
|
|
const manifestNames = new Set((manifest.files ?? []).map((file) => file.name).filter(Boolean));
|
|
expect(manifestNames.has('logs/daemon/latest.log')).toBe(true);
|
|
expect(manifestNames.has('logs/web/latest.log')).toBe(true);
|
|
expect(manifestNames.has('logs/desktop/latest.log')).toBe(true);
|
|
|
|
const daemonLog = await unzipRead(zipPath, 'logs/daemon/latest.log');
|
|
const webLog = await unzipRead(zipPath, 'logs/web/latest.log');
|
|
const desktopLog = await unzipRead(zipPath, 'logs/desktop/latest.log');
|
|
expect(daemonLog.length).toBeGreaterThan(0);
|
|
expect(webLog.length).toBeGreaterThan(0);
|
|
expect(desktopLog.length).toBeGreaterThan(0);
|
|
} finally {
|
|
await rm(tmpRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
async function gotoEntryHome(page: Page) {
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
await waitForLoadingToClear(page);
|
|
const privacyDialog = page.getByRole('dialog').filter({ hasText: 'Help us improve OpenDesign' });
|
|
if (await privacyDialog.isVisible().catch(() => false)) {
|
|
await privacyDialog.getByRole('button', { name: /I get it|not now|got it|don't share/i }).click();
|
|
}
|
|
await expect(page.getByTestId('home-hero')).toBeVisible();
|
|
}
|
|
|
|
async function waitForLoadingToClear(page: Page) {
|
|
await expect(page.getByText('Loading OpenDesign…')).toHaveCount(0, { timeout: 15_000 });
|
|
}
|
|
|
|
async function unzipList(zipPath: string): Promise<string[]> {
|
|
const { stdout } = await execFileAsync('unzip', ['-Z1', zipPath]);
|
|
return stdout
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0);
|
|
}
|
|
|
|
async function unzipRead(zipPath: string, entryName: string): Promise<string> {
|
|
const { stdout } = await execFileAsync('unzip', ['-p', zipPath, entryName], {
|
|
encoding: 'utf8',
|
|
maxBuffer: 8 * 1024 * 1024,
|
|
});
|
|
return stdout;
|
|
}
|