mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
feat: enhance ESLint configuration and improve component error handling
- Updated ESLint configuration to include support for `.mjs` and `.cjs` file types, adding necessary global variables for Node.js and browser environments. - Introduced a new `vite-env.d.ts` file to define environment variables for Vite, improving type safety. - Refactored error handling in `file-browser-dialog.tsx`, `description-image-dropzone.tsx`, and `feature-image-upload.tsx` to omit error parameters, simplifying the catch blocks. - Removed unused bug report button functionality from the sidebar, streamlining the component structure. - Adjusted various components to improve code readability and maintainability, including updates to type imports and component props. These changes aim to enhance the development experience by improving linting support and simplifying error handling across components.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
resetFixtureSpec,
|
||||
setupProjectWithFixture,
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
fillInput,
|
||||
waitForNetworkIdle,
|
||||
waitForElement,
|
||||
} from "./utils";
|
||||
} from './utils';
|
||||
|
||||
test.describe("Spec Editor Persistence", () => {
|
||||
test.describe('Spec Editor Persistence', () => {
|
||||
test.beforeEach(async () => {
|
||||
// Reset the fixture spec file to original content before each test
|
||||
resetFixtureSpec();
|
||||
@@ -25,7 +25,7 @@ test.describe("Spec Editor Persistence", () => {
|
||||
resetFixtureSpec();
|
||||
});
|
||||
|
||||
test("should open project, edit spec, save, and persist changes after refresh", async ({
|
||||
test('should open project, edit spec, save, and persist changes after refresh', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Use the resolved fixture path
|
||||
@@ -35,33 +35,33 @@ test.describe("Spec Editor Persistence", () => {
|
||||
await setupProjectWithFixture(page, fixturePath);
|
||||
|
||||
// Step 2: Navigate to the app
|
||||
await page.goto("/");
|
||||
await page.goto('/');
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Step 3: Verify we're on the dashboard with the project loaded
|
||||
// The sidebar should show the project selector
|
||||
const sidebar = await getByTestId(page, "sidebar");
|
||||
await sidebar.waitFor({ state: "visible", timeout: 10000 });
|
||||
const sidebar = await getByTestId(page, 'sidebar');
|
||||
await sidebar.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Step 4: Click on the Spec Editor in the sidebar
|
||||
await navigateToSpecEditor(page);
|
||||
|
||||
// Step 5: Wait for the spec view to load (not empty state)
|
||||
await waitForElement(page, "spec-view", { timeout: 10000 });
|
||||
await waitForElement(page, 'spec-view', { timeout: 10000 });
|
||||
|
||||
// Step 6: Wait for the spec editor to load
|
||||
const specEditor = await getByTestId(page, "spec-editor");
|
||||
await specEditor.waitFor({ state: "visible", timeout: 10000 });
|
||||
const specEditor = await getByTestId(page, 'spec-editor');
|
||||
await specEditor.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Step 7: Wait for CodeMirror to initialize (it has a .cm-content element)
|
||||
await specEditor.locator(".cm-content").waitFor({ state: "visible", timeout: 10000 });
|
||||
await specEditor.locator('.cm-content').waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Step 8: Modify the editor content to "hello world"
|
||||
await setEditorContent(page, "hello world");
|
||||
await setEditorContent(page, 'hello world');
|
||||
|
||||
// Verify content was set before saving
|
||||
const contentBeforeSave = await getEditorContent(page);
|
||||
expect(contentBeforeSave.trim()).toBe("hello world");
|
||||
expect(contentBeforeSave.trim()).toBe('hello world');
|
||||
|
||||
// Step 9: Click the save button and wait for save to complete
|
||||
await clickSaveButton(page);
|
||||
@@ -72,14 +72,16 @@ test.describe("Spec Editor Persistence", () => {
|
||||
|
||||
// Step 11: Navigate back to the spec editor
|
||||
// After reload, we need to wait for the app to initialize
|
||||
await waitForElement(page, "sidebar", { timeout: 10000 });
|
||||
await waitForElement(page, 'sidebar', { timeout: 10000 });
|
||||
|
||||
// Navigate to spec editor again
|
||||
await navigateToSpecEditor(page);
|
||||
|
||||
// Wait for CodeMirror to be ready
|
||||
const specEditorAfterReload = await getByTestId(page, "spec-editor");
|
||||
await specEditorAfterReload.locator(".cm-content").waitFor({ state: "visible", timeout: 10000 });
|
||||
const specEditorAfterReload = await getByTestId(page, 'spec-editor');
|
||||
await specEditorAfterReload
|
||||
.locator('.cm-content')
|
||||
.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Wait for CodeMirror content to update with the loaded spec
|
||||
// The spec might need time to load into the editor after page reload
|
||||
@@ -91,11 +93,11 @@ test.describe("Spec Editor Persistence", () => {
|
||||
try {
|
||||
const contentElement = page.locator('[data-testid="spec-editor"] .cm-content');
|
||||
const text = await contentElement.textContent();
|
||||
if (text && text.trim() === "hello world") {
|
||||
if (text && text.trim() === 'hello world') {
|
||||
contentMatches = true;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Element might not be ready yet, continue
|
||||
}
|
||||
|
||||
@@ -111,20 +113,20 @@ test.describe("Spec Editor Persistence", () => {
|
||||
(expectedContent) => {
|
||||
const contentElement = document.querySelector('[data-testid="spec-editor"] .cm-content');
|
||||
if (!contentElement) return false;
|
||||
const text = (contentElement.textContent || "").trim();
|
||||
const text = (contentElement.textContent || '').trim();
|
||||
return text === expectedContent;
|
||||
},
|
||||
"hello world",
|
||||
'hello world',
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
}
|
||||
|
||||
// Step 12: Verify the content was persisted
|
||||
const persistedContent = await getEditorContent(page);
|
||||
expect(persistedContent.trim()).toBe("hello world");
|
||||
expect(persistedContent.trim()).toBe('hello world');
|
||||
});
|
||||
|
||||
test("should handle opening project via Open Project button and file browser", async ({
|
||||
test('should handle opening project via Open Project button and file browser', async ({
|
||||
page,
|
||||
}) => {
|
||||
// This test covers the flow of:
|
||||
@@ -139,49 +141,47 @@ test.describe("Spec Editor Persistence", () => {
|
||||
state: {
|
||||
projects: [],
|
||||
currentProject: null,
|
||||
currentView: "welcome",
|
||||
theme: "dark",
|
||||
currentView: 'welcome',
|
||||
theme: 'dark',
|
||||
sidebarOpen: true,
|
||||
apiKeys: { anthropic: "", google: "" },
|
||||
apiKeys: { anthropic: '', google: '' },
|
||||
chatSessions: [],
|
||||
chatHistoryOpen: false,
|
||||
maxConcurrency: 3,
|
||||
},
|
||||
version: 0,
|
||||
};
|
||||
localStorage.setItem("automaker-storage", JSON.stringify(mockState));
|
||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
||||
|
||||
// Mark setup as complete
|
||||
const setupState = {
|
||||
state: {
|
||||
isFirstRun: false,
|
||||
setupComplete: true,
|
||||
currentStep: "complete",
|
||||
currentStep: 'complete',
|
||||
skipClaudeSetup: false,
|
||||
},
|
||||
version: 0,
|
||||
};
|
||||
localStorage.setItem("automaker-setup", JSON.stringify(setupState));
|
||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
||||
});
|
||||
|
||||
// Navigate to the app
|
||||
await page.goto("/");
|
||||
await page.goto('/');
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Wait for the sidebar to be visible
|
||||
const sidebar = await getByTestId(page, "sidebar");
|
||||
await sidebar.waitFor({ state: "visible", timeout: 10000 });
|
||||
const sidebar = await getByTestId(page, 'sidebar');
|
||||
await sidebar.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Click the Open Project button
|
||||
const openProjectButton = await getByTestId(page, "open-project-button");
|
||||
const openProjectButton = await getByTestId(page, 'open-project-button');
|
||||
|
||||
// Check if the button is visible (it might not be in collapsed sidebar)
|
||||
const isButtonVisible = await openProjectButton
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
const isButtonVisible = await openProjectButton.isVisible().catch(() => false);
|
||||
|
||||
if (isButtonVisible) {
|
||||
await clickElement(page, "open-project-button");
|
||||
await clickElement(page, 'open-project-button');
|
||||
|
||||
// The file browser dialog should open
|
||||
// Note: In web mode, this might use the FileBrowserDialog component
|
||||
@@ -200,10 +200,10 @@ test.describe("Spec Editor Persistence", () => {
|
||||
|
||||
// For now, let's verify the dialog appeared and close it
|
||||
// A full test would navigate through directories
|
||||
console.log("File browser dialog opened successfully");
|
||||
console.log('File browser dialog opened successfully');
|
||||
|
||||
// Press Escape to close the dialog
|
||||
await page.keyboard.press("Escape");
|
||||
await page.keyboard.press('Escape');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ test.describe("Spec Editor Persistence", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Spec Editor - Full Open Project Flow", () => {
|
||||
test.describe('Spec Editor - Full Open Project Flow', () => {
|
||||
test.beforeEach(async () => {
|
||||
// Reset the fixture spec file to original content before each test
|
||||
resetFixtureSpec();
|
||||
@@ -232,11 +232,9 @@ test.describe("Spec Editor - Full Open Project Flow", () => {
|
||||
});
|
||||
|
||||
// Skip in CI - file browser navigation is flaky in headless environments
|
||||
test.skip("should open project via file browser, edit spec, and persist", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.skip('should open project via file browser, edit spec, and persist', async ({ page }) => {
|
||||
// Navigate to app first
|
||||
await page.goto("/");
|
||||
await page.goto('/');
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Set up localStorage state (without a current project, but mark setup complete)
|
||||
@@ -247,29 +245,29 @@ test.describe("Spec Editor - Full Open Project Flow", () => {
|
||||
state: {
|
||||
projects: [],
|
||||
currentProject: null,
|
||||
currentView: "welcome",
|
||||
theme: "dark",
|
||||
currentView: 'welcome',
|
||||
theme: 'dark',
|
||||
sidebarOpen: true,
|
||||
apiKeys: { anthropic: "", google: "" },
|
||||
apiKeys: { anthropic: '', google: '' },
|
||||
chatSessions: [],
|
||||
chatHistoryOpen: false,
|
||||
maxConcurrency: 3,
|
||||
},
|
||||
version: 0,
|
||||
};
|
||||
localStorage.setItem("automaker-storage", JSON.stringify(mockState));
|
||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
||||
|
||||
// Mark setup as complete (fallback for when NEXT_PUBLIC_SKIP_SETUP isn't set)
|
||||
const setupState = {
|
||||
state: {
|
||||
isFirstRun: false,
|
||||
setupComplete: true,
|
||||
currentStep: "complete",
|
||||
currentStep: 'complete',
|
||||
skipClaudeSetup: false,
|
||||
},
|
||||
version: 0,
|
||||
};
|
||||
localStorage.setItem("automaker-setup", JSON.stringify(setupState));
|
||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
||||
});
|
||||
|
||||
// Reload to apply the localStorage state
|
||||
@@ -277,69 +275,68 @@ test.describe("Spec Editor - Full Open Project Flow", () => {
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Wait for sidebar
|
||||
await waitForElement(page, "sidebar", { timeout: 10000 });
|
||||
await waitForElement(page, 'sidebar', { timeout: 10000 });
|
||||
|
||||
// Click the Open Project button
|
||||
const openProjectButton = await getByTestId(page, "open-project-button");
|
||||
await openProjectButton.waitFor({ state: "visible", timeout: 10000 });
|
||||
await clickElement(page, "open-project-button");
|
||||
const openProjectButton = await getByTestId(page, 'open-project-button');
|
||||
await openProjectButton.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await clickElement(page, 'open-project-button');
|
||||
|
||||
// Wait for the file browser dialog to open
|
||||
const dialogTitle = page.locator('text="Select Project Directory"');
|
||||
await dialogTitle.waitFor({ state: "visible", timeout: 10000 });
|
||||
await dialogTitle.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Wait for the dialog to fully load (loading to complete)
|
||||
await page.waitForFunction(
|
||||
() => !document.body.textContent?.includes("Loading directories..."),
|
||||
() => !document.body.textContent?.includes('Loading directories...'),
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
|
||||
// Use the path input to directly navigate to the fixture directory
|
||||
const pathInput = await getByTestId(page, "path-input");
|
||||
await pathInput.waitFor({ state: "visible", timeout: 5000 });
|
||||
const pathInput = await getByTestId(page, 'path-input');
|
||||
await pathInput.waitFor({ state: 'visible', timeout: 5000 });
|
||||
|
||||
// Clear the input and type the full path to the fixture
|
||||
await fillInput(page, "path-input", getFixturePath());
|
||||
await fillInput(page, 'path-input', getFixturePath());
|
||||
|
||||
// Click the Go button to navigate to the path
|
||||
await clickElement(page, "go-to-path-button");
|
||||
await clickElement(page, 'go-to-path-button');
|
||||
|
||||
// Wait for loading to complete
|
||||
await page.waitForFunction(
|
||||
() => !document.body.textContent?.includes("Loading directories..."),
|
||||
() => !document.body.textContent?.includes('Loading directories...'),
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
|
||||
// Verify we're in the right directory by checking the path display
|
||||
const pathDisplay = page.locator(".font-mono.text-sm.truncate");
|
||||
await expect(pathDisplay).toContainText("projectA");
|
||||
const pathDisplay = page.locator('.font-mono.text-sm.truncate');
|
||||
await expect(pathDisplay).toContainText('projectA');
|
||||
|
||||
// Click "Select Current Folder" button
|
||||
const selectFolderButton = page.locator(
|
||||
'button:has-text("Select Current Folder")'
|
||||
);
|
||||
const selectFolderButton = page.locator('button:has-text("Select Current Folder")');
|
||||
await selectFolderButton.click();
|
||||
|
||||
// Wait for dialog to close and project to load
|
||||
await page.waitForFunction(
|
||||
() => !document.querySelector('[role="dialog"]'),
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
await page.waitForFunction(() => !document.querySelector('[role="dialog"]'), {
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Navigate to spec editor
|
||||
const specNav = await getByTestId(page, "nav-spec");
|
||||
await specNav.waitFor({ state: "visible", timeout: 10000 });
|
||||
await clickElement(page, "nav-spec");
|
||||
const specNav = await getByTestId(page, 'nav-spec');
|
||||
await specNav.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await clickElement(page, 'nav-spec');
|
||||
|
||||
// Wait for spec view with the editor (not the empty state)
|
||||
await waitForElement(page, "spec-view", { timeout: 10000 });
|
||||
const specEditorForOpenFlow = await getByTestId(page, "spec-editor");
|
||||
await specEditorForOpenFlow.locator(".cm-content").waitFor({ state: "visible", timeout: 10000 });
|
||||
await waitForElement(page, 'spec-view', { timeout: 10000 });
|
||||
const specEditorForOpenFlow = await getByTestId(page, 'spec-editor');
|
||||
await specEditorForOpenFlow
|
||||
.locator('.cm-content')
|
||||
.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Edit the content
|
||||
await setEditorContent(page, "hello world");
|
||||
await setEditorContent(page, 'hello world');
|
||||
|
||||
// Click save button
|
||||
await clickSaveButton(page);
|
||||
@@ -349,15 +346,17 @@ test.describe("Spec Editor - Full Open Project Flow", () => {
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Navigate back to spec editor
|
||||
await specNav.waitFor({ state: "visible", timeout: 10000 });
|
||||
await clickElement(page, "nav-spec");
|
||||
await specNav.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await clickElement(page, 'nav-spec');
|
||||
|
||||
const specEditorAfterRefresh = await getByTestId(page, "spec-editor");
|
||||
await specEditorAfterRefresh.locator(".cm-content").waitFor({ state: "visible", timeout: 10000 });
|
||||
const specEditorAfterRefresh = await getByTestId(page, 'spec-editor');
|
||||
await specEditorAfterRefresh
|
||||
.locator('.cm-content')
|
||||
.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify the content persisted
|
||||
const persistedContent = await getEditorContent(page);
|
||||
expect(persistedContent.trim()).toBe("hello world");
|
||||
expect(persistedContent.trim()).toBe('hello world');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user