Add -ExcludeProperty parameter to Format-* cmdlets (#26514)

This commit is contained in:
Yoshifumi
2025-12-05 04:54:52 +00:00
committed by GitHub
parent 5c57c3e19c
commit 6311c7c2f6
16 changed files with 404 additions and 127 deletions
@@ -41,6 +41,12 @@ namespace Microsoft.PowerShell.Commands
private object[] _props;
/// <summary>
/// Gets or sets the properties to exclude from formatting.
/// </summary>
[Parameter]
public string[] ExcludeProperty { get; set; }
/// <summary>
/// </summary>
/// <value></value>
@@ -61,6 +67,18 @@ namespace Microsoft.PowerShell.Commands
{
FormattingCommandLineParameters parameters = new();
// Check View conflicts first (before any auto-expansion)
if (!string.IsNullOrEmpty(this.View))
{
// View cannot be used with Property or ExcludeProperty
if ((_props is not null && _props.Length != 0) || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
{
ReportCannotSpecifyViewAndProperty();
}
parameters.viewName = this.View;
}
if (_props != null)
{
ParameterProcessor processor = new(new FormatObjectParameterDefinition());
@@ -68,15 +86,17 @@ namespace Microsoft.PowerShell.Commands
parameters.mshParameterList = processor.ProcessParameters(_props, invocationContext);
}
if (!string.IsNullOrEmpty(this.View))
if (ExcludeProperty is not null)
{
// we have a view command line switch
if (parameters.mshParameterList.Count != 0)
{
ReportCannotSpecifyViewAndProperty();
}
parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
parameters.viewName = this.View;
// ExcludeProperty implies -Property * for better UX
if (_props is null || _props.Length == 0)
{
ParameterProcessor processor = new(new FormatObjectParameterDefinition());
TerminatingErrorContext invocationContext = new(this);
parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
}
}
parameters.groupByParameter = this.ProcessGroupByParameter();
@@ -42,9 +42,14 @@ namespace Microsoft.PowerShell.Commands
private object _prop;
/// <summary>
/// Optional, non positional parameter.
/// Gets or sets the properties to exclude from formatting.
/// </summary>
[Parameter]
public string[] ExcludeProperty { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to autosize the output.
/// </summary>
/// <value></value>
[Parameter]
public SwitchParameter AutoSize
{
@@ -74,6 +79,18 @@ namespace Microsoft.PowerShell.Commands
{
FormattingCommandLineParameters parameters = new();
// Check View conflicts first (before any auto-expansion)
if (!string.IsNullOrEmpty(this.View))
{
// View cannot be used with Property or ExcludeProperty
if (_prop is not null || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
{
ReportCannotSpecifyViewAndProperty();
}
parameters.viewName = this.View;
}
if (_prop != null)
{
ParameterProcessor processor = new(new FormatWideParameterDefinition());
@@ -81,15 +98,17 @@ namespace Microsoft.PowerShell.Commands
parameters.mshParameterList = processor.ProcessParameters(new object[] { _prop }, invocationContext);
}
if (!string.IsNullOrEmpty(this.View))
if (ExcludeProperty is not null)
{
// we have a view command line switch
if (parameters.mshParameterList.Count != 0)
{
ReportCannotSpecifyViewAndProperty();
}
parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
parameters.viewName = this.View;
// ExcludeProperty implies -Property * for better UX
if (_prop is null)
{
ParameterProcessor processor = new(new FormatWideParameterDefinition());
TerminatingErrorContext invocationContext = new(this);
parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
}
}
// we cannot specify -column and -autosize, they are mutually exclusive
@@ -12,47 +12,6 @@ using Microsoft.PowerShell.Commands.Internal.Format;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Helper class to do wildcard matching on PSPropertyExpressions.
/// </summary>
internal sealed class PSPropertyExpressionFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="PSPropertyExpressionFilter"/> class
/// with the specified array of patterns.
/// </summary>
/// <param name="wildcardPatternsStrings">Array of pattern strings to use.</param>
internal PSPropertyExpressionFilter(string[] wildcardPatternsStrings)
{
ArgumentNullException.ThrowIfNull(wildcardPatternsStrings);
_wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length];
for (int k = 0; k < wildcardPatternsStrings.Length; k++)
{
_wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase);
}
}
/// <summary>
/// Try to match the expression against the array of wildcard patterns.
/// The first match shortcircuits the search.
/// </summary>
/// <param name="expression">PSPropertyExpression to test against.</param>
/// <returns>True if there is a match, else false.</returns>
internal bool IsMatch(PSPropertyExpression expression)
{
for (int k = 0; k < _wildcardPatterns.Length; k++)
{
if (_wildcardPatterns[k].IsMatch(expression.ToString()))
return true;
}
return false;
}
private readonly WildcardPattern[] _wildcardPatterns;
}
internal sealed class SelectObjectExpressionParameterDefinition : CommandParameterDefinition
{
protected override void SetEntries()
@@ -9,6 +9,7 @@ using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell.Commands;
namespace Microsoft.PowerShell.Commands.Internal.Format
{
@@ -729,6 +730,12 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
[Parameter(Position = 0)]
public object[] Property { get; set; }
/// <summary>
/// Optional parameter for excluding properties from formatting.
/// </summary>
[Parameter]
public string[] ExcludeProperty { get; set; }
#endregion
internal override FormattingCommandLineParameters GetCommandLineParameters()
@@ -751,6 +758,18 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
internal void GetCommandLineProperties(FormattingCommandLineParameters parameters, bool isTable)
{
// Check View conflicts first (before any auto-expansion)
if (!string.IsNullOrEmpty(this.View))
{
// View cannot be used with Property or ExcludeProperty
if ((Property is not null && Property.Length != 0) || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
{
ReportCannotSpecifyViewAndProperty();
}
parameters.viewName = this.View;
}
if (Property != null)
{
CommandParameterDefinition def;
@@ -765,15 +784,21 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
parameters.mshParameterList = processor.ProcessParameters(Property, invocationContext);
}
if (!string.IsNullOrEmpty(this.View))
if (ExcludeProperty is not null)
{
// we have a view command line switch
if (parameters.mshParameterList.Count != 0)
{
ReportCannotSpecifyViewAndProperty();
}
parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
parameters.viewName = this.View;
// ExcludeProperty implies -Property * for better UX
if (Property is null || Property.Length == 0)
{
CommandParameterDefinition def = isTable
? new FormatTableParameterDefinition()
: new FormatListParameterDefinition();
ParameterProcessor processor = new ParameterProcessor(def);
TerminatingErrorContext invocationContext = new TerminatingErrorContext(this);
parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
}
}
}
}
@@ -70,6 +70,11 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// Extension mechanism for shape specific parameters.
/// </summary>
internal ShapeSpecificParameters shapeParameters = null;
/// <summary>
/// Filter for excluding properties from formatting.
/// </summary>
internal PSPropertyExpressionFilter excludePropertyFilter = null;
}
/// <summary>
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Internal;
@@ -348,7 +349,19 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
protected DataBaseInfo dataBaseInfo = new DataBaseInfo();
protected List<MshResolvedExpressionParameterAssociation> activeAssociationList = null;
protected FormattingCommandLineParameters inputParameters = null;
/// <summary>
/// Apply ExcludeProperty filter to activeAssociationList if specified.
/// This method filters and updates "activeAssociationList" instance property.
/// </summary>
protected void ApplyExcludePropertyFilter()
{
if (this.parameters is not null && this.parameters.excludePropertyFilter is not null)
{
this.activeAssociationList = this.activeAssociationList
.Where(item => !this.parameters.excludePropertyFilter.IsMatch(item.ResolvedExpression))
.ToList();
}
}
protected string GetExpressionDisplayValue(PSObject so, int enumerationLimit, PSPropertyExpression ex,
FieldFormattingDirective directive)
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Internal;
@@ -16,7 +17,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
PSObject so, TypeInfoDataBase db, FormattingCommandLineParameters parameters)
{
base.Initialize(errorContext, expressionFactory, so, db, parameters);
this.inputParameters = parameters;
}
internal override FormatStartData GenerateStartData(PSObject so)
@@ -40,7 +40,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
private ComplexViewEntry GenerateComplexViewEntryFromProperties(PSObject so, int enumerationLimit)
{
ComplexViewObjectBrowser browser = new ComplexViewObjectBrowser(this.ErrorManager, this.expressionFactory, enumerationLimit);
return browser.GenerateView(so, this.inputParameters);
return browser.GenerateView(so, this.parameters);
}
private ComplexViewEntry GenerateComplexViewEntryFromDataBaseInfo(PSObject so, int enumerationLimit)
@@ -425,17 +425,18 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// of the object.
/// </summary>
/// <param name="so">Object to process.</param>
/// <param name="inputParameters">Parameters from the command line.</param>
/// <param name="parameters">Parameters from the command line.</param>
/// <returns>Complex view entry to send to the output command.</returns>
internal ComplexViewEntry GenerateView(PSObject so, FormattingCommandLineParameters inputParameters)
internal ComplexViewEntry GenerateView(PSObject so, FormattingCommandLineParameters parameters)
{
_complexSpecificParameters = (ComplexSpecificParameters)inputParameters.shapeParameters;
_parameters = parameters;
_complexSpecificParameters = (ComplexSpecificParameters)parameters.shapeParameters;
int maxDepth = _complexSpecificParameters.maxDepth;
TraversalInfo level = new TraversalInfo(0, maxDepth);
List<MshParameter> mshParameterList = null;
mshParameterList = inputParameters.mshParameterList;
mshParameterList = parameters.mshParameterList;
// create a top level entry as root of the tree
ComplexViewEntry cve = new ComplexViewEntry();
@@ -513,6 +514,14 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
List<MshResolvedExpressionParameterAssociation> activeAssociationList =
AssociationManager.SetupActiveProperties(parameterList, so, _expressionFactory);
// Apply ExcludeProperty filter if specified
if (_parameters != null && _parameters.excludePropertyFilter != null)
{
activeAssociationList = activeAssociationList
.Where(item => !_parameters.excludePropertyFilter.IsMatch(item.ResolvedExpression))
.ToList();
}
// create a format entry
FormatEntry fe = new FormatEntry();
formatValueList.Add(fe);
@@ -758,6 +767,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
return feFrame.formatValueList;
}
private FormattingCommandLineParameters _parameters;
private ComplexSpecificParameters _complexSpecificParameters;
/// <summary>
@@ -31,7 +31,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
_listBody = (ListControlBody)this.dataBaseInfo.view.mainControl;
}
this.inputParameters = parameters;
this.parameters = parameters;
SetUpActiveProperties(so);
}
@@ -227,10 +227,12 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
{
List<MshParameter> mshParameterList = null;
if (this.inputParameters != null)
mshParameterList = this.inputParameters.mshParameterList;
if (this.parameters != null)
mshParameterList = this.parameters.mshParameterList;
this.activeAssociationList = AssociationManager.SetupActiveProperties(mshParameterList, so, this.expressionFactory);
ApplyExcludePropertyFilter();
}
}
}
@@ -40,40 +40,45 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
rawMshParameterList = parameters.mshParameterList;
// check if we received properties from the command line
if (rawMshParameterList != null && rawMshParameterList.Count > 0)
if (rawMshParameterList is not null && rawMshParameterList.Count > 0)
{
this.activeAssociationList = AssociationManager.ExpandTableParameters(rawMshParameterList, so);
return;
}
// we did not get any properties:
// try to get properties from the default property set of the object
this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
if (this.activeAssociationList.Count > 0)
else
{
// we got a valid set of properties from the default property set..add computername for
// remoteobjects (if available)
if (PSObjectHelper.ShouldShowComputerNameProperty(so))
// we did not get any properties:
// try to get properties from the default property set of the object
this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
if (this.activeAssociationList.Count > 0)
{
activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null,
new PSPropertyExpression(RemotingConstants.ComputerNameNoteProperty)));
// we got a valid set of properties from the default property set..add computername for
// remoteobjects (if available)
if (PSObjectHelper.ShouldShowComputerNameProperty(so))
{
activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null,
new PSPropertyExpression(RemotingConstants.ComputerNameNoteProperty)));
}
}
else
{
// we failed to get anything from the default property set
this.activeAssociationList = AssociationManager.ExpandAll(so);
if (this.activeAssociationList.Count > 0)
{
// Remove PSComputerName and PSShowComputerName from the display as needed.
AssociationManager.HandleComputerNameProperties(so, activeAssociationList);
FilterActiveAssociationList();
}
else
{
// we were unable to retrieve any properties, so we leave an empty list
this.activeAssociationList = new List<MshResolvedExpressionParameterAssociation>();
return;
}
}
return;
}
// we failed to get anything from the default property set
this.activeAssociationList = AssociationManager.ExpandAll(so);
if (this.activeAssociationList.Count > 0)
{
// Remove PSComputerName and PSShowComputerName from the display as needed.
AssociationManager.HandleComputerNameProperties(so, activeAssociationList);
FilterActiveAssociationList();
return;
}
// we were unable to retrieve any properties, so we leave an empty list
this.activeAssociationList = new List<MshResolvedExpressionParameterAssociation>();
ApplyExcludePropertyFilter();
}
/// <summary>
@@ -13,7 +13,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
PSObject so, TypeInfoDataBase db, FormattingCommandLineParameters parameters)
{
base.Initialize(errorContext, expressionFactory, so, db, parameters);
this.inputParameters = parameters;
}
internal override FormatStartData GenerateStartData(PSObject so)
@@ -159,39 +158,37 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
private void SetUpActiveProperty(PSObject so)
{
List<MshParameter> rawMshParameterList = null;
if (this.inputParameters != null)
rawMshParameterList = this.inputParameters.mshParameterList;
List<MshParameter> rawMshParameterList = this.parameters?.mshParameterList;
// check if we received properties from the command line
if (rawMshParameterList != null && rawMshParameterList.Count > 0)
if (rawMshParameterList is not null && rawMshParameterList.Count > 0)
{
this.activeAssociationList = AssociationManager.ExpandParameters(rawMshParameterList, so);
return;
}
// we did not get any properties:
// try to get the display property of the object
PSPropertyExpression displayNameExpression = PSObjectHelper.GetDisplayNameExpression(so, this.expressionFactory);
if (displayNameExpression != null)
else
{
this.activeAssociationList = new List<MshResolvedExpressionParameterAssociation>();
this.activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null, displayNameExpression));
return;
// we did not get any properties:
// try to get the display property of the object
PSPropertyExpression displayNameExpression = PSObjectHelper.GetDisplayNameExpression(so, this.expressionFactory);
if (displayNameExpression is not null)
{
this.activeAssociationList = new List<MshResolvedExpressionParameterAssociation>();
this.activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null, displayNameExpression));
}
else
{
// try to get the default property set (we will use the first property)
this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
if (this.activeAssociationList.Count == 0)
{
// we failed to get anything from the default property set
// just get all the properties
this.activeAssociationList = AssociationManager.ExpandAll(so);
}
}
}
// try to get the default property set (we will use the first property)
this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
if (this.activeAssociationList.Count > 0)
{
// we got a valid set of properties from the default property set
return;
}
// we failed to get anything from the default property set
// just get all the properties
this.activeAssociationList = AssociationManager.ExpandAll(so);
ApplyExcludePropertyFilter();
}
}
}
@@ -375,5 +375,42 @@ namespace Microsoft.PowerShell.Commands
private bool _isResolved = false;
#endregion Private Members
}
/// <summary>
/// Helper class to do wildcard matching on PSPropertyExpressions.
/// </summary>
internal sealed class PSPropertyExpressionFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="PSPropertyExpressionFilter"/> class
/// with the specified array of patterns.
/// </summary>
/// <param name="wildcardPatternsStrings">Array of pattern strings to use.</param>
internal PSPropertyExpressionFilter(string[] wildcardPatternsStrings)
{
ArgumentNullException.ThrowIfNull(wildcardPatternsStrings);
_wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length];
for (int k = 0; k < wildcardPatternsStrings.Length; k++)
{
_wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase);
}
}
/// <summary>
/// Try to match the expression against the array of wildcard patterns.
/// The first match short-circuits the search.
/// </summary>
/// <param name="expression">PSPropertyExpression to test against.</param>
/// <returns>True if there is a match, else false.</returns>
internal bool IsMatch(PSPropertyExpression expression)
{
string expressionString = expression.ToString();
return _wildcardPatterns.Any(pattern => pattern.IsMatch(expressionString));
}
private readonly WildcardPattern[] _wildcardPatterns;
}
}
@@ -2504,7 +2504,8 @@ namespace System.Management.Automation
case "Format-Table":
case "Format-Wide":
{
if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase))
if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)
|| parameterName.Equals("ExcludeProperty", StringComparison.OrdinalIgnoreCase))
{
NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]);
}
@@ -466,4 +466,48 @@ SelectScriptBlock
$ps.Invoke() -replace '\r?\n', "^" | Should -BeExactly $expectedOutput
$ps.Streams.Error | Should -BeNullOrEmpty
}
Context 'ExcludeProperty parameter' {
It 'Should exclude specified properties' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
$result = $obj | Format-Custom -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Not -Match 'Age'
}
It 'Should work with wildcard patterns' {
$obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
$result = $obj | Format-Custom -ExcludeProperty Prop* | Out-String
$result | Should -Match 'Other'
$result | Should -Not -Match 'Prop1'
$result | Should -Not -Match 'Prop2'
}
It 'Should work without Property parameter (implies -Property *)' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
$result = $obj | Format-Custom -ExcludeProperty B | Out-String
$result | Should -Match 'A\s*='
$result | Should -Match 'C\s*='
$result | Should -Not -Match 'B\s*='
}
It 'Should work with Property parameter' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
$result = $obj | Format-Custom -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Match 'Country'
$result | Should -Not -Match 'Age'
}
It 'Should handle multiple excluded properties' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
$result = $obj | Format-Custom -ExcludeProperty B, D | Out-String
$result | Should -Match 'A\s*='
$result | Should -Match 'C\s*='
$result | Should -Not -Match 'B\s*='
$result | Should -Not -Match 'D\s*='
}
}
}
@@ -261,4 +261,48 @@ Describe 'Format-List color tests' -Tag 'CI' {
$output = Get-Content "$TestDrive/outfile.txt" -Raw
$output.Trim().Replace("`r", "") | Should -BeExactly $expected.Replace("`r", "")
}
Context 'ExcludeProperty parameter' {
It 'Should exclude specified properties' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
$result = $obj | Format-List -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Not -Match 'Age'
}
It 'Should work with wildcard patterns' {
$obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
$result = $obj | Format-List -ExcludeProperty Prop* | Out-String
$result | Should -Match 'Other'
$result | Should -Not -Match 'Prop1'
$result | Should -Not -Match 'Prop2'
}
It 'Should work without Property parameter (implies -Property *)' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
$result = $obj | Format-List -ExcludeProperty B | Out-String
$result | Should -Match 'A'
$result | Should -Match 'C'
$result | Should -Not -Match 'B'
}
It 'Should work with Property parameter' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
$result = $obj | Format-List -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Match 'Country'
$result | Should -Not -Match 'Age'
}
It 'Should handle multiple excluded properties' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
$result = $obj | Format-List -ExcludeProperty B, D | Out-String
$result | Should -Match 'A'
$result | Should -Match 'C'
$result | Should -Not -Match 'B'
$result | Should -Not -Match 'D'
}
}
}
@@ -953,4 +953,48 @@ Describe 'Table color tests' -Tag 'CI' {
$actual | Should -BeExactly $expected
}
Context 'ExcludeProperty parameter' {
It 'Should exclude specified properties' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
$result = $obj | Format-Table -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Not -Match 'Age'
}
It 'Should work with wildcard patterns' {
$obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
$result = $obj | Format-Table -ExcludeProperty Prop* | Out-String
$result | Should -Match 'Other'
$result | Should -Not -Match 'Prop1'
$result | Should -Not -Match 'Prop2'
}
It 'Should work without Property parameter (implies -Property *)' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
$result = $obj | Format-Table -ExcludeProperty B | Out-String
$result | Should -Match 'A'
$result | Should -Match 'C'
$result | Should -Not -Match 'B'
}
It 'Should work with Property parameter' {
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
$result = $obj | Format-Table -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
$result | Should -Match 'Name'
$result | Should -Match 'City'
$result | Should -Match 'Country'
$result | Should -Not -Match 'Age'
}
It 'Should handle multiple excluded properties' {
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
$result = $obj | Format-Table -ExcludeProperty B, D | Out-String
$result | Should -Match 'A'
$result | Should -Match 'C'
$result | Should -Not -Match 'B'
$result | Should -Not -Match 'D'
}
}
}
@@ -105,4 +105,56 @@ Describe "Format-Wide DRT basic functionality" -Tags "CI" {
$result | Should -Match " GroupingKey:"
$result | Should -Match "name2\s+name3"
}
Context 'ExcludeProperty parameter' {
It 'Should exclude specified property and display first remaining' {
# PSCustomObject properties are in definition order: Name, Age, City
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
# Exclude Name, should display Age (first remaining)
$result = $obj | Format-Wide -ExcludeProperty Name | Out-String
$result | Should -Match '30'
$result | Should -Not -Match 'Test'
}
It 'Should work with wildcard patterns' {
# Properties: Prop1, Prop2, Other
$obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
# Exclude Prop*, should display Other (only remaining)
$result = $obj | Format-Wide -ExcludeProperty Prop* | Out-String
$result | Should -Match '3'
$result | Should -Not -Match '1'
$result | Should -Not -Match '2'
}
It 'Should work without Property parameter (implies -Property *)' {
# Properties: A, B, C
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
# Exclude B, C - should display A (first remaining)
$result = $obj | Format-Wide -ExcludeProperty B, C | Out-String
$result | Should -Match '1'
$result | Should -Not -Match '2'
$result | Should -Not -Match '3'
}
It 'Should display first remaining property after exclusion' {
# Properties: Name, Age, City, Country
$obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
# Exclude Name and Age, should display City (first remaining)
$result = $obj | Format-Wide -ExcludeProperty Name, Age | Out-String
$result | Should -Match 'Seattle'
$result | Should -Not -Match 'Test'
$result | Should -Not -Match '30'
# USA might appear but not in the wide field, or might not appear at all since only first property is shown
}
It 'Should handle multiple excluded properties' {
# Properties: A, B, C, D
$obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
# Exclude A and B, should display C (first remaining)
$result = $obj | Format-Wide -ExcludeProperty A, B | Out-String
$result | Should -Match '3'
$result | Should -Not -Match '1'
$result | Should -Not -Match '2'
}
}
}