feat(app): checkout diff pane styling and e2e test support
- Style diff file cards edge-to-edge with single divider between files - Remove monospace font from filenames for better readability - Add checkout-ship e2e test for app - Export Page type from e2e fixtures - Add checkout query hooks for diff, status, and PR status
This commit is contained in:
@@ -151,17 +151,76 @@ export const openSettings = async (page: Page) => {
|
||||
export const setWorkingDirectory = async (page: Page, directory: string) => {
|
||||
const workingDirectoryLabel = page.getByText('WORKING DIRECTORY', { exact: true }).first();
|
||||
await expect(workingDirectoryLabel).toBeVisible();
|
||||
await workingDirectoryLabel.click();
|
||||
|
||||
const input = page.getByRole('textbox', { name: '/path/to/project' });
|
||||
await expect(input).toBeVisible();
|
||||
const worktreePicker = page.getByTestId('worktree-attach-picker');
|
||||
const worktreeSheetTitle = page.getByText('Select worktree', { exact: true });
|
||||
const closeBottomSheet = async () => {
|
||||
const bottomSheetBackdrop = page
|
||||
.getByRole('button', { name: 'Bottom sheet backdrop' })
|
||||
.first();
|
||||
const bottomSheetHandle = page
|
||||
.getByRole('slider', { name: 'Bottom sheet handle' })
|
||||
.first();
|
||||
for (let attempt = 0; attempt < 3; attempt += 1) {
|
||||
if (!(await bottomSheetBackdrop.isVisible())) {
|
||||
return;
|
||||
}
|
||||
await bottomSheetBackdrop.click({ force: true });
|
||||
await page.keyboard.press('Escape').catch(() => undefined);
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
if (await bottomSheetBackdrop.isVisible()) {
|
||||
const box = await bottomSheetHandle.boundingBox();
|
||||
if (box) {
|
||||
const startX = box.x + box.width / 2;
|
||||
const startY = box.y + box.height / 2;
|
||||
await page.mouse.move(startX, startY);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(startX, startY + 400);
|
||||
await page.mouse.up();
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
}
|
||||
};
|
||||
const closeWorktreeSheetIfOpen = async () => {
|
||||
if (!(await worktreeSheetTitle.isVisible()) && !(await worktreePicker.isVisible())) {
|
||||
return;
|
||||
}
|
||||
const attachToggle = page.getByTestId('worktree-attach-toggle');
|
||||
if (await attachToggle.isVisible()) {
|
||||
await attachToggle.click({ force: true });
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
await closeBottomSheet();
|
||||
};
|
||||
|
||||
await closeWorktreeSheetIfOpen();
|
||||
|
||||
if (!(await input.isVisible())) {
|
||||
await closeBottomSheet();
|
||||
await workingDirectoryLabel.click({ force: true });
|
||||
if (!(await input.isVisible())) {
|
||||
await closeBottomSheet();
|
||||
await workingDirectoryLabel.click({ force: true });
|
||||
}
|
||||
await expect(input).toBeVisible();
|
||||
}
|
||||
|
||||
await input.fill(directory);
|
||||
await input.press('Enter');
|
||||
|
||||
const useOption = page.getByText(`Use "${directory}"`);
|
||||
await expect(useOption).toBeVisible();
|
||||
await useOption.click();
|
||||
await expect(page.getByText(directory, { exact: true })).toBeVisible();
|
||||
await useOption.click({ force: true });
|
||||
const normalizedDirectory = directory.startsWith('/var/')
|
||||
? `/private${directory}`
|
||||
: directory;
|
||||
const workingDirectoryContainer = workingDirectoryLabel.locator('..');
|
||||
await expect.poll(async () => {
|
||||
const text = await workingDirectoryContainer.innerText();
|
||||
return text.includes(directory) || text.includes(normalizedDirectory);
|
||||
}).toBe(true);
|
||||
};
|
||||
|
||||
export const ensureHostSelected = async (page: Page) => {
|
||||
|
||||
@@ -5,20 +5,57 @@ import path from 'node:path';
|
||||
|
||||
type TempRepo = {
|
||||
path: string;
|
||||
owner?: string;
|
||||
name?: string;
|
||||
cleanup: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const createTempGitRepo = async (prefix = 'paseo-e2e-'): Promise<TempRepo> => {
|
||||
export const createTempGitRepo = async (
|
||||
prefix = 'paseo-e2e-',
|
||||
options?: { withRemote?: boolean }
|
||||
): Promise<TempRepo> => {
|
||||
const repoPath = await mkdtemp(path.join(tmpdir(), prefix));
|
||||
const repoName = `paseo-e2e-${Date.now().toString(36)}-${Math.random()
|
||||
.toString(36)
|
||||
.slice(2, 8)}`;
|
||||
let owner: string | undefined;
|
||||
const withRemote = options?.withRemote ?? false;
|
||||
|
||||
execSync('git init', { cwd: repoPath, stdio: 'ignore' });
|
||||
execSync('git init -b main', { cwd: repoPath, stdio: 'ignore' });
|
||||
execSync('git config user.email "e2e@paseo.test"', { cwd: repoPath, stdio: 'ignore' });
|
||||
execSync('git config user.name "Paseo E2E"', { cwd: repoPath, stdio: 'ignore' });
|
||||
await writeFile(path.join(repoPath, 'README.md'), '# Temp Repo\n');
|
||||
execSync('git add README.md', { cwd: repoPath, stdio: 'ignore' });
|
||||
execSync('git commit -m "Initial commit"', { cwd: repoPath, stdio: 'ignore' });
|
||||
|
||||
if (withRemote) {
|
||||
try {
|
||||
owner = execSync('gh api user -q .login', { encoding: 'utf8' }).trim();
|
||||
execSync(
|
||||
`gh repo create ${repoName} --private --confirm --source=. --remote=origin --push`,
|
||||
{
|
||||
cwd: repoPath,
|
||||
stdio: 'ignore',
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
await rm(repoPath, { recursive: true, force: true });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
path: repoPath,
|
||||
owner,
|
||||
name: repoName,
|
||||
cleanup: async () => {
|
||||
if (owner && withRemote) {
|
||||
try {
|
||||
execSync(`gh repo delete ${owner}/${repoName} --yes`, { stdio: 'ignore' });
|
||||
} catch {
|
||||
// Best-effort cleanup
|
||||
}
|
||||
}
|
||||
await rm(repoPath, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user