diff --git a/playwright/app.spec.ts b/playwright/app.spec.ts index 14a2a6f..bdd38b5 100644 --- a/playwright/app.spec.ts +++ b/playwright/app.spec.ts @@ -27,6 +27,8 @@ type PullRequestCreateBody = { base?: string } +type BranchesByRepo = Record + const waitForAppReady = async (page: Page, path = appEntryPath) => { await page.goto(path) await expect(page.getByRole('heading', { name: '@knighted/develop' })).toBeVisible() @@ -147,6 +149,28 @@ const ensureOpenPrDrawerOpen = async (page: Page) => { await expect(page.locator('#github-pr-drawer')).toBeVisible() } +const mockRepositoryBranches = async ( + page: Page, + branchesByRepo: BranchesByRepo = {}, +) => { + await page.route('https://api.github.com/repos/**/branches**', async route => { + const url = new URL(route.request().url()) + const match = url.pathname.match(/^\/repos\/([^/]+)\/([^/]+)\/branches$/) + const repositoryKey = match ? `${match[1]}/${match[2]}` : '' + + const branchNames = + branchesByRepo[repositoryKey] && branchesByRepo[repositoryKey].length > 0 + ? branchesByRepo[repositoryKey] + : ['main'] + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(branchNames.map(name => ({ name }))), + }) + }) +} + const connectByotWithSingleRepo = async (page: Page) => { await page.route('https://api.github.com/user/repos**', async route => { await route.fulfill({ @@ -165,6 +189,10 @@ const connectByotWithSingleRepo = async (page: Page) => { }) }) + await mockRepositoryBranches(page, { + 'knightedcodemonkey/develop': ['main', 'release'], + }) + await page.locator('#github-token-input').fill('github_pat_fake_chat_1234567890') await page.locator('#github-token-add').click() await expect(page.locator('#status')).toHaveText('Loaded 1 writable repositories') @@ -528,6 +556,11 @@ test('BYOT remembers selected repository across reloads', async ({ page }) => { }) }) + await mockRepositoryBranches(page, { + 'knightedcodemonkey/develop': ['main', 'release'], + 'knightedcodemonkey/css': ['main', 'release/1.x'], + }) + await waitForAppReady(page, `${appEntryPath}?feature-ai=true`) await page.locator('#github-token-input').fill('github_pat_fake_1234567890') @@ -577,6 +610,10 @@ test('Open PR drawer confirms and submits component/styles filepaths', async ({ }) }) + await mockRepositoryBranches(page, { + 'knightedcodemonkey/develop': ['main', 'release'], + }) + await page.route( 'https://api.github.com/repos/knightedcodemonkey/develop/git/ref/**', async route => { @@ -717,6 +754,82 @@ test('Open PR drawer confirms and submits component/styles filepaths', async ({ ) }) +test('Open PR drawer base dropdown updates from mocked repo branches', async ({ + page, +}) => { + const branchRequestUrls: string[] = [] + + await page.route('https://api.github.com/user/repos**', async route => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify([ + { + id: 2, + owner: { login: 'knightedcodemonkey' }, + name: 'develop', + full_name: 'knightedcodemonkey/develop', + default_branch: 'main', + permissions: { push: true }, + }, + { + id: 1, + owner: { login: 'knightedcodemonkey' }, + name: 'css', + full_name: 'knightedcodemonkey/css', + default_branch: 'stable', + permissions: { push: true }, + }, + ]), + }) + }) + + await page.route('https://api.github.com/repos/**/branches**', async route => { + const url = route.request().url() + branchRequestUrls.push(url) + + const branchNames = url.includes('/repos/knightedcodemonkey/css/branches') + ? ['stable', 'release/1.x'] + : ['main', 'develop-next'] + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(branchNames.map(name => ({ name }))), + }) + }) + + await waitForAppReady(page, `${appEntryPath}?feature-ai=true`) + + await page.locator('#github-token-input').fill('github_pat_fake_1234567890') + await page.locator('#github-token-add').click() + await expect(page.locator('#status')).toHaveText('Loaded 2 writable repositories') + + await ensureOpenPrDrawerOpen(page) + + const repoSelect = page.locator('#github-pr-repo-select') + const baseSelect = page.locator('#github-pr-base-branch') + + await repoSelect.selectOption('knightedcodemonkey/develop') + await expect(baseSelect).toHaveValue('main') + await expect(baseSelect.locator('option')).toHaveText(['main', 'develop-next']) + + await repoSelect.selectOption('knightedcodemonkey/css') + await expect(baseSelect).toHaveValue('stable') + await expect(baseSelect.locator('option')).toHaveText(['stable', 'release/1.x']) + + expect( + branchRequestUrls.some(url => + url.includes('https://api.github.com/repos/knightedcodemonkey/develop/branches'), + ), + ).toBe(true) + expect( + branchRequestUrls.some(url => + url.includes('https://api.github.com/repos/knightedcodemonkey/css/branches'), + ), + ).toBe(true) +}) + test('Open PR drawer validates unsafe filepaths', async ({ page }) => { await waitForAppReady(page, `${appEntryPath}?feature-ai=true`) await connectByotWithSingleRepo(page) diff --git a/src/index.html b/src/index.html index d7fecba..6c8be3e 100644 --- a/src/index.html +++ b/src/index.html @@ -659,13 +659,13 @@

Open Pull Request

for="github-pr-base-branch" > Base - + aria-label="Pull request base branch" + disabled + > + +