UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

3,210 lines 165 kB
import { fakeAsync, TestBed, tick, flush, ComponentFixture } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IgxGridComponent } from './grid.component';
import { UIInteractions } from '../../test-utils/ui-interactions.spec';
import { configureTestSuite } from '../../test-utils/configure-suite';
import {
    IgxNumberFilteringOperand,
    IgxStringFilteringOperand
} from '../../data-operations/filtering-condition';
import { GridFunctions } from '../../test-utils/grid-functions.spec';
import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
import { FilteringLogic } from '../../data-operations/filtering-expression.interface';
import {
    IgxGridAdvancedFilteringColumnGroupComponent,
    IgxGridAdvancedFilteringComponent,
    IgxGridExternalAdvancedFilteringComponent,
    IgxGridAdvancedFilteringBindingComponent
} from '../../test-utils/grid-samples.spec';
import { ControlsFunction } from '../../test-utils/controls-functions.spec';
import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy';
import { IgxHierGridExternalAdvancedFilteringComponent } from '../../test-utils/hierarchical-grid-components.spec';
import { IgxHierarchicalGridComponent } from '../hierarchical-grid/public_api';

const ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS = 'igx-filter-tree__line--and';
const ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS = 'igx-filter-tree__line--or';
const ADVANCED_FILTERING_OPERATOR_LINE_SELECTED_CSS_CLASS = 'igx-filter-tree__line--selected';
const ADVANCED_FILTERING_TOOLBAR_BUTTON_FILTERED_CSS_CLASS = 'igx-grid-toolbar__adv-filter--filtered';
const ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS = 'igx-filter-tree__expression-item';
const CHIP_SELECT_CLASS = '.igx-chip__select';

