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:
Kacper
2025-12-21 23:08:08 +01:00
parent 43c93fe19a
commit 26236d3d5b
40 changed files with 2013 additions and 2587 deletions

View File

@@ -1,32 +1,23 @@
import { Page, Locator } from "@playwright/test";
import { Page, Locator } from '@playwright/test';
/**
* Get a kanban card by feature ID
*/
export async function getKanbanCard(
page: Page,
featureId: string
): Promise<Locator> {
export async function getKanbanCard(page: Page, featureId: string): Promise<Locator> {
return page.locator(`[data-testid="kanban-card-${featureId}"]`);
}
/**
* Get a kanban column by its ID
*/
export async function getKanbanColumn(
page: Page,
columnId: string
): Promise<Locator> {
export async function getKanbanColumn(page: Page, columnId: string): Promise<Locator> {
return page.locator(`[data-testid="kanban-column-${columnId}"]`);
}
/**
* Get the width of a kanban column
*/
export async function getKanbanColumnWidth(
page: Page,
columnId: string
): Promise<number> {
export async function getKanbanColumnWidth(page: Page, columnId: string): Promise<number> {
const column = page.locator(`[data-testid="kanban-column-${columnId}"]`);
const box = await column.boundingBox();
return box?.width ?? 0;
@@ -35,19 +26,16 @@ export async function getKanbanColumnWidth(
/**
* Check if a kanban column has CSS columns (masonry) layout
*/
export async function hasKanbanColumnMasonryLayout(
page: Page,
columnId: string
): Promise<boolean> {
export async function hasKanbanColumnMasonryLayout(page: Page, columnId: string): Promise<boolean> {
const column = page.locator(`[data-testid="kanban-column-${columnId}"]`);
const contentDiv = column.locator("> div").nth(1); // Second child is the content area
const contentDiv = column.locator('> div').nth(1); // Second child is the content area
const columnCount = await contentDiv.evaluate((el) => {
const style = window.getComputedStyle(el);
return style.columnCount;
});
return columnCount === "2";
return columnCount === '2';
}
/**
@@ -58,11 +46,8 @@ export async function dragKanbanCard(
featureId: string,
targetColumnId: string
): Promise<void> {
const card = page.locator(`[data-testid="kanban-card-${featureId}"]`);
const dragHandle = page.locator(`[data-testid="drag-handle-${featureId}"]`);
const targetColumn = page.locator(
`[data-testid="kanban-column-${targetColumnId}"]`
);
const targetColumn = page.locator(`[data-testid="kanban-column-${targetColumnId}"]`);
// Perform drag and drop
await dragHandle.dragTo(targetColumn);
@@ -71,15 +56,10 @@ export async function dragKanbanCard(
/**
* Click the view output button on a kanban card
*/
export async function clickViewOutput(
page: Page,
featureId: string
): Promise<void> {
export async function clickViewOutput(page: Page, featureId: string): Promise<void> {
// Try the running version first, then the in-progress version
const runningBtn = page.locator(`[data-testid="view-output-${featureId}"]`);
const inProgressBtn = page.locator(
`[data-testid="view-output-inprogress-${featureId}"]`
);
const inProgressBtn = page.locator(`[data-testid="view-output-inprogress-${featureId}"]`);
if (await runningBtn.isVisible()) {
await runningBtn.click();
@@ -104,10 +84,7 @@ export async function isDragHandleVisibleForFeature(
/**
* Get the drag handle element for a specific feature card
*/
export async function getDragHandleForFeature(
page: Page,
featureId: string
): Promise<Locator> {
export async function getDragHandleForFeature(page: Page, featureId: string): Promise<Locator> {
return page.locator(`[data-testid="drag-handle-${featureId}"]`);
}
@@ -134,9 +111,7 @@ export async function fillAddFeatureDialog(
options?: { branch?: string; category?: string }
): Promise<void> {
// Fill description (using the dropzone textarea)
const descriptionInput = page
.locator('[data-testid="add-feature-dialog"] textarea')
.first();
const descriptionInput = page.locator('[data-testid="add-feature-dialog"] textarea').first();
await descriptionInput.fill(description);
// Fill branch if provided (it's a combobox autocomplete)
@@ -145,36 +120,34 @@ export async function fillAddFeatureDialog(
const otherBranchRadio = page
.locator('[data-testid="feature-radio-group"]')
.locator('[id="feature-other"]');
await otherBranchRadio.waitFor({ state: "visible", timeout: 5000 });
await otherBranchRadio.waitFor({ state: 'visible', timeout: 5000 });
await otherBranchRadio.click();
// Wait for the branch input to appear
await page.waitForTimeout(300);
// Now click on the branch input (autocomplete)
const branchInput = page.locator('[data-testid="feature-input"]');
await branchInput.waitFor({ state: "visible", timeout: 5000 });
await branchInput.waitFor({ state: 'visible', timeout: 5000 });
await branchInput.click();
// Wait for the popover to open
await page.waitForTimeout(300);
// Type in the command input
const commandInput = page.locator("[cmdk-input]");
const commandInput = page.locator('[cmdk-input]');
await commandInput.fill(options.branch);
// Press Enter to select/create the branch
await commandInput.press("Enter");
await commandInput.press('Enter');
// Wait for popover to close
await page.waitForTimeout(200);
}
// Fill category if provided (it's also a combobox autocomplete)
if (options?.category) {
const categoryButton = page.locator(
'[data-testid="feature-category-input"]'
);
const categoryButton = page.locator('[data-testid="feature-category-input"]');
await categoryButton.click();
await page.waitForTimeout(300);
const commandInput = page.locator("[cmdk-input]");
const commandInput = page.locator('[cmdk-input]');
await commandInput.fill(options.category);
await commandInput.press("Enter");
await commandInput.press('Enter');
await page.waitForTimeout(200);
}
}
@@ -185,10 +158,9 @@ export async function fillAddFeatureDialog(
export async function confirmAddFeature(page: Page): Promise<void> {
await page.click('[data-testid="confirm-add-feature"]');
// Wait for dialog to close
await page.waitForFunction(
() => !document.querySelector('[data-testid="add-feature-dialog"]'),
{ timeout: 5000 }
);
await page.waitForFunction(() => !document.querySelector('[data-testid="add-feature-dialog"]'), {
timeout: 5000,
});
}
/**
@@ -218,12 +190,9 @@ export async function getWorktreeSelector(page: Page): Promise<Locator> {
/**
* Click on a branch button in the worktree selector
*/
export async function selectWorktreeBranch(
page: Page,
branchName: string
): Promise<void> {
const branchButton = page.getByRole("button", {
name: new RegExp(branchName, "i"),
export async function selectWorktreeBranch(page: Page, branchName: string): Promise<void> {
const branchButton = page.getByRole('button', {
name: new RegExp(branchName, 'i'),
});
await branchButton.click();
await page.waitForTimeout(500); // Wait for UI to update
@@ -232,9 +201,7 @@ export async function selectWorktreeBranch(
/**
* Get the currently selected branch in the worktree selector
*/
export async function getSelectedWorktreeBranch(
page: Page
): Promise<string | null> {
export async function getSelectedWorktreeBranch(page: Page): Promise<string | null> {
// The main branch button has aria-pressed="true" when selected
const selectedButton = page.locator(
'[data-testid="worktree-selector"] button[aria-pressed="true"]'
@@ -246,12 +213,9 @@ export async function getSelectedWorktreeBranch(
/**
* Check if a branch button is visible in the worktree selector
*/
export async function isWorktreeBranchVisible(
page: Page,
branchName: string
): Promise<boolean> {
const branchButton = page.getByRole("button", {
name: new RegExp(branchName, "i"),
export async function isWorktreeBranchVisible(page: Page, branchName: string): Promise<boolean> {
const branchButton = page.getByRole('button', {
name: new RegExp(branchName, 'i'),
});
return await branchButton.isVisible().catch(() => false);
}

View File

@@ -1,20 +1,19 @@
import { Page, Locator } from "@playwright/test";
import { getByTestId } from "../core/elements";
import { waitForElement } from "../core/waiting";
import { setupFirstRun } from "../project/setup";
import { Page, Locator } from '@playwright/test';
import { getByTestId } from '../core/elements';
import { waitForElement } from '../core/waiting';
/**
* Wait for setup view to be visible
*/
export async function waitForSetupView(page: Page): Promise<Locator> {
return waitForElement(page, "setup-view", { timeout: 10000 });
return waitForElement(page, 'setup-view', { timeout: 10000 });
}
/**
* Click "Get Started" button on setup welcome step
*/
export async function clickSetupGetStarted(page: Page): Promise<void> {
const button = await getByTestId(page, "setup-start-button");
const button = await getByTestId(page, 'setup-start-button');
await button.click();
}
@@ -22,7 +21,7 @@ export async function clickSetupGetStarted(page: Page): Promise<void> {
* Click continue on Claude setup step
*/
export async function clickClaudeContinue(page: Page): Promise<void> {
const button = await getByTestId(page, "claude-next-button");
const button = await getByTestId(page, 'claude-next-button');
await button.click();
}
@@ -30,46 +29,40 @@ export async function clickClaudeContinue(page: Page): Promise<void> {
* Click finish on setup complete step
*/
export async function clickSetupFinish(page: Page): Promise<void> {
const button = await getByTestId(page, "setup-finish-button");
const button = await getByTestId(page, 'setup-finish-button');
await button.click();
}
/**
* Enter Anthropic API key in setup
*/
export async function enterAnthropicApiKey(
page: Page,
apiKey: string
): Promise<void> {
export async function enterAnthropicApiKey(page: Page, apiKey: string): Promise<void> {
// Click "Use Anthropic API Key Instead" button
const useApiKeyButton = await getByTestId(page, "use-api-key-button");
const useApiKeyButton = await getByTestId(page, 'use-api-key-button');
await useApiKeyButton.click();
// Enter the API key
const input = await getByTestId(page, "anthropic-api-key-input");
const input = await getByTestId(page, 'anthropic-api-key-input');
await input.fill(apiKey);
// Click save button
const saveButton = await getByTestId(page, "save-anthropic-key-button");
const saveButton = await getByTestId(page, 'save-anthropic-key-button');
await saveButton.click();
}
/**
* Enter OpenAI API key in setup
*/
export async function enterOpenAIApiKey(
page: Page,
apiKey: string
): Promise<void> {
export async function enterOpenAIApiKey(page: Page, apiKey: string): Promise<void> {
// Click "Enter OpenAI API Key" button
const useApiKeyButton = await getByTestId(page, "use-openai-key-button");
const useApiKeyButton = await getByTestId(page, 'use-openai-key-button');
await useApiKeyButton.click();
// Enter the API key
const input = await getByTestId(page, "openai-api-key-input");
const input = await getByTestId(page, 'openai-api-key-input');
await input.fill(apiKey);
// Click save button
const saveButton = await getByTestId(page, "save-openai-key-button");
const saveButton = await getByTestId(page, 'save-openai-key-button');
await saveButton.click();
}