foreman_webhooks 5.1.0 → 6.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c90b385f92db59657a6f026239c2ccfda1b574c852563755fd4dab6522cbf84c
4
- data.tar.gz: 05324c9d1589fed4e35b0bf477d0edc7380b5e1abe391872d27a18457355f6f9
3
+ metadata.gz: 2504e406a10a1be7d18fe058224497d23d5888a67c73c3a37a79a3c60d14b91d
4
+ data.tar.gz: f558a5e1d7f670ba489dea28d371c7ddd71a604131ffe0b50206286899794e5c
5
5
  SHA512:
6
- metadata.gz: d8956d6c344db760a44ee50c9699d701078a5cae604dc561a40d55ecb25717d03129dceed5bb770c337cd9b1bc28455b0fff5aa80431e2b6834a368476f358ad
7
- data.tar.gz: 5d47beee070a29ac44960ea8c8bbe81e19179be5d806f062809a804457fc2929ea63dab6c6569b89d8ebf0d22a0af7c8a14dee2e1c53b15f1a3e6889eb485755
6
+ metadata.gz: 3c31f081144dad2899b5b54f9eb6dd7fc04a8314ba684b82925856771d80449872c859e399015c5daaa542d0a743cbe44741d11251b8d1318f1fbed034140453
7
+ data.tar.gz: a18a91b96ddf51b0989c85fe74a9285c0e4a341253bcdc88ea152bbef3e8cce500dfc5c00f62d8b3fe9765f3e4e4464d59339b939df183d78d7ca418f355485f
@@ -3,4 +3,4 @@
3
3
  proxy_feature = Feature.where(name: 'Shellhooks').first_or_create
4
4
  return unless proxy_feature.nil? || proxy_feature.errors.any?
5
5
 
6
- raise "Unable to create proxy feature: #{format_errors proxy_feature}"
6
+ raise "Unable to create proxy feature: #{SeedHelper.format_errors(proxy_feature)}"
@@ -14,7 +14,7 @@ module ForemanWebhooks
14
14
  initializer 'foreman_webhooks.register_plugin', before: :finisher_hook do |app|
15
15
  app.reloader.to_prepare do
16
16
  Foreman::Plugin.register :foreman_webhooks do
17
- requires_foreman '>= 3.18'
17
+ requires_foreman '>= 5.0'
18
18
  register_gettext
19
19
 
20
20
  apipie_documented_controllers ["#{ForemanWebhooks::Engine.root}/app/controllers/api/v2/*.rb"]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanWebhooks
4
- VERSION = "5.1.0"
4
+ VERSION = '6.0.1'
5
5
  end
@@ -2,10 +2,10 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
4
4
 
5
+ import FieldConstructor from 'foremanReact/components/common/FieldConstructor/FieldConstructor';
5
6
  import { sprintf, translate as __ } from 'foremanReact/common/I18n';
6
7
 
7
8
  import './WebhookFormTabs.css';
8
- import FieldConstructor from './FieldConstructor';
9
9
 