describe('IgxGrid - Advanced Filtering #grid - ', () => {
    configureTestSuite((() => {
        return TestBed.configureTestingModule({
            imports: [
                NoopAnimationsModule,
                IgxGridAdvancedFilteringColumnGroupComponent,
                IgxGridAdvancedFilteringComponent,
                IgxGridExternalAdvancedFilteringComponent,
                IgxGridAdvancedFilteringBindingComponent,
                IgxHierGridExternalAdvancedFilteringComponent
            ]
        });
    }));

    describe('General tests - ', () => {
        let fix: ComponentFixture<IgxGridAdvancedFilteringComponent>;
        let grid: IgxGridComponent;

        beforeEach(fakeAsync(() => {
            fix = TestBed.createComponent(IgxGridAdvancedFilteringComponent);
            grid = fix.componentInstance.grid;
            fix.detectChanges();
        }));

        it('Should show/hide Advanced Filtering button in toolbar based on respective input.', fakeAsync(() => {
            // Verify Advanced Filtering button in toolbar is visible.
            let advFilterButton = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterButton !== null && advFilterButton !== undefined).toBe(true, 'Adv.Filter button is not visible.');

            grid.allowAdvancedFiltering = false;
            fix.detectChanges();

            // Verify Advanced Filtering button in toolbar is not visible.
            advFilterButton = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterButton !== null && advFilterButton !== undefined).toBe(false, 'Adv.Filter button is visible.');

            grid.allowAdvancedFiltering = true;
            fix.detectChanges();

            // Verify Advanced Filtering button in toolbar is visible.
            advFilterButton = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterButton !== null && advFilterButton !== undefined).toBe(true, 'Adv.Filter button is not visible.');
        }));

        it('Should correctly initialize the Advanced Filtering dialog.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify AF dialog is opened.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull();

            // Verify there are not filters present and that the default text is shown.
            expect(grid.advancedFilteringExpressionsTree).toBeUndefined();
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull();
            expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull();

            // Close Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringCancelButton(fix);
            tick(200);
            fix.detectChanges();

            // Verify AF dialog is closed.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
        }));

        it('Should open/close Advanced Filtering dialog through API.', fakeAsync(() => {
            // Open dialog through API.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify AF dialog is opened.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull();

            // Close dialog through API.
            grid.closeAdvancedFilteringDialog(false);
            tick(100);
            fix.detectChanges();

            // Verify AF dialog is closed.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
        }));

        it('Should close Advanced Filtering dialog through API by respecting \'applyChanges\' argument.', fakeAsync(() => {
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Close dialog through API.
            grid.closeAdvancedFilteringDialog(true);
            tick(100);
            fix.detectChanges();

            // Verify AF dialog is closed.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
            // Verify the filter changes are applied.
            expect(grid.filteredData.length).toEqual(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');

            // Open the dialog again.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Double-click the existing chip to enter edit mode.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0], true);
            tick(50);
            fix.detectChanges();
            // Edit the filter value.
            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'some non-existing value', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Close dialog through API.
            grid.closeAdvancedFilteringDialog(false);
            tick(100);
            fix.detectChanges();

            // Verify AF dialog is closed.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
            // Verify the filter changes are NOT applied.
            expect(grid.filteredData.length).toEqual(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');
        }));

        it('Should show/hide initial adding buttons depending on the existence of groups.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Verify that the initial buttons are visible.
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2);

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Verify that the initial buttons are not visible.
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0);

            // Discard the new group and verify that the initial buttons are visible.
            GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix);
            fix.detectChanges();
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2);

            // Click the initial 'Add Or Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 1);
            tick(100);
            fix.detectChanges();

            // Verify that the initial buttons are not visible.
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0);
        }));

        it('Should correctly initialize a newly added \'And\' group.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Verify there is a new root group, which is empty.
            const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(group).not.toBeNull('There is no root group.');
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group).length).toBe(0, 'The group has children.');

            // Verify the operator line of the root group is an 'And' line.
            verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'and');

            // Verify the enabled/disabled state of each input of the expression in edit mode.
            verifyEditModeExpressionInputStates(fix, true, false, false, false);

            // Verify the edit inputs are empty.
            verifyEditModeExpressionInputValues(fix, '', '', '');

            // Verify adding buttons are disabled.
            const buttons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0);
            for (const button of buttons) {
                ControlsFunction.verifyButtonIsDisabled(button);
            }
        }));

        it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add Or Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 1);
            tick(100);
            fix.detectChanges();

            // Verify there is a new root group, which is empty.
            const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(group).not.toBeNull('There is no root group.');
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group).length).toBe(0, 'The group has children.');

            // Verify the operator line of the root group is an 'Or' line.
            verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'or');

            // Verify the enabled/disabled state of each input of the expression in edit mode.
            verifyEditModeExpressionInputStates(fix, true, false, false, false);

            // Verify the edit inputs are empty.
            verifyEditModeExpressionInputValues(fix, '', '', '');

            // Verify adding buttons are disabled.
            const buttons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0);
            for (const button of buttons) {
                ControlsFunction.verifyButtonIsDisabled(button);
            }
        }));

        it('Should add a new group through initial adding button and filter by it.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Verify the enabled/disabled state of each input of the expression in edit mode.
            verifyEditModeExpressionInputStates(fix, true, false, false, false);

            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            verifyEditModeExpressionInputStates(fix, true, true, false, false);

            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            verifyEditModeExpressionInputStates(fix, true, true, true, false);

            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.
            tick();
            fix.detectChanges();
            verifyEditModeExpressionInputStates(fix, true, true, true, true);

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Verify the new expression has been added to the group.
            const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(group).length).toBe(1, 'Incorrect children count.');

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');
        }));

        it('Should update the Advanced Filtering button in toolbar when (filtering)/(clear filtering).',
        fakeAsync(() => {
            // Verify that the advanced filtering button indicates there are no filters.
            let advFilterBtn = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterBtn.classList.contains(ADVANCED_FILTERING_TOOLBAR_BUTTON_FILTERED_CSS_CLASS))
                .toBe(false, 'Button indicates there is active filtering.');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify that the advanced filtering button indicates there are filters.
            advFilterBtn = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterBtn.classList.contains(ADVANCED_FILTERING_TOOLBAR_BUTTON_FILTERED_CSS_CLASS))
                .toBe(true, 'Button indicates there is no active filtering.');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Clear the filters.
            GridFunctions.clickAdvancedFilteringClearFilterButton(fix);
            fix.detectChanges();

            // Close the dialog.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify that the advanced filtering button indicates there are no filters.
            advFilterBtn = GridFunctions.getAdvancedFilteringButton(fix);
            expect(advFilterBtn.classList.contains(ADVANCED_FILTERING_TOOLBAR_BUTTON_FILTERED_CSS_CLASS))
                .toBe(false, 'Button indicates there is active filtering.');
        }));

        it('The Clear/Cancel/Apply buttons type should be set to "button"', () => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Get Clear/Cancel/Apply buttons types.
            const clearButtonType = GridFunctions.getAdvancedFilteringClearFilterButton(fix).getAttributeNode('type').value;
            const cancelButtonType = GridFunctions.getAdvancedFilteringCancelButton(fix).getAttributeNode('type').value;
            const applyButtonType = GridFunctions.getAdvancedFilteringApplyButton(fix).getAttributeNode('type').value;

            const expectedButtonType = 'button';

            // Verify buttons type is set to "button".
            expect(clearButtonType).toBe(expectedButtonType, 'Clear button type is not "button"');
            expect(cancelButtonType).toBe(expectedButtonType, 'Cancel button type is not "button"');
            expect(applyButtonType).toBe(expectedButtonType, 'Apply button type is not "button"');
        });

        it('Should correctly display header name in select dropdown and in chip expression.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Open column dropdown and verify header name is displayed for first item
            GridFunctions.clickAdvancedFilteringColumnSelect(fix);
            fix.detectChanges();
            const dropdownItems = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix);
            expect(dropdownItems[0].innerText).toBe('HeaderID');

            selectColumnInEditModeExpression(fix, 0); // Select 'HeaderID' column
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Verify header name in chip text
            verifyExpressionChipContent(fix, [0], 'HeaderID', 'Contains', 'a');

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Close Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringCancelButton(fix);
            tick(100);
            fix.detectChanges();

            // Open Advanced Filtering dialog again.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify header name in chip text
            verifyExpressionChipContent(fix, [0], 'HeaderID', 'Contains', 'a');
        }));

        it('Should correctly filter by a \'string\' column through UI.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');
        }));

        it('Should correctly filter by a \'Greater Than\' with \'number\' column through UI.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '20', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(5);
            expect(grid.rowList.length).toBe(5);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');
        }));

        it('Should correctly filter by a \'Equals\' with \'number\' column through UI.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '127', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(1);
            expect(grid.rowList.length).toBe(1);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('NetAdvantage');
        }));

        it('Should correctly filter by a \'boolean\' column through UI.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 3); // Select 'Released' column.
            selectOperatorInEditModeExpression(fix, 1); // Select 'True' operator.
            verifyEditModeExpressionInputStates(fix, true, true, false, true); // Third input should be disabled for unary operators.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(3);
            expect(grid.rowList.length).toBe(3);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('NetAdvantage');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe(null);
        }));

        it('Should correctly filter by a \'date\' column through UI with unary operator.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column.
            selectOperatorInEditModeExpression(fix, 9); // Select 'This Year' operator.
            verifyEditModeExpressionInputStates(fix, true, true, false, true); // Third input should be disabled for unary operators.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix, true);
            input.click();
            fix.detectChanges();

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            const expectedData = fix.componentInstance.data.filter(r =>
                r.ReleaseDate && r.ReleaseDate.getFullYear() === (new Date()).getFullYear());
            expect(grid.filteredData.length).toEqual(expectedData.length);
            expect(grid.rowList.length).toBe(expectedData.length);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe(expectedData[0].ProductName);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe(expectedData[1].ProductName);
        }));

        it('Should correctly filter by a \'date\' column through UI with value from calendar.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator.
            verifyEditModeExpressionInputStates(fix, true, true, true, false);
            const input = GridFunctions.getAdvancedFilteringValueInput(fix, true);
            input.click();
            fix.detectChanges();

            // Click on 'today' item in calendar.
            const calendar = GridFunctions.getAdvancedFilteringCalendar(fix);
            const todayItem = calendar.querySelector('.igx-calendar__date--current');
            todayItem.click();
            tick(100);
            fix.detectChanges();

            verifyEditModeExpressionInputStates(fix, true, true, true, true);

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(1);
            expect(grid.rowList.length).toBe(1);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 0).value.toString()).toBe('8');

            flush();
        }));

        it('Should emit the filteringDone event when applying filters.', fakeAsync(() => {
            spyOn(grid.filteringDone, 'emit');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            expect(grid.filteringDone.emit).toHaveBeenCalledWith(grid.advancedFilteringExpressionsTree);
        }));

        it('Applying/Clearing filter through the API should correctly update the UI.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify the initial state of the grid and that no filters are present.
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Verify the state of the grid after filtering.
            expect(grid.filteredData.length).toBe(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verfiy there is a root group with 'And' operator line and 2 children.
            const rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(rootGroup).not.toBeNull();
            verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'and');
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup).length).toBe(2);

            // Verify the contnet of the first child (expression) of the root group.
            verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100');

            // Verify the content of the second child (group) of the root group.
            const group = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(2);
            verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular');
            verifyExpressionChipContent(fix, [1, 1], 'ProductName', 'Contains', 'script');
            // Verify the operator line of the child group.
            verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or');

            // Close Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringCancelButton(fix);
            tick(100);
            fix.detectChanges();

            // Clear filters through API.
            grid.advancedFilteringExpressionsTree = null;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify there are not filters present and that the default text is shown.
            expect(grid.advancedFilteringExpressionsTree).toBeNull();
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull();
            expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull();
        }));

        it('Applying/Clearing filter through the UI should correctly update the API.', fakeAsync(() => {
            // Test prerequisites
            grid.height = '800px';
            fix.detectChanges();
            tick(50);

            // Verify the initial state of the grid and that no filters are present.
            expect(grid.advancedFilteringExpressionsTree).toBeUndefined();
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Add a root 'and' group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator.
            let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '100', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Add a child 'or' group.
            const addOrGroupBtn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2];
            addOrGroupBtn.click();
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Add new expression to the child group.
            const addExpressionBtn = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [1], 0)[0];
            addExpressionBtn.click();
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the state of the grid after the filtering.
            expect(grid.advancedFilteringExpressionsTree !== null && grid.advancedFilteringExpressionsTree !== undefined).toBe(true);
            expect(grid.filteredData.length).toBe(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            tick(100);
            fix.detectChanges();

            // Clear the filters.
            GridFunctions.clickAdvancedFilteringClearFilterButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify that no filters are present.
            expect(grid.advancedFilteringExpressionsTree).toBeNull();
            expect(grid.filteredData).toBeNull();
            expect(grid.rowList.length).toBe(8);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');
        }));

        it('Should discard the newly added group when clicking the \'close\' button of its initial condition.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Verify there is a new group and the initial buttons are not visible.
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).not.toBeNull();
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0);

            // Populate edit inputs
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.

            // Discard the populated expression, so the whole new group gets discarded.
            GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix);
            fix.detectChanges();

            // Verify there are no groups in the dialog and the initial buttons are visible.
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull();
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2);
        }));

        it('Should apply filters on Apply button click without prior Commit button click', fakeAsync(() => {
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value.

            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the filter results.
            expect(grid.filteredData.length).toEqual(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');
        }));

        it('Should close the dialog on Apply button click if not all expression inputs are set', fakeAsync(() => {
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the dialog is closed an no records are filtered
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
            expect(grid.filteredData).toBe(null);

            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.

            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
            expect(grid.filteredData).toBe(null);

            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator.

            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull();
            expect(grid.filteredData).toBe(null);
        }));

        it('Column dropdown should contain only filterable columns.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Make the 'Downloads', 'Released' and 'ReleaseDate' columns non-filterable.
            grid.getColumnByName('Downloads').filterable = false;
            grid.getColumnByName('Released').filterable = false;
            grid.getColumnByName('ReleaseDate').filterable = false;
            grid.cdr.detectChanges();
            tick(100);

            // Click the initial 'Add and Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Open column dropdown and verify that only filterable columns are present.
            GridFunctions.clickAdvancedFilteringColumnSelect(fix);
            fix.detectChanges();
            const dropdownItems = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix);
            expect(dropdownItems.length).toBe(3);
            expect(dropdownItems[0].innerText).toBe('HeaderID');
            expect(dropdownItems[1].innerText).toBe('ProductName');
            expect(dropdownItems[2].innerText).toBe('Another Field');
        }));

        it('Operator dropdown should contain operators based on the column\'s datatype (\'string\' or \'number\').', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Add a new group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Select 'string' type column ('ProductName').
            selectColumnInEditModeExpression(fix, 1);
            // Open the operator dropdown and verify they are 'string' specific.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            fix.detectChanges();
            let dropdownValues: string[] = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText);
            let expectedValues = ['Contains', 'Does Not Contain', 'Starts With', 'Ends With', 'Equals',
                                  'Does Not Equal', 'Empty', 'Not Empty', 'Null', 'Not Null'];
            expect(dropdownValues).toEqual(expectedValues);

            // Close current dropdown by a random select.
            GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, 0);
            tick();
            fix.detectChanges();

            // Select 'number' type column ('Downloads').
            selectColumnInEditModeExpression(fix, 2);
            // Open the operator dropdown and verify they are 'number' specific.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            fix.detectChanges();
            dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText);
            expectedValues = ['Equals', 'Does Not Equal', 'Greater Than', 'Less Than', 'Greater Than Or Equal To',
                              'Less Than Or Equal To', 'Empty', 'Not Empty', 'Null', 'Not Null'];
            expect(dropdownValues).toEqual(expectedValues);
        }));

        it('Operator dropdown should contain operators based on the column\'s datatype (\'date\' or \'boolean\').', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Add a new group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Select 'date' type column ('ReleaseDate').
            selectColumnInEditModeExpression(fix, 4);
            // Open the operator dropdown and verify they are 'date' specific.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            fix.detectChanges();
            let dropdownValues: string[] = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText);
            let expectedValues = ['Equals', 'Does Not Equal', 'Before', 'After', 'Today', 'Yesterday',
                                  'This Month', 'Last Month', 'Next Month', 'This Year', 'Last Year',
                                  'Next Year', 'Empty', 'Not Empty', 'Null', 'Not Null'];
            expect(dropdownValues).toEqual(expectedValues);

            // Close current dropdown by a random select.
            GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, 0);
            tick();
            fix.detectChanges();

            // Select 'boolean' type column ('Released').
            selectColumnInEditModeExpression(fix, 3);
            // Open the operator dropdown and verify they are 'boolean' specific.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            fix.detectChanges();
            dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText);
            expectedValues = ['All', 'True', 'False', 'Empty', 'Not Empty', 'Null', 'Not Null'];
            expect(dropdownValues).toEqual(expectedValues);
        }));

        it('Should not commit and close currently edited condition when the \'close\' button is clicked.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100');

            // Edit the first expression in the inner 'or' group.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0], true); // Double-click the chip
            tick(200);
            fix.detectChanges();
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '500', fix); // Type filter value.

            // Verify the edit mode container is visible.
            expect(GridFunctions.getAdvancedFilteringEditModeContainer(fix)).not.toBeNull();

            // Close the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix);
            fix.detectChanges();

            // Verify the edit mode container is no longer visible.
            expect(GridFunctions.getAdvancedFilteringEditModeContainer(fix)).toBeNull();

            verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100');
        }));

        it('Should scroll the adding buttons into view when the add icon of a chip is clicked.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            for (let index = 0; index < 30; index++) {
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals')
                });
            }
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Scroll to the top.
            const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix);
            tick(50);
            exprContainer.scrollTop = 0;

            // Hover the last visible expression chip
            const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[9];
            expressionItem.dispatchEvent(new MouseEvent('mouseenter'));
            tick();
            fix.detectChanges();

            // Click the add icon to display the adding buttons.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [9]);
            fix.detectChanges();
            tick(50);

            // Verify the adding buttons are in view.
            const addingButtons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0);
            for (const addingButton of addingButtons) {
                verifyElementIsInExpressionsContainerView(fix, addingButton);
            }
        }));

        it('Should scroll the newly added expression into view when the respective add button is clicked.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            for (let index = 0; index < 30; index++) {
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals')
                });
            }
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Scroll to the top.
            const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix);
            tick(50);
            exprContainer.scrollTop = 0;

            // Hover the previous to last visible expression chip.
            const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[8];
            expressionItem.dispatchEvent(new MouseEvent('mouseenter'));
            tick();
            fix.detectChanges();

            // Click the add icon to display the adding buttons.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [8]);
            tick(50);

            // Click the 'add condition' button.
            const addCondButton = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0];
            addCondButton.click();
            fix.detectChanges();

            // Verify the edit mode container (the one with the editing inputs) is in view.
            verifyElementIsInExpressionsContainerView(fix, GridFunctions.getAdvancedFilteringEditModeContainer(fix));
        }));

        it('Should scroll to the expression when entering its edit mode.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            for (let index = 0; index < 30; index++) {
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals')
                });
            }
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            GridFunctions.clickAdvancedFilteringButton(fix);
            fix.detectChanges();

            // Scroll to the top.
            const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix);
            tick(50);
            exprContainer.scrollTop = 0;

            // Hover the last visible expression chip
            const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[9];
            expressionItem.dispatchEvent(new MouseEvent('mouseenter'));
            tick();
            fix.detectChanges();

            // Click the edit icon to enter edit mode of the expression.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipEditIcon(fix, [9]);
            tick(50);

            // Verify the edit mode container (the one with the editing inputs) is in view.
            verifyElementIsInExpressionsContainerView(fix, GridFunctions.getAdvancedFilteringEditModeContainer(fix));
        }));

        it('Should keep the context menu in view when scrolling the expressions container.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            for (let index = 0; index < 20; index++) {
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals')
                });
            }
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Scroll to the top.
            const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix);
            tick(50);
            exprContainer.scrollTop = 0;

            // Select the first two chips.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
            fix.detectChanges();

            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]);
            tick(200); // Await click timeout
            fix.detectChanges();

            // Scroll to the bottom.
            exprContainer.scrollTop = exprContainer.scrollHeight;

            // Verify that the context menu is correctly repositioned and in view.
            const contextMenu = GridFunctions.getAdvancedFilteringContextMenu(fix);
            verifyElementIsInExpressionsContainerView(fix, contextMenu);
        }));

        it('Should clear all conditions and groups when the \'clear filter\' button is clicked.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify there are filters in the dialog.
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).not.toBeNull();
            expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).toBeNull();

            // Clear the filters.
            GridFunctions.clickAdvancedFilteringClearFilterButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify there are no filters in the dialog.
            expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull();
            expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull();
        }));

        it('Should keep edited conditions and groups inside AF dialog when applying and opening it again.',
        fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Verify the current filter state.
            expect(grid.filteredData.length).toBe(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify the content of the first expression in the inner 'or' group.
            verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular');

            // Edit the first expression in the inner 'or' group.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 0], true); // Double-click the chip
            tick(200);
            fix.detectChanges();
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the new current filter state.
            expect(grid.filteredData.length).toBe(3);
            expect(grid.rowList.length).toBe(3);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify the content of the first expression in the inner 'or' group.
            verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'a');
        }));

        it('Should not keep changes over edited conditions and groups inside AF dialog when canceling and opening it again.',
        fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Verify the current filter state.
            expect(grid.filteredData.length).toBe(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify the content of the first expression in the inner 'or' group.
            verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular');

            // Edit the first expression in the inner 'or' group.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 0], true); // Double-click the chip
            tick(200);
            fix.detectChanges();
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Cancel the filters.
            GridFunctions.clickAdvancedFilteringCancelButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the new filter state remains unchanged.
            expect(grid.filteredData.length).toBe(2);
            expect(grid.rowList.length).toBe(2);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
            expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script');

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify the content of the first expression in the inner 'or' group.
            verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular');
        }));

        it('Should not close the AF dialog when clicking outside of it.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify that the Advanced Filtering dialog is opened.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.');

            grid.nativeElement.click();
            tick(200);
            fix.detectChanges();

            // Verify that the Advanced Filtering dialog remains opened.
            expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.');
        }));

        it('Should display the adding buttons and the cancel button when trying to add a new condition/group to existing group.',
        fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Select a chip from the child group.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]);
            tick(200);
            fix.detectChanges();
            // Click the add icon to display the adding buttons.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]);
            fix.detectChanges();

            // Verify the adding buttons and cancel button are visible and enabled.
            const buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0);
            expect(buttons.length).toBe(4);
            for (const button of buttons) {
                ControlsFunction.verifyButtonIsDisabled(button, false);
            }

            // Click the cancel button to hide the buttons.
            const cancelButton = buttons[3];
            cancelButton.click();
            fix.detectChanges();

            // Verify the adding buttons and cancel button are no longer visible.
            const group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]);
            const childrenContainer = group.querySelector('.igx-filter-tree__expression');
            const buttonsContainers = childrenContainer.querySelectorAll(':scope > .igx-filter-tree__buttons');
            expect(buttonsContainers.length).toBe(0, 'Adding buttons are visible.');
        }));

        it('Should add a new condition to existing group by using add buttons.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify group's children count before adding a new child.
            let group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]);
            let groupDirectChildren = GridFunctions.getAdvancedFilteringTreeChildItems(group);
            expect(groupDirectChildren.length).toBe(2, 'incorrect direct children count of inner group');

            //  Select chip from group and click the add button.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]);
            tick(200);
            fix.detectChanges();
            GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]);
            fix.detectChanges();

            // Add new 'expression'.
            const buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0);
            buttons[0].click();
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'some value', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Verify group's children count before adding a new child.
            group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]);
            groupDirectChildren = GridFunctions.getAdvancedFilteringTreeChildItems(group);
            expect(groupDirectChildren.length).toBe(3, 'incorrect direct children count of inner group');
        }));

        it('Should add a new group to existing group by using add buttons.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify group's children count before adding a new child.
            let group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, true).length).toBe(2,
                'incorrect direct children count of group with path [0]');
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(2,
                'incorrect all children count of group with path [0]');

            //  Select chip from group and click the add button.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]);
            tick(200);
            fix.detectChanges();
            GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]);
            fix.detectChanges();

            // Add new 'and group'.
            let buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0);
            buttons[1].click();
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'some value', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Add new 'expression' to the newly added group.
            buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0, 1], 0);
            buttons[0].click();
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'another value', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // End the newly added group.
            buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0, 1], 0);
            buttons[3].click();
            fix.detectChanges();

            // Verify group's children count before adding a new child.
            group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, true).length).toBe(3,
                'incorrect direct children count of group with path [0]');
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(5,
                'incorrect all children count of group with path [0]');
        }));

        it('Should remove a condition from an existing group by using delete icon of respective chip.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify tree layout before deleting chips.
            let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

            // Delete a chip and verify layout.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipRemoveIcon(fix, [0]);
            tick(100);
            fix.detectChanges();

            rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3);

            // Delete a chip and verify layout.
            GridFunctions.clickAdvancedFilteringTreeExpressionChipRemoveIcon(fix, [0, 1]);
            tick(100);
            flush();
            fix.detectChanges();
            rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
            expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2);

            // Verify remaining chip's content.
            verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular');
        }));

        it('Should select/deselect a condition when its respective chip is clicked.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            tree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify first chip is not selected.
            verifyExpressionChipSelection(fix, [0], false);

            // Click first chip and verify it is selected.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
            tick(200);
            fix.detectChanges();
            verifyExpressionChipSelection(fix, [0], true);

            // Click first chip again and verify it is not selected.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
            tick(200);
            fix.detectChanges();
            verifyExpressionChipSelection(fix, [0], false);
        }));

        it('Should display edit and add buttons when hovering a chip.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.Or);
            tree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            tree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Verify actions container is not visible. (This container contains the 'edit' and the 'add' buttons.)
            expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0]))
                .toBeNull('actions container is visible');

            // Hover the first chip and verify actions container is visible.
            UIInteractions.hoverElement(GridFunctions.getAdvancedFilteringTreeItem(fix, [0]));
            tick(50);
            fix.detectChanges();
            expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0]))
                .not.toBeNull('actions container is not visible');

            // Unhover the first chip and verify actions container is not visible.
            UIInteractions.unhoverElement(GridFunctions.getAdvancedFilteringTreeItem(fix, [0]));
            tick(50);
            fix.detectChanges();
            expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0]))
                .toBeNull('actions container is visible');
        }));

        it('Should select/deselect all child conditions and groups when clicking a group\'s operator line.', fakeAsync(() => {
            // Apply advanced filter through API.
            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands.push({
                fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
            });
            const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            const andTree = new FilteringExpressionsTree(FilteringLogic.Or);
            andTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            andTree.filteringOperands.push({
                fieldName: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'),
                ignoreCase: true
            });
            orTree.filteringOperands.push(andTree);
            tree.filteringOperands.push(orTree);
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click root group's operator line and verify that the root group and all of its children become selected.
            let rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
            rootOperatorLine.click();
            tick(200);
            fix.detectChanges();
            verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), true);

            // Click root group's operator line again and verify that the root group and all of its children become unselected.
            rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
            rootOperatorLine.click();
            tick(200);
            fix.detectChanges();
            verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), false);

            // Click an inner group's operator line and verify its children become selected.
            GridFunctions.clickAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
            tick(200);
            fix.detectChanges();
            verifyChildrenSelection(GridFunctions.getAdvancedFilteringTreeItem(fix, [1]), true);

            // Click an inner group's operator line again and verify its children become unselected.
            GridFunctions.clickAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
            tick(200);
            fix.detectChanges();
            verifyChildrenSelection(GridFunctions.getAdvancedFilteringTreeItem(fix, [1]), false);
        }));

        it('Should open the operator dropdown below its respective input-group.', fakeAsync(() => {
            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Add root group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Add a new expression
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Add another expression to root group.
            const btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0];
            btn.click();
            tick(100);
            fix.detectChanges();

            // Populate column input.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.

            // Open the dropdown.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            tick(50);
            fix.detectChanges();
            expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).not.toBeNull('dropdown is not opened');

            // Close the dropdown.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            tick(50);
            fix.detectChanges();
            expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).toBeNull('dropdown is opened');

            // Open the operator dropdown again.
            GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
            tick(50);
            fix.detectChanges();

            // Verify the operator dropdown is positioned below its respective input-group.
            const dropdown: HTMLElement = GridFunctions.getAdvancedFilteringSelectDropdown(fix);
            expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).not.toBeNull('dropdown is not opened');

            const dropdownRect = dropdown.getBoundingClientRect();
            const inputGroup: HTMLElement = GridFunctions.getAdvancedFilteringOperatorSelect(fix).querySelector('igx-input-group');
            const inputGroupRect = inputGroup.getBoundingClientRect();
            const delta = 2;
            expect(Math.abs(dropdownRect.top - inputGroupRect.bottom) < delta).toBe(true,
                'incorrect vertical position of operator dropdown');
            expect(Math.abs(dropdownRect.left - inputGroupRect.left) < delta).toBe(true,
                'incorrect horizontal position of operator dropdown');
        }));

        it('Should filter by cells formatted data when using FormattedValuesFilteringStrategy', fakeAsync(() => {
            const formattedStrategy = new FormattedValuesFilteringStrategy(['Downloads']);
            grid.filterStrategy = formattedStrategy;
            const downloadsFormatter = (val: number): number => {
                if (!val || val > 0 && val < 100) {
                    return 1;
                } else if (val >= 100 && val < 500) {
                    return 2;
                } else {
                    return 3;
                }
            };
            grid.columnList.get(2).formatter = downloadsFormatter;
            fix.detectChanges();

            grid.openAdvancedFilteringDialog();
            tick(200);
            fix.detectChanges();

            // Add root group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Add a new expression
            selectColumnInEditModeExpression(fix, 2); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '1', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            tick(100);
            fix.detectChanges();
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            const rows = GridFunctions.getRows(fix);
            expect(rows.length).toEqual(3, 'Wrong filtered rows count');
        }));

        it('Should filter by cells formatted data when using FormattedValuesFilteringStrategy with rowData', fakeAsync(() => {
            const formattedStrategy = new FormattedValuesFilteringStrategy(['ProductName']);
            grid.filterStrategy = formattedStrategy;
            const anotherFieldFormatter = (value: any, rowData: any) => rowData.ID + ':' + value;
            grid.columnList.get(1).formatter = anotherFieldFormatter;
            fix.detectChanges();

            grid.openAdvancedFilteringDialog();
            tick(200);
            fix.detectChanges();

            // Add root group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Add a new expression
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '1:', fix); // Type filter value.

            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            tick(100);
            fix.detectChanges();
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            const rows = GridFunctions.getRows(fix);
            expect(rows.length).toEqual(1, 'Wrong filtered rows count');
        }));

        describe('Context Menu - ', () => {
            it('Should discard added group when clicking its operator line without having a single expression.', fakeAsync(() => {
                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Add initial 'and' group.
                GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
                tick(100);
                fix.detectChanges();

                // Click operator line.
                const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                rootOperatorLine.click();
                tick(200);
                fix.detectChanges();

                // Verify group is discarded and the context menu was not opened.
                expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull('Group is not discarded.');
                verifyContextMenuVisibility(fix, false);
            }));

            it('Selecting multiple conditions should display the (create group)/(delete filters) context menu.', fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify context menu is not visible.
                verifyContextMenuVisibility(fix, false);

                // Select two chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]);
                tick(200);
                fix.detectChanges();

                // Verify context menu is visible.
                verifyContextMenuVisibility(fix, true);
                verifyContextMenuType(fix, false);

                // Unselect one of the two selected chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                tick(200);
                fix.detectChanges();

                // Verify context menu is no longer visible.
                verifyContextMenuVisibility(fix, false);
            }));

            it('Should create an \'and\' group from multiple selected conditions when respective context menu button is clicked.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before the creation of a new group with context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

                let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                let secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2);

                // Select two chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]);
                tick(200);

                // Click "Create And Group" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[1].click();
                tick(100);
                fix.detectChanges();

                // Verify tree layout after the creation of a new group with context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(5);

                firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // the new group
                verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [0]), 'and');
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(false);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(2);

                secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or');
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(1);
            }));

            it('Should create an \'or\' group from multiple selected conditions when respective context menu button is clicked.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before the creation of a new group with context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

                let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                let secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2);

                // Select two chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]);
                tick(200);

                // Click "Create Or Group" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[2].click();
                tick(100);
                fix.detectChanges();

                // Verify tree layout after the creation of a new group with context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(5);

                firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // the new group
                verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [0]), 'or');
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(false);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(2);

                secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or');
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(1);
            }));

            it('Should respect the changes of the groups applied from the context menu.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Verify the filter changes are applied.
                expect(grid.filteredData.length).toEqual(0);

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before the group change through context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2);
                
                // Select two chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]);
                tick(200);

                // Click "Create Or Group" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[2].click();
                tick(100);
                fix.detectChanges();
                         
                // Close dialog through API.
                grid.closeAdvancedFilteringDialog(true);
                tick(100);
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();
                
                // Verify tree layout after the group change through context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3);
                
                // Verify the filter changes are applied.
                expect(grid.filteredData.length).toEqual(3);
            }));


            it('Should delete all selected conditions when the \'delete filters\' option from context menu is clicked.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before the creation of a new group with context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

                let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2);

                // Select two chips.
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
                GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]);
                tick(200);

                // Click "Delete Filters" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[3].click();
                tick(100);
                fix.detectChanges();

                // Verify tree layout after deleting some expressions with the context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2);

                firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(1);
                verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular');
            }));

            it('Should show/hide group\'s context menu when clicking its operator line.', fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify context menu is not visible.
                verifyContextMenuVisibility(fix, false);

                // Click the innner group's operator line.
                const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                operatorLine.click();
                fix.detectChanges();

                // Verify context menu is visible.
                verifyContextMenuVisibility(fix, true);
                verifyContextMenuType(fix, true);

                // Click the innner group's operator line again.
                operatorLine.click();
                tick();
                fix.detectChanges();

                // Verify context menu is no longer visible.
                verifyContextMenuVisibility(fix, false);
            }));

            it('Should change the group\'s operator when using its context menu buttons.', fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify current operator of inner group.
                let operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'or');

                // Click the innner group's operator line.
                operatorLine.click();
                tick(400);
                fix.detectChanges();

                // Click the 'and' button of the button group in the context menu.
                const buttonGroup = GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix);
                const andOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item'))
                                                    .find((b: any) => b.textContent.toLowerCase() === 'and');
                andOperatorButton.click();
                fix.detectChanges();

                // Verify new operator of inner group.
                operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'and');

                // Click the 'or' button of the button group in the context menu.
                const orOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item'))
                                                   .find((b: any) => b.textContent.toLowerCase() === 'or');
                orOperatorButton.click();
                fix.detectChanges();

                // Verify new operator of inner group.
                operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'or');
            }));

            it('Should apply changes in the group\'s operator made via its context menu buttons.', fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify current operator of inner group.
                let operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'or');

                // Verify the advancedFilteringExpressionsTree inner group's filteringOperand's operator
                expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(2);
                expect((grid.advancedFilteringExpressionsTree.filteringOperands[1] as IFilteringExpressionsTree).operator).toEqual(1);

                // Click the innner group's operator line.
                operatorLine.click();
                tick(400);
                fix.detectChanges();

                // Click the 'and' button of the button group in the context menu.
                const buttonGroup = GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix);
                const andOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item'))
                                                    .find((b: any) => b.textContent.toLowerCase() === 'and');
                andOperatorButton.click();
                fix.detectChanges();

                // Verify new operator of inner group.
                operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'and');

                // Apply new advanced filtering logic
                GridFunctions.clickAdvancedFilteringApplyButton(fix);
                tick(100);
                fix.detectChanges();

                // Open the advanced filtering dialog
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify the operator of inner group should persist as "And"
                operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                verifyOperatorLine(operatorLine, 'and');

                // Verify the advancedFilteringExpressionsTree inner group's filteringOperand's operator
                expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(2);
                expect((grid.advancedFilteringExpressionsTree.filteringOperands[1] as IFilteringExpressionsTree).operator).toEqual(0);
            }));

            it('Should ungroup the group\'s children and append them to next parent group when click \'ungroup\' from context menu.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before the the ungrouping with context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

                let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2);

                // Click the innner group's operator line.
                const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                operatorLine.click();
                tick(400);
                fix.detectChanges();

                // Click "Ungroup" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[3].click();
                tick(100);
                fix.detectChanges();

                // Verify tree layout after ungrouping a group with the context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(3);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3);
                // Verify three expression in the root group are what remains.
                for (let index = 0; index < 3; index++) {
                    firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [index]); // expression
                    expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);
                }

                // Should apply this change to the advancedFilteringExpressionsTree
                GridFunctions.clickAdvancedFilteringApplyButton(fix);
                tick(100);
                fix.detectChanges();

                // Open the advanced filtering dialog
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify the layout after as above
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(3);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3);
                // Verify three expression in the root group are what remains, no inner groups
                for (let index = 0; index < 3; index++) {
                    firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [index]); // expression
                    expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);
                }
            }));

            it('Ungroup button of the root group\'s context menu should be disabled.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Click root group's operator line.
                const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                rootOperatorLine.click();
                tick(200);
                fix.detectChanges();

                // Verify the ungroup button is disabled.
                const ungroupButton = GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3];
                ControlsFunction.verifyButtonIsDisabled(ungroupButton);
            }));

            it('Should delete the group from the tree when click \'delete\' from context menu.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify tree layout before deleting a group through context menu.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4);

                let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2);

                // Click the innner group's operator line.
                const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]);
                operatorLine.click();
                tick(400);
                fix.detectChanges();

                // Click "Delete" in context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[4].click();
                tick(100);
                fix.detectChanges();

                // Verify tree layout after deleting a group through context menu.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1);

                firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                // Should apply this change to the advancedFilteringExpressionsTree
                GridFunctions.clickAdvancedFilteringApplyButton(fix);
                tick(100);
                fix.detectChanges();

                // Open the advanced filtering dialog
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify the layout after as above
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1);

                firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression
                expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true);

                // Verify the advancedFilteringExpressionsTree filteringOperands operator
                expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(1);
            }));

            it('Should close the context menu when clicking its close button.' , fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.Or);
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Click root operator line to open the context menu.
                const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                rootOperatorLine.click();
                fix.detectChanges();

                // Verify context menu is opened.
                verifyContextMenuVisibility(fix, true);

                // Click close button of context menu.
                const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
                buttons[0].click();
                tick();

                // Verify context menu is closed.
                verifyContextMenuVisibility(fix, false);
            }));
        });

        describe('Keyboard Navigation/Interaction - ', () => {
            it('Should close the context menu when pressing \'Escape\' on it.' , fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.Or);
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Click root operator line to open the context menu.
                const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                rootOperatorLine.click();
                fix.detectChanges();

                // Verify context menu is opened.
                verifyContextMenuVisibility(fix, true);

                // Press 'Escape' on the context menu.
                UIInteractions.triggerKeyDownEvtUponElem('Escape', GridFunctions.getAdvancedFilteringContextMenu(fix));
                tick();

                // Verify context menu is closed.
                verifyContextMenuVisibility(fix, false);
            }));

            it('Should select/deselect a condition when pressing \'Enter\' on its respective chip.' , fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.Or);
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify first chip is not selected.
                verifyExpressionChipSelection(fix, [1], false);

                // Press 'Enter' on the second chip and verify it is selected.
                UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1]));
                tick(200);
                fix.detectChanges();
                verifyExpressionChipSelection(fix, [1], true);

                // Press 'Enter' on the second chip again and verify it is not selected.
                UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1]));
                tick(200);
                fix.detectChanges();
                verifyExpressionChipSelection(fix, [1], false);
            }));

            it('Should remove a chip in when pressing \'Enter\' on its \'remove\' icon.' , fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.Or);
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Verify the there are two chip expressions.
                let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2);
                expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(rootGroup, true).length).toBe(2);

                // Press 'Enter' on the remove icon of the second chip.
                const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1]);
                const removeIcon = ControlsFunction.getChipRemoveButton(chip);
                UIInteractions.triggerKeyDownEvtUponElem('Enter', removeIcon);
                  tick(200);
                fix.detectChanges();

                // Verify the there is only one chip expression.
                rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix);
                expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1);
                expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(rootGroup, true).length).toBe(1);
            }));

            it('Should select/deselect all child conditions and groups when pressing \'Enter\' on  a group\'s operator line.',
            fakeAsync(() => {
                // Apply advanced filter through API.
                const tree = new FilteringExpressionsTree(FilteringLogic.And);
                tree.filteringOperands.push({
                    fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan')
                });
                const orTree = new FilteringExpressionsTree(FilteringLogic.Or);
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                orTree.filteringOperands.push({
                    fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
                    ignoreCase: true
                });
                tree.filteringOperands.push(orTree);
                grid.advancedFilteringExpressionsTree = tree;
                fix.detectChanges();

                // Open Advanced Filtering dialog.
                grid.openAdvancedFilteringDialog();
                fix.detectChanges();

                // Press 'Enter' on the root group's operator line
                let rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                const keyboardEvent = new KeyboardEvent('keydown', { key: 'Enter' });
                rootOperatorLine.dispatchEvent(keyboardEvent);
                tick();
                fix.detectChanges();

                // Simulate end of chip selection animation
                const chipSelect = fix.nativeElement.querySelector(CHIP_SELECT_CLASS);
                const transitionEvent = new TransitionEvent('transitionend', {
                    propertyName: 'width'
                });
                chipSelect.dispatchEvent(transitionEvent);

                // Verify items are selected and context menu is opened
                verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), true);
                verifyContextMenuVisibility(fix, true);

                // Press 'Enter' on the root group's operator line again
                rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
                rootOperatorLine.dispatchEvent(keyboardEvent);
                tick();

                // Verify items are not selected and context menu is closed
                verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), false);
                verifyContextMenuVisibility(fix, false);
            }));

        });

    });

    describe('Localization - ', () => {
        it('Should correctly change resource strings for Advanced Filtering dialog.', fakeAsync(() => {
            const fix = TestBed.createComponent(IgxGridAdvancedFilteringComponent);
            const grid: IgxGridComponent = fix.componentInstance.grid;
            grid.resourceStrings = Object.assign({}, grid.resourceStrings, {
                igx_grid_filter_operator_and: 'My and',
                igx_grid_filter_operator_or: 'My or',
                igx_grid_advanced_filter_title: 'My advanced filter',
                igx_grid_advanced_filter_and_group: 'My and group',
                igx_grid_advanced_filter_or_group: 'My or group',
                igx_grid_advanced_filter_end_group: 'My end group',
                igx_grid_advanced_filter_create_and_group: 'My create and group',
                igx_grid_advanced_filter_create_or_group: 'My create or group',
                igx_grid_advanced_filter_and_label: 'My and',
                igx_grid_advanced_filter_or_label: 'My or',
                igx_grid_advanced_filter_add_condition: 'My condition',
                igx_grid_advanced_filter_ungroup: 'My ungroup',
                igx_grid_advanced_filter_delete: 'My delete',
                igx_grid_advanced_filter_delete_filters: 'My delete filters',
                igx_grid_advanced_filter_initial_text: 'My initial text'
            });
            fix.detectChanges();

            // Open Advanced Filtering dialog.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            expect(GridFunctions.getAdvancedFilteringHeaderText(fix)).toBe('My advanced filter');
            expect(GridFunctions.getAdvancedFilteringHeaderLegendItemAnd(fix).innerText).toBe('My and');
            expect(GridFunctions.getAdvancedFilteringHeaderLegendItemOr(fix).innerText).toBe('My or');
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix)[0].querySelector('span').innerText)
                .toBe('My and group');
            expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix)[1].querySelector('span').innerText)
                .toBe('My or group');
            expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix).innerText).toBe('My initial text');

            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0].querySelector('span').innerText)
                .toBe('My condition');
            expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[1].querySelector('span').innerText)
                .toBe('My and group');
            expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2].querySelector('span').innerText)
                .toBe('My or group');

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.

            let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix);
            rootOperatorLine.click();
            fix.detectChanges();

            const buttonGroupItems = GridFunctions.sortNativeElementsHorizontally(
                Array.from(GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix)
                                        .querySelectorAll('.igx-button-group__item-content')));
            expect(buttonGroupItems[0].textContent).toBe('My and');
            expect(buttonGroupItems[1].textContent).toBe('My or');
            expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3].querySelector('span').innerText)
                .toBe('My ungroup');
            expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[4].querySelector('span').innerText)
                .toBe('My delete');

            // Close context menu.
            GridFunctions.clickAdvancedFilteringContextMenuCloseButton(fix);
            fix.detectChanges();

            // Add another expression to root group.
            let btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0];
            btn.click();
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.

            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Select two chips.
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]);
            GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]);
            tick(200);
            fix.detectChanges();

            expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[1].innerText).toBe('My create and group');
            expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[2].innerText).toBe('My create or group');
            expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3].innerText).toBe('My delete filters');

            // Close context menu.
            GridFunctions.clickAdvancedFilteringContextMenuCloseButton(fix);
            tick(100);
            fix.detectChanges();

            // Add an 'or' group to root group.
            btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2];
            btn.click();
            tick(100);
            fix.detectChanges();

            const endGroupButton = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [2], 0)[3];
            expect(endGroupButton.querySelector('span').innerText).toBe('My end group');
        }));
    });

    describe('Column groups - ', () => {
        let fix; let grid: IgxGridComponent;
        beforeEach(fakeAsync(() => {
            fix = TestBed.createComponent(IgxGridAdvancedFilteringColumnGroupComponent);
            grid = fix.componentInstance.grid;
            fix.detectChanges();
        }));

        it('Should not display column groups in advanced filtering dialog.', fakeAsync(() => {
            // Open dialog through API.
            grid.openAdvancedFilteringDialog();
            fix.detectChanges();

            // Click the initial 'Add And Group' button.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Open column dropdown and verify that there are no column groups present.
            GridFunctions.clickAdvancedFilteringColumnSelect(fix);
            fix.detectChanges();
            const dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText);
            const expectedValues = ['ID', 'ProductName', 'Downloads', 'Released', 'ReleaseDate', 'Another Field'];
            expect(expectedValues).toEqual(dropdownValues);
        }));
    });

    describe('External - ', () => {
        let fix; let grid: IgxGridComponent;
        beforeEach(fakeAsync(() => {
            fix = TestBed.createComponent(IgxGridExternalAdvancedFilteringComponent);
            grid = fix.componentInstance.grid;
            fix.detectChanges();
        }));

        it('Should allow hosting Advanced Filtering dialog outside of the grid.', fakeAsync(() => {
            // Add a root 'and' group.
            GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0);
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column.
            selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator.
            let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, '100', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Add new expression to the root group.
            const addExpressionBtn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0];
            addExpressionBtn.click();
            tick(100);
            fix.detectChanges();

            // Populate edit inputs.
            selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column.
            selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator.
            input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input');
            UIInteractions.clickAndSendInputElementValue(input, 'ignite', fix); // Type filter value.
            // Commit the populated expression.
            GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix);
            fix.detectChanges();

            // Apply the filters.
            GridFunctions.clickAdvancedFilteringApplyButton(fix);
            tick(100);
            fix.detectChanges();

            // Verify the state of the grid after the filtering.
            expect(grid.advancedFilteringExpressionsTree).toBeTruthy();
            expect(grid.filteredData.length).toBe(1);
            expect(grid.rowList.length).toBe(1);
            expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
        }));

        it('Should allow hosting Advanced Filtering dialog outside of the hierarchical grid without any console errors.', fakeAsync(() => {
            fix = TestBed.createComponent(IgxHierGridExternalAdvancedFilteringComponent);
            const hgrid: IgxHierarchicalGridComponent = fix.componentInstance.hgrid;
            fix.detectChanges();
            spyOn(console, 'error');

            const advFilterDialog = fix.nativeElement.querySelector('.igx-advanced-filter');
            const applyFilterButton: any = Array.from(advFilterDialog.querySelectorAll('button'))
                .find((b: any) => b.innerText.toLowerCase() === 'apply');

            applyFilterButton.click();
            tick(100);
            fix.detectChanges();

            UIInteractions.simulatePointerEvent('pointerenter',
                hgrid.nativeElement.querySelectorAll('igx-hierarchical-grid-cell')[0], 5, 5);
            fix.detectChanges();

            expect(console.error).not.toHaveBeenCalled();
        }));

    });

    describe('Expression tree bindings - ', () => {
        let fix; let grid: IgxGridComponent;
        beforeEach(fakeAsync(() => {
            fix = TestBed.createComponent(IgxGridAdvancedFilteringBindingComponent);
            fix.detectChanges();
            grid = fix.componentInstance.grid;
        }));

        it('should correctly filter with \'advancedFilteringExpressionsTree\' binding', fakeAsync(() => {
            // Verify initially filtered in Advanced Filtering - 'Downloads > 200'
            expect(grid.filteredData.length).toEqual(3);
            expect(grid.rowList.length).toBe(3);

            // Verify filtering expressions tree binding state
            expect(grid.advancedFilteringExpressionsTree).toBe(fix.componentInstance.filterTree);

            // Clear filter
            grid.advancedFilteringExpressionsTree = null;
            fix.detectChanges();

            // Verify filtering expressions tree binding state
            expect(grid.advancedFilteringExpressionsTree).toBe(fix.componentInstance.filterTree);

            // Verify no filtered data
            expect(grid.filteredData).toBe(null);
            expect(grid.rowList.length).toBe(8);
        }));

        it('should correctly set filteredData if advancedFilteringExpressionsTree is empty', fakeAsync(() => {
            // Verify filtering expressions tree binding state
            expect(grid.advancedFilteringExpressionsTree).toBe(fix.componentInstance.filterTree);

            const tree = new FilteringExpressionsTree(FilteringLogic.And);
            tree.filteringOperands = [];

            // Clear filter
            grid.advancedFilteringExpressionsTree = tree;
            fix.detectChanges();

            // Verify filtering expressions tree binding state
            expect(grid.advancedFilteringExpressionsTree).toBe(tree);

            // Verify no filtered data
            expect(grid.filteredData).toBe(null);
        }));
    });
});

