Skip to content

Commit

Permalink
Add IVisualNodeProducer.ProduceVisualNode implementations (#5213)
Browse files Browse the repository at this point in the history
Fixes #5212
  • Loading branch information
1 parent 22a143d commit ce89890
Show file tree
Hide file tree
Showing 10 changed files with 436 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.Generic;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SonarLint.VisualStudio.Education.Layout.Logical;
using SonarLint.VisualStudio.Education.Layout.Visual;
using SonarLint.VisualStudio.Education.Layout.Visual.Tabs;
using SonarLint.VisualStudio.Education.XamlGenerator;

namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical;

[TestClass]
public class ContextualRuleDescriptionTabTests
{
private const string Context1 = "context1";
private const string ContextTabTitle1 = "contexttitle1";
private const string ContextTabContent1 = "htmlcontent1";
private const string ContextTabXamlContent1 = "xamlcontent1";
private const string DefaultContextTabTitle = "default";
private const string DefaultContext = "defaultcontext";
private const string DefaultContextTabContent = "defaultcontent";
private const string DefaultContextTabXamlContent = "defaultxamlcontent";
private const string ContextTabTitle2 = "contexttitle2";
private const string Context2 = "context2";
private const string ContextTabContent2 = "htmlcontent2";
private const string ContextTabXamlContent2 = "xamlcontent2";

[TestMethod]
public void Title_ReturnsCorrectTitle()
{
const string title = "title";

var testSubject = new ContextualRuleDescriptionTab(title, null, null);

testSubject.Title.Should().BeSameAs(title);
}

[TestMethod]
public void ProduceVisualNode_ReturnsCorrectStructure()
{
var testSubject = new ContextualRuleDescriptionTab("title",
DefaultContext,
GetContextTabs());
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
SetupHtmlToXamlConversion(translatorMock);

var visualNode = testSubject.ProduceVisualNode(new VisualizationParameters(translatorMock.Object, Context1));

visualNode.Should().BeEquivalentTo(
new TabGroup(new List<ITabItem>
{
new TabItem(ContextTabTitle1, new ContentSection(ContextTabXamlContent1)),
new TabItem(DefaultContextTabTitle, new ContentSection(DefaultContextTabXamlContent)),
new TabItem(ContextTabTitle2, new ContentSection(ContextTabXamlContent2)),
}, 0));
}

[DataRow(Context1, 0)]
[DataRow(DefaultContext, 1)]
[DataRow(Context2, 2)]
[DataTestMethod]
public void ProduceVisualNode_PrefersSelectedContext(string context, int expectedIndex)
{
var testSubject = new ContextualRuleDescriptionTab("title",
DefaultContext,
GetContextTabs());
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
SetupHtmlToXamlConversion(translatorMock);

var visualNode = testSubject.ProduceVisualNode(new VisualizationParameters(translatorMock.Object, context));

visualNode.Should().BeOfType<TabGroup>().Which.selectedTabIndex.Should().Be(expectedIndex);
}

[TestMethod]
public void ProduceVisualNode_NoSelectedContext_FallsBackToDefaultContext()
{
var testSubject = new ContextualRuleDescriptionTab("title",
DefaultContext,
GetContextTabs());
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
SetupHtmlToXamlConversion(translatorMock);

var visualNode = testSubject.ProduceVisualNode(new VisualizationParameters(translatorMock.Object, null));

visualNode.Should().BeOfType<TabGroup>().Which.selectedTabIndex.Should().Be(1);
}

[TestMethod]
public void ProduceVisualNode_NoContextProvided_SelectsFirstTab()
{
string NOCONTEXT = null;

var testSubject = new ContextualRuleDescriptionTab("title",
NOCONTEXT,
GetContextTabs());
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
SetupHtmlToXamlConversion(translatorMock);

var visualNode = testSubject.ProduceVisualNode(new VisualizationParameters(translatorMock.Object, NOCONTEXT));

visualNode.Should().BeOfType<TabGroup>().Which.selectedTabIndex.Should().Be(0);
}

private static List<ContextualRuleDescriptionTab.ContextContentTab> GetContextTabs()
{
return new List<ContextualRuleDescriptionTab.ContextContentTab>
{
new(ContextTabTitle1, Context1, ContextTabContent1),
new(DefaultContextTabTitle, DefaultContext, DefaultContextTabContent),
new(ContextTabTitle2, Context2, ContextTabContent2),
};
}

private static void SetupHtmlToXamlConversion(Mock<IRuleHelpXamlTranslator> translator)
{
translator.Setup(x => x.TranslateHtmlToXaml(ContextTabContent1)).Returns(ContextTabXamlContent1);
translator.Setup(x => x.TranslateHtmlToXaml(ContextTabContent2)).Returns(ContextTabXamlContent2);
translator.Setup(x => x.TranslateHtmlToXaml(DefaultContextTabContent)).Returns(DefaultContextTabXamlContent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SonarLint.VisualStudio.Education.Layout.Logical;
using SonarLint.VisualStudio.Education.Layout.Visual;
using SonarLint.VisualStudio.Education.XamlGenerator;

namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical;

[TestClass]
public class NonContextualRuleDescriptionTabTests
{
[TestMethod]
public void ProduceVisualNode_ReturnsSingleContentSection()
{
const string contentHtml = "contenthtml";
const string contentXaml = "contentxaml";
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
translatorMock.Setup(x => x.TranslateHtmlToXaml(contentHtml)).Returns(contentXaml);
var parameters = new VisualizationParameters(translatorMock.Object, "context");

var testSubject = new NonContextualRuleDescriptionTab("title", contentHtml);


var visualNode = testSubject.ProduceVisualNode(parameters);


visualNode.Should().BeEquivalentTo(new ContentSection(contentXaml));
translatorMock.Verify(x => x.TranslateHtmlToXaml(contentHtml), Times.Once);
translatorMock.VerifyNoOtherCalls();
}

[TestMethod]
public void Title_ReturnsCorrectTitle()
{
const string title = "title";

var testSubject = new NonContextualRuleDescriptionTab(title, "contenthtml");

testSubject.Title.Should().BeSameAs(title);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.Generic;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SonarLint.VisualStudio.Education.Layout.Logical;
using SonarLint.VisualStudio.Education.Layout.Visual;
using SonarLint.VisualStudio.Education.Layout.Visual.Tabs;
using SonarLint.VisualStudio.Education.XamlGenerator;

namespace SonarLint.VisualStudio.Education.UnitTests.Layout.Logical;

[TestClass]
public class RuleSplitDescriptionTests
{
[TestMethod]
public void ProduceVisualNode_ProducesMultiBlockSectionWithIntroAndTabs()
{
const string introHtml = "introhtml";
const string introXaml = "introxaml";
const string tabTitle = "tabTitle";
var translatorMock = new Mock<IRuleHelpXamlTranslator>();
translatorMock.Setup(x => x.TranslateHtmlToXaml(introHtml)).Returns(introXaml);
var parameters = new VisualizationParameters(translatorMock.Object, "context");
var tabMock = new Mock<IRuleDescriptionTab>();
tabMock.SetupGet(x => x.Title).Returns(tabTitle);
var tabVisualNodeMock = new Mock<IAbstractVisualizationTreeNode>();
tabMock.Setup(x => x.ProduceVisualNode(parameters)).Returns(tabVisualNodeMock.Object);

var testSubject = new RuleSplitDescription(introHtml, new List<IRuleDescriptionTab> { tabMock.Object });


var visualNode = testSubject.ProduceVisualNode(parameters);


visualNode.Should().BeEquivalentTo(
new MultiBlockSection(
new ContentSection(introXaml),
new TabGroup(new List<ITabItem>{new TabItem(tabTitle, tabVisualNodeMock.Object)}, 0)));
translatorMock.Verify(x => x.TranslateHtmlToXaml(introHtml), Times.Once);
translatorMock.VerifyNoOtherCalls();
}
}
1 change: 1 addition & 0 deletions src/Education/Education.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<PropertyGroup>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>SonarLint.VisualStudio.Education</RootNamespace>
Expand Down
89 changes: 89 additions & 0 deletions src/Education/Layout/Logical/ContextualRuleDescriptionTab.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using SonarLint.VisualStudio.Education.Layout.Visual;
using SonarLint.VisualStudio.Education.Layout.Visual.Tabs;

namespace SonarLint.VisualStudio.Education.Layout.Logical
{
internal class ContextualRuleDescriptionTab : IRuleDescriptionTab
{
private readonly List<ContextContentTab> contexts;
private readonly string defaultContext;

public ContextualRuleDescriptionTab(string title, string defaultContext, List<ContextContentTab> contexts)
{
Title = title;
this.contexts = contexts;
this.defaultContext = defaultContext;
}

public string Title { get; }

public IAbstractVisualizationTreeNode ProduceVisualNode(VisualizationParameters parameters)
{
var contextTabs = contexts
.Select(x => new TabItem(x.Title,
new ContentSection(parameters.HtmlToXamlTranslator.TranslateHtmlToXaml(x.HtmlContent))))
.ToList<ITabItem>();

return new TabGroup(contextTabs, GetSelectedTabIndex(parameters.RelevantContext));
}

private int GetSelectedTabIndex(string contextToDisplay)
{
var selectedIndex = FindContextIndex(contextToDisplay);

if (selectedIndex == -1)
{
selectedIndex = FindContextIndex(defaultContext);
}

return Math.Max(selectedIndex, 0);
}

private int FindContextIndex(string context)
{
if (context == null)
{
return -1;
}

return contexts.FindIndex(x => x.ContextKey.Equals(context));
}

internal class ContextContentTab
{
public ContextContentTab(string title, string contextKey, string htmlContent)
{
Title = title;
ContextKey = contextKey;
HtmlContent = htmlContent;
}

public string Title { get; }
public string ContextKey { get; }
public string HtmlContent { get; }
}
}
}
27 changes: 27 additions & 0 deletions src/Education/Layout/Logical/IRuleDescriptionTab.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarLint.VisualStudio.Education.Layout.Logical
{
internal interface IRuleDescriptionTab : IVisualNodeProducer
{
string Title { get; }
}
}
Loading

0 comments on commit ce89890

Please sign in to comment.