From bb3eaaa6f1acc1267d1879c142fdd1eae81429af Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Thu, 22 Mar 2018 18:01:47 +0100 Subject: [PATCH 01/17] added test run parsing --- .../BuildsAppReborn.Access.csproj | 3 ++ BuildsAppReborn.Access/TFS/Models/TfsBuild.cs | 14 +++--- .../TFS/Models/TfsProject.cs | 2 +- .../TFS/Models/TfsTestRun.cs | 41 ++++++++++++++++++ .../TFS/TfsBuildProviderBase.cs | 43 +++++++++++++------ .../TFS2017/Models/TfsTestRun.cs | 7 +++ .../TFS2017/Tfs2017BuildProvider.cs | 2 +- .../VSTS/Models/VstsBuild.cs | 1 + .../VSTS/Models/VstsTestRun.cs | 7 +++ .../VSTS/VstsBuildProvider.cs | 2 +- .../ViewModels/BuildsStatusViewModel.cs | 2 +- .../BuildsAppReborn.Contracts.csproj | 3 ++ .../Models/Base/IObjectItem.cs | 13 ++++++ .../Models/Base/IWebItem.cs | 13 ++++++ BuildsAppReborn.Contracts/Models/IBuild.cs | 7 ++- .../Models/IBuildDefinition.cs | 5 +-- BuildsAppReborn.Contracts/Models/IProject.cs | 5 +-- BuildsAppReborn.Contracts/Models/ITestRun.cs | 26 +++++++++++ BuildsAppReborn.Contracts/Models/IUser.cs | 5 +-- 19 files changed, 166 insertions(+), 35 deletions(-) create mode 100644 BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs create mode 100644 BuildsAppReborn.Access/TFS2017/Models/TfsTestRun.cs create mode 100644 BuildsAppReborn.Access/VSTS/Models/VstsTestRun.cs create mode 100644 BuildsAppReborn.Contracts/Models/Base/IObjectItem.cs create mode 100644 BuildsAppReborn.Contracts/Models/Base/IWebItem.cs create mode 100644 BuildsAppReborn.Contracts/Models/ITestRun.cs diff --git a/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj b/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj index ab098ef..cb1787f 100644 --- a/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj +++ b/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj @@ -62,6 +62,7 @@ + @@ -71,10 +72,12 @@ + + diff --git a/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs b/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs index b468e9a..8d6c3e0 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs @@ -82,6 +82,9 @@ public BuildStatus Status } } + [JsonIgnore] + public ITestRun TestRun { get; internal set; } + [JsonProperty("finishTime")] public DateTime FinishDateTime { get; private set; } @@ -98,7 +101,7 @@ public BuildStatus Status public String Url { get; private set; } [JsonIgnore] - public String PortalUrl => base.WebLink; + public String WebUrl => base.WebLink; [JsonIgnore] public ISourceVersion SourceVersion { get; internal set; } @@ -124,17 +127,18 @@ internal String SourceVersionInternal private set { this.sourceVersionInternal = value; } } + [JsonProperty("uri")] + internal String Uri { get; private set; } + #endregion #region Private Fields - [JsonProperty("result")] - private String result; + [JsonProperty("result")] private String result; private String sourceVersionInternal; - [JsonProperty("status")] - private String status; + [JsonProperty("status")] private String status; #endregion } diff --git a/BuildsAppReborn.Access/TFS/Models/TfsProject.cs b/BuildsAppReborn.Access/TFS/Models/TfsProject.cs index 4889c2e..85cbb8f 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsProject.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsProject.cs @@ -7,7 +7,7 @@ namespace BuildsAppReborn.Access.Models { // ReSharper disable once ClassNeverInstantiated.Global - public abstract class TfsProject : IProject + internal abstract class TfsProject : IProject { #region Implementation of IProject diff --git a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs new file mode 100644 index 0000000..eca8875 --- /dev/null +++ b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs @@ -0,0 +1,41 @@ +using System; +using BuildsAppReborn.Contracts.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace BuildsAppReborn.Access.Models +{ + internal abstract class TfsTestRun : ITestRun + { + #region Implementation of ITestRun + + [JsonProperty("url")] + public String Url { get; private set; } + + [JsonProperty("webAccessUrl")] + public String WebUrl { get; private set; } + + [JsonProperty("id")] + public Int32 Id { get; private set; } + + [JsonProperty("incompleteTests")] + public Int32 IncompleteTests { get; private set; } + + [JsonProperty("name")] + public String Name { get; private set; } + + [JsonProperty("passedTests")] + public Int32 PassedTests { get; private set; } + + [JsonProperty("state")] + public String State { get; private set; } + + [JsonProperty("totalTests")] + public Int32 TotalTests { get; private set; } + + [JsonProperty("unanalyzedTests")] + public Int32 UnanalyzedTests { get; private set; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs index 02ed514..d1fb91e 100644 --- a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs +++ b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs @@ -4,24 +4,23 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; - using BuildsAppReborn.Access.Models; using BuildsAppReborn.Access.Models.Internal; using BuildsAppReborn.Contracts; using BuildsAppReborn.Contracts.Models; using BuildsAppReborn.Infrastructure; - using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BuildsAppReborn.Access { - internal abstract class TfsBuildProviderBase : TfsBuildProviderBase, IBuildProvider - where TBuild : TfsBuild, new() - where TBuildDefinition : TfsBuildDefinition, new() - where TUser : TfsUser, new() + internal abstract class TfsBuildProviderBase : TfsBuildProviderBase, IBuildProvider + where TBuild : TfsBuild, new() + where TBuildDefinition : TfsBuildDefinition, new() + where TUser : TfsUser, new() where TSourceVersion : TfsSourceVersion, new() where TArtifact : TfsArtifact, new() + where TTestRun : TfsTestRun, new() { #region Implementation of IBuildProvider @@ -42,9 +41,10 @@ public virtual async Task>> GetBuildD buildDefinition.BuildSettingsId = settings.UniqueId; } - return new DataResponse> { Data = data, StatusCode = requestResponse.StatusCode }; + return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; } - return new DataResponse> { Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode }; + + return new DataResponse> {Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode}; } throw new Exception($"Error while processing method!"); @@ -55,7 +55,7 @@ public virtual async Task>> GetBuilds(IEnumerab var buildDefinitionsList = buildDefinitions.ToList(); if (!buildDefinitionsList.Any()) { - return new DataResponse> { Data = Enumerable.Empty(), StatusCode = HttpStatusCode.NoContent }; + return new DataResponse> {Data = Enumerable.Empty(), StatusCode = HttpStatusCode.NoContent}; } var projectUrl = settings.GetValueStrict(ProjectUrlSettingKey).TrimEnd('/'); @@ -78,17 +78,17 @@ public virtual async Task>> GetBuilds(IEnumerab await ResolveSourceVersion(data, projectUrl, settings); await ResolveArtifacts(data, projectUrl, settings); + await ResolveTestRuns(data, projectUrl, settings); - return new DataResponse> { Data = data, StatusCode = requestResponse.StatusCode }; + return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; } - return new DataResponse> { Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode }; + + return new DataResponse> {Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode}; } throw new Exception($"Error while processing method!"); } - - #endregion #region Protected Properties @@ -119,6 +119,7 @@ private static async Task GetRequestResponse(String request { return await HttpRequestHelper.GetRequestResponse(requestUrl, accessToken); } + return await HttpRequestHelper.GetRequestResponse(requestUrl, credentials); } @@ -206,6 +207,22 @@ private async Task ResolveSourceVersion(IEnumerable builds, String proje } } + private async Task ResolveTestRuns(IEnumerable builds, String projectUrl, BuildMonitorSettings settings) + { + foreach (var build in builds) + { + var requestUrl = $"{projectUrl}/_apis/test/runs?api-version=1.0&buildUri={build.Uri}"; + + var requestResponse = await GetRequestResponse(requestUrl, settings); + if (requestResponse.IsSuccessStatusCode) + { + var result = await requestResponse.Content.ReadAsStringAsync(); + var value = JObject.Parse(result)["value"].ToString(); + build.TestRun = JsonConvert.DeserializeObject(value); + } + } + } + private async Task SetSourceVersion(BuildMonitorSettings settings, String requestUrl, TBuild build) where TInnerSourceVersion : ISourceVersion, new() { var requestResponse = await GetRequestResponse(requestUrl, settings); diff --git a/BuildsAppReborn.Access/TFS2017/Models/TfsTestRun.cs b/BuildsAppReborn.Access/TFS2017/Models/TfsTestRun.cs new file mode 100644 index 0000000..1aa98e1 --- /dev/null +++ b/BuildsAppReborn.Access/TFS2017/Models/TfsTestRun.cs @@ -0,0 +1,7 @@ +namespace BuildsAppReborn.Access.Models +{ + // ReSharper disable once UnusedMember.Global + internal class Tfs2017TestRun : TfsTestRun + { + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Access/TFS2017/Tfs2017BuildProvider.cs b/BuildsAppReborn.Access/TFS2017/Tfs2017BuildProvider.cs index 74859d6..987b99f 100644 --- a/BuildsAppReborn.Access/TFS2017/Tfs2017BuildProvider.cs +++ b/BuildsAppReborn.Access/TFS2017/Tfs2017BuildProvider.cs @@ -9,7 +9,7 @@ namespace BuildsAppReborn.Access { [BuildProviderExport(typeof(IBuildProvider), Id, Name, AuthenticationModes.Default | AuthenticationModes.AccessToken)] [PartCreationPolicy(CreationPolicy.Shared)] - internal class Tfs2017BuildProvider : TfsBuildProviderBase + internal class Tfs2017BuildProvider : TfsBuildProviderBase { #region Overrides of Base diff --git a/BuildsAppReborn.Access/VSTS/Models/VstsBuild.cs b/BuildsAppReborn.Access/VSTS/Models/VstsBuild.cs index 0f86492..45b80d2 100644 --- a/BuildsAppReborn.Access/VSTS/Models/VstsBuild.cs +++ b/BuildsAppReborn.Access/VSTS/Models/VstsBuild.cs @@ -14,6 +14,7 @@ internal class VstsBuild : TfsBuild [JsonConverter(typeof(InterfaceTypeConverter))] public override IUser Requester { get; protected set; } + #endregion } } \ No newline at end of file diff --git a/BuildsAppReborn.Access/VSTS/Models/VstsTestRun.cs b/BuildsAppReborn.Access/VSTS/Models/VstsTestRun.cs new file mode 100644 index 0000000..e8aea01 --- /dev/null +++ b/BuildsAppReborn.Access/VSTS/Models/VstsTestRun.cs @@ -0,0 +1,7 @@ +namespace BuildsAppReborn.Access.Models +{ + // ReSharper disable once UnusedMember.Global + internal class VstsTestRun : TfsTestRun + { + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Access/VSTS/VstsBuildProvider.cs b/BuildsAppReborn.Access/VSTS/VstsBuildProvider.cs index aeb1743..b6dd7fe 100644 --- a/BuildsAppReborn.Access/VSTS/VstsBuildProvider.cs +++ b/BuildsAppReborn.Access/VSTS/VstsBuildProvider.cs @@ -9,7 +9,7 @@ namespace BuildsAppReborn.Access { [BuildProviderExport(typeof(IBuildProvider), Id, Name, AuthenticationModes.AccessToken)] [PartCreationPolicy(CreationPolicy.Shared)] - internal class VstsBuildProvider : TfsBuildProviderBase + internal class VstsBuildProvider : TfsBuildProviderBase { #region Overrides of Base diff --git a/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs b/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs index c44de96..b1bd5db 100644 --- a/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs +++ b/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs @@ -70,7 +70,7 @@ private void OnHistoryClickCommand(BuildItem item) { using (new WaitingIndicator()) { - StartProcess(item?.Build?.PortalUrl); + StartProcess(item?.Build?.WebUrl); } } diff --git a/BuildsAppReborn.Contracts/BuildsAppReborn.Contracts.csproj b/BuildsAppReborn.Contracts/BuildsAppReborn.Contracts.csproj index a27a76e..9068db0 100644 --- a/BuildsAppReborn.Contracts/BuildsAppReborn.Contracts.csproj +++ b/BuildsAppReborn.Contracts/BuildsAppReborn.Contracts.csproj @@ -60,17 +60,20 @@ + + + diff --git a/BuildsAppReborn.Contracts/Models/Base/IObjectItem.cs b/BuildsAppReborn.Contracts/Models/Base/IObjectItem.cs new file mode 100644 index 0000000..7a36f1f --- /dev/null +++ b/BuildsAppReborn.Contracts/Models/Base/IObjectItem.cs @@ -0,0 +1,13 @@ +using System; + +namespace BuildsAppReborn.Contracts.Models.Base +{ + public interface IObjectItem + { + #region Public Properties + + String Url { get; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Contracts/Models/Base/IWebItem.cs b/BuildsAppReborn.Contracts/Models/Base/IWebItem.cs new file mode 100644 index 0000000..d579dfb --- /dev/null +++ b/BuildsAppReborn.Contracts/Models/Base/IWebItem.cs @@ -0,0 +1,13 @@ +using System; + +namespace BuildsAppReborn.Contracts.Models.Base +{ + public interface IWebItem + { + #region Public Properties + + String WebUrl { get; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Contracts/Models/IBuild.cs b/BuildsAppReborn.Contracts/Models/IBuild.cs index 7220c14..49bbab4 100644 --- a/BuildsAppReborn.Contracts/Models/IBuild.cs +++ b/BuildsAppReborn.Contracts/Models/IBuild.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using BuildsAppReborn.Contracts.Models.Base; namespace BuildsAppReborn.Contracts.Models { - public interface IBuild + public interface IBuild : IObjectItem, IWebItem { #region Public Properties @@ -17,8 +18,6 @@ public interface IBuild Int32 Id { get; } - String PortalUrl { get; } - DateTime QueueDateTime { get; } IUser Requester { get; } @@ -29,7 +28,7 @@ public interface IBuild BuildStatus Status { get; } - String Url { get; } + ITestRun TestRun { get; } #endregion } diff --git a/BuildsAppReborn.Contracts/Models/IBuildDefinition.cs b/BuildsAppReborn.Contracts/Models/IBuildDefinition.cs index 3a611b3..c321b0f 100644 --- a/BuildsAppReborn.Contracts/Models/IBuildDefinition.cs +++ b/BuildsAppReborn.Contracts/Models/IBuildDefinition.cs @@ -1,8 +1,9 @@ using System; +using BuildsAppReborn.Contracts.Models.Base; namespace BuildsAppReborn.Contracts.Models { - public interface IBuildDefinition + public interface IBuildDefinition : IObjectItem { #region Public Properties @@ -16,8 +17,6 @@ public interface IBuildDefinition String Type { get; } - String Url { get; } - #endregion } } \ No newline at end of file diff --git a/BuildsAppReborn.Contracts/Models/IProject.cs b/BuildsAppReborn.Contracts/Models/IProject.cs index f9954fb..0da4045 100644 --- a/BuildsAppReborn.Contracts/Models/IProject.cs +++ b/BuildsAppReborn.Contracts/Models/IProject.cs @@ -1,8 +1,9 @@ using System; +using BuildsAppReborn.Contracts.Models.Base; namespace BuildsAppReborn.Contracts.Models { - public interface IProject + public interface IProject : IObjectItem { #region Public Properties @@ -12,8 +13,6 @@ public interface IProject String Name { get; } - String Url { get; } - #endregion } } \ No newline at end of file diff --git a/BuildsAppReborn.Contracts/Models/ITestRun.cs b/BuildsAppReborn.Contracts/Models/ITestRun.cs new file mode 100644 index 0000000..5caa391 --- /dev/null +++ b/BuildsAppReborn.Contracts/Models/ITestRun.cs @@ -0,0 +1,26 @@ +using System; +using BuildsAppReborn.Contracts.Models.Base; + +namespace BuildsAppReborn.Contracts.Models +{ + public interface ITestRun : IObjectItem, IWebItem + { + #region Public Properties + + Int32 Id { get; } + + Int32 IncompleteTests { get; } + + String Name { get; } + + Int32 PassedTests { get; } + + String State { get; } + + Int32 TotalTests { get; } + + Int32 UnanalyzedTests { get; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Contracts/Models/IUser.cs b/BuildsAppReborn.Contracts/Models/IUser.cs index d5683f5..d5cfb03 100644 --- a/BuildsAppReborn.Contracts/Models/IUser.cs +++ b/BuildsAppReborn.Contracts/Models/IUser.cs @@ -1,8 +1,9 @@ using System; +using BuildsAppReborn.Contracts.Models.Base; namespace BuildsAppReborn.Contracts.Models { - public interface IUser + public interface IUser : IObjectItem { #region Public Properties @@ -16,8 +17,6 @@ public interface IUser String UniqueName { get; } - String Url { get; } - #endregion } } \ No newline at end of file From 817b237c26eee11d556534ec7d131805d9dea56a Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Mon, 2 Apr 2018 20:22:21 +0200 Subject: [PATCH 02/17] added loading of test runs --- BuildsAppReborn.Access/TFS/Models/TfsBuild.cs | 8 +++++--- BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs | 3 +-- BuildsAppReborn.Contracts/Models/IBuild.cs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs b/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs index 8d6c3e0..4f8e6cd 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsBuild.cs @@ -83,7 +83,7 @@ public BuildStatus Status } [JsonIgnore] - public ITestRun TestRun { get; internal set; } + public IEnumerable TestRuns { get; internal set; } [JsonProperty("finishTime")] public DateTime FinishDateTime { get; private set; } @@ -134,11 +134,13 @@ internal String SourceVersionInternal #region Private Fields - [JsonProperty("result")] private String result; + [JsonProperty("result")] + private String result; private String sourceVersionInternal; - [JsonProperty("status")] private String status; + [JsonProperty("status")] + private String status; #endregion } diff --git a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs index d1fb91e..bf8cf4f 100644 --- a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs +++ b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs @@ -217,8 +217,7 @@ private async Task ResolveTestRuns(IEnumerable builds, String projectUrl if (requestResponse.IsSuccessStatusCode) { var result = await requestResponse.Content.ReadAsStringAsync(); - var value = JObject.Parse(result)["value"].ToString(); - build.TestRun = JsonConvert.DeserializeObject(value); + build.TestRuns = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); } } } diff --git a/BuildsAppReborn.Contracts/Models/IBuild.cs b/BuildsAppReborn.Contracts/Models/IBuild.cs index 49bbab4..c0128fb 100644 --- a/BuildsAppReborn.Contracts/Models/IBuild.cs +++ b/BuildsAppReborn.Contracts/Models/IBuild.cs @@ -28,7 +28,7 @@ public interface IBuild : IObjectItem, IWebItem BuildStatus Status { get; } - ITestRun TestRun { get; } + IEnumerable TestRuns { get; } #endregion } From 6e4073233f0f7f415113f7a62432c1f4da3087a1 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Mon, 2 Apr 2018 22:49:53 +0200 Subject: [PATCH 03/17] began with stacked bar graph control for test runs --- .../TFS/Models/TfsTestRun.cs | 5 ++- .../BuildsAppReborn.Client.csproj | 11 +++++ .../Controls/StackedBarGraph.xaml | 40 +++++++++++++++++++ .../Controls/StackedBarGraph.xaml.cs | 33 +++++++++++++++ .../Controls/StackedItem.cs | 21 ++++++++++ .../Controls/StackedItemCollection.cs | 23 +++++++++++ .../Converter/NullVisibilityConverter.cs | 24 +++++++++++ .../TestRunToStackedItemsConverter.cs | 40 +++++++++++++++++++ .../Views/BuildsStatusView.xaml | 11 ++++- BuildsAppReborn.Contracts.UI/BuildItem.cs | 4 ++ BuildsAppReborn.Contracts/Models/ITestRun.cs | 6 ++- 11 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 BuildsAppReborn.Client/Controls/StackedBarGraph.xaml create mode 100644 BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs create mode 100644 BuildsAppReborn.Client/Controls/StackedItem.cs create mode 100644 BuildsAppReborn.Client/Controls/StackedItemCollection.cs create mode 100644 BuildsAppReborn.Client/Converter/NullVisibilityConverter.cs create mode 100644 BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs diff --git a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs index eca8875..cd72db2 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs @@ -19,11 +19,14 @@ internal abstract class TfsTestRun : ITestRun public Int32 Id { get; private set; } [JsonProperty("incompleteTests")] - public Int32 IncompleteTests { get; private set; } + public Int32 FailedTests { get; private set; } [JsonProperty("name")] public String Name { get; private set; } + [JsonProperty("notApplicableTests")] + public Int32 InconclusiveTests { get; private set; } + [JsonProperty("passedTests")] public Int32 PassedTests { get; private set; } diff --git a/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj b/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj index f2d8f24..562184c 100644 --- a/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj +++ b/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj @@ -146,12 +146,19 @@ + + StackedBarGraph.xaml + + + + + @@ -174,6 +181,10 @@ + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml new file mode 100644 index 0000000..8b51e63 --- /dev/null +++ b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + diff --git a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs new file mode 100644 index 0000000..5577e59 --- /dev/null +++ b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; + +namespace BuildsAppReborn.Client.Controls +{ + public partial class StackedBarGraph : UserControl + { + #region Constructors + + public StackedBarGraph() + { + InitializeComponent(); + + this.DataContext = this; + } + + #endregion + + #region Public Properties + + public StackedItemCollection Items + { + get { return (StackedItemCollection) GetValue(ItemsProperty); } + set { SetValue(ItemsProperty, value); } + } + + #endregion + + public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register( + "Items", typeof(StackedItemCollection), typeof(StackedBarGraph), new PropertyMetadata(default(StackedItemCollection))); + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Controls/StackedItem.cs b/BuildsAppReborn.Client/Controls/StackedItem.cs new file mode 100644 index 0000000..61792ad --- /dev/null +++ b/BuildsAppReborn.Client/Controls/StackedItem.cs @@ -0,0 +1,21 @@ +using System; +using System.Windows.Input; +using System.Windows.Media; + +namespace BuildsAppReborn.Client.Controls +{ + public class StackedItem + { + #region Public Properties + + public ICommand ClickCommand { get; set; } + + public Brush Color { get; set; } + + public String Title { get; set; } + + public Int32 Value { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Controls/StackedItemCollection.cs b/BuildsAppReborn.Client/Controls/StackedItemCollection.cs new file mode 100644 index 0000000..e7917d5 --- /dev/null +++ b/BuildsAppReborn.Client/Controls/StackedItemCollection.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace BuildsAppReborn.Client.Controls +{ + public class StackedItemCollection : ObservableCollection + { + #region Constructors + + public StackedItemCollection(List list) : base(list) + { + } + + #endregion + + #region Public Properties + + public Int32 TotalCount { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Converter/NullVisibilityConverter.cs b/BuildsAppReborn.Client/Converter/NullVisibilityConverter.cs new file mode 100644 index 0000000..0d3497a --- /dev/null +++ b/BuildsAppReborn.Client/Converter/NullVisibilityConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace BuildsAppReborn.Client.Converter +{ + public class NullVisibilityConverter : IValueConverter + { + #region Implementation of IValueConverter + + public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) + { + return value == null ? Visibility.Collapsed : Visibility.Visible; + } + + public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs new file mode 100644 index 0000000..fd9c21e --- /dev/null +++ b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using BuildsAppReborn.Client.Controls; +using BuildsAppReborn.Contracts.Models; + +namespace BuildsAppReborn.Client.Converter +{ + public class TestRunToStackedItemsConverter : IValueConverter + { + #region Implementation of IValueConverter + + public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) + { + var testRun = value as ITestRun; + if (testRun == null) + { + return Binding.DoNothing; + } + + var stackedItems = new List(); + + stackedItems.Add(new StackedItem {Value = testRun.PassedTests, Title = "Passed Tests", Color = Brushes.Green}); + stackedItems.Add(new StackedItem {Value = testRun.FailedTests, Title = "Failed Tests", Color = Brushes.Red}); + stackedItems.Add(new StackedItem {Value = testRun.UnanalyzedTests, Title = "Not run Tests", Color = Brushes.Blue}); + stackedItems.Add(new StackedItem {Value = testRun.InconclusiveTests, Title = "Inconclusive Tests", Color = Brushes.Yellow}); + + return new StackedItemCollection(stackedItems) {TotalCount = testRun.TotalTests}; + } + + public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml index 0289af5..22865f4 100644 --- a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml +++ b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml @@ -8,6 +8,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="clr-namespace:BuildsAppReborn.Contracts.Models;assembly=BuildsAppReborn.Contracts" xmlns:viewModels="clr-namespace:BuildsAppReborn.Client.ViewModels" + xmlns:localControls="clr-namespace:BuildsAppReborn.Client.Controls" Title="Builds Overview" Width="500" Height="400" @@ -30,6 +31,8 @@ + + @@ -78,6 +81,7 @@ + @@ -188,6 +192,11 @@ Margin="7 0 0 0" Source="{Binding CurrentBuild.RequesterImage}" /> + + + - + diff --git a/BuildsAppReborn.Contracts.UI/BuildItem.cs b/BuildsAppReborn.Contracts.UI/BuildItem.cs index a9d8627..13b6ea4 100644 --- a/BuildsAppReborn.Contracts.UI/BuildItem.cs +++ b/BuildsAppReborn.Contracts.UI/BuildItem.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using BuildsAppReborn.Contracts.Models; using BuildsAppReborn.Infrastructure; @@ -90,6 +91,8 @@ public String Comment } } + public ITestRun CurrentTestRun => Build?.TestRuns?.FirstOrDefault(); + public String Description => $"{Build.Definition.Name} - {Build.BuildNumber}"; public Byte[] RequesterImage => Build?.Requester?.ImageData; @@ -108,6 +111,7 @@ public void Refresh() OnPropertyChanged(nameof(BuildStateTime)); OnPropertyChanged(nameof(BuildDuration)); OnPropertyChanged(nameof(BuildStatus)); + OnPropertyChanged(nameof(CurrentTestRun)); } #endregion diff --git a/BuildsAppReborn.Contracts/Models/ITestRun.cs b/BuildsAppReborn.Contracts/Models/ITestRun.cs index 5caa391..d664379 100644 --- a/BuildsAppReborn.Contracts/Models/ITestRun.cs +++ b/BuildsAppReborn.Contracts/Models/ITestRun.cs @@ -7,12 +7,14 @@ public interface ITestRun : IObjectItem, IWebItem { #region Public Properties - Int32 Id { get; } + Int32 FailedTests { get; } - Int32 IncompleteTests { get; } + Int32 Id { get; } String Name { get; } + Int32 InconclusiveTests { get; } + Int32 PassedTests { get; } String State { get; } From 7e1fb4a70196742f3080ffad06cd1244e8d03459 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Tue, 3 Apr 2018 23:02:30 +0200 Subject: [PATCH 04/17] added stacked bar graph for test runs in build --- .../BuildsAppReborn.Client.csproj | 1 + .../Controls/StackedBarGraph.xaml | 86 +++++++++++++------ .../Controls/StackedBarGraph.xaml.cs | 33 ++++++- .../StackedBarGraphHeightConverter.cs | 34 ++++++++ .../Controls/StackedItem.cs | 3 - .../Controls/StackedItemCollection.cs | 1 + .../ViewModels/BuildsStatusViewModel.cs | 13 +-- .../Views/BuildsStatusView.xaml | 6 +- 8 files changed, 134 insertions(+), 43 deletions(-) create mode 100644 BuildsAppReborn.Client/Controls/StackedBarGraphHeightConverter.cs diff --git a/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj b/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj index 562184c..8a4c43b 100644 --- a/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj +++ b/BuildsAppReborn.Client/BuildsAppReborn.Client.csproj @@ -149,6 +149,7 @@ StackedBarGraph.xaml + diff --git a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml index 8b51e63..cef6d62 100644 --- a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml +++ b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml @@ -2,6 +2,7 @@ x:Class="BuildsAppReborn.Client.Controls.StackedBarGraph" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converter="clr-namespace:BuildsAppReborn.Client.Converter" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:BuildsAppReborn.Client.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -9,32 +10,63 @@ d:DesignHeight="450" d:DesignWidth="800" mc:Ignorable="d"> + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + \ No newline at end of file diff --git a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs index 5577e59..c9deb7d 100644 --- a/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs +++ b/BuildsAppReborn.Client/Controls/StackedBarGraph.xaml.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; namespace BuildsAppReborn.Client.Controls { @@ -11,22 +12,48 @@ public partial class StackedBarGraph : UserControl public StackedBarGraph() { InitializeComponent(); - - this.DataContext = this; } #endregion #region Public Properties + public ICommand ClickCommand + { + get { return (ICommand) GetValue(ClickCommandProperty); } + set { SetValue(ClickCommandProperty, value); } + } + + public Object ClickCommandParameter + { + get { return GetValue(ClickCommandParameterProperty); } + set { SetValue(ClickCommandParameterProperty, value); } + } + public StackedItemCollection Items { get { return (StackedItemCollection) GetValue(ItemsProperty); } set { SetValue(ItemsProperty, value); } } + public String Title + { + get { return (String) GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + #endregion + + public static readonly DependencyProperty ClickCommandParameterProperty = DependencyProperty.Register( + "ClickCommandParameter", typeof(Object), typeof(StackedBarGraph), new PropertyMetadata(default(Object))); + + public static readonly DependencyProperty ClickCommandProperty = DependencyProperty.Register( + "ClickCommand", typeof(ICommand), typeof(StackedBarGraph), new PropertyMetadata(default(ICommand))); + + public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( + "Title", typeof(String), typeof(StackedBarGraph), new PropertyMetadata(default(String))); + public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register( "Items", typeof(StackedItemCollection), typeof(StackedBarGraph), new PropertyMetadata(default(StackedItemCollection))); } diff --git a/BuildsAppReborn.Client/Controls/StackedBarGraphHeightConverter.cs b/BuildsAppReborn.Client/Controls/StackedBarGraphHeightConverter.cs new file mode 100644 index 0000000..af19c36 --- /dev/null +++ b/BuildsAppReborn.Client/Controls/StackedBarGraphHeightConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace BuildsAppReborn.Client.Controls +{ + internal class StackedBarGraphHeightConverter : IMultiValueConverter + { + #region Implementation of IMultiValueConverter + + public Object Convert(Object[] values, Type targetType, Object parameter, CultureInfo culture) + { + var maxHeight = (Double) values[0]; + var count = (Int32) values[1]; + var maxCount = (Int32) values[2]; + + if (maxCount == 0) + { + return 0; + } + + var factor = count / maxCount; + + return maxHeight * factor; + } + + public Object[] ConvertBack(Object value, Type[] targetTypes, Object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Client/Controls/StackedItem.cs b/BuildsAppReborn.Client/Controls/StackedItem.cs index 61792ad..4c2c5be 100644 --- a/BuildsAppReborn.Client/Controls/StackedItem.cs +++ b/BuildsAppReborn.Client/Controls/StackedItem.cs @@ -1,5 +1,4 @@ using System; -using System.Windows.Input; using System.Windows.Media; namespace BuildsAppReborn.Client.Controls @@ -8,8 +7,6 @@ public class StackedItem { #region Public Properties - public ICommand ClickCommand { get; set; } - public Brush Color { get; set; } public String Title { get; set; } diff --git a/BuildsAppReborn.Client/Controls/StackedItemCollection.cs b/BuildsAppReborn.Client/Controls/StackedItemCollection.cs index e7917d5..51e4a54 100644 --- a/BuildsAppReborn.Client/Controls/StackedItemCollection.cs +++ b/BuildsAppReborn.Client/Controls/StackedItemCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Windows.Input; namespace BuildsAppReborn.Client.Controls { diff --git a/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs b/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs index b1bd5db..d88ade0 100644 --- a/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs +++ b/BuildsAppReborn.Client/ViewModels/BuildsStatusViewModel.cs @@ -28,8 +28,9 @@ public BuildsStatusViewModel(BuildCache buildCache) this.timer = new Timer {Interval = 10000, AutoReset = true}; // update every 10 seconds this.timer.Elapsed += (sender, args) => { OnBuildCacheUpdated(null, null); }; BuildCache.CacheUpdated += OnBuildCacheUpdated; - HistoryClickCommand = new DelegateCommand(a => Task.Run(() => OnHistoryClickCommand(a))); + HistoryClickCommand = new DelegateCommand(a => Task.Run(() => StartProcess(a?.Build?.WebUrl))); OpenArtifactCommand = new DelegateCommand(a => Task.Run(() => OnOpenArtifactCommand(a))); + TestRunClickCommand = new DelegateCommand(a => Task.Run(() => StartProcess(a?.WebUrl))); } #endregion @@ -54,6 +55,8 @@ public void OnClose() public DelegateCommand OpenArtifactCommand { get; set; } + public DelegateCommand TestRunClickCommand { get; set; } + #endregion #region Private Methods @@ -66,14 +69,6 @@ private void OnBuildCacheUpdated(Object sender, EventArgs eventArgs) this.timer?.Start(); } - private void OnHistoryClickCommand(BuildItem item) - { - using (new WaitingIndicator()) - { - StartProcess(item?.Build?.WebUrl); - } - } - private void OnOpenArtifactCommand(IArtifact artifact) { if (artifact != null) diff --git a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml index 22865f4..ac59506 100644 --- a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml +++ b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml @@ -194,8 +194,12 @@ + Visibility="{Binding CurrentBuild.CurrentTestRun, Converter={StaticResource NullVisibilityConverter}}" /> - - + + - - + + @@ -91,115 +95,123 @@ - - - - - - - - - - - - - + + + + + + + - + Visibility="{Binding CurrentBuild.CurrentTestRun, Converter={StaticResource NullVisibilityConverter}}" />--> - + @@ -291,6 +303,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 814bddb9af1866d0823bed44dd0a22e4bf63132c Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Sun, 8 Apr 2018 04:05:17 +0200 Subject: [PATCH 13/17] added support to load SVG user images --- .../BuildsAppReborn.Access.csproj | 4 +++ .../TFS/TfsBuildProviderBase.cs | 34 ++++++++++++++++--- BuildsAppReborn.Access/packages.config | 1 + 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj b/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj index cb1787f..e7e0a76 100644 --- a/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj +++ b/BuildsAppReborn.Access/BuildsAppReborn.Access.csproj @@ -38,9 +38,13 @@ ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll True + + ..\packages\Svg.2.3.0\lib\net35\Svg.dll + + diff --git a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs index bf8cf4f..5d371f4 100644 --- a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs +++ b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Drawing; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -11,6 +13,7 @@ using BuildsAppReborn.Infrastructure; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Svg; namespace BuildsAppReborn.Access { @@ -20,7 +23,7 @@ internal abstract class TfsBuildProviderBase>> GetBuilds(IEnumerab private static async Task GetImageData(BuildMonitorSettings settings, IUser user) { - var response = await GetRequestResponse(user.ImageUrl, settings); - if (response.IsSuccessStatusCode) + try { - return await response.Content.ReadAsByteArrayAsync(); + var response = await GetRequestResponse(user.ImageUrl, settings); + if (response.IsSuccessStatusCode) + { + if (response.Content.Headers.ContentType.MediaType == "image/png") + { + return await response.Content.ReadAsByteArrayAsync(); + } + + if (response.Content.Headers.ContentType.MediaType == "image/svg+xml") + { + using (var memoryStream = new MemoryStream()) + { + await response.Content.CopyToAsync(memoryStream); + memoryStream.Position = 0; + var svgDoc = SvgDocument.Open(memoryStream); + + var converter = new ImageConverter(); + return (Byte[]) converter.ConvertTo(svgDoc.Draw(128, 128), typeof(Byte[])); + } + } + } + } + catch (Exception) + { + return null; } return null; diff --git a/BuildsAppReborn.Access/packages.config b/BuildsAppReborn.Access/packages.config index 1dd904b..7053b92 100644 --- a/BuildsAppReborn.Access/packages.config +++ b/BuildsAppReborn.Access/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file From 8cafbd175e068bd902f5d271c7a027a8b6d05009 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Sun, 8 Apr 2018 04:21:57 +0200 Subject: [PATCH 14/17] expanded additional information now stays open --- BuildsAppReborn.Client/Cache/BuildCache.cs | 11 +- .../Views/BuildsStatusView.xaml | 418 +++++++++--------- .../BuildStatusGroup.cs | 31 +- 3 files changed, 240 insertions(+), 220 deletions(-) diff --git a/BuildsAppReborn.Client/Cache/BuildCache.cs b/BuildsAppReborn.Client/Cache/BuildCache.cs index 2111fb8..bf868ee 100644 --- a/BuildsAppReborn.Client/Cache/BuildCache.cs +++ b/BuildsAppReborn.Client/Cache/BuildCache.cs @@ -90,10 +90,15 @@ private void OnBuildsUpdated(ICollection builds) var groupByDefinition = builds.GroupBy(a => a.Definition, build => build, this.buildDefinitionEqualityComparer); foreach (var grp in groupByDefinition) { - var newStatus = new BuildStatusGroup(); + var oldStatus = BuildsStatus.SingleOrDefault(a => this.buildDefinitionEqualityComparer.Equals(grp.Key, a.BuildDefinition)); + var newStatus = new BuildStatusGroup(grp.Key, grp.Select(a => new BuildItem(a)).ToList()); + + if (oldStatus != null) + { + // ToDo: implement proper update of bound viewmodel objects instead of creating new ones everytime + newStatus.AdditionalInformationShown = oldStatus.AdditionalInformationShown; + } - newStatus.BuildDefinition = grp.Key; - newStatus.AllBuildItems = grp.Select(a => new BuildItem(a)).ToList(); buildStatusGroups.Add(newStatus); } Application.Current.Dispatcher.Invoke(() => diff --git a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml index 091bb18..c177b22 100644 --- a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml +++ b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml @@ -81,229 +81,229 @@ - - - - - - - - - - - - - - - + + + + + + + + + - - - + - - - - - - - - - - - - - - - - + + + + + + + + + - - + + - - - - - - - - + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + - - - - - - + Source="{Binding CurrentBuild.Build.Status, Converter={StaticResource BuildStatusToImageConverter}}" /> + - + - - - - - + + + + + + - - + diff --git a/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs b/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs index 1012d87..51c9702 100644 --- a/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs +++ b/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs @@ -1,24 +1,39 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using BuildsAppReborn.Contracts.Models; +using BuildsAppReborn.Infrastructure; namespace BuildsAppReborn.Contracts.UI { - public class BuildStatusGroup + public class BuildStatusGroup : ViewModelBase { + #region Constructors + + public BuildStatusGroup(IBuildDefinition buildDefinition, IEnumerable buildItems) + { + BuildDefinition = buildDefinition; + + AllBuildItems = buildItems?.OrderBy(a => a.Build?.QueueDateTime).ToList(); + } + + #endregion + #region Public Properties - public List AllBuildItems + public Boolean AdditionalInformationShown { - get { return this.allBuildItems; } + get { return this.additionalInformationShown; } set { - this.allBuildItems = value; - this.allBuildItems = this.allBuildItems?.OrderBy(a => a.Build?.QueueDateTime).ToList(); + this.additionalInformationShown = value; + OnPropertyChanged(); } } - public IBuildDefinition BuildDefinition { get; set; } + public IEnumerable AllBuildItems { get; } + + public IBuildDefinition BuildDefinition { get; } public BuildItem CurrentBuild => AllBuildItems.Last(); @@ -28,7 +43,7 @@ public List AllBuildItems #region Private Fields - private List allBuildItems; + private Boolean additionalInformationShown; #endregion } From e5d361d9439eebbd161eea51c728c1e52a4c6750 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Sun, 8 Apr 2018 16:56:48 +0200 Subject: [PATCH 15/17] changed test names to be the orignal provided --- BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs | 5 ++--- .../Converter/TestRunToStackedItemsConverter.cs | 6 +++--- BuildsAppReborn.Contracts/Models/ITestRun.cs | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs index cd72db2..52ab1a6 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs @@ -1,7 +1,6 @@ using System; using BuildsAppReborn.Contracts.Models; using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; namespace BuildsAppReborn.Access.Models { @@ -19,13 +18,13 @@ internal abstract class TfsTestRun : ITestRun public Int32 Id { get; private set; } [JsonProperty("incompleteTests")] - public Int32 FailedTests { get; private set; } + public Int32 IncompleteTests { get; private set; } [JsonProperty("name")] public String Name { get; private set; } [JsonProperty("notApplicableTests")] - public Int32 InconclusiveTests { get; private set; } + public Int32 NotApplicableTests { get; private set; } [JsonProperty("passedTests")] public Int32 PassedTests { get; private set; } diff --git a/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs index 3843425..f5f7472 100644 --- a/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs +++ b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs @@ -23,10 +23,10 @@ public Object Convert(Object value, Type targetType, Object parameter, CultureIn var stackedItems = new List(); - stackedItems.Add(new StackedItem {Value = testRun.FailedTests, Title = "Failed Tests", Color = Brushes.Red, Icon = IconProvider.FailIcon}); - stackedItems.Add(new StackedItem {Value = testRun.InconclusiveTests, Title = "Inconclusive Tests", Color = Brushes.Yellow, Icon = IconProvider.UnknownIcon}); + stackedItems.Add(new StackedItem {Value = testRun.IncompleteTests, Title = "Incomplete Tests", Color = Brushes.Red, Icon = IconProvider.FailIcon}); + stackedItems.Add(new StackedItem {Value = testRun.NotApplicableTests, Title = "Not Applicable Tests", Color = Brushes.Yellow, Icon = IconProvider.UnknownIcon}); stackedItems.Add(new StackedItem {Value = testRun.PassedTests, Title = "Passed Tests", Color = Brushes.Green, Icon = IconProvider.SuccessIcon}); - stackedItems.Add(new StackedItem {Value = testRun.UnanalyzedTests, Title = "Not run Tests", Color = Brushes.Blue, Icon = IconProvider.LoadingIcon}); + stackedItems.Add(new StackedItem {Value = testRun.UnanalyzedTests, Title = "Unanalyzed Tests", Color = Brushes.Blue, Icon = IconProvider.LoadingIcon}); return new StackedItemCollection(stackedItems) {TotalCount = testRun.TotalTests, Title = "Total Tests"}; } diff --git a/BuildsAppReborn.Contracts/Models/ITestRun.cs b/BuildsAppReborn.Contracts/Models/ITestRun.cs index d664379..a43780f 100644 --- a/BuildsAppReborn.Contracts/Models/ITestRun.cs +++ b/BuildsAppReborn.Contracts/Models/ITestRun.cs @@ -7,13 +7,13 @@ public interface ITestRun : IObjectItem, IWebItem { #region Public Properties - Int32 FailedTests { get; } + Int32 IncompleteTests { get; } Int32 Id { get; } String Name { get; } - Int32 InconclusiveTests { get; } + Int32 NotApplicableTests { get; } Int32 PassedTests { get; } From 37e4d56b353d2af390e367164007948bca8a39c0 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Sun, 15 Apr 2018 18:43:55 +0200 Subject: [PATCH 16/17] additional information only shown if there are any available --- BuildsAppReborn.Client/Views/BuildsStatusView.xaml | 10 ++++++++-- BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml index c177b22..988d377 100644 --- a/BuildsAppReborn.Client/Views/BuildsStatusView.xaml +++ b/BuildsAppReborn.Client/Views/BuildsStatusView.xaml @@ -303,9 +303,15 @@ - + - + diff --git a/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs b/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs index 51c9702..222f859 100644 --- a/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs +++ b/BuildsAppReborn.Contracts.UI/BuildStatusGroup.cs @@ -21,6 +21,8 @@ public BuildStatusGroup(IBuildDefinition buildDefinition, IEnumerable #region Public Properties + public Boolean AdditionalInformationAvailable => AllBuildItems.Any(a => a.CurrentTestRun != null); + public Boolean AdditionalInformationShown { get { return this.additionalInformationShown; } From 458a408ba884679187b664b28b93a63672add068 Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Thu, 19 Apr 2018 14:13:15 +0200 Subject: [PATCH 17/17] changed test run test names (again) --- BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs | 2 +- .../Converter/TestRunToStackedItemsConverter.cs | 4 ++-- BuildsAppReborn.Contracts/Models/ITestRun.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs index 52ab1a6..648e705 100644 --- a/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs +++ b/BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs @@ -36,7 +36,7 @@ internal abstract class TfsTestRun : ITestRun public Int32 TotalTests { get; private set; } [JsonProperty("unanalyzedTests")] - public Int32 UnanalyzedTests { get; private set; } + public Int32 FailedTests { get; private set; } #endregion } diff --git a/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs index f5f7472..53b71da 100644 --- a/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs +++ b/BuildsAppReborn.Client/Converter/TestRunToStackedItemsConverter.cs @@ -23,10 +23,10 @@ public Object Convert(Object value, Type targetType, Object parameter, CultureIn var stackedItems = new List(); - stackedItems.Add(new StackedItem {Value = testRun.IncompleteTests, Title = "Incomplete Tests", Color = Brushes.Red, Icon = IconProvider.FailIcon}); + stackedItems.Add(new StackedItem {Value = testRun.FailedTests, Title = "Failed Tests", Color = Brushes.Red, Icon = IconProvider.FailIcon}); stackedItems.Add(new StackedItem {Value = testRun.NotApplicableTests, Title = "Not Applicable Tests", Color = Brushes.Yellow, Icon = IconProvider.UnknownIcon}); stackedItems.Add(new StackedItem {Value = testRun.PassedTests, Title = "Passed Tests", Color = Brushes.Green, Icon = IconProvider.SuccessIcon}); - stackedItems.Add(new StackedItem {Value = testRun.UnanalyzedTests, Title = "Unanalyzed Tests", Color = Brushes.Blue, Icon = IconProvider.LoadingIcon}); + stackedItems.Add(new StackedItem {Value = testRun.IncompleteTests, Title = "Incomplete Tests", Color = Brushes.Blue, Icon = IconProvider.LoadingIcon}); return new StackedItemCollection(stackedItems) {TotalCount = testRun.TotalTests, Title = "Total Tests"}; } diff --git a/BuildsAppReborn.Contracts/Models/ITestRun.cs b/BuildsAppReborn.Contracts/Models/ITestRun.cs index a43780f..c66e51e 100644 --- a/BuildsAppReborn.Contracts/Models/ITestRun.cs +++ b/BuildsAppReborn.Contracts/Models/ITestRun.cs @@ -7,10 +7,12 @@ public interface ITestRun : IObjectItem, IWebItem { #region Public Properties - Int32 IncompleteTests { get; } + Int32 FailedTests { get; } Int32 Id { get; } + Int32 IncompleteTests { get; } + String Name { get; } Int32 NotApplicableTests { get; } @@ -21,8 +23,6 @@ public interface ITestRun : IObjectItem, IWebItem Int32 TotalTests { get; } - Int32 UnanalyzedTests { get; } - #endregion } } \ No newline at end of file