const selectColumnInEditModeExpression = (fix, dropdownItemIndex: number) => {
    GridFunctions.clickAdvancedFilteringColumnSelect(fix);
    fix.detectChanges();
    GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, dropdownItemIndex);
    tick();
    fix.detectChanges();
};

const selectOperatorInEditModeExpression = (fix, dropdownItemIndex: number) => {
    GridFunctions.clickAdvancedFilteringOperatorSelect(fix);
    fix.detectChanges();
    GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, dropdownItemIndex);
    tick();
    fix.detectChanges();
};

/**
 * Verifies the type of the operator line ('and' or 'or').
 * (NOTE: The 'operator' argument must be a string with a value that is either 'and' or 'or'.)
 */
const verifyOperatorLine = (operatorLine: HTMLElement, operator: string) => {
    expect(operator === 'and' || operator === 'or').toBe(true, 'operator must be \'and\' or \'or\'');

    if (operator === 'and') {
        expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS)).toBe(true, 'incorrect operator line');
        expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS)).toBe(false, 'incorrect operator line');
    } else {
        expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS)).toBe(false, 'incorrect operator line');
        expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS)).toBe(true, 'incorrect operator line');
    }
};

const verifyOperatorLineSelection = (operatorLine: HTMLElement, shouldBeSelected: boolean) => {
    expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_SELECTED_CSS_CLASS))
        .toBe(shouldBeSelected, 'incorrect selection state of the operator line');
};

