@@ -296,15 +296,20 @@ describe('Slack Search settings and shared wizard', () => {
296296 expect ( mocks . install ) . not . toHaveBeenCalled ( )
297297 } )
298298
299- it ( 'starts with one setup action and a Slack app link, with no manifest preview or form ' , async ( ) => {
299+ it ( 'offers direct manifest copying and a Slack app link before collecting credentials ' , async ( ) => {
300300 await render ( )
301301 expect ( container . querySelectorAll ( 'button' ) ) . toHaveLength ( 1 )
302302 await click ( 'Set up' )
303303 expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Create Slack app' )
304304 expect ( document . querySelector ( '[role="dialog"]' ) ) . not . toHaveTextContent ( 'Step 1' )
305- expect ( document . querySelector ( '[role="dialog"]' ) ) . not . toHaveTextContent ( 'App manifest' )
305+ expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'App manifest' )
306+ expect ( button ( 'Copy manifest' ) ) . toBeDefined ( )
307+ expect ( button ( 'Continue' ) ) . toBeDisabled ( )
308+ await click ( 'Copy manifest' )
309+ expect ( mocks . copy ) . toHaveBeenCalledExactlyOnceWith ( '{}' )
310+ expect ( button ( 'Continue' ) ) . not . toBeDisabled ( )
306311 expect ( document . querySelector ( 'a[href="https://api.slack.com/apps"]' ) ) . toHaveTextContent (
307- 'Create app '
312+ 'Open Slack Apps '
308313 )
309314 expect ( document . querySelectorAll ( 'input' ) ) . toHaveLength ( 0 )
310315 expect ( mocks . manifest ) . toHaveBeenCalledWith ( 'org-1' , 'Sim Search' )
@@ -370,8 +375,9 @@ describe('Slack Search settings and shared wizard', () => {
370375 )
371376 } else {
372377 expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Create Slack app' )
378+ await click ( 'Copy manifest' )
373379 await click ( 'Continue' )
374- expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Client ID ' )
380+ expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Bot User OAuth Token ' )
375381 expect ( mocks . install ) . not . toHaveBeenCalled ( )
376382 }
377383 }
@@ -386,18 +392,14 @@ describe('Slack Search settings and shared wizard', () => {
386392 expect ( button ( 'Continue' ) ) . toBeDisabled ( )
387393 await click ( 'Continue' )
388394 expect ( document . querySelector ( '[role="dialog"]' ) ) . not . toHaveTextContent ( 'Client ID' )
389- await click ( 'Copy configuration ' )
395+ await click ( 'Copy manifest ' )
390396 expect ( button ( 'Continue' ) ) . not . toBeDisabled ( )
391397 expect ( mocks . copy ) . toHaveBeenCalledExactlyOnceWith ( '{}' )
392398 expect ( document . querySelector ( 'a[href="https://api.slack.com/apps/A1"]' ) ) . not . toBeNull ( )
393- expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Configuration copied' )
394- expect ( document . querySelector ( 'pre' ) ) . toBeNull ( )
395- await click ( 'Continue' )
396- expect (
397- document . querySelectorAll ( 'input[placeholder="Leave blank to keep the saved value"]' )
398- ) . toHaveLength ( 3 )
399+ expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Manifest copied' )
400+ expect ( document . querySelector ( 'details' ) ) . not . toHaveAttribute ( 'open' )
399401 await click ( 'Continue' )
400- expect ( button ( 'Connect app ' ) ) . toBeDisabled ( )
402+ expect ( button ( 'Continue ' ) ) . toBeDisabled ( )
401403 await act ( async ( ) => {
402404 const input = document . querySelector < HTMLInputElement > ( 'input[placeholder="xoxb-..."]' ) !
403405 Object . getOwnPropertyDescriptor ( HTMLInputElement . prototype , 'value' ) ! . set ! . call (
@@ -406,6 +408,10 @@ describe('Slack Search settings and shared wizard', () => {
406408 )
407409 input . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) )
408410 } )
411+ await click ( 'Continue' )
412+ expect (
413+ document . querySelectorAll ( 'input[placeholder="Leave blank to keep the saved value"]' )
414+ ) . toHaveLength ( 3 )
409415 await click ( 'Connect app' )
410416 expect ( mocks . connect ) . toHaveBeenCalledWith (
411417 expect . objectContaining ( {
@@ -423,12 +429,12 @@ describe('Slack Search settings and shared wizard', () => {
423429 mocks . copy . mockRejectedValueOnce ( new Error ( 'Clipboard access denied' ) )
424430 await render ( true )
425431 await action ( 'Reconnect' )
426- await click ( 'Copy configuration ' )
432+ await click ( 'Copy manifest ' )
427433 expect ( document . querySelector ( '[role="alert"]' ) ) . toHaveTextContent ( 'Allow clipboard access' )
428- expect ( document . querySelector ( 'a[href="https://api.slack.com/apps/A1"]' ) ) . toBeNull ( )
429- expect ( button ( 'Copy configuration ' ) ) . toBeDefined ( )
434+ expect ( document . querySelector ( 'a[href="https://api.slack.com/apps/A1"]' ) ) . not . toBeNull ( )
435+ expect ( button ( 'Copy manifest ' ) ) . toBeDefined ( )
430436 expect ( button ( 'Continue' ) ) . toBeDisabled ( )
431- await click ( 'Copy configuration ' )
437+ await click ( 'Copy manifest ' )
432438 expect ( document . querySelector ( '[role="alert"]' ) ) . toBeNull ( )
433439 expect ( document . querySelector ( 'a[href="https://api.slack.com/apps/A1"]' ) ) . not . toBeNull ( )
434440 } )
@@ -446,12 +452,81 @@ describe('Slack Search settings and shared wizard', () => {
446452 await render ( )
447453 await click ( 'Set up' )
448454 expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Update Slack app' )
449- await click ( 'Copy configuration ' )
455+ await click ( 'Copy manifest ' )
450456 expect ( mocks . copy ) . toHaveBeenCalledExactlyOnceWith (
451457 '{"display_information":{"name":"Shared Slack app"}}'
452458 )
453459 expect ( document . querySelector ( 'a[href="https://api.slack.com/apps/A2"]' ) ) . not . toBeNull ( )
454- expect ( document . querySelector ( 'pre' ) ) . toBeNull ( )
460+ expect ( document . querySelector ( 'details' ) ) . not . toHaveAttribute ( 'open' )
461+ } )
462+
463+ it ( 'keeps the token and credentials when navigating back through setup' , async ( ) => {
464+ await render ( )
465+ await click ( 'Set up' )
466+ await click ( 'Copy manifest' )
467+ await click ( 'Continue' )
468+ expect ( document . querySelector ( '[role="dialog"]' ) ) . toHaveTextContent ( 'Install Slack app' )
469+ expect ( button ( 'Continue' ) ) . toBeDisabled ( )
470+ async function fill ( placeholder : string , value : string ) {
471+ await act ( async ( ) => {
472+ const input = document . querySelector < HTMLInputElement > (
473+ `input[placeholder="${ placeholder } "]`
474+ ) !
475+ Object . getOwnPropertyDescriptor ( HTMLInputElement . prototype , 'value' ) ! . set ! . call (
476+ input ,
477+ value
478+ )
479+ input . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) )
480+ } )
481+ }
482+ await fill ( 'xoxb-...' , 'xoxb-test-token' )
483+ await click ( 'Continue' )
484+ expect ( button ( 'Connect app' ) ) . toBeDisabled ( )
485+ await fill ( 'Paste your Slack app’s client ID' , 'client-id' )
486+ await fill ( 'Paste your Slack app’s client secret' , 'client-secret' )
487+ await fill ( 'Paste your Slack app’s signing secret' , 'signing-secret' )
488+ await click ( 'Back' )
489+ expect ( document . querySelector ( 'input' ) ) . toHaveValue ( 'xoxb-test-token' )
490+ await click ( 'Back' )
491+ expect ( button ( 'Copy manifest' ) ) . toBeDefined ( )
492+ await click ( 'Continue' )
493+ await click ( 'Continue' )
494+ expect (
495+ document . querySelector ( 'input[placeholder="Paste your Slack app’s client ID"]' )
496+ ) . toHaveValue ( 'client-id' )
497+ await click ( 'Connect app' )
498+ expect ( mocks . connect ) . toHaveBeenCalledWith (
499+ expect . objectContaining ( {
500+ botToken : 'xoxb-test-token' ,
501+ clientId : 'client-id' ,
502+ clientSecret : 'client-secret' ,
503+ signingSecret : 'signing-secret' ,
504+ } ) ,
505+ expect . any ( Object )
506+ )
507+ } )
508+
509+ it ( 'requires copying a refreshed manifest and blocks cached preparation errors' , async ( ) => {
510+ await render ( )
511+ await click ( 'Set up' )
512+ await click ( 'Copy manifest' )
513+ const data = { manifest : '{"display_information":{"name":"Updated"}}' , existingApp : null }
514+ mocks . manifest . mockReturnValue ( { data } )
515+ await render ( )
516+ expect ( button ( 'Continue' ) ) . toBeDisabled ( )
517+ await click ( 'Copy manifest' )
518+ expect ( mocks . copy ) . toHaveBeenLastCalledWith ( data . manifest )
519+ expect ( button ( 'Continue' ) ) . not . toBeDisabled ( )
520+ mocks . manifest . mockReturnValue ( {
521+ data,
522+ error : new Error ( 'Manifest unavailable' ) ,
523+ refetch : mocks . refetch ,
524+ } )
525+ await render ( )
526+ expect ( button ( 'Copy manifest' ) ) . toBeDisabled ( )
527+ expect ( button ( 'Continue' ) ) . toBeDisabled ( )
528+ await click ( 'Retry' )
529+ expect ( mocks . refetch ) . toHaveBeenCalledOnce ( )
455530 } )
456531
457532 it ( 'disables the selected connection from the actions menu' , async ( ) => {
0 commit comments