diff --git a/src/org/labkey/test/components/ChartLayoutDialog.java b/src/org/labkey/test/components/ChartLayoutDialog.java index 63bf6db606..5a2763e27e 100644 --- a/src/org/labkey/test/components/ChartLayoutDialog.java +++ b/src/org/labkey/test/components/ChartLayoutDialog.java @@ -190,6 +190,35 @@ public ChartLayoutDialog setYAxisRangeMinMax(String min, String max) return this; } + public ChartLayoutDialog setYAxisAggregateMethod(String method) + { + clickYAxisTab(); + getWrapper()._ext4Helper.selectComboBoxItem("Aggregate Method:", method); + return this; + } + + public boolean isYAxisAggregateMethodVisible() + { + return getWrapper().isElementPresent(Ext4Helper.Locators.formItemWithLabel("Aggregate Method:")); + } + + public ChartLayoutDialog setYAxisErrorBarsMethod(String method) + { + clickYAxisTab(); + if (method.equals("Standard Deviation")) + getWrapper().click(elementCache().sdErrorBarRadioButton); + else if (method.equals("Standard Error of the Mean")) + getWrapper().click(elementCache().semErrorBarRadioButton); + else + getWrapper().click(elementCache().noneErrorBarRadioButton); + return this; + } + + public boolean isYAxisErrorBarsMethodVisible() + { + return getWrapper().isElementPresent(Ext4Helper.Locators.formItemWithLabel("Error Bars:")); + } + protected void setLabel(String label) { getWrapper().setFormElement(elementCache().visibleLabelTextBox, label); @@ -461,6 +490,9 @@ class ElementCache extends ChartWizardDialog.ElementCache public Locator visibleLabelTextBox = Locator.xpath(VISIBLE_PANEL_XPATH + "//input[@name='label']"); public Locator visibleRangeMinTextBox = Locator.xpath(VISIBLE_PANEL_XPATH + "//input[@name='rangeMin']"); public Locator visibleRangeMaxTextBox = Locator.xpath(VISIBLE_PANEL_XPATH + "//input[@name='rangeMax']"); + public Locator noneErrorBarRadioButton = Locator.xpath(VISIBLE_PANEL_XPATH + "//label[text()='None']/preceding-sibling::input[@type='button']"); + public Locator sdErrorBarRadioButton = Locator.xpath(VISIBLE_PANEL_XPATH + "//label[text()='Standard Deviation']/preceding-sibling::input[@type='button']"); + public Locator semErrorBarRadioButton = Locator.xpath(VISIBLE_PANEL_XPATH + "//label[text()='Standard Error of the Mean']/preceding-sibling::input[@type='button']"); } public enum ScaleType diff --git a/src/org/labkey/test/components/react/QueryChartDialog.java b/src/org/labkey/test/components/react/QueryChartDialog.java index 3782d5396f..29edbd878a 100644 --- a/src/org/labkey/test/components/react/QueryChartDialog.java +++ b/src/org/labkey/test/components/react/QueryChartDialog.java @@ -98,10 +98,9 @@ public boolean hasAxisFieldOptions(String label) return elementCache().fieldOptionIconByLabel(label) != null; } - private QueryChartDialog clickAxisFieldOptions(String label) + private void clickFieldOptions(String label) { elementCache().fieldOptionIconByLabel(label).click(); - return this; } /** @@ -112,23 +111,23 @@ private QueryChartDialog clickAxisFieldOptions(String label) */ public QueryChartDialog setAxisScaleType(String label, String value) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover if ("linear".equalsIgnoreCase(value)) elementCache().scaleLinearRadio.check(); else if ("log".equalsIgnoreCase(value)) elementCache().scaleLogRadio.check(); else throw new IllegalArgumentException("Invalid scale value: " + value); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return this; } public boolean isAxisScaleTypeSelected(String label, String value) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover boolean selected = "linear".equalsIgnoreCase(value) ? elementCache().scaleLinearRadio.isChecked() : "log".equalsIgnoreCase(value) && elementCache().scaleLogRadio.isChecked(); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return selected; } @@ -140,51 +139,51 @@ public boolean isAxisScaleTypeSelected(String label, String value) */ public QueryChartDialog setAxisRangeType(String label, String value) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover if ("automatic".equalsIgnoreCase(value)) elementCache().scaleAutomaticRadio.check(); else if ("manual".equalsIgnoreCase(value)) elementCache().scaleManualRadio.check(); else throw new IllegalArgumentException("Invalid range value: " + value); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return this; } public boolean isAxisRangeTypeSelected(String label, String value) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover boolean selected = "automatic".equalsIgnoreCase(value) ? elementCache().scaleAutomaticRadio.isChecked() : "manual".equalsIgnoreCase(value) && elementCache().scaleManualRadio.isChecked(); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return selected; } public QueryChartDialog setAxisRange(String label, String min, String max) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover if (elementCache().scaleManualRadio.isChecked()) { elementCache().scaleRangeMinInput.set(min); elementCache().scaleRangeMaxInput.set(max); } - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return this; } public String getAxisRangeMin(String label) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover String min = elementCache().scaleRangeMinInput.get(); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return min; } public String getAxisRangeMax(String label) { - clickAxisFieldOptions(label); // open the popover + clickFieldOptions(label); // open the popover String max = elementCache().scaleRangeMaxInput.get(); - clickAxisFieldOptions(label); // close the popover + clickFieldOptions(label); // close the popover return max; } @@ -207,18 +206,57 @@ public List getYAxisSelectionOptions() return elementCache().reactSelectByLabel("Y Axis").getOptions(); } - /* - Y Axis Aggregate Method is an option for bar chart - */ public QueryChartDialog selectYAxisAggregateMethod(String option) { - elementCache().reactSelectByLabel("Y Axis Aggregate Method").select(option); + clickFieldOptions("Y Axis"); // open the popover + getAggregateMethodSelect().select(option); + Locator.tagWithText("label", "Name *").findElement(this).click(); // close the popover return this; } + public List getYAxisAggregateMethodOptions() + { + clickFieldOptions("Y Axis"); // open the popover + List options = getAggregateMethodSelect().getOptions(); + Locator.tagWithText("label", "Name *").findElement(this).click(); // close the popover + return options; + } + public String getYAxisAggregateMethod() { - return elementCache().reactSelectByLabel("Y Axis Aggregate Method").getValue(); + clickFieldOptions("Y Axis"); // open the popover + String value = getAggregateMethodSelect().getValue(); + Locator.tagWithText("label", "Name *").findElement(this).click(); // close the popover + return value; + } + + private ReactSelect getAggregateMethodSelect() + { + // can't use elementCache() because the popover is outside the dialog + Locator loc = Locator.tag("div").withChild(Locator.tagContainingText("label", "Aggregate Method")); + return ReactSelect.finder(getDriver()).find(loc.waitForElement(getDriver(), 1500)); + } + + private RadioButton getErrorBarsRadio(String value) + { + // can't use elementCache() because the popover is outside the dialog + return RadioButton.RadioButton(Locator.radioButtonByNameAndValue("error-bar-method", value)).find(getDriver()); + } + + public QueryChartDialog selectYAxisErrorBar(String value) + { + clickFieldOptions("Y Axis"); // open the popover + getErrorBarsRadio(value).check(); + Locator.tagWithText("label", "Name *").findElement(this).click(); // close the popover + return this; + } + + public boolean isYAxisErrorBarOptionEnabled(String value) + { + clickFieldOptions("Y Axis"); // open the popover + boolean enabled = getErrorBarsRadio(value).isEnabled(); + Locator.tagWithText("label", "Name *").findElement(this).click(); // close the popover + return enabled; } /* @@ -404,10 +442,16 @@ public QueryChartPanel clickCreateChart() */ public QueryChartPanel clickSaveChart() { + return clickSaveChart(getName()); + } + public QueryChartPanel clickSaveChart(String previousChartName) + { + String name = getName(); + WebElement prevChart = _queryGrid.getChartPanel(previousChartName).getSvgChart(); WebDriverWrapper.waitFor(this::isSaveChartButtonEnabled, "the Save chart button did not become enabled", 2000); - String name = getName(); dismiss("Save Chart"); + getWrapper().shortWait().until(ExpectedConditions.stalenessOf(prevChart)); return _queryGrid.getChartPanel(name); } @@ -489,7 +533,7 @@ public String grayTextPreviewInstruction() } private final Locator previewBodyLoc = Locator.tagWithClass("div", "chart-builder-preview-body"); - private final Locator svgLoc = Locator.tagWithClass("div", "svg-chart__chart"); + private final Locator svgLoc = Locator.tagWithClass("div", "svg-chart__chart").childTag("svg"); public WebElement svg() { diff --git a/src/org/labkey/test/components/react/QueryChartPanel.java b/src/org/labkey/test/components/react/QueryChartPanel.java index 53c67f2005..9ba0135e58 100644 --- a/src/org/labkey/test/components/react/QueryChartPanel.java +++ b/src/org/labkey/test/components/react/QueryChartPanel.java @@ -52,7 +52,7 @@ public String getTitle() public WebElement getSvgChart() { - return Locator.byClass("svg-chart").waitForElement(this, WAIT_FOR_JAVASCRIPT); + return Locator.byClass("svg-chart__chart").childTag("svg").waitForElement(this, WAIT_FOR_JAVASCRIPT); } public QueryGrid clickClose() diff --git a/src/org/labkey/test/tests/visualization/BarPlotTest.java b/src/org/labkey/test/tests/visualization/BarPlotTest.java index aec1e17bdd..3dacb3153c 100644 --- a/src/org/labkey/test/tests/visualization/BarPlotTest.java +++ b/src/org/labkey/test/tests/visualization/BarPlotTest.java @@ -60,12 +60,17 @@ public class BarPlotTest extends GenericChartsTest private final String ALT_GROUPED_BAR_PLOT_SVG_TEXT = "0\nNormal\nNot Done\n0\n2\n4\n6\n8\n10\n12\n14\n16\n18\n20\n22\nAPX-1: Abbreviated Physical Exam\nNew Label\n0\nNegative"; private final String SECOND_BAR_PLOT_SVG_TEXT = "0\nNegative\n0\n200\n400\n600\n800\n1000\n1200\n1400\n1600\n1800\n2000\n2200\n2400\n" + TRICKY_CHART_TITLE + "\n" + PREG_TEST_RESULTS + "\nSum of " + BP_DIASTOLIC; private final String THIRD_BAR_PLOT_SVG_TEXT = "0\nNegative\n-50\n-49\n-48\n-47\n-46\n-45\n-44\n-43\n-42\n-41\n-40\n"+ TRICKY_CHART_TITLE + "\n" + PREG_TEST_RESULTS + "\nSum of " + BP_DIASTOLIC; - private final String FOURTH_BAR_PLOT_SVG_TEXT = "0\nNegative\n200\n400\n600\n800\n1000\n1200\n1400\n1600\n1800\n2000\n2200\n2400\n2600\n2800\n3000\n"+ TRICKY_CHART_TITLE + "\n" + PREG_TEST_RESULTS + "\nSum of " + BP_DIASTOLIC; + private final String FOURTH_BAR_PLOT_SVG_TEXT = "0\nNegative\n0\n500\n1000\n1500\n2000\n2500\n3000\n"+ TRICKY_CHART_TITLE + "\n" + PREG_TEST_RESULTS + "\nSum of " + BP_DIASTOLIC; private final String SUM_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n2e+7\n4e+7\n6e+7\n8e+7\n1e+8\n1.2e+8\n1.4e+8\n1.6e+8\n1.8e+8\n2e+8\n2.2e+8\n2.4e+8\nTypes\nStudy: Cohort\nSum of Double"; private final String COUNT_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n2\n4\n6\n8\n10\n12\n14\n16\n18\n20\nTypes\nStudy: Cohort\nCount (non-blank) of Double"; private final String MIN_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n-1\n-0.9\n-0.8\n-0.7\n-0.6\n-0.5\n-0.4\n-0.3\n-0.2\n-0.1\n0\nTypes\nStudy: Cohort\nMin of Double"; private final String MAX_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n1e+7\n2e+7\n3e+7\n4e+7\n5e+7\n6e+7\n7e+7\n8e+7\n9e+7\n1e+8\n1.1e+8\n1.2e+8\nTypes\nStudy: Cohort\nMax of Double"; private final String MEAN_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n2e+6\n4e+6\n6e+6\n8e+6\n1e+7\n1.2e+7\n1.4e+7\n1.6e+7\n1.8e+7\n2e+7\nTypes\nStudy: Cohort\nMean of Double"; + private final String MEAN_SD_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n1e+7\n2e+7\n3e+7\n4e+7\n5e+7\n6e+7\nTypes\nStudy: Cohort\nMean of Double"; + private final String MEAN_SEM_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n5e+6\n1e+7\n1.5e+7\n2e+7\n2.5e+7\n3e+7\nTypes\nStudy: Cohort\nMean of Double"; + private final String MEAN_NONE_GROUPED_BAR_PLOT_SVG_TEXT = "Enroll/Vacc#1\nScreening\n0\n2e+6\n4e+6\n6e+6\n8e+6\n1e+7\n1.2e+7\n1.4e+7\n1.6e+7\n1.8e+7\n2e+7\n2.2e+7\n2.4e+7\nTypes\nVisit\nMean of Double\nGroup 1\nGroup 2"; + private final String MEAN_SD_GROUPED_BAR_PLOT_SVG_TEXT = "Enroll/Vacc#1\nScreening\n0\n1e+7\n2e+7\n3e+7\n4e+7\n5e+7\n6e+7\n7e+7\nTypes\nVisit\nMean of Double\nGroup 1\nGroup 2"; + private final String MEAN_SEM_GROUPED_BAR_PLOT_SVG_TEXT = "Enroll/Vacc#1\nScreening\n0\n5e+6\n1e+7\n1.5e+7\n2e+7\n2.5e+7\n3e+7\n3.5e+7\n4e+7\n4.5e+7\nTypes\nVisit\nMean of Double\nGroup 1\nGroup 2"; private final String MEDIAN_BAR_PLOT_SVG_TEXT = "Group 1\nGroup 2\n0\n0.05\n0.1\n0.15\n0.2\n0.25\n0.3\n0.35\n0.4\n0.45\n0.5\n0.55\n0.6\nTypes\nStudy: Cohort\nMedian of Double"; @Override @@ -462,14 +467,34 @@ private void doYAxisAggregateMethodTest() setBarAggregateMethodAndVerify("Mean", MEAN_BAR_PLOT_SVG_TEXT); setBarAggregateMethodAndVerify("Median", MEDIAN_BAR_PLOT_SVG_TEXT); + log("Change and verify error bar types for Mean aggregate method"); + setBarAggregateMethodAndVerify("Mean", null); + setBarErrorBarMethodAndVerify("Standard Deviation", MEAN_SD_BAR_PLOT_SVG_TEXT); + setBarErrorBarMethodAndVerify("Standard Error of the Mean", MEAN_SEM_BAR_PLOT_SVG_TEXT); + + log("Add a Group By variable and verify aggregate and error bar case."); + chartWizard.clickChartTypeButton().setXSubCategory("Visit").clickApply(); + setBarErrorBarMethodAndVerify("None", MEAN_NONE_GROUPED_BAR_PLOT_SVG_TEXT); + setBarErrorBarMethodAndVerify("Standard Deviation", MEAN_SD_GROUPED_BAR_PLOT_SVG_TEXT); + setBarErrorBarMethodAndVerify("Standard Error of the Mean", MEAN_SEM_GROUPED_BAR_PLOT_SVG_TEXT); + savePlot(BAR_PLOT_SAVE_NAME_4, null); } + private void setBarErrorBarMethodAndVerify(String method, String svgTxt) + { + LookAndFeelBarPlot lookAndFeelDialog = clickChartLayoutButton(); + lookAndFeelDialog.setYAxisErrorBarsMethod(method); + lookAndFeelDialog.clickApply(); + assertSVG(svgTxt); + } + private void setBarAggregateMethodAndVerify(String method, String svgTxt) { clickButton("Chart Type", 0); ChartTypeDialog chartTypeDialog = new ChartTypeDialog(getDriver()); chartTypeDialog.setYAxisAggregateMethod(method).clickApply(); - assertSVG(svgTxt); + if (svgTxt != null) + assertSVG(svgTxt); } } diff --git a/src/org/labkey/test/tests/visualization/LinePlotTest.java b/src/org/labkey/test/tests/visualization/LinePlotTest.java index 7c0b1dd198..89b3503dfe 100644 --- a/src/org/labkey/test/tests/visualization/LinePlotTest.java +++ b/src/org/labkey/test/tests/visualization/LinePlotTest.java @@ -77,6 +77,7 @@ protected void testPlots() private static final String LINE_PLOT_MV_2 = "60\n70\n80\n90\n100\n110\n32.0\n33.0\n34.0\n35.0\n36.0\n37.0\n38.0\n39.0\n40.0\nTestTitle\nTestXAxis\nTestYAxis"; private static final String LINE_PLOT_MULTI_YAXIS_1 = "60\n80\n100\n120\n140\n160\n180\n200\n10\n20\n30\n40\n50\n60\n70\n80\n90\n100\n110APX-1: Abbreviated Physical Exam\n1. Weight\n4. Pulse, 5. Respirations\n4. Pulse\n5. Respirations"; private static final String LINE_PLOT_MULTI_YAXIS_2 = "60\n80\n100\n120\n140\n160\n180\n200\n60\n65\n70\n75\n80\n85\n90\n95\n100\n105\n110\n115\n6\n8\n10\n12\n14\n16\n18APX-1: Abbreviated Physical Exam\n1. Weight\n4. Pulse\n5. Respirations\n4. Pulse\n5. Respirations"; + private static final String LINE_PLOT_MULTI_YAXIS_3 = "60\n80\n100\n120\n140\n160\n180\n200\n60\n65\n70\n75\n80\n85\n90\n95\n100\n105\n110\n115APX-1: Abbreviated Physical Exam\n1. Weight\n4. Pulse"; private static final String LINE_PLOT_NAME_MV = "ManageViewsLinePlot"; private static final String LINE_PLOT_DESC_MV = "This line plot was created through the manage views UI"; private static final String LINE_PLOT_MULTI_YAXIS_NAME = "LinePlotMultiYAxis"; @@ -199,6 +200,49 @@ private void doMultiYAxisLinePlotTest() waitForElement(Locator.linkWithText(LINE_PLOT_MULTI_YAXIS_NAME)); clickAndWait(Locator.linkWithText(LINE_PLOT_MULTI_YAXIS_NAME), WAIT_FOR_PAGE); export(EXPORTED_SCRIPT_CHECK_TYPE, MEASURE_1_WEIGHT, MEASURE_5_RESPIRATIONS); + + log("verify aggregate and error bar options are hidden for multi chart"); + chartWizard = openSavedPlotInEditMode(LINE_PLOT_MULTI_YAXIS_NAME); + chartLayoutDialog = clickChartLayoutButton(); + chartLayoutDialog.clickYAxisTabRight(); + checker().verifyFalse("Aggregate method should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisAggregateMethodVisible()); + checker().verifyFalse("Error Bars should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisErrorBarsMethodVisible()); + chartLayoutDialog.clickYAxisTabLeft(); + checker().verifyFalse("Aggregate method should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisAggregateMethodVisible()); + checker().verifyFalse("Error Bars should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisErrorBarsMethodVisible()); + chartLayoutDialog.clickCancel(); + log("verify aggregate and error bar options are hidden for right chart"); + chartTypeDialog = chartWizard.clickChartTypeButton(); + chartWizard = chartTypeDialog + .removeYAxis() + .removeYAxis() + .setYAxis(MEASURE_4_PULSE) + .setYAxisSide(0,ChartTypeDialog.YAxisSide.Right) + .clickApply(); + chartLayoutDialog = clickChartLayoutButton(); + click(Locator.xpath("//div[contains(@class, 'item')][text()='Y-Axis']").index(1)); // click the right y-axis tab + checker().verifyFalse("Aggregate method should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisAggregateMethodVisible()); + checker().verifyFalse("Error Bars should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisErrorBarsMethodVisible()); + chartLayoutDialog.clickCancel(); + log("change back to y-axis left and verify aggregate and error bar options"); + chartTypeDialog = chartWizard.clickChartTypeButton(); + chartWizard = chartTypeDialog.setYAxisSide(0, ChartTypeDialog.YAxisSide.Left).clickApply(); + assertSVG(LINE_PLOT_MULTI_YAXIS_3); + checker().verifyEquals("Point count in line plot not as expected", 33, Locator.css("svg g a path").findElements(getDriver()).size()); + chartLayoutDialog = clickChartLayoutButton(); + chartLayoutDialog.clickYAxisTab(); + checker().verifyTrue("Aggregate method should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisAggregateMethodVisible()); + checker().verifyTrue("Error Bars should not be visible for multiple y axis line plot", chartLayoutDialog.isYAxisErrorBarsMethodVisible()); + chartLayoutDialog.setYAxisAggregateMethod("Mean").clickApply(); + assertSVG(LINE_PLOT_MULTI_YAXIS_3); + checker().verifyEquals("Point count in line plot not as expected", 19, Locator.css("svg g a path").findElements(getDriver()).size()); + checker().verifyEquals("Point count in line plot not as expected", 0, Locator.css("svg g.error-bar").findElements(getDriver()).size()); + chartLayoutDialog = clickChartLayoutButton(); + chartLayoutDialog.clickYAxisTab(); + chartLayoutDialog.setYAxisErrorBarsMethod("Standard Deviation").clickApply(); + assertSVG(LINE_PLOT_MULTI_YAXIS_3); + checker().verifyEquals("Point count in line plot not as expected", 19, Locator.css("svg g a path").findElements(getDriver()).size()); + checker().verifyEquals("Point count in line plot not as expected", 19, Locator.css("svg g.error-bar").findElements(getDriver()).size()); } private static final String LINE_PLOT_DR_1 = "60\n65\n70\n75\n80\n85\n90\n50\n55\n60\n65\n70\n75\n80\n85\n90\n95\n100\n105\n110\nAPX-1: Abbreviated Physical Exam\n4. Pulse\n1. Weight";