10
10
  const WebhookFormTabs = ({
11
11
  inputValues,
@@ -48,7 +48,7 @@ const WebhookFormTabs = ({
48
48
  type="select"
49
49
  label={__('Subscribe to')}
50
50
  required
51
- allowClear={false}
51
+ allowClear
52
52
  options={availableEvents}
53
53
  isLoading={isEventsLoading}
54
54
  placeholder={__('Start typing to search...')}
@@ -93,7 +93,7 @@ const WebhookFormTabs = ({
93
93
  type="select"
94
94
  label={__('Template')}
95
95
  required
96
- allowClear={false}
96
+ allowClear
97
97
  options={webhookTemplates}
98
98
  isLoading={isTemplatesLoading}
99
99
  placeholder={__('Start typing to search...')}
@@ -105,7 +105,7 @@ const WebhookFormTabs = ({
105
105
  type="select"
106
106
  label={__('HTTP Method')}
107
107
  required
108
- allowClear={false}
108
+ allowClear
109
109
  options={httpMethods}
110
110
  placeholder={__('Start typing to search...')}
111
111
  />
@@ -173,7 +173,6 @@ const WebhookFormTabs = ({
173
173
  placeholder={__(
174
174
  "Optional CAs in PEM format concatenated to verify the receiver's SSL certificate"
175
175
  )}
176
- inputSizeClass="col-md-8"
177
176
  rows={8}
178
177
  setValue={updateFieldValue}
179
178
  />
@@ -200,7 +199,6 @@ const WebhookFormTabs = ({
200
199
  label={__('HTTP Headers')}
201
200
  labelHelp={__('Optional. Must be a JSON object (ERB allowed)')}
202
201
  placeholder='{&#13;&#10;"X-Shellhook-Arg-1": "value",&#13;&#10;"X-Shellhook-Arg-2": "<%= @object.id %>"&#13;&#10;}'
203
- inputSizeClass="col-md-8"
204
202
  rows={8}
205
203
  setValue={updateFieldValue}
206
204
  />
@@ -12,6 +12,7 @@ import {
12
12
  ModalVariant,
13
13
  } from '@patternfly/react-core';
14
14
 
15
+ import FieldConstructor from 'foremanReact/components/common/FieldConstructor/FieldConstructor';
15
16
  import { APIActions } from 'foremanReact/redux/API';
16
17
  import {
17
18
  WEBHOOK_TEST_MODAL_ID,
@@ -19,7 +20,6 @@ import {
19
20
  } from '../../constants';
20
21
 
21
22
  import './WebhookModal.scss';
22
- import FieldConstructor from '../../Components/WebhookForm/Components/FieldConstructor';
23
23
 
24
24
  const WebhookTestModal = ({ toTest, modalState }) => {
25
25
  const dispatch = useDispatch();
@@ -80,7 +80,6 @@ const WebhookTestModal = ({ toTest, modalState }) => {
80
80
  label={__('Payload')}
81
81
  labelHelp={__('Will be sent as is')}
82
82
  placeholder="{&#13;&#10;id: 1,&#13;&#10;name: test&#13;&#10;}"
83
- inputSizeClass="col-md-8"
84
83
  rows={8}
85
84
  value={value.payload}
86
85
  setValue={(key, val) => {
@@ -38,9 +38,10 @@ export const queryParams = {
38
38
  ...querySort,
39
39
  };
40
40
 
41
- export const stateFactory = state => ({
41
+ export const stateFactory = (state = {}, { status } = {}) => ({
42
42
  API: {
43
43
  [WEBHOOKS_API_REQUEST_KEY]: {
44
+ ...(status !== undefined && { status }),
44
45
  response: {
45
46
  ...stateParams,
46
47
  ...state,
@@ -1,4 +1,4 @@
1
- import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
1
+ import { STATUS } from 'foremanReact/constants';
2
2
 
3
3
  import {
4
4
  selectWebhooks,
@@ -11,6 +11,7 @@ import {
11
11
  selectIsLoading,
12
12
  selectSubtotal,
13
13
  selectMessage,
14
+ selectCanCreate,
14
15
  } from '../WebhooksPageSelectors';
15
16
 
16
17
  import {
@@ -18,28 +19,132 @@ import {
18
19
  webhooks,
19
20
  } from '../WebhooksIndexPage/__tests__/WebhooksIndexPage.fixtures';
20
21
 
21
- const state = stateFactory({
22
+ const populatedState = stateFactory({
22
23
  results: webhooks,
23
24
  sort: { by: 'name', order: 'DESC' },
24
25
  page: 1,
25
26
  perPage: 1,
26
27
  search: 'name ~ foo',
27
28
  subtotal: 42,
28
- message: { type: 'error', text: 'This is error' },
29
+ canCreate: true,
30
+ message: { type: 'info', text: 'Saved successfully' },
29
31
  });
30
32
 
31
- const fixtures = {
32
- 'should return webhooks': () => selectWebhooks(state),
33
- 'should return page': () => selectPage(state),
34
- 'should return perPage': () => selectPerPage(state),
35
- 'should return search': () => selectSearch(state),
36
- 'should return sort': () => selectSort(state),
37
- 'should return hasData': () => selectHasData(state),
38
- 'should return hasError': () => selectHasError(state),
39
- 'should return isLoading': () => selectIsLoading(state),
40
- 'should return subtotal': () => selectSubtotal(state),
41
- 'should return message': () => selectMessage(state),
33
+ const resolvedState = stateFactory(
34
+ { results: webhooks, subtotal: webhooks.length },
35
+ { status: STATUS.RESOLVED }
36
+ );
37
+
38
+ const pendingState = stateFactory({ results: [] }, { status: STATUS.PENDING });
39
+
40
+ const errorState = {
41
+ API: {
42
+ webhooks: {
43
+ status: STATUS.ERROR,
44
+ response: {
45
+ message: 'Request failed with status code 500',
46
+ response: {
47
+ data: { error: { message: 'Unable to load webhooks' } },
48
+ },
49
+ },
50
+ },
51
+ },
42
52
  };
43
53
 
44
- describe('WebhooksPage selectors', () =>
45
- testSelectorsSnapshotWithFixtures(fixtures));
54
+ describe('WebhooksPage selectors', () => {
55
+ it('returns webhooks from the API response', () => {
56
+ expect(selectWebhooks(populatedState)).toEqual(webhooks);
57
+ });
58
+
59
+ it('returns page from the API response', () => {
60
+ expect(selectPage(populatedState)).toBe(1);
61
+ });
62
+
63
+ it('returns perPage from the API response', () => {
64
+ expect(selectPerPage(populatedState)).toBe(1);
65
+ });
66
+
67
+ it('returns search from the API response', () => {
68
+ expect(selectSearch(populatedState)).toBe('name ~ foo');
69
+ });
70
+
71
+ it('returns sort with camelCased column name', () => {
72
+ expect(selectSort(populatedState)).toEqual({ by: 'name', order: 'DESC' });
73
+ });
74
+
75
+ it('camelCases snake_case sort column names', () => {
76
+ const state = stateFactory({
77
+ sort: { by: 'target_url', order: 'ASC' },
78
+ });
79
+
80
+ expect(selectSort(state)).toEqual({ by: 'targetUrl', order: 'ASC' });
81
+ });
82
+
83
+ it('returns hasData as false when the API request is not resolved', () => {
84
+ expect(selectHasData(populatedState)).toBe(false);
85
+ });
86
+
87
+ it('returns hasData as true when the API request is resolved with results', () => {
88
+ expect(selectHasData(resolvedState)).toBe(true);
89
+ });
90
+
91
+ it('returns hasError as false for a successful API response', () => {
92
+ expect(selectHasError(populatedState)).toBe(false);
93
+ });
94
+
95
+ it('returns hasError as true when the API request failed', () => {
96
+ expect(selectHasError(errorState)).toBe(true);
97
+ });
98
+
99
+ it('returns isLoading as true when the API status is missing or pending', () => {
100
+ expect(selectIsLoading(populatedState)).toBe(true);
101
+ expect(selectIsLoading(pendingState)).toBe(true);
102
+ });
103
+
104
+ it('returns isLoading as false when the API request is resolved', () => {
105
+ expect(selectIsLoading(resolvedState)).toBe(false);
106
+ });
107
+
108
+ it('returns subtotal from the API response', () => {
109
+ expect(selectSubtotal(populatedState)).toBe(42);
110
+ });
111
+
112
+ it('returns message from the API response when there is no error', () => {
113
+ expect(selectMessage(populatedState)).toEqual({
114
+ type: 'info',
115
+ text: 'Saved successfully',
116
+ });
117
+ });
118
+
119
+ it('returns an error message from the API when the request failed', () => {
120
+ expect(selectMessage(errorState)).toEqual({
121
+ type: 'error',
122
+ text: 'Unable to load webhooks',
123
+ });
124
+ });
125
+
126
+ it('returns an empty webhook list when the API request failed', () => {
127
+ expect(selectWebhooks(errorState)).toEqual([]);
128
+ });
129
+
130
+ it('returns canCreate from the API response', () => {
131
+ expect(selectCanCreate(populatedState)).toBe(true);
132
+ });
133
+
134
+ it('returns default pagination values when the API response is empty', () => {
135
+ const emptyState = {
136
+ API: {
137
+ webhooks: {
138
+ status: STATUS.RESOLVED,
139
+ response: {},
140
+ },
141
+ },
142
+ };
143
+
144
+ expect(selectPage(emptyState)).toBe(1);
145
+ expect(selectPerPage(emptyState)).toBe(20);
146
+ expect(selectWebhooks(emptyState)).toEqual([]);
147
+ expect(selectSubtotal(emptyState)).toBe(0);
148
+ expect(selectCanCreate(emptyState)).toBe(false);
149
+ });
150
+ });
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_webhooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timo Goebel
@@ -138,10 +138,8 @@ files:
138
138
  - test/test_plugin_helper.rb
139
139
  - test/unit/foreman_webhooks/webhook_service_test.rb
140
140
  - webpack/ForemanWebhooks/Routes/ForemanWebhooksRoutes.js
141
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/FieldConstructor.js
142
141
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.css
143
142
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/WebhookFormTabs.js
144
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js
145
143
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookForm.js
146
144
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/WebhookFormSelectors.js
147
145
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js
@@ -170,7 +168,6 @@ files:
170
168
  - webpack/ForemanWebhooks/Routes/Webhooks/WebhooksPageSelectors.js
171
169
  - webpack/ForemanWebhooks/Routes/Webhooks/__tests__/WebhooksPageHelpers.test.js
172
170
  - webpack/ForemanWebhooks/Routes/Webhooks/__tests__/WebhooksPageSelectors.test.js
173
- - webpack/ForemanWebhooks/Routes/Webhooks/__tests__/__snapshots__/WebhooksPageSelectors.test.js.snap
174
171
  - webpack/ForemanWebhooks/Routes/Webhooks/constants.js
175
172
  - webpack/index.js
176
173
  - webpack/routes_index.js
@@ -197,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
194
  - !ruby/object:Gem::Version
198
195
  version: '0'
199
196
  requirements: []
200
- rubygems_version: 4.0.10
197
+ rubygems_version: 4.0.20
201
198
  specification_version: 4
202
199
  summary: Configure webhooks for Foreman.
203
200
  test_files:
@@ -214,7 +211,6 @@ test_files:
214
211
  - test/models/webhook_test.rb
215
212
  - test/test_plugin_helper.rb
216
213
  - test/unit/foreman_webhooks/webhook_service_test.rb
217
- - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/Components/__tests__/FieldConstructor.test.js
218
214
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookForm/__tests__/WebhookForm.test.js
219
215
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/actionCellFormatter.test.js
220
216
  - webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/enabledCellFormatter.test.js
@@ -1,312 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import React, { useEffect, useState } from 'react';
3
- import PropTypes from 'prop-types';
4
- import AutocompleteInput from 'foremanReact/components/common/AutocompleteInput/AutocompleteInput';
5
- import { translate as __ } from 'foremanReact/common/I18n';
6
- import {
7
- ExclamationCircleIcon,
8
- HelpIcon,
9
- PencilAltIcon,
10
- } from '@patternfly/react-icons';
11
- import {
12
- TextInput,
13
- Checkbox,
14
- Button,
15
- FormHelperText,
16
- HelperText,
17
- HelperTextItem,
18
- Icon,
19
- TextArea,
20
- FormGroup,
21
- Popover,
22
- Grid,
23
- GridItem,
24
- } from '@patternfly/react-core';
25
-
26
- const FormField = ({
27
- name,
28
- type,
29
- required,
30
- options,
31
- validated,
32
- value,
33
- disabled,
34
- setValue,
35
- placeholder,
36
- errMsg,
37
- fieldId,
38
- setIsPasswordDisabled,
39
- ...props
40
- }) => {
41
- const [fieldValidated, setFieldValidated] = useState('default');
42
- const [firstLoad, setFirstLoad] = useState(true);
43
-
44
- const requiredValidate = () => {
45
- if (firstLoad || !required) return;
46
- if (!value || value === '' || validated === 'error')
47
- setFieldValidated('error');
48
- else setFieldValidated('success');
49
- };
50
-
51
- const localHandler = (_event, newValue) => {
52
- setValue(name, newValue);
53
- };
54
-
55
- useEffect(() => {
56
- setFirstLoad(false);
57
- }, []);
58
-
59
- useEffect(() => {
60
- requiredValidate();
61
- // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, [value]);
63
-
64
- if (type === 'checkbox') {
65
- return (
66
- <>
67
- <Checkbox
68
- ouiaId={name}
69
- id={fieldId ?? `id-${name}`}
70
- isChecked={value || false}
71
- onChange={(_, newValue) => {
72
- setValue(name, newValue);
73
- }}
74
- isDisabled={disabled}
75
- isRequired={required}
76
- type={type}
77
- validated={fieldValidated}
78
- />
79
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
80
- </>
81
- );
82
- } else if (type === 'textarea') {
83
- return (
84
- <>
85
- <TextArea
86
- rows={6}
87
- name={name}
88
- id={fieldId ?? `id-${name}`}
89
- value={value ?? ''}
90
- onChange={(_, newValue) => setValue(name, newValue)}
91
- isRequired={required}
92
- isDisabled={disabled}
93
- type={type}
94
- validated={fieldValidated}
95
- placeholder={placeholder}
96
- />
97
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
98
- </>
99
- );
100
- } else if (options.length !== 0) {
101
- return (
102
- <>
103
- <AutocompleteInput
104
- options={options}
105
- selected={value ?? ''}
106
- onSelect={newValue => setValue(name, newValue)}
107
- onChange={() => requiredValidate()}
108
- name={name}
109
- placeholder={placeholder}
110
- validationStatus={fieldValidated}
111
- validationMsg={errMsg}
112
- fieldId={fieldId}
113
- />
114
- </>
115
- );
116
- }
117
- return (
118
- <>
119
- {name === 'password' && disabled && setIsPasswordDisabled ? (
120
- <Grid hasGutter={false}>
121
- <GridItem span={11}>
122
- <TextInput
123
- name={name}
124
- value={value ?? ''}
125
- id={fieldId ?? `id-${name}`}
126
- onChange={localHandler}
127
- isDisabled={disabled}
128
- isRequired={required}
129
- type={type}
130
- validated={fieldValidated}
131
- placeholder={placeholder}
132
- onBlur={requiredValidate}
133
- autoComplete="off"
134
- />
135
- </GridItem>
136
- <GridItem span={1}>
137
- <Button
138
- ouiaId={`reset-${name}`}
139
- onClick={() => setIsPasswordDisabled(!disabled)}
140
- variant="control"
141
- icon={<PencilAltIcon />}
142
- />
143
- </GridItem>
144
- </Grid>
145
- ) : (
146
- <TextInput
147
- name={name}
148
- value={value ?? ''}
149
- id={fieldId ?? `id-${name}`}
150
- onChange={localHandler}
151
- isRequired={required}
152
- type={type}
153
- validated={fieldValidated}
154
- onBlur={requiredValidate}
155
- autoComplete={type === 'password' ? 'new-password' : null}
156
- />
157
- )}
158
-
159
- <ValidationComponent localValidated={fieldValidated} errMsg={errMsg} />
160
- </>
161
- );
162
- };
163
-
164
- const ValidationComponent = ({ localValidated, errMsg }) => (
165
- <>
166
- {localValidated === 'error' && (
167
- <FormHelperText>
168
- <HelperText>
169
- <HelperTextItem
170
- icon={
171
- <Icon>
172
- <ExclamationCircleIcon />
173
- </Icon>
174
- }
175
- variant={localValidated}
176
- >
177
- {errMsg ?? __('Field is required')}
178
- </HelperTextItem>
179
- </HelperText>
180
- </FormHelperText>
181
- )}
182
- </>
183
- );
184
-
185
- const FieldConstructor = ({
186
- label,
187
- value,
188
- required,
189
- labelHelp,
190
- fieldId,
191
- name,
192
- ...props
193
- }) => (
194
- <FormGroup
195
- isInline
196
- fieldId={fieldId ?? `id-${name}`}
197
- label={label}
198
- isRequired={required}
199
- labelIcon={
200
- labelHelp ? (
201
- <Popover
202
- bodyContent={
203
- typeof labelHelp === 'string' ? (
204
- <div dangerouslySetInnerHTML={{ __html: labelHelp }} />
205
- ) : (
206
- labelHelp
207
- )
208
- }
209
- >
210
- <Button
211
- ouiaId={`label-${label}`}
212
- type="button"
213
- variant="plain"
214
- onClick={e => e.preventDefault()}
215
- >
216
- <Icon>
217
- <HelpIcon />
218
- </Icon>
219
- </Button>
220
- </Popover>
221
- ) : (
222
- ''
223
- )
224
- }
225
- >
226
- <FormField
227
- value={value}
228
- required={required}
229
- name={name}
230
- {...props}
231
- fieldId={fieldId}
232
- />
233
- </FormGroup>
234
- );
235
-
236
- FieldConstructor.propTypes = {
237
- label: PropTypes.string.isRequired,
238
- required: PropTypes.bool,
239
- labelHelp: PropTypes.oneOfType([
240
- PropTypes.string,
241
- PropTypes.object,
242
- PropTypes.node,
243
- ]),
244
- setValue: PropTypes.func.isRequired,
245
- name: PropTypes.string.isRequired,
246
- disabled: PropTypes.bool,
247
- value: PropTypes.oneOfType([
248
- PropTypes.string,
249
- PropTypes.object,
250
- PropTypes.bool,
251
- PropTypes.number,
252
- ]),
253
- fieldId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
254
- };
255
-
256
- FieldConstructor.defaultProps = {
257
- labelHelp: undefined,
258
- required: false,
259
- disabled: false,
260
- value: '',
261
- fieldId: undefined,
262
- };
263
-
264
- FormField.propTypes = {
265
- disabled: PropTypes.bool,
266
- setValue: PropTypes.func.isRequired,
267
- name: PropTypes.string.isRequired,
268
- type: PropTypes.string.isRequired,
269
- label: PropTypes.string,
270
- required: PropTypes.bool,
271
- allowClear: PropTypes.bool,
272
- options: PropTypes.arrayOf(
273
- PropTypes.shape({
274
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
275
- .isRequired,
276
- label: PropTypes.string.isRequired,
277
- })
278
- ),
279
- validated: PropTypes.string,
280
- placeholder: PropTypes.string,
281
- errMsg: PropTypes.string,
282
- value: PropTypes.oneOfType([
283
- PropTypes.bool,
284
- PropTypes.string,
285
- PropTypes.number,
286
- ]),
287
- fieldId: PropTypes.string,
288
- };
289
-
290
- FormField.defaultProps = {
291
- label: '',
292
- errMsg: null,
293
- placeholder: '',
294
- validated: 'default',
295
- required: false,
296
- allowClear: false,
297
- options: [],
298
- disabled: false,
299
- value: '',
300
- fieldId: undefined,
301
- };
302
-
303
- ValidationComponent.propTypes = {
304
- localValidated: PropTypes.string.isRequired,
305
- errMsg: PropTypes.string,
306
- };
307
-
308
- ValidationComponent.defaultProps = {
309
- errMsg: null,
310
- };
311
-
312
- export default FieldConstructor;
@@ -1,216 +0,0 @@
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 enabled when loading', () => {
64
- render(<FieldConstructor {...defaultProps} type="text" isLoading />);
65
-
66
- expect(document.getElementById('id-test-field')).toBeEnabled();
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
- });
@@ -1,50 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`WebhooksPage selectors should return hasData 1`] = `false`;
4
-
5
- exports[`WebhooksPage selectors should return hasError 1`] = `false`;
6
-
7
- exports[`WebhooksPage selectors should return isLoading 1`] = `true`;
8
-
9
- exports[`WebhooksPage selectors should return message 1`] = `
10
- Object {
11
- "text": "This is error",
12
- "type": "error",
13
- }
14
- `;
15
-
16
- exports[`WebhooksPage selectors should return page 1`] = `1`;
17
-
18
- exports[`WebhooksPage selectors should return perPage 1`] = `1`;
19
-
20
- exports[`WebhooksPage selectors should return search 1`] = `"name ~ foo"`;
21
-
22
- exports[`WebhooksPage selectors should return sort 1`] = `
23
- Object {
24
- "by": "name",
25
- "order": "DESC",
26
- }
27
- `;
28
-
29
- exports[`WebhooksPage selectors should return subtotal 1`] = `42`;
30
-
31
- exports[`WebhooksPage selectors should return webhooks 1`] = `
32
- Array [
33
- Object {
34
- "canDelete": true,
35
- "canEdit": true,
36
- "enabled": true,
37
- "id": 1,
38
- "name": "my-webhook",
39
- "targetUrl": "https://my-machine.example.com",
40
- },
41
- Object {
42
- "canDelete": false,
43
- "canEdit": false,
44
- "enabled": false,
45
- "id": 2,
46
- "name": "your-webhook",
47
- "targetUrl": "https://your-machine.example.com",
48
- },
49
- ]
50
- `;