@@ -15,120 +15,110 @@ function renderForm(props: Partial<Parameters<typeof PolicyForm>[0]> = {}) {
1515 )
1616}
1717
18- describe ( 'PolicyForm — deny + column_mask validation ' , ( ) => {
19- it ( 'column_mask option is visible when effect is permit' , async ( ) => {
18+ describe ( 'PolicyForm — policy type selector ' , ( ) => {
19+ it ( 'renders all 5 policy type options' , ( ) => {
2020 renderForm ( )
21- // Add an obligation — effect defaults to permit
22- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
23- const comboboxes = screen . getAllByRole ( 'combobox' )
24- // The obligation type select is the last combobox
25- const obligationTypeSelect = comboboxes [ comboboxes . length - 1 ]
26- const options = Array . from ( obligationTypeSelect . querySelectorAll ( 'option' ) ) . map ( o => o . value )
21+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
22+ const options = Array . from ( select . querySelectorAll ( 'option' ) ) . map ( ( o ) => o . value )
23+ expect ( options ) . toContain ( 'row_filter' )
2724 expect ( options ) . toContain ( 'column_mask' )
25+ expect ( options ) . toContain ( 'column_allow' )
26+ expect ( options ) . toContain ( 'column_deny' )
27+ expect ( options ) . toContain ( 'table_deny' )
2828 } )
2929
30- it ( 'column_mask option is hidden when effect is deny ' , async ( ) => {
30+ it ( 'shows deny note when column_deny is selected ' , async ( ) => {
3131 renderForm ( )
32- // Switch effect to deny
33- const effectSelect = screen . getAllByRole ( 'combobox' ) [ 0 ]
34- await userEvent . selectOptions ( effectSelect , 'deny' )
35- // Add an obligation
36- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
37- const typeSelects = screen . getAllByRole ( 'combobox' )
38- // The obligation type select is the last combobox
39- const obligationTypeSelect = typeSelects [ typeSelects . length - 1 ]
40- const options = Array . from ( obligationTypeSelect . querySelectorAll ( 'option' ) ) . map ( o => o . value )
41- expect ( options ) . not . toContain ( 'column_mask' )
32+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
33+ await userEvent . selectOptions ( select , 'column_deny' )
34+ expect ( screen . getByText ( / D e n y p o l i c i e s s h o r t - c i r c u i t o r s t r i p c o l u m n s / i) ) . toBeTruthy ( )
4235 } )
4336
44- it ( 'switching effect to deny removes existing column_mask obligations ' , async ( ) => {
37+ it ( 'shows filter expression field when row_filter is selected ' , async ( ) => {
4538 renderForm ( )
46- // Add a column_mask obligation while effect is permit
47- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
48- const typeSelects = screen . getAllByRole ( 'combobox' )
49- const obligationTypeSelect = typeSelects [ typeSelects . length - 1 ]
50- await userEvent . selectOptions ( obligationTypeSelect , 'column_mask' )
51- // Verify the column_mask obligation is shown
52- expect ( screen . getByText ( 'Obligation 1' ) ) . toBeTruthy ( )
53- // Switch effect to deny
54- const effectSelect = screen . getAllByRole ( 'combobox' ) [ 0 ]
55- await userEvent . selectOptions ( effectSelect , 'deny' )
56- // The column_mask obligation should have been removed
57- expect ( screen . queryByText ( 'Obligation 1' ) ) . toBeNull ( )
39+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
40+ await userEvent . selectOptions ( select , 'row_filter' )
41+ expect ( screen . getByText ( / F i l t e r E x p r e s s i o n / i) ) . toBeTruthy ( )
42+ expect ( screen . queryByText ( / M a s k E x p r e s s i o n / i) ) . toBeNull ( )
5843 } )
5944
60- it ( 'shows a note about column masking when effect is deny ' , async ( ) => {
45+ it ( 'shows mask expression field when column_mask is selected ' , async ( ) => {
6146 renderForm ( )
62- const effectSelect = screen . getAllByRole ( 'combobox' ) [ 0 ]
63- await userEvent . selectOptions ( effectSelect , 'deny' )
64- expect ( screen . getByText ( / C o l u m n m a s k i n g i s n o t a v a i l a b l e o n d e n y p o l i c i e s / i) ) . toBeTruthy ( )
47+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
48+ await userEvent . selectOptions ( select , 'column_mask' )
49+ expect ( screen . getByText ( / M a s k E x p r e s s i o n / i) ) . toBeTruthy ( )
50+ expect ( screen . queryByText ( / F i l t e r E x p r e s s i o n / i) ) . toBeNull ( )
51+ } )
52+
53+ it ( 'hides definition fields when table_deny is selected' , async ( ) => {
54+ renderForm ( )
55+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
56+ await userEvent . selectOptions ( select , 'table_deny' )
57+ expect ( screen . queryByText ( / F i l t e r E x p r e s s i o n / i) ) . toBeNull ( )
58+ expect ( screen . queryByText ( / M a s k E x p r e s s i o n / i) ) . toBeNull ( )
6559 } )
6660} )
6761
68- describe ( 'PolicyForm — object_access obligation type ' , ( ) => {
69- it ( 'object_access option is available in the type selector' , async ( ) => {
62+ describe ( 'PolicyForm — targets ' , ( ) => {
63+ it ( 'starts with one target entry' , ( ) => {
7064 renderForm ( )
71- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
72- const typeSelects = screen . getAllByRole ( 'combobox' )
73- const obligationTypeSelect = typeSelects [ typeSelects . length - 1 ]
74- const options = Array . from ( obligationTypeSelect . querySelectorAll ( 'option' ) ) . map ( o => o . value )
75- expect ( options ) . toContain ( 'object_access' )
65+ expect ( screen . getByText ( 'Target 1' ) ) . toBeTruthy ( )
7666 } )
7767
78- it ( 'object_access is available on deny policies ' , async ( ) => {
68+ it ( 'adds a target when "+ Add target" is clicked ' , async ( ) => {
7969 renderForm ( )
80- const effectSelect = screen . getAllByRole ( 'combobox' ) [ 0 ]
81- await userEvent . selectOptions ( effectSelect , 'deny' )
82- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
83- const typeSelects = screen . getAllByRole ( 'combobox' )
84- const obligationTypeSelect = typeSelects [ typeSelects . length - 1 ]
85- const options = Array . from ( obligationTypeSelect . querySelectorAll ( 'option' ) ) . map ( o => o . value )
86- expect ( options ) . toContain ( 'object_access' )
70+ await userEvent . click ( screen . getByText ( '+ Add target' ) )
71+ expect ( screen . getByText ( 'Target 2' ) ) . toBeTruthy ( )
8772 } )
8873
89- it ( 'object_access shows schema field and optional table field ' , async ( ) => {
74+ it ( 'removes a target when Remove is clicked ' , async ( ) => {
9075 renderForm ( )
91- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
92- const typeSelects = screen . getAllByRole ( 'combobox' )
93- const obligationTypeSelect = typeSelects [ typeSelects . length - 1 ]
94- await userEvent . selectOptions ( obligationTypeSelect , 'object_access' )
95- // Should show the hint text for object_access
96- expect ( screen . getByText ( / H i d e s t h e e n t i r e s c h e m a / i) ) . toBeTruthy ( )
97- // Table field should have "leave blank" placeholder hint
98- expect ( document . body . textContent ) . toMatch ( / o p t i o n a l / i)
76+ await userEvent . click ( screen . getByText ( '+ Add target' ) )
77+ expect ( screen . getByText ( 'Target 2' ) ) . toBeTruthy ( )
78+ const removeButtons = screen . getAllByText ( 'Remove' )
79+ await userEvent . click ( removeButtons [ 0 ] )
80+ expect ( screen . queryByText ( 'Target 2' ) ) . toBeNull ( )
81+ } )
82+
83+ it ( 'shows columns field for column_allow' , async ( ) => {
84+ renderForm ( )
85+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
86+ await userEvent . selectOptions ( select , 'column_allow' )
87+ expect ( screen . getByText ( 'Columns' ) ) . toBeTruthy ( )
88+ } )
89+
90+ it ( 'hides columns field for row_filter' , async ( ) => {
91+ renderForm ( )
92+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
93+ await userEvent . selectOptions ( select , 'row_filter' )
94+ expect ( screen . queryByText ( 'Columns' ) ) . toBeNull ( )
95+ } )
96+
97+ it ( 'hides columns field for table_deny' , async ( ) => {
98+ renderForm ( )
99+ const select = screen . getAllByRole ( 'combobox' ) [ 0 ]
100+ await userEvent . selectOptions ( select , 'table_deny' )
101+ expect ( screen . queryByText ( 'Columns' ) ) . toBeNull ( )
99102 } )
100103} )
101104
102- describe ( 'PolicyForm — submits object_access obligation correctly ' , ( ) => {
103- it ( 'omits table field when blank for schema-level deny ' , async ( ) => {
105+ describe ( 'PolicyForm — submission ' , ( ) => {
106+ it ( 'calls onSubmit with policy_type and targets ' , async ( ) => {
104107 const onSubmit = vi . fn ( ) . mockResolvedValue ( undefined )
105108 renderForm ( { onSubmit } )
106109
107- // Add object_access obligation
108- await userEvent . click ( screen . getByText ( '+ Add obligation' ) )
109- const typeSelects = screen . getAllByRole ( 'combobox' )
110- await userEvent . selectOptions ( typeSelects [ typeSelects . length - 1 ] , 'object_access' )
111-
112- // Set schema
113- const textboxes = screen . getAllByRole ( 'textbox' )
114- // Find the schema input (has placeholder 'analytics')
115- const schemaInput = textboxes . find ( i => ( i as HTMLInputElement ) . placeholder === 'analytics' )
116- if ( schemaInput ) {
117- await userEvent . clear ( schemaInput )
118- await userEvent . type ( schemaInput , 'analytics' )
119- }
110+ // Name is required — fill it in
111+ const nameInput = screen . getAllByRole ( 'textbox' ) [ 0 ]
112+ await userEvent . clear ( nameInput )
113+ await userEvent . type ( nameInput , 'my-policy' )
120114
121- // Submit
122115 fireEvent . submit ( document . querySelector ( 'form' ) ! )
123- // Wait for onSubmit to be called
124- await new Promise ( r => setTimeout ( r , 0 ) )
116+ await new Promise ( ( r ) => setTimeout ( r , 0 ) )
125117
126118 if ( onSubmit . mock . calls . length > 0 ) {
127119 const values = onSubmit . mock . calls [ 0 ] [ 0 ]
128- const obl = values . obligations [ 0 ]
129- expect ( obl . obligation_type ) . toBe ( 'object_access' )
130- expect ( obl . definition . action ) . toBe ( 'deny' )
131- expect ( obl . definition . table ) . toBeUndefined ( )
120+ expect ( values . policy_type ) . toBe ( 'row_filter' )
121+ expect ( Array . isArray ( values . targets ) ) . toBe ( true )
132122 }
133123 } )
134124} )
0 commit comments