foreman_webhooks 4.0.1 → 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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_webhooks/locale/ca/foreman_webhooks.js +1 -1
  3. data/app/assets/javascripts/foreman_webhooks/locale/cs_CZ/foreman_webhooks.js +1 -1
  4. data/app/assets/javascripts/foreman_webhooks/locale/de/foreman_webhooks.js +1 -1
  5. data/app/assets/javascripts/foreman_webhooks/locale/en/foreman_webhooks.js +6 -6
  6. data/app/assets/javascripts/foreman_webhooks/locale/en_GB/foreman_webhooks.js +11 -11
  7. data/app/assets/javascripts/foreman_webhooks/locale/es/foreman_webhooks.js +1 -1
  8. data/app/assets/javascripts/foreman_webhooks/locale/fr/foreman_webhooks.js +74 -74
  9. data/app/assets/javascripts/foreman_webhooks/locale/it/foreman_webhooks.js +1 -1
  10. data/app/assets/javascripts/foreman_webhooks/locale/ja/foreman_webhooks.js +75 -75
  11. data/app/assets/javascripts/foreman_webhooks/locale/ka/foreman_webhooks.js +9 -9
  12. data/app/assets/javascripts/foreman_webhooks/locale/ko/foreman_webhooks.js +80 -80
  13. data/app/assets/javascripts/foreman_webhooks/locale/pl/foreman_webhooks.js +1 -1
  14. data/app/assets/javascripts/foreman_webhooks/locale/pt_BR/foreman_webhooks.js +1 -1
  15. data/app/assets/javascripts/foreman_webhooks/locale/ru/foreman_webhooks.js +1 -1
  16. data/app/assets/javascripts/foreman_webhooks/locale/zh_CN/foreman_webhooks.js +78 -78
  17. data/app/assets/javascripts/foreman_webhooks/locale/zh_TW/foreman_webhooks.js +1 -1
  18. data/lib/foreman_webhooks/engine.rb +1 -1
  19. data/lib/foreman_webhooks/version.rb +1 -1
  20. data/locale/ca/LC_MESSAGES/foreman_webhooks.mo +0 -0
  21. data/locale/ca/foreman_webhooks.po +1 -1
  22. data/locale/cs_CZ/LC_MESSAGES/foreman_webhooks.mo +0 -0
  23. data/locale/cs_CZ/foreman_webhooks.po +1 -1
  24. data/locale/de/LC_MESSAGES/foreman_webhooks.mo +0 -0
  25. data/locale/de/foreman_webhooks.po +1 -1
  26. data/locale/en/LC_MESSAGES/foreman_webhooks.mo +0 -0
  27. data/locale/en/foreman_webhooks.po +9 -9
  28. data/locale/en_GB/LC_MESSAGES/foreman_webhooks.mo +0 -0
  29. data/locale/en_GB/foreman_webhooks.po +12 -12
  30. data/locale/es/LC_MESSAGES/foreman_webhooks.mo +0 -0
  31. data/locale/es/foreman_webhooks.po +1 -1
  32. data/locale/fr/LC_MESSAGES/foreman_webhooks.mo +0 -0
  33. data/locale/fr/foreman_webhooks.po +77 -74
  34. data/locale/it/LC_MESSAGES/foreman_webhooks.mo +0 -0
  35. data/locale/it/foreman_webhooks.po +2 -2
  36. data/locale/ja/LC_MESSAGES/foreman_webhooks.mo +0 -0
  37. data/locale/ja/foreman_webhooks.po +77 -75
  38. data/locale/ka/LC_MESSAGES/foreman_webhooks.mo +0 -0
  39. data/locale/ka/foreman_webhooks.po +9 -9
  40. data/locale/ko/LC_MESSAGES/foreman_webhooks.mo +0 -0
  41. data/locale/ko/foreman_webhooks.po +82 -81
  42. data/locale/pl/LC_MESSAGES/foreman_webhooks.mo +0 -0
  43. data/locale/pl/foreman_webhooks.po +1 -1
  44. data/locale/pt_BR/LC_MESSAGES/foreman_webhooks.mo +0 -0
  45. data/locale/pt_BR/foreman_webhooks.po +1 -1
  46. data/locale/ru/LC_MESSAGES/foreman_webhooks.mo +0 -0
  47. data/locale/ru/foreman_webhooks.po +1 -1
  48. data/locale/zh_CN/LC_MESSAGES/foreman_webhooks.mo +0 -0
  49. data/locale/zh_CN/foreman_webhooks.po +80 -78
  50. data/locale/zh_TW/LC_MESSAGES/foreman_webhooks.mo +0 -0
  51. data/locale/zh_TW/foreman_webhooks.po +1 -1
  52. data/test/integration/webhooks_test.rb +234 -0
  53. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js +321 -0
  54. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css +23 -4
  55. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js +191 -149
  56. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js +216 -0
  57. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js +55 -26
  58. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js +253 -37
  59. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/index.js +3 -4
  60. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +30 -19
  61. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +52 -18
  62. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +37 -26
  63. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookTestModal.js +57 -32
  64. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/NameToEditCell.js +8 -1
  65. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/__tests__/__snapshots__/EnabledCell.test.js.snap +2 -14
  66. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +24 -11
  67. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +64 -0
  68. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +21 -22
  69. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +9 -15
  70. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/integration.test.js +0 -3
  71. metadata +8 -9
  72. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/ForemanFormikField.js +0 -152
  73. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/__snapshots__/WebhookForm.test.js.snap +0 -512
  74. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/integration.test.js.snap +0 -31