const verifyExpressionChipContent = (fix, path: number[], columnText: string, operatorText: string, valueText: string) => {
    const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path);
    const chipSpans = GridFunctions.sortNativeElementsHorizontally(Array.from(chip.querySelectorAll('span')));
    const columnSpan = chipSpans[0];
    const operatorSpan = chipSpans[1];
    const valueSpan = chipSpans[2];
    expect(columnSpan.innerText.toLowerCase().trim()).toBe(columnText.toLowerCase(), 'incorrect chip column');
    expect(operatorSpan.innerText.toLowerCase().trim()).toBe(operatorText.toLowerCase(), 'incorrect chip operator');
    expect(valueSpan.innerText.toLowerCase().trim()).toBe(valueText.toLowerCase(), 'incorrect chip filter value');
};

const verifyExpressionChipSelection = (fix, path: number[], shouldBeSelected: boolean) => {
    const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path);
    verifyExpressionChipSelectionByChip(chip, shouldBeSelected);
};

const verifyExpressionChipSelectionByChip = (chip: HTMLElement, shouldBeSelected: boolean) => {
    const chipItem = chip.querySelector('.igx-chip__item');
    if (shouldBeSelected) {
        expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(true, 'chip is not selected');
        expect(chipItem.querySelector(CHIP_SELECT_CLASS)).not.toBeNull();
    } else {
        expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(false, 'chip is selected');
        expect(chipItem.querySelector(CHIP_SELECT_CLASS)).toBeNull();
    }
};

