@rancher/shell 0.3.26 → 0.3.28

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 (99) hide show
  1. package/assets/translations/en-us.yaml +8 -23
  2. package/assets/translations/zh-hans.yaml +2 -26
  3. package/chart/gatekeeper.vue +2 -11
  4. package/chart/istio.vue +1 -10
  5. package/chart/logging/index.vue +2 -11
  6. package/chart/monitoring/index.vue +1 -9
  7. package/chart/rancher-backup/index.vue +1 -9
  8. package/components/AlertTable.vue +8 -6
  9. package/components/Carousel.vue +2 -1
  10. package/components/EmberPage.vue +2 -2
  11. package/components/EtcdInfoBanner.vue +12 -2
  12. package/components/GlobalRoleBindings.vue +10 -0
  13. package/components/GrafanaDashboard.vue +8 -3
  14. package/components/Wizard.vue +17 -1
  15. package/components/auth/RoleDetailEdit.vue +17 -1
  16. package/components/form/ArrayList.vue +20 -11
  17. package/components/form/__tests__/ArrayList.test.ts +44 -0
  18. package/components/formatter/ClusterProvider.vue +1 -18
  19. package/components/nav/Header.vue +5 -4
  20. package/components/nav/TopLevelMenu.vue +38 -15
  21. package/components/nav/WindowManager/ContainerLogs.vue +22 -19
  22. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -0
  23. package/components/nav/__tests__/Type.test.ts +139 -0
  24. package/config/private-label.js +1 -1
  25. package/config/product/manager.js +0 -13
  26. package/config/settings.ts +0 -2
  27. package/config/types.js +0 -4
  28. package/core/types.ts +11 -4
  29. package/edit/management.cattle.io.project.vue +1 -52
  30. package/edit/management.cattle.io.setting.vue +31 -2
  31. package/edit/provisioning.cattle.io.cluster/Basics.vue +19 -107
  32. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
  33. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
  34. package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
  35. package/edit/workload/mixins/workload.js +14 -4
  36. package/middleware/authenticated.js +4 -2
  37. package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
  38. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
  39. package/models/cluster.x-k8s.io.machine.js +1 -1
  40. package/models/fleet.cattle.io.cluster.js +11 -1
  41. package/models/management.cattle.io.cluster.js +4 -0
  42. package/models/management.cattle.io.project.js +0 -36
  43. package/models/management.cattle.io.setting.js +11 -7
  44. package/models/provisioning.cattle.io.cluster.js +16 -4
  45. package/package.json +1 -1
  46. package/pages/auth/setup.vue +38 -1
  47. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
  48. package/pages/c/_cluster/apps/charts/index.vue +0 -15
  49. package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
  50. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  51. package/pages/c/_cluster/auth/roles/index.vue +11 -1
  52. package/pages/c/_cluster/explorer/index.vue +7 -49
  53. package/pages/c/_cluster/manager/pages/_page.vue +4 -5
  54. package/pages/c/_cluster/monitoring/index.vue +26 -39
  55. package/pages/support/index.vue +1 -8
  56. package/promptRemove/management.cattle.io.project.vue +6 -9
  57. package/rancher-components/BadgeState/BadgeState.vue +1 -5
  58. package/rancher-components/Banner/Banner.test.ts +1 -51
  59. package/rancher-components/Banner/Banner.vue +53 -134
  60. package/rancher-components/Card/Card.vue +7 -24
  61. package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
  62. package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
  63. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
  64. package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
  65. package/rancher-components/Form/Radio/RadioButton.vue +13 -30
  66. package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
  67. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  68. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
  69. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
  70. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
  71. package/rancher-components/StringList/StringList.test.ts +49 -453
  72. package/rancher-components/StringList/StringList.vue +58 -92
  73. package/rancher-components/components/Form/Radio/RadioGroup.test.ts +30 -0
  74. package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -0
  75. package/rancher-components/components/StringList/StringList.test.ts +270 -0
  76. package/rancher-components/components/StringList/StringList.vue +57 -18
  77. package/store/features.js +1 -0
  78. package/store/prefs.js +0 -3
  79. package/types/shell/index.d.ts +26 -17
  80. package/utils/__tests__/object.test.ts +67 -1
  81. package/utils/__tests__/version.test.ts +13 -23
  82. package/utils/cluster.js +1 -1
  83. package/utils/custom-validators.js +0 -2
  84. package/utils/error.js +16 -1
  85. package/utils/grafana.js +1 -2
  86. package/utils/monitoring.js +25 -1
  87. package/utils/object.js +4 -3
  88. package/utils/sort.js +1 -1
  89. package/utils/validators/formRules/__tests__/index.test.ts +49 -4
  90. package/utils/validators/formRules/index.ts +13 -10
  91. package/utils/validators/role-template.js +1 -1
  92. package/utils/validators/setting.js +6 -10
  93. package/utils/version.js +0 -13
  94. package/components/ChartPsp.vue +0 -76
  95. package/components/__tests__/ChartPsp.test.ts +0 -75
  96. package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
  97. package/rancher-components/Card/Card.test.ts +0 -37
  98. package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
  99. package/yarn-error.log +0 -200
