-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathDomainFormPanel.java
More file actions
770 lines (674 loc) · 28.3 KB
/
DomainFormPanel.java
File metadata and controls
770 lines (674 loc) · 28.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
package org.labkey.test.components.domain;
import org.apache.commons.lang3.StringUtils;
import org.labkey.test.BootstrapLocators;
import org.labkey.test.Locator;
import org.labkey.test.WebDriverWrapper;
import org.labkey.test.components.bootstrap.ModalDialog;
import org.labkey.test.components.html.Checkbox;
import org.labkey.test.components.react.ToggleButton;
import org.labkey.test.components.ui.grids.ResponsiveGrid;
import org.labkey.test.pages.core.admin.BaseSettingsPage.DATE_FORMAT;
import org.labkey.test.pages.core.admin.BaseSettingsPage.TIME_FORMAT;
import org.labkey.test.params.FieldDefinition;
import org.labkey.test.selenium.WebElementWrapper;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import static org.labkey.test.WebDriverWrapper.WAIT_FOR_JAVASCRIPT;
import static org.labkey.test.WebDriverWrapper.waitFor;
/**
* Automates the LabKey ui component defined in: packages/components/src/components/domainproperties/DomainForm.tsx
*/
public class DomainFormPanel extends DomainPanel<DomainFormPanel.ElementCache, DomainFormPanel>
{
public DomainFormPanel(DomainPanel<?,?> panel)
{
super(panel);
}
private DomainFormPanel(WebElement element, WebDriver driver)
{
super(element, driver);
}
@Override
protected void waitForReady()
{
waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 10_000);
}
public static List<AdvancedFieldSetting> advancedSettingsFromFieldDefinition(FieldDefinition def)
{
List<AdvancedFieldSetting> advancedSettings = new ArrayList<>();
//TODO: Add missing settings to 'FieldDefinitions:
// Show in default view
// Default type
// Exclude from shifting
// Recommended variable
if (def.getShownInUpdateView() != null)
{
advancedSettings.add(AdvancedFieldSetting.shownInUpdateView(def.getShownInUpdateView()));
}
if (def.getShownInInsertView() != null)
{
advancedSettings.add(AdvancedFieldSetting.shownInInsertView(def.getShownInInsertView()));
}
if (def.getShownInDetailsView() != null)
{
advancedSettings.add(AdvancedFieldSetting.shownInDetailsView(def.getShownInDetailsView()));
}
if (def.getMeasure() != null)
{
advancedSettings.add(AdvancedFieldSetting.measure(def.getMeasure()));
}
if (def.getDimension() != null)
{
advancedSettings.add(AdvancedFieldSetting.dimension(def.getDimension()));
}
if (def.getMvEnabled() != null)
{
advancedSettings.add(AdvancedFieldSetting.mvEnabled(def.getMvEnabled()));
}
if (def.getPHI() != null)
{
advancedSettings.add(AdvancedFieldSetting.PHI(def.getPhiLevel()));
}
return advancedSettings;
}
@Override
protected DomainFormPanel getThis()
{
return this;
}
public DomainFormPanel addField(FieldDefinition fieldDefinition)
{
DomainFieldRow fieldRow = addField(fieldDefinition.getName());
return editField(fieldRow, fieldDefinition);
}
public DomainFormPanel addFields(List<FieldDefinition> fieldDefinitions)
{
for (FieldDefinition fieldDefinition : fieldDefinitions)
{
addField(fieldDefinition);
}
return this;
}
public DomainFormPanel setField(FieldDefinition fieldDefinition)
{
DomainFieldRow fieldRow = getField(fieldDefinition.getName());
return editField(fieldRow, fieldDefinition);
}
private DomainFormPanel editField(DomainFieldRow fieldRow, FieldDefinition fieldDefinition)
{
if (fieldDefinition.getType() != null)
{
if (fieldDefinition.getType().isLookup())
fieldRow.setLookup(fieldDefinition.getLookup());
else
fieldRow.setType(fieldDefinition.getType());
}
if (fieldDefinition.getDescription() != null)
fieldRow.setDescription(fieldDefinition.getDescription());
if (fieldDefinition.getLabel() != null)
fieldRow.setLabel(fieldDefinition.getLabel());
if (fieldDefinition.getFormat() != null)
{
if (fieldDefinition.getType().equals(FieldDefinition.ColumnType.Date))
{
fieldRow.setDateFormat(DATE_FORMAT.get(fieldDefinition.getFormat()));
}
else if (fieldDefinition.getType().equals(FieldDefinition.ColumnType.Time))
{
fieldRow.setTimeFormat(TIME_FORMAT.get(fieldDefinition.getFormat()));
}
else if (fieldDefinition.getType().equals(FieldDefinition.ColumnType.DateAndTime))
{
// Identify the part of the format that is the date and the part that is the time.
String formatStr = fieldDefinition.getFormat().trim();
int index = formatStr.indexOf(":");
if (index == -1)
{
// If there is no ':' then it is a date only format.
fieldRow.setDateTimeFormat(DATE_FORMAT.get(formatStr));
}
else
{
// Split the format into the date part and the time part. Take into account that the date part may
// have several spaces and the time part may have a space if there is an am/pm indicator.
// Find the last index of the ' ' before the ':'.
String tmpStr = formatStr.substring(0, index);
index = tmpStr.lastIndexOf(" ");
fieldRow.setDateTimeFormat(
DATE_FORMAT.get(formatStr.substring(0, index)),
TIME_FORMAT.get(formatStr.substring(index + 1)));
}
}
else
{
fieldRow.setFormat(fieldDefinition.getFormat(), fieldDefinition.getRangeURI());
}
}
if (fieldDefinition.getScale() != null)
fieldRow.setCharCount(fieldDefinition.getScale());
if (fieldDefinition.getURL() != null)
fieldRow.setUrl(fieldDefinition.getURL());
if (fieldDefinition.isURLOpenNewTab())
fieldRow.setUrlOpenNewTab(fieldDefinition.isURLOpenNewTab());
if (fieldDefinition.getImportAliases() != null)
fieldRow.setImportAliases(fieldDefinition.getImportAliases());
if (fieldDefinition.getRequired())
fieldRow.setRequiredField(fieldDefinition.getRequired());
if (fieldDefinition.getLookupValidatorEnabled() != null)
fieldRow.setLookupValidatorEnabled(fieldDefinition.getLookupValidatorEnabled());
if (fieldDefinition.getAliquotOption() != null)
fieldRow.setAliquotOption(fieldDefinition.getAliquotOption());
if (fieldDefinition.getValueExpression() != null)
fieldRow.setValueExpression(fieldDefinition.getValueExpression());
// ontology-specific
if (fieldDefinition.getSourceOntology() != null)
fieldRow.setSelectedOntology(fieldDefinition.getSourceOntology());
if (fieldDefinition.getConceptImportColumn() != null)
fieldRow.setConceptImportField(fieldDefinition.getConceptImportColumn());
if (fieldDefinition.getConceptLabelColumn() != null)
fieldRow.setConceptLabelField(fieldDefinition.getConceptLabelColumn());
if (fieldDefinition.getConceptSubTree() != null)
{
var subTreePath = Arrays.asList(fieldDefinition.getConceptSubTree().split("/"));
fieldRow.clickExpectedVocabulary()
.selectOntology(fieldDefinition.getPrincipalConceptSearchSourceOntology())
.selectNodeFromPath(subTreePath)
.clickApply();
}
if (fieldDefinition.getPrincipalConceptCode() != null)
fieldRow.clickSelectConcept()
.selectOntology(fieldDefinition.getPrincipalConceptSearchSourceOntology())
.searchConcept(fieldDefinition.getPrincipalConceptSearchExpression(), fieldDefinition.getPrincipalConceptCode())
.waitForActiveTreeNode()
.clickApply();
if (fieldDefinition.getValidators() != null && !fieldDefinition.getValidators().isEmpty())
{
List<FieldDefinition.RegExValidator> regexValidators = new ArrayList<>();
List<FieldDefinition.RangeValidator> rangeValidators = new ArrayList<>();
List<FieldDefinition.FieldValidator<?>> validators = fieldDefinition.getValidators();
for (FieldDefinition.FieldValidator<?> validator : validators)
{
if (validator instanceof FieldDefinition.RegExValidator)
{
regexValidators.add((FieldDefinition.RegExValidator) validator);
}
else if (validator instanceof FieldDefinition.RangeValidator)
{
rangeValidators.add((FieldDefinition.RangeValidator) validator);
}
else if (validator instanceof FieldDefinition.TextChoiceValidator textChoiceValidator)
{
// TextChoice is a field type; implemented using a special validator. TextChoice field cannot have other validators.
if (validators.size() > 1)
{
throw new IllegalArgumentException("TextChoice fields cannot have additional validators.");
}
fieldRow.setTextChoiceValues(textChoiceValidator.getValues());
if(fieldDefinition.getType() == FieldDefinition.ColumnType.MultiValueTextChoice)
{
fieldRow.setAllowMultipleSelections(true);
}
}
else
{
throw new IllegalArgumentException("Validator not supported: " + validator.getClass().getName());
}
}
if (!regexValidators.isEmpty())
{
fieldRow.setRegExValidators(regexValidators);
}
if (!rangeValidators.isEmpty())
{
fieldRow.setRangeValidators(rangeValidators);
}
}
fieldRow.setAdvancedSettings(advancedSettingsFromFieldDefinition(fieldDefinition));
fieldRow.collapse();
return this;
}
/**
* Get the fields panel without adding/creating a field. Can be useful when testing fields that should be auto-created.
*
* @return A {@link DomainFormPanel}
*/
public DomainFormPanel clickManuallyDefineFields()
{
getWrapper().shortWait().until(ExpectedConditions.elementToBeClickable(elementCache().manuallyDefineButton)); // give modal dialogs time to disappear
getWrapper().scrollIntoView(elementCache().manuallyDefineButton, true);
elementCache().manuallyDefineButton.click();
return this;
}
public DomainFieldRow addField(String name)
{
if (isManuallyDefineFieldsPresent())
return manuallyDefineFields(name);
getWrapper().scrollIntoView(elementCache().addFieldButton, true);
getWrapper().shortWait().until(ExpectedConditions.elementToBeClickable(elementCache().addFieldButton)); // give modal dialogs time to disappear
elementCache().addFieldButton.click();
List<DomainFieldRow> fieldRows = elementCache().findFieldRows();
DomainFieldRow newFieldRow = fieldRows.get(fieldRows.size() - 1);
newFieldRow.setName(name);
return newFieldRow;
}
public boolean isManuallyDefineFieldsPresent()
{
return !getThis().findElements(elementCache().manuallyDefineFieldsLoc).isEmpty();
}
/**
* Switch from "Import or infer fields from file" mode to "Manually define fields" mode.
* The designer adds a field automatically.
* This is only valid for a domain with no fields defined (usually a newly created domain)
* @param name Name will be applied to the automatically added field.
* @return row for the initially added field
*/
public DomainFieldRow manuallyDefineFields(String name)
{
clickManuallyDefineFields();
DomainFieldRow newFieldRow = elementCache().findFieldRows().get(0);
newFieldRow.setName(name);
return newFieldRow;
}
public DomainFieldRow manuallyDefineFields(FieldDefinition fieldDefinition)
{
DomainFieldRow fieldRow = manuallyDefineFields(fieldDefinition.getName());
editField(fieldRow, fieldDefinition);
return fieldRow;
}
public DomainFormPanel removeField(String name)
{
return removeField(name, false);
}
public DomainFormPanel removeField(String name, boolean confirmDialogExpected)
{
getWrapper().log("attempting to remove field " + name);
getField(name).clickRemoveField(confirmDialogExpected);
clearElementCache();
return this;
}
public DomainFieldRow getField(String name)
{
DomainFieldRow row = elementCache().findFieldRow(name);
scrollRowIntoView(row);
return row;
}
public DomainFieldRow getField(int tabIndex)
{
DomainFieldRow row = elementCache().findFieldRows().get(tabIndex);
scrollRowIntoView(row);
return row;
}
private void scrollRowIntoView(DomainFieldRow row)
{
if (null != row) // only do this if it's non-null
{
getWrapper().scrollIntoView(row.getComponentElement());
}
}
public DomainFormPanel removeAllFields(boolean confirmDialogExpected)
{
List<String> fieldNames = fieldNames();
for (String name : fieldNames)
{
removeField(name, confirmDialogExpected);
}
return this;
}
public DomainFormPanel checkSelectAll(boolean value)
{
elementCache().selectAll.set(value);
return this;
}
public File clickExportFields() throws Exception
{
getWrapper().scrollIntoView(elementCache().exportFieldsButton);
File[] exportFiles = getWrapper().doAndWaitForDownload(()-> {
elementCache().exportFieldsButton.click();
}, 1);
return exportFiles[0];
}
public DomainFormPanel clickDeleteFields()
{
getWrapper().scrollIntoView(elementCache().deleteFieldsButton);
elementCache().deleteFieldsButton.click();
ModalDialog confirmDialog = new ModalDialog.ModalDialogFinder(getDriver())
.withTitle("Confirm Delete Selected Fields").timeout(1000).waitFor();
confirmDialog.dismiss("Yes, Delete Fields");
return this;
}
// default system fields
/*
Default System Fields, for the nonce, will appear in the 'Fields' form of the domain designer for Sample and
DataClass domains. They will be shown in a ResponsiveGrid that can be shown/hidden via a toggle
*/
/*
shows the DefaultSystemFields grid if it is available and not shown
*/
public DomainFormPanel expandDefaultSystemFields()
{
if (!isDefaultSystemFieldsExpanded())
elementCache().defaultSystemFieldsToggle().click();
WebDriverWrapper.waitFor(()-> isDefaultSystemFieldsExpanded(),
"the default system fields display did not expand in time", 2000);
return this;
}
/*
collapses the DefaultSystemFields grid if it is available and expanded
*/
public DomainFormPanel collapseDefaultSystemFields()
{
if (isDefaultSystemFieldsExpanded())
elementCache().defaultSystemFieldsToggle().click();
WebDriverWrapper.waitFor(()-> !isDefaultSystemFieldsExpanded(),
"the default system fields display did not collapse in time", 2000);
return this;
}
public ResponsiveGrid getDefaultSystemFieldsGrid()
{
expandDefaultSystemFields();
WebElement gridContainer = Locator.tagWithClass("div", "domain-system-fields__grid")
.waitForElement(this, 2000);
return new ResponsiveGrid.ResponsiveGridFinder(getDriver()).waitFor(gridContainer);
}
/*
When expanded, the i tag will show a minus, when collapsed it will advertise its expandability
by showing a "+" icon
*/
public boolean isDefaultSystemFieldsExpanded()
{
var toggleClass = elementCache().defaultSystemFieldsToggle().getAttribute("class");
return toggleClass != null && toggleClass.contains("fa-minus-square");
}
/*
Mode, in this context, means how the fields in this domain will be shown.
Mode is toggled via the toggle in the custom fields action bar.
in 'Summary mode', fields are shown in a grid, with columns representing field attributes like 'name', 'range uri', etc
in 'Detail mode', fields appear as editable, in FieldRows
*/
private String getMode()
{
return elementCache().customFieldsViewToggle.getSelectedStatus(); // will be either "Summary" or "Detail"
}
/**
* Selects the desired mode. Possible values are "Summary" and "Detail"
* @param name The name of the desired mode, or the text to appear in the toggle in its desired state
* @return an instance of the current DomainFormPanel
*/
private DomainFormPanel switchMode(String name)
{
if (!getMode().equalsIgnoreCase(name))
{
boolean isSummary = elementCache().customFieldsViewToggle.isOn();
elementCache().customFieldsViewToggle.set(!isSummary);
}
WebDriverWrapper.waitFor(()-> getMode().equalsIgnoreCase(name),
"the mode select toggle did not become [" +name+ "] as expected", 2000);
return this;
}
/*
Summary mode shows a responsive grid containing a row per field, with columns for field attributes
*/
public boolean isSummaryMode()
{
return getMode().equalsIgnoreCase("Summary");
}
public DomainFormPanel selectSummaryMode()
{
return switchMode("Summary");
}
/*
gets the grid containing row data/metadata in summary view
*/
public ResponsiveGrid getSummaryModeGrid()
{
selectSummaryMode();
return new ResponsiveGrid.ResponsiveGridFinder(getDriver())
.locatedBy(Locator.tagWithClass("div", "domain-field-toolbar").followingSibling("div").followingSibling("div"))
.waitFor(this);
}
/*
*/
public DomainFormPanel clickSummaryGridNameLink(String linkText)
{
var targetCell = getSummaryModeGrid().getRow("Name", linkText).getCell("Name");
var clickable = Locator.linkWithText(linkText).findElement(targetCell);
clickable.click();
WebDriverWrapper.waitFor(()-> isDetailMode(),
"DomainFormPanel did not switch to detail mode as expected", 2000);
return this;
}
public List<String> getSummaryModeColumns()
{
var rawColumns = getSummaryModeGrid().getColumnLabels();
return rawColumns.subList(1, rawColumns.size()); // omit the 'select' column, it is an empty value
}
/*
Detail mode is the default view, it shows the list of (expandable, collapsable, editable) fieldRows
*/
public boolean isDetailMode()
{
return getMode().equalsIgnoreCase("Detail");
}
public DomainFormPanel selectDetailMode()
{
return switchMode("Detail");
}
/*
Summary mode shows a table containing a row per field, with columns representing field attributes
*/
public int getRowcountInSummaryMode()
{
selectSummaryMode();
return getSummaryModeGrid().getRows().size();
}
public DomainFormPanel setInferFieldFile(File file)
{
getWrapper().setFormElement(elementCache().fileUploadInput, file);
getWrapper().waitFor(()-> !elementCache().findFieldRows().isEmpty(),
"fields were not inferred from file in time", WAIT_FOR_JAVASCRIPT);
return this;
}
public List<String> fieldNames()
{
return elementCache().findFieldRows()
.stream()
.map(DomainFieldRow::getName)
.collect(Collectors.toList());
}
public String getPanelErrorText()
{
return getPanelErrorWebElement().getText();
}
public WebElement getPanelErrorWebElement()
{
getWrapper().waitFor(()-> BootstrapLocators.errorBanner.existsIn(getDriver()),
"the error alert did not appear as expected", 1000);
// It would be better to not return a raw WebElement but who knows what the future holds, different alerts
// may show different controls.
return BootstrapLocators.errorBanner.existsIn(getDriver()) ? BootstrapLocators.errorBanner.findElement(getDriver()) : null;
}
/**
* Get the alert message that is shown only in the alert panel. An example of this is the Results Field in
* Sample Manager requires a field that is a sample look-up, if it missing an alert is shown.
* This alert can only be dismissed by adding the field.
* @return String of the alert message, empty if not present.
*/
public String getPanelAlertText()
{
return getPanelAlertText(0);
}
public String getPanelAlertText(int index)
{
WebElement panelAlertWebElement = getPanelAlertWebElement(index);
if (panelAlertWebElement != null)
return panelAlertWebElement.getText();
else
return "";
}
public List<String> getPanelAlertTexts()
{
return getWrapper().getTexts(getPanelAlertElements());
}
/**
* There may be an element in the alert that a test will need to interact with so return the alert element and let
* the test find the control it needs.
* @return The div wrapping the alert in the panel, null otherwise.
*/
public WebElement getPanelAlertWebElement()
{
return getPanelAlertWebElement(0);
}
public WebElement getPanelAlertWebElement(int index)
{
List<WebElement> panelAlertElements = getPanelAlertElements();
if (panelAlertElements.size() > index)
return panelAlertElements.get(index);
else
return null;
}
public List<WebElement> getPanelAlertElements()
{
try
{
return BootstrapLocators.infoBanner.waitForElements(this, 1000);
}
catch (NoSuchElementException nothing)
{
return Collections.emptyList();
}
}
@Override
protected ElementCache newElementCache()
{
return new ElementCache();
}
protected class ElementCache extends DomainPanel<ElementCache, DomainFormPanel>.ElementCache
{
public final Checkbox selectAll = new Checkbox(Locator.tagWithAttributeContaining("input", "id", "domain-select-all-checkbox")
.findWhenNeeded(this));
public final WebElement toggleButton = Locator.tagWithAttributeContaining("div", "id", "domain-toggle-summary").
findWhenNeeded(this);
public final ToggleButton customFieldsViewToggle = new ToggleButton.ToggleButtonFinder(getDriver())
.withState("Detail").timeout(5000).findWhenNeeded(this);
protected WebElement addFieldButton = new WebElementWrapper()
{
final WebElement el = Locator.css(".domain-form-add-btn .btn").findWhenNeeded(DomainFormPanel.this);
@Override
public WebElement getWrappedElement()
{
return el;
}
@Override
public void click()
{
int initialCount = findFieldRows().size();
super.click();
WebDriverWrapper.waitFor(() -> {
clearFieldCache();
return findFieldRows().size() == initialCount + 1;
}, "New field didn't appear", 10000);
}
};
protected WebElement exportFieldsButton = Locator.tagWithClass("div", "domain-toolbar-export-btn")
.findWhenNeeded(this);
protected WebElement deleteFieldsButton = Locator.tagWithClass("div", "domain-toolbar-delete-btn")
.findWhenNeeded(this);
protected void clearFieldCache()
{
fieldRows = null;
fieldNames.clear();
}
// Should only modify row collections with findFieldRows() and addFieldButton.click()
private List<DomainFieldRow> fieldRows;
private final Map<String, Integer> fieldNames = new TreeMap<>();
private final Locator rowLoc = Locator.tagWithClass("div", "domain-field-row").withoutClass("domain-floating-hdr");
private List<DomainFieldRow> findFieldRows()
{
fieldRows = new ArrayList<>(); // this method used to cache this arraylist,
// but it was too fragile and didn't save us much runtime
// now we look for it when we ask for it
rowLoc.findElements(DomainFormPanel.this.getComponentElement())
.forEach(e -> fieldRows.add(new DomainFieldRow(DomainFormPanel.this, e, getDriver())));
return fieldRows;
}
private DomainFieldRow findFieldRow(String name)
{
WebDriverWrapper.waitFor(() -> !findFieldRows().isEmpty(), 1_000);
List<DomainFieldRow> fieldRows = findFieldRows();
for (int i = 0; i < fieldRows.size(); i++)
{
DomainFieldRow fieldRow = fieldRows.get(i);
String fieldRowName = fieldRow.getName();
if (!fieldNames.containsValue(i) && !StringUtils.trimToEmpty(fieldRowName).isEmpty())
{
fieldNames.put(fieldRowName, i);
}
if (name.equalsIgnoreCase(fieldRowName))
{
return fieldRow;
}
}
if (!fieldNames.containsKey(name))
return null;
return fieldRows.get(fieldNames.get(name));
}
Locator.XPathLocator manuallyDefineFieldsLoc = Locator.tagWithClass("div", "domain-form-manual-btn");
protected WebElement manuallyDefineButton = new WebElementWrapper()
{
final WebElement el = Locator.css(".domain-form-manual-btn").findWhenNeeded(DomainFormPanel.this);
@Override
public WebElement getWrappedElement()
{
return el;
}
@Override
public void click()
{
super.click();
WebDriverWrapper.waitFor(() -> {
clearFieldCache();
return findFieldRows().size() == 1;
}, "New manually defined field didn't appear", 10000);
}
};
WebElement fileUploadInput = Locator.tagWithClass("input", "file-upload__input").findWhenNeeded(DomainFormPanel.this).withTimeout(2000);
WebElement defaultSystemFieldsContainer = Locator.tagWithClass("div", "domain-system-fields")
.findWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT);
WebElement defaultSystemFieldsToggle()
{
return Locator.tagWithClass("div", "domain-system-fields-header__icon")
.child("i").waitForElement(defaultSystemFieldsContainer, 2000);
}
}
/**
* This will find any domain panel.
* There is no simple method to differentiate field editor panels from other domain panels
*/
public static class DomainFormPanelFinder extends BaseDomainPanelFinder<DomainFormPanel, DomainFormPanelFinder>
{
public DomainFormPanelFinder(WebDriver driver)
{
super(driver);
}
@Override
protected DomainFormPanelFinder getThis()
{
return this;
}
@Override
protected DomainFormPanel construct(WebElement el, WebDriver driver)
{
return new DomainFormPanel(el, driver);
}
}
}