@@ -44,7 +44,7 @@ function createMockUseConnector() {
4444 op . status === 'pending' ||
4545 op . status === 'approved' ||
4646 op . status === 'running' ||
47- ( op . status === 'failed' && op . result ?. requiresOtp ) ,
47+ ( op . status === 'failed' && ( op . result ?. requiresOtp || op . result ?. authFailure ) ) ,
4848 ) ,
4949 ) ,
5050 hasOperations : computed ( ( ) => mockState . value . operations . length > 0 ) ,
@@ -60,12 +60,14 @@ function createMockUseConnector() {
6060 op . status === 'pending' ||
6161 op . status === 'approved' ||
6262 op . status === 'running' ||
63- ( op . status === 'failed' && op . result ?. requiresOtp ) ,
63+ ( op . status === 'failed' && ( op . result ?. requiresOtp || op . result ?. authFailure ) ) ,
6464 ) ,
6565 ) ,
6666 hasCompletedOperations : computed ( ( ) =>
6767 mockState . value . operations . some (
68- op => op . status === 'completed' || ( op . status === 'failed' && ! op . result ?. requiresOtp ) ,
68+ op =>
69+ op . status === 'completed' ||
70+ ( op . status === 'failed' && ! op . result ?. requiresOtp && ! op . result ?. authFailure ) ,
6971 ) ,
7072 ) ,
7173 connect : vi . fn ( ) . mockResolvedValue ( true ) ,
@@ -198,61 +200,19 @@ afterEach(() => {
198200} )
199201
200202describe ( 'HeaderConnectorModal' , ( ) => {
201- describe ( 'Web auth settings (connected)' , ( ) => {
202- it ( 'shows web auth toggle when connected' , async ( ) => {
203- const dialog = await mountAndOpen ( 'connected' )
204- const labels = Array . from ( dialog ?. querySelectorAll ( 'label, span' ) ?? [ ] )
205- const webAuthLabel = labels . find ( el => el . textContent ?. includes ( 'web authentication' ) )
206- expect ( webAuthLabel ) . toBeTruthy ( )
207- } )
208-
203+ describe ( 'Connector preferences (connected)' , ( ) => {
209204 it ( 'shows auto-open URL toggle when connected' , async ( ) => {
210205 const dialog = await mountAndOpen ( 'connected' )
211206 const labels = Array . from ( dialog ?. querySelectorAll ( 'label, span' ) ?? [ ] )
212207 const autoOpenLabel = labels . find ( el => el . textContent ?. includes ( 'open auth page' ) )
213208 expect ( autoOpenLabel ) . toBeTruthy ( )
214209 } )
215210
216- it ( 'auto-open URL toggle is disabled when webAuth is off' , async ( ) => {
217- mockSettings . value . connector . webAuth = false
218- const dialog = await mountAndOpen ( 'connected' )
219-
220- // Find the auto-open toggle container - it should have opacity-50 class
221- const toggleContainers = Array . from ( dialog ?. querySelectorAll ( '[class*="opacity-50"]' ) ?? [ ] )
222- expect ( toggleContainers . length ) . toBeGreaterThan ( 0 )
223- } )
224-
225- it ( 'auto-open URL toggle is not disabled when webAuth is on' , async ( ) => {
226- mockSettings . value . connector . webAuth = true
211+ it ( 'does not show a web auth toggle (web auth is now always on)' , async ( ) => {
227212 const dialog = await mountAndOpen ( 'connected' )
228-
229- // When webAuth is ON, the auto-open toggle should not have opacity-50
230- // Verify by checking that we can find the toggle with the "open auth page" label
231- // and it does NOT have opacity-50 in its parent
232- const autoOpenLabels = Array . from ( dialog ?. querySelectorAll ( '*' ) ?? [ ] ) . filter ( el =>
233- el . textContent ?. includes ( 'open auth page' ) ,
234- )
235- expect ( autoOpenLabels . length ) . toBeGreaterThan ( 0 )
236- } )
237- } )
238-
239- describe ( 'Web auth settings (disconnected advanced)' , ( ) => {
240- it ( 'shows web auth toggles in advanced details section' , async ( ) => {
241- const dialog = await mountAndOpen ( )
242-
243- // Open the advanced details section
244- const details = dialog ?. querySelector ( 'details' )
245- expect ( details ) . not . toBeNull ( )
246-
247- // Programmatically open it
248- details ?. setAttribute ( 'open' , '' )
249- await nextTick ( )
250-
251- const labels = Array . from ( details ?. querySelectorAll ( 'label, span' ) ?? [ ] )
213+ const labels = Array . from ( dialog ?. querySelectorAll ( 'label, span' ) ?? [ ] )
252214 const webAuthLabel = labels . find ( el => el . textContent ?. includes ( 'web authentication' ) )
253- const autoOpenLabel = labels . find ( el => el . textContent ?. includes ( 'open auth page' ) )
254- expect ( webAuthLabel ) . toBeTruthy ( )
255- expect ( autoOpenLabel ) . toBeTruthy ( )
215+ expect ( webAuthLabel ) . toBeUndefined ( )
256216 } )
257217 } )
258218
@@ -350,18 +310,16 @@ describe('HeaderConnectorModal', () => {
350310 expect ( dialog ?. innerHTML ) . toContain ( 'otp-input' )
351311 } )
352312
353- it ( 'does not show retry with web auth button when webAuth setting is off' , async ( ) => {
354- mockSettings . value . connector . webAuth = false
313+ it ( 'does not show retry with web auth when there are no auth failures' , async ( ) => {
355314 mockState . value . operations = [
356315 {
357316 id : '0000000000000001' ,
358317 type : 'org:add-user' ,
359318 params : { org : 'myorg' , user : 'alice' , role : 'developer' } ,
360319 description : 'Add alice' ,
361320 command : 'npm org set myorg alice developer' ,
362- status : 'failed ' ,
321+ status : 'approved ' ,
363322 createdAt : Date . now ( ) ,
364- result : { stdout : '' , stderr : 'otp required' , exitCode : 1 , requiresOtp : true } ,
365323 } ,
366324 ]
367325 const dialog = await mountAndOpen ( 'connected' )
@@ -371,6 +329,26 @@ describe('HeaderConnectorModal', () => {
371329 html . includes ( 'Retry with web auth' ) || html . includes ( 'retry_web_auth' )
372330 expect ( hasWebAuthButton ) . toBe ( false )
373331 } )
332+
333+ it ( 'shows OTP alert section for operations with authFailure (not just requiresOtp)' , async ( ) => {
334+ mockState . value . operations = [
335+ {
336+ id : '0000000000000001' ,
337+ type : 'org:add-user' ,
338+ params : { org : 'myorg' , user : 'alice' , role : 'developer' } ,
339+ description : 'Add alice' ,
340+ command : 'npm org set myorg alice developer' ,
341+ status : 'failed' ,
342+ createdAt : Date . now ( ) ,
343+ result : { stdout : '' , stderr : 'auth failed' , exitCode : 1 , authFailure : true } ,
344+ } ,
345+ ]
346+ const dialog = await mountAndOpen ( 'connected' )
347+
348+ // The OTP/auth failures section should render for authFailure too
349+ const otpAlert = dialog ?. querySelector ( '[role="alert"]' )
350+ expect ( otpAlert ) . not . toBeNull ( )
351+ } )
374352 } )
375353
376354 describe ( 'Disconnected state' , ( ) => {
0 commit comments