mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-21 22:58:43 +00:00
Compare commits
4 Commits
wip/hotfix
...
snapshot-i
Author | SHA1 | Date | |
---|---|---|---|
cdb1a456ef | |||
5769f95419 | |||
1941ec6b41 | |||
f3ae5ee10b |
@ -2,12 +2,12 @@ version: 2.1
|
||||
|
||||
orbs:
|
||||
node: circleci/node@5.1.0
|
||||
browser-tools: circleci/browser-tools@1.4.7
|
||||
browser-tools: circleci/browser-tools@1.4.6
|
||||
|
||||
executors:
|
||||
java17:
|
||||
docker:
|
||||
- image: 'cimg/openjdk:17.0.9'
|
||||
- image: 'cimg/openjdk:17.0'
|
||||
|
||||
commands:
|
||||
install_java17:
|
||||
|
@ -354,7 +354,8 @@ const CommandMenu = ({metaData}: Props) =>
|
||||
<Grid container columnSpacing={5} rowSpacing={1}>
|
||||
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>n</span>Create a New Record</Grid>
|
||||
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>r</span>Refresh the Query</Grid>
|
||||
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>f</span>Open the Filter Builder (Advanced mode only)</Grid>
|
||||
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>c</span>Open the Columns Panel</Grid>
|
||||
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>f</span>Open the Filter Panel</Grid>
|
||||
</Grid>
|
||||
|
||||
<Typography variant="h6" pt={3}>Record View</Typography>
|
||||
|
@ -98,7 +98,7 @@ export default function ExportMenuItem(props: QExportMenuItemProps)
|
||||
</head>
|
||||
<body>
|
||||
Generating file <u>${filename}</u>${totalRecords ? " with " + totalRecords.toLocaleString() + " record" + (totalRecords == 1 ? "" : "s") : ""}...
|
||||
<form id="exportForm" method="post" action="${url}" enctype="multipart/form-data">
|
||||
<form id="exportForm" method="post" action="${url}" >
|
||||
<input type="hidden" name="fields" value="${visibleFields.join(",")}">
|
||||
<input type="hidden" name="filter" id="filter">
|
||||
</form>
|
||||
|
@ -398,7 +398,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
|
||||
criteria.values = [];
|
||||
}
|
||||
|
||||
if(newValue.valueMode && !newValue.implicitValues)
|
||||
if(newValue.valueMode)
|
||||
{
|
||||
const requiredValueCount = getValueModeRequiredCount(newValue.valueMode);
|
||||
if(requiredValueCount != null && criteria.values.length > requiredValueCount)
|
||||
|
@ -30,7 +30,7 @@ import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import React, {SyntheticEvent, useContext, useReducer, useState} from "react";
|
||||
import React, {SyntheticEvent, useContext, useState} from "react";
|
||||
import QContext from "QContext";
|
||||
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
|
||||
import {getDefaultCriteriaValue, getOperatorOptions, getValueModeRequiredCount, OperatorOption, validateCriteria} from "qqq/components/query/FilterCriteriaRow";
|
||||
@ -148,10 +148,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [isMouseOver, setIsMouseOver] = useState(false);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// copy the criteriaParam to a new object in here - so changes won't apply until user closes the menu //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const [criteria, setCriteria] = useState(criteriaParamIsCriteria(criteriaParam) ? Object.assign({}, criteriaParam) as QFilterCriteriaWithId : null);
|
||||
const [criteria, setCriteria] = useState(criteriaParamIsCriteria(criteriaParam) ? criteriaParam as QFilterCriteriaWithId : null);
|
||||
const [id, setId] = useState(criteriaParamIsCriteria(criteriaParam) ? (criteriaParam as QFilterCriteriaWithId).id : ++seedId);
|
||||
|
||||
const [operatorSelectedValue, setOperatorSelectedValue] = useState(getOperatorSelectedValue(operatorOptions, criteria, defaultOperator));
|
||||
@ -161,11 +158,6 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
|
||||
const {accentColor} = useContext(QContext);
|
||||
|
||||
//////////////////////
|
||||
// ole' faithful... //
|
||||
//////////////////////
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -190,30 +182,15 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (criteriaParamIsCriteria(criteriaParam) && JSON.stringify(criteriaParam) !== JSON.stringify(criteria))
|
||||
{
|
||||
if(isOpen)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// this was firing too-often for case where: there was a criteria originally //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
console.log("Not handling outside change (A), because dropdown is-open");
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// copy the criteriaParam to a new object in here - so changes won't apply until user closes the menu //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const newCriteria = Object.assign({}, criteriaParam) as QFilterCriteriaWithId;
|
||||
setCriteria(newCriteria);
|
||||
const operatorOption = operatorOptions.filter(o => o.value == newCriteria.operator)[0];
|
||||
setOperatorSelectedValue(operatorOption);
|
||||
setOperatorInputValue(operatorOption.label);
|
||||
}
|
||||
const newCriteria = criteriaParam as QFilterCriteriaWithId;
|
||||
setCriteria(newCriteria);
|
||||
const operatorOption = operatorOptions.filter(o => o.value == newCriteria.operator)[0];
|
||||
setOperatorSelectedValue(operatorOption);
|
||||
setOperatorInputValue(operatorOption.label);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Test if we need to construct a new criteria object
|
||||
** This is (at least for some cases) for when the criteria gets changed
|
||||
** from outside of this component - e.g., a reset on the query screen
|
||||
*******************************************************************************/
|
||||
const criteriaNeedsReset = (): boolean =>
|
||||
{
|
||||
@ -222,16 +199,6 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
const defaultOperatorOption = operatorOptions.filter(o => o.value == defaultOperator)[0];
|
||||
if(criteria.operator !== defaultOperatorOption?.value || JSON.stringify(criteria.values) !== JSON.stringify(getDefaultCriteriaValue()))
|
||||
{
|
||||
if(isOpen)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// this was firing too-often for case where: there was no criteria originally, //
|
||||
// so, by adding this is-open check, we eliminated those. //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
console.log("Not handling outside change (B), because dropdown is-open");
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
@ -240,7 +207,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Construct a new criteria object - resetting the values tied to the operator
|
||||
** Construct a new criteria object - resetting the values tied to the oprator
|
||||
** autocomplete at the same time.
|
||||
*******************************************************************************/
|
||||
const makeNewCriteria = (): QFilterCriteria =>
|
||||
@ -274,11 +241,6 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
*******************************************************************************/
|
||||
const closeMenu = () =>
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// when closing the menu, that's when we'll update the criteria from the caller //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
updateCriteria(criteria, false, false);
|
||||
|
||||
setIsOpen(false);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
@ -309,7 +271,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
criteria.values = [];
|
||||
}
|
||||
|
||||
if(newValue.valueMode && !newValue.implicitValues)
|
||||
if(newValue.valueMode)
|
||||
{
|
||||
const requiredValueCount = getValueModeRequiredCount(newValue.valueMode);
|
||||
if(requiredValueCount != null && criteria.values.length > requiredValueCount)
|
||||
@ -324,8 +286,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
setOperatorInputValue("");
|
||||
}
|
||||
|
||||
setCriteria(criteria);
|
||||
forceUpdate();
|
||||
updateCriteria(criteria, false, false);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
@ -359,8 +320,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
criteria.values[valueIndex] = value;
|
||||
}
|
||||
|
||||
setCriteria(criteria);
|
||||
forceUpdate();
|
||||
updateCriteria(criteria, true, false);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
@ -440,18 +400,16 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
buttonAdditionalStyles.color = "white !important";
|
||||
buttonClassName = "filterActive";
|
||||
|
||||
let valuesString = FilterUtils.getValuesString(fieldMetaData, criteria, 1, "+N");
|
||||
|
||||
///////////////////////////////////////////
|
||||
// don't show the Equals or In operators //
|
||||
///////////////////////////////////////////
|
||||
let operatorString = (<>{operatorSelectedValue.label} </>);
|
||||
if(operatorSelectedValue.value == QCriteriaOperator.EQUALS || operatorSelectedValue.value == QCriteriaOperator.IN)
|
||||
let valuesString = FilterUtils.getValuesString(fieldMetaData, criteria);
|
||||
if(fieldMetaData.type == QFieldType.BOOLEAN)
|
||||
{
|
||||
operatorString = (<></>)
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// for booleans, in here, the operator-label is "equals yes" or "equals no", so we don't want the values string //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
valuesString = "";
|
||||
}
|
||||
|
||||
buttonContent = (<><span style={{fontWeight: 700}}>{buttonContent}:</span> <span style={{fontWeight: 400}}>{operatorString}{valuesString}</span></>);
|
||||
buttonContent = (<><span style={{fontWeight: 700}}>{buttonContent}:</span> <span style={{fontWeight: 400}}>{operatorSelectedValue.label} {valuesString}</span></>);
|
||||
}
|
||||
|
||||
const mouseEvents =
|
||||
@ -520,12 +478,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
<Box display="inline-block" width={widthAndMaxWidth} maxWidth={widthAndMaxWidth} className="operatorColumn">
|
||||
<Autocomplete
|
||||
id={"criteriaOperator"}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ok, so, by default, if you type an 'o' as the first letter in the FilterCriteriaRowValues box, //
|
||||
// something is causing THIS element to become selected, if the first letter in its label is 'O'. //
|
||||
// ... work around is to put invisible ‌ entity as first character in label instead... //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
renderInput={(params) => (<TextField {...params} label={<>‌Operator</>} variant="standard" autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
||||
renderInput={(params) => (<TextField {...params} label={"Operator"} variant="standard" autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
||||
options={operatorOptions}
|
||||
value={operatorSelectedValue as any}
|
||||
inputValue={operatorInputValue}
|
||||
|
@ -39,79 +39,65 @@ ChartJS.register(
|
||||
Legend
|
||||
);
|
||||
|
||||
export const makeOptions = (data: DefaultChartData) =>
|
||||
{
|
||||
return({
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
elements: {
|
||||
bar: {
|
||||
borderRadius: 4
|
||||
}
|
||||
},
|
||||
onHover: function (event: any, elements: any[], chart: any)
|
||||
{
|
||||
if(event.type == "mousemove" && elements.length > 0 && data.urls && data.urls.length > elements[0].index && data.urls[elements[0].index])
|
||||
{
|
||||
chart.canvas.style.cursor = "pointer";
|
||||
}
|
||||
else
|
||||
{
|
||||
chart.canvas.style.cursor = "default";
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
// todo - some configs around this
|
||||
callbacks: {
|
||||
title: function(context: any)
|
||||
export const options = {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
elements: {
|
||||
bar: {
|
||||
borderRadius: 4
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
// todo - some configs around this
|
||||
callbacks: {
|
||||
title: function(context: any)
|
||||
{
|
||||
return ("");
|
||||
},
|
||||
label: function(context: any)
|
||||
{
|
||||
if(context.dataset.label.startsWith(context.label))
|
||||
{
|
||||
return `${context.label}: ${context.formattedValue}`;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ("");
|
||||
},
|
||||
label: function(context: any)
|
||||
{
|
||||
if(context.dataset.label.startsWith(context.label))
|
||||
{
|
||||
return `${context.label}: ${context.formattedValue}`;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ("");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
pointStyle: "circle",
|
||||
boxHeight: 6,
|
||||
boxWidth: 6,
|
||||
padding: 12,
|
||||
font: {
|
||||
size: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: {display: false},
|
||||
ticks: {autoSkip: false, maxRotation: 90}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
position: "right",
|
||||
ticks: {precision: 0}
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
pointStyle: "circle",
|
||||
boxHeight: 6,
|
||||
boxWidth: 6,
|
||||
padding: 12,
|
||||
font: {
|
||||
size: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: {display: false},
|
||||
ticks: {autoSkip: false, maxRotation: 90}
|
||||
},
|
||||
});
|
||||
}
|
||||
y: {
|
||||
stacked: true,
|
||||
position: "right",
|
||||
ticks: {precision: 0}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -165,7 +151,7 @@ function StackedBarChart({data, chartSubheaderData}: Props): JSX.Element
|
||||
<Box>
|
||||
{chartSubheaderData && (<ChartSubheaderWithData chartSubheaderData={chartSubheaderData} />)}
|
||||
<Box width="100%" height="300px">
|
||||
<Bar data={data} options={makeOptions(data)} getElementsAtEvent={handleClick} />
|
||||
<Bar data={data} options={options} getElementsAtEvent={handleClick} />
|
||||
</Box>
|
||||
</Box>
|
||||
) : <Skeleton sx={{marginLeft: "20px", marginRight: "20px", height: "200px"}} />;
|
||||
|
@ -70,7 +70,7 @@ function PieChart({description, chartData, chartSubheaderData}: Props): JSX.Elem
|
||||
chartData.dataset.backgroundColors = chartColors;
|
||||
}
|
||||
}
|
||||
const {data, options} = configs(chartData?.labels || [], chartData?.dataset || {}, chartData?.dataset?.urls);
|
||||
const {data, options} = configs(chartData?.labels || [], chartData?.dataset || {});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ import colors from "qqq/assets/theme/base/colors";
|
||||
|
||||
const {gradients, dark} = colors;
|
||||
|
||||
function configs(labels: any, datasets: any, urls: string[] | undefined)
|
||||
function configs(labels: any, datasets: any)
|
||||
{
|
||||
const backgroundColors = [];
|
||||
|
||||
@ -66,17 +66,6 @@ function configs(labels: any, datasets: any, urls: string[] | undefined)
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
onHover: function (event: any, elements: any[], chart: any)
|
||||
{
|
||||
if(event.type == "mousemove" && elements.length > 0 && urls && urls.length > elements[0].index && urls[elements[0].index])
|
||||
{
|
||||
chart.canvas.style.cursor = "pointer";
|
||||
}
|
||||
else
|
||||
{
|
||||
chart.canvas.style.cursor = "default";
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
|
||||
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||
@ -147,11 +146,6 @@ function ColumnStats({tableMetaData, fieldMetaData, fieldTableName, filter}: Pro
|
||||
const rows = DataGridUtils.makeRows(valueCounts, fakeTableMetaData);
|
||||
const columns = DataGridUtils.setupGridColumns(fakeTableMetaData, null, null, "bySection");
|
||||
|
||||
if(fieldMetaData.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
columns[0].headerName = fieldMetaData.label + " (grouped by hour)"
|
||||
}
|
||||
|
||||
columns.forEach((c) =>
|
||||
{
|
||||
c.filterable = false;
|
||||
|
@ -168,7 +168,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
// define some default values (e.g., to be used if nothing in local storage or no active view) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
let defaultSort = [] as GridSortItem[];
|
||||
let defaultRowsPerPage = 50;
|
||||
let defaultRowsPerPage = 10;
|
||||
let defaultDensity = "standard" as GridDensity;
|
||||
let defaultTableVariant: QTableVariant = null;
|
||||
let defaultMode = "basic";
|
||||
@ -610,14 +610,11 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
e.preventDefault()
|
||||
updateTable("'r' keyboard event");
|
||||
}
|
||||
/*
|
||||
// disable until we add a ... ref down to let us programmatically open Columns button
|
||||
else if (! e.metaKey && e.key === "c")
|
||||
{
|
||||
e.preventDefault()
|
||||
gridApiRef.current.showPreferences(GridPreferencePanelsValue.columns)
|
||||
}
|
||||
*/
|
||||
else if (! e.metaKey && e.key === "f")
|
||||
{
|
||||
e.preventDefault()
|
||||
@ -1383,18 +1380,12 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
if (selectFullFilterState === "filter")
|
||||
{
|
||||
const filterForBackend = prepQueryFilterForBackend(queryFilter);
|
||||
filterForBackend.skip = 0;
|
||||
filterForBackend.limit = null;
|
||||
return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(filterForBackend))}`;
|
||||
return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(queryFilter))}`;
|
||||
}
|
||||
|
||||
if (selectFullFilterState === "filterSubset")
|
||||
{
|
||||
const filterForBackend = prepQueryFilterForBackend(queryFilter);
|
||||
filterForBackend.skip = 0;
|
||||
filterForBackend.limit = selectionSubsetSize;
|
||||
return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(filterForBackend))}`;
|
||||
return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(queryFilter))}`;
|
||||
}
|
||||
|
||||
if (selectedIds.length > 0)
|
||||
@ -1414,17 +1405,11 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
if (selectFullFilterState === "filter")
|
||||
{
|
||||
const filterForBackend = prepQueryFilterForBackend(queryFilter);
|
||||
filterForBackend.skip = 0;
|
||||
filterForBackend.limit = null;
|
||||
setRecordIdsForProcess(filterForBackend);
|
||||
setRecordIdsForProcess(queryFilter);
|
||||
}
|
||||
else if (selectFullFilterState === "filterSubset")
|
||||
{
|
||||
const filterForBackend = prepQueryFilterForBackend(queryFilter);
|
||||
filterForBackend.skip = 0;
|
||||
filterForBackend.limit = selectionSubsetSize;
|
||||
setRecordIdsForProcess(filterForBackend);
|
||||
setRecordIdsForProcess(new QQueryFilter(queryFilter.criteria, queryFilter.orderBys, queryFilter.booleanOperator, 0, selectionSubsetSize));
|
||||
}
|
||||
else if (selectedIds.length > 0)
|
||||
{
|
||||
@ -2112,32 +2097,20 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
if(selectedIndex == 0)
|
||||
{
|
||||
///////////////
|
||||
// this page //
|
||||
///////////////
|
||||
programmaticallySelectSomeOrAllRows();
|
||||
setSelectFullFilterState("checked")
|
||||
}
|
||||
else if(selectedIndex == 1)
|
||||
{
|
||||
///////////////////////
|
||||
// full query result //
|
||||
///////////////////////
|
||||
programmaticallySelectSomeOrAllRows();
|
||||
setSelectFullFilterState("filter")
|
||||
}
|
||||
else if(selectedIndex == 2)
|
||||
{
|
||||
////////////////////////////
|
||||
// subset of query result //
|
||||
////////////////////////////
|
||||
setSelectionSubsetSizePromptOpen(true);
|
||||
}
|
||||
else if(selectedIndex == 3)
|
||||
{
|
||||
/////////////////////
|
||||
// clear selection //
|
||||
/////////////////////
|
||||
setSelectFullFilterState("n/a")
|
||||
setRowSelectionModel([]);
|
||||
setSelectedIds([]);
|
||||
|
@ -64,6 +64,7 @@ class FilterUtils
|
||||
let rs = [];
|
||||
for (let i = 0; i < param.length; i++)
|
||||
{
|
||||
console.log(param[i]);
|
||||
if (param[i] && param[i].id && param[i].label)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////
|
||||
@ -318,7 +319,7 @@ class FilterUtils
|
||||
** get the values associated with a criteria as a string, e.g., for showing
|
||||
** in a tooltip.
|
||||
*******************************************************************************/
|
||||
public static getValuesString(fieldMetaData: QFieldMetaData, criteria: QFilterCriteria, maxValuesToShow: number = 3, andMoreFormat: "andNOther" | "+N" = "andNOther"): string
|
||||
public static getValuesString(fieldMetaData: QFieldMetaData, criteria: QFilterCriteria, maxValuesToShow: number = 3): string
|
||||
{
|
||||
let valuesString = "";
|
||||
|
||||
@ -339,10 +340,6 @@ class FilterUtils
|
||||
{
|
||||
maxLoops = maxValuesToShow;
|
||||
}
|
||||
else if(maxValuesToShow == 1 && criteria.values.length > 1)
|
||||
{
|
||||
maxLoops = 1;
|
||||
}
|
||||
|
||||
for (let i = 0; i < maxLoops; i++)
|
||||
{
|
||||
@ -360,12 +357,7 @@ class FilterUtils
|
||||
else if (value.type == "ThisOrLastPeriod")
|
||||
{
|
||||
const expression = new ThisOrLastPeriodExpression(value);
|
||||
let startOfPrefix = "";
|
||||
if(fieldMetaData.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS")
|
||||
{
|
||||
startOfPrefix = "start of ";
|
||||
}
|
||||
labels.push(`${startOfPrefix}${expression.toString()}`);
|
||||
labels.push(expression.toString());
|
||||
}
|
||||
else if(fieldMetaData.type == QFieldType.BOOLEAN)
|
||||
{
|
||||
@ -391,16 +383,7 @@ class FilterUtils
|
||||
|
||||
if (maxLoops < criteria.values.length)
|
||||
{
|
||||
const n = criteria.values.length - maxLoops;
|
||||
switch (andMoreFormat)
|
||||
{
|
||||
case "andNOther":
|
||||
labels.push(` and ${n.toLocaleString()} other value${n == 1 ? "" : "s"}.`);
|
||||
break;
|
||||
case "+N":
|
||||
labels[labels.length-1] += ` +${n.toLocaleString()}`;
|
||||
break;
|
||||
}
|
||||
labels.push(" and " + (criteria.values.length - maxLoops) + " other values.");
|
||||
}
|
||||
|
||||
valuesString = (labels.join(", "));
|
||||
|
@ -93,11 +93,6 @@ class TableUtils
|
||||
*******************************************************************************/
|
||||
public static getFieldAndTable(tableMetaData: QTableMetaData, fieldName: string): [QFieldMetaData, QTableMetaData]
|
||||
{
|
||||
if(fieldName == null || tableMetaData == null)
|
||||
{
|
||||
return ([null, null]);
|
||||
}
|
||||
|
||||
if (fieldName.indexOf(".") > -1)
|
||||
{
|
||||
const nameParts = fieldName.split(".", 2);
|
||||
@ -115,7 +110,7 @@ class TableUtils
|
||||
return ([tableMetaData.fields.get(fieldName), tableMetaData]);
|
||||
}
|
||||
|
||||
return ([null, null]);
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,7 +29,6 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -505,33 +504,7 @@ public class QSeleniumLib
|
||||
*******************************************************************************/
|
||||
public WebElement waitForSelectorContaining(String cssSelector, String textContains)
|
||||
{
|
||||
return (waitForSelectorMatchingPredicate(cssSelector, "containing text [" + textContains + "]", (WebElement element) ->
|
||||
{
|
||||
return (element.getText() != null && element.getText().toLowerCase().contains(textContains.toLowerCase()));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public WebElement waitForSelectorContainingTextMatchingRegex(String cssSelector, String regExp)
|
||||
{
|
||||
return (waitForSelectorMatchingPredicate(cssSelector, "matching regexp [" + regExp + "]", (WebElement element) ->
|
||||
{
|
||||
return (element.getText() != null && element.getText().matches(regExp));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private WebElement waitForSelectorMatchingPredicate(String cssSelector, String description, Function<WebElement, Boolean> predicate)
|
||||
{
|
||||
LOG.debug("Waiting for element matching selector [" + cssSelector + "] " + description);
|
||||
LOG.debug("Waiting for element matching selector [" + cssSelector + "] containing text [" + textContains + "].");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
do
|
||||
@ -541,9 +514,9 @@ public class QSeleniumLib
|
||||
{
|
||||
try
|
||||
{
|
||||
if(predicate.apply(element))
|
||||
if(element.getText() != null && element.getText().toLowerCase().contains(textContains.toLowerCase()))
|
||||
{
|
||||
LOG.debug("Found element matching selector [" + cssSelector + "] " + description);
|
||||
LOG.debug("Found element matching selector [" + cssSelector + "] containing text [" + textContains + "].");
|
||||
Actions actions = new Actions(driver);
|
||||
actions.moveToElement(element);
|
||||
conditionallyAutoHighlight(element);
|
||||
@ -564,7 +537,7 @@ public class QSeleniumLib
|
||||
}
|
||||
while(start + (1000 * WAIT_SECONDS) > System.currentTimeMillis());
|
||||
|
||||
fail("Failed to find element matching selector [" + cssSelector + "] " + description + " after [" + WAIT_SECONDS + "] seconds.");
|
||||
fail("Failed to find element matching selector [" + cssSelector + "] containing text [" + textContains + "] after [" + WAIT_SECONDS + "] seconds.");
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,6 @@
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.selenium.lib;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.WebElement;
|
||||
@ -106,7 +103,7 @@ public class QueryScreenLib
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void clickFilterBuilderButton()
|
||||
public void clickFilterButton()
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContaining("BUTTON", "FILTER BUILDER").click();
|
||||
}
|
||||
@ -184,11 +181,10 @@ public class QueryScreenLib
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addAdvancedQueryFilterInput(int index, String fieldLabel, String operator, String value, String booleanOperator)
|
||||
public void addAdvancedQueryFilterInput(QSeleniumLib qSeleniumLib, int index, String fieldLabel, String operator, String value, String booleanOperator)
|
||||
{
|
||||
if(index > 0)
|
||||
{
|
||||
@ -225,91 +221,10 @@ public class QueryScreenLib
|
||||
operatorInput.sendKeys("\n");
|
||||
qSeleniumLib.waitForMillis(100);
|
||||
|
||||
if(StringUtils.hasContent(value))
|
||||
{
|
||||
WebElement valueInput = subFormForField.findElement(By.cssSelector(".filterValuesColumn INPUT"));
|
||||
valueInput.click();
|
||||
valueInput.sendKeys(value);
|
||||
qSeleniumLib.waitForMillis(100);
|
||||
}
|
||||
WebElement valueInput = subFormForField.findElement(By.cssSelector(".filterValuesColumn INPUT"));
|
||||
valueInput.click();
|
||||
valueInput.sendKeys(value);
|
||||
qSeleniumLib.waitForMillis(100);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addBasicFilter(String fieldLabel)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContaining("BUTTON", "Add Filter").click();
|
||||
qSeleniumLib.waitForSelectorContaining(".fieldListMenuBody-addQuickFilter LI", fieldLabel).click();
|
||||
qSeleniumLib.clickBackdrop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBasicFilter(String fieldLabel, String operatorLabel, String value)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContaining("BUTTON", fieldLabel).click();
|
||||
qSeleniumLib.waitForMillis(250);
|
||||
qSeleniumLib.waitForSelector("#criteriaOperator").click();
|
||||
qSeleniumLib.waitForSelectorContaining("LI", operatorLabel).click();
|
||||
|
||||
if(StringUtils.hasContent(value))
|
||||
{
|
||||
qSeleniumLib.waitForSelector(".filterValuesColumn INPUT").click();
|
||||
// todo - no, not in a listbox/LI here...
|
||||
qSeleniumLib.waitForSelectorContaining(".MuiAutocomplete-listbox LI", value).click();
|
||||
System.out.println(value);
|
||||
}
|
||||
|
||||
qSeleniumLib.clickBackdrop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBasicFilterPossibleValues(String fieldLabel, String operatorLabel, List<String> values)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContaining("BUTTON", fieldLabel).click();
|
||||
qSeleniumLib.waitForMillis(250);
|
||||
qSeleniumLib.waitForSelector("#criteriaOperator").click();
|
||||
qSeleniumLib.waitForSelectorContaining("LI", operatorLabel).click();
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(values))
|
||||
{
|
||||
qSeleniumLib.waitForSelector(".filterValuesColumn INPUT").click();
|
||||
for(String value : values)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContaining(".MuiAutocomplete-listbox LI", value).click();
|
||||
}
|
||||
}
|
||||
|
||||
qSeleniumLib.clickBackdrop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void waitForAdvancedQueryStringMatchingRegex(String regEx)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContainingTextMatchingRegex(".advancedQueryString", regEx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void waitForBasicFilterButtonMatchingRegex(String regEx)
|
||||
{
|
||||
qSeleniumLib.waitForSelectorContainingTextMatchingRegex("BUTTON", regEx);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QSeleniumLib;
|
||||
import io.javalin.Javalin;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -285,6 +284,7 @@ public class QSeleniumJavalin
|
||||
|
||||
do
|
||||
{
|
||||
// LOG.debug(" captured paths: " + captured.stream().map(CapturedContext::getPath).collect(Collectors.joining(",")));
|
||||
for(CapturedContext context : captured)
|
||||
{
|
||||
if(context.getPath().equals(path))
|
||||
@ -301,7 +301,6 @@ public class QSeleniumJavalin
|
||||
}
|
||||
while(start + (1000 * WAIT_SECONDS) > System.currentTimeMillis());
|
||||
|
||||
LOG.debug(" captured paths: \n " + captured.stream().map(cc -> cc.getPath() + "[" + cc.getBody() + "]").collect(Collectors.joining("\n ")));
|
||||
fail("Failed to capture a request for path [" + path + "] with body containing [" + bodyContaining + "] after [" + WAIT_SECONDS + "] seconds.");
|
||||
return (null);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(1);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"is not empty\"]");
|
||||
|
||||
///////////////////////////////
|
||||
@ -93,7 +93,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(1);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"is between\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"1701\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"74656\"]");
|
||||
@ -116,7 +116,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(1);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"is any of\"]");
|
||||
qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "St. Louis");
|
||||
qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "Chesterfield");
|
||||
@ -129,7 +129,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(1);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"is after\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"5 days ago\"]");
|
||||
|
||||
@ -142,7 +142,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(2);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"is at or before\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"start of this year\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"starts with\"]");
|
||||
@ -165,7 +165,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.assertFilterButtonBadge(1);
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
qSeleniumLib.waitForSelector("input[value=\"does not equal\"]");
|
||||
qSeleniumLib.waitForSelector("input[value=\"St. Louis\"]");
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.selenium.tests.query;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QBaseSeleniumTest;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QQQMaterialDashboardSelectors;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QueryScreenLib;
|
||||
@ -49,7 +48,6 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/person/query", "data/person/index.json")
|
||||
.withRouteToFile("/data/person/variants", "data/person/variants.json")
|
||||
.withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json")
|
||||
.withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json");
|
||||
}
|
||||
|
||||
@ -66,13 +64,13 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.gotoAdvancedMode();
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// open the filter window, enter a value, wait for query to re-run //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.addAdvancedQueryFilterInput(0, "Id", "equals", "1", null);
|
||||
queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 0, "Id", "equals", "1", null);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// assert that query & count both have the expected filter value //
|
||||
@ -119,11 +117,11 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
queryScreenLib.gotoAdvancedMode();
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.clickFilterButton();
|
||||
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.addAdvancedQueryFilterInput(0, "First Name", "contains", "Dar", "Or");
|
||||
queryScreenLib.addAdvancedQueryFilterInput(1, "First Name", "contains", "Jam", "Or");
|
||||
queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 0, "First Name", "contains", "Dar", "Or");
|
||||
queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 1, "First Name", "contains", "Jam", "Or");
|
||||
|
||||
String expectedFilterContents0 = """
|
||||
{"fieldName":"firstName","operator":"CONTAINS","values":["Dar"]}""";
|
||||
@ -139,138 +137,6 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBasicBooleanOperators()
|
||||
{
|
||||
QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib);
|
||||
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
|
||||
queryScreenLib.addBasicFilter("Is Employed");
|
||||
|
||||
testBasicCriteria(queryScreenLib, "Is Employed", "equals yes", null, "(?s).*Is Employed:.*yes.*", """
|
||||
{"fieldName":"isEmployed","operator":"EQUALS","values":[true]}""");
|
||||
|
||||
testBasicCriteria(queryScreenLib, "Is Employed", "equals no", null, "(?s).*Is Employed:.*no.*", """
|
||||
{"fieldName":"isEmployed","operator":"EQUALS","values":[false]}""");
|
||||
|
||||
testBasicCriteria(queryScreenLib, "Is Employed", "is empty", null, "(?s).*Is Employed:.*is empty.*", """
|
||||
{"fieldName":"isEmployed","operator":"IS_BLANK","values":[]}""");
|
||||
|
||||
testBasicCriteria(queryScreenLib, "Is Employed", "is not empty", null, "(?s).*Is Employed:.*is not empty.*", """
|
||||
{"fieldName":"isEmployed","operator":"IS_NOT_BLANK","values":[]}""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBasicPossibleValues()
|
||||
{
|
||||
QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib);
|
||||
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
|
||||
String field = "Home City";
|
||||
queryScreenLib.addBasicFilter(field);
|
||||
|
||||
testBasicCriteriaPossibleValues(queryScreenLib, field, "is any of", List.of("St. Louis", "Chesterfield"), "(?s).*" + field + ":.*St. Louis.*\\+1.*", """
|
||||
{"fieldName":"homeCityId","operator":"IN","values":[1,2]}""");
|
||||
|
||||
testBasicCriteriaPossibleValues(queryScreenLib, field, "equals", List.of("Chesterfield"), "(?s).*" + field + ":.*Chesterfield.*", """
|
||||
{"fieldName":"homeCityId","operator":"EQUALS","values":[2]}""");
|
||||
|
||||
testBasicCriteriaPossibleValues(queryScreenLib, field, "is empty", null, "(?s).*" + field + ":.*is empty.*", """
|
||||
{"fieldName":"homeCityId","operator":"IS_BLANK","values":[]}""");
|
||||
|
||||
testBasicCriteriaPossibleValues(queryScreenLib, field, "does not equal", List.of("St. Louis"), "(?s).*" + field + ":.*does not equal.*St. Louis.*", """
|
||||
{"fieldName":"homeCityId","operator":"NOT_EQUALS_OR_IS_NULL","values":[1]}""");
|
||||
|
||||
testBasicCriteriaPossibleValues(queryScreenLib, field, "is none of", List.of("Chesterfield"), "(?s).*" + field + ":.*is none of.*St. Louis.*\\+1", """
|
||||
{"fieldName":"homeCityId","operator":"NOT_IN","values":[1,2]}""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void testBasicCriteria(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, String value, String expectButtonStringRegex, String expectFilterJsonContains)
|
||||
{
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.setBasicFilter(fieldLabel, operatorLabel, value);
|
||||
queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void testBasicCriteriaPossibleValues(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, List<String> values, String expectButtonStringRegex, String expectFilterJsonContains)
|
||||
{
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.setBasicFilterPossibleValues(fieldLabel, operatorLabel, values);
|
||||
queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAdvancedBooleanOperators()
|
||||
{
|
||||
QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib);
|
||||
|
||||
qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person");
|
||||
queryScreenLib.waitForQueryToHaveRan();
|
||||
|
||||
queryScreenLib.gotoAdvancedMode();
|
||||
|
||||
testAdvancedCriteria(queryScreenLib, "Is Employed", "equals yes", null, "(?s).*Is Employed.*equals yes.*", """
|
||||
{"fieldName":"isEmployed","operator":"EQUALS","values":[true]}""");
|
||||
|
||||
testAdvancedCriteria(queryScreenLib, "Is Employed", "equals no", null, "(?s).*Is Employed.*equals no.*", """
|
||||
{"fieldName":"isEmployed","operator":"EQUALS","values":[false]}""");
|
||||
|
||||
testAdvancedCriteria(queryScreenLib, "Is Employed", "is empty", null, "(?s).*Is Employed.*is empty.*", """
|
||||
{"fieldName":"isEmployed","operator":"IS_BLANK","values":[]}""");
|
||||
|
||||
testAdvancedCriteria(queryScreenLib, "Is Employed", "is not empty", null, "(?s).*Is Employed.*is not empty.*", """
|
||||
{"fieldName":"isEmployed","operator":"IS_NOT_BLANK","values":[]}""");
|
||||
}
|
||||
|
||||
// todo - table requires variant - prompt for it, choose it, see query; change variant, change on-screen, re-query
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void testAdvancedCriteria(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, String value, String expectQueryStringRegex, String expectFilterJsonContains)
|
||||
{
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.clickFilterBuilderButton();
|
||||
queryScreenLib.addAdvancedQueryFilterInput(0, fieldLabel, operatorLabel, value, null);
|
||||
qSeleniumLib.clickBackdrop();
|
||||
queryScreenLib.waitForAdvancedQueryStringMatchingRegex(expectQueryStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
queryScreenLib.clickAdvancedFilterClearIcon();
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user