Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
15 changes: 15 additions & 0 deletions playwright/github-byot-ai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,28 @@
exact: true,
includeHidden: true,
})
const workspacesToggle = page.getByRole('button', {
name: 'Workspaces',
exact: true,
includeHidden: true,
})
await expect(byotControls).toBeVisible()
await expect(page.getByRole('textbox', { name: 'GitHub token' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Add GitHub token' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Chat' })).toBeHidden()
await expect(page.getByRole('heading', { name: 'AI Chat' })).toBeHidden()
await expect(prToggle).toHaveCount(1)
await expect(prToggle).toBeHidden()
await expect(workspacesToggle).toHaveCount(1)
await expect(workspacesToggle).toBeHidden()
})

test('chat becomes available after token connect', async ({ page }) => {
await waitForAppReady(page)
await connectByotWithSingleRepo(page)

await expect(page.getByRole('button', { name: 'Open pull request' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Workspaces' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Chat' })).toBeVisible()
})

Expand All @@ -49,12 +57,19 @@
exact: true,
includeHidden: true,
})
const workspacesToggle = page.getByRole('button', {
name: 'Workspaces',
exact: true,
includeHidden: true,
})
await expect(byotControls).toBeVisible()
await expect(page.getByRole('textbox', { name: 'GitHub token' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Add GitHub token' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Chat' })).toBeHidden()
await expect(prToggle).toHaveCount(1)
await expect(prToggle).toBeHidden()
await expect(workspacesToggle).toHaveCount(1)
await expect(workspacesToggle).toBeHidden()
})

