Filtering of collection by item property tests added

This commit is contained in:
Vladimir Enchev
2025-10-14 14:24:27 +03:00
parent b08b3aa3b1
commit b95bf75dce
2 changed files with 1260 additions and 0 deletions

View File

@@ -2539,5 +2539,504 @@ namespace Radzen.Blazor.Tests
Assert.Contains("rz-grid-filter", component.Markup);
}
// Property/FilterProperty tests for filtering collection items
[Fact]
public void DataGrid_FiltersCollectionItems_WithSimpleMode()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Name = "Product1", Tags = new[] { new { Label = "new", Priority = 1 } } },
new { Id = 2, Name = "Product2", Tags = new[] { new { Label = "sale", Priority = 2 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Name");
builder.AddAttribute(2, "Title", "Name");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Simple);
});
Assert.Contains("rz-cell-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithSimpleWithMenuMode()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Name = "Order1", Items = new[] { new { Product = "A", Quantity = 5 } } },
new { Id = 2, Name = "Order2", Items = new[] { new { Product = "B", Quantity = 10 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Name");
builder.AddAttribute(2, "Title", "Name");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("rz-filter-button", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithAdvancedMode()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Name = "Customer1", Orders = new[] { new { OrderId = "A1", Total = 100 } } },
new { Id = 2, Name = "Customer2", Orders = new[] { new { OrderId = "B1", Total = 200 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Name");
builder.AddAttribute(2, "Title", "Name");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Advanced);
});
Assert.Contains("rz-grid-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_StringProperty_WithEquals()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "important", Value = 10 } } },
new { Id = 2, Tags = new[] { new { Name = "normal", Value = 20 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Equals", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_NumericProperty_WithComparison()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Items = new[] { new { Quantity = 5, Price = 100 } } },
new { Id = 2, Items = new[] { new { Quantity = 10, Price = 200 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Greater than", component.Markup);
Assert.Contains("Less than", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithStartsWithOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Files = new[] { new { Name = "file.txt", Size = 100 } } },
new { Id = 2, Files = new[] { new { Name = "image.png", Size = 200 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Advanced);
});
Assert.Contains("rz-grid-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithEndsWithOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Files = new[] { new { Name = "document.pdf", Extension = ".pdf" } } },
new { Id = 2, Files = new[] { new { Name = "photo.jpg", Extension = ".jpg" } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Advanced);
});
Assert.Contains("rz-grid-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithNotEqualsOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Statuses = new[] { new { Code = "active", Description = "Active" } } },
new { Id = 2, Statuses = new[] { new { Code = "inactive", Description = "Inactive" } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Not equals", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithIsNullOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = (string)null, Value = 10 } } },
new { Id = 2, Tags = new[] { new { Name = "tag2", Value = 20 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Is null", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithIsNotNullOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = (string)null, Value = 10 } } },
new { Id = 2, Tags = new[] { new { Name = "tag2", Value = 20 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Is not null", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithContainsOperator_InAllModes()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "important", Priority = 1 } } },
new { Id = 2, Tags = new[] { new { Name = "normal", Priority = 2 } } }
};
foreach (var mode in new[] { FilterMode.Simple, FilterMode.SimpleWithMenu, FilterMode.Advanced })
{
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, mode);
});
Assert.Contains("rz-", component.Markup);
}
}
[Fact]
public void DataGrid_FiltersCollectionItems_NumericProperty_WithAllComparisons()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Scores = new[] { new { Value = 90, Subject = "Math" } } },
new { Id = 2, Scores = new[] { new { Value = 75, Subject = "Science" } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.SimpleWithMenu);
});
Assert.Contains("Less than or equals", component.Markup);
Assert.Contains("Greater than or equals", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_WithCaseInsensitiveFilter()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Label = "IMPORTANT" } } },
new { Id = 2, Tags = new[] { new { Label = "normal" } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Simple);
parameterBuilder.Add<FilterCaseSensitivity>(p => p.FilterCaseSensitivity, FilterCaseSensitivity.CaseInsensitive);
});
Assert.Contains("rz-cell-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_MultipleFilters_WithAndOperator()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Name = "Product1", Tags = new[] { new { Label = "premium", Price = 100 } } },
new { Id = 2, Name = "Product2", Tags = new[] { new { Label = "standard", Price = 50 } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Name");
builder.AddAttribute(2, "Title", "Name");
builder.CloseComponent();
builder.OpenComponent(3, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(4, "Property", "Id");
builder.AddAttribute(5, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Advanced);
parameterBuilder.Add<LogicalFilterOperator>(p => p.LogicalFilterOperator, LogicalFilterOperator.And);
});
Assert.Contains("rz-grid-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_NestedProperty()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Details = new { Category = "A" } } } },
new { Id = 2, Items = new[] { new { Name = "item2", Details = new { Category = "B" } } } }
};
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Advanced);
});
Assert.Contains("rz-grid-filter", component.Markup);
}
[Fact]
public void DataGrid_FiltersCollectionItems_LoadDataEvent_TriggeredOnFilter()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1" } } },
new { Id = 2, Tags = new[] { new { Name = "tag2" } } }
};
var raised = false;
var component = ctx.RenderComponent<RadzenDataGrid<dynamic>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<dynamic>>(p => p.Data, testData);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<dynamic>));
builder.AddAttribute(1, "Property", "Id");
builder.AddAttribute(2, "Title", "Id");
builder.CloseComponent();
});
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.Simple);
parameterBuilder.Add<LoadDataArgs>(p => p.LoadData, args => { raised = true; });
});
var filterInput = component.Find("input");
filterInput.Change("1");
Assert.True(raised);
}
}
}