/**
 * Verifies that all children (operator lines and expression chips) of the provided 'parent' are selected.
 */
const verifyChildrenSelection = (parent: HTMLElement, shouldBeSelected: boolean) => {
    const allOperatorLines: any[] = Array.from(parent.querySelectorAll('.igx-filter-tree__line'));
    const allExpressionChips: any[] = Array.from(parent.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`));
    for (const operatorLine of allOperatorLines) {
        verifyOperatorLineSelection(operatorLine, shouldBeSelected);
    }
    for (const expressionChip of allExpressionChips) {
        verifyExpressionChipSelectionByChip(expressionChip, shouldBeSelected);
    }
};

const verifyEditModeExpressionInputStates = (fix,
                                             columnSelectEnabled: boolean,
                                             operatorSelectEnabled: boolean,
                                             valueInputEnabled: boolean,
                                             commitButtonEnabled: boolean) => {
    // Verify the column select state.
    const columnInputGroup = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('igx-input-group');
    expect(!columnInputGroup.classList.contains('igx-input-group--disabled')).toBe(columnSelectEnabled,
        'incorrect column select state');

    // Verify the operator select state.
    const operatorInputGroup = GridFunctions.getAdvancedFilteringOperatorSelect(fix).querySelector('igx-input-group');
    expect(!operatorInputGroup.classList.contains('igx-input-group--disabled')).toBe(operatorSelectEnabled,
        'incorrect operator select state');

    // Verify the value input state.
    const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix);
    const valueInputGroup = GridFunctions.sortNativeElementsHorizontally(
        Array.from(editModeContainer.querySelectorAll('igx-input-group')))[2];
    expect(!valueInputGroup.classList.contains('igx-input-group--disabled')).toBe(valueInputEnabled,
        'incorrect value input state');

    // Verify commit expression button state
    const commitButton = GridFunctions.getAdvancedFilteringExpressionCommitButton(fix);
    ControlsFunction.verifyButtonIsDisabled(commitButton, !commitButtonEnabled);


    // Verify close expression button is enabled.
    const closeButton = GridFunctions.getAdvancedFilteringExpressionCloseButton(fix);
    ControlsFunction.verifyButtonIsDisabled(closeButton, false);
};

const verifyEditModeExpressionInputValues = (fix,
                                             columnText: string,
                                             operatorText: string,
                                             valueText: string) => {
    const columnInput = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('input');
    const operatorInput = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('input');
    const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix);
    const valueInput = GridFunctions.sortNativeElementsHorizontally(Array.from(editModeContainer.querySelectorAll('input')))[2];
    expect(columnInput.value).toBe(columnText);
    expect(operatorInput.value).toBe(operatorText);
    expect(valueInput.value).toBe(valueText);
};

const verifyElementIsInExpressionsContainerView = (fix, element: HTMLElement) => {
    const elementRect = element.getBoundingClientRect();
    const exprContainer: HTMLElement = GridFunctions.getAdvancedFilteringExpressionsContainer(fix);
    const exprContainerRect = exprContainer.getBoundingClientRect();
    expect(elementRect.top >= exprContainerRect.top).toBe(true, 'top is not in view');
    expect(elementRect.bottom <= exprContainerRect.bottom).toBe(true, 'bottom is not in view');
    expect(elementRect.left >= exprContainerRect.left).toBe(true, 'left is not in view');
    expect(elementRect.right <= exprContainerRect.right).toBe(true, 'right is not in view');
};

const verifyContextMenuVisibility = (fix, shouldBeVisible: boolean) => {
    const contextMenu: HTMLElement = GridFunctions.getAdvancedFilteringContextMenu(fix);
    const contextMenuRect = contextMenu.getBoundingClientRect();
    expect(contextMenu.classList.contains('igx-toggle--hidden')).toBe(!shouldBeVisible, 'incorrect context menu visibility');
    expect(contextMenuRect.width === 0 && contextMenuRect.height === 0).toBe(!shouldBeVisible, 'incorrect context menu dimensions');
};

/**
 * Verifies the type of the context menu (menu for specific group or menu for combining expressions).
 * If contextual group is expected, the context menu should contain buttons for operator change, ungroup and delete.
 * If contextual group is not expected, the context menu should contain buttons for creating new group by combining expressions.
 */
const verifyContextMenuType = (fix, shouldBeContextualGroup: boolean) => {
    const contextMenuButtons =  GridFunctions.getAdvancedFilteringContextMenuButtons(fix);
    expect(GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix) !== null).toBe(shouldBeContextualGroup);

    if (shouldBeContextualGroup) {
        expect(contextMenuButtons.length).toBe(5, 'incorrect buttons count in context menu');
        expect(contextMenuButtons[0].innerText.toLowerCase()).toBe('close');
        expect(contextMenuButtons[3].querySelector('span').innerText.toLowerCase()).toBe('ungroup');
        expect(contextMenuButtons[4].querySelector('span').innerText.toLowerCase()).toBe('delete');
    } else {
        expect(contextMenuButtons.length).toBe(4, 'incorrect buttons count in context menu');
        expect(contextMenuButtons[0].innerText.toLowerCase()).toBe('close');
        expect(contextMenuButtons[1].innerText.toLowerCase().trim()).toBe('create "and" group');
        expect(contextMenuButtons[2].innerText.toLowerCase().trim()).toBe('create "or" group');
        expect(contextMenuButtons[3].innerText.toLowerCase().trim()).toBe('delete filters');
    }
};