1+ import { describe , it , expect , vi } from 'vitest'
2+ import { render , screen , fireEvent , createEvent } from '@testing-library/react'
3+ import userEvent from '@testing-library/user-event'
4+ import { RequestUrlBar } from '@/components/RequestUrlBar'
5+
6+ // Mock Radix UI's Select component
7+ vi . mock ( '@/components/ui/select' , ( ) => ( {
8+ Select : ( { value, onValueChange, children } : any ) => (
9+ < select
10+ value = { value }
11+ onChange = { ( e ) => onValueChange ( e . target . value ) }
12+ data-testid = "method-select"
13+ >
14+ { children }
15+ </ select >
16+ ) ,
17+ SelectTrigger : ( { children } : any ) => children ,
18+ SelectValue : ( { placeholder } : any ) => placeholder ,
19+ SelectContent : ( { children } : any ) => children ,
20+ SelectItem : ( { value, children } : any ) => (
21+ < option value = { value } > { children } </ option >
22+ ) ,
23+ } ) )
24+
25+ describe ( 'RequestUrlBar' , ( ) => {
26+ interface SetupOptions {
27+ method ?: string
28+ url ?: string
29+ loading ?: boolean
30+ }
31+
32+ const setup = ( options : SetupOptions = { } ) => {
33+ const user = userEvent . setup ( )
34+ const props = {
35+ method : options . method || 'GET' ,
36+ url : options . url || '' ,
37+ loading : options . loading || false ,
38+ onMethodChange : vi . fn ( ) ,
39+ onUrlChange : vi . fn ( ) ,
40+ onSend : vi . fn ( ) ,
41+ onSave : vi . fn ( ) ,
42+ }
43+
44+ const utils = render ( < RequestUrlBar { ...props } /> )
45+
46+ return {
47+ user,
48+ ...utils ,
49+ ...props ,
50+ }
51+ }
52+
53+ it ( 'renders with default props' , ( ) => {
54+ setup ( )
55+
56+ expect ( screen . getByTestId ( 'method-select' ) ) . toBeInTheDocument ( )
57+ expect ( screen . getByPlaceholderText ( 'Enter request URL' ) ) . toBeInTheDocument ( )
58+ expect ( screen . getByRole ( 'button' , { name : / S a v e / i } ) ) . toBeInTheDocument ( )
59+ expect ( screen . getByRole ( 'button' , { name : / S e n d / i } ) ) . toBeInTheDocument ( )
60+ } )
61+
62+ it ( 'displays the current method and URL' , ( ) => {
63+ setup ( {
64+ method : 'POST' ,
65+ url : 'https://api.example.com'
66+ } )
67+
68+ expect ( screen . getByTestId ( 'method-select' ) ) . toHaveValue ( 'POST' )
69+ expect ( screen . getByPlaceholderText ( 'Enter request URL' ) ) . toHaveValue ( 'https://api.example.com' )
70+ } )
71+
72+ it ( 'calls onMethodChange when method is changed' , ( ) => {
73+ const { onMethodChange } = setup ( { method : 'GET' } )
74+
75+ const methodSelect = screen . getByTestId ( 'method-select' )
76+ fireEvent . change ( methodSelect , { target : { value : 'POST' } } )
77+
78+ expect ( onMethodChange ) . toHaveBeenCalledWith ( 'POST' )
79+ } )
80+
81+ it ( 'calls onUrlChange when URL is changed' , ( ) => {
82+ const { onUrlChange } = setup ( )
83+
84+ const urlInput = screen . getByPlaceholderText ( 'Enter request URL' )
85+ fireEvent . change ( urlInput , { target : { value : 'https://api.example.com' } } )
86+
87+ expect ( onUrlChange ) . toHaveBeenCalledWith ( 'https://api.example.com' )
88+ } )
89+
90+ it ( 'prevents default question mark behavior in URL input' , ( ) => {
91+ setup ( )
92+
93+ const urlInput = screen . getByPlaceholderText ( 'Enter request URL' )
94+ const event = createEvent . keyDown ( urlInput , { key : '?' } )
95+ event . stopPropagation = vi . fn ( )
96+
97+ fireEvent ( urlInput , event )
98+
99+ expect ( event . stopPropagation ) . toHaveBeenCalled ( )
100+ } )
101+
102+ it ( 'does not stop propagation for non-question mark keys' , ( ) => {
103+ setup ( )
104+
105+ const urlInput = screen . getByPlaceholderText ( 'Enter request URL' )
106+ const event = createEvent . keyDown ( urlInput , { key : 'a' } )
107+ event . stopPropagation = vi . fn ( )
108+
109+ fireEvent ( urlInput , event )
110+
111+ expect ( event . stopPropagation ) . not . toHaveBeenCalled ( )
112+ } )
113+
114+ it ( 'calls onSave when save button is clicked' , async ( ) => {
115+ const { user, onSave } = setup ( )
116+
117+ const saveButton = screen . getByRole ( 'button' , { name : / S a v e / i } )
118+ await user . click ( saveButton )
119+
120+ expect ( onSave ) . toHaveBeenCalled ( )
121+ } )
122+
123+ it ( 'calls onSend when send button is clicked' , async ( ) => {
124+ const { user, onSend } = setup ( )
125+
126+ const sendButton = screen . getByRole ( 'button' , { name : / S e n d / i } )
127+ await user . click ( sendButton )
128+
129+ expect ( onSend ) . toHaveBeenCalled ( )
130+ } )
131+
132+ it ( 'disables send button when loading' , ( ) => {
133+ setup ( { loading : true } )
134+
135+ const sendButton = screen . getByRole ( 'button' , { name : / S e n d i n g / i } )
136+ expect ( sendButton ) . toBeDisabled ( )
137+ } )
138+
139+ it ( 'changes send button text to "Sending..." when loading' , ( ) => {
140+ setup ( { loading : true } )
141+
142+ expect ( screen . getByRole ( 'button' , { name : / S e n d i n g / i } ) ) . toBeInTheDocument ( )
143+ expect ( screen . queryByRole ( 'button' , { name : / ^ S e n d $ / i } ) ) . not . toBeInTheDocument ( )
144+ } )
145+
146+ it ( 'supports all HTTP methods' , ( ) => {
147+ const { onMethodChange } = setup ( )
148+ const methods = [ "GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "HEAD" , "OPTIONS" ]
149+
150+ const methodSelect = screen . getByTestId ( 'method-select' )
151+
152+ for ( const method of methods ) {
153+ fireEvent . change ( methodSelect , { target : { value : method } } )
154+ expect ( onMethodChange ) . toHaveBeenCalledWith ( method )
155+ }
156+ } )
157+ } )
0 commit comments