test('GitHub token info panel reflects missing and present token states', async ({
Expand Down Expand Up @@ -758,7 +773,7 @@

await page.reload()
await expect(page.getByRole('heading', { name: '@knighted/develop' })).toBeVisible()
await expect(page.getByRole('status', { name: 'App status' })).toHaveText(

Check failure on line 776 in playwright/github-byot-ai.spec.ts

View workflow job for this annotation

GitHub Actions / E2E (Playwright, chromium)

[chromium] › playwright/github-byot-ai.spec.ts:723:1 › BYOT remembers selected repository across reloads

1) [chromium] › playwright/github-byot-ai.spec.ts:723:1 › BYOT remembers selected repository across reloads Error: expect(locator).toHaveText(expected) failed Locator: getByRole('status', { name: 'App status' }) Expected: "Loaded 2 writable repositories" Received: "Rendered" Timeout: 60000ms Call log: - Expect "toHaveText" with timeout 60000ms - waiting for getByRole('status', { name: 'App status' }) 3 × locator resolved to <div id="status" role="status" class="status" aria-label="App status">Idle</div> - unexpected value "Idle" - locator resolved to <div id="status" role="status" aria-label="App status" class="status status--pending">Loading writable repositories from GitHub...</div> - unexpected value "Loading writable repositories from GitHub..." - locator resolved to <div id="status" role="status" aria-label="App status" class="status status--pending">Loading CDN assets…</div> - unexpected value "Loading CDN assets…" 58 × locator resolved to <div id="status" role="status" aria-label="App status" class="status status--neutral">Rendered</div> - unexpected value "Rendered" 774 | await page.reload() 775 | await expect(page.getByRole('heading', { name: '@knighted/develop' })).toBeVisible() > 776 | await expect(page.getByRole('status', { name: 'App status' })).toHaveText( | ^ 777 | 'Loaded 2 writable repositories', 778 | { 779 | timeout: 60_000, at /home/runner/work/develop/develop/playwright/github-byot-ai.spec.ts:776:66

Check failure on line 776 in playwright/github-byot-ai.spec.ts

View workflow job for this annotation

GitHub Actions / E2E (Playwright, webkit, shard 1/4)

[webkit] › playwright/github-byot-ai.spec.ts:723:1 › BYOT remembers selected repository across reloads

1) [webkit] › playwright/github-byot-ai.spec.ts:723:1 › BYOT remembers selected repository across reloads Error: expect(locator).toHaveText(expected) failed Locator: getByRole('status', { name: 'App status' }) Expected: "Loaded 2 writable repositories" Received: "Rendered" Timeout: 60000ms Call log: - Expect "toHaveText" with timeout 60000ms - waiting for getByRole('status', { name: 'App status' }) 3 × locator resolved to <div id="status" role="status" class="status" aria-label="App status">Idle</div> - unexpected value "Idle" 2 × locator resolved to <div id="status" role="status" aria-label="App status" class="status status--pending">Loading CDN assets…</div> - unexpected value "Loading CDN assets…" 58 × locator resolved to <div id="status" role="status" aria-label="App status" class="status status--neutral">Rendered</div> - unexpected value "Rendered" 774 | await page.reload() 775 | await expect(page.getByRole('heading', { name: '@knighted/develop' })).toBeVisible() > 776 | await expect(page.getByRole('status', { name: 'App status' })).toHaveText( | ^ 777 | 'Loaded 2 writable repositories', 778 | { 779 | timeout: 60_000, at /home/runner/work/develop/develop/playwright/github-byot-ai.spec.ts:776:66
'Loaded 2 writable repositories',
{
timeout: 60_000,
Expand Down
34 changes: 22 additions & 12 deletions playwright/helpers/app-test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
attempt += 1

try {
await page.goto(path, { waitUntil: 'domcontentloaded' })

Check failure on line 62 in playwright/helpers/app-test-helpers.ts

View workflow job for this annotation

GitHub Actions / E2E (Playwright, webkit, shard 3/4)

[webkit] › playwright/rendering-modes.spec.ts:718:1 › persists render mode across reload

1) [webkit] › playwright/rendering-modes.spec.ts:718:1 › persists render mode across reload ────── Error: page.goto: Target page, context or browser has been closed at helpers/app-test-helpers.ts:62 60 | 61 | try { > 62 | await page.goto(path, { waitUntil: 'domcontentloaded' }) | ^ 63 | return 64 | } catch (error) { 65 | if (attempt >= 3 || !isRetryableGotoError(error)) { at navigateToApp (/home/runner/work/develop/develop/playwright/helpers/app-test-helpers.ts:62:18) at waitForAppReady (/home/runner/work/develop/develop/playwright/helpers/app-test-helpers.ts:75:3) at waitForInitialRender (/home/runner/work/develop/develop/playwright/helpers/app-test-helpers.ts:140:3) at /home/runner/work/develop/develop/playwright/rendering-modes.spec.ts:726:3
return
} catch (error) {
if (attempt >= 3 || !isRetryableGotoError(error)) {
Expand Down Expand Up @@ -171,6 +171,22 @@
await page.getByRole('button', { name: pattern }).click()
}

const replaceEditorSource = async ({
editorContent,
source,
}: {
editorContent: ReturnType<Page['locator']>
source: string
}) => {
for (let attempt = 0; attempt < 2; attempt += 1) {
await editorContent.fill('')
await editorContent.fill(source)
await editorContent.press('End')
await editorContent.type(' ')
await editorContent.press('Backspace')
}
}

export const reorderWorkspaceTabBefore = async (
page: Page,
{ from, to }: { from: string; to: string },
Expand Down Expand Up @@ -199,35 +215,29 @@
},
) => {
await openWorkspaceTab(page, fileName)
await expect(page.getByRole('region', { name: fileName })).toBeVisible()
const editorContent = page
.locator(`.editor-panel[data-editor-kind="${kind}"] .cm-content`)
.first()
await editorContent.fill(source)
await editorContent.press('End')
await editorContent.type(' ')
await editorContent.press('Backspace')
await replaceEditorSource({ editorContent, source })
}

export const setComponentEditorSource = async (page: Page, source: string) => {
await page.getByRole('button', { name: 'Open tab App.tsx' }).click()
await expect(page.getByRole('region', { name: 'App.tsx' })).toBeVisible()
const editorContent = page
.locator('.editor-panel[data-editor-kind="component"] .cm-content')
.first()
await editorContent.fill(source)
await editorContent.press('End')
await editorContent.type(' ')
await editorContent.press('Backspace')
await replaceEditorSource({ editorContent, source })
}

export const setStylesEditorSource = async (page: Page, source: string) => {
await page.getByRole('button', { name: 'Open tab app.css' }).click()
await expect(page.getByRole('region', { name: 'app.css' })).toBeVisible()
const editorContent = page
.locator('.editor-panel[data-editor-kind="styles"] .cm-content')
.first()
await editorContent.fill(source)
await editorContent.press('End')
await editorContent.type(' ')
await editorContent.press('Backspace')
await replaceEditorSource({ editorContent, source })
}

export const getActiveComponentEditorLineNumber = async (page: Page) => {
Expand Down
50 changes: 30 additions & 20 deletions playwright/rendering-modes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { expect, test } from '@playwright/test'
import {
addWorkspaceTab,
Expand Down Expand Up @@ -577,7 +577,7 @@
)
})

test('auto render implicitly wraps source with App in dom and react modes', async ({
test('auto render shows App-only error in dom and react modes when App is missing', async ({
page,
}) => {
await waitForInitialRender(page)
Expand All @@ -589,9 +589,9 @@
'const Button = () => <button type="button">implicit app dom</button>',
)

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
await expect(getPreviewFrame(page).getByRole('button')).toContainText(
'implicit app dom',
await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Error')
await expect(page.locator('#preview-host pre')).toContainText(
'Expected a function or const named App.',
)

await page.getByRole('combobox', { name: 'Render mode' }).selectOption('react')
Expand All @@ -604,13 +604,13 @@
page.locator('.editor-panel[data-editor-kind="component"] .cm-content').first(),
).toContainText('implicit app react')

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
await expect(getPreviewFrame(page).getByRole('button')).toContainText(
'implicit app react',
await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Error')
await expect(page.locator('#preview-host pre')).toContainText(
'Expected a function or const named App.',
)
})

test('auto render implicit App includes multiple component declarations', async ({
test('auto render renders successfully when explicit App is defined in dom and react modes', async ({
page,
}) => {
await waitForInitialRender(page)
Expand All @@ -619,15 +619,25 @@

await setComponentEditorSource(
page,
[
'const OtherButton = () => <button type="button">bar</button>',
'const Button = () => <button type="button">foo</button>',
].join('\n'),
'const App = () => <button type="button">explicit app dom</button>',
)

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
await expect(getPreviewFrame(page).getByRole('button')).toHaveCount(2)
await expect(getPreviewFrame(page).getByRole('button')).toContainText(['bar', 'foo'])
await expect(getPreviewFrame(page).getByRole('button')).toContainText(
'explicit app dom',
)

await page.getByRole('combobox', { name: 'Render mode' }).selectOption('react')
await expect(page.getByRole('combobox', { name: 'Render mode' })).toHaveValue('react')
await setComponentEditorSource(
page,
'const App = () => <button type="button">explicit app react</button>',
)

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
await expect(getPreviewFrame(page).getByRole('button')).toContainText(
'explicit app react',
)
})

test('auto render does not treat lowercase helpers as implicit components', async ({
Expand All @@ -651,7 +661,7 @@
)
})

test('auto render wraps standalone JSX with trailing semicolon and comment', async ({
test('auto render shows App-only error for standalone JSX expression', async ({
page,
}) => {
await waitForInitialRender(page)
Expand All @@ -663,13 +673,13 @@
'(<button type="button">implicit app from jsx expression</button>) as any; // trailing',
)

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
await expect(getPreviewFrame(page).getByRole('button')).toContainText(
'implicit app from jsx expression',
await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Error')
await expect(page.locator('#preview-host pre')).toContainText(
'Expected a function or const named App.',
)
})

test('auto render requires explicit App for declarations plus top-level JSX expression', async ({
test('auto render shows App-only error for declarations plus top-level JSX expression', async ({
page,
}) => {
await waitForInitialRender(page)
Expand All @@ -687,7 +697,7 @@

await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Error')
await expect(page.locator('#preview-host pre')).toContainText(
'Top-level JSX with declarations or imports requires an explicit App component.',
'Expected a function or const named App.',
)
})

Expand Down
1 change: 1 addition & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ <h1>
aria-controls="workspaces-drawer"
title="Manage local workspaces"
disabled
hidden
>
<svg class="workspaces-toggle__icon" viewBox="0 0 24 24" aria-hidden="true">
<path
Expand Down
28 changes: 21 additions & 7 deletions src/modules/app-core/github-pr-context-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,35 @@ export const createGitHubPrContextUiController = ({
workspacesToggle.disabled = false
}

aiChatToggle?.removeAttribute('hidden')
if (aiChatToggle instanceof HTMLElement) {
aiChatToggle.hidden = false
}

githubPrToggle?.removeAttribute('hidden')
if (githubPrToggle instanceof HTMLElement) {
githubPrToggle.hidden = false
}
if (!contextState.activePrContext) {
workspacesToggle?.removeAttribute('hidden')
if (workspacesToggle instanceof HTMLElement) {
workspacesToggle.hidden = false
}
}

if (contextState.activePrContext) {
githubPrContextClose?.removeAttribute('hidden')
githubPrContextDisconnect?.removeAttribute('hidden')
workspacesToggle?.setAttribute('hidden', '')
if (workspacesToggle instanceof HTMLElement) {
workspacesToggle.hidden = true
}
} else {
githubPrContextClose?.setAttribute('hidden', '')
githubPrContextDisconnect?.setAttribute('hidden', '')
}
return
}

aiChatToggle?.setAttribute('hidden', '')
if (aiChatToggle instanceof HTMLElement) {
aiChatToggle.hidden = true
}
aiChatToggle?.setAttribute('aria-expanded', 'false')
if (workspacesToggle instanceof HTMLButtonElement) {
workspacesToggle.disabled = true
Expand All @@ -139,9 +149,13 @@ export const createGitHubPrContextUiController = ({
contextState.hasSyncedActivePrEditorContent = false
syncEditorPrContextIndicators(false)
setGitHubPrToggleVisual('open-pr')
githubPrToggle?.setAttribute('hidden', '')
if (githubPrToggle instanceof HTMLElement) {
githubPrToggle.hidden = true
}
githubPrToggle?.setAttribute('aria-expanded', 'false')
workspacesToggle?.setAttribute('hidden', '')
if (workspacesToggle instanceof HTMLElement) {
workspacesToggle.hidden = true
}
workspacesToggle?.setAttribute('aria-expanded', 'false')
githubPrContextClose?.setAttribute('hidden', '')
githubPrContextDisconnect?.setAttribute('hidden', '')
Expand Down
3 changes: 0 additions & 3 deletions src/modules/app-core/github-workflows.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,6 @@ const initializeGitHubWorkflows = ({
onActivePrContextChange: activeContext => {
prContextUi.setActivePrContext(activeContext)
prContextUi.syncAiChatTokenVisibility(getTokenForVisibility())
if (workspacesToggle instanceof HTMLButtonElement) {
workspacesToggle.hidden = Boolean(activeContext)
}

if (activeContext) {
closeWorkspacesDrawer()
Expand Down
4 changes: 0 additions & 4 deletions src/modules/app-core/workspace-controllers-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,6 @@ const createWorkspaceControllersSetup = ({
const setActiveWorkspaceTab = tabId =>
workspaceTabSelectionController.setActiveWorkspaceTab(tabId)

const syncEditorFromActiveWorkspaceTabDelegate = () =>
workspaceTabSelectionController.syncEditorFromActiveWorkspaceTab()

workspaceTabMutationsController = createWorkspaceTabMutationsController({
Comment thread
knightedcodemonkey marked this conversation as resolved.
toNonEmptyWorkspaceText,
workspaceTabsState,
Expand Down Expand Up @@ -186,7 +183,6 @@ const createWorkspaceControllersSetup = ({
getShouldShowEditedDesign,
workspaceTabsShell,
workspaceTabAddWrap,
syncEditorFromActiveWorkspaceTab: syncEditorFromActiveWorkspaceTabDelegate,
})

const workspaceContextController = createWorkspaceContextController({
Expand Down
3 changes: 0 additions & 3 deletions src/modules/app-core/workspace-tabs-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const createWorkspaceTabsRenderer = ({
getShouldShowEditedDesign,
workspaceTabsShell,
workspaceTabAddWrap,
syncEditorFromActiveWorkspaceTab,
}) => {
const clearWorkspaceTabDragState = () => {
setDraggedWorkspaceTabId('')
Expand Down Expand Up @@ -294,8 +293,6 @@ const createWorkspaceTabsRenderer = ({
renderWorkspaceTabs()
return
}

syncEditorFromActiveWorkspaceTab()
}

return {
Expand Down
Loading
Loading