/* eslint-disable no-restricted-globals */
import { aTimeout, elementUpdated, expect, fixture } from '@open-wc/testing';
import { html } from 'lit';
import sinon from 'sinon';
import { Filter, IxGridRowFilter } from '../components/IxGridRowFilter.js';
const columns = [
{
name: 'firstName',
header: 'First name',
filterable: true,
filterOperators: ['contains', 'equals'],
},
{
name: 'lastName',
header: 'Last name',
filterable: true,
filterOperators: ['contains', 'equals'],
},
{
name: 'middleName',
header: 'Middle name',
filterable: false,
filterOperators: ['contains', 'equals'],
},
{
name: 'email',
header: 'Email',
filterable: true,
filterOperators: ['contains', 'equals'],
},
];
const filters: Filter[] = [
{
columnField: 'firstName',
operatorValue: 'contains',
value: 'John',
},
];
describe('IxGridRowFilter', () => {
it('should render the grid row filter', async () => {
const el = await fixture(
html``
);
expect(el).to.be.instanceOf(IxGridRowFilter);
});
it('renders and popluates the grid row filter', async () => {
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLInputElement;
openMenu.click();
await elementUpdated(el);
const columnOptions = el.shadowRoot?.querySelectorAll(
'select[data-v="firstName"] option'
);
expect(columnOptions?.length).to.equal(3);
const filterOperatorInput = el.shadowRoot?.querySelector(
'select.filterOperatorInput'
) as HTMLSelectElement;
filterOperatorInput.value = 'equals';
filterOperatorInput.dispatchEvent(
new Event('change', {
bubbles: true,
cancelable: true,
})
);
await elementUpdated(el);
const selectedOperator = filterOperatorInput.querySelector(
'option[selected]'
) as HTMLOptionElement;
expect(selectedOperator?.value).to.equal('equals');
});
it('should debounce the filter input', async () => {
const debounceTime = 100;
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLInputElement;
openMenu.click();
await elementUpdated(el);
const filterInput = el.shadowRoot?.querySelector(
'div.filterValueField input'
) as HTMLInputElement;
const debouncedOnFilterValueChangeSpy = sinon.spy(
el,
'debouncedOnFilterValueChange'
);
const onFilterValueChangeSpy = sinon.spy(el, 'onfilterValueChange');
for (let i = 0; i < 5; i += 1) {
filterInput.dispatchEvent(
new InputEvent('input', {
bubbles: true,
cancelable: true,
composed: true,
})
);
}
expect(debouncedOnFilterValueChangeSpy).to.have.been.called;
expect(onFilterValueChangeSpy).to.not.have.been.called;
await aTimeout(debounceTime);
expect(onFilterValueChangeSpy).to.have.been.calledOnce;
});
describe('Filter type input', () => {
it('should render a string input control for non-supplied data type', async () => {
const columnsWithDataType = [
{
name: 'firstName',
header: 'First name',
filterable: true,
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const filterInput = el.shadowRoot?.querySelector(
'div.filterValueField input'
);
expect(filterInput).to.exist;
expect(filterInput).to.be.instanceOf(HTMLInputElement);
});
it('should render a string input control for string data type', async () => {
const columnsWithDataType = [
{
name: 'firstName',
header: 'First name',
filterable: true,
dataType: 'string',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const filterInput = el.shadowRoot?.querySelector(
'div.filterValueField input'
);
expect(filterInput).to.exist;
expect(filterInput).to.be.instanceOf(HTMLInputElement);
});
it('should render a date input control for dateTime data type', async () => {
const columnsWithDataType = [
{
name: 'createdDate',
header: 'Created Date',
filterable: true,
dataType: 'dateTime',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const dateInput = el.shadowRoot?.querySelector(
'div.filterValueField ix-date'
);
expect(dateInput).to.exist;
expect(dateInput).to.be.instanceOf(HTMLElement);
});
it('should render nothing when an unknown data type is used', async () => {
const columnsWithDataType = [
{
name: 'unknownTypeField',
header: 'Unknown Type',
filterable: true,
dataType: 'unknownType',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const inputField = el.shadowRoot?.querySelector(
'div.filterValueField input'
);
expect(inputField).to.be.null;
});
it('should build filter from URL when readParamsFromURL is True', async () => {
const columnsWithDataType = [
{
name: 'firstName',
header: 'First name',
filterable: true,
dataType: 'string',
filterOperators: ['contains', 'equals'],
},
{
name: 'lastName',
header: 'Last Name',
filterable: true,
dataType: 'string',
filterOperators: ['equals'],
},
{
name: 'createdDate',
header: 'Created Date',
filterable: true,
dataType: 'dateTime',
filterOperators: ['equals'],
},
];
const el = (await fixture(
html``
)) as any;
const url = 'firstName_contains=John&createdDate_equals=2021-09-01';
history.pushState(null, '', `${location.pathname}?${url}`);
el.firstUpdated();
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const formFilters = el.shadowRoot?.querySelectorAll('.filter-form');
const filter1Name = formFilters[0]
.querySelector('.filterColumnField')
.querySelector('select')
.attributes.getNamedItem('data-v')?.value;
const filter1Operator = formFilters[0]
.querySelector('.filterOperatorField')
.querySelector('select')
.attributes.getNamedItem('data-v')?.value;
const filter1Value = formFilters[0]
.querySelector('.filterValueField')
.querySelector('input').value;
const filter2Name = formFilters[1]
.querySelector('.filterColumnField')
.querySelector('select')
.attributes.getNamedItem('data-v')?.value;
const filter2Operator = formFilters[1]
.querySelector('.filterOperatorField')
.querySelector('select')
.attributes.getNamedItem('data-v')?.value;
const filter2Value = formFilters[1]
.querySelector('.filterValueField')
.querySelector('ix-date').value;
expect(formFilters.length).to.be.eq(2);
expect(filter1Name).to.be.eq('firstName');
expect(filter1Operator).to.be.eq('contains');
expect(filter1Value).to.be.eq('John');
expect(filter2Name).to.be.eq('createdDate');
expect(filter2Operator).to.be.eq('equals');
expect(filter2Value).to.be.eq('2021-09-01');
});
it('should use maxDate for date inputs if provided', async () => {
const columnsWithDataType = [
{
name: 'dateField',
header: 'Date Field',
filterable: true,
filterOperators: ['='],
dataType: 'dateTime',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const dateInput = el.shadowRoot?.querySelector(
'div.filterValueField ix-date'
);
expect(dateInput).to.exist;
expect(dateInput?.getAttribute('max')).to.equal('2025-12-31');
});
it('should fallback to default maxDate if maxDate is not provided', async () => {
const columnsWithDataType = [
{
name: 'dateField',
header: 'Date Field',
filterable: true,
filterOperators: ['='],
dataType: 'dateTime',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const dateInput = el.shadowRoot?.querySelector(
'div.filterValueField ix-date'
);
expect(dateInput).to.exist;
const today = new Date().toISOString().split('T')[0];
expect(dateInput?.getAttribute('max')).to.equal(today);
});
it('should render ix-date-next if "useNewDatePicker" is true', async () => {
const columnsWithDataType = [
{
name: 'dateField',
header: 'Date Field',
filterable: true,
filterOperators: ['='],
dataType: 'dateTime',
},
];
const el = await fixture(
html``
);
const openMenu = el.shadowRoot?.querySelector(
'.filter-button'
) as HTMLElement;
openMenu?.click();
await elementUpdated(el);
const dateInput = el.shadowRoot?.querySelector(
'div.filterValueField ix-date-next'
);
expect(dateInput).to.exist;
});
});
describe('Tooltip', () => {
it('should display the column header in tooltip instead of formatted name', async () => {
const columnsWithDifferentNameAndHeader = [
{
name: 'accessRequestedFor',
header: 'Access Requested For',
filterable: true,
filterOperators: ['contains', 'equals'],
},
];
const filtersWithValue: Filter[] = [
{
columnField: 'accessRequestedFor',
operatorValue: 'contains',
value: 'John',
},
];
const el = (await fixture(
html``
)) as IxGridRowFilter;
(el as any).filters = filtersWithValue;
el.updateActiveFilters();
await elementUpdated(el);
const tooltip = el.renderToolTip();
const tooltipHtml = (tooltip as any).strings.join('');
expect(tooltipHtml).to.not.include('Access requested for');
});
it('should fallback to formatted name when column header is not found', async () => {
const el = (await fixture(
html``
)) as IxGridRowFilter;
(el as any).filters = [
{
columnField: 'nonExistentColumn',
operatorValue: 'contains',
value: 'test',
},
];
el.updateActiveFilters();
await elementUpdated(el);
const tooltip = el.renderToolTip();
expect(tooltip).to.exist;
});
});
});