- Fix 98 TypeScript errors across 37 files: - Service layer double-unwrapping (subscriptionService, distributionService, gearService) - Self-referencing variables in SearchPageResults - FeedView/ExploreView .posts→.items alignment - useQueueSync Zustand subscribe API - AdminAuditLogsView missing interface fields - Toast proxy type, interceptor type narrowing - 22 unused imports/variables removed - 5 storybook mock data fixes - Align frontend API calls with backend endpoints: - Analytics: useAnalyticsView now calls /creator/analytics/dashboard (was /analytics) - Chat: chatService uses /conversations (was mock data), WS URL from backend token - Dashboard StatsSection: uses real /dashboard API data (was hardcoded zeros) - Settings: suppress 2FA toast error when endpoint unavailable - Fix marketplace products: seed uses 'active' status (was 'published') - Enrich seed: admin follows all creators (feed has content) - Optimize bundle: vendor catch-all 793KB→318KB gzip (-60%) Split into vendor-charts, vendor-emoji, vendor-swagger, vendor-media, etc. - Clean repo: remove ~100 orphaned screenshots, audit reports, logs from root Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import { test, expect } from '@chromatic-com/playwright';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
/**
|
|
* Storybook - All Stories Test
|
|
*
|
|
* Iterates over every story in the built Storybook index and verifies
|
|
* that each story renders without console errors or page errors.
|
|
*
|
|
* Prerequisites:
|
|
* npm run build-storybook
|
|
* npx serve storybook-static -l 6007
|
|
*/
|
|
|
|
const INDEX_PATH = path.join(process.cwd(), 'storybook-static', 'index.json');
|
|
const IFRAME_URL = (id: string) =>
|
|
`/iframe.html?id=${encodeURIComponent(id)}&viewMode=story`;
|
|
const NAV_TIMEOUT_MS = 20000;
|
|
const POST_LOAD_MS = 200;
|
|
|
|
/** Story IDs from built Storybook index (available at load time). */
|
|
function getStoryIds(): string[] {
|
|
if (!fs.existsSync(INDEX_PATH)) return [];
|
|
try {
|
|
const index = JSON.parse(fs.readFileSync(INDEX_PATH, 'utf8'));
|
|
const entries = index.entries ?? {};
|
|
return Object.values(entries)
|
|
.map((e: { id?: string }) => e.id)
|
|
.filter(Boolean) as string[];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** Ignore known benign Storybook/addon or runtime messages. */
|
|
function isIgnoredConsoleError(text: string): boolean {
|
|
const ignored = [
|
|
'ResizeObserver',
|
|
'Warning: ReactDOM.render',
|
|
'Download the React DevTools',
|
|
'sb-manager',
|
|
'sb-addons',
|
|
'sb-common-assets',
|
|
'mockServiceWorker',
|
|
'Failed to load resource: net::ERR_ABORTED',
|
|
'ChunkLoadError',
|
|
'Loading chunk',
|
|
'hydration',
|
|
];
|
|
return ignored.some((s) => text.includes(s));
|
|
}
|
|
|
|
const storyIds = getStoryIds();
|
|
|
|
test.describe('STORYBOOK - ALL STORIES', () => {
|
|
if (storyIds.length === 0) {
|
|
test('run build-storybook first', async () => {
|
|
// No stories found — run npm run build-storybook first.
|
|
});
|
|
return;
|
|
}
|
|
|
|
for (const storyId of storyIds) {
|
|
test(storyId, async ({ page }) => {
|
|
const consoleErrors: string[] = [];
|
|
const pageErrors: string[] = [];
|
|
|
|
page.on('console', (msg) => {
|
|
const type = msg.type();
|
|
if (type === 'error') {
|
|
const text = msg.text();
|
|
if (!isIgnoredConsoleError(text)) consoleErrors.push(text);
|
|
}
|
|
});
|
|
page.on('pageerror', (err) => {
|
|
pageErrors.push(err.message);
|
|
});
|
|
|
|
const response = await page.goto(IFRAME_URL(storyId), {
|
|
waitUntil: 'domcontentloaded',
|
|
timeout: NAV_TIMEOUT_MS,
|
|
});
|
|
expect(response?.status()).toBe(200);
|
|
await page.waitForTimeout(POST_LOAD_MS);
|
|
|
|
const errors = [...pageErrors, ...consoleErrors];
|
|
expect(
|
|
errors,
|
|
errors.length
|
|
? `Story ${storyId}: ${errors.slice(0, 3).join('; ')}`
|
|
: undefined,
|
|
).toHaveLength(0);
|
|
});
|
|
}
|
|
});
|