Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions playwright/github-pr-drawer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,179 @@ test('Open PR drawer does not prune saved PR context on repo switch before save'
expect(contexts[0]?.parsed?.componentFilePath).toBe('examples/develop/App.tsx')
})

test('Active PR context disconnect uses local-only confirmation flow', async ({
page,
}) => {
let closePullRequestRequestCount = 0

await page.route('https://api.github.com/user/repos**', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{
id: 11,
owner: { login: 'knightedcodemonkey' },
name: 'develop',
full_name: 'knightedcodemonkey/develop',
default_branch: 'main',
permissions: { push: true },
},
]),
})
})

await mockRepositoryBranches(page, {
'knightedcodemonkey/develop': ['main', 'release'],
})

await page.route(
'https://api.github.com/repos/knightedcodemonkey/develop/pulls/2',
async route => {
if (route.request().method() === 'PATCH') {
closePullRequestRequestCount += 1
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
number: 2,
state: 'closed',
title: 'Existing PR context from storage',
html_url: 'https://github.com/knightedcodemonkey/develop/pull/2',
head: { ref: 'develop/open-pr-test' },
base: { ref: 'main' },
}),
})
return
}

await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
number: 2,
state: 'open',
title: 'Existing PR context from storage',
html_url: 'https://github.com/knightedcodemonkey/develop/pull/2',
head: { ref: 'develop/open-pr-test' },
base: { ref: 'main' },
}),
})
},
)

await page.route(
'https://api.github.com/repos/knightedcodemonkey/develop/git/ref/**',
async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
ref: 'refs/heads/develop/open-pr-test',
object: { type: 'commit', sha: 'existing-head-sha' },
}),
})
},
)

await waitForAppReady(page, `${appEntryPath}`)

await page.evaluate(() => {
localStorage.setItem(
'knighted:develop:github-pr-config:knightedcodemonkey/develop',
JSON.stringify({
componentFilePath: 'examples/component/App.tsx',
stylesFilePath: 'examples/styles/app.css',
renderMode: 'react',
baseBranch: 'main',
headBranch: 'develop/open-pr-test',
prTitle: 'Existing PR context from storage',
prBody: 'Saved body',
isActivePr: true,
pullRequestNumber: 2,
pullRequestUrl: 'https://github.com/knightedcodemonkey/develop/pull/2',
}),
)
})

await connectByotWithSingleRepo(page)

await expect(
page.getByRole('button', { name: 'Disconnect active pull request context' }),
).toBeVisible()

await page
.getByRole('button', { name: 'Disconnect active pull request context' })
.click()

const dialog = page.getByRole('dialog')
await expect(dialog).toBeVisible()
await expect(dialog).toContainText('Disconnect PR context?')
await expect(dialog).toContainText(
'This will disconnect the active pull request context in this app only.',
)
await expect(dialog).toContainText('Your pull request will stay open on GitHub.')
await expect(dialog).toContainText(
'Your GitHub token and selected repository will stay connected.',
)

await dialog.getByRole('button', { name: 'Cancel' }).click()

await expect(
page.getByRole('button', { name: 'Push commit to active pull request branch' }),
).toBeVisible()

const savedActiveStateAfterCancel = await page.evaluate(() => {
const raw = localStorage.getItem(
'knighted:develop:github-pr-config:knightedcodemonkey/develop',
)

if (!raw) {
return null
}

try {
const parsed = JSON.parse(raw)
return parsed?.isActivePr === true
} catch {
return null
}
})

expect(savedActiveStateAfterCancel).toBe(true)

await page
.getByRole('button', { name: 'Disconnect active pull request context' })
.click()
await dialog.getByRole('button', { name: 'Disconnect' }).click()

await expect(page.getByRole('button', { name: 'Open pull request' })).toBeVisible()
await expect(
page.getByRole('button', { name: 'Disconnect active pull request context' }),
).toBeHidden()

const savedContextAfterDisconnect = await page.evaluate(() => {
const raw = localStorage.getItem(
'knighted:develop:github-pr-config:knightedcodemonkey/develop',
)

if (!raw) {
return null
}

try {
return JSON.parse(raw)
} catch {
return null
}
})

expect(savedContextAfterDisconnect).not.toBeNull()
expect(savedContextAfterDisconnect?.isActivePr).toBe(false)
expect(savedContextAfterDisconnect?.pullRequestNumber).toBe(2)
expect(closePullRequestRequestCount).toBe(0)
})

