diff --git a/docs/backend-services/graphql/GraphQL-Filtering.md b/docs/backend-services/graphql/GraphQL-Filtering.md
index bc9fb3f41..70cdca618 100644
--- a/docs/backend-services/graphql/GraphQL-Filtering.md
+++ b/docs/backend-services/graphql/GraphQL-Filtering.md
@@ -2,6 +2,7 @@
- [filterBy](#filterby)
- [Complex Objects](#complex-objects)
- [Extra Query Arguments](#extra-query-arguments)
+- [Override the filter query](#override-the-filter-query)
### Introduction
The implementation of a GraphQL Service requires a certain structure to follow for `Slickgrid-Universal` to work correctly (it will fail if your GraphQL Schema is any different than what is shown below).
@@ -131,4 +132,25 @@ this.gridOptions = {
}
}
}
+```
+
+### Override the filter query
+
+Column filters may have a `Custom` Operator, that acts as a placeholder for you to define your own logic. To do so, the easiest way is to provide the `filterQueryOverride` callback in the GraphQL Options. This method will be called with `BackendServiceFilterQueryOverrideArgs` to let you decide dynamically on how the filter should be assembled.
+
+E.g. you could listen for a specific column and the active `OperatorType.custom` in order to switch the filter to an SQL LIKE search in GraphQL:
+
+> **Note** technically speaking GraphQL isn't a database query language like SQL, it's an application query language. Depending on your configuration, your GraphQL Server might already support regex querying (e.g. Hasura [_regex](https://hasura.io/docs/latest/queries/postgres/filters/text-search-operators/#_regex)) or you could add your own implementation (e.g. see this SO: https://stackoverflow.com/a/37981802/1212166). Just make sure that whatever custom operator that you want to use, is already included in your GraphQL Schema.
+
+```ts
+backendServiceApi: {
+ options: {
+ filterQueryOverride: ({ fieldName, columnDef, columnFilterOperator, searchValue }) => {
+ if (columnFilterOperator === OperatorType.custom && columnDef?.id === 'name') {
+ // the `operator` is a string, make sure to implement this new operator in your GraphQL Schema
+ return { field: fieldName, operator: 'Like', value: searchValue };
+ }
+ },
+ }
+}
```
\ No newline at end of file
diff --git a/docs/grid-functionalities/Export-to-Excel.md b/docs/grid-functionalities/Export-to-Excel.md
index 95d94a059..ce88437a7 100644
--- a/docs/grid-functionalities/Export-to-Excel.md
+++ b/docs/grid-functionalities/Export-to-Excel.md
@@ -173,9 +173,9 @@ export class MyExample {
// push empty data on A1
cols.push({ value: '' });
// push data in B1 cell with metadata formatter
- cols.push({
- value: customTitle,
- metadata: { style: excelFormat.id }
+ cols.push({
+ value: customTitle,
+ metadata: { style: excelFormat.id }
});
sheet.data.push(cols);
}
@@ -315,7 +315,6 @@ Below is a preview of the previous customizations shown above
![image](https://user-images.githubusercontent.com/643976/208590003-b637dcda-5164-42cc-bfad-e921a22c1837.png)
-
### Cell Format Auto-Detect Disable
##### requires `v3.2.0` or higher
The system will auto-detect the Excel format to use for Date and Number field types, if for some reason you wish to disable it then you provide the excel export options below
diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example10.html b/examples/vite-demo-vanilla-bundle/src/examples/example10.html
index 1d9495c7b..940cf169e 100644
--- a/examples/vite-demo-vanilla-bundle/src/examples/example10.html
+++ b/examples/vite-demo-vanilla-bundle/src/examples/example10.html
@@ -17,7 +17,10 @@
(*) NO DATA SHOWN
- - just change any of Filters/Sorting/Pages and look at the "GraphQL Query" changing :)
+ - just change any of Filters/Sorting/Pages and look at the "GraphQL Query" changing.
+ Also note that the column Name has a filter with a custom %% operator that behaves like an SQL LIKE operator supporting % wildcards.
+ Depending on your configuration, your GraphQL Server might already support regex querying (e.g. Hasura [_regex](https://hasura.io/docs/latest/queries/postgres/filters/text-search-operators/#_regex))
+ or you could add your own implementation (e.g. see this SO: https://stackoverflow.com/a/37981802/1212166).
diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example10.ts b/examples/vite-demo-vanilla-bundle/src/examples/example10.ts
index ac2e39856..7d8eb5548 100644
--- a/examples/vite-demo-vanilla-bundle/src/examples/example10.ts
+++ b/examples/vite-demo-vanilla-bundle/src/examples/example10.ts
@@ -83,7 +83,15 @@ export default class Example10 {
sortable: true,
filterable: true,
filter: {
- model: Filters.compoundInput
+ model: Filters.compoundInput,
+ compoundOperatorList: [
+ { operator: '', desc: 'Contains' },
+ { operator: '<>', desc: 'Not Contains' },
+ { operator: '=', desc: 'Equals' },
+ { operator: '!=', desc: 'Not equal to' },
+ { operator: 'a*', desc: 'Starts With' },
+ { operator: 'Custom', desc: 'SQL Like' },
+ ],
}
},
{
@@ -144,6 +152,10 @@ export default class Example10 {
enableAutoResize: false,
gridHeight: 275,
gridWidth: 900,
+ compoundOperatorAltTexts: {
+ // where '=' is any of the `OperatorString` type shown above
+ text: { 'Custom': { operatorAlt: '%%', descAlt: 'SQL Like' } },
+ },
enableFiltering: true,
enableCellNavigation: true,
createPreHeaderPanel: true,
@@ -193,6 +205,15 @@ export default class Example10 {
field: 'userId',
value: 123
}],
+ filterQueryOverride: ({ fieldName, columnDef, columnFilterOperator, searchValue }) => {
+ if (columnFilterOperator === OperatorType.custom && columnDef?.id === 'name') {
+ // technically speaking GraphQL isn't a database query language like SQL, it's an application query language.
+ // What that means is that GraphQL won't let you write arbitrary queries out of the box.
+ // It will only support the types of queries defined in your GraphQL schema.
+ // see this SO: https://stackoverflow.com/a/37981802/1212166
+ return { field: fieldName, operator: 'Like', value: searchValue };
+ }
+ },
useCursor: this.isWithCursor, // sets pagination strategy, if true requires a call to setPageInfo() when graphql call returns
// when dealing with complex objects, we want to keep our field name with double quotes
// example with gender: query { users (orderBy:[{field:"gender",direction:ASC}]) {}
diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example23.ts b/examples/vite-demo-vanilla-bundle/src/examples/example23.ts
index 1a8186582..2103e0c2a 100644
--- a/examples/vite-demo-vanilla-bundle/src/examples/example23.ts
+++ b/examples/vite-demo-vanilla-bundle/src/examples/example23.ts
@@ -97,7 +97,7 @@ export class CustomSumAggregator implements Aggregator {
export default class Example19 {
private _bindingEventService: BindingEventService;
columnDefinitions: Column[] = [];
- dataset: any[] = [];
+ dataset: GroceryItem[] = [];
gridOptions!: GridOption;
gridContainerElm: HTMLDivElement;
sgb: SlickVanillaGridBundle;
@@ -287,6 +287,7 @@ export default class Example19 {
maxDecimal: 2,
minDecimal: 2,
},
+ enableGrouping: true,
externalResources: [this.excelExportService],
enableExcelExport: true,
excelExportOptions: {
@@ -426,7 +427,7 @@ export default class Example19 {
{ id: i++, name: 'Chicken Wings', qty: 12, taxable: true, price: .53 },
{ id: i++, name: 'Drinkable Yogurt', qty: 6, taxable: true, price: 1.22 },
{ id: i++, name: 'Milk', qty: 3, taxable: true, price: 3.11 },
- ];
+ ] as GroceryItem[];
return datasetTmp;
}
diff --git a/packages/graphql/src/interfaces/graphqlFilteringOption.interface.ts b/packages/graphql/src/interfaces/graphqlFilteringOption.interface.ts
index de8aa929c..182cf4481 100644
--- a/packages/graphql/src/interfaces/graphqlFilteringOption.interface.ts
+++ b/packages/graphql/src/interfaces/graphqlFilteringOption.interface.ts
@@ -10,3 +10,14 @@ export interface GraphqlFilteringOption {
/** Value to use when filtering */
value: any | any[];
}
+
+export interface GraphqlCustomFilteringOption {
+ /** Field name to use when filtering */
+ field: string;
+
+ /** Custom Operator to use when filtering. Please note that any new Custom Operator must be implemented in your GraphQL Schema. */
+ operator: OperatorType | OperatorString;
+
+ /** Value to use when filtering */
+ value: any | any[];
+}
diff --git a/packages/graphql/src/interfaces/graphqlServiceOption.interface.ts b/packages/graphql/src/interfaces/graphqlServiceOption.interface.ts
index ab1ac9107..086232e99 100644
--- a/packages/graphql/src/interfaces/graphqlServiceOption.interface.ts
+++ b/packages/graphql/src/interfaces/graphqlServiceOption.interface.ts
@@ -1,6 +1,6 @@
-import type { BackendServiceOption } from '@slickgrid-universal/common';
+import type { BackendServiceOption, BackendServiceFilterQueryOverrideArgs } from '@slickgrid-universal/common';
-import type { GraphqlFilteringOption } from './graphqlFilteringOption.interface';
+import type { GraphqlCustomFilteringOption, GraphqlFilteringOption } from './graphqlFilteringOption.interface';
import type { GraphqlSortingOption } from './graphqlSortingOption.interface';
import type { GraphqlCursorPaginationOption } from './graphqlCursorPaginationOption.interface';
import type { GraphqlPaginationOption } from './graphqlPaginationOption.interface';
@@ -29,6 +29,9 @@ export interface GraphqlServiceOption extends BackendServiceOption {
/** array of Filtering Options, ex.: { field: name, operator: EQ, value: "John" } */
filteringOptions?: GraphqlFilteringOption[];
+ /** An optional predicate function to overide the built-in filter construction */
+ filterQueryOverride?: (args: BackendServiceFilterQueryOverrideArgs) => GraphqlCustomFilteringOption | undefined;
+
/** What are the pagination options? ex.: (first, last, offset) */
paginationOptions?: GraphqlPaginationOption | GraphqlCursorPaginationOption;
diff --git a/packages/graphql/src/services/__tests__/graphql.service.spec.ts b/packages/graphql/src/services/__tests__/graphql.service.spec.ts
index 0fae0c141..d167da4bc 100644
--- a/packages/graphql/src/services/__tests__/graphql.service.spec.ts
+++ b/packages/graphql/src/services/__tests__/graphql.service.spec.ts
@@ -944,6 +944,50 @@ describe('GraphqlService', () => {
expect(removeSpaces(query)).toBe(removeSpaces(expectation));
});
+ it('should bypass default behavior if filterQueryOverride is defined and does not return undefined', () => {
+ const expectation = `query{users(first:10, offset:0, filterBy:[{field:foo, operator:EQ, value:"bar"}]) { totalCount,nodes{ id,company,gender,name } }}`;
+ const mockColumn = { id: 'name', field: 'name' } as Column;
+ const mockColumnFilters = {
+ name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: FieldType.string },
+ } as ColumnFilters;
+
+ const sOptions = { ...serviceOptions, filterQueryOverride: () => ({ field: 'foo', operator: OperatorType.equal, value: 'bar' }) };
+ service.init(sOptions, paginationOptions, gridStub);
+ service.updateFilters(mockColumnFilters, false);
+ const query = service.buildQuery();
+
+ expect(removeSpaces(query)).toBe(removeSpaces(expectation));
+ });
+
+ it('should continue with default behavior if filterQueryOverride returns undefined', () => {
+ const expectation = `query{users(first:10, offset:0, filterBy:[{field:name, operator:StartsWith, value:"Ca"},{field:name, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`;
+ const mockColumn = { id: 'name', field: 'name' } as Column;
+ const mockColumnFilters = {
+ name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: FieldType.string },
+ } as ColumnFilters;
+
+ const sOptions = { ...serviceOptions, filterQueryOverride: () => undefined };
+ service.init(sOptions, paginationOptions, gridStub);
+ service.updateFilters(mockColumnFilters, false);
+ const query = service.buildQuery();
+
+ expect(removeSpaces(query)).toBe(removeSpaces(expectation));
+ });
+
+ it('should continue with default behavior if filterQueryOverride is not provided', () => {
+ const expectation = `query{users(first:10, offset:0, filterBy:[{field:name, operator:StartsWith, value:"Ca"},{field:name, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`;
+ const mockColumn = { id: 'name', field: 'name' } as Column;
+ const mockColumnFilters = {
+ name: { columnId: 'name', columnDef: mockColumn, searchTerms: ['Ca*le'], operator: 'a*z', type: FieldType.string },
+ } as ColumnFilters;
+
+ service.init(serviceOptions, paginationOptions, gridStub);
+ service.updateFilters(mockColumnFilters, false);
+ const query = service.buildQuery();
+
+ expect(removeSpaces(query)).toBe(removeSpaces(expectation));
+ });
+
it('should return a query with search having the operator Greater of Equal when the search value was provided as ">=10"', () => {
const expectation = `query{users(first:10, offset:0, filterBy:[{field:age, operator:GE, value:"10"}]) { totalCount,nodes{ id,company,gender,name } }}`;
const mockColumn = { id: 'age', field: 'age' } as Column;
diff --git a/packages/graphql/src/services/graphql.service.ts b/packages/graphql/src/services/graphql.service.ts
index f23ae7a63..e989e2a82 100644
--- a/packages/graphql/src/services/graphql.service.ts
+++ b/packages/graphql/src/services/graphql.service.ts
@@ -31,6 +31,7 @@ import { getHtmlStringOutput, stripTags } from '@slickgrid-universal/utils';
import type {
GraphqlCursorPaginationOption,
+ GraphqlCustomFilteringOption,
GraphqlDatasetFilter,
GraphqlFilteringOption,
GraphqlPaginationOption,
@@ -378,7 +379,7 @@ export class GraphqlService implements BackendService {
* @param columnFilters
*/
updateFilters(columnFilters: ColumnFilters | CurrentFilter[], isUpdatedByPresetOrDynamically: boolean) {
- const searchByArray: GraphqlFilteringOption[] = [];
+ const searchByArray: Array = [];
let searchValue: string | string[];
// on filter preset load, we need to keep current filters
@@ -442,80 +443,98 @@ export class GraphqlService implements BackendService {
continue;
}
- // StartsWith + EndsWith combo
- if (comboStartsWith && comboEndsWith) {
- searchTerms = [comboStartsWith, comboEndsWith];
- operator = OperatorType.startsWithEndsWith;
- } else if (Array.isArray(searchTerms) && searchTerms.length === 1 && typeof searchTerms[0] === 'string' && searchTerms[0].indexOf('..') >= 0) {
- if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) {
- operator = this._gridOptions.defaultFilterRangeOperator ?? OperatorType.rangeInclusive;
+ let filterQueryOverride: GraphqlCustomFilteringOption | undefined = undefined;
+ if (typeof this.options?.filterQueryOverride === 'function') {
+ filterQueryOverride = this.options?.filterQueryOverride({
+ fieldName: getHtmlStringOutput(fieldName),
+ columnDef,
+ operator,
+ columnFilterOperator: columnFilter.operator,
+ searchValue,
+ grid: this._grid
+ });
+ }
+
+ if (filterQueryOverride !== undefined) {
+ // since this is a Custom Filter, we expect Operator to be a string
+ // and it is assumed that the developer will implement this custom operator in their GraphQL Schema
+ // e.g.: https://stackoverflow.com/a/37981802/1212166
+ searchByArray.push(filterQueryOverride);
+ } else {
+ if (comboStartsWith && comboEndsWith) {
+ searchTerms = [comboStartsWith, comboEndsWith];
+ operator = OperatorType.startsWithEndsWith;
+ } else if (Array.isArray(searchTerms) && searchTerms.length === 1 && typeof searchTerms[0] === 'string' && searchTerms[0].indexOf('..') >= 0) {
+ if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) {
+ operator = this._gridOptions.defaultFilterRangeOperator ?? OperatorType.rangeInclusive;
+ }
+ searchTerms = searchTerms[0].split('..', 2);
+ if (searchTerms[0] === '') {
+ operator = operator === OperatorType.rangeInclusive ? '<=' : operator === OperatorType.rangeExclusive ? '<' : operator;
+ searchTerms = searchTerms.slice(1);
+ searchValue = searchTerms[0];
+ } else if (searchTerms[1] === '') {
+ operator = operator === OperatorType.rangeInclusive ? '>=' : operator === OperatorType.rangeExclusive ? '>' : operator;
+ searchTerms = searchTerms.slice(0, 1);
+ searchValue = searchTerms[0];
+ }
}
- searchTerms = searchTerms[0].split('..', 2);
- if (searchTerms[0] === '') {
- operator = operator === OperatorType.rangeInclusive ? '<=' : operator === OperatorType.rangeExclusive ? '<' : operator;
- searchTerms = searchTerms.slice(1);
- searchValue = searchTerms[0];
- } else if (searchTerms[1] === '') {
- operator = operator === OperatorType.rangeInclusive ? '>=' : operator === OperatorType.rangeExclusive ? '>' : operator;
- searchTerms = searchTerms.slice(0, 1);
- searchValue = searchTerms[0];
+
+ if (typeof searchValue === 'string') {
+ if (operator === '*' || operator === 'a*' || operator === '*z' || lastValueChar === '*') {
+ operator = ((operator === '*' || operator === '*z') ? 'EndsWith' : 'StartsWith') as OperatorString;
+ }
}
- }
- if (typeof searchValue === 'string') {
- if (operator === '*' || operator === 'a*' || operator === '*z' || lastValueChar === '*') {
- operator = ((operator === '*' || operator === '*z') ? 'EndsWith' : 'StartsWith') as OperatorString;
+ // if we didn't find an Operator but we have a Column Operator inside the Filter (DOM Element), we should use its default Operator
+ // multipleSelect is "IN", while singleSelect is "EQ", else don't map any operator
+ if (!operator && columnDef.filter && columnDef.filter.operator) {
+ operator = columnDef.filter.operator;
}
- }
- // if we didn't find an Operator but we have a Column Operator inside the Filter (DOM Element), we should use its default Operator
- // multipleSelect is "IN", while singleSelect is "EQ", else don't map any operator
- if (!operator && columnDef.filter && columnDef.filter.operator) {
- operator = columnDef.filter.operator;
- }
+ // No operator and 2 search terms should lead to default range operator.
+ if (!operator && Array.isArray(searchTerms) && searchTerms.length === 2 && searchTerms[0] && searchTerms[1]) {
+ operator = this._gridOptions.defaultFilterRangeOperator as OperatorString;
+ }
- // No operator and 2 search terms should lead to default range operator.
- if (!operator && Array.isArray(searchTerms) && searchTerms.length === 2 && searchTerms[0] && searchTerms[1]) {
- operator = this._gridOptions.defaultFilterRangeOperator as OperatorString;
- }
+ // Range with 1 searchterm should lead to equals for a date field.
+ if ((operator === OperatorType.rangeInclusive || operator === OperatorType.rangeExclusive) && Array.isArray(searchTerms) && searchTerms.length === 1 && fieldType === FieldType.date) {
+ operator = OperatorType.equal;
+ }
- // Range with 1 searchterm should lead to equals for a date field.
- if ((operator === OperatorType.rangeInclusive || operator === OperatorType.rangeExclusive) && Array.isArray(searchTerms) && searchTerms.length === 1 && fieldType === FieldType.date) {
- operator = OperatorType.equal;
- }
+ // Normalize all search values
+ searchValue = this.normalizeSearchValue(fieldType, searchValue);
+ if (Array.isArray(searchTerms)) {
+ searchTerms.forEach((_part, index) => {
+ searchTerms[index] = this.normalizeSearchValue(fieldType, searchTerms[index]);
+ });
+ }
- // Normalize all search values
- searchValue = this.normalizeSearchValue(fieldType, searchValue);
- if (Array.isArray(searchTerms)) {
- searchTerms.forEach((_part, index) => {
- searchTerms[index] = this.normalizeSearchValue(fieldType, searchTerms[index]);
- });
- }
+ // StartsWith + EndsWith combo
+ if (operator === OperatorType.startsWithEndsWith && Array.isArray(searchTerms) && searchTerms.length === 2) {
+ // add 2 conditions (StartsWith A + EndsWith B) to the search array
+ searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: OperatorType.startsWith, value: comboStartsWith });
+ searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: OperatorType.endsWith, value: comboEndsWith });
+ continue;
+ }
- // StartsWith + EndsWith combo
- if (operator === OperatorType.startsWithEndsWith && Array.isArray(searchTerms) && searchTerms.length === 2) {
- // add 2 conditions (StartsWith A + EndsWith B) to the search array
- searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: OperatorType.startsWith, value: comboStartsWith });
- searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: OperatorType.endsWith, value: comboEndsWith });
- continue;
- }
+ // when having more than 1 search term (we need to create a CSV string for GraphQL "IN" or "NOT IN" filter search)
+ if (searchTerms?.length > 1 && (operator === 'IN' || operator === 'NIN' || operator === 'NOT_IN')) {
+ searchValue = searchTerms.join(',');
+ } else if (searchTerms?.length === 2 && (operator === OperatorType.rangeExclusive || operator === OperatorType.rangeInclusive)) {
+ searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: (operator === OperatorType.rangeInclusive ? 'GE' : 'GT'), value: searchTerms[0] });
+ searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: (operator === OperatorType.rangeInclusive ? 'LE' : 'LT'), value: searchTerms[1] });
+ continue;
+ }
- // when having more than 1 search term (we need to create a CSV string for GraphQL "IN" or "NOT IN" filter search)
- if (searchTerms?.length > 1 && (operator === 'IN' || operator === 'NIN' || operator === 'NOT_IN')) {
- searchValue = searchTerms.join(',');
- } else if (searchTerms?.length === 2 && (operator === OperatorType.rangeExclusive || operator === OperatorType.rangeInclusive)) {
- searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: (operator === OperatorType.rangeInclusive ? 'GE' : 'GT'), value: searchTerms[0] });
- searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: (operator === OperatorType.rangeInclusive ? 'LE' : 'LT'), value: searchTerms[1] });
- continue;
- }
+ // if we still don't have an operator find the proper Operator to use according field type
+ if (!operator) {
+ operator = mapOperatorByFieldType(fieldType);
+ }
- // if we still don't have an operator find the proper Operator to use according field type
- if (!operator) {
- operator = mapOperatorByFieldType(fieldType);
+ // build the search array
+ searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: mapOperatorType(operator), value: searchValue });
}
-
- // build the search array
- searchByArray.push({ field: getHtmlStringOutput(fieldName), operator: mapOperatorType(operator), value: searchValue });
}
}
diff --git a/test/cypress/e2e/example10.cy.ts b/test/cypress/e2e/example10.cy.ts
index 94a1ffd37..c502f5aaa 100644
--- a/test/cypress/e2e/example10.cy.ts
+++ b/test/cypress/e2e/example10.cy.ts
@@ -307,6 +307,29 @@ describe('Example 10 - GraphQL Grid', () => {
});
});
+ it('should perform filterQueryOverride when operator "%%" is selected', () => {
+ cy.get('.search-filter.filter-name select').find('option').last().then((element) => {
+ cy.get('.search-filter.filter-name select').select(element.val());
+ });
+
+ cy.get('.search-filter.filter-name')
+ .find('input')
+ .clear()
+ .type('Jo%yn%er');
+
+ // wait for the query to finish
+ cy.get('[data-test=status]').should('contain', 'finished');
+
+ cy.get('[data-test=graphql-query-result]')
+ .should(($span) => {
+ const text = removeSpaces($span.text()); // remove all white spaces
+ expect(text).to.eq(removeSpaces(`query { users (first:30,offset:0,
+ orderBy:[{field:"name",direction:ASC}],
+ filterBy:[{field:"name",operator:Like,value:"Jo%yn%er"}],
+ locale:"en",userId:123) { totalCount, nodes { id,name,gender,company,billing{address{street,zip}},finish } } }`));
+ });
+ });
+
it('should click on Set Dynamic Filter and expect query and filters to be changed', () => {
cy.get('[data-test=set-dynamic-filter]')
.click();