foreman_webhooks 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/foreman_webhooks/engine.rb +1 -1
- data/lib/foreman_webhooks/version.rb +1 -1
- data/test/integration/webhooks_test.rb +234 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js +321 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css +23 -4
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +191 -157
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js +216 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +55 -26
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js +253 -37
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +3 -4
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +30 -19
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +52 -18
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +37 -26
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookTestModal.js +57 -32
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +24 -11
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +64 -0
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +21 -22
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +9 -15
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/integration.test.js +0 -3
- metadata +7 -5
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/ForemanFormikField.js +0 -152
- data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap +0 -512
- data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/integration.test.js.snap +0 -31
data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js
CHANGED
|
@@ -4,13 +4,12 @@ import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
|
|
|
4
4
|
|
|
5
5
|
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
|
6
6
|
|
|
7
|
-
import ForemanFormikField from './ForemanFormikField';
|
|
8
|
-
|
|
9
7
|
import './WebhookFormTabs.css';
|
|
8
|
+
import FieldConstructor from './FieldConstructor';
|
|
10
9
|
|
|
11
10
|
const WebhookFormTabs = ({
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
inputValues,
|
|
12
|
+
setInputValues,
|
|
14
13
|
activeTab,
|
|
15
14
|
handleTabClick,
|
|
16
15
|
webhookTemplates,
|
|
@@ -19,160 +18,198 @@ const WebhookFormTabs = ({
|
|
|
19
18
|
isTemplatesLoading,
|
|
20
19
|
isEventsLoading,
|
|
21
20
|
isPasswordDisabled,
|
|
22
|
-
|
|
23
|
-
}) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
urlValidated,
|
|
22
|
+
}) => {
|
|
23
|
+
const updateFieldValue = (key, value) => {
|
|
24
|
+
setInputValues(prev => ({ ...prev, [key]: value }));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const URL_ERR_STRING = __('Enter valid URL');
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Tabs
|
|
31
|
+
activeKey={activeTab}
|
|
32
|
+
onSelect={handleTabClick}
|
|
33
|
+
isFilled
|
|
34
|
+
ouiaId="webhook-form-tabs"
|
|
35
35
|
>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
36
|
+
<Tab
|
|
37
|
+
ouiaId="webhook-form-tab-general"
|
|
38
|
+
className="webhook-form-tab"
|
|
39
|
+
eventKey={0}
|
|
40
|
+
title={<TabTitleText>{__('General')}</TabTitleText>}
|
|
41
|
+
>
|
|
42
|
+
<div className="webhook-form-tab-content">
|
|
43
|
+
<FieldConstructor
|
|
44
|
+
name="event"
|
|
45
|
+
value={inputValues.event}
|
|
46
|
+
setValue={updateFieldValue}
|
|
47
|
+
type="select"
|
|
48
|
+
label={__('Subscribe to')}
|
|
49
|
+
required
|
|
50
|
+
allowClear={false}
|
|
51
|
+
options={availableEvents}
|
|
52
|
+
isLoading={isEventsLoading}
|
|
53
|
+
placeholder={__('Start typing to search...')}
|
|
54
|
+
/>
|
|
55
|
+
<FieldConstructor
|
|
56
|
+
name="name"
|
|
57
|
+
value={inputValues.name}
|
|
58
|
+
setValue={updateFieldValue}
|
|
59
|
+
type="text"
|
|
60
|
+
required
|
|
61
|
+
label={__('Name')}
|
|
62
|
+
/>
|
|
63
|
+
<FieldConstructor
|
|
64
|
+
name="target_url"
|
|
65
|
+
value={inputValues.target_url}
|
|
66
|
+
setValue={updateFieldValue}
|
|
67
|
+
validated={urlValidated()}
|
|
68
|
+
errMsg={urlValidated() === 'error' ? URL_ERR_STRING : null}
|
|
69
|
+
type="text"
|
|
70
|
+
required
|
|
71
|
+
label={__('Target URL')}
|
|
72
|
+
labelHelp={
|
|
64
73
|
<div>
|
|
65
|
-
|
|
66
|
-
__(
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
<div>
|
|
75
|
+
{__(
|
|
76
|
+
'Target URL that should be called by Foreman (ERB allowed).'
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
<div>
|
|
80
|
+
{sprintf(
|
|
81
|
+
__('Example: %s'),
|
|
82
|
+
'https://host.example.com/inventory/<%= @object.id %>'
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
69
85
|
</div>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
86
|
+
}
|
|
87
|
+
/>
|
|
88
|
+
<FieldConstructor
|
|
89
|
+
name="webhook_template_id"
|
|
90
|
+
value={inputValues.webhook_template_id}
|
|
91
|
+
setValue={updateFieldValue}
|
|
92
|
+
type="select"
|
|
93
|
+
label={__('Template')}
|
|
94
|
+
required
|
|
95
|
+
allowClear={false}
|
|
96
|
+
options={webhookTemplates}
|
|
97
|
+
isLoading={isTemplatesLoading}
|
|
98
|
+
placeholder={__('Start typing to search...')}
|
|
99
|
+
/>
|
|
100
|
+
<FieldConstructor
|
|
101
|
+
name="http_method"
|
|
102
|
+
value={inputValues.http_method}
|
|
103
|
+
setValue={updateFieldValue}
|
|
104
|
+
type="select"
|
|
105
|
+
label={__('HTTP Method')}
|
|
106
|
+
required
|
|
107
|
+
allowClear={false}
|
|
108
|
+
options={httpMethods}
|
|
109
|
+
placeholder={__('Start typing to search...')}
|
|
110
|
+
/>
|
|
111
|
+
<FieldConstructor
|
|
112
|
+
name="enabled"
|
|
113
|
+
value={inputValues.enabled}
|
|
114
|
+
setValue={updateFieldValue}
|
|
115
|
+
type="checkbox"
|
|
116
|
+
label={__('Enabled')}
|
|
117
|
+
labelHelp={__('If unchecked, the webhook will be inactive')}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
</Tab>
|
|
121
|
+
<Tab
|
|
122
|
+
ouiaId="webhook-form-tab-creds"
|
|
123
|
+
className="webhook-form-tab"
|
|
124
|
+
eventKey={1}
|
|
125
|
+
title={<TabTitleText>{__('Credentials')}</TabTitleText>}
|
|
126
|
+
>
|
|
127
|
+
<div className="webhook-form-tab-content">
|
|
128
|
+
<FieldConstructor
|
|
129
|
+
name="user"
|
|
130
|
+
value={inputValues.user}
|
|
131
|
+
type="text"
|
|
132
|
+
label={__('User')}
|
|
133
|
+
labelHelp={__('Authentication credentials')}
|
|
134
|
+
setValue={updateFieldValue}
|
|
135
|
+
/>
|
|
136
|
+
<FieldConstructor
|
|
137
|
+
name="password"
|
|
138
|
+
type="password"
|
|
139
|
+
value={inputValues.password}
|
|
140
|
+
label={__('Password')}
|
|
141
|
+
labelHelp={__('Authentication credentials')}
|
|
142
|
+
disabled={isPasswordDisabled}
|
|
143
|
+
setValue={updateFieldValue}
|
|
144
|
+
/>
|
|
145
|
+
<FieldConstructor
|
|
146
|
+
name="verify_ssl"
|
|
147
|
+
value={inputValues.verify_ssl}
|
|
148
|
+
type="checkbox"
|
|
149
|
+
label={__('Verify SSL')}
|
|
150
|
+
labelHelp={__(
|
|
151
|
+
"Uncheck this option to disable validation of the receiver's SSL certificate"
|
|
152
|
+
)}
|
|
153
|
+
setValue={updateFieldValue}
|
|
154
|
+
/>
|
|
155
|
+
<FieldConstructor
|
|
156
|
+
name="proxy_authorization"
|
|
157
|
+
type="checkbox"
|
|
158
|
+
value={inputValues.proxy_authorization}
|
|
159
|
+
label={__('Proxy Authorization')}
|
|
160
|
+
labelHelp={__(
|
|
161
|
+
'Authorize with Foreman client certificate and validate smart-proxy CA from Settings'
|
|
162
|
+
)}
|
|
163
|
+
setValue={updateFieldValue}
|
|
164
|
+
/>
|
|
165
|
+
<FieldConstructor
|
|
166
|
+
name="ssl_ca_certs"
|
|
167
|
+
value={inputValues.ssl_ca_certs}
|
|
168
|
+
type="textarea"
|
|
169
|
+
label={__('X509 Certificate Authorities')}
|
|
170
|
+
placeholder={__(
|
|
171
|
+
"Optional CAs in PEM format concatenated to verify the receiver's SSL certificate"
|
|
172
|
+
)}
|
|
173
|
+
inputSizeClass="col-md-8"
|
|
174
|
+
rows={8}
|
|
175
|
+
setValue={updateFieldValue}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</Tab>
|
|
179
|
+
<Tab
|
|
180
|
+
ouiaId="webhook-form-tab-add"
|
|
181
|
+
className="webhook-form-tab"
|
|
182
|
+
eventKey={2}
|
|
183
|
+
title={<TabTitleText>{__('Additional')}</TabTitleText>}
|
|
184
|
+
>
|
|
185
|
+
<div className="webhook-form-tab-content">
|
|
186
|
+
<FieldConstructor
|
|
187
|
+
value={inputValues.http_content_type}
|
|
188
|
+
name="http_content_type"
|
|
189
|
+
type="text"
|
|
190
|
+
label={__('HTTP Content Type')}
|
|
191
|
+
setValue={updateFieldValue}
|
|
192
|
+
/>
|
|
193
|
+
<FieldConstructor
|
|
194
|
+
name="http_headers"
|
|
195
|
+
type="textarea"
|
|
196
|
+
value={inputValues.http_headers}
|
|
197
|
+
label={__('HTTP Headers')}
|
|
198
|
+
labelHelp={__('Optional. Must be a JSON object (ERB allowed)')}
|
|
199
|
+
placeholder='{ "X-Shellhook-Arg-1": "value", "X-Shellhook-Arg-2": "<%= @object.id %>" }'
|
|
200
|
+
inputSizeClass="col-md-8"
|
|
201
|
+
rows={8}
|
|
202
|
+
setValue={updateFieldValue}
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
</Tab>
|
|
206
|
+
</Tabs>
|
|
207
|
+
);
|
|
208
|
+
};
|
|
172
209
|
|
|
173
210
|
WebhookFormTabs.propTypes = {
|
|
174
|
-
|
|
175
|
-
|
|
211
|
+
inputValues: PropTypes.object.isRequired,
|
|
212
|
+
setInputValues: PropTypes.func.isRequired,
|
|
176
213
|
activeTab: PropTypes.number.isRequired,
|
|
177
214
|
handleTabClick: PropTypes.func.isRequired,
|
|
178
215
|
webhookTemplates: PropTypes.array.isRequired,
|
|
@@ -181,14 +218,11 @@ WebhookFormTabs.propTypes = {
|
|
|
181
218
|
isTemplatesLoading: PropTypes.bool.isRequired,
|
|
182
219
|
isEventsLoading: PropTypes.bool.isRequired,
|
|
183
220
|
isPasswordDisabled: PropTypes.bool,
|
|
184
|
-
|
|
221
|
+
urlValidated: PropTypes.func.isRequired,
|
|
185
222
|
};
|
|
186
223
|
|
|
187
224
|
WebhookFormTabs.defaultProps = {
|
|
188
|
-
disabled: false,
|
|
189
|
-
formProps: {},
|
|
190
225
|
isPasswordDisabled: false,
|
|
191
|
-
setIsPasswordDisabled: undefined,
|
|
192
226
|
};
|
|
193
227
|
|
|
194
228
|
export default WebhookFormTabs;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
|
+
import FieldConstructor from '../FieldConstructor';
|
|
6
|
+
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
name: 'test-field',
|
|
9
|
+
setValue: jest.fn(),
|
|
10
|
+
label: 'Test Field',
|
|
11
|
+
value: '',
|
|
12
|
+
type: 'text',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('FieldConstructor RTL Tests', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Text Input Fields', () => {
|
|
21
|
+
test('renders text input field', () => {
|
|
22
|
+
render(<FieldConstructor {...defaultProps} type="text" />);
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText('Test Field')).toBeInTheDocument();
|
|
25
|
+
expect(document.getElementById('id-test-field')).toHaveAttribute(
|
|
26
|
+
'type',
|
|
27
|
+
'text'
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('renders password input field', () => {
|
|
32
|
+
render(<FieldConstructor {...defaultProps} type="password" />);
|
|
33
|
+
|
|
34
|
+
expect(screen.getByText('Test Field')).toBeInTheDocument();
|
|
35
|
+
expect(document.getElementById('id-test-field')).toHaveAttribute(
|
|
36
|
+
'type',
|
|
37
|
+
'password'
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('calls setValue when text input changes', () => {
|
|
42
|
+
render(<FieldConstructor {...defaultProps} type="text" />);
|
|
43
|
+
|
|
44
|
+
const input = document.getElementById('id-test-field');
|
|
45
|
+
fireEvent.change(input, { target: { value: 'test value' } });
|
|
46
|
+
|
|
47
|
+
expect(defaultProps.setValue).toHaveBeenCalledWith(
|
|
48
|
+
'test-field',
|
|
49
|
+
'test value'
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('renders with initial value', () => {
|
|
54
|
+
render(
|
|
55
|
+
<FieldConstructor {...defaultProps} type="text" value="initial value" />
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(document.getElementById('id-test-field')).toHaveValue(
|
|
59
|
+
'initial value'
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('renders as disabled when loading', () => {
|
|
64
|
+
render(<FieldConstructor {...defaultProps} type="text" isLoading />);
|
|
65
|
+
|
|
66
|
+
expect(document.getElementById('id-test-field')).toBeDisabled();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Textarea Fields', () => {
|
|
71
|
+
test('renders textarea field', () => {
|
|
72
|
+
render(<FieldConstructor {...defaultProps} type="textarea" />);
|
|
73
|
+
|
|
74
|
+
expect(screen.getByText('Test Field')).toBeInTheDocument();
|
|
75
|
+
expect(document.getElementById('id-test-field').tagName).toBe('TEXTAREA');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('calls setValue when textarea changes', () => {
|
|
79
|
+
render(<FieldConstructor {...defaultProps} type="textarea" />);
|
|
80
|
+
|
|
81
|
+
const textarea = document.getElementById('id-test-field');
|
|
82
|
+
fireEvent.change(textarea, { target: { value: 'textarea content' } });
|
|
83
|
+
|
|
84
|
+
expect(defaultProps.setValue).toHaveBeenCalledWith(
|
|
85
|
+
'test-field',
|
|
86
|
+
'textarea content'
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('renders with specified rows', () => {
|
|
91
|
+
render(<FieldConstructor {...defaultProps} type="textarea" rows={5} />);
|
|
92
|
+
|
|
93
|
+
expect(document.getElementsByTagName('textarea')).toHaveLength(1);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('Checkbox Fields', () => {
|
|
98
|
+
test('renders checkbox field', () => {
|
|
99
|
+
render(<FieldConstructor {...defaultProps} type="checkbox" />);
|
|
100
|
+
|
|
101
|
+
expect(screen.getByText('Test Field')).toBeInTheDocument();
|
|
102
|
+
expect(document.getElementById('id-test-field')).toHaveAttribute(
|
|
103
|
+
'type',
|
|
104
|
+
'checkbox'
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('renders unchecked checkbox', () => {
|
|
109
|
+
render(
|
|
110
|
+
<FieldConstructor {...defaultProps} type="checkbox" value={false} />
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(document.getElementById('id-test-field')).not.toBeChecked();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Autocomplete render test', () => {
|
|
118
|
+
test('renders autocomplete', () => {
|
|
119
|
+
const props = {
|
|
120
|
+
...defaultProps,
|
|
121
|
+
onChange: defaultProps.setValue,
|
|
122
|
+
options: [
|
|
123
|
+
{ value: 'host_created.event.foreman', label: 'Host Created' },
|
|
124
|
+
{ value: 'host_updated.event.foreman', label: 'Host Updated' },
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
render(<FieldConstructor {...props} type="select" />);
|
|
128
|
+
|
|
129
|
+
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('Label Help', () => {
|
|
134
|
+
test('renders help icon when labelHelp is provided', () => {
|
|
135
|
+
render(
|
|
136
|
+
<FieldConstructor {...defaultProps} labelHelp="This is help text" />
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('does not render help icon when labelHelp is not provided', () => {
|
|
143
|
+
render(<FieldConstructor {...defaultProps} />);
|
|
144
|
+
|
|
145
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('renders help icon with JSX content', async () => {
|
|
149
|
+
const helpContent = <div>JSX help content</div>;
|
|
150
|
+
render(<FieldConstructor {...defaultProps} labelHelp={helpContent} />);
|
|
151
|
+
|
|
152
|
+
const button = screen.getByRole('button');
|
|
153
|
+
expect(button).toBeInTheDocument();
|
|
154
|
+
|
|
155
|
+
userEvent.click(button);
|
|
156
|
+
|
|
157
|
+
expect(await screen.findByText('JSX help content')).toBeInTheDocument();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('Validation', () => {
|
|
162
|
+
test('renders error message when validated is error', async () => {
|
|
163
|
+
render(
|
|
164
|
+
<>
|
|
165
|
+
<FieldConstructor {...defaultProps} required type="text" />
|
|
166
|
+
<button>Dummy</button>
|
|
167
|
+
</>
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const input = document.getElementById('id-test-field');
|
|
171
|
+
await userEvent.click(input);
|
|
172
|
+
expect(input).toHaveFocus();
|
|
173
|
+
|
|
174
|
+
await userEvent.tab();
|
|
175
|
+
expect(input).not.toHaveFocus();
|
|
176
|
+
|
|
177
|
+
expect(screen.getByText('Field is required')).toBeInTheDocument();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('does not render error message when validated is not error', () => {
|
|
181
|
+
render(
|
|
182
|
+
<FieldConstructor {...defaultProps} type="text" validated="success" />
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(screen.queryByText('Field is required')).not.toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('Edge Cases', () => {
|
|
190
|
+
test('handles undefined value', () => {
|
|
191
|
+
render(
|
|
192
|
+
<FieldConstructor {...defaultProps} type="text" value={undefined} />
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(document.getElementById('id-test-field')).toHaveValue('');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('handles null value', () => {
|
|
199
|
+
render(<FieldConstructor {...defaultProps} type="text" value={null} />);
|
|
200
|
+
|
|
201
|
+
expect(document.getElementById('id-test-field')).toHaveValue('');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('handles numeric value', () => {
|
|
205
|
+
render(<FieldConstructor {...defaultProps} type="text" value={123} />);
|
|
206
|
+
|
|
207
|
+
expect(document.getElementById('id-test-field')).toHaveValue('123');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('handles boolean value for checkbox', () => {
|
|
211
|
+
render(<FieldConstructor {...defaultProps} type="checkbox" value />);
|
|
212
|
+
|
|
213
|
+
expect(document.getElementById('id-test-field')).toBeChecked();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|