mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
* feat(terminal): Add core infrastructure for custom terminal configurations - Add TerminalConfig types to settings schema (global & project-specific) - Create RC generator with hex-to-xterm-256 color mapping - Create RC file manager for .automaker/terminal/ directory - Add terminal theme color data (40 themes) to platform package - Integrate terminal config injection into TerminalService - Support bash, zsh, and sh with proper env var injection (BASH_ENV, ZDOTDIR, ENV) - Add onThemeChange hook for theme synchronization Part of custom terminal configurations feature implementation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat(terminal): Wire terminal service with settings service - Pass SettingsService to TerminalService constructor - Initialize terminal service with settings service dependency - Enable terminal config injection to work with actual settings This completes Steps 1-4 of the terminal configuration plan: - RC Generator (color mapping, prompt formats) - RC File Manager (file I/O, atomic writes) - Settings Schema (GlobalSettings + ProjectSettings) - Terminal Service Integration (env var injection) Next steps: Settings UI and theme change hooks. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat(terminal): Add Settings UI and theme change synchronization Complete Steps 5 & 6 of terminal configuration implementation: Settings UI Components: - Add PromptPreview component with live theme-aware rendering - Add TerminalConfigSection with comprehensive controls: * Enable/disable toggle with confirmation dialog * Custom prompt toggle * Prompt format selector (4 formats) * Git branch/status toggles * Custom aliases textarea * Custom env vars key-value editor with validation * Info box explaining behavior - Integrate into existing TerminalSection Theme Change Hook: - Add theme detection in update-global settings route - Regenerate RC files for all projects when theme changes - Skip projects with terminal config disabled - Error handling with per-project logging - Inject terminal service with settings service dependency This completes the full terminal configuration feature: ✓ RC Generator (color mapping, prompts) ✓ RC File Manager (file I/O, versioning) ✓ Settings Schema (types, defaults) ✓ Terminal Service Integration (env vars, PTY spawn) ✓ Settings UI (comprehensive controls, preview) ✓ Theme Synchronization (automatic RC regeneration) New terminals will use custom prompts matching app theme. Existing terminals unaffected. User RC files preserved. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(terminal): Add error handling and explicit field mapping for terminal config - Add try-catch block to handleToggleEnabled - Explicitly set all required terminalConfig fields - Add console logging for debugging - Show error toast if update fails - Include rcFileVersion: 1 in config object This should fix the issue where the toggle doesn't enable after clicking OK in the confirmation dialog. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(terminal): Use React Query mutation hook for settings updates The issue was that `updateGlobalSettings` doesn't exist in the app store. The correct pattern is to use the `useUpdateGlobalSettings` hook from use-settings-mutations.ts, which is a React Query mutation. Changes: - Import useUpdateGlobalSettings from mutations hook - Use mutation.mutate() instead of direct function call - Add proper onSuccess/onError callbacks - Remove async/await pattern (React Query handles this) This fixes the toggle not enabling after clicking OK in the confirmation dialog. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix(terminal): Use React Query hook for globalSettings instead of store The root cause: Component was reading globalSettings from the app store, which doesn't update reactively when the mutation completes. Solution: Use useGlobalSettings() React Query hook which: - Automatically refetches when the mutation invalidates the cache - Triggers re-render with updated data - Makes the toggle reflect the new state Now the flow is: 1. User clicks toggle → confirmation dialog 2. Click OK → mutation.mutate() called 3. Mutation succeeds → invalidates queryKeys.settings.global() 4. Query refetches → component re-renders with new globalSettings 5. Toggle shows enabled state ✓ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * debug(terminal): Add detailed logging for terminal config application Add logging to track: - When terminal config check happens - CWD being used - Global and project enabled states - Effective enabled state This will help diagnose why RC files aren't being generated when opening terminals in Automaker. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix terminal rc updates and bash rcfile loading * feat(terminal): add banner on shell start * feat(terminal): colorize banner per theme * chore(terminal): bump rc version for banner colors * feat(terminal): match banner colors to launcher * feat(terminal): add prompt customization controls * feat: integrate oh-my-posh prompt themes * fix: resolve oh-my-posh theme path * fix: correct oh-my-posh config invocation * docs: add terminal theme screenshot * fix: address review feedback and stabilize e2e test * ui: split terminal config into separate card * fix: enable cross-platform Warp terminal detection - Remove macOS-only platform restriction for Warp - Add Linux CLI alias 'warp-terminal' (primary on Linux) - Add CLI launch handler using --cwd flag - Fixes issue where Warp was not detected on Linux systems Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
146 lines
5.0 KiB
TypeScript
146 lines
5.0 KiB
TypeScript
/**
|
|
* Edit Feature E2E Test
|
|
*
|
|
* Happy path: Edit an existing feature's description and verify changes persist
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import {
|
|
createTempDirPath,
|
|
cleanupTempDir,
|
|
setupRealProject,
|
|
waitForNetworkIdle,
|
|
clickAddFeature,
|
|
fillAddFeatureDialog,
|
|
confirmAddFeature,
|
|
clickElement,
|
|
authenticateForTests,
|
|
handleLoginScreenIfPresent,
|
|
} from '../utils';
|
|
|
|
const TEST_TEMP_DIR = createTempDirPath('edit-feature-test');
|
|
|
|
test.describe('Edit Feature', () => {
|
|
let projectPath: string;
|
|
const projectName = `test-project-${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(automakerDir, { recursive: true });
|
|
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 e2e testing.`
|
|
);
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
cleanupTempDir(TEST_TEMP_DIR);
|
|
});
|
|
|
|
test('should edit an existing feature description', async ({ page }) => {
|
|
const originalDescription = `Original feature ${Date.now()}`;
|
|
const updatedDescription = `Updated feature ${Date.now()}`;
|
|
|
|
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 });
|
|
await expect(page.locator('[data-testid="kanban-column-backlog"]')).toBeVisible({
|
|
timeout: 5000,
|
|
});
|
|
|
|
// Create a feature first
|
|
await clickAddFeature(page);
|
|
await fillAddFeatureDialog(page, originalDescription);
|
|
await confirmAddFeature(page);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Wait for the feature to appear in the backlog
|
|
await expect(async () => {
|
|
const backlogColumn = page.locator('[data-testid="kanban-column-backlog"]');
|
|
const featureCard = backlogColumn.locator('[data-testid^="kanban-card-"]').filter({
|
|
hasText: originalDescription,
|
|
});
|
|
expect(await featureCard.count()).toBeGreaterThan(0);
|
|
}).toPass({ timeout: 20000 });
|
|
|
|
// Get the feature ID from the card
|
|
const featureCard = page
|
|
.locator('[data-testid="kanban-column-backlog"]')
|
|
.locator('[data-testid^="kanban-card-"]')
|
|
.filter({ hasText: originalDescription })
|
|
.first();
|
|
const cardTestId = await featureCard.getAttribute('data-testid');
|
|
const featureId = cardTestId?.replace('kanban-card-', '');
|
|
|
|
// Collapse the sidebar first to avoid it intercepting clicks
|
|
const collapseSidebarButton = page.locator('button:has-text("Collapse sidebar")');
|
|
if (await collapseSidebarButton.isVisible()) {
|
|
await collapseSidebarButton.click();
|
|
await page.waitForTimeout(300); // Wait for sidebar animation
|
|
}
|
|
|
|
// Click the edit button on the card using JavaScript click to bypass pointer interception
|
|
const editButton = page.locator(`[data-testid="edit-backlog-${featureId}"]`);
|
|
await expect(editButton).toBeVisible({ timeout: 5000 });
|
|
await editButton.evaluate((el) => (el as HTMLElement).click());
|
|
|
|
// Wait for edit dialog to appear
|
|
await expect(page.locator('[data-testid="edit-feature-dialog"]')).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Update the description - the input is inside the DescriptionImageDropZone
|
|
const descriptionInput = page
|
|
.locator('[data-testid="edit-feature-dialog"]')
|
|
.getByPlaceholder('Describe the feature...');
|
|
await expect(descriptionInput).toBeVisible({ timeout: 5000 });
|
|
await descriptionInput.fill(updatedDescription);
|
|
|
|
// Save changes
|
|
await clickElement(page, 'confirm-edit-feature');
|
|
|
|
// Wait for dialog to close
|
|
await page.waitForFunction(
|
|
() => !document.querySelector('[data-testid="edit-feature-dialog"]'),
|
|
{ timeout: 5000 }
|
|
);
|
|
|
|
// Verify the updated description appears in the card
|
|
await expect(async () => {
|
|
const backlogColumn = page.locator('[data-testid="kanban-column-backlog"]');
|
|
const updatedCard = backlogColumn.locator('[data-testid^="kanban-card-"]').filter({
|
|
hasText: updatedDescription,
|
|
});
|
|
expect(await updatedCard.count()).toBeGreaterThan(0);
|
|
}).toPass({ timeout: 10000 });
|
|
});
|
|
});
|