@scality/data-browser-library 1.0.4 → 1.0.6

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 (60) hide show
  1. package/dist/components/DataBrowserUI.js +18 -8
  2. package/dist/components/__tests__/BucketCreate.test.js +60 -20
  3. package/dist/components/__tests__/BucketList.test.js +91 -6
  4. package/dist/components/__tests__/BucketNotificationFormPage.test.js +54 -19
  5. package/dist/components/__tests__/BucketReplicationFormPage.test.js +183 -61
  6. package/dist/components/__tests__/MetadataSearch.test.js +18 -12
  7. package/dist/components/__tests__/ObjectList.test.js +94 -2
  8. package/dist/components/buckets/BucketCreate.d.ts +1 -0
  9. package/dist/components/buckets/BucketCreate.js +57 -7
  10. package/dist/components/buckets/BucketDetails.js +0 -1
  11. package/dist/components/buckets/BucketLifecycleFormPage.js +209 -213
  12. package/dist/components/buckets/BucketList.js +25 -4
  13. package/dist/components/buckets/BucketReplicationFormPage.js +9 -3
  14. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
  15. package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
  16. package/dist/components/objects/DeleteObjectButton.d.ts +1 -0
  17. package/dist/components/objects/DeleteObjectButton.js +11 -5
  18. package/dist/components/objects/ObjectDetails/FormComponents.d.ts +9 -0
  19. package/dist/components/objects/ObjectDetails/FormComponents.js +37 -0
  20. package/dist/components/objects/ObjectDetails/ObjectMetadata.js +182 -204
  21. package/dist/components/objects/ObjectDetails/ObjectSummary.js +22 -5
  22. package/dist/components/objects/ObjectDetails/ObjectTags.js +109 -154
  23. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +3 -3
  24. package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.d.ts +1 -0
  25. package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.js +230 -0
  26. package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.d.ts +1 -0
  27. package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.js +342 -0
  28. package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.d.ts +1 -0
  29. package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.js +202 -0
  30. package/dist/components/objects/ObjectDetails/index.d.ts +2 -1
  31. package/dist/components/objects/ObjectDetails/index.js +12 -16
  32. package/dist/components/objects/ObjectList.d.ts +3 -2
  33. package/dist/components/objects/ObjectList.js +204 -104
  34. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +78 -26
  35. package/dist/components/objects/ObjectPage.js +22 -5
  36. package/dist/components/ui/ArrayFieldActions.js +0 -2
  37. package/dist/components/ui/FilterFormSection.js +17 -36
  38. package/dist/components/ui/FormGrid.d.ts +7 -0
  39. package/dist/components/ui/FormGrid.js +37 -0
  40. package/dist/components/ui/Table.elements.js +1 -0
  41. package/dist/config/__tests__/factory.test.js +29 -1
  42. package/dist/config/factory.d.ts +2 -0
  43. package/dist/config/factory.js +3 -1
  44. package/dist/config/types.d.ts +45 -2
  45. package/dist/hooks/__tests__/usePresigningS3Client.test.d.ts +1 -0
  46. package/dist/hooks/__tests__/usePresigningS3Client.test.js +104 -0
  47. package/dist/hooks/factories/index.d.ts +1 -1
  48. package/dist/hooks/factories/index.js +2 -2
  49. package/dist/hooks/factories/useCreateS3MutationHook.d.ts +2 -1
  50. package/dist/hooks/factories/useCreateS3MutationHook.js +10 -3
  51. package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -0
  52. package/dist/hooks/factories/useCreateS3QueryHook.js +9 -6
  53. package/dist/hooks/index.d.ts +1 -0
  54. package/dist/hooks/index.js +2 -1
  55. package/dist/hooks/presignedOperations.js +4 -4
  56. package/dist/hooks/useBucketLocations.d.ts +6 -0
  57. package/dist/hooks/useBucketLocations.js +45 -0
  58. package/dist/hooks/usePresigningS3Client.d.ts +13 -0
  59. package/dist/hooks/usePresigningS3Client.js +21 -0
  60. package/package.json +4 -3