@@ -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
- formProps,
13
- disabled,
11
+ inputValues,
12
+ setInputValues,
14
13
  activeTab,
15
14
  handleTabClick,
16
15
  webhookTemplates,
@@ -19,152 +18,198 @@ const WebhookFormTabs = ({
19
18
  isTemplatesLoading,
20
19
  isEventsLoading,
21
20
  isPasswordDisabled,
22
- setIsPasswordDisabled,
23
- }) => (
24
- <Tabs activeKey={activeTab} onSelect={handleTabClick} isFilled>
25
- <Tab
26
- className="webhook-form-tab"
27
- eventKey={0}
28
- title={<TabTitleText>{__('General')}</TabTitleText>}
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"
29
35
  >
30
- <div className="webhook-form-tab-content">
31
- <ForemanFormikField
32
- name="event"
33
- type="select"
34
- label={__('Subscribe to')}
35
- required
36
- allowClear={false}
37
- options={availableEvents}
38
- isLoading={isEventsLoading}
39
- />
40
- <ForemanFormikField
41
- name="name"
42
- type="text"
43
- required
44
- label={__('Name')}
45
- />
46
- <ForemanFormikField
47
- name="target_url"
48
- type="text"
49
- required
50
- label={__('Target URL')}
51
- labelHelp={
52
- <>
53
- <div>
54
- {__(
55
- 'Target URL that should be called by Foreman (ERB allowed).'
56
- )}
57
- </div>
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={
58
73
  <div>
59
- {sprintf(
60
- __('Example: %s'),
61
- 'https://host.example.com/inventory/<%= @object.id %>'
62
- )}
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>
63
85
  </div>
64
- </>
65
- }
66
- />
67
- <ForemanFormikField
68
- name="webhook_template_id"
69
- type="select"
70
- label={__('Template')}
71
- required
72
- allowClear={false}
73
- options={webhookTemplates}
74
- isLoading={isTemplatesLoading}
75
- />
76
- <ForemanFormikField
77
- name="http_method"
78
- type="select"
79
- label={__('HTTP Method')}
80
- required
81
- allowClear={false}
82
- options={httpMethods}
83
- />
84
- <ForemanFormikField
85
- name="enabled"
86
- type="checkbox"
87
- label={__('Enabled')}
88
- labelHelp={__('If unchecked, the webhook will be inactive')}
89
- />
90
- </div>
91
- </Tab>
92
- <Tab
93
- className="webhook-form-tab"
94
- eventKey={1}
95
- title={<TabTitleText>{__('Credentials')}</TabTitleText>}
96
- >
97
- <div className="webhook-form-tab-content">
98
- <ForemanFormikField
99
- name="user"
100
- type="text"
101
- label={__('User')}
102
- labelHelp={__('Authentication credentials')}
103
- />
104
- <ForemanFormikField
105
- name="password"
106
- type="password"
107
- label={__('Password')}
108
- labelHelp={__('Authentication credentials')}
109
- disabled={isPasswordDisabled}
110
- setDisabled={setIsPasswordDisabled}
111
- />
112
- <ForemanFormikField
113
- name="verify_ssl"
114
- type="checkbox"
115
- label={__('Verify SSL')}
116
- labelHelp={__(
117
- "Uncheck this option to disable validation of the receiver's SSL certificate"
118
- )}
119
- />
120
- <ForemanFormikField
121
- name="proxy_authorization"
122
- type="checkbox"
123
- label={__('Proxy Authorization')}
124
- labelHelp={__(
125
- 'Authorize with Foreman client certificate and validate smart-proxy CA from Settings'
126
- )}
127
- />
128
- <ForemanFormikField
129
- name="ssl_ca_certs"
130
- type="textarea"
131
- label={__('X509 Certification Authorities')}
132
- placeholder={__(
133
- "Optional CAs in PEM format concatenated to verify the receiver's SSL certificate"
134
- )}
135
- inputSizeClass="col-md-8"
136
- rows={8}
137
- />
138
- </div>
139
- </Tab>
140
- <Tab
141
- className="webhook-form-tab"
142
- eventKey={2}
143
- title={<TabTitleText>{__('Additional')}</TabTitleText>}
144
- >
145
- <div className="webhook-form-tab-content">
146
- <ForemanFormikField
147
- name="http_content_type"
148
- type="text"
149
- label={__('HTTP Content Type')}
150
- />
151
- <ForemanFormikField
152
- name="http_headers"
153
- type="textarea"
154
- label={__('HTTP Headers')}
155
- labelHelp={__('Optional. Must be a JSON object (ERB allowed)')}
156
- placeholder='{&#13;&#10;"X-Shellhook-Arg-1": "value",&#13;&#10;"X-Shellhook-Arg-2": "<%= @object.id %>"&#13;&#10;}'
157
- inputSizeClass="col-md-8"
158
- rows={8}
159
- />
160
- </div>
161
- </Tab>
162
- </Tabs>
163
- );
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='{&#13;&#10;"X-Shellhook-Arg-1": "value",&#13;&#10;"X-Shellhook-Arg-2": "<%= @object.id %>"&#13;&#10;}'
200
+ inputSizeClass="col-md-8"
201
+ rows={8}
202
+ setValue={updateFieldValue}
203
+ />
204
+ </div>
205
+ </Tab>
206
+ </Tabs>
207
+ );
208
+ };
164
209
 
165
210
  WebhookFormTabs.propTypes = {
166
- formProps: PropTypes.object,
167
- disabled: PropTypes.bool,
211
+ inputValues: PropTypes.object.isRequired,
212
+ setInputValues: PropTypes.func.isRequired,
168
213
  activeTab: PropTypes.number.isRequired,
169
214
  handleTabClick: PropTypes.func.isRequired,
170
215
  webhookTemplates: PropTypes.array.isRequired,
@@ -173,14 +218,11 @@ WebhookFormTabs.propTypes = {
173
218
  isTemplatesLoading: PropTypes.bool.isRequired,
174
219
  isEventsLoading: PropTypes.bool.isRequired,
175
220
  isPasswordDisabled: PropTypes.bool,
176
- setIsPasswordDisabled: PropTypes.func,
221
+ urlValidated: PropTypes.func.isRequired,
177
222
  };
178
223
 
179
224
  WebhookFormTabs.defaultProps = {
180
- disabled: false,
181
- formProps: {},
182
225
  isPasswordDisabled: false,
183
- setIsPasswordDisabled: undefined,
184
226
  };
185
227
 
186
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
+ });