@@ -3,41 +3,20 @@ import {OrganizationFixture} from 'sentry-fixture/organization';
33import { render , screen , userEvent , waitFor } from 'sentry-test/reactTestingLibrary' ;
44
55import { OrganizationSampling } from 'sentry/views/settings/dynamicSampling/organizationSampling' ;
6- import type { ProjectsPreviewTableProps } from 'sentry/views/settings/dynamicSampling/projectsPreviewTable' ;
7-
8- // Render only the form-relevant parts: the input and the action buttons.
9- // This avoids pulling in the virtualized ProjectsTable.
10- jest . mock ( 'sentry/views/settings/dynamicSampling/projectsPreviewTable' , ( ) => ( {
11- ProjectsPreviewTable : ( {
12- actions,
13- targetSampleRate,
14- savedTargetSampleRate,
15- onTargetSampleRateChange,
16- targetSampleRateError,
17- } : ProjectsPreviewTableProps ) => (
18- < div >
19- < label htmlFor = "target-sample-rate" > Target Sample Rate</ label >
20- < input
21- id = "target-sample-rate"
22- type = "number"
23- value = { targetSampleRate }
24- onChange = { e => onTargetSampleRateChange ( e . target . value ) }
25- />
26- { targetSampleRateError && < span role = "alert" > { targetSampleRateError } </ span > }
27- { savedTargetSampleRate !== targetSampleRate && (
28- < span > previous: { savedTargetSampleRate } %</ span >
29- ) }
30- { actions }
31- </ div >
32- ) ,
33- } ) ) ;
34-
35- jest . mock ( 'sentry/views/settings/dynamicSampling/samplingModeSwitch' , ( ) => ( {
36- SamplingModeSwitch : ( ) => null ,
37- } ) ) ;
386
39- jest . mock ( 'sentry/views/settings/dynamicSampling/projectionPeriodControl' , ( ) => ( {
40- ProjectionPeriodControl : ( ) => null ,
7+ jest . mock ( '@tanstack/react-virtual' , ( ) => ( {
8+ useVirtualizer : jest . fn ( ( { count} : { count : number } ) => ( {
9+ getVirtualItems : jest . fn ( ( ) =>
10+ Array . from ( { length : count } , ( _ , index ) => ( {
11+ key : index ,
12+ index,
13+ start : index * 63 ,
14+ size : 63 ,
15+ } ) )
16+ ) ,
17+ getTotalSize : jest . fn ( ( ) => count * 63 ) ,
18+ measure : jest . fn ( ) ,
19+ } ) ) ,
4120} ) ) ;
4221
4322describe ( 'OrganizationSampling' , ( ) => {
@@ -50,12 +29,16 @@ describe('OrganizationSampling', () => {
5029
5130 beforeEach ( ( ) => {
5231 MockApiClient . clearMockResponses ( ) ;
32+ MockApiClient . addMockResponse ( {
33+ url : '/organizations/org-slug/sampling/project-root-counts/' ,
34+ body : { data : [ ] , end : '' , intervals : [ ] , start : '' } ,
35+ } ) ;
5336 } ) ;
5437
5538 it ( 'pre-fills the input with the organization target sample rate' , ( ) => {
5639 render ( < OrganizationSampling /> , { organization} ) ;
5740
58- expect ( screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ) . toHaveValue ( 50 ) ;
41+ expect ( screen . getByRole ( 'spinbutton' ) ) . toHaveValue ( 50 ) ;
5942 } ) ;
6043
6144 it ( 'Save and Reset buttons are disabled when the form is clean' , ( ) => {
@@ -68,9 +51,8 @@ describe('OrganizationSampling', () => {
6851 it ( 'enables Save and Reset buttons after changing the rate' , async ( ) => {
6952 render ( < OrganizationSampling /> , { organization} ) ;
7053
71- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
72- await userEvent . clear ( input ) ;
73- await userEvent . type ( input , '30' ) ;
54+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
55+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
7456
7557 expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeEnabled ( ) ;
7658 expect ( screen . getByRole ( 'button' , { name : 'Reset' } ) ) . toBeEnabled ( ) ;
@@ -79,22 +61,20 @@ describe('OrganizationSampling', () => {
7961 it ( 'disables Save and shows a validation error for an out-of-range value' , async ( ) => {
8062 render ( < OrganizationSampling /> , { organization} ) ;
8163
82- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
83- await userEvent . clear ( input ) ;
84- await userEvent . type ( input , '150' ) ;
64+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
65+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '150' ) ;
8566
8667 expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( ) ;
87- expect ( screen . getByRole ( 'alert' ) ) . toHaveTextContent ( ' Must be between 0% and 100%') ;
68+ expect ( screen . getByText ( ' Must be between 0% and 100%') ) . toBeInTheDocument ( ) ;
8869 } ) ;
8970
9071 it ( 'disables Save and shows a validation error for an empty value' , async ( ) => {
9172 render ( < OrganizationSampling /> , { organization} ) ;
9273
93- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
94- await userEvent . clear ( input ) ;
74+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
9575
9676 expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( ) ;
97- expect ( screen . getByRole ( 'alert' ) ) . toHaveTextContent ( ' This field is required.') ;
77+ expect ( screen . getByText ( ' This field is required.') ) . toBeInTheDocument ( ) ;
9878 } ) ;
9979
10080 it ( 'calls the API with the correct payload on save' , async ( ) => {
@@ -106,17 +86,14 @@ describe('OrganizationSampling', () => {
10686
10787 render ( < OrganizationSampling /> , { organization} ) ;
10888
109- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
110- await userEvent . clear ( input ) ;
111- await userEvent . type ( input , '30' ) ;
89+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
90+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
11291 await userEvent . click ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) ;
11392
11493 await waitFor ( ( ) => {
11594 expect ( putMock ) . toHaveBeenCalledWith (
11695 '/organizations/org-slug/' ,
117- expect . objectContaining ( {
118- data : { targetSampleRate : 0.3 } ,
119- } )
96+ expect . objectContaining ( { data : { targetSampleRate : 0.3 } } )
12097 ) ;
12198 } ) ;
12299 } ) ;
@@ -130,45 +107,18 @@ describe('OrganizationSampling', () => {
130107
131108 render ( < OrganizationSampling /> , { organization} ) ;
132109
133- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
134- await userEvent . clear ( input ) ;
135- await userEvent . type ( input , '30' ) ;
110+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
111+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
136112 await userEvent . click ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) ;
137113
138- await waitFor ( ( ) => {
139- expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( ) ;
140- } ) ;
114+ await waitFor ( ( ) =>
115+ expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( )
116+ ) ;
141117 expect ( screen . getByRole ( 'button' , { name : 'Reset' } ) ) . toBeDisabled ( ) ;
142118 } ) ;
143119
144- it ( 'updates the previous value display after a successful save ' , async ( ) => {
120+ it ( 'keeps form dirty after an API error ' , async ( ) => {
145121 MockApiClient . addMockResponse ( {
146- url : '/organizations/org-slug/' ,
147- method : 'PUT' ,
148- body : OrganizationFixture ( { targetSampleRate : 0.3 } ) ,
149- } ) ;
150-
151- render ( < OrganizationSampling /> , { organization} ) ;
152-
153- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
154- await userEvent . clear ( input ) ;
155- await userEvent . type ( input , '30' ) ;
156- await userEvent . click ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) ;
157-
158- // After save, change the value again to reveal the "previous" display
159- await waitFor ( ( ) => {
160- expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( ) ;
161- } ) ;
162-
163- await userEvent . clear ( input ) ;
164- await userEvent . type ( input , '20' ) ;
165-
166- // Previous value should now be the just-saved 30, not the original 50
167- expect ( screen . getByText ( 'previous: 30%' ) ) . toBeInTheDocument ( ) ;
168- } ) ;
169-
170- it ( 'keeps form dirty and does not call API again after an error' , async ( ) => {
171- const putMock = MockApiClient . addMockResponse ( {
172122 url : '/organizations/org-slug/' ,
173123 method : 'PUT' ,
174124 statusCode : 500 ,
@@ -177,27 +127,24 @@ describe('OrganizationSampling', () => {
177127
178128 render ( < OrganizationSampling /> , { organization} ) ;
179129
180- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
181- await userEvent . clear ( input ) ;
182- await userEvent . type ( input , '30' ) ;
130+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
131+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
183132 await userEvent . click ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) ;
184133
185- await waitFor ( ( ) => expect ( putMock ) . toHaveBeenCalledTimes ( 1 ) ) ;
186-
187- expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeEnabled ( ) ;
134+ await waitFor ( ( ) =>
135+ expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeEnabled ( )
136+ ) ;
188137 expect ( screen . getByRole ( 'button' , { name : 'Reset' } ) ) . toBeEnabled ( ) ;
189138 } ) ;
190139
191140 it ( 'resets the input back to the saved value when Reset is clicked' , async ( ) => {
192141 render ( < OrganizationSampling /> , { organization} ) ;
193142
194- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
195- await userEvent . clear ( input ) ;
196- await userEvent . type ( input , '30' ) ;
197-
143+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
144+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
198145 await userEvent . click ( screen . getByRole ( 'button' , { name : 'Reset' } ) ) ;
199146
200- expect ( input ) . toHaveValue ( 50 ) ;
147+ expect ( screen . getByRole ( 'spinbutton' ) ) . toHaveValue ( 50 ) ;
201148 } ) ;
202149
203150 it ( 'disables the Save button for users without org:write access' , async ( ) => {
@@ -209,12 +156,10 @@ describe('OrganizationSampling', () => {
209156
210157 render ( < OrganizationSampling /> , { organization : orgWithoutAccess } ) ;
211158
212- const input = screen . getByRole ( 'spinbutton' , { name : 'Target Sample Rate' } ) ;
213- await userEvent . clear ( input ) ;
214- await userEvent . type ( input , '30' ) ;
159+ await userEvent . clear ( screen . getByRole ( 'spinbutton' ) ) ;
160+ await userEvent . type ( screen . getByRole ( 'spinbutton' ) , '30' ) ;
215161
216162 expect ( screen . getByRole ( 'button' , { name : 'Save changes' } ) ) . toBeDisabled ( ) ;
217- // Reset is unrelated to permissions so it stays enabled
218163 expect ( screen . getByRole ( 'button' , { name : 'Reset' } ) ) . toBeEnabled ( ) ;
219164 } ) ;
220165} ) ;
0 commit comments