mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-20 11:03:08 +00:00
Add orphaned features management routes and UI integration (#819)
* test(copilot): add edge case test for error with code field Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Changes from fix/bug-fixes-1-0 * refactor(auto-mode): enhance orphaned feature detection and improve project initialization - Updated detectOrphanedFeatures method to accept preloaded features, reducing redundant disk reads. - Improved project initialization by creating required directories and files in parallel for better performance. - Adjusted planning mode handling in UI components to clarify approval requirements for different modes. - Added refresh functionality for file editor tabs to ensure content consistency with disk state. These changes enhance performance, maintainability, and user experience across the application. * feat(orphaned-features): add orphaned features management routes and UI integration - Introduced new routes for managing orphaned features, including listing, resolving, and bulk resolving. - Updated the UI to include an Orphaned Features section in project settings and navigation. - Enhanced the execution service to support new orphaned feature functionalities. These changes improve the application's capability to handle orphaned features effectively, enhancing user experience and project management. * fix: Normalize line endings and resolve stale dirty states in file editor * chore: Update .gitignore and enhance orphaned feature handling - Added a blank line in .gitignore for better readability. - Introduced a hash to worktree paths in orphaned feature resolution to prevent conflicts. - Added validation for target branch existence during orphaned feature resolution. - Improved prompt formatting in execution service for clarity. - Enhanced error handling in project selector for project initialization failures. - Refactored orphaned features section to improve state management and UI responsiveness. These changes improve code maintainability and user experience when managing orphaned features. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* E2E tests for AgentOutputModal responsive behavior
|
||||
* These tests verify the modal width changes across different screen sizes
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
createTempDirPath,
|
||||
cleanupTempDir,
|
||||
setupRealProject,
|
||||
waitForNetworkIdle,
|
||||
authenticateForTests,
|
||||
handleLoginScreenIfPresent,
|
||||
} from '../../utils';
|
||||
|
||||
const TEST_TEMP_DIR = createTempDirPath('responsive-modal-test');
|
||||
|
||||
/**
|
||||
* Create a verified feature with agent output on disk so the Logs button appears
|
||||
*/
|
||||
function createVerifiedFeature(projectPath: string, featureId: string, description: string): void {
|
||||
const featureDir = path.join(projectPath, '.automaker', 'features', featureId);
|
||||
fs.mkdirSync(featureDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(featureDir, 'agent-output.md'),
|
||||
`## Summary\nFeature implemented successfully.\n\n## Details\n${description}`,
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(featureDir, 'feature.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: featureId,
|
||||
title: description,
|
||||
category: 'default',
|
||||
description,
|
||||
status: 'verified',
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
}
|
||||
|
||||
test.describe('AgentOutputModal Responsive Behavior', () => {
|
||||
let projectPath: string;
|
||||
const projectName = `test-responsive-${Date.now()}`;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
if (!fs.existsSync(TEST_TEMP_DIR)) {
|
||||
fs.mkdirSync(TEST_TEMP_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
projectPath = path.join(TEST_TEMP_DIR, projectName);
|
||||
fs.mkdirSync(projectPath, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(projectPath, 'package.json'),
|
||||
JSON.stringify({ name: projectName, version: '1.0.0' }, null, 2)
|
||||
);
|
||||
|
||||
const automakerDir = path.join(projectPath, '.automaker');
|
||||
fs.mkdirSync(path.join(automakerDir, 'features'), { recursive: true });
|
||||
fs.mkdirSync(path.join(automakerDir, 'context'), { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(automakerDir, 'categories.json'),
|
||||
JSON.stringify({ categories: [] }, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(automakerDir, 'app_spec.txt'),
|
||||
`# ${projectName}\n\nA test project for responsive modal testing.`
|
||||
);
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
cleanupTempDir(TEST_TEMP_DIR);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper: set up project, create a verified feature on disk, navigate to board,
|
||||
* and open the agent output modal via the Logs button.
|
||||
*/
|
||||
async function setupAndOpenModal(page: import('@playwright/test').Page): Promise<string> {
|
||||
const featureId = `responsive-feat-${Date.now()}`;
|
||||
createVerifiedFeature(projectPath, featureId, 'Responsive test feature');
|
||||
|
||||
await setupRealProject(page, projectPath, projectName, { setAsCurrent: true });
|
||||
await authenticateForTests(page);
|
||||
await page.goto('/board');
|
||||
await page.waitForLoadState('load');
|
||||
await handleLoginScreenIfPresent(page);
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
await expect(page.locator('[data-testid="board-view"]')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Wait for the verified feature card to appear
|
||||
const featureCard = page.locator(`[data-testid="kanban-card-${featureId}"]`);
|
||||
await expect(featureCard).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Click the Logs button on the verified feature card to open the output modal
|
||||
const logsButton = page.locator(`[data-testid="view-output-verified-${featureId}"]`);
|
||||
await expect(logsButton).toBeVisible({ timeout: 5000 });
|
||||
await logsButton.click();
|
||||
|
||||
// Wait for modal
|
||||
await expect(page.locator('[data-testid="agent-output-modal"]')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
return featureId;
|
||||
}
|
||||
|
||||
test.describe('Mobile View (< 640px)', () => {
|
||||
test('should use full width on mobile screens', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const modalWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const viewportWidth = await page.evaluate(() => window.innerWidth);
|
||||
|
||||
// Modal should be close to full width (within 2rem = 32px margins)
|
||||
expect(modalWidth).toBeGreaterThan(viewportWidth - 40);
|
||||
});
|
||||
|
||||
test('should have proper max width constraint on mobile', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 320, height: 568 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const classList = await modal.evaluate((el) => el.className);
|
||||
expect(classList).toContain('max-w-[calc(100%-2rem)]');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Small View (640px - 768px)', () => {
|
||||
test('should use 60vw on small screens', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 640, height: 768 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const modalWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const viewportWidth = await page.evaluate(() => window.innerWidth);
|
||||
|
||||
// At 640px (sm breakpoint), width should be ~60vw = 384px
|
||||
const expected60vw = viewportWidth * 0.6;
|
||||
expect(modalWidth).toBeLessThanOrEqual(expected60vw + 5);
|
||||
expect(modalWidth).toBeGreaterThanOrEqual(expected60vw - 5);
|
||||
});
|
||||
|
||||
test('should have 80vh max height on small screens', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 640, height: 768 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const classList = await modal.evaluate((el) => el.className);
|
||||
expect(classList).toContain('sm:max-h-[80vh]');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Tablet View (>= 768px)', () => {
|
||||
test('should use 90vw on tablet screens', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const modalWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const viewportWidth = await page.evaluate(() => window.innerWidth);
|
||||
|
||||
// At 768px (md breakpoint), width should be ~90vw = ~691px
|
||||
const expected90vw = viewportWidth * 0.9;
|
||||
expect(modalWidth).toBeLessThanOrEqual(expected90vw + 5);
|
||||
expect(modalWidth).toBeGreaterThanOrEqual(expected90vw - 5);
|
||||
});
|
||||
|
||||
test('should have 1200px max width on tablet', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const classList = await modal.evaluate((el) => el.className);
|
||||
expect(classList).toContain('md:max-w-[1200px]');
|
||||
});
|
||||
|
||||
test('should have 85vh max height on tablet screens', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const classList = await modal.evaluate((el) => el.className);
|
||||
expect(classList).toContain('md:max-h-[85vh]');
|
||||
});
|
||||
|
||||
test('should maintain correct height on larger tablets', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 1024, height: 1366 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const modalHeight = await modal.evaluate((el) => el.offsetHeight);
|
||||
const viewportHeight = await page.evaluate(() => window.innerHeight);
|
||||
|
||||
// Height should be <= 85vh
|
||||
const expected85vh = viewportHeight * 0.85;
|
||||
expect(modalHeight).toBeLessThanOrEqual(expected85vh + 5);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Responsive Transitions', () => {
|
||||
test('should update modal size when resizing from mobile to tablet', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
// Start with mobile size
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const mobileWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const mobileViewport = 375;
|
||||
|
||||
// Mobile: close to full width
|
||||
expect(mobileWidth).toBeGreaterThan(mobileViewport - 40);
|
||||
|
||||
// Resize to tablet
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const tabletWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const tabletViewport = 768;
|
||||
|
||||
// Tablet: should be ~90vw
|
||||
const expected90vw = tabletViewport * 0.9;
|
||||
expect(tabletWidth).toBeLessThanOrEqual(expected90vw + 5);
|
||||
expect(tabletWidth).toBeGreaterThanOrEqual(expected90vw - 5);
|
||||
});
|
||||
|
||||
test('should update modal size when resizing from tablet to mobile', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
// Start with tablet size
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const tabletWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const tabletViewport = 768;
|
||||
|
||||
// Tablet: ~90vw
|
||||
expect(tabletWidth).toBeLessThanOrEqual(tabletViewport * 0.9 + 5);
|
||||
|
||||
// Resize to mobile
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const mobileWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
const mobileViewport = 375;
|
||||
|
||||
// Mobile: close to full width
|
||||
expect(mobileWidth).toBeGreaterThan(mobileViewport - 40);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Content Responsiveness', () => {
|
||||
test('should display content correctly on tablet view', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
// Modal should be visible
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
await expect(modal).toBeVisible();
|
||||
|
||||
// Description should be visible
|
||||
const description = modal.locator('[data-testid="agent-output-description"]');
|
||||
await expect(description).toBeVisible();
|
||||
});
|
||||
|
||||
test('should maintain readability on tablet with wider width', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 800 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
const modalWidth = await modal.evaluate((el) => el.offsetWidth);
|
||||
|
||||
// At 1200px, max-width is 1200px so modal should not exceed that
|
||||
expect(modalWidth).toBeLessThanOrEqual(1200);
|
||||
expect(modalWidth).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Modal Functionality Across Screens', () => {
|
||||
test('should maintain functionality while resizing', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
// Test on mobile
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="agent-output-modal"]')).toBeVisible();
|
||||
|
||||
// Test on tablet
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
await expect(page.locator('[data-testid="agent-output-modal"]')).toBeVisible();
|
||||
|
||||
// Close modal and verify
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(page.locator('[data-testid="agent-output-modal"]')).not.toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle view mode buttons on tablet', async ({ page }) => {
|
||||
await setupAndOpenModal(page);
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
// Logs button should be visible and clickable
|
||||
const logsButton = page.getByTestId('view-mode-parsed');
|
||||
await expect(logsButton).toBeVisible();
|
||||
|
||||
// Raw button should be visible
|
||||
const rawButton = page.getByTestId('view-mode-raw');
|
||||
await expect(rawButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
244
apps/ui/tests/features/success-log-contrast.spec.ts
Normal file
244
apps/ui/tests/features/success-log-contrast.spec.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* E2E test for success log output contrast improvement
|
||||
* Verifies that success tool output has better visual contrast in the parsed log view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
createTempDirPath,
|
||||
cleanupTempDir,
|
||||
setupRealProject,
|
||||
waitForNetworkIdle,
|
||||
authenticateForTests,
|
||||
handleLoginScreenIfPresent,
|
||||
} from '../utils';
|
||||
|
||||
/**
|
||||
* Create a test feature with agent output for contrast verification
|
||||
*/
|
||||
function createTestFeature(
|
||||
projectPath: string,
|
||||
featureId: string,
|
||||
outputContent: string,
|
||||
title: string = 'Test Success Contrast',
|
||||
description: string = 'Testing success log output contrast'
|
||||
): void {
|
||||
const featureDir = path.join(projectPath, '.automaker', 'features', featureId);
|
||||
fs.mkdirSync(featureDir, { recursive: true });
|
||||
|
||||
// Write agent output
|
||||
fs.writeFileSync(path.join(featureDir, 'agent-output.md'), outputContent, {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
// Write feature metadata with all required fields
|
||||
const featureData = {
|
||||
id: featureId,
|
||||
title,
|
||||
category: 'default',
|
||||
description,
|
||||
status: 'verified',
|
||||
};
|
||||
|
||||
fs.writeFileSync(path.join(featureDir, 'feature.json'), JSON.stringify(featureData, null, 2), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
}
|
||||
|
||||
const TEST_TEMP_DIR = createTempDirPath('success-log-contrast');
|
||||
|
||||
test.describe('Success log output contrast', () => {
|
||||
let projectPath: string;
|
||||
const projectName = `test-contrast-${Date.now()}`;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
if (!fs.existsSync(TEST_TEMP_DIR)) {
|
||||
fs.mkdirSync(TEST_TEMP_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
projectPath = path.join(TEST_TEMP_DIR, projectName);
|
||||
fs.mkdirSync(projectPath, { recursive: true });
|
||||
|
||||
// Create minimal project structure
|
||||
fs.writeFileSync(
|
||||
path.join(projectPath, 'package.json'),
|
||||
JSON.stringify({ name: projectName, version: '1.0.0' }, null, 2)
|
||||
);
|
||||
|
||||
// Create .automaker directory structure
|
||||
const automakerDir = path.join(projectPath, '.automaker');
|
||||
fs.mkdirSync(path.join(automakerDir, 'features'), { recursive: true });
|
||||
fs.mkdirSync(path.join(automakerDir, 'context'), { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(automakerDir, 'categories.json'),
|
||||
JSON.stringify({ categories: [] }, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(automakerDir, 'app_spec.txt'),
|
||||
`# ${projectName}\n\nA test project for success log contrast verification.`
|
||||
);
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
cleanupTempDir(TEST_TEMP_DIR);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper: set up project, create a verified feature, navigate to board,
|
||||
* and open the agent output modal with the parsed/logs view active.
|
||||
*/
|
||||
async function setupAndOpenLogsView(
|
||||
page: import('@playwright/test').Page,
|
||||
featureId: string,
|
||||
outputContent: string,
|
||||
title: string,
|
||||
description: string
|
||||
): Promise<void> {
|
||||
createTestFeature(projectPath, featureId, outputContent, title, description);
|
||||
|
||||
await setupRealProject(page, projectPath, projectName, { setAsCurrent: true });
|
||||
await authenticateForTests(page);
|
||||
await page.goto('/board');
|
||||
await page.waitForLoadState('load');
|
||||
await handleLoginScreenIfPresent(page);
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
await expect(page.locator('[data-testid="board-view"]')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Wait for the verified feature card to appear
|
||||
const featureCard = page.locator(`[data-testid="kanban-card-${featureId}"]`);
|
||||
await expect(featureCard).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Click the Logs button on the verified feature card
|
||||
const logsButton = page.locator(`[data-testid="view-output-verified-${featureId}"]`);
|
||||
await expect(logsButton).toBeVisible({ timeout: 5000 });
|
||||
await logsButton.click();
|
||||
|
||||
// Wait for modal to open
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// The modal opens in Logs view by default. Verify the Logs tab is active.
|
||||
const parsedButton = page.getByTestId('view-mode-parsed');
|
||||
await expect(parsedButton).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
|
||||
test('should display success log output with improved contrast', async ({ page }) => {
|
||||
const testFeatureId = `test-success-contrast-${Date.now()}`;
|
||||
|
||||
const mockOutput = `## Summary
|
||||
Successfully implemented the feature with improved contrast.
|
||||
|
||||
## Action Phase
|
||||
✓ Created component with proper styling
|
||||
✓ Verified success message contrast is improved
|
||||
✓ All tests passing
|
||||
|
||||
The feature is complete and ready for review.
|
||||
`;
|
||||
|
||||
await setupAndOpenLogsView(
|
||||
page,
|
||||
testFeatureId,
|
||||
mockOutput,
|
||||
'Test Success Contrast',
|
||||
'Testing success log output contrast'
|
||||
);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
|
||||
// Verify the modal shows the parsed log view with log entries
|
||||
// The log viewer should display entries parsed from the agent output
|
||||
// Use .first() because "Summary" appears in both the badge and the content preview
|
||||
await expect(modal.locator('text=Summary').first()).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Verify the description is shown
|
||||
await expect(modal.locator('text=Testing success log output contrast')).toBeVisible();
|
||||
|
||||
// Close modal
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(modal).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should maintain consistency across all log types', async ({ page }) => {
|
||||
const testFeatureId = `test-all-logs-${Date.now()}`;
|
||||
|
||||
const mixedOutput = `## Planning Phase
|
||||
Analyzing requirements and creating implementation plan.
|
||||
|
||||
## Development Phase
|
||||
Creating components and implementing features.
|
||||
|
||||
## Testing Phase
|
||||
Running tests and verifying functionality.
|
||||
|
||||
## Summary
|
||||
Feature implementation complete with all tests passing.
|
||||
`;
|
||||
|
||||
await setupAndOpenLogsView(
|
||||
page,
|
||||
testFeatureId,
|
||||
mixedOutput,
|
||||
'Test All Logs',
|
||||
'Testing all log types'
|
||||
);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
|
||||
// Verify log entries are displayed in the parsed view
|
||||
// Use .first() because "Summary" appears in both the badge and the content preview
|
||||
await expect(modal.locator('text=Summary').first()).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Verify the description is shown
|
||||
await expect(modal.locator('text=Testing all log types')).toBeVisible();
|
||||
|
||||
// Close modal
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(modal).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should have consistent badge styling with improved contrast', async ({ page }) => {
|
||||
const testFeatureId = `test-badge-contrast-${Date.now()}`;
|
||||
|
||||
const badgeOutput = `## Summary
|
||||
✅ Component created successfully
|
||||
✅ Tests passing with improved contrast
|
||||
✅ Ready for deployment
|
||||
|
||||
All tasks completed successfully.
|
||||
`;
|
||||
|
||||
await setupAndOpenLogsView(
|
||||
page,
|
||||
testFeatureId,
|
||||
badgeOutput,
|
||||
'Test Badge Contrast',
|
||||
'Testing badge contrast in success logs'
|
||||
);
|
||||
|
||||
const modal = page.locator('[data-testid="agent-output-modal"]');
|
||||
|
||||
// Verify the parsed log view shows content
|
||||
await expect(modal.locator('text=Summary')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Verify the description is shown
|
||||
await expect(modal.locator('text=Testing badge contrast in success logs')).toBeVisible();
|
||||
|
||||
// Verify the filter badges are displayed (showing log type counts)
|
||||
// The log viewer shows filter badges like "success: 1" to indicate log types
|
||||
const filterSection = modal.locator('button:has-text("success")');
|
||||
if (await filterSection.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
// Success filter badge is present, indicating logs were categorized correctly
|
||||
await expect(filterSection).toBeVisible();
|
||||
}
|
||||
|
||||
// Close modal
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(modal).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user