Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
.prompts/

node_modules/
.DS_Store
Expand Down
32 changes: 28 additions & 4 deletions __tests__/e2e/agent-review.spec.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
const { test, expect } = require('./fixtures');
const { test, expect, getSessionsWithRetry } = require('./fixtures');

test.describe('Agent Review Tests', () => {
let SESSION_ID;
let REVIEWABLE_SESSION_ID;

const getEventfulSessionId = async (request, sessions) => {
for (const session of sessions) {
if (!session?.hasEvents || session.eventCount <= 0) {
continue;
}

const response = await request.get(`/api/sessions/${session.id}/events`);
if (!response.ok()) {
continue;
}

const events = await response.json();
if (Array.isArray(events) && events.length > 0) {
return session.id;
}
}

return null;
};

test.beforeAll(async ({ request }) => {
// Get first session ID from API
const response = await request.get('/api/sessions');
const sessions = await response.json();
const sessions = await getSessionsWithRetry(request);
if (sessions.length > 0) {
SESSION_ID = sessions[0].id;
} else {
throw new Error('No sessions available for testing');
}

REVIEWABLE_SESSION_ID = await getEventfulSessionId(request, sessions);
});

test.describe('Agent Review Tab on Analysis Page', () => {
Expand Down Expand Up @@ -203,7 +225,9 @@ test.describe('Agent Review Tests', () => {
// Note: We won't actually generate in CI to avoid API costs
// This test verifies the endpoint exists and accepts requests

const response = await request.post(`/session/${SESSION_ID}/insight`, {
test.skip(!REVIEWABLE_SESSION_ID, 'No eventful session available for agent review generation');

const response = await request.post(`/session/${REVIEWABLE_SESSION_ID}/insight`, {
failOnStatusCode: false // Don't fail on rate limits or feature flags
});

Expand Down
6 changes: 3 additions & 3 deletions __tests__/e2e/api-pagination.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const { test, expect } = require('./fixtures');
const { test, expect, getSessionsWithRetry } = require('./fixtures');

test.describe('API Pagination', () => {
test.beforeEach(async ({ request }) => {
// Verify API is accessible
const response = await request.get('/api/sessions');
expect(response.ok()).toBeTruthy();
const sessions = await getSessionsWithRetry(request);
expect(Array.isArray(sessions)).toBeTruthy();
});

test('should return sessions with load-more endpoint', async ({ request }) => {
Expand Down
28 changes: 8 additions & 20 deletions __tests__/e2e/api.spec.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
const { test, expect } = require('./fixtures');
const { test, expect, getSessionsWithRetry } = require('./fixtures');

test.describe('API Endpoints', () => {
test('GET /api/sessions should return JSON array', async ({ request }) => {
Comment thread
haochenghuang marked this conversation as resolved.
const response = await request.get('/api/sessions');

// Check status
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);

// Check content type
expect(response.headers()['content-type']).toContain('application/json');

// Check body is array
const sessions = await response.json();
const sessions = await getSessionsWithRetry(request);
expect(Array.isArray(sessions)).toBeTruthy();

// Check sessions have required fields
Expand All @@ -27,9 +17,8 @@ test.describe('API Endpoints', () => {

test('GET /api/sessions/:id/events should return events', async ({ request }) => {
// First get a session ID
const sessionsResponse = await request.get('/api/sessions');
const sessions = await sessionsResponse.json();

const sessions = await getSessionsWithRetry(request);

if (sessions.length > 0) {
const sessionId = sessions[0].id;

Expand All @@ -51,14 +40,13 @@ test.describe('API Endpoints', () => {

test('GET /api/sessions should be fast', async ({ request }) => {
const startTime = Date.now();

const response = await request.get('/api/sessions');

const sessions = await getSessionsWithRetry(request);

const endTime = Date.now();
const duration = endTime - startTime;

// Should respond in less than 20 seconds (generous for CI/cold-start/large datasets)
expect(duration).toBeLessThan(20000);
expect(response.ok()).toBeTruthy();
expect(Array.isArray(sessions)).toBeTruthy();
});
});
5 changes: 2 additions & 3 deletions __tests__/e2e/core-functionality.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const { test, expect } = require('./fixtures');
const { test, expect, getSessionsWithRetry } = require('./fixtures');

test.describe('Core Functionality Tests', () => {
let SESSION_ID;

test.beforeAll(async ({ request }) => {
// Get first session ID from API for testing
const response = await request.get('/api/sessions');
const sessions = await response.json();
const sessions = await getSessionsWithRetry(request);
if (sessions.length > 0) {
SESSION_ID = sessions[0].id;
}
Expand Down
38 changes: 25 additions & 13 deletions __tests__/e2e/export.spec.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
const { test, expect } = require('./fixtures');
const { test, expect, getSessionsWithRetry } = require('./fixtures');
const AdmZip = require('adm-zip');

test.describe('Export Tests', () => {
let copilotSessionId, claudeSessionId, piSessionId;

test.beforeAll(async ({ request }) => {
// Get sessions from API to find different source types
const response = await request.get('/api/sessions');
const sessions = await response.json();

// Find sessions by source type
const getEventfulSessionId = async (request, sessions, source) => {
for (const session of sessions) {
if (session.source === 'copilot' && !copilotSessionId) {
copilotSessionId = session.id;
} else if (session.source === 'claude' && !claudeSessionId) {
claudeSessionId = session.id;
} else if (session.source === 'pi-mono' && !piSessionId) {
piSessionId = session.id;
if (session.source !== source || !session?.hasEvents || session.eventCount <= 0) {
continue;
}

const response = await request.get(`/api/sessions/${session.id}/events`);
if (!response.ok()) {
continue;
}

const events = await response.json();
if (Array.isArray(events) && events.length > 0) {
return session.id;
}
}

return null;
};

test.beforeAll(async ({ request }) => {
// Get sessions from API to find different source types
const sessions = await getSessionsWithRetry(request);

copilotSessionId = await getEventfulSessionId(request, sessions, 'copilot');
claudeSessionId = await getEventfulSessionId(request, sessions, 'claude');
piSessionId = await getEventfulSessionId(request, sessions, 'pi-mono');

// At minimum, we need one session
if (!copilotSessionId && !claudeSessionId && !piSessionId) {
throw new Error('No sessions available for testing');
Expand Down
57 changes: 56 additions & 1 deletion __tests__/e2e/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,62 @@ const playwright = require('@playwright/test');

const test = playwright.test;

async function getJsonWithRetry(request, url, options = {}) {
const {
retries = 5,
retryDelayMs = 500,
validate = null
} = options;

let lastError;

for (let attempt = 0; attempt < retries; attempt++) {
try {
const response = await request.get(url);

if (!response.ok()) {
throw new Error(`${url} returned ${response.status()}`);
}

const data = await response.json();

if (typeof validate === 'function') {
validate(data);
}

return data;
} catch (error) {
lastError = error;

if (attempt === retries - 1) {
throw new Error(`Failed to fetch ${url} after ${retries} attempts: ${error.message}`, { cause: error });
}

await new Promise(resolve => setTimeout(resolve, retryDelayMs * (attempt + 1)));
}
}

throw lastError;
}

async function getSessionsWithRetry(request, options = {}) {
return getJsonWithRetry(request, '/api/sessions', {
...options,
validate(data) {
if (!Array.isArray(data)) {
throw new Error('/api/sessions did not return an array of sessions');
}

if (typeof options.validate === 'function') {
options.validate(data);
}
}
});
}

module.exports = {
test,
expect: playwright.expect
expect: playwright.expect,
getJsonWithRetry,
getSessionsWithRetry
};
Loading
Loading