Initial commit: Brachnha Insight project setup
- Next.js 14+ with App Router and TypeScript - Tailwind CSS and ShadCN UI styling - Zustand state management - Dexie.js for IndexedDB (local-first data) - Auth.js v5 for authentication - BMAD framework integration Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
48
tests/support/factories/history-entry.factory.ts
Normal file
48
tests/support/factories/history-entry.factory.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
export interface ChatSession {
|
||||
id: string;
|
||||
title: string;
|
||||
date: string; // ISO string
|
||||
preview: string;
|
||||
tags: string[];
|
||||
status: 'active' | 'completed';
|
||||
messages: Array<{
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
timestamp: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const createChatSession = (overrides: Partial<ChatSession> = {}): ChatSession => {
|
||||
const timestamp = faker.date.recent().getTime();
|
||||
|
||||
return {
|
||||
id: faker.string.uuid(),
|
||||
title: faker.lorem.sentence(3),
|
||||
date: new Date(timestamp).toISOString(),
|
||||
preview: faker.lorem.sentence(),
|
||||
tags: [faker.word.sample(), faker.word.sample()],
|
||||
status: 'completed',
|
||||
messages: [
|
||||
{
|
||||
id: faker.string.uuid(),
|
||||
role: 'user',
|
||||
content: faker.lorem.paragraph(),
|
||||
timestamp: timestamp - 10000,
|
||||
},
|
||||
{
|
||||
id: faker.string.uuid(),
|
||||
role: 'assistant',
|
||||
content: faker.lorem.paragraph(),
|
||||
timestamp: timestamp,
|
||||
}
|
||||
],
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const createChatSessions = (count: number, overrides: Partial<ChatSession> = {}): ChatSession[] => {
|
||||
return Array.from({ length: count }, () => createChatSession(overrides));
|
||||
};
|
||||
24
tests/support/factories/provider.factory.ts
Normal file
24
tests/support/factories/provider.factory.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
export interface ProviderConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
baseUrl: string;
|
||||
apiKey: string;
|
||||
modelId: string;
|
||||
}
|
||||
|
||||
export const createProviderConfig = (overrides: Partial<ProviderConfig> = {}): ProviderConfig => {
|
||||
return {
|
||||
id: faker.string.uuid(),
|
||||
name: faker.company.name() + ' AI',
|
||||
baseUrl: faker.internet.url(),
|
||||
apiKey: 'sk-' + faker.string.alphanumeric(20),
|
||||
modelId: faker.helpers.arrayElement(['gpt-4', 'claude-3', 'deepseek-chat']),
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const createProviderConfigs = (count: number): ProviderConfig[] => {
|
||||
return Array.from({ length: count }, () => createProviderConfig());
|
||||
};
|
||||
50
tests/support/fixtures/db.fixture.ts
Normal file
50
tests/support/fixtures/db.fixture.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
type DbFixture = {
|
||||
resetDb: () => Promise<void>;
|
||||
seedHistory: (data: any[]) => Promise<void>;
|
||||
getHistory: () => Promise<any[]>;
|
||||
getSyncQueue: () => Promise<any[]>;
|
||||
};
|
||||
|
||||
export const test = base.extend<{ db: DbFixture }>({
|
||||
db: async ({ page }, use) => {
|
||||
const dbFixture = {
|
||||
resetDb: async () => {
|
||||
await page.evaluate(async () => {
|
||||
// Assuming 'Dexie' is globally available or we use indexedDB directly
|
||||
// Using indexedDB directly to be safe as Dexie might be bundled
|
||||
const req = indexedDB.deleteDatabase('Test01DB'); // Match your DB name
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = () => resolve(true);
|
||||
req.onerror = () => reject(req.error);
|
||||
req.onblocked = () => console.warn('Delete DB blocked');
|
||||
});
|
||||
});
|
||||
},
|
||||
seedHistory: async (data: any[]) => {
|
||||
// This is tricky if logic is encapsulated.
|
||||
// We might need to expose a hidden window helper or just rely on UI for seeding in E2E
|
||||
// OR use a specialized seeding script.
|
||||
// For Integration/Component, we might assume we can import the DB client directly if running in same context.
|
||||
// Since playwight runs in browser context, we'd need to expose it.
|
||||
// For now, let's assume we can invoke a window helper if useful, or just implement specific logic
|
||||
await page.evaluate(async (items) => {
|
||||
// We'll need the app to expose a way to seed, or we assume specific DB structure
|
||||
// This requires the app to have the DB initialized.
|
||||
// Implementation deferred to actual test via page.evaluate if needed
|
||||
}, data);
|
||||
},
|
||||
getHistory: async () => {
|
||||
// Placeholder for getting history from DB
|
||||
return [];
|
||||
},
|
||||
getSyncQueue: async () => {
|
||||
// Placeholder
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
await use(dbFixture);
|
||||
},
|
||||
});
|
||||
37
tests/support/fixtures/factories/user-factory.ts
Normal file
37
tests/support/fixtures/factories/user-factory.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
/**
|
||||
* UserFactory
|
||||
*
|
||||
* Handles creation and cleanup of test users.
|
||||
* Note: Since this is a local-first app without a real backend API for user creation yet,
|
||||
* this factory currently generates mock data. adapting to real API calls later.
|
||||
*/
|
||||
export class UserFactory {
|
||||
// In a real app, we would track IDs here for cleanup
|
||||
// private createdUserIds: string[] = [];
|
||||
|
||||
async createUser(overrides = {}) {
|
||||
const user = {
|
||||
id: faker.string.uuid(),
|
||||
email: faker.internet.email(),
|
||||
name: faker.person.fullName(),
|
||||
password: faker.internet.password(),
|
||||
createdAt: new Date().toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
|
||||
// Placeholder: In a real app, you would POST to API here
|
||||
// const response = await fetch(\`\${process.env.API_URL}/users\`, ...);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
// Placeholder: In a real app, you would DELETE users here
|
||||
// for (const id of this.createdUserIds) { ... }
|
||||
|
||||
// For now, no cleanup needed for transient mock data
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
16
tests/support/fixtures/index.ts
Normal file
16
tests/support/fixtures/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { test as base } from '@playwright/test';
|
||||
import { UserFactory } from './factories/user-factory';
|
||||
|
||||
type TestFixtures = {
|
||||
userFactory: UserFactory;
|
||||
};
|
||||
|
||||
export const test = base.extend<TestFixtures>({
|
||||
userFactory: async ({ }, use) => {
|
||||
const factory = new UserFactory();
|
||||
await use(factory);
|
||||
await factory.cleanup();
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from '@playwright/test';
|
||||
27
tests/support/fixtures/offline.fixture.ts
Normal file
27
tests/support/fixtures/offline.fixture.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { test as base, BrowserContext } from '@playwright/test';
|
||||
|
||||
type OfflineFixture = {
|
||||
goOffline: (context: BrowserContext) => Promise<void>;
|
||||
goOnline: (context: BrowserContext) => Promise<void>;
|
||||
};
|
||||
|
||||
export const test = base.extend<{ offlineControl: OfflineFixture }>({
|
||||
offlineControl: async ({ }, use) => {
|
||||
const offlineFixture = {
|
||||
goOffline: async (context: BrowserContext) => {
|
||||
await context.setOffline(true);
|
||||
for (const page of context.pages()) {
|
||||
await page.evaluate(() => window.dispatchEvent(new Event('offline'))).catch(() => { });
|
||||
}
|
||||
},
|
||||
goOnline: async (context: BrowserContext) => {
|
||||
await context.setOffline(false);
|
||||
for (const page of context.pages()) {
|
||||
await page.evaluate(() => window.dispatchEvent(new Event('online'))).catch(() => { });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
await use(offlineFixture);
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user