@@ -30,10 +30,10 @@ const CLASS = {
30
30
  * Manage a list of strings
31
31
  */
32
32
  export default Vue.extend({
33
-
34
- name: 'StringList',
35
33
  components: { LabeledInput },
36
34
 
35
+ name: 'StringList',
36
+
37
37
  props: {
38
38
  /**
39
39
  * The items source
@@ -85,11 +85,11 @@ export default Vue.extend({
85
85
  },
86
86
  data() {
87
87
  return {
88
- value: null as string | null,
89
- selected: null as string | null,
90
- editedItem: null as string | null,
91
- isCreateItem: false,
92
- errors: { duplicate: false } as Record<Error, boolean>
88
+ value: null as string | null,
89
+ selected: null as string | null,
90
+ isEditItem: null as string | null,
91
+ isCreateItem: false,
92
+ errors: { duplicate: false } as Record<Error, boolean>
93
93
  };
94
94
  },
95
95
 
@@ -100,8 +100,8 @@ export default Vue.extend({
100
100
  */
101
101
  errorMessagesArray(): string[] {
102
102
  return (Object.keys(this.errors) as Error[])
103
- .filter((f) => this.errors[f] && this.errorMessages[f])
104
- .map((k) => this.errorMessages[k]);
103
+ .filter(f => !!(this.errors)[f])
104
+ .map(k => this.errorMessages[k]);
105
105
  },
106
106
  },
107
107
 
@@ -113,35 +113,23 @@ export default Vue.extend({
113
113
  this.toggleEditMode(false);
114
114
  this.toggleCreateMode(false);
115
115
  },
116
- value(val) {
117
- this.$emit('type:item', val);
118
- },
119
- errors: {
120
- handler(val) {
121
- this.$emit('errors', val);
122
- },
123
- deep: true
124
- }
125
116
  },
126
117
 
127
118
  methods: {
128
119
  onChange(value: string) {
129
120
  this.value = value;
130
-
131
- const items = [
132
- ...this.items,
133
- this.value
134
- ];
135
-
121
+ /**
122
+ * Remove duplicate error when a new value is typed
123
+ */
136
124
  this.toggleError(
137
125
  'duplicate',
138
- hasDuplicatedStrings(items, this.caseSensitive),
139
- this.isCreateItem ? INPUT.create : INPUT.edit
126
+ false,
127
+ this.isCreateItem ? INPUT.create : INPUT.edit,
140
128
  );
141
129
  },
142
130
 
143
131
  onSelect(item: string) {
144
- if (this.readonly || this.isCreateItem || this.editedItem === item) {
132
+ if (this.isCreateItem || this.isEditItem === item) {
145
133
  return;
146
134
  }
147
135
  this.selected = item;
@@ -172,7 +160,7 @@ export default Vue.extend({
172
160
  },
173
161
 
174
162
  onClickEmptyBody() {
175
- if (!this.isCreateItem && !this.editedItem) {
163
+ if (!this.isCreateItem && !this.isEditItem) {
176
164
  this.toggleCreateMode(true);
177
165
  }
178
166
  },
@@ -188,43 +176,38 @@ export default Vue.extend({
188
176
 
189
177
  return;
190
178
  }
191
- if (this.editedItem) {
192
- this.deleteAndSelectNext(this.editedItem);
179
+ if (this.isEditItem) {
193
180
  this.toggleEditMode(false);
194
181
 
195
182
  return;
196
183
  }
197
184
  if (this.selected) {
198
- this.deleteAndSelectNext(this.selected);
199
- }
200
- },
201
-
202
- deleteAndSelectNext(currItem: string) {
203
- const index = findStringIndex(this.items, currItem, false);
185
+ const index = findStringIndex(this.items, this.selected, false);
204
186
 
205
- if (index !== -1) {
206
- /**
207
- * Select the next item in the list.
208
- */
209
- const item = (this.items[index + 1] || this.items[index - 1]);
187
+ if (index !== -1) {
188
+ /**
189
+ * Select the next item in the list when an item is to be deleted.
190
+ */
191
+ const item = (this.items[index + 1] || this.items[index - 1]);
210
192
 
211
- this.onSelect(item);
212
- this.setFocus(item);
193
+ this.onSelect(item);
194
+ this.setFocus(item);
213
195
 
214
- this.deleteItem(this.items[index]);
196
+ this.deleteItem(this.items[index]);
197
+ }
215
198
  }
216
199
  },
217
200
 
218
201
  setFocus(refId: string) {
219
- this.$nextTick(() => (this.getElemByRef(refId) as Vue & HTMLElement)?.focus());
202
+ this.$nextTick(() => this.getElemByRef(refId)?.focus());
220
203
  },
221
204
 
222
205
  /**
223
206
  * Move scrollbar when the selected item is over the top or bottom side of the box
224
207
  */
225
208
  moveScrollbar(arrow: Arrow, value?: number) {
226
- const box = this.getElemByRef(BOX) as HTMLElement;
227
- const item = this.getElemByRef(this.selected || '') as HTMLElement;
209
+ const box = this.getElemByRef(BOX);
210
+ const item = this.getElemByRef(this.selected || '');
228
211
 
229
212
  if (box && item && item.className.includes(CLASS.item)) {
230
213
  const boxRect = box.getClientRects()[0];
@@ -246,14 +229,13 @@ export default Vue.extend({
246
229
  */
247
230
  toggleError(type: Error, val: boolean, refId?: string) {
248
231
  this.errors[type] = val;
249
-
250
232
  if (refId) {
251
233
  this.toggleErrorClass(refId, val);
252
234
  }
253
235
  },
254
236
 
255
237
  toggleErrorClass(refId: string, val: boolean) {
256
- const input = (this.getElemByRef(refId) as Vue)?.$el;
238
+ const input = this.getElemByRef(refId)?.$el;
257
239
 
258
240
  if (input) {
259
241
  if (val) {
@@ -268,11 +250,7 @@ export default Vue.extend({
268
250
  * Show/Hide the input line to create new item
269
251
  */
270
252
  toggleCreateMode(show: boolean) {
271
- if (this.readonly) {
272
- return;
273
- }
274
253
  if (show) {
275
- this.toggleEditMode(false);
276
254
  this.value = '';
277
255
 
278
256
  this.isCreateItem = true;
@@ -290,34 +268,31 @@ export default Vue.extend({
290
268
  * Show/Hide the in-line editing to edit an existing item
291
269
  */
292
270
  toggleEditMode(show: boolean, item?: string) {
293
- if (this.readonly) {
294
- return;
295
- }
296
271
  if (show) {
297
272
  this.toggleCreateMode(false);
298
- this.value = this.editedItem;
273
+ this.value = this.isEditItem;
299
274
 
300
- this.editedItem = item || '';
275
+ this.isEditItem = item || '';
301
276
  this.setFocus(INPUT.edit);
302
277
  } else {
303
278
  this.value = null;
304
279
  this.toggleError('duplicate', false);
305
280
  this.onSelectLeave();
306
281
 
307
- this.editedItem = null;
282
+ this.isEditItem = null;
308
283
  }
309
284
  },
310
285
 
311
286
  getElemByRef(id: string) {
312
287
  const ref = this.$refs[id];
313
288
 
314
- return Array.isArray(ref) ? ref[0] : ref;
289
+ return (Array.isArray(ref) ? ref[0] : ref) as any;
315
290
  },
316
291
 
317
292
  /**
318
293
  * Create a new item and insert in the items list
319
294
  */
320
- saveItem(closeInput = true) {
295
+ saveItem() {
321
296
  const value = this.value?.trim();
322
297
 
323
298
  if (value) {
@@ -326,20 +301,21 @@ export default Vue.extend({
326
301
  value,
327
302
  ];
328
303
 
329
- if (!hasDuplicatedStrings(items, this.caseSensitive)) {
330
- this.updateItems(items);
304
+ if (hasDuplicatedStrings(items, this.caseSensitive)) {
305
+ this.toggleError('duplicate', true, INPUT.create);
306
+
307
+ return;
331
308
  }
332
- }
333
309
 
334
- if (closeInput) {
335
- this.toggleCreateMode(false);
310
+ this.updateItems(items);
336
311
  }
312
+ this.toggleCreateMode(false);
337
313
  },
338
314
 
339
315
  /**
340
316
  * Update an existing item in the items list
341
317
  */
342
- updateItem(item: string, closeInput = true) {
318
+ updateItem(item: string) {
343
319
  const value = this.value?.trim();
344
320
 
345
321
  if (value) {
@@ -350,21 +326,22 @@ export default Vue.extend({
350
326
  items[index] = value;
351
327
  }
352
328
 
353
- if (!hasDuplicatedStrings(items, this.caseSensitive)) {
354
- this.updateItems(items);
329
+ if (hasDuplicatedStrings(items, this.caseSensitive)) {
330
+ this.toggleError('duplicate', true, INPUT.edit);
331
+
332
+ return;
355
333
  }
356
- }
357
334
 
358
- if (closeInput) {
359
- this.toggleEditMode(false);
335
+ this.updateItems(items);
360
336
  }
337
+ this.toggleEditMode(false);
361
338
  },
362
339
 
363
340
  /**
364
341
  * Remove an item from items list
365
342
  */
366
343
  deleteItem(item?: string) {
367
- const items = this.items.filter((f) => f !== item);
344
+ const items = this.items.filter(f => f !== item);
368
345
 
369
346
  this.updateItems(items);
370
347
  },
@@ -410,20 +387,19 @@ export default Vue.extend({
410
387
  @blur="onSelectLeave(item)"
411
388
  >
412
389
  <span
413
- v-if="!editedItem || editedItem !== item"
390
+ v-if="!isEditItem || isEditItem !== item"
414
391
  class="label static"
415
392
  >
416
393
  {{ item }}
417
394
  </span>
418
395
  <LabeledInput
419
- v-if="editedItem && editedItem === item"
396
+ v-if="isEditItem && isEditItem === item"
420
397
  ref="item-edit"
421
- :data-testid="`item-edit-${item}`"
422
398
  class="edit-input static"
423
399
  :value="value != null ? value : item"
424
400
  @input="onChange($event)"
425
- @blur.prevent="updateItem(item)"
426
- @keydown.native.enter="updateItem(item, !errors.duplicate)"
401
+ @blur.prevent="toggleEditMode(false)"
402
+ @keydown.native.enter="updateItem(item)"
427
403
  />
428
404
  </div>
429
405
  <div
@@ -432,14 +408,12 @@ export default Vue.extend({
432
408
  >
433
409
  <LabeledInput
434
410
  ref="item-create"
435
- data-testid="item-create"
436
411
  class="create-input static"
437
412
  type="text"
438
413
  :value="value"
439
414
  :placeholder="placeholder"
440
415
  @input="onChange($event)"
441
- @blur.prevent="saveItem"
442
- @keydown.native.enter="saveItem(!errors.duplicate)"
416
+ @keydown.native.enter="saveItem"
443
417
  />
444
418
  </div>
445
419
  </div>
@@ -453,32 +427,25 @@ export default Vue.extend({
453
427
  class="action-buttons"
454
428
  >
455
429
  <button
456
- data-testid="button-remove"
457
430
  class="btn btn-sm role-tertiary remove-button"
458
- :disabled="!selected && !isCreateItem && !editedItem"
431
+ :disabled="!selected && !isCreateItem && !isEditItem"
459
432
  @mousedown.prevent="onClickMinusButton"
460
433
  >
461
434
  <span class="icon icon-minus icon-sm" />
462
435
  </button>
463
436
  <button
464
- data-testid="button-add"
465
437
  class="btn btn-sm role-tertiary add-button"
466
- :disabled="isCreateItem || editedItem"
438
+ :disabled="isCreateItem"
467
439
  @click.prevent="onClickPlusButton"
468
440
  >
469
441
  <span class="icon icon-plus icon-sm" />
470
442
  </button>
471
443
  </div>
472
444
  <div class="messages">
473
- <i
474
- v-if="errorMessagesArray.length > 0"
475
- data-testid="i-warning-icon"
476
- class="icon icon-warning icon-lg"
477
- />
445
+ <i v-if="errorMessagesArray.length > 0" class="icon icon-warning icon-lg" />
478
446
  <span
479
447
  v-for="(msg, idx) in errorMessagesArray"
480
448
  :key="idx"
481
- :data-testid="`span-error-message-${msg}`"
482
449
  class="error"
483
450
  >
484
451
  {{ idx > 0 ? '; ' : '' }}
@@ -532,7 +499,6 @@ export default Vue.extend({
532
499
  width: auto;
533
500
  user-select: none;
534
501
  overflow: hidden;
535
- white-space: no-wrap;
536
502
  text-overflow: ellipsis;
537
503
  padding-top: 1px;
538
504
  }
@@ -0,0 +1,30 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { RadioGroup } from './index';
3
+
4
+ describe('component: RadioGroup', () => {
5
+ describe('when disabled', () => {
6
+ it.each([true, false])('should expose disabled slot prop for indexed slots for %p', (disabled) => {
7
+ const wrapper = mount(RadioGroup, {
8
+ propsData: {
9
+ name: 'whatever',
10
+ options: [{ label: 'whatever', value: 'whatever' }],
11
+ disabled
12
+ },
13
+ scopedSlots: {
14
+ 0(props: {isDisabled: boolean}) {
15
+ return this.$createElement('input', {
16
+ attrs: {
17
+ id: 'test',
18
+ disabled: props.isDisabled
19
+ }
20
+ });
21
+ }
22
+ }
23
+ });
24
+
25
+ const slot = wrapper.find('#test').element as HTMLInputElement;
26
+
27
+ expect(slot.disabled).toBe(disabled);
28
+ });
29
+ });
30
+ });
@@ -169,6 +169,7 @@ export default Vue.extend({
169
169
 
170
170
  <template>
171
171
  <div>
172
+ <!-- Label -->
172
173
  <div
173
174
  v-if="label || labelKey || tooltip || tooltipKey || $slots.label"
174
175
  class="radio-group label"
@@ -195,6 +196,8 @@ export default Vue.extend({
195
196
  </h3>
196
197
  </slot>
197
198
  </div>
199
+
200
+ <!-- Group -->
198
201
  <div
199
202
  class="radio-group"
200
203
  :class="{'row':row}"
@@ -212,6 +215,7 @@ export default Vue.extend({
212
215
  :is-disabled="isDisabled"
213
216
  :name="i"
214
217
  >
218
+ <!-- Default input -->
215
219
  <RadioButton
216
220
  :key="name+'-'+i"
217
221
  :name="name"
@@ -398,6 +398,276 @@ describe('stringList.vue', () => {
398
398
  });
399
399
  });
400
400
 
401
+ describe('bulk delimiter', () => {
402
+ const delimiter = /;/;
403
+
404
+ describe('add', () => {
405
+ const items: string[] = [];
406
+
407
+ beforeEach(() => {
408
+ wrapper = mount(StringList, {
409
+ propsData: {
410
+ items,
411
+ bulkAdditionDelimiter: delimiter,
412
+ errorMessages: { duplicate: 'error, item is duplicate.' },
413
+ }
414
+ });
415
+ });
416
+
417
+ it('should split values if delimiter set', async() => {
418
+ const value = 'test;test1;test2';
419
+ const result = ['test', 'test1', 'test2'];
420
+
421
+ // activate create mode
422
+ await wrapper.setData({ isCreateItem: true });
423
+ const inputField = wrapper.find('[data-testid="item-create"]');
424
+
425
+ await inputField.setValue(value);
426
+
427
+ // press enter
428
+ await inputField.trigger('keydown.enter');
429
+ await wrapper.vm.$nextTick();
430
+
431
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
432
+
433
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
434
+ });
435
+
436
+ it('should show warning if one of the values is a duplicate', async() => {
437
+ const value = 'test;test1;test2';
438
+
439
+ await wrapper.setProps({ items: ['test1'] });
440
+
441
+ // activate create mode
442
+ await wrapper.setData({ isCreateItem: true });
443
+ const inputField = wrapper.find('[data-testid="item-create"]');
444
+
445
+ await inputField.setValue(value);
446
+
447
+ // press enter
448
+ await inputField.trigger('keydown.enter');
449
+ await wrapper.vm.$nextTick();
450
+
451
+ const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
452
+
453
+ expect(isDuplicate).toBe(true);
454
+ });
455
+
456
+ it('should show a warning if the new values are all duplicates', async() => {
457
+ const value = 'test;test';
458
+
459
+ // activate create mode
460
+ await wrapper.setData({ isCreateItem: true });
461
+ const inputField = wrapper.find('[data-testid="item-create"]');
462
+
463
+ await inputField.setValue(value);
464
+
465
+ // press enter
466
+ await inputField.trigger('keydown.enter');
467
+ await wrapper.vm.$nextTick();
468
+
469
+ const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
470
+
471
+ expect(isDuplicate).toBe(true);
472
+ });
473
+
474
+ it('should not consider empty strings at the beginning', async() => {
475
+ const value = ';test;test1;test2';
476
+ const result = ['test', 'test1', 'test2'];
477
+
478
+ // activate create mode
479
+ await wrapper.setData({ isCreateItem: true });
480
+ const inputField = wrapper.find('[data-testid="item-create"]');
481
+
482
+ await inputField.setValue(value);
483
+
484
+ // press enter
485
+ await inputField.trigger('keydown.enter');
486
+ await wrapper.vm.$nextTick();
487
+
488
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
489
+
490
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
491
+ });
492
+
493
+ it('should not consider empty strings in the middle', async() => {
494
+ const value = 'test;test1;;test2';
495
+ const result = ['test', 'test1', 'test2'];
496
+
497
+ // activate create mode
498
+ await wrapper.setData({ isCreateItem: true });
499
+ const inputField = wrapper.find('[data-testid="item-create"]');
500
+
501
+ await inputField.setValue(value);
502
+
503
+ // press enter
504
+ await inputField.trigger('keydown.enter');
505
+ await wrapper.vm.$nextTick();
506
+
507
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
508
+
509
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
510
+ });
511
+
512
+ it('should not consider empty strings at the end', async() => {
513
+ const value = 'test;test1;test2;';
514
+ const result = ['test', 'test1', 'test2'];
515
+
516
+ // activate create mode
517
+ await wrapper.setData({ isCreateItem: true });
518
+ const inputField = wrapper.find('[data-testid="item-create"]');
519
+
520
+ await inputField.setValue(value);
521
+
522
+ // press enter
523
+ await inputField.trigger('keydown.enter');
524
+ await wrapper.vm.$nextTick();
525
+
526
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
527
+
528
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
529
+ });
530
+ });
531
+
532
+ describe('edit', () => {
533
+ const items = ['test1', 'test2'];
534
+
535
+ beforeEach(() => {
536
+ wrapper = mount(StringList, {
537
+ propsData: {
538
+ items,
539
+ bulkAdditionDelimiter: delimiter,
540
+ errorMessages: { duplicate: 'error, item is duplicate.' },
541
+ }
542
+ });
543
+ });
544
+
545
+ it('should split values if delimiter set', async() => {
546
+ const newValue = 'test1;new;values';
547
+ const result = ['test1', 'new', 'values', 'test2'];
548
+
549
+ await wrapper.setData({ editedItem: items[0] });
550
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
551
+
552
+ await inputField.setValue(newValue);
553
+
554
+ // press enter
555
+ await inputField.trigger('keydown.enter');
556
+ await wrapper.vm.$nextTick();
557
+
558
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
559
+
560
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
561
+ });
562
+
563
+ it('should show warning if one of the values is a duplicate', async() => {
564
+ const newValue = 'test1;test2';
565
+
566
+ await wrapper.setData({ editedItem: items[0] });
567
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
568
+
569
+ await inputField.setValue(newValue);
570
+
571
+ // press enter
572
+ await inputField.trigger('keydown.enter');
573
+ await wrapper.vm.$nextTick();
574
+
575
+ const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
576
+
577
+ expect(isDuplicate).toBe(true);
578
+ });
579
+
580
+ it('should show a warning if the new values are all duplicates', async() => {
581
+ const newValue = 'test;test';
582
+
583
+ await wrapper.setData({ editedItem: items[0] });
584
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
585
+
586
+ await inputField.setValue(newValue);
587
+
588
+ // press enter
589
+ await inputField.trigger('keydown.enter');
590
+ await wrapper.vm.$nextTick();
591
+
592
+ const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
593
+
594
+ expect(isDuplicate).toBe(true);
595
+ });
596
+
597
+ it('should not consider empty strings at the beginning', async() => {
598
+ const newValue = ';test1;new;value';
599
+ const result = ['test1', 'new', 'value', 'test2'];
600
+
601
+ await wrapper.setData({ editedItem: items[0] });
602
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
603
+
604
+ await inputField.setValue(newValue);
605
+
606
+ // press enter
607
+ await inputField.trigger('keydown.enter');
608
+ await wrapper.vm.$nextTick();
609
+
610
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
611
+
612
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
613
+ });
614
+
615
+ it('should not consider empty strings in the middle 1', async() => {
616
+ const newValue = 'test1; ;new;value';
617
+ const result = ['test1', 'new', 'value', 'test2'];
618
+
619
+ await wrapper.setData({ editedItem: items[0] });
620
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
621
+
622
+ await inputField.setValue(newValue);
623
+
624
+ // press enter
625
+ await inputField.trigger('keydown.enter');
626
+ await wrapper.vm.$nextTick();
627
+
628
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
629
+
630
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
631
+ });
632
+
633
+ it('should not consider empty strings in the middle 2', async() => {
634
+ const newValue = 'test1;;new;value';
635
+ const result = ['test1', 'new', 'value', 'test2'];
636
+
637
+ await wrapper.setData({ editedItem: items[0] });
638
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
639
+
640
+ await inputField.setValue(newValue);
641
+
642
+ // press enter
643
+ await inputField.trigger('keydown.enter');
644
+ await wrapper.vm.$nextTick();
645
+
646
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
647
+
648
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
649
+ });
650
+
651
+ it('should not consider empty strings at the end', async() => {
652
+ const newValue = 'test1;new;value;';
653
+ const result = ['test1', 'new', 'value', 'test2'];
654
+
655
+ await wrapper.setData({ editedItem: items[0] });
656
+ const inputField = wrapper.find('[data-testid^="item-edit"]');
657
+
658
+ await inputField.setValue(newValue);
659
+
660
+ // press enter
661
+ await inputField.trigger('keydown.enter');
662
+ await wrapper.vm.$nextTick();
663
+
664
+ const itemsResult = (wrapper.emitted('change') || [])[0][0];
665
+
666
+ expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
667
+ });
668
+ });
669
+ });
670
+
401
671
  describe('errors handling', () => {
402
672
  it('show duplicate warning icon when errorMessages is defined', async() => {
403
673
  const items = ['test'];