View File

@@ -928,6 +928,767 @@ namespace Radzen.Blazor.Tests
Assert.Equal(3, result.Count);
}
// Property/FilterProperty tests for collection item filtering
[Fact]
public void Where_FiltersCollectionItemProperty_WithEquals()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag1", Value = 50 }, new { Name = "tag5", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "tag1",
FilterOperator = FilterOperator.Equals
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithContains()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "important", Value = 10 }, new { Name = "urgent", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "normal", Value = 30 }, new { Name = "low", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "important", Value = 50 }, new { Name = "high", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "import",
FilterOperator = FilterOperator.Contains
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithStartsWith()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag_alpha", Value = 10 }, new { Name = "tag_beta", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "item_gamma", Value = 30 }, new { Name = "item_delta", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag_epsilon", Value = 50 }, new { Name = "other", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "tag_",
FilterOperator = FilterOperator.StartsWith
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithEndsWith()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "file.txt", Value = 10 }, new { Name = "doc.pdf", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "image.png", Value = 30 }, new { Name = "photo.jpg", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "data.txt", Value = 50 }, new { Name = "info.doc", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = ".txt",
FilterOperator = FilterOperator.EndsWith
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithNotEquals()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "active", Value = 10 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "inactive", Value = 20 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "pending", Value = 30 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "inactive",
FilterOperator = FilterOperator.NotEquals
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithDoesNotContain()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "important-task", Value = 10 }, new { Name = "important-note", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "simple", Value = 30 }, new { Name = "basic", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "important-meeting", Value = 50 }, new { Name = "review", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "important",
FilterOperator = FilterOperator.DoesNotContain
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 2);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_Numeric_WithGreaterThan()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 40,
FilterOperator = FilterOperator.GreaterThan
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(3, result[0].Id);
}
[Fact]
public void Where_FiltersCollectionItemProperty_Numeric_WithLessThan()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 25,
FilterOperator = FilterOperator.LessThan
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(1, result[0].Id);
}
[Fact]
public void Where_FiltersCollectionItemProperty_Numeric_WithGreaterThanOrEquals()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 50,
FilterOperator = FilterOperator.GreaterThanOrEquals
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(3, result[0].Id);
}
[Fact]
public void Where_FiltersCollectionItemProperty_Numeric_WithLessThanOrEquals()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 20,
FilterOperator = FilterOperator.LessThanOrEquals
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(1, result[0].Id);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithIsNull()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = (string)null, Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = (string)null, Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterOperator = FilterOperator.IsNull
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithIsNotNull()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = (string)null, Value = 10 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterOperator = FilterOperator.IsNotNull
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 2);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_CaseInsensitive()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "IMPORTANT", Value = 10 }, new { Name = "urgent", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "normal", Value = 30 }, new { Name = "low", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "Important", Value = 50 }, new { Name = "high", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterValue = "important",
FilterOperator = FilterOperator.Contains
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.CaseInsensitive).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_MultipleConditions_WithAnd()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 15 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag1", Value = 50 }, new { Name = "tag5", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 20,
FilterOperator = FilterOperator.LessThan,
SecondFilterValue = 10,
SecondFilterOperator = FilterOperator.GreaterThan,
LogicalFilterOperator = LogicalFilterOperator.And
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
// Only items with any tag Value < 20 AND any tag Value > 10
Assert.Single(result);
Assert.Equal(1, result[0].Id);
}
[Fact]
public void Where_FiltersCollectionItemProperty_MultipleConditions_WithOr()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "tag1", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 70 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag1", Value = 50 }, new { Name = "tag5", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Value",
FilterValue = 20,
FilterOperator = FilterOperator.LessThan,
SecondFilterValue = 60,
SecondFilterOperator = FilterOperator.GreaterThan,
LogicalFilterOperator = LogicalFilterOperator.Or
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
// Items with tags where Value<20 OR Value>60
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 2);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithIsEmpty()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "", Value = 10 }, new { Name = "tag2", Value = 20 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 }, new { Name = "tag4", Value = 40 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "", Value = 50 }, new { Name = "tag6", Value = 60 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterOperator = FilterOperator.IsEmpty
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_WithIsNotEmpty()
{
var testData = new[]
{
new { Id = 1, Tags = new[] { new { Name = "", Value = 10 } }.ToList() },
new { Id = 2, Tags = new[] { new { Name = "tag3", Value = 30 } }.ToList() },
new { Id = 3, Tags = new[] { new { Name = "tag5", Value = 50 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Name",
FilterOperator = FilterOperator.IsNotEmpty
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 2);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersCollectionItemProperty_CombinedWithRegularFilter()
{
var testData = new[]
{
new { Id = 1, Name = "Product1", Tags = new[] { new { Label = "premium", Price = 100 } }.ToList() },
new { Id = 2, Name = "Product2", Tags = new[] { new { Label = "standard", Price = 50 } }.ToList() },
new { Id = 3, Name = "Product3", Tags = new[] { new { Label = "premium", Price = 150 } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Tags",
FilterProperty = "Label",
FilterValue = "premium",
FilterOperator = FilterOperator.Equals
},
new FilterDescriptor
{
Property = "Name",
FilterValue = "Product1",
FilterOperator = FilterOperator.Contains
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(1, result[0].Id);
}
[Fact]
public void Where_FiltersNestedCollectionItemProperty()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new { Category = "A" } } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new { Category = "B" } } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new { Category = "A" } } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta.Category",
FilterValue = "A",
FilterOperator = FilterOperator.Equals
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersNestedCollectionItemProperty_WithIn()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new { Category = "A" } } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new { Category = "B" } } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new { Category = "C" } } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta.Category",
FilterValue = new[] { "A", "C" },
FilterOperator = FilterOperator.In
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersNestedCollectionItemProperty_WithNotIn()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new { Category = "A" } } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new { Category = "B" } } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new { Category = "C" } } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta.Category",
FilterValue = new[] { "A" },
FilterOperator = FilterOperator.NotIn
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 2);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersNestedCollectionItemProperty_WithContains()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new { Category = "Alpha" } } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new { Category = "Beta" } } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new { Category = "Alpine" } } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta.Category",
FilterValue = "Al",
FilterOperator = FilterOperator.Contains
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersNestedCollectionItemProperty_WithDoesNotContain()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new { Category = "Entertainment" } } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new { Category = "Sports" } } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new { Category = "Edutainment" } } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta.Category",
FilterValue = "tain",
FilterOperator = FilterOperator.DoesNotContain
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
// Excludes categories containing "tain" -> Entertainment, Edutainment
Assert.Single(result);
Assert.Equal(2, result[0].Id);
}
// Nested collection (Items) with Meta also a collection. Use indexed access to Meta[0].Category
[Fact]
public void Where_FiltersDoubleNested_WithIn_OnFirstMeta()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new[] { new { Category = "A" }, new { Category = "X" } }.ToList() } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new[] { new { Category = "B" } }.ToList() } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new[] { new { Category = "C" } }.ToList() } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta[0].Category",
FilterValue = new[] { "A", "C" },
FilterOperator = FilterOperator.In
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersDoubleNested_WithNotIn_OnFirstMeta()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new[] { new { Category = "A" } }.ToList() } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new[] { new { Category = "B" } }.ToList() } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new[] { new { Category = "C" } }.ToList() } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta[0].Category",
FilterValue = new[] { "A" },
FilterOperator = FilterOperator.NotIn
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 2);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersDoubleNested_WithContains_OnFirstMeta()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new[] { new { Category = "Alpha" } }.ToList() } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new[] { new { Category = "Beta" } }.ToList() } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new[] { new { Category = "Alpine" } }.ToList() } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta[0].Category",
FilterValue = "Al",
FilterOperator = FilterOperator.Contains
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Equal(2, result.Count);
Assert.Contains(result, r => r.Id == 1);
Assert.Contains(result, r => r.Id == 3);
}
[Fact]
public void Where_FiltersDoubleNested_WithDoesNotContain_OnFirstMeta()
{
var testData = new[]
{
new { Id = 1, Items = new[] { new { Name = "item1", Meta = new[] { new { Category = "Entertainment" } }.ToList() } }.ToList() },
new { Id = 2, Items = new[] { new { Name = "item2", Meta = new[] { new { Category = "Sports" } }.ToList() } }.ToList() },
new { Id = 3, Items = new[] { new { Name = "item3", Meta = new[] { new { Category = "Edutainment" } }.ToList() } }.ToList() }
}.AsQueryable();
var filters = new List<FilterDescriptor>
{
new FilterDescriptor
{
Property = "Items",
FilterProperty = "Meta[0].Category",
FilterValue = "tain",
FilterOperator = FilterOperator.DoesNotContain
}
};
var result = testData.Where(filters, LogicalFilterOperator.And, FilterCaseSensitivity.Default).ToList();
Assert.Single(result);
Assert.Equal(2, result[0].Id);
}
}
}