test('Active PR context updates controls and can be closed from AI controls', async ({
page,
}) => {
Expand Down
39 changes: 33 additions & 6 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const githubPrToggleLabel = document.getElementById('github-pr-toggle-label')
const githubPrToggleIcon = document.getElementById('github-pr-toggle-icon')
const githubPrToggleIconPath = document.getElementById('github-pr-toggle-icon-path')
const githubPrContextClose = document.getElementById('github-pr-context-close')
const githubPrContextDisconnect = document.getElementById('github-pr-context-disconnect')
const githubPrDrawer = document.getElementById('github-pr-drawer')
const openPrTitle = document.getElementById('open-pr-title')
const githubPrClose = document.getElementById('github-pr-close')
Expand Down Expand Up @@ -634,16 +635,14 @@ const syncActivePrContextUi = activeContext => {
setGitHubPrToggleVisual(hasActiveContext ? 'push-commit' : 'open-pr')
syncEditorPrContextIndicators(shouldShowEditorSyncIndicators)

if (!(githubPrContextClose instanceof HTMLButtonElement)) {
return
}

if (!hasActiveContext) {
githubPrContextClose.setAttribute('hidden', '')
githubPrContextClose?.setAttribute('hidden', '')
githubPrContextDisconnect?.setAttribute('hidden', '')
return
}

githubPrContextClose.removeAttribute('hidden')
githubPrContextClose?.removeAttribute('hidden')
githubPrContextDisconnect?.removeAttribute('hidden')
}

const syncAiChatTokenVisibility = token => {
Expand All @@ -656,8 +655,10 @@ const syncAiChatTokenVisibility = token => {

if (githubAiContextState.activePrContext) {
githubPrContextClose?.removeAttribute('hidden')
githubPrContextDisconnect?.removeAttribute('hidden')
} else {
githubPrContextClose?.setAttribute('hidden', '')
githubPrContextDisconnect?.setAttribute('hidden', '')
}
return
}
Expand All @@ -672,6 +673,7 @@ const syncAiChatTokenVisibility = token => {
githubPrToggle?.setAttribute('hidden', '')
githubPrToggle?.setAttribute('aria-expanded', 'false')
githubPrContextClose?.setAttribute('hidden', '')
githubPrContextDisconnect?.setAttribute('hidden', '')
chatDrawerController.setOpen(false)
prDrawerController.setOpen(false)
}
Expand Down Expand Up @@ -919,6 +921,31 @@ githubPrContextClose?.addEventListener('click', () => {
})
})

githubPrContextDisconnect?.addEventListener('click', () => {
if (!githubAiContextState.activePrContext) {
return
}

const activePrReference = formatActivePrReference(githubAiContextState.activePrContext)
const referenceLine = activePrReference ? `PR: ${activePrReference}\n` : ''

confirmAction({
title: 'Disconnect PR context?',
copy: `${referenceLine}This will disconnect the active pull request context in this app only.\nYour pull request will stay open on GitHub.\nYour GitHub token and selected repository will stay connected.`,
confirmButtonText: 'Disconnect',
onConfirm: () => {
const result = prDrawerController.disconnectActivePrContext()
const reference = result?.reference
setStatus(
reference
? `Disconnected PR context (${reference}). Pull request remains open on GitHub.`
: 'Disconnected PR context. Pull request remains open on GitHub.',
'neutral',
)
},
})
})

const getStyleEditorLanguage = mode => {
if (mode === 'less') return 'less'
if (mode === 'sass') return 'sass'
Expand Down
43 changes: 35 additions & 8 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,30 @@ <h1>
<span class="github-pr-context-close__label">Close</span>
</button>

<button
class="diagnostics-toggle github-pr-context-disconnect"
id="github-pr-context-disconnect"
type="button"
aria-label="Disconnect active pull request context"
title="Disconnect active pull request context"
hidden
>
<svg
class="github-pr-context-disconnect__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M9.036 7.976a.75.75 0 0 0-1.06 1.06L10.939 12l-2.963 2.963a.75.75 0 1 0 1.06 1.06L12 13.06l2.963 2.964a.75.75 0 0 0 1.061-1.06L13.061 12l2.963-2.964a.75.75 0 1 0-1.06-1.06L12 10.939 9.036 7.976Z"
></path>
<path
d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM2.5 12a9.5 9.5 0 0 0 9.5 9.5 9.5 9.5 0 0 0 9.5-9.5A9.5 9.5 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Z"
></path>
</svg>
<span class="github-pr-context-disconnect__label">Disconnect</span>
</button>

<button
class="diagnostics-toggle ai-chat-toggle"
id="ai-chat-toggle"
Expand All @@ -185,16 +209,19 @@ <h1>
aria-controls="ai-chat-drawer"
hidden
>
<span
class="ai-chat-toggle__emoji ai-chat-toggle__emoji--dark"
aria-hidden="true"
>✨</span
>
<span
class="ai-chat-toggle__emoji ai-chat-toggle__emoji--light"
<svg
class="ai-chat-toggle__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
aria-hidden="true"
>🤖</span
>
<path
d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"
></path>
<path
d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"
></path>
</svg>
<span>Chat</span>
</button>
</div>
Expand Down
32 changes: 32 additions & 0 deletions src/modules/github-pr-drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,38 @@ export const createGitHubPrDrawer = ({
setOpen,
isOpen: () => open,
getActivePrContext: () => getCurrentActivePrContext(),
disconnectActivePrContext: () => {
const repository = getSelectedRepositoryObject()
const repositoryFullName = getRepositoryFullName(repository)
if (!repositoryFullName) {
return { reference: '' }
}

const savedConfig = readRepositoryPrConfig(repositoryFullName)
if (Object.keys(savedConfig).length > 0) {
saveRepositoryPrConfig({
repositoryFullName,
config: {
...savedConfig,
isActivePr: false,
},
})
}

const previousActiveContext =
savedConfig?.isActivePr === true
? getActiveRepositoryPrContext(repositoryFullName)
: null
Comment thread
knightedcodemonkey marked this conversation as resolved.
Outdated

lastActiveContentSyncKey = ''
abortPendingActiveContentSyncRequest()
setSubmitButtonLabel()
emitActivePrContextChange()

return {
reference: formatActivePrReference(previousActiveContext),
}
},
clearActivePrContext: () => {
const repository = getSelectedRepositoryObject()
const repositoryFullName = getRepositoryFullName(repository)
Expand Down
Loading
Loading