Skip to content

Commit

Permalink
Merge pull request #379 from tonyhallett/hide-namespace-when-grouping…
Browse files Browse the repository at this point in the history
…-by-namespace

Hide namespace when grouping by namespace
  • Loading branch information
tonyhallett committed Jan 14, 2024
2 parents 3987815 + 13fc156 commit 13ec57e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 22 deletions.
1 change: 1 addition & 0 deletions FineCodeCoverageTests/AppOptionsProvider_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ internal void Should_Use_Deseralized_String_From_Store_For_AppOption_Property(Fu
{ nameof(IAppOptions.ShowToolWindowToolbar),true},
{nameof(IAppOptions.ExcludeAssemblies),new string[]{ "Exclude"} },
{nameof(IAppOptions.IncludeAssemblies),new string[]{ "Include"} },
{nameof(IAppOptions.NamespaceQualification),NamespaceQualification.AlwaysUnqualified }
};
var mockJsonConvertService = autoMocker.GetMock<IJsonConvertService>();
mockJsonConvertService.Setup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,5 +701,6 @@ internal class TestCoverageProjectOptions : IAppOptions
public string[] ExcludeAssemblies { get; set; }
public string[] IncludeAssemblies { get; set; }
public bool DisabledNoCoverage { get; set; }
public NamespaceQualification NamespaceQualification { get; set; }
}
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ If you are using option 1) then project and global options will only be used whe
|ToolsDirectory|Folder to which copy tools subfolder. Must alredy exist. Requires restart of VS.|
|ThresholdForCyclomaticComplexity| When [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) exceeds this value for a method then the method will be present in the risk hotspots tab. |
|StickyCoverageTable|Set to true for coverage table to have a sticky thead.|
|NamespacedClasses|Set to false to show classes in report in short form. Affects grouping.|
|NamespacedClasses|Set to false to show types in report in short form. Affects grouping.|
|NamespaceQualification|Control qualification of types in report when NamespacedClasses is true.<br>FullyQualified - always fully qualify.<br>AlwaysUnqualified - always unqualified.<br>UnqualifiedByNamespace - unqualified when grouping by namespace.<br>QualifiedByNamespaceLevel - omits the first grouping level identifier parts. Reduces space whilst maintaining uniqueness.|
|HideFullyCovered|Set to true to hide classes, namespaces and assemblies that are fully covered.|
|Hide0Coverage|Set to true to hide classes, namespaces and assemblies that have 0% coverage.|
|Hide0Coverable|Set to false to show classes, namespaces and assemblies that are not coverable.|
Expand Down
123 changes: 103 additions & 20 deletions SharedProject/Core/ReportGenerator/ReportGeneratorUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ async Task run(string outputReportType, string inputReports)
reportTypeSettings.Add($@"""-reports:{inputReports}""");
reportTypeSettings.Add($@"""-plugins:{typeof(FccLightReportBuilder).Assembly.Location}""");
reportTypeSettings.Add($@"""-reporttypes:{FccLightReportBuilder.REPORT_TYPE}""");
var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds();
var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds(appOptionsProvider.Get());

reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity={cyclomaticThreshold}""");
reportTypeSettings.Add($@"""riskHotspotsAnalysisThresholds:metricThresholdForCrapScore={crapScoreThreshold}""");
Expand Down Expand Up @@ -337,9 +337,9 @@ Alternative is lighten / darken the background color
style.InnerHtml = changedCss;
}

private string GetStickyTableHead()
private string GetStickyTableHead(IAppOptions appOptions)
{
if (!appOptionsProvider.Get().StickyCoverageTable)
if (!appOptions.StickyCoverageTable)
{
return "";
}
Expand Down Expand Up @@ -829,9 +829,40 @@ private string HideGroupingCss()
";
}

private string ObserveAndHideFullyCovered()
private string CoverageInfoObserver()
{
var code = @"
var coverageInfoObserver = (function(){
var mutationObserver;
var callbacks = [];
function observe(){
mutationObserver.observe(
document.querySelector(""coverage-info""),
{ attributes: false, childList: true, subtree: true }
)
}
function cb(record,obs){
mutationObserver.disconnect();
for(var i=0;i<callbacks.length;i++){
callbacks[i]();
}
observe();
}
return {
observe:function(callback){
callbacks.push(callback);
if(!mutationObserver){
mutationObserver = new MutationObserver(cb);
observe();
}
}
}
})();
";
return code;
}
private string ObserveAndHideFullyCovered(IAppOptions appOptions)
{
var appOptions = appOptionsProvider.Get();
if (!(appOptions.HideFullyCovered | appOptions.Hide0Coverage | appOptions.Hide0Coverable))
{
return "";
Expand All @@ -840,11 +871,9 @@ private string ObserveAndHideFullyCovered()
function getCellValue(row, index){{
return parseInt(row.cells[index].innerText);
}}
var targetNode = document;
var config = {{ attributes: false, childList: true, subtree: true }};
var callback = function(mutationsList, observer) {{
var hideCoverage = function() {{
var rows = document.querySelectorAll(""coverage-info table tbody tr"");
for(var i=0;i<rows.length;i++){{
var row = rows[i];
Expand Down Expand Up @@ -878,14 +907,67 @@ function getCellValue(row, index){{
}};
}};
var observer = new MutationObserver(callback);
observer.observe(targetNode, config);
hideCoverage();
coverageInfoObserver.observe(hideCoverage);
";
return code;
}

private string HackGroupingToAllowAll(int groupingLevel)
private string ObserveAndHideNamespaceWhenGroupingByNamespace(IAppOptions appOptions)
{

if (!appOptions.NamespacedClasses || appOptions.NamespaceQualification == NamespaceQualification.FullyQualified)
{
return "";
}
var fullyQualifiedToName = "";
switch(appOptions.NamespaceQualification)
{
case NamespaceQualification.AlwaysUnqualified:
case NamespaceQualification.UnqualifiedByNamespace:
fullyQualifiedToName = "var name = fullyQualified.substring(fullyQualified.lastIndexOf(\".\") + 1);";
break;
case NamespaceQualification.QualifiedByNamespaceLevel:
fullyQualifiedToName = @"
var parts = fullyQualified.split(""."");
var namespaceParts = parts.slice(0,parts.length-1);
var type = parts[parts.length-1];
var name = type;
if(namespaceParts.length > groupingLevel){
name = namespaceParts.slice(groupingLevel).join(""."") + ""."" + type;
}";
break;
default:
throw new Exception($"Unknown GroupingNamespaceQualification '{appOptions.NamespaceQualification}'");
}
var alwaysUnqualified = appOptions.NamespaceQualification == NamespaceQualification.AlwaysUnqualified;
var code = $@"
var config = {{ attributes: false, childList: true, subtree: true }};
var changeQualification = function() {{
var groupingInput = document.querySelector(""coverage-info .customizebox input"");
if(!groupingInput || groupingInput.value <= 0 && !{alwaysUnqualified.ToString().ToLower()}){{
return;
}}
var groupingLevel = groupingInput.value;
var rows = document.querySelectorAll(""coverage-info table tbody tr[class-row]"");
for(var i=0;i<rows.length;i++){{
var row = rows[i];
var cell = row.cells[0];
var a = cell.querySelector(""a"");
var fullyQualified = a.innerText;
{fullyQualifiedToName}
a.innerText = name;
}};
}};
changeQualification();
coverageInfoObserver.observe(changeQualification);
";
return code;
}

private string HackGroupingToAllowAll(int groupingLevel)
{
return $@"
var customizeBox = document.getElementsByClassName('customizebox')[0];
Expand Down Expand Up @@ -969,7 +1051,7 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
ReportColours = reportColoursProvider.GetColours();
return assemblyUtil.RunInAssemblyResolvingContext(() =>
{
var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds();
var (cyclomaticThreshold, crapScoreThreshold, nPathThreshold) = HotspotThresholds(appOptions);
var noRiskHotspotsHeader = "No risk hotspots that exceed options :";
var noRiskHotspotsCyclomaticMsg = $"Cyclomatic complexity : {cyclomaticThreshold}";
var noRiskHotspotsNpathMsg =$"NPath complexity : {nPathThreshold}";
Expand Down Expand Up @@ -1123,9 +1205,11 @@ public string ProcessUnifiedHtml(string htmlForProcessing, string reportOutputFo
htmlSb.Replace("</body>", $@"
<script type=""text/javascript"">
{GetStickyTableHead()}
{GetStickyTableHead(appOptions)}
{HackGroupingToAllowAll(groupingLevel)}
{ObserveAndHideFullyCovered()}
{CoverageInfoObserver()}
{ObserveAndHideNamespaceWhenGroupingByNamespace(appOptions)}
{ObserveAndHideFullyCovered(appOptions)}
function getRuleBySelector(cssRules,selector){{
for(var i=0;i<cssRules.length;i++){{
if(cssRules[i].selectorText == selector){{
Expand Down Expand Up @@ -1640,13 +1724,12 @@ private void HideRowsFromOverviewTable(HtmlDocument doc)
}
}

private (int cyclomaticThreshold, int crapScoreThreshold, int nPathThreshold) HotspotThresholds()
private (int cyclomaticThreshold, int crapScoreThreshold, int nPathThreshold) HotspotThresholds(IAppOptions appOptions)
{
var options = appOptionsProvider.Get();
return (
options.ThresholdForCyclomaticComplexity,
options.ThresholdForCrapScore,
options.ThresholdForNPathComplexity
appOptions.ThresholdForCyclomaticComplexity,
appOptions.ThresholdForCrapScore,
appOptions.ThresholdForNPathComplexity
);

}
Expand Down
6 changes: 5 additions & 1 deletion SharedProject/Options/AppOptionsPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,13 @@ You can also ignore additional attributes by adding to this list (short name or
public bool StickyCoverageTable { get; set; }

[Category(commonReportCategory)]
[Description("Set to false to show classes in report in short form.")]
[Description("Set to false to show types in report in short form.")]
public bool NamespacedClasses { get; set; }

[Category(commonReportCategory)]
[Description("Control qualification of types when NamespacedClasses is true.")]
public NamespaceQualification NamespaceQualification { get; set; }

[Category(commonReportCategory)]
[Description("Set to true to hide classes, namespaces and assemblies that are fully covered.")]
public bool HideFullyCovered { get; set; }
Expand Down
1 change: 1 addition & 0 deletions SharedProject/Options/AppOptionsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,6 @@ internal class AppOptions : IAppOptions
public bool ShowToolWindowToolbar { get; set; }
public string[] ExcludeAssemblies { get; set; }
public string[] IncludeAssemblies { get; set; }
public NamespaceQualification NamespaceQualification { get; set; }
}
}
10 changes: 10 additions & 0 deletions SharedProject/Options/IAppOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,15 @@ internal interface IAppOptions : IMsCodeCoverageOptions, IOpenCoverCoverletExclu
bool AdjacentBuildOutput { get; set; }
RunMsCodeCoverage RunMsCodeCoverage { get; set; }
bool ShowToolWindowToolbar { get; set; }

NamespaceQualification NamespaceQualification { get; set; }
}

internal enum NamespaceQualification
{
FullyQualified,
AlwaysUnqualified,
UnqualifiedByNamespace,
QualifiedByNamespaceLevel
}
}

0 comments on commit 13ec57e

Please sign in to comment.