@@ -201,6 +201,78 @@ test('BYOT controls render when feature flag is enabled by query param', async (
201201 await expect ( page . locator ( '#github-ai-controls #ai-chat-toggle' ) ) . toBeHidden ( )
202202} )
203203
204+ test ( 'GitHub token info panel reflects missing and present token states' , async ( {
205+ page,
206+ } ) => {
207+ await waitForAppReady ( page , `${ appEntryPath } ?feature-ai=true` )
208+
209+ const infoButton = page . locator ( '#github-token-info' )
210+ const infoPanel = page . locator ( '#github-token-info-panel' )
211+ const missingMessage = page . locator ( '.github-token-info-message--missing-token' )
212+ const presentMessage = page . locator ( '.github-token-info-message--has-token' )
213+
214+ await expect ( infoButton ) . toHaveText ( '?' )
215+ await expect ( infoButton ) . toHaveAttribute ( 'data-token-state' , 'missing' )
216+
217+ await infoButton . click ( )
218+ await expect ( infoPanel ) . toBeVisible ( )
219+ await expect ( missingMessage ) . toBeVisible ( )
220+ await expect ( missingMessage ) . toContainText ( 'Provide a GitHub PAT' )
221+ await expect ( missingMessage . getByRole ( 'link' , { name : 'docs' } ) ) . toHaveAttribute (
222+ 'href' ,
223+ 'https://github.com/knightedcodemonkey/develop/blob/main/docs/byot.md' ,
224+ )
225+ await expect ( presentMessage ) . toBeHidden ( )
226+
227+ await connectByotWithSingleRepo ( page )
228+ await expect ( infoButton ) . toHaveText ( 'i' )
229+ await expect ( infoButton ) . toHaveAttribute ( 'data-token-state' , 'present' )
230+
231+ await infoButton . click ( )
232+ await expect ( infoPanel ) . toBeVisible ( )
233+ await expect ( presentMessage ) . toBeVisible ( )
234+ await expect ( presentMessage ) . toContainText (
235+ 'Use the trash icon to remove it from storage.' ,
236+ )
237+ await expect ( missingMessage ) . toBeHidden ( )
238+ } )
239+
240+ test ( 'deleting saved GitHub token requires confirmation modal' , async ( { page } ) => {
241+ await waitForAppReady ( page , `${ appEntryPath } ?feature-ai=true` )
242+ await connectByotWithSingleRepo ( page )
243+
244+ const dialog = page . locator ( '#clear-confirm-dialog' )
245+ const tokenDelete = page . locator ( '#github-token-delete' )
246+ const tokenAdd = page . locator ( '#github-token-add' )
247+ const tokenInput = page . locator ( '#github-token-input' )
248+
249+ await expect ( tokenDelete ) . toBeVisible ( )
250+
251+ await tokenDelete . click ( )
252+ await expect ( dialog ) . toHaveAttribute ( 'open' , '' )
253+ await expect ( page . locator ( '#clear-confirm-title' ) ) . toHaveText (
254+ 'Remove saved GitHub token?' ,
255+ )
256+ await expect ( page . locator ( '#clear-confirm-copy' ) ) . toHaveText (
257+ 'This action removes the token from browser storage. You can add another token at any time.' ,
258+ )
259+
260+ await dialog . getByRole ( 'button' , { name : 'Cancel' } ) . click ( )
261+ await expect ( dialog ) . not . toHaveAttribute ( 'open' , '' )
262+ await expect ( tokenDelete ) . toBeVisible ( )
263+ await expect ( tokenAdd ) . toBeHidden ( )
264+
265+ await tokenDelete . click ( )
266+ await expect ( dialog ) . toHaveAttribute ( 'open' , '' )
267+ await dialog . getByRole ( 'button' , { name : 'Clear' } ) . click ( )
268+ await expect ( dialog ) . not . toHaveAttribute ( 'open' , '' )
269+
270+ await expect ( page . locator ( '#status' ) ) . toHaveText ( 'GitHub token removed' )
271+ await expect ( tokenAdd ) . toBeVisible ( )
272+ await expect ( tokenDelete ) . toBeHidden ( )
273+ await expect ( tokenInput ) . toHaveValue ( '' )
274+ } )
275+
204276test ( 'AI chat drawer opens and closes when feature flag is enabled' , async ( { page } ) => {
205277 await waitForAppReady ( page , `${ appEntryPath } ?feature-ai=true` )
206278 await connectByotWithSingleRepo ( page )
0 commit comments