@@ -65,12 +65,17 @@ describe('TokenDialog component', () => {
6565    const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
6666    const  instance : any  =  wrapper . instance ( ) ; 
6767
68-     wrapper . setState ( {  verifying : true ,  tokenInput : 'hello'  } ) ; 
68+     wrapper . setState ( { 
69+       verifying : true , 
70+       tokenInput : 'hello' , 
71+       errorMessage : 'test error' , 
72+     } ) ; 
6973    instance . reset ( ) ; 
7074
7175    expect ( wrapper . state ( ) ) . toEqual ( { 
7276      verifying : false , 
7377      error : false , 
78+       errorMessage : undefined , 
7479      tokenInput : '' , 
7580    } ) ; 
7681  } ) ; 
@@ -79,12 +84,17 @@ describe('TokenDialog component', () => {
7984    const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
8085    const  instance : any  =  wrapper . instance ( ) ; 
8186
82-     wrapper . setState ( {  verifying : true ,  tokenInput : 'hello'  } ) ; 
87+     wrapper . setState ( { 
88+       verifying : true , 
89+       tokenInput : 'hello' , 
90+       errorMessage : 'test error' , 
91+     } ) ; 
8392    instance . onClose ( ) ; 
8493
8594    expect ( wrapper . state ( ) ) . toEqual ( { 
8695      verifying : false , 
8796      error : false , 
97+       errorMessage : undefined , 
8898      tokenInput : '' , 
8999    } ) ; 
90100  } ) ; 
@@ -121,7 +131,12 @@ describe('TokenDialog component', () => {
121131      mockOctokit  =  { 
122132        authenticate : vi . fn ( ) , 
123133        users : { 
124-           getAuthenticated : vi . fn ( ) . mockResolvedValue ( {  data : mockUser  } ) , 
134+           getAuthenticated : vi . fn ( ) . mockResolvedValue ( { 
135+             data : mockUser , 
136+             headers : { 
137+               'x-oauth-scopes' : 'gist, repo' , 
138+             } , 
139+           } ) , 
125140        } , 
126141      }  as  unknown  as  Octokit ; 
127142
@@ -149,11 +164,55 @@ describe('TokenDialog component', () => {
149164      expect ( store . gitHubLogin ) . toBe ( mockUser . login ) ; 
150165      expect ( store . gitHubName ) . toBe ( mockUser . name ) ; 
151166      expect ( store . gitHubAvatarUrl ) . toBe ( mockUser . avatar_url ) ; 
167+       expect ( wrapper . state ( 'error' ) ) . toBe ( false ) ; 
168+       expect ( store . isTokenDialogShowing ) . toBe ( false ) ; 
169+     } ) ; 
170+ 
171+     it ( 'handles an invalid token error' ,  async  ( )  =>  { 
172+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockRejectedValue ( 
173+         new  Error ( 'Bad credentials' ) , 
174+       ) ; 
175+ 
176+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
177+       wrapper . setState ( {  tokenInput : mockValidToken  } ) ; 
178+       const  instance : any  =  wrapper . instance ( ) ; 
179+ 
180+       await  instance . onSubmitToken ( ) ; 
181+ 
182+       expect ( wrapper . state ( 'error' ) ) . toBe ( true ) ; 
183+       expect ( wrapper . state ( 'errorMessage' ) ) . toBe ( 
184+         'Invalid GitHub token. Please check your token and try again.' , 
185+       ) ; 
186+       expect ( store . gitHubToken ) . toEqual ( null ) ; 
187+     } ) ; 
188+ 
189+     it ( 'handles missing gist scope' ,  async  ( )  =>  { 
190+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockResolvedValue ( { 
191+         data : mockUser , 
192+         headers : { 
193+           'x-oauth-scopes' : 'repo, user' ,  // Missing 'gist' scope 
194+         } , 
195+       }  as  any ) ; 
196+ 
197+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
198+       wrapper . setState ( {  tokenInput : mockValidToken  } ) ; 
199+       const  instance : any  =  wrapper . instance ( ) ; 
200+ 
201+       await  instance . onSubmitToken ( ) ; 
202+ 
203+       expect ( wrapper . state ( 'error' ) ) . toBe ( true ) ; 
204+       expect ( wrapper . state ( 'errorMessage' ) ) . toBe ( 
205+         'Token is missing the "gist" scope. Please generate a new token with gist permissions.' , 
206+       ) ; 
207+       expect ( store . gitHubToken ) . toEqual ( null ) ; 
152208    } ) ; 
153209
154-     it ( 'handles an error ' ,  async  ( )  =>  { 
210+     it ( 'handles empty scopes header ' ,  async  ( )  =>  { 
155211      vi . mocked ( mockOctokit . users . getAuthenticated ) . mockResolvedValue ( { 
156-         data : null , 
212+         data : mockUser , 
213+         headers : { 
214+           // No x-oauth-scopes header 
215+         } , 
157216      }  as  any ) ; 
158217
159218      const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
@@ -163,7 +222,110 @@ describe('TokenDialog component', () => {
163222      await  instance . onSubmitToken ( ) ; 
164223
165224      expect ( wrapper . state ( 'error' ) ) . toBe ( true ) ; 
225+       expect ( wrapper . state ( 'errorMessage' ) ) . toBe ( 
226+         'Token is missing the "gist" scope. Please generate a new token with gist permissions.' , 
227+       ) ; 
166228      expect ( store . gitHubToken ) . toEqual ( null ) ; 
167229    } ) ; 
168230  } ) ; 
231+ 
232+   describe ( 'validateGitHubToken()' ,  ( )  =>  { 
233+     let  mockOctokit : Octokit ; 
234+     const  mockUser  =  { 
235+       avatar_url : 'https://avatars.fake/hi' , 
236+       login : 'test-login' , 
237+       name : 'Test User' , 
238+     }  as  const ; 
239+ 
240+     beforeEach ( ( )  =>  { 
241+       mockOctokit  =  { 
242+         users : { 
243+           getAuthenticated : vi . fn ( ) , 
244+         } , 
245+       }  as  unknown  as  Octokit ; 
246+ 
247+       vi . mocked ( getOctokit ) . mockResolvedValue ( mockOctokit ) ; 
248+     } ) ; 
249+ 
250+     it ( 'validates a token with gist scope' ,  async  ( )  =>  { 
251+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockResolvedValue ( { 
252+         data : mockUser , 
253+         headers : { 
254+           'x-oauth-scopes' : 'gist, repo, user' , 
255+         } , 
256+       }  as  any ) ; 
257+ 
258+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
259+       const  instance : any  =  wrapper . instance ( ) ; 
260+ 
261+       const  result  =  await  instance . validateGitHubToken ( 'valid-token' ) ; 
262+ 
263+       expect ( result ) . toEqual ( { 
264+         isValid : true , 
265+         scopes : [ 'gist' ,  'repo' ,  'user' ] , 
266+         hasGistScope : true , 
267+         user : mockUser , 
268+       } ) ; 
269+     } ) ; 
270+ 
271+     it ( 'validates a token without gist scope' ,  async  ( )  =>  { 
272+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockResolvedValue ( { 
273+         data : mockUser , 
274+         headers : { 
275+           'x-oauth-scopes' : 'repo, user' , 
276+         } , 
277+       }  as  any ) ; 
278+ 
279+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
280+       const  instance : any  =  wrapper . instance ( ) ; 
281+ 
282+       const  result  =  await  instance . validateGitHubToken ( 'token-without-gist' ) ; 
283+ 
284+       expect ( result ) . toEqual ( { 
285+         isValid : true , 
286+         scopes : [ 'repo' ,  'user' ] , 
287+         hasGistScope : false , 
288+         user : mockUser , 
289+       } ) ; 
290+     } ) ; 
291+ 
292+     it ( 'handles invalid token' ,  async  ( )  =>  { 
293+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockRejectedValue ( 
294+         new  Error ( 'Bad credentials' ) , 
295+       ) ; 
296+ 
297+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
298+       const  instance : any  =  wrapper . instance ( ) ; 
299+ 
300+       const  result  =  await  instance . validateGitHubToken ( 'invalid-token' ) ; 
301+ 
302+       expect ( result ) . toEqual ( { 
303+         isValid : false , 
304+         scopes : [ ] , 
305+         hasGistScope : false , 
306+         error : 'Bad credentials' , 
307+       } ) ; 
308+     } ) ; 
309+ 
310+     it ( 'handles missing scopes header' ,  async  ( )  =>  { 
311+       vi . mocked ( mockOctokit . users . getAuthenticated ) . mockResolvedValue ( { 
312+         data : mockUser , 
313+         headers : { } , 
314+       }  as  any ) ; 
315+ 
316+       const  wrapper  =  shallow ( < TokenDialog  appState = { store }  /> ) ; 
317+       const  instance : any  =  wrapper . instance ( ) ; 
318+ 
319+       const  result  =  await  instance . validateGitHubToken ( 
320+         'token-no-scopes-header' , 
321+       ) ; 
322+ 
323+       expect ( result ) . toEqual ( { 
324+         isValid : true , 
325+         scopes : [ ] , 
326+         hasGistScope : false , 
327+         user : mockUser , 
328+       } ) ; 
329+     } ) ; 
330+   } ) ; 
169331} ) ; 
0 commit comments