@@ -55,8 +55,12 @@ describe('BucketReplicationFormPage', ()=>{
55
55
  const mockMutate = jest.fn();
56
56
  const fillRequiredFields = async (options)=>{
57
57
  const { roleArn = 'arn:aws:iam::123456789012:role/replication-role', ruleId, targetBucket = 'destination-bucket' } = options;
58
- await user_event.type(screen.getByLabelText(/role arn/i), roleArn);
59
- await user_event.type(screen.getByLabelText(/rule id/i), ruleId);
58
+ await user_event.type(screen.getByRole('textbox', {
59
+ name: /role arn/i
60
+ }), roleArn);
61
+ await user_event.type(screen.getByRole('textbox', {
62
+ name: /rule id/i
63
+ }), ruleId);
60
64
  const targetBucketSelect = screen.getByLabelText(/target bucket/i);
61
65
  await user_event.click(targetBucketSelect);
62
66
  await user_event.click(screen.getByRole('option', {
@@ -147,8 +151,12 @@ describe('BucketReplicationFormPage', ()=>{
147
151
  it('displays all form sections', async ()=>{
148
152
  renderBucketReplicationFormPage();
149
153
  await waitFor(()=>{
150
- expect(screen.getByLabelText(/role arn/i)).toBeInTheDocument();
151
- expect(screen.getByLabelText(/rule id/i)).toBeInTheDocument();
154
+ expect(screen.getByRole('textbox', {
155
+ name: /role arn/i
156
+ })).toBeInTheDocument();
157
+ expect(screen.getByRole('textbox', {
158
+ name: /rule id/i
159
+ })).toBeInTheDocument();
152
160
  expect(screen.getByText('Replicate encrypted objects')).toBeInTheDocument();
153
161
  expect(screen.getByText('Delete marker replication')).toBeInTheDocument();
154
162
  });
@@ -157,14 +165,20 @@ describe('BucketReplicationFormPage', ()=>{
157
165
  describe('Form Fields - Initial State', ()=>{
158
166
  it('renders required form fields in create mode', ()=>{
159
167
  renderBucketReplicationFormPage();
160
- expect(screen.getByLabelText(/role arn/i)).toBeInTheDocument();
161
- expect(screen.getByLabelText(/rule id/i)).toBeInTheDocument();
168
+ expect(screen.getByRole('textbox', {
169
+ name: /role arn/i
170
+ })).toBeInTheDocument();
171
+ expect(screen.getByRole('textbox', {
172
+ name: /rule id/i
173
+ })).toBeInTheDocument();
162
174
  expect(screen.getByLabelText(/status/i)).toBeInTheDocument();
163
175
  expect(screen.getByLabelText(/target bucket/i)).toBeInTheDocument();
164
176
  });
165
177
  it('shows Role ARN input when no existing rules', ()=>{
166
178
  renderBucketReplicationFormPage();
167
- const roleInput = screen.getByLabelText(/role arn/i);
179
+ const roleInput = screen.getByRole('textbox', {
180
+ name: /role arn/i
181
+ });
168
182
  expect(roleInput).toBeInTheDocument();
169
183
  expect(roleInput.tagName).toBe('INPUT');
170
184
  });
@@ -194,7 +208,9 @@ describe('BucketReplicationFormPage', ()=>{
194
208
  });
195
209
  it('shows Rule ID input in create mode', ()=>{
196
210
  renderBucketReplicationFormPage();
197
- const ruleIdInput = screen.getByLabelText(/rule id/i);
211
+ const ruleIdInput = screen.getByRole('textbox', {
212
+ name: /rule id/i
213
+ });
198
214
  expect(ruleIdInput).toBeInTheDocument();
199
215
  expect(ruleIdInput.tagName).toBe('INPUT');
200
216
  });
@@ -229,14 +245,18 @@ describe('BucketReplicationFormPage', ()=>{
229
245
  it('renders Status select field', async ()=>{
230
246
  renderBucketReplicationFormPage();
231
247
  await waitFor(()=>{
232
- expect(screen.getByLabelText(/rule id/i)).toBeInTheDocument();
248
+ expect(screen.getByRole('textbox', {
249
+ name: /rule id/i
250
+ })).toBeInTheDocument();
233
251
  });
234
252
  const statusElement = document.querySelector('[id="status"]');
235
253
  expect(statusElement).toBeInTheDocument();
236
254
  });
237
255
  it('renders Priority number input with auto-assigned placeholder', ()=>{
238
256
  renderBucketReplicationFormPage();
239
- const priorityInput = screen.getByLabelText(/rule priority/i);
257
+ const priorityInput = screen.getByRole('spinbutton', {
258
+ name: /rule priority/i
259
+ });
240
260
  expect(priorityInput).toBeInTheDocument();
241
261
  expect(priorityInput).toHaveAttribute('placeholder', 'Example: Auto-assigned: 0');
242
262
  });
@@ -255,7 +275,9 @@ describe('BucketReplicationFormPage', ()=>{
255
275
  });
256
276
  it('renders storage class select with all options', async ()=>{
257
277
  renderBucketReplicationFormPage();
258
- const storageClassSelect = screen.getByLabelText(/storage class/i);
278
+ const storageClassSelect = screen.getByLabelText(/storage class/i, {
279
+ selector: 'input'
280
+ });
259
281
  await user_event.click(storageClassSelect);
260
282
  expect(screen.getByRole('option', {
261
283
  name: 'Same as source'
@@ -283,7 +305,9 @@ describe('BucketReplicationFormPage', ()=>{
283
305
  describe('Form Validation', ()=>{
284
306
  it('validates Role ARN is required for first replication rule', async ()=>{
285
307
  renderBucketReplicationFormPage();
286
- const roleInput = screen.getByLabelText(/role arn/i);
308
+ const roleInput = screen.getByRole('textbox', {
309
+ name: /role arn/i
310
+ });
287
311
  await user_event.type(roleInput, 'test');
288
312
  await user_event.clear(roleInput);
289
313
  await waitFor(()=>{
@@ -310,7 +334,9 @@ describe('BucketReplicationFormPage', ()=>{
310
334
  status: 'success'
311
335
  });
312
336
  renderBucketReplicationFormPage();
313
- await user_event.type(screen.getByLabelText(/rule id/i), 'new-rule');
337
+ await user_event.type(screen.getByRole('textbox', {
338
+ name: /rule id/i
339
+ }), 'new-rule');
314
340
  const targetBucketSelect = screen.getByLabelText(/target bucket/i);
315
341
  await user_event.click(targetBucketSelect);
316
342
  await user_event.click(screen.getByRole('option', {
@@ -325,7 +351,9 @@ describe('BucketReplicationFormPage', ()=>{
325
351
  });
326
352
  it('validates Rule ID is required', async ()=>{
327
353
  renderBucketReplicationFormPage();
328
- const ruleIdInput = screen.getByLabelText(/rule id/i);
354
+ const ruleIdInput = screen.getByRole('textbox', {
355
+ name: /rule id/i
356
+ });
329
357
  await user_event.type(ruleIdInput, 'test');
330
358
  await user_event.clear(ruleIdInput);
331
359
  await waitFor(()=>{
@@ -352,7 +380,9 @@ describe('BucketReplicationFormPage', ()=>{
352
380
  status: 'success'
353
381
  });
354
382
  renderBucketReplicationFormPage();
355
- const ruleIdInput = screen.getByLabelText(/rule id/i);
383
+ const ruleIdInput = screen.getByRole('textbox', {
384
+ name: /rule id/i
385
+ });
356
386
  await user_event.type(ruleIdInput, 'existing-rule');
357
387
  await waitFor(()=>{
358
388
  expect(screen.getByText(/a rule with this id already exists/i)).toBeInTheDocument();
@@ -360,8 +390,12 @@ describe('BucketReplicationFormPage', ()=>{
360
390
  });
361
391
  it('validates Priority must be >= 0', async ()=>{
362
392
  renderBucketReplicationFormPage();
363
- await user_event.type(screen.getByLabelText(/rule id/i), 'test-rule');
364
- const priorityInput = screen.getByLabelText(/rule priority/i);
393
+ await user_event.type(screen.getByRole('textbox', {
394
+ name: /rule id/i
395
+ }), 'test-rule');
396
+ const priorityInput = screen.getByRole('spinbutton', {
397
+ name: /rule priority/i
398
+ });
365
399
  await user_event.clear(priorityInput);
366
400
  await user_event.type(priorityInput, '-1');
367
401
  await user_event.tab();
@@ -371,8 +405,12 @@ describe('BucketReplicationFormPage', ()=>{
371
405
  });
372
406
  it('disables submit button when Target Bucket is not selected', async ()=>{
373
407
  renderBucketReplicationFormPage();
374
- await user_event.type(screen.getByLabelText(/role arn/i), 'arn:aws:iam::123456789012:role/role');
375
- await user_event.type(screen.getByLabelText(/rule id/i), 'test-rule');
408
+ await user_event.type(screen.getByRole('textbox', {
409
+ name: /role arn/i
410
+ }), 'arn:aws:iam::123456789012:role/role');
411
+ await user_event.type(screen.getByRole('textbox', {
412
+ name: /rule id/i
413
+ }), 'test-rule');
376
414
  await waitFor(()=>{
377
415
  const createButton = screen.getByRole('button', {
378
416
  name: /create/i
@@ -385,7 +423,9 @@ describe('BucketReplicationFormPage', ()=>{
385
423
  const sameAccountToggle = findToggleByLabel('Same account destination');
386
424
  await user_event.click(sameAccountToggle);
387
425
  await waitFor(()=>{
388
- expect(screen.getByLabelText(/target account id/i)).toBeInTheDocument();
426
+ expect(screen.getByRole('textbox', {
427
+ name: /target account id/i
428
+ })).toBeInTheDocument();
389
429
  });
390
430
  });
391
431
  it('shows Replica KMS Key ID field when encryptReplicatedObjects is true', async ()=>{
@@ -393,7 +433,9 @@ describe('BucketReplicationFormPage', ()=>{
393
433
  const encryptToggle = findToggleByLabel('Encrypt replicated objects');
394
434
  await user_event.click(encryptToggle);
395
435
  await waitFor(()=>{
396
- expect(screen.getByLabelText(/replica kms key id/i)).toBeInTheDocument();
436
+ expect(screen.getByRole('textbox', {
437
+ name: /replica kms key id/i
438
+ })).toBeInTheDocument();
397
439
  });
398
440
  });
399
441
  it('shows prefix field when filterType is prefix', async ()=>{
@@ -409,7 +451,9 @@ describe('BucketReplicationFormPage', ()=>{
409
451
  name: 'Prefix filter'
410
452
  }));
411
453
  await waitFor(()=>{
412
- expect(screen.getByLabelText(/prefix/i)).toBeInTheDocument();
454
+ expect(screen.getByRole('textbox', {
455
+ name: /prefix/i
456
+ })).toBeInTheDocument();
413
457
  });
414
458
  });
415
459
  });
@@ -505,7 +549,9 @@ describe('BucketReplicationFormPage', ()=>{
505
549
  const sameAccountToggle = findToggleByLabel('Same account destination');
506
550
  await user_event.click(sameAccountToggle);
507
551
  await waitFor(()=>{
508
- expect(screen.getByLabelText(/target account id/i)).toBeInTheDocument();
552
+ expect(screen.getByRole('textbox', {
553
+ name: /target account id/i
554
+ })).toBeInTheDocument();
509
555
  });
510
556
  });
511
557
  it('renders target bucket as Input for cross-account replication', async ()=>{
@@ -523,7 +569,9 @@ describe('BucketReplicationFormPage', ()=>{
523
569
  const encryptToggle = findToggleByLabel('Encrypt replicated objects');
524
570
  await user_event.click(encryptToggle);
525
571
  await waitFor(()=>{
526
- expect(screen.getByLabelText(/replica kms key id/i)).toBeInTheDocument();
572
+ expect(screen.getByRole('textbox', {
573
+ name: /replica kms key id/i
574
+ })).toBeInTheDocument();
527
575
  });
528
576
  });
529
577
  });
@@ -556,9 +604,15 @@ describe('BucketReplicationFormPage', ()=>{
556
604
  });
557
605
  it('submits complete replication rule with all optional fields', async ()=>{
558
606
  renderBucketReplicationFormPage();
559
- await user_event.type(screen.getByLabelText(/role arn/i), 'arn:aws:iam::123456789012:role/replication-role');
560
- await user_event.type(screen.getByLabelText(/rule id/i), 'complete-rule');
561
- await user_event.type(screen.getByLabelText(/rule priority/i), '5');
607
+ await user_event.type(screen.getByRole('textbox', {
608
+ name: /role arn/i
609
+ }), 'arn:aws:iam::123456789012:role/replication-role');
610
+ await user_event.type(screen.getByRole('textbox', {
611
+ name: /rule id/i
612
+ }), 'complete-rule');
613
+ await user_event.type(screen.getByRole('spinbutton', {
614
+ name: /rule priority/i
615
+ }), '5');
562
616
  const filterSelect = document.querySelector('[id="filterType"]');
563
617
  await user_event.click(filterSelect);
564
618
  await waitFor(()=>{
@@ -570,15 +624,23 @@ describe('BucketReplicationFormPage', ()=>{
570
624
  name: 'Prefix filter'
571
625
  }));
572
626
  await waitFor(()=>{
573
- expect(screen.getByLabelText(/prefix/i)).toBeInTheDocument();
627
+ expect(screen.getByRole('textbox', {
628
+ name: /prefix/i
629
+ })).toBeInTheDocument();
574
630
  });
575
- await user_event.type(screen.getByLabelText(/prefix/i), 'logs/');
631
+ await user_event.type(screen.getByRole('textbox', {
632
+ name: /prefix/i
633
+ }), 'logs/');
576
634
  const sameAccountToggle = findToggleByLabel('Same account destination');
577
635
  await user_event.click(sameAccountToggle);
578
636
  await waitFor(()=>{
579
- expect(screen.getByLabelText(/target account id/i)).toBeInTheDocument();
637
+ expect(screen.getByRole('textbox', {
638
+ name: /target account id/i
639
+ })).toBeInTheDocument();
580
640
  });
581
- await user_event.type(screen.getByLabelText(/target account id/i), '987654321098');
641
+ await user_event.type(screen.getByRole('textbox', {
642
+ name: /target account id/i
643
+ }), '987654321098');
582
644
  await user_event.type(screen.getByLabelText(/target bucket/i), 'cross-account-bucket');
583
645
  const storageClassSelect = document.querySelector('[id="storageClass"]');
584
646
  await user_event.click(storageClassSelect);
@@ -595,9 +657,13 @@ describe('BucketReplicationFormPage', ()=>{
595
657
  const includeEncryptedToggle = findToggleByLabel('Replicate encrypted objects');
596
658
  await user_event.click(includeEncryptedToggle);
597
659
  await waitFor(()=>{
598
- expect(screen.getByLabelText(/replica kms key id/i)).toBeInTheDocument();
660
+ expect(screen.getByRole('textbox', {
661
+ name: /replica kms key id/i
662
+ })).toBeInTheDocument();
599
663
  });
600
- await user_event.type(screen.getByLabelText(/replica kms key id/i), 'arn:aws:kms:us-east-1:987654321098:key/12345678-1234-1234-1234-123456789012');
664
+ await user_event.type(screen.getByRole('textbox', {
665
+ name: /replica kms key id/i
666
+ }), 'arn:aws:kms:us-east-1:987654321098:key/12345678-1234-1234-1234-123456789012');
601
667
  const enforceRTCToggle = findToggleByLabel('Replication Time Control (RTC)');
602
668
  await user_event.click(enforceRTCToggle);
603
669
  const replicaModToggle = findToggleByLabel('Replica modification sync');
@@ -674,7 +740,9 @@ describe('BucketReplicationFormPage', ()=>{
674
740
  status: 'success'
675
741
  });
676
742
  renderBucketReplicationFormPage();
677
- await user_event.type(screen.getByLabelText(/rule id/i), 'new-rule');
743
+ await user_event.type(screen.getByRole('textbox', {
744
+ name: /rule id/i
745
+ }), 'new-rule');
678
746
  const targetBucketSelect = screen.getByLabelText(/target bucket/i);
679
747
  await user_event.click(targetBucketSelect);
680
748
  await user_event.click(screen.getByRole('option', {
@@ -753,7 +821,9 @@ describe('BucketReplicationFormPage', ()=>{
753
821
  });
754
822
  it('disables submit button when form is invalid', async ()=>{
755
823
  renderBucketReplicationFormPage();
756
- await user_event.type(screen.getByLabelText(/rule id/i), 'test');
824
+ await user_event.type(screen.getByRole('textbox', {
825
+ name: /rule id/i
826
+ }), 'test');
757
827
  await waitFor(()=>{
758
828
  const createButton = screen.getByRole('button', {
759
829
  name: /create/i
@@ -1033,7 +1103,9 @@ describe('BucketReplicationFormPage', ()=>{
1033
1103
  });
1034
1104
  renderBucketReplicationFormPage('test-bucket', 'prefix-rule');
1035
1105
  await waitFor(()=>{
1036
- const prefixInput = screen.getByLabelText(/prefix/i);
1106
+ const prefixInput = screen.getByRole('textbox', {
1107
+ name: /prefix/i
1108
+ });
1037
1109
  expect(prefixInput).toHaveValue('documents/');
1038
1110
  });
1039
1111
  });
@@ -1106,7 +1178,9 @@ describe('BucketReplicationFormPage', ()=>{
1106
1178
  });
1107
1179
  renderBucketReplicationFormPage('test-bucket', 'and-rule');
1108
1180
  await waitFor(()=>{
1109
- const prefixInput = screen.getByLabelText(/prefix/i);
1181
+ const prefixInput = screen.getByRole('textbox', {
1182
+ name: /prefix/i
1183
+ });
1110
1184
  expect(prefixInput).toHaveValue('logs/');
1111
1185
  expect(screen.getByDisplayValue('type')).toBeInTheDocument();
1112
1186
  expect(screen.getByDisplayValue('audit')).toBeInTheDocument();
@@ -1172,7 +1246,9 @@ describe('BucketReplicationFormPage', ()=>{
1172
1246
  await waitFor(()=>{
1173
1247
  const encryptReplicatedToggle = findToggleByLabel('Encrypt replicated objects');
1174
1248
  expect(encryptReplicatedToggle).toBeChecked();
1175
- const kmsKeyInput = screen.getByLabelText(/replica kms key id/i);
1249
+ const kmsKeyInput = screen.getByRole('textbox', {
1250
+ name: /replica kms key id/i
1251
+ });
1176
1252
  expect(kmsKeyInput).toHaveValue('arn:aws:kms:us-east-1:123456789012:key/12345');
1177
1253
  });
1178
1254
  });
@@ -1300,7 +1376,9 @@ describe('BucketReplicationFormPage', ()=>{
1300
1376
  await waitFor(()=>{
1301
1377
  const sameAccountToggle = screen.getByText(/not same account destination/i);
1302
1378
  expect(sameAccountToggle).toBeInTheDocument();
1303
- const accountIdInput = screen.getByLabelText(/target account id/i);
1379
+ const accountIdInput = screen.getByRole('textbox', {
1380
+ name: /target account id/i
1381
+ });
1304
1382
  expect(accountIdInput).toHaveValue('987654321098');
1305
1383
  const targetBucketInput = screen.getByLabelText(/target bucket/i);
1306
1384
  expect(targetBucketInput).toHaveValue('cross-account-bucket');
@@ -1359,7 +1437,9 @@ describe('BucketReplicationFormPage', ()=>{
1359
1437
  });
1360
1438
  renderBucketReplicationFormPage('test-bucket', 'storage-class-rule');
1361
1439
  await waitFor(()=>{
1362
- const storageClassSelect = screen.getByLabelText(/storage class/i);
1440
+ const storageClassSelect = screen.getByLabelText(/storage class/i, {
1441
+ selector: 'input'
1442
+ });
1363
1443
  expect(storageClassSelect).toBeInTheDocument();
1364
1444
  });
1365
1445
  });
@@ -1385,7 +1465,9 @@ describe('BucketReplicationFormPage', ()=>{
1385
1465
  });
1386
1466
  renderBucketReplicationFormPage('test-bucket', 'priority-rule');
1387
1467
  await waitFor(()=>{
1388
- const priorityInput = screen.getByLabelText(/rule priority/i);
1468
+ const priorityInput = screen.getByRole('spinbutton', {
1469
+ name: /rule priority/i
1470
+ });
1389
1471
  expect(priorityInput).toHaveValue(99);
1390
1472
  });
1391
1473
  });
@@ -1393,7 +1475,9 @@ describe('BucketReplicationFormPage', ()=>{
1393
1475
  describe('Priority Auto-assignment', ()=>{
1394
1476
  it('auto-assigns priority 0 when no existing rules', ()=>{
1395
1477
  renderBucketReplicationFormPage();
1396
- const priorityInput = screen.getByLabelText(/rule priority/i);
1478
+ const priorityInput = screen.getByRole('spinbutton', {
1479
+ name: /rule priority/i
1480
+ });
1397
1481
  expect(priorityInput).toHaveAttribute('placeholder', 'Example: Auto-assigned: 0');
1398
1482
  });
1399
1483
  it('auto-assigns next available priority when rules exist', ()=>{
@@ -1424,19 +1508,27 @@ describe('BucketReplicationFormPage', ()=>{
1424
1508
  status: 'success'
1425
1509
  });
1426
1510
  renderBucketReplicationFormPage();
1427
- const priorityInput = screen.getByLabelText(/rule priority/i);
1511
+ const priorityInput = screen.getByRole('spinbutton', {
1512
+ name: /rule priority/i
1513
+ });
1428
1514
  expect(priorityInput).toHaveAttribute('placeholder', 'Example: Auto-assigned: 11');
1429
1515
  });
1430
1516
  it('allows manual priority override', async ()=>{
1431
1517
  renderBucketReplicationFormPage();
1432
- const priorityInput = screen.getByLabelText(/rule priority/i);
1518
+ const priorityInput = screen.getByRole('spinbutton', {
1519
+ name: /rule priority/i
1520
+ });
1433
1521
  await user_event.type(priorityInput, '25');
1434
1522
  expect(priorityInput).toHaveValue(25);
1435
1523
  });
1436
1524
  it('accepts empty priority and defaults to 0 on submit', async ()=>{
1437
1525
  renderBucketReplicationFormPage();
1438
- await user_event.type(screen.getByLabelText(/role arn/i), 'arn:aws:iam::123456789012:role/role');
1439
- await user_event.type(screen.getByLabelText(/rule id/i), 'no-priority-rule');
1526
+ await user_event.type(screen.getByRole('textbox', {
1527
+ name: /role arn/i
1528
+ }), 'arn:aws:iam::123456789012:role/role');
1529
+ await user_event.type(screen.getByRole('textbox', {
1530
+ name: /rule id/i
1531
+ }), 'no-priority-rule');
1440
1532
  const targetBucketSelect = screen.getByLabelText(/target bucket/i);
1441
1533
  await user_event.click(targetBucketSelect);
1442
1534
  await user_event.click(screen.getByRole('option', {
@@ -1520,8 +1612,12 @@ describe('BucketReplicationFormPage', ()=>{
1520
1612
  isPending: true
1521
1613
  });
1522
1614
  renderBucketReplicationFormPage();
1523
- await user_event.type(screen.getByLabelText(/role arn/i), 'arn:aws:iam::123456789012:role/role');
1524
- await user_event.type(screen.getByLabelText(/rule id/i), 'test-rule');
1615
+ await user_event.type(screen.getByRole('textbox', {
1616
+ name: /role arn/i
1617
+ }), 'arn:aws:iam::123456789012:role/role');
1618
+ await user_event.type(screen.getByRole('textbox', {
1619
+ name: /rule id/i
1620
+ }), 'test-rule');
1525
1621
  const targetBucketSelect = screen.getByLabelText(/target bucket/i);
1526
1622
  await user_event.click(targetBucketSelect);
1527
1623
  await user_event.click(screen.getByRole('option', {
@@ -1619,7 +1715,9 @@ describe('BucketReplicationFormPage', ()=>{
1619
1715
  });
1620
1716
  renderBucketReplicationFormPage('test-bucket', 'no-priority');
1621
1717
  await waitFor(()=>{
1622
- const priorityInput = screen.getByLabelText(/rule priority/i);
1718
+ const priorityInput = screen.getByRole('spinbutton', {
1719
+ name: /rule priority/i
1720
+ });
1623
1721
  expect(priorityInput).toHaveValue(null);
1624
1722
  });
1625
1723
  });
@@ -1645,7 +1743,9 @@ describe('BucketReplicationFormPage', ()=>{
1645
1743
  });
1646
1744
  renderBucketReplicationFormPage('test-bucket', 'no-storage-class');
1647
1745
  await waitFor(()=>{
1648
- const storageClassSelect = screen.getByLabelText(/storage class/i);
1746
+ const storageClassSelect = screen.getByLabelText(/storage class/i, {
1747
+ selector: 'input'
1748
+ });
1649
1749
  expect(storageClassSelect).toBeInTheDocument();
1650
1750
  });
1651
1751
  });
@@ -1654,7 +1754,9 @@ describe('BucketReplicationFormPage', ()=>{
1654
1754
  it('accepts Rule ID with reasonable length', async ()=>{
1655
1755
  renderBucketReplicationFormPage();
1656
1756
  const longRuleId = 'rule-name-with-many-segments-' + 'segment-'.repeat(10);
1657
- const ruleIdInput = screen.getByLabelText(/rule id/i);
1757
+ const ruleIdInput = screen.getByRole('textbox', {
1758
+ name: /rule id/i
1759
+ });
1658
1760
  await user_event.type(ruleIdInput, longRuleId);
1659
1761
  await waitFor(()=>{
1660
1762
  expect(ruleIdInput).toHaveValue(longRuleId);
@@ -1663,7 +1765,9 @@ describe('BucketReplicationFormPage', ()=>{
1663
1765
  it('handles special characters in Rule ID', async ()=>{
1664
1766
  renderBucketReplicationFormPage();
1665
1767
  const validSpecialChars = 'rule-name_with.special-chars_123';
1666
- const ruleIdInput = screen.getByLabelText(/rule id/i);
1768
+ const ruleIdInput = screen.getByRole('textbox', {
1769
+ name: /rule id/i
1770
+ });
1667
1771
  await user_event.type(ruleIdInput, validSpecialChars);
1668
1772
  await user_event.tab();
1669
1773
  await waitFor(()=>{
@@ -1672,8 +1776,12 @@ describe('BucketReplicationFormPage', ()=>{
1672
1776
  });
1673
1777
  it('validates Priority with boundary values', async ()=>{
1674
1778
  renderBucketReplicationFormPage();
1675
- await user_event.type(screen.getByLabelText(/rule id/i), 'boundary-test');
1676
- const priorityInput = screen.getByLabelText(/rule priority/i);
1779
+ await user_event.type(screen.getByRole('textbox', {
1780
+ name: /rule id/i
1781
+ }), 'boundary-test');
1782
+ const priorityInput = screen.getByRole('spinbutton', {
1783
+ name: /rule priority/i
1784
+ });
1677
1785
  await user_event.clear(priorityInput);
1678
1786
  await user_event.type(priorityInput, '0');
1679
1787
  await user_event.tab();
@@ -1700,10 +1808,14 @@ describe('BucketReplicationFormPage', ()=>{
1700
1808
  name: 'Prefix filter'
1701
1809
  }));
1702
1810
  await waitFor(()=>{
1703
- expect(screen.getByLabelText(/prefix/i)).toBeInTheDocument();
1811
+ expect(screen.getByRole('textbox', {
1812
+ name: /prefix/i
1813
+ })).toBeInTheDocument();
1704
1814
  });
1705
1815
  const longPrefix = 'logs/' + 'a'.repeat(100);
1706
- const prefixInput = screen.getByLabelText(/prefix/i);
1816
+ const prefixInput = screen.getByRole('textbox', {
1817
+ name: /prefix/i
1818
+ });
1707
1819
  await user_event.type(prefixInput, longPrefix);
1708
1820
  await waitFor(()=>{
1709
1821
  expect(prefixInput).toHaveValue(longPrefix);
@@ -1711,7 +1823,9 @@ describe('BucketReplicationFormPage', ()=>{
1711
1823
  });
1712
1824
  it('accepts valid Role ARN format', async ()=>{
1713
1825
  renderBucketReplicationFormPage();
1714
- const roleArnInput = screen.getByLabelText(/role arn/i);
1826
+ const roleArnInput = screen.getByRole('textbox', {
1827
+ name: /role arn/i
1828
+ });
1715
1829
  await user_event.type(roleArnInput, 'arn:aws:iam::123456789012:role/replication-role');
1716
1830
  await user_event.tab();
1717
1831
  await waitFor(()=>{
@@ -1723,9 +1837,13 @@ describe('BucketReplicationFormPage', ()=>{
1723
1837
  const sameAccountToggle = findToggleByLabel('Same account destination');
1724
1838
  await user_event.click(sameAccountToggle);
1725
1839
  await waitFor(()=>{
1726
- expect(screen.getByLabelText(/target account id/i)).toBeInTheDocument();
1840
+ expect(screen.getByRole('textbox', {
1841
+ name: /target account id/i
1842
+ })).toBeInTheDocument();
1843
+ });
1844
+ const accountIdInput = screen.getByRole('textbox', {
1845
+ name: /target account id/i
1727
1846
  });
1728
- const accountIdInput = screen.getByLabelText(/target account id/i);
1729
1847
  await user_event.type(accountIdInput, '123456789012');
1730
1848
  await user_event.tab();
1731
1849
  await waitFor(()=>{
@@ -1744,9 +1862,13 @@ describe('BucketReplicationFormPage', ()=>{
1744
1862
  const encryptToggle = findToggleByLabel('Encrypt replicated objects');
1745
1863
  await user_event.click(encryptToggle);
1746
1864
  await waitFor(()=>{
1747
- expect(screen.getByLabelText(/replica kms key id/i)).toBeInTheDocument();
1865
+ expect(screen.getByRole('textbox', {
1866
+ name: /replica kms key id/i
1867
+ })).toBeInTheDocument();
1868
+ });
1869
+ const kmsKeyInput = screen.getByRole('textbox', {
1870
+ name: /replica kms key id/i
1748
1871
  });
1749
- const kmsKeyInput = screen.getByLabelText(/replica kms key id/i);
1750
1872
  const validKmsArn = 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012';
1751
1873
  await user_event.type(kmsKeyInput, validKmsArn);
1752
1874
  await waitFor(()=>{
@@ -47,27 +47,33 @@ describe('MetadataSearch', ()=>{
47
47
  await user_event.type(input, 'test query');
48
48
  expect(searchButton).toBeEnabled();
49
49
  });
50
- it('shows search icon by default', ()=>{
50
+ it('shows search icon by default', async ()=>{
51
51
  renderMetadataSearch();
52
- const input = getInput();
53
- const searchIcon = input.parentElement?.querySelector('[aria-label*="Search"]');
54
- expect(searchIcon).toBeInTheDocument();
52
+ await waitFor(()=>{
53
+ expect(screen.getByRole('img', {
54
+ hidden: true
55
+ })).toBeInTheDocument();
56
+ });
55
57
  });
56
- it('shows check icon when metadata search is active', ()=>{
58
+ it('shows check icon when metadata search is active', async ()=>{
57
59
  renderMetadataSearch({}, [
58
60
  '/bucket/test-bucket?metadatasearch=test'
59
61
  ]);
60
- const input = getInput();
61
- const checkIcon = input.parentElement?.querySelector('[aria-label*="Check"]');
62
- expect(checkIcon).toBeInTheDocument();
62
+ await waitFor(()=>{
63
+ expect(screen.getByRole('img', {
64
+ hidden: true
65
+ })).toBeInTheDocument();
66
+ });
63
67
  });
64
- it('shows error icon when error prop is true', ()=>{
68
+ it('shows error icon when error prop is true', async ()=>{
65
69
  renderMetadataSearch({
66
70
  isError: true
67
71
  });
68
- const input = getInput();
69
- const errorIcon = input.parentElement?.querySelector('[aria-label*="Close"]');
70
- expect(errorIcon).toBeInTheDocument();
72
+ await waitFor(()=>{
73
+ expect(screen.getByRole('img', {
74
+ hidden: true
75
+ })).toBeInTheDocument();
76
+ });
71
77
  });
72
78
  it('populates input with existing search query from URL', ()=>{
73
79
  renderMetadataSearch({}, [