diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config new file mode 100644 index 0000000..67f8ea0 --- /dev/null +++ b/.nuget/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe new file mode 100644 index 0000000..8f61340 Binary files /dev/null and b/.nuget/NuGet.exe differ diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets new file mode 100644 index 0000000..83fe906 --- /dev/null +++ b/.nuget/NuGet.targets @@ -0,0 +1,136 @@ + + + + $(MSBuildProjectDirectory)\..\ + + + false + + + false + + + true + + + false + + + + + + + + + + + $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) + $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) + + + + + $(SolutionDir).nuget + packages.config + + + + + $(NuGetToolsPath)\NuGet.exe + @(PackageSource) + + "$(NuGetExePath)" + mono --runtime=v4.0.30319 $(NuGetExePath) + + $(TargetDir.Trim('\\')) + + -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + + + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols + + + + RestorePackages; + $(BuildDependsOn); + + + + + $(BuildDependsOn); + BuildPackage; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestRail.sln b/TestRail.sln new file mode 100644 index 0000000..6d28958 --- /dev/null +++ b/TestRail.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C7307807-F4CD-44C6-AB5D-BD5F38D575DF}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.Config = .nuget\NuGet.Config + .nuget\NuGet.exe = .nuget\NuGet.exe + .nuget\NuGet.targets = .nuget\NuGet.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestRail", "TestRail\TestRail.csproj", "{68C04A3F-DA7C-4588-8AA8-11A01C13630B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68C04A3F-DA7C-4588-8AA8-11A01C13630B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68C04A3F-DA7C-4588-8AA8-11A01C13630B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68C04A3F-DA7C-4588-8AA8-11A01C13630B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68C04A3F-DA7C-4588-8AA8-11A01C13630B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestRail/CommandResult.cs b/TestRail/CommandResult.cs new file mode 100644 index 0000000..5f94fac --- /dev/null +++ b/TestRail/CommandResult.cs @@ -0,0 +1,45 @@ +using System; + +namespace TestRail +{ + /// represents the result of a command + public class CommandResult : CommandResult + { + /// constructor + /// true if the command was successful + /// result of the command + /// exception thrown by the command + public CommandResult(bool wasSuccessful, string result, Exception e = null) : base(wasSuccessful, result, e) { } + } + + /// represents the result of a command + /// type of the result + public class CommandResult + { + /// true if the command was successful + public bool WasSuccessful { get; set; } + /// result of the command + public T Value { get; set; } + /// exception thrown by the command + public Exception Exception { get; set; } + + /// parameterless constructor + public CommandResult() + { + WasSuccessful = false; + Value = default(T); + Exception = null; + } + + /// constructor + /// true if the command was successful + /// result of the command + /// exception thrown by the command + public CommandResult(bool wasSuccessful, T result, Exception e=null) + { + WasSuccessful = wasSuccessful; + Value = result; + Exception = e; + } + } +} diff --git a/TestRail/DateTimeExtensions.cs b/TestRail/DateTimeExtensions.cs new file mode 100644 index 0000000..a6de976 --- /dev/null +++ b/TestRail/DateTimeExtensions.cs @@ -0,0 +1,23 @@ +using System; + +namespace TestRail +{ + /// extension methods for the datetime class + public static class DateTimeExtensions + { + /// converts the date to a unix timestamp + /// a unix time stamp representing the birthday + public static double ToUnixTimestamp(this DateTime dt) + { + return dt.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + } + + /// generates a datetime from a unix timestamp + /// a unix timestamp + /// datetime corresponding to the supplied unix timestamp + public static DateTime FromUnixTimeStamp(double timestamp) + { + return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(timestamp).ToLocalTime(); + } + } +} diff --git a/TestRail/JsonUtility.cs b/TestRail/JsonUtility.cs new file mode 100644 index 0000000..07fd790 --- /dev/null +++ b/TestRail/JsonUtility.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace TestRail +{ + /// + /// Helper class for Json Objects + /// + internal static class JsonUtility + { + /// Merge two Json Objects + /// object 1 to merge + /// object 2 to merge + /// a non null Json object (NOTE: may be empty) + internal static JObject Merge(JObject obj1, JObject obj2) + { + if (null == obj1) + { + obj1 = new JObject(); + } + + if (null != obj2) + { + JToken token = obj2.First; + while (null != token) + { + obj1.Add(token); + token = token.Next; + } + } + return obj1; + } + + /// Converts a JArray into a List of type T + /// JArray to parse + /// returns a list of objects corresponding to the json, empty list if nothing exists + internal static List ConvertJArrayToList(JArray jarray, Func parse) + { + List list = new List(); + if (null != jarray && null != parse) + { + foreach (JObject json in jarray) + { + list.Add(parse(json)); + } + } + return list; + } + } +} diff --git a/TestRail/Properties/AssemblyInfo.cs b/TestRail/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5782804 --- /dev/null +++ b/TestRail/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestRail")] +[assembly: AssemblyDescription("TestRail Client Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Zoosk")] +[assembly: AssemblyProduct("TestRail Client Library for .NET")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b91a1174-5709-412a-9e95-ccaad637454c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/TestRail/TestRail.csproj b/TestRail/TestRail.csproj new file mode 100644 index 0000000..fe781a1 --- /dev/null +++ b/TestRail/TestRail.csproj @@ -0,0 +1,89 @@ + + + + + Debug + AnyCPU + {68C04A3F-DA7C-4588-8AA8-11A01C13630B} + Library + Properties + TestRail + TestRail + v4.5 + 512 + + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 0 + false + bin\Debug\TestRail.XML + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestRail/TestRailClient.cs b/TestRail/TestRailClient.cs new file mode 100644 index 0000000..61f2a44 --- /dev/null +++ b/TestRail/TestRailClient.cs @@ -0,0 +1,1117 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using TestRail.Types; + +namespace TestRail +{ + /// client used to access test case data in testrail + public class TestRailClient + { + /// url for testrail + protected string _URL_; + /// testrail username + protected string _UserName_; + /// testrail password + protected string _Password_; + /// projects in the test rail database + public List Projects { get { return _Projects.Value; } } + + /// called when the client sends an http request + public event EventHandler OnHTTPRequestSent = (s, e) => { }; + /// called when the client receives an http response + public event EventHandler OnHTTPResponseReceived = (s, e) => { }; + /// called when an operation fails + public event EventHandler OnOperationFailed = (s, e) => { }; + + /// event args for http request sent + public class HTTPRequestSentEventArgs : EventArgs + { + /// http method (GET, POST, PUT, DELETE, etc.) + public string Method; + /// uri + public Uri Uri; + /// post data + public string PostContent; + + /// constructor + /// http method used + /// uri used + /// post content sent (if any) + public HTTPRequestSentEventArgs(string method, Uri uri, string postContent = null) + { + this.Method = method; + this.Uri = uri; + this.PostContent = postContent; + } + } + + private Lazy> _Projects; + + private Dictionary _PriorityIDToLevel { get { return _LazyPriorityIDToLevel.Value; } } + + private Lazy> _LazyPriorityIDToLevel { get; set; } + + #region Constants + protected const string _NODE_CASE_ = "case"; + protected const string _NODE_CASES_ = "cases"; + protected const string _NODE_CASE_TYPES_ = "case_types"; + protected const string _NODE_CASE_FIELDS_ = "case_fields"; + protected const string _NODE_MILESTONE_ = "milestone"; + protected const string _NODE_MILESTONES_ = "milestones"; + protected const string _NODE_PLAN_ = "plan"; + protected const string _NODE_PLANS_ = "plans"; + protected const string _NODE_PLAN_ENTRY_ = "plan_entry"; + protected const string _NODE_PROJECT_ = "project"; + protected const string _NODE_RESULTS_ = "results"; + protected const string _NODE_RESULTS_FOR_CASE_ = "results_for_case"; + protected const string _NODE_RUN_ = "run"; + protected const string _NODE_RUNS_ = "runs"; + protected const string _NODE_SECTION_ = "section"; + protected const string _NODE_SECTIONS_ = "sections"; + protected const string _NODE_SUITE_ = "suite"; + protected const string _NODE_SUITES_ = "suites"; + protected const string _NODE_TEST_ = "test"; + protected const string _NODE_TESTS_ = "tests"; + protected const string _NODE_USER_ = "user"; + protected const string _NODE_USERS_ = "users"; + #endregion Constants + + #region Constructor + /// constructor + /// url for test rail + /// user name + /// password + public TestRailClient(string url, string username, string password) + { + _URL_ = url; + _UserName_ = username; + _Password_ = password; + + _Projects = new Lazy>(() => GetProjects()); + + // set up the lazy loading of the priority dictionary (priority id to priority value) + _LazyPriorityIDToLevel = new Lazy>(() => _CreatePrioritiesDict()); + } + #endregion Constructor + + #region Public Methods + + /// + /// Get the priority for the case if we can + /// + /// case to get the priority from + /// int value of priority if possible, null if not found + public int? GetPriorityForCase(Case c) + { + int? priority = null; + if (null != c && c.PriorityID.HasValue && null != _PriorityIDToLevel && _PriorityIDToLevel.ContainsKey(c.PriorityID.Value)) + { + priority = _PriorityIDToLevel[c.PriorityID.Value]; + } + return priority; + } + + #region Add Commands + /// adds a result for a test + /// id of the test + /// status of the result + /// comment to log + /// version + /// time elapsed to complete the test + /// defects associated with the result + /// id of the user the result is assigned to + /// result of the command + public CommandResult AddResult(ulong testID, ResultStatus? status, string comment = null, string version = null, + TimeSpan? elapsed = null, string defects = null, ulong? assignedToID = null, JObject customs = null) + { + string uri = _CreateUri_(_CommandType_.add, "result", testID); + Result r = new Result() { TestID = testID, StatusID = (ulong?)status, Comment = comment, Version = version, Elapsed = elapsed, Defects = defects, AssignedToID = assignedToID }; + JObject jsonParams = JsonUtility.Merge(r.GetJson(), customs); + return _SendCommand(uri, jsonParams); + } + + /// creates a new test result for a test run and case combination + /// the id of the test run + /// the id of the test case + /// status of the result + /// comment to log + /// version + /// time elapsed to complete the test + /// defects associated with the result + /// id of the user the result is assigned to + /// + public CommandResult AddResultForCase(ulong runID, ulong caseID, ResultStatus? status, string comment = null, string version = null, + TimeSpan? elapsed = null, string defects = null, ulong? assignedToID = null, JObject customs = null) + { + string uri = _CreateUri_(_CommandType_.add, "result_for_case", runID, caseID); + + Result r = new Result() { StatusID = (ulong?)status, Comment = comment, Version = version, Elapsed = elapsed, Defects = defects, AssignedToID = assignedToID }; + JObject jsonParams = JsonUtility.Merge(r.GetJson(), customs); + //JObject jsonParams = JsonHelper.Merge(_CreateJsonForResult(status, comment, version, elapsed, defects, assignedToID), customs); + return _SendCommand(uri, jsonParams); + + } + + /// adds a run + /// id of the project + /// id of the suite + /// name of the run + /// description of the run + /// id of the milestone + /// id of the user the run should be assigned to + ///(optional)an array of case IDs for the custom case selection, if null, then will include all case ids from the suite + /// result of the command + public CommandResult AddRun(ulong projectID, ulong suiteID, string name, string description, ulong milestoneID, ulong? assignedToID = null, HashSet caseIDs = null) + { + bool includeAll = true; + + // validates whether we are in include all or custom case selection mode + if (null != caseIDs) + { + bool atLeastOneCaseFoundInSuite = _CasesFoundInSuite(projectID, suiteID, caseIDs); + if (atLeastOneCaseFoundInSuite) + { + includeAll = false; + } + else + { + return new CommandResult(false, 0, new Exception("Case IDs not found in the Suite")); + } + } + + string uri = _CreateUri_(_CommandType_.add, _NODE_RUN_, projectID); + Run r = new Run() { SuiteID = suiteID, Name = name, Description = description, MilestoneID = milestoneID, AssignedTo = assignedToID, IncludeAll = includeAll, CaseIDs = caseIDs }; + return _SendCommand(uri, r.GetJson()); + } + + /// Add a case + /// section id to add the case to + /// title of the case + /// (optional)the ID of the case type + /// (optional)the id of the case priority + /// (optional)the estimate, e.g. "30s" or "1m 45s" + /// (optional)the ID of the milestone to link to the test case + /// (optional)a comma-separated list of references/requirements + /// result of the command + public CommandResult AddCase(ulong sectionID, string title, ulong? typeID = null, ulong? priorityID = null, string estimate = null, ulong? milestoneID = null, string refs = null) + { + return _AddCase_(sectionID, title, typeID, priorityID, estimate, milestoneID, refs, null); + } + + /// Add a project + /// the name of the project + /// (optional)the description of the project + /// (optional)true if the announcement should be displayed on the project's overview page and false otherwise + /// result of the command + public CommandResult AddProject(string projectName, string announcement = null, bool? showAnnouncement = null) + { + if (string.IsNullOrWhiteSpace(projectName)) + { + return new CommandResult(false, 0, new ArgumentNullException("projectName")); + } + + string uri = _CreateUri_(_CommandType_.add, _NODE_PROJECT_); + Project p = new Project() { Name = projectName, Announcement = announcement, ShowAnnouncement = showAnnouncement }; + return _SendCommand(uri, p.GetJson()); + } + + /// creates a new section + /// the ID of the project + /// the ID of the test suite + /// the name of the section + /// (optional)the ID of the parent section (to build section hierarchies) + /// result of the command + public CommandResult AddSection(ulong projectID, ulong suiteID, string name, ulong? parentID = null) + { + if (string.IsNullOrWhiteSpace(name)) + { + return new CommandResult(false, 0, new ArgumentNullException("name")); + } + + string uri = _CreateUri_(_CommandType_.add, _NODE_SECTION_, projectID); + Section s = new Section() { SuiteID = suiteID, ParentID = parentID, Name = name }; + return _SendCommand(uri, s.GetJson()); + } + + /// Creates a new test suite + /// the ID of the project the test suite should be added to + /// the name of the test suite + /// (optional)the description of the test suite + /// result of the command + public CommandResult AddSuite(ulong projectID, string name, string description = null) + { + if (string.IsNullOrWhiteSpace(name)) + { + return new CommandResult(false, 0, new ArgumentNullException("name")); + } + + string uri = _CreateUri_(_CommandType_.add, _NODE_SUITE_, projectID); + Suite s = new Suite() { Name = name, Description = description }; + return _SendCommand(uri, s.GetJson()); + } + + /// creates a new plan + /// id of the project the test plan should be added to + /// name of the test plan + /// (optional)description of the test plan + /// (optional)id of the milestone to link the test plan + /// an array of objects describing the test runs of the plan + /// result of the command + public CommandResult AddPlan(ulong projectID, string name, string description = null, ulong? milestoneID = null, List entries = null) + // , params ulong[] suiteIDs) + { + if (string.IsNullOrWhiteSpace(name)) + { + return new CommandResult(false, 0, new ArgumentNullException("name")); + } + + string uri = _CreateUri_(_CommandType_.add, _NODE_PLAN_, projectID); + Plan p = new Plan() { Name = name, Description = description, MilestoneID = milestoneID, Entries = entries }; + JObject jsonParams = p.GetJson(); + return _SendCommand(uri, jsonParams); + } + + /// Creates a new test run for a test plan + /// the ID of the plan the test run should be added to + /// the ID of the test suite for the test run + /// (optional)the name of the test run + /// (optional)the ID of the user the test run should be assigned to + /// (optional)true for including all test cases of the test suite and false for a custom selection (default: true) + /// + public CommandResult AddPlanEntry(ulong planID, ulong suiteID, string name = null, ulong? assignedToID = null, List caseIDs = null) + { + string uri = _CreateUri_(_CommandType_.add, _NODE_PLAN_ENTRY_, planID); + PlanEntry pe = new PlanEntry() { AssignedToID = assignedToID, SuiteID = suiteID, Name = name, CaseIDs = caseIDs }; + JObject jsonParams = pe.GetJson(); + return _SendCommand(uri, jsonParams); + } + + /// adds a milestone + /// id of the project + /// name of the milestone + /// (optional)description of the milestone + /// (optional)date on which the milestone is due + /// result of the command + public CommandResult AddMilestone(ulong projectID, string name, string description = null, DateTime? dueOn = null) + { + string uri = _CreateUri_(_CommandType_.add, _NODE_MILESTONE_, projectID); + Milestone m = new Milestone() { Name = name, Description = description, DueOn = dueOn }; + return _SendCommand(uri, m.GetJson()); + } + + #endregion Add Commands + + #region Update Commands + /// update an existing case + /// the ID of the test case + /// title of the case + /// (optional)the ID of the case type + /// (optional)the id of the case priority + /// (optional)the estimate, e.g. "30s" or "1m 45s" + /// (optional)the ID of the milestone to link to the test case + /// (optional)a comma-separated list of references/requirements + /// result of the command + public CommandResult UpdateCase(ulong caseID, string title, ulong? typeID = null, ulong? priorityID = null, string estimate = null, ulong? milestoneID = null, string refs = null) + { + return _UpdateCase_(caseID, title, typeID, priorityID, estimate, milestoneID, refs, null); + } + + + /// update an existing milestone + /// id of the milestone + /// (optional)name of the milestone + /// (optional)description of the milestone + /// (optional)date on which the milestone is due + /// result of the command + public CommandResult UpdateMilestone(ulong milestoneID, string name = null, string description = null, DateTime? dueOn = null, bool? isCompleted = null) + { + string uri = _CreateUri_(_CommandType_.update, _NODE_MILESTONE_, milestoneID); + Milestone m = new Milestone() { Name = name, Description = description, DueOn = dueOn, IsCompleted = isCompleted }; + return _SendCommand(uri, m.GetJson()); + } + + /// Update an existing plan + /// id of the plan + /// (optional)name of the test plan + /// (optional)the description of the test plan + /// (optional)the id of the milestone to link to the test plan + /// + public CommandResult UpdatePlan(ulong planID, string name = null, string description = null, ulong? milestoneID = null) + { + string uri = _CreateUri_(_CommandType_.update, _NODE_PLAN_, planID); + Plan p = new Plan() { Name = name, Description = description, MilestoneID = milestoneID }; + JObject jsonParams = p.GetJson(); + return _SendCommand(uri, jsonParams); + } + + /// Creates a new test run for a test plan + /// the ID of the plan the test run should be added to + /// the ID of the test plan entry + /// (optional)the name of the test run + /// (optional)the ID of the user the test run should be assigned to + /// (optional)true for including all test cases of the test suite and false for a custom selection (default: true) + /// + public CommandResult UpdatePlanEntry(ulong planID, string entryID, string name = null, ulong? assignedToID = null, List caseIDs = null) + { + string uri = _CreateUri_(_CommandType_.update, _NODE_PLAN_ENTRY_, planID, null, null, entryID); + PlanEntry pe = new PlanEntry() { AssignedToID = assignedToID, Name = name, CaseIDs = caseIDs }; + JObject jsonParams = pe.GetJson(); + return _SendCommand(uri, jsonParams); + } + + /// Update an existing project + /// the id of the project + /// the name of the project + /// (optional)the description of the project + /// (optional)true if the announcement should be displayed on the project's overview page and false otherwise + /// (optional)specifies whether a project is considered completed or not + /// + public CommandResult UpdateProject(ulong projectID, string projectName, string announcement = null, bool? showAnnouncement = null, bool? isCompleted = null) + { + string uri = _CreateUri_(_CommandType_.update, _NODE_PROJECT_, projectID); + Project p = new Project() { Name = projectName, Announcement = announcement, ShowAnnouncement = showAnnouncement, IsCompleted = isCompleted }; + return _SendCommand(uri, p.GetJson()); + } + + /// update an existing test run + /// the id of an existing run + /// (optional)name of the test run + /// (optional)description of the test run + /// (optional)the id of the milestone to link to the test run + /// (optional)true for including all test cases of the test suite and false for a custom case selection + ///an array of case IDs for the custom case selection + /// + public CommandResult UpdateRun(ulong runID, string name = null, string description = null, ulong? milestoneID = null, HashSet caseIDs = null) + { + bool includeAll = true; + Run run = GetRun(runID); + + // validates whether we are in include all or custom case selection mode + if (null != run && run.ProjectID.HasValue && run.SuiteID.HasValue && null != caseIDs) + { + bool atLeastOneCaseFoundInSuite = _CasesFoundInSuite(run.ProjectID.Value, run.SuiteID.Value, caseIDs); + if (atLeastOneCaseFoundInSuite) + { + includeAll = false; + } + else + { + return new CommandResult(false, 0, new Exception("Case IDs not found in the Suite")); + } + } + + string uri = _CreateUri_(_CommandType_.update, _NODE_RUN_, runID); + Run r = new Run() { Name = name, Description = description, MilestoneID = milestoneID, IncludeAll = includeAll, CaseIDs = caseIDs }; + return _SendCommand(uri, r.GetJson()); + } + + + /// Updates an existing section + /// id of the section to update + /// name of the section + /// + public CommandResult UpdateSection(ulong sectionID, string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return new CommandResult(false, 0, new ArgumentNullException("name")); + } + + string uri = _CreateUri_(_CommandType_.update, _NODE_SECTION_, sectionID); + Section s = new Section() { ID = sectionID, Name = name }; + return _SendCommand(uri, s.GetJson()); + } + + /// Update an existing suite + /// id of the suite to update + /// (optional)new name to update to + /// (optional)new description to update to + /// + public CommandResult UpdateSuite(ulong suiteID, string name = null, string description = null) + { + string uri = _CreateUri_(_CommandType_.update, _NODE_SUITE_, suiteID); + Suite s = new Suite() { Name = name, Description = description }; + return _SendCommand(uri, s.GetJson()); + } + #endregion Update Commands + + #region Close Commands + /// closes a plan + /// id of the plan + /// true if successful + public bool ClosePlan(ulong planID) + { + string uri = _CreateUri_(_CommandType_.close, _NODE_PLAN_, planID); + + var result = _CallPostEndpoint(uri); + if (result.WasSuccessful) + { + JObject json = JObject.Parse(result.Value); + return result.WasSuccessful; + } + + OnOperationFailed(this, "Could not close plan : " + result.Value); + return false; + } + + /// closes a run + /// id of the run + /// true if successful + public bool CloseRun(ulong runID) + { + string uri = _CreateUri_(_CommandType_.close, _NODE_RUN_, runID); + var result = _CallPostEndpoint(uri); + // var result = _CallPostEndpoint("?/api/v2/close_run/" + runID.ToString()); + if (result.WasSuccessful) + { + JObject json = JObject.Parse(result.Value); + return result.WasSuccessful; + } + + OnOperationFailed(this, "Could not close run : " + result.Value); + return false; + } + #endregion Close Commands + + #region Delete Commands + /// Delete a milestone + /// id of the milestone + /// result of the deletion + public CommandResult DeleteMilestone(ulong milestoneID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_MILESTONE_, milestoneID); + return _SendCommand(uri); + } + + /// Delete a case + /// id of the case to delete + /// result of the deletion + public CommandResult DeleteCase(ulong caseID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_CASE_, caseID); + return _SendCommand(uri); + } + + /// Delete a plan + /// id of the plan to delete + /// result of the deletion + public CommandResult DeletePlan(ulong planID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_PLAN_, planID); + return _SendCommand(uri); + } + + /// Delete a specific plan entry for a plan id + /// id of the plan + /// string representation of the GUID for the entryID + /// result of the deletion + public CommandResult DeletePlanEntry(ulong planID, string entryID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_PLAN_ENTRY_, planID, null, null, entryID); + return _SendCommand(uri); + } + + /// Delete the Project + /// id of the project to delete + /// result of the deletion + public CommandResult DeleteProject(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_PROJECT_, projectID); + return _SendCommand(uri); + } + + /// Delete the section + /// id of the section to delete + /// result of the deletion + public CommandResult DeleteSection(ulong sectionID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_SECTION_, sectionID); + return _SendCommand(uri); + } + + /// Delete the suite + /// id of the suite to delete + /// result of the deletion + public CommandResult DeleteSuite(ulong suiteID) + { + string uri = _CreateUri_(_CommandType_.delete, _NODE_SUITE_, suiteID); + return _SendCommand(uri); + } + #endregion Delete Commands + + #region Get Commands + /// gets a test + /// id of the test + /// information about the test + public Test GetTest(ulong testID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_TEST_, testID); + return _GetItem_(_NODE_TEST_, uri, Test.Parse); + } + + /// gets tests associated with a run + /// id of the run + /// tests associated with the run + public List GetTests(ulong runID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_TESTS_, runID); + return _GetItems_(_NODE_TESTS_, uri, Test.Parse); + } + + /// gets a case + /// id of the case + /// information about the case + public Case GetCase(ulong caseID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_CASE_, caseID); + return _GetItem_(_NODE_CASE_, uri, Case.Parse); + } + + /// gets cases associated with a suite + /// id of the project + /// id of the suite + /// (optional) id of the section + /// cases associated with the suite + public List GetCases(ulong projectID, ulong suiteID, ulong? sectionID = null) + { + string optionalSectionID = sectionID.HasValue ? string.Format("§ion_id={0}", sectionID.Value) : string.Empty; + string options = string.Format("&suite_id={0}{1}", suiteID, optionalSectionID); + string uri = _CreateUri_(_CommandType_.get, _NODE_CASES_, projectID, null, options); + return _GetItems_(_NODE_CASES_, uri, Case.Parse); + } + + public List GetCaseFields() + { + string uri = _CreateUri_(_CommandType_.get, _NODE_CASE_FIELDS_); + return _GetItems_(_NODE_CASE_TYPES_, uri, CaseField.Parse); + } + + public List GetCaseTypes() + { + string uri = _CreateUri_(_CommandType_.get, _NODE_CASE_TYPES_); + return _GetItems_(_NODE_CASE_TYPES_, uri, CaseType.Parse); + } + + /// gets a suite + /// id of the suite + /// information about the suite + public Suite GetSuite(ulong suiteID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_SUITE_, suiteID); + return _GetItem_(_NODE_SUITE_, uri, Suite.Parse); + } + + /// gets suites associated with a project + /// id of the project + /// suites associated with the project + public List GetSuites(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_SUITES_, projectID); + return _GetItems_(_NODE_SUITES_, uri, Suite.Parse); + } + + /// gets a section + /// id of the section + /// information about the section + public Section GetSection(ulong sectionID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_SECTION_, sectionID); + return _GetItem_
(_NODE_SECTION_, uri, Section.Parse); + } + + /// gets sections associated with a suite + /// id of the project + /// id of the suite + /// sections associated with the suite + public List
GetSections(ulong projectID, ulong suiteID) + { + string options = string.Format("&suite_id={0}", suiteID); + string uri = _CreateUri_(_CommandType_.get, _NODE_SECTIONS_, projectID, null, options); + return _GetItems_
(_NODE_SECTIONS_, uri, Section.Parse); + } + + /// gets a run + /// id of the run + /// information about the run + public Run GetRun(ulong runID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_RUN_, runID); + return _GetItem_(_NODE_RUN_, uri, Run.Parse); + } + + /// gets runs associated with a project + /// id of the project + /// runs associated with the project + public List GetRuns(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_RUNS_, projectID); + return _GetItems_(_NODE_RUNS_, uri, Run.Parse); + } + + /// gets a plan + /// id of the plan + /// information about the plan + public Plan GetPlan(ulong planID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_PLAN_, planID); + return _GetItem_(_NODE_PLAN_, uri, Plan.Parse); + } + + /// gets plans associated with a project + /// id of the project + /// plans associated with the project + public List GetPlans(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_PLANS_, projectID); + return _GetItems_(_NODE_PLANS_, uri, Plan.Parse); + } + + /// gets a milestone + /// id of the milestone + /// information about the milestone + public Milestone GetMilestone(ulong milestoneID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_MILESTONE_, milestoneID); + return _GetItem_(_NODE_MILESTONE_, uri, Milestone.Parse); + } + + /// gets milestones associated with a project + /// id of the project + /// milestone associated with project + public List GetMilestones(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_MILESTONES_, projectID); + return _GetItems_(_NODE_MILESTONES_, uri, Milestone.Parse); + } + + /// gets a project + /// id of the project + /// information about the project + public Project GetProject(ulong projectID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_PROJECT_, projectID); + return _GetItem_(_NODE_PROJECT_, uri, Project.Parse); + } + + /// gets all projects contained in the testrail instance + /// + public List GetProjects() + { + string nodeName = "projects"; + string uri = _CreateUri_(_CommandType_.get, nodeName); + return _GetItems_(nodeName, uri, Project.Parse); + } + + /// Get User for user id + /// user id to search for + /// a User object + public User GetUser(ulong userID) + { + string uri = _CreateUri_(_CommandType_.get, _NODE_USER_, userID); + return _GetItem_(_NODE_USER_, uri, User.Parse); + } + + /// Find a user by their email address + /// email address of the user + /// user if found + public User GetUserByEmail(string email) + { + // validate the email string + if (string.IsNullOrWhiteSpace(email)) + { + return default(User); + } + + string nodeName = "user_by_email"; + string optional = string.Format("&email={0}", email); + string uri = _CreateUri_(_CommandType_.get, nodeName, null, null, optional); + return _GetItem_(nodeName, uri, User.Parse); + } + + /// Get a list of users in the testrail instance + /// List of users + public List GetUsers() + { + string uri = _CreateUri_(_CommandType_.get, _NODE_USERS_); + return _GetItems_(_NODE_USERS_, uri, User.Parse); + } + + /// + /// Returns a list of test results for a test + /// + /// id of the test + /// (optional) maximum amount of test results to return, latest first + /// + public List GetResults(ulong testID, ulong? limit = null) + { + string optional = (limit.HasValue) ? string.Format("&limit={0}", limit.Value) : string.Empty; + string uri = _CreateUri_(_CommandType_.get, _NODE_RESULTS_, testID, null, optional); + return _GetItems_(_NODE_RESULTS_, uri, Result.Parse); + } + + /// + /// Return the list of test results for a test run and the case combination + /// + /// id of the rest run + /// id of the test case + /// (optional) maximum amount of test results to return, latest first + /// list of test results for a case + public List GetResultsForCase(ulong runID, ulong caseID, ulong? limit = null) + { + string optional = (limit.HasValue) ? string.Format("&limit={0}", limit.Value) : string.Empty; + string uri = _CreateUri_(_CommandType_.get, _NODE_RESULTS_FOR_CASE_, runID, caseID, optional); + return _GetItems_(_NODE_RESULTS_FOR_CASE_, uri, Result.Parse); + } + + /// + /// Returns the list of statuses available to test rail + /// + /// list of possible statuses + public List GetStatuses() + { + string nodeName = "statuses"; + string uri = _CreateUri_(_CommandType_.get, nodeName); + return _GetItems_(nodeName, uri, Status.Parse); + } + + /// + /// Get a list of all available priorities + /// + /// list of priorities + public List GetPriorities() + { + string nodeName = "priorities"; + string uri = _CreateUri_(_CommandType_.get, nodeName); + return _GetItems_(nodeName, uri, Priority.Parse); + } + #endregion Get Commands + + #endregion Public Methods + + #region Protected Methods + /// executes a get request for an item + /// the type of item + /// the name of item's node + /// a method which parse json into the item + /// the id of the item + /// object of the supplied type containing information about the item + protected T _GetItem_(string nodeName, string uri, Func parse) + where T : new() + { + + var result = _CallTestRailGetEndpoint(uri); + if (!result.WasSuccessful) + { + OnOperationFailed(this, "Could not get " + nodeName + ": " + result.Value); + return default(T); + } + + JObject json = JObject.Parse(result.Value); + return parse(json); + } + + /// executes a get request for an item + /// the type of the item + /// the name of the item's node + /// a method which parses the json into the item + /// the id of the first item on which to filter the get request + /// the id of the second item on which to filter the get request + /// additional options to append to the get request + /// list of objects of the supplied type corresponding th supplied filters + protected List _GetItems_(string nodeName, string uri, Func parse) + where T : new() + { + List items = new List(); + var result = _CallTestRailGetEndpoint(uri); + + if (!result.WasSuccessful) + { + OnOperationFailed(this, "Could not get " + nodeName + "s: " + result.Value); + } + else + { + JArray jarray = JArray.Parse(result.Value); + if (null != jarray) + { + items = JsonUtility.ConvertJArrayToList(jarray, parse); + } + } + return items; + } + + /// Creates a URI with the parameters given in the format + /// the type of action the server is going to take (i.e. get, add, update, close) + /// + /// + /// + /// + /// the uri + protected static string _CreateUri_(_CommandType_ uriType, string nodeName, ulong? id1 = null, ulong? id2 = null, string options = null, string id2Str = null) + { + string uri = string.Format("?/api/v2/{0}_{1}{2}{3}{4}", + uriType.ToString(), + nodeName, + (id1.HasValue ? "/" + id1.Value.ToString() : string.Empty), + (id2.HasValue ? "/" + id2.Value.ToString() : (!string.IsNullOrWhiteSpace(id2Str)) ? "/" + id2Str : string.Empty), + (!string.IsNullOrWhiteSpace(options) ? options : string.Empty)); + return uri; + } + + /// Add a case + /// section id to add the case to + /// title of the case + /// (optional)the ID of the case type + /// (optional)the id of the case priority + /// (optional)the estimate, e.g. "30s" or "1m 45s" + /// (optional)the ID of the milestone to link to the test case + /// (optional)a comma-separated list of references/requirements + /// (optional)custom json params to add to the current json parameters + /// result of the command + protected CommandResult _AddCase_(ulong sectionID, string title, ulong? typeID = null, ulong? priorityID = null, string estimate = null, ulong? milestoneID = null, string refs = null, JObject customs = null) + { + if (string.IsNullOrWhiteSpace(title)) + { + return new CommandResult(false, 0, new ArgumentNullException("title")); + } + string uri = _CreateUri_(_CommandType_.add, _NODE_CASE_, sectionID); + Case tmpCase = new Case() { Title = title, TypeID = typeID, PriorityID = priorityID, Estimate = estimate, MilestoneID = milestoneID, References = refs }; + JObject jsonParams = JsonUtility.Merge(tmpCase.GetJson(), customs); + return _SendCommand(uri, jsonParams); + } + + /// update an existing case + /// the ID of the test case + /// title of the case + /// (optional)the ID of the case type + /// (optional)the id of the case priority + /// (optional)the estimate, e.g. "30s" or "1m 45s" + /// (optional)the ID of the milestone to link to the test case + /// (optional)a comma-separated list of references/requirements + /// (optional) + /// result of the command + protected CommandResult _UpdateCase_(ulong caseID, string title, ulong? typeID = null, ulong? priorityID = null, string estimate = null, ulong? milestoneID = null, string refs = null, JObject customs = null) + { + if (string.IsNullOrWhiteSpace(title)) + { + return new CommandResult(false, 0, new ArgumentNullException("title")); + } + string uri = _CreateUri_(_CommandType_.update, _NODE_CASE_, caseID); + Case tmpCase = new Case() { Title = title, TypeID = typeID, PriorityID = priorityID, Estimate = estimate, MilestoneID = milestoneID, References = refs }; + JObject jsonParams = JsonUtility.Merge(tmpCase.GetJson(), customs); + return _SendCommand(uri, jsonParams); + } + + /// + /// Command type's available + /// + protected enum _CommandType_ + { + get, + add, + update, + delete, + close + } + + #endregion Protected Methods + + #region Private Methods + /// makes an http get call to the testrail + /// uri of the endpoint + /// result of the call + private CommandResult _CallTestRailGetEndpoint(string uri) + { + uri = _URL_ + uri; + OnHTTPRequestSent(this, new HTTPRequestSentEventArgs("GET", new Uri(uri))); + CommandResult cr; + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); + request.AllowAutoRedirect = true; + string authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(_UserName_ + ":" + _Password_)); + request.Headers["Authorization"] = "Basic " + authInfo; + request.UserAgent = "TestRail Client for .NET"; + request.Method = "GET"; + request.Accept = "application/json"; + request.ContentType = "application/json"; + + // receive the response + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + Stream responseDataStream = response.GetResponseStream(); + StreamReader reader = new StreamReader(responseDataStream); + string responseFromServer = reader.ReadToEnd(); + reader.Close(); + response.Close(); + cr = new CommandResult(response.StatusCode == HttpStatusCode.OK, responseFromServer); + } + catch (Exception e) { cr = new CommandResult(false, e.ToString()); } + if (!cr.WasSuccessful) + { + OnOperationFailed(this, "HTTP RESPONSE: " + cr.Value); + } + else + { + OnHTTPResponseReceived(this, cr.Value); + } + return cr; + } + + /// makes an http post call to the testrail + /// uri of the endpoint + /// post parameters alternating between keys and values + /// result of the call + private CommandResult _CallPostEndpoint(string uri, JObject json = null) + { + uri = _URL_ + uri; + string postContent = null; + if (null != json) + { + postContent = json.ToString(); + } + OnHTTPRequestSent(this, new HTTPRequestSentEventArgs("POST", new Uri(uri), postContent)); + + CommandResult cr; + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); + request.AllowAutoRedirect = true; + string authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(_UserName_ + ":" + _Password_)); + request.Headers["Authorization"] = "Basic " + authInfo; + request.UserAgent = "TestRail Client for .NET"; + request.Method = "POST"; + request.ContentType = "application/json"; + request.Accept = "application/json"; + + // add post data to the request + if (!string.IsNullOrWhiteSpace(postContent)) + { + byte[] byteArray = Encoding.UTF8.GetBytes(postContent); + request.ContentLength = byteArray.Length; + Stream requestDataStream = request.GetRequestStream(); + requestDataStream.Write(byteArray, 0, byteArray.Length); + requestDataStream.Close(); + } + + // receive the response + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + Stream responseDataStream = response.GetResponseStream(); + StreamReader reader = new StreamReader(responseDataStream); + string responseFromServer = reader.ReadToEnd(); + reader.Close(); + response.Close(); + cr = new CommandResult(response.StatusCode == HttpStatusCode.OK, responseFromServer); + } + catch (Exception e) { cr = new CommandResult(false, e.ToString()); } + if (!cr.WasSuccessful) + { + OnOperationFailed(this, "HTTP RESPONSE: " + cr.Value); + } + else + { + OnHTTPResponseReceived(this, cr.Value); + } + return cr; + } + + /// Send a command to the server + /// uri to send + /// parameter + /// + private CommandResult _SendCommand(string uri, JObject jsonParams = null) + { + Exception exe = null; + ulong resultValue = 0; + bool wasSuccessful = false; + + try + { + CommandResult result = _CallPostEndpoint(uri, jsonParams); + wasSuccessful = result.WasSuccessful; + if (wasSuccessful) + { + if (!string.IsNullOrWhiteSpace(result.Value)) + { + JObject json = JObject.Parse(result.Value); + JToken token = json["id"]; + + try + { + if (null == token) + { + // do nothing + } + else if (JTokenType.String == token.Type) + { + // for plan entry + resultValue = (ulong)(json["runs"][0]["id"]); + } + else if (JTokenType.Integer == token.Type) + { + resultValue = (ulong)json["id"]; + } + } + catch + { + // do nothing since result value is already 0 + } + } + } + else + { + exe = new Exception(result.Value); + } + } + catch (Exception e) + { + exe = e; + } + + return new CommandResult(wasSuccessful, resultValue, exe); + } + + /// + /// Determines if at least one of the case ids given is contained in the project and suite + /// + /// id of the project + /// id of the suite + /// + /// + private bool _CasesFoundInSuite(ulong projectID, ulong suiteID, HashSet caseIDs) + { + bool atLeastOneCaseFoundInSuite = false; + List validCases = GetCases(projectID, suiteID); + foreach (Case tmpCase in validCases) + { + if (tmpCase.ID.HasValue && caseIDs.Contains(tmpCase.ID.Value)) + { + atLeastOneCaseFoundInSuite = true; + break; + } + } + return atLeastOneCaseFoundInSuite; + } + + /// + /// Create a priority dictionary + /// + /// dictionary of priority ID (from test rail) to priority levels(where Higher value means higher priority) + private Dictionary _CreatePrioritiesDict() + { + Dictionary tmpDict = new Dictionary(); + List priorityList = GetPriorities(); + foreach (Priority priority in priorityList) + { + if (null != priority) + { + tmpDict[priority.ID] = priority.PriorityLevel; + } + } + return tmpDict; + } + #endregion Private Methods + } +} diff --git a/TestRail/Types/Case.cs b/TestRail/Types/Case.cs new file mode 100644 index 0000000..73b32c2 --- /dev/null +++ b/TestRail/Types/Case.cs @@ -0,0 +1,98 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace TestRail.Types +{ + /// stores information about a case + public class Case + { + #region Public Properties + /// id of the case + public ulong? ID { get; set; } + + /// title of the case + public string Title { get; set; } + + /// section id of the case + public ulong? SectionID { get; set; } + + /// type id of the case + public ulong? TypeID { get; set; } + + /// priority id of the case + public ulong? PriorityID { get; set; } + + /// references for the case + public string References { get; set; } + + /// the milestone this case was associated with + public ulong? MilestoneID { get; set; } + + /// the user who created this case + public ulong? CreatedBy { get; set; } + + /// creation date + public DateTime? CreatedOn { get; set; } + + /// estimate time this case will take + public string Estimate { get; set; } + + /// estimate forecast + public string EstimateForecast { get; set; } + + /// suite id for this case + public ulong? SuiteID { get; set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Title; + } + + /// parses json into a case + /// json to parse + /// case corresponding to the json + public static Case Parse(JObject json) + { + Case c = new Case(); + c.ID = (ulong?)json["id"]; + c.Title = (string)json["title"]; + c.SectionID = (ulong?)json["section_id"]; + c.TypeID = (ulong?)json["type_id"]; + c.PriorityID = (ulong?)json["priority_id"]; + c.References = (string)json["refs"]; + c.MilestoneID = (ulong?)json["milestone_id"]; + c.CreatedBy = (ulong)json["created_by"]; + c.CreatedOn = (null == (int?)json["created_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["created_on"]); + c.Estimate = (string)json["estimate"]; + c.EstimateForecast = (string)json["estimate_forecast"]; + c.SuiteID = (ulong)json["suite_id"]; + + return c; + } + + /// creates a json object with the given parameters + /// title of the case + /// (optional)the ID of the case type + /// (optional)the id of the case priority + /// (optional)the estimate, e.g. "30s" or "1m 45s" + /// (optional)the ID of the milestone to link to the test case + /// (optional)a comma-separated list of references/requirements + /// json object for case + public virtual JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (!string.IsNullOrWhiteSpace(Title)) { jsonParams.title = Title; } + if (null != TypeID) { jsonParams.type_id = TypeID.Value; } + if (null != PriorityID) { jsonParams.priority_id = PriorityID.Value; } + if (!string.IsNullOrWhiteSpace(Estimate)) { jsonParams.estimate = Estimate; } + if (null != MilestoneID) { jsonParams.milestone_id = MilestoneID.Value; } + if (!string.IsNullOrWhiteSpace(References)) { jsonParams.refs = References; } + return jsonParams; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/CaseField.cs b/TestRail/Types/CaseField.cs new file mode 100644 index 0000000..c21b5b4 --- /dev/null +++ b/TestRail/Types/CaseField.cs @@ -0,0 +1,78 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace TestRail.Types +{ + /// stores information about a case field + public class CaseField + { + #region Public Properties + /// id of the field + public ulong? ID { get; private set; } + + /// easy name of the custom case field + public string Name { get; private set; } + + /// system name of the custom case field + public string SystemName { get; private set; } + + /// entity id + public ulong? EntityID { get; private set; } + + /// display label for the custom case field + public string Label { get; private set; } + + /// description of the custom case field + public string Description { get; private set; } + + /// type of custom case field as described by the case type + public ulong? TypeID { get; private set; } + + /// location id + public ulong? LocationID { get; private set; } + + /// display order + public ulong? DisplayOrder { get; private set; } + + /// list of configurations for this case field + public List Configs { get; private set; } + + /// is multi + public bool? IsMulti { get; private set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// Parses the json object into a CaseField object + /// json to parse into a CaseField + /// CaseField corresponding to the json + public static CaseField Parse(JObject json) + { + CaseField cf = new CaseField(); + cf.ID = (ulong?)json["id"]; + cf.Name = (string)json["name"]; + cf.SystemName = (string)json["system_name"]; + cf.EntityID = (ulong?)json["entity_id"]; + cf.Label = (string)json["label"]; + cf.Description = (string)json["description"]; + cf.TypeID = (ulong?)json["type_id"]; + cf.LocationID = (ulong?)json["location_id"]; + cf.DisplayOrder = (ulong?)json["display_order"]; + JArray jarray = json["configs"] as JArray; + if (null != jarray) + { + cf.Configs = JsonUtility.ConvertJArrayToList(jarray, Config.Parse); + } + cf.IsMulti = (bool?)json["is_multi"]; + return cf; + } + + #endregion Public Methods + } +} diff --git a/TestRail/Types/CaseType.cs b/TestRail/Types/CaseType.cs new file mode 100644 index 0000000..39d2bc5 --- /dev/null +++ b/TestRail/Types/CaseType.cs @@ -0,0 +1,40 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a case type + public class CaseType + { + #region Public Properties + /// ID of the case type + public ulong? ID { get; protected set; } + + /// Name of the case type + public string Name { get; protected set; } + + /// is the case type the default + public bool? IsDefault { get; protected set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a suite + /// json to parse + /// suite corresponding to the json + public static CaseType Parse(JObject json) + { + CaseType ct = new CaseType(); + ct.ID = (ulong?)json["id"]; + ct.Name = (string)json["name"]; + ct.IsDefault = (bool?)json["is_default"]; + return ct; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/Config.cs b/TestRail/Types/Config.cs new file mode 100644 index 0000000..c8e312f --- /dev/null +++ b/TestRail/Types/Config.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a config + public class Config + { + #region Public Properties + /// Options for this configuration + public ConfigOption Option { get; private set; } + + /// Guid unique identifier as a string + public string ID { get; private set; } + + /// Configuration context + public ConfigContext Context { get; private set; } + #endregion Public Properties + + #region Public Methods + /// Constructor + /// json object to parse into a Config + public static Config Parse(JObject json) + { + Config c = new Config(); + c.Option = ConfigOption.Parse((JObject)json["options"]); + c.Context = ConfigContext.Parse((JObject)json["context"]); + c.ID = (string)json["id"]; + return c; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/ConfigContext.cs b/TestRail/Types/ConfigContext.cs new file mode 100644 index 0000000..34c32c4 --- /dev/null +++ b/TestRail/Types/ConfigContext.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace TestRail.Types +{ + /// stores informations about the context for a case field's config section + public class ConfigContext + { + #region Public Properties + /// Is the context global + public bool? IsGlobal { get; private set; } + + /// List of project IDs + public List ProjectIDs { get; private set; } + #endregion Public Properties + + #region Public Methods + /// parse a json object into a Config Context + /// takes a json object and converts it to a ConfigContext + public static ConfigContext Parse(JObject json) + { + ConfigContext cc = new ConfigContext(); + cc.IsGlobal = (bool?)json["is_global"]; + + // check to see if the project ids is empty + JToken jval = json["project_ids"]; + if (null != jval && jval.HasValues) + { + // add values to the list if not empty + cc.ProjectIDs = new List(); + JArray jarray = (JArray)jval; + foreach (JValue jsonItem in jarray) + { + cc.ProjectIDs.Add((string)jsonItem); + } + } + return cc; + } + #endregion + } +} diff --git a/TestRail/Types/ConfigOption.cs b/TestRail/Types/ConfigOption.cs new file mode 100644 index 0000000..6e847e5 --- /dev/null +++ b/TestRail/Types/ConfigOption.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about an option for a case field's configuration + public class ConfigOption + { + #region Public Properties + /// is this option required + public bool? IsRequired { get; private set; } + + /// Default value for the option + public string DefaultValue { get; private set; } + + /// format of the option + public string Format { get; private set; } + + /// row + public string Rows { get; private set; } + #endregion Public Properties + + #region Public Methods + /// parse a json object into a Config Option + /// converts the json object to a ConfigOption + public static ConfigOption Parse(JObject json) + { + ConfigOption co = new ConfigOption(); + co.IsRequired = (bool?)json["is_required"]; + co.DefaultValue = (string)json["default_value"]; + co.Format = (string)json["format"]; + co.Rows = (string)json["rows"]; + return co; + } + #endregion + } +} diff --git a/TestRail/Types/Milestone.cs b/TestRail/Types/Milestone.cs new file mode 100644 index 0000000..3fe0db8 --- /dev/null +++ b/TestRail/Types/Milestone.cs @@ -0,0 +1,74 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace TestRail.Types +{ + /// stores information about a milestone + public class Milestone + { + #region Properties + /// id of the milestone + public ulong ID { get; private set; } + + /// name of the milestone + public string Name { get; set; } + + /// description of the milestone + public string Description { get; set; } + + /// true if the milestone is completed + public bool? IsCompleted { get; set; } + + /// date on which the milestone is due + public DateTime? DueOn { get; set; } + + /// date on which the milestone was completed + public DateTime? CompletedOn { get; private set; } + + /// id of the project with which the milestone is associated + public ulong ProjectID { get; private set; } + + /// the url for to view the milestone + public string Url { get; private set; } + #endregion Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a milestone + /// json to parse + /// milestone corresponding to the json + public static Milestone Parse(JObject json) + { + Milestone m = new Milestone(); + m.ID = (ulong)json["id"]; + m.Name = (string)json["name"]; + m.Description = (string)json["description"]; + m.IsCompleted = (bool?)json["is_completed"]; + m.DueOn = ((null == (int?)json["due_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["due_on"])); + m.CompletedOn = ((null == (int?)json["completed_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["completed_on"])); + m.ProjectID = (ulong)json["project_id"]; + m.Url = (string)json["url"]; + return m; + } + + /// Creates a json object for this class + /// json object that represents this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (!string.IsNullOrWhiteSpace(Description)) { jsonParams.description = Description; } + if (null != DueOn) { jsonParams.dueOn = DueOn.Value.ToUnixTimestamp(); } + if (null != IsCompleted) { jsonParams.is_completed = IsCompleted; } + return jsonParams; + } + #endregion Public Methods + + } +} diff --git a/TestRail/Types/Plan.cs b/TestRail/Types/Plan.cs new file mode 100644 index 0000000..c944bc4 --- /dev/null +++ b/TestRail/Types/Plan.cs @@ -0,0 +1,147 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace TestRail.Types +{ + /// stores information about a plan + public class Plan + { + #region Public Properties + /// id of the plan + public ulong ID { get; set; } + + /// name of the plan + public string Name { get; set; } + + /// description of the plan + public string Description { get; set; } + + /// id of the milestone associated with the plan + public ulong? MilestoneID { get; set; } + + /// true if the plan has been completed + public bool IsCompleted { get; set; } + + /// date on which the plan was completed + public DateTime? CompletedOn { get; set; } + + /// number of tests in the plan that passed + public uint PassedCount { get; set; } + + /// number of tests in the plan that are blocked + public uint BlockedCount { get; set; } + + /// number of tests in the plan that are untested + public uint UntestedCount { get; set; } + + /// number of tests in the plan that need to be retested + public uint RetestCount { get; set; } + + /// number of tests in the plan that failed + public uint FailedCount { get; set; } + + /// id of the project associated with the plan + public ulong ProjectID { get; set; } + + /// ID of the user the plan is assigned to + public ulong? AssignedToID { get; set; } + + /// url to view the results of the plan + public string Url { get; set; } + + /// Custom Status 1 Count + public ulong CustomStatus1Count { get; set; } + + /// Custom Status 2 Count + public ulong CustomStatus2Count { get; set; } + + /// Custom Status 3 Count> + public ulong CustomStatus3Count { get; set; } + + /// Custom Status 4 Count + public ulong CustomStatus4Count { get; set; } + + /// Custom Status 5 Count + public ulong CustomStatus5Count { get; set; } + + /// Custom Status 6 Count + public ulong CustomStatus6Count { get; set; } + + /// Custom Status 7 Count + public ulong CustomStatus7Count { get; set; } + + public List Entries { get; set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a plan + /// json to parse + /// plan corresponding to the json + public static Plan Parse(JObject json) + { + Plan p = new Plan(); + p.ID = (ulong)json["id"]; + p.Name = (string)json["name"]; + p.Description = (string)json["description"]; + p.MilestoneID = (ulong?)json["milestone_id"]; + p.IsCompleted = (bool)json["is_completed"]; + p.CompletedOn = (null == (int?)json["completed_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["completed_on"]); + p.PassedCount = (uint)json["passed_count"]; + p.BlockedCount = (uint)json["blocked_count"]; + p.UntestedCount = (uint)json["untested_count"]; + p.RetestCount = (uint)json["retest_count"]; + p.FailedCount = (uint)json["failed_count"]; + p.ProjectID = (ulong)json["project_id"]; + p.AssignedToID = (ulong?)json["assignedto_id"]; + p.Url = (string)json["url"]; + p.CustomStatus1Count = (ulong)json["custom_status1_count"]; + p.CustomStatus2Count = (ulong)json["custom_status2_count"]; + p.CustomStatus3Count = (ulong)json["custom_status3_count"]; + p.CustomStatus4Count = (ulong)json["custom_status4_count"]; + p.CustomStatus5Count = (ulong)json["custom_status5_count"]; + p.CustomStatus6Count = (ulong)json["custom_status6_count"]; + p.CustomStatus7Count = (ulong)json["custom_status7_count"]; + + JArray jarray = json["entries"] as JArray; + if (null != jarray) + { + p.Entries = JsonUtility.ConvertJArrayToList(jarray, PlanEntry.Parse); + } + + return p; + } + + /// Get the json object that describes this object + /// the json object + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (!string.IsNullOrWhiteSpace(Description)) { jsonParams.description = Description; } + if (null != MilestoneID) { jsonParams.milestone_id = MilestoneID; } + if (null != Entries && 0 < Entries.Count) + { + JArray jarray = new JArray(); + foreach (PlanEntry pe in Entries) + { + if (null != pe) + { + jarray.Add(pe.GetJson()); + } + } + jsonParams.entries = jarray; + } + + return jsonParams; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/PlanEntry.cs b/TestRail/Types/PlanEntry.cs new file mode 100644 index 0000000..d097399 --- /dev/null +++ b/TestRail/Types/PlanEntry.cs @@ -0,0 +1,118 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace TestRail.Types +{ + /// stores information about a plan entry + public class PlanEntry + { + #region Public Properties + /// Guid of the plan entry + public string ID { get; set; } + + public List RunIDList { get; private set; } + + /// the id of the test suite for the test run + public ulong? SuiteID { get; set; } + + /// name of the test run + public string Name { get; set; } + + /// the ID of the user the test run should be assigned to + public ulong? AssignedToID { get; set; } + + /// true for including all test cases of the test suite, false for a custom case selection + public bool? IncludeAll { get; private set; } + + /// an array of case IDs for the custom case selection + public List CaseIDs { get; set; } + #endregion Public Properties + + #region Public Methods + /// Returns a json Object that represents this class + /// Json object that corresponds to this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (null != SuiteID) { jsonParams.suite_id = SuiteID; } + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (null != AssignedToID) { jsonParams.assignedto_id = AssignedToID.Value; } + + if (null != CaseIDs && 0 < CaseIDs.Count) + { + JArray jarray = new JArray(); + foreach (ulong caseID in CaseIDs) + { + jarray.Add(caseID); + } + + jsonParams.include_all = false; + jsonParams.case_ids = jarray; + } + else + { + jsonParams.include_all = true; + } + return jsonParams; + } + + /// Parse a json object to a PlanEntry + /// json object to parse + /// PlanEntry corresponding to a json object + public static PlanEntry Parse(JObject json) + { + PlanEntry pe = new PlanEntry(); + pe.ID = (string)json["id"]; + pe.SuiteID = (ulong?)json["suite_id"]; + pe.Name = (string)json["name"]; + pe.AssignedToID = (ulong?)json["assignedto_id"]; + pe.IncludeAll = (bool?)json["include_all"]; + + pe.RunIDList = _ConvertToRunIDs(json["runs"] as JArray); + pe.CaseIDs = _ConvertToCaseIDs(json["case_ids"] as JArray); + return pe; + } + #endregion Public Methods + + #region Private Methods + /// + /// Convert the JArray to a list of Run IDs + /// + /// json to parse + /// a list of run IDs, list of size 0 if none exist + private static List _ConvertToRunIDs(JArray jarray) + { + List list = new List(); + if (null != jarray) + { + foreach (JToken jt in jarray) + { + if (null != (ulong?)jt["id"]) + { + list.Add((ulong)jt["id"]); + } + } + } + return list; + } + + /// + /// Convert the Jarray to a list of case IDs + /// + /// json to parse + /// a list of case IDs, list of size 0 if none exist + private static List _ConvertToCaseIDs(JArray jarray) + { + List list = new List(); + if (null != jarray) + { + foreach (JValue jsonItem in jarray) + { + list.Add((ulong)jsonItem); + } + } + return list; + } + #endregion Private Methods + } +} diff --git a/TestRail/Types/Priority.cs b/TestRail/Types/Priority.cs new file mode 100644 index 0000000..f6d0f40 --- /dev/null +++ b/TestRail/Types/Priority.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a priority + public class Priority + { + #region Public Properties + /// id of the priority + public ulong ID { get; private set; } + + /// name of the priority + public string Name { get; private set; } + + /// a shortened name of the priority + public string ShortName { get; private set; } + + /// true if the priority is default + public bool IsDefault { get; private set; } + + /// Priority level + public int PriorityLevel { get; private set; } + #endregion Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a plan + /// json to parse + /// plan corresponding to the json + public static Priority Parse(JObject json) + { + Priority p = new Priority(); + p.ID = (ulong)json["id"]; + p.Name = (string)json["name"]; + p.ShortName = (string)json["short_name"]; + p.IsDefault = (bool)json["is_default"]; + p.PriorityLevel = (int)json["priority"]; + return p; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/Project.cs b/TestRail/Types/Project.cs new file mode 100644 index 0000000..43f55e9 --- /dev/null +++ b/TestRail/Types/Project.cs @@ -0,0 +1,73 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace TestRail.Types +{ + /// stores information about a project + public class Project + { + #region Public Properties + /// id of the project + public ulong ID { get; private set; } + + /// name of the project + public string Name { get; set; } + + /// url of the project + public string Url { get; private set; } + + /// announcement associated with the project + public string Announcement { get; set; } + + /// + /// true if the announcement should be displayed on the project's overview page and + /// false otherwise + /// + public bool? ShowAnnouncement { get; set; } + + /// true if the project has been completed + public bool? IsCompleted { get; set; } + + /// date on which the milestone was completed + public DateTime? CompletedOn { get; private set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a project + /// json to parse + /// project corresponding to the json + public static Project Parse(JObject json) + { + Project p = new Project(); + p.ID = (ulong)json["id"]; + p.Name = (string)json["name"]; + p.Announcement = (string)json["announcement"]; + p.ShowAnnouncement = (bool?)json["show_announcement"]; + p.IsCompleted = (bool?)json["is_completed"]; + p.Url = (string)json["url"]; + p.CompletedOn = (null == (int?)json["completed_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["completed_on"]); + return p; + } + + /// Creates a json object for this class + /// json object that represents this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (!string.IsNullOrWhiteSpace(Announcement)) { jsonParams.announcement = Announcement; } + if (null != ShowAnnouncement) { jsonParams.show_announcement = ShowAnnouncement.Value; } + if (null != IsCompleted) { jsonParams.is_completed = IsCompleted.Value; } + return jsonParams; + } + + #endregion Public Methods + } +} diff --git a/TestRail/Types/Result.cs b/TestRail/Types/Result.cs new file mode 100644 index 0000000..62c7ffc --- /dev/null +++ b/TestRail/Types/Result.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json.Linq; +using System; + +namespace TestRail.Types +{ + /// stores information about the result of a test + public class Result + { + #region Public Properties + /// ID of the result + public ulong ID { get; set; } + + /// ID of the test + public ulong TestID { get; set; } + + /// ID of the test status + public ulong? StatusID { get; set; } + + /// created by + public ulong? CreatedBy { get; set; } + + /// result created on + public DateTime? CreatedOn { get; set; } + + /// the ID of the user the test should be assigned to + public ulong? AssignedToID { get; set; } + + /// the comment /description for the test result + public string Comment { get; set; } + + /// the version or build tested against + public string Version { get; set; } + + /// the time it took to execute the test + public TimeSpan? Elapsed { get; set; } + + /// a comma-separated list of defects to link to the test result + public string Defects { get; set; } + #endregion Public Properties + + #region Public Methods + /// + /// string representation of the object + /// + /// string representation of the object + public override string ToString() + { + return string.Format("{0}:{1}", ID, Comment); + } + /// Parse the JSON into a Result + /// json object to parse + /// a Result + public static Result Parse(JObject json) + { + Result r = new Result(); + r.ID = (ulong)json["id"]; + r.TestID = (ulong)json["test_id"]; + r.StatusID = (ulong?)json["status_id"]; + r.CreatedBy = (ulong?)json["created_by"]; + r.CreatedOn = (null != (int?)json["created_on"]) ? new DateTime(1970, 1, 1).AddSeconds((int)json["created_on"]) : (DateTime?)null; + r.AssignedToID = (ulong?)json["assignedto_id"]; + r.Comment = (string)json["comment"]; + r.Version = (string)json["version"]; + r.Elapsed = (null != (long?)json["elapsed"]) ? new TimeSpan((long)json["elapsed"]) : (TimeSpan?)null; + r.Defects = (string)json["defects"]; + return r; + } + + /// Returns a json object that represents this class + /// json object that represents this class + public virtual JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (null != StatusID) { jsonParams.status_id = (int)StatusID; } + if (null != Comment) { jsonParams.comment = Comment; } + if (null != Version) { jsonParams.version = Version; } + if (null != Elapsed) { jsonParams.elapsed = Elapsed.Value.Ticks; } + if (null != Defects) { jsonParams.defects = Defects; } + if (null != AssignedToID) { jsonParams.assignedto_id = AssignedToID.Value; } + return jsonParams; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/ResultStatus.cs b/TestRail/Types/ResultStatus.cs new file mode 100644 index 0000000..c54e6f8 --- /dev/null +++ b/TestRail/Types/ResultStatus.cs @@ -0,0 +1,43 @@ +namespace TestRail.Types +{ + /// the enumeration represents the status of a test result + public enum ResultStatus + { + /// the test has not been run + Untested = 0, + /// the test passed + Passed = 1, + /// the test is blocked + Blocked = 2, + /// the test needs to be rerun + Retest = 4, + /// the test failed + Failed = 5, + /// custom status 1 + CustomStatus1 = 6, + /// custom status 2 + CustomStatus2 = 7, + /// custom status 3 + CustomStatus3 = 8, + /// custom status 4 + CustomStatus4 = 9, + /// custom status 5 + CustomStatus5 = 10, + /// custom status 6 + CustomStatus6 = 11, + /// custom status 7 + CustomStatus7 = 12, + } + + /// extension methods for the status enum + public static class ResultStatusExtensions + { + /// gets the value of the enum as a string + /// the status + /// the value of the status enum as a string + public static string ValueAsString(this ResultStatus s) + { + return ((int)s).ToString(); + } + } +} diff --git a/TestRail/Types/Run.cs b/TestRail/Types/Run.cs new file mode 100644 index 0000000..5ad24d9 --- /dev/null +++ b/TestRail/Types/Run.cs @@ -0,0 +1,157 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace TestRail.Types +{ + /// stores information about a run + public class Run + { + #region Properties + /// id of the run + public ulong? ID { get; private set; } + + /// name of the run + public string Name { get; set; } + + /// description of the run + public string Description { get; set; } + + /// id of the suite associated with the run + public ulong? SuiteID { get; set; } + + /// id of the milestone associated with the run + public ulong? MilestoneID { get; set; } + + /// config for the run + public string Config { get; private set; } + + /// true if the run has been completes + public bool? IsCompleted { get; private set; } + + /// date on which the run which was completed + public DateTime? CompletedOn { get; private set; } + + /// number of tests in the plan that passed + public uint? PassedCount { get; private set; } + + /// number of tests in the plan that are blocked + public uint? BlockedCount { get; private set; } + + /// number of tests in the plan that are untested + public uint? UntestedCount { get; private set; } + + /// number of tests in the plan that need to be retested + public uint? RetestCount { get; private set; } + + /// number of tests in the plan that failed + public uint? FailedCount { get; private set; } + + /// id of the project associated with the run + public ulong? ProjectID { get; private set; } + + /// id of the plan associated with the run + public ulong? PlanID { get; private set; } + + /// is of the user it is assigned to + public ulong? AssignedTo { get; set; } + + /// + public bool IncludeAll { get; set; } + + /// + public ulong CustomStatus1Count { get; private set; } + + /// + public ulong CustomStatus2Count { get; private set; } + + /// + public ulong CustomStatus3Count { get; private set; } + + /// + public ulong CustomStatus4Count { get; private set; } + + /// + public ulong CustomStatus5Count { get; private set; } + + /// + public ulong CustomStatus6Count { get; private set; } + + /// + public ulong CustomStatus7Count { get; private set; } + + /// + public string Url { get; private set; } + + /// an array of case IDs for the custom case selection + public HashSet CaseIDs { get; set;} + #endregion Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a run + /// json to parse + /// run corresponding to the json + public static Run Parse(JObject json) + { + Run r = new Run(); + r.ID = (ulong?)json["id"]; + r.Name = (string)json["name"]; + r.Description = (string)json["description"]; + r.SuiteID = (ulong?)json["suite_id"]; + r.MilestoneID = (ulong?)json["milestone_id"]; + r.Config = (string)json["config"]; + r.IsCompleted = (bool?)json["is_completed"]; + r.CompletedOn = ((null == (int?)json["completed_on"]) ? (DateTime?)null : new DateTime(1970, 1, 1).AddSeconds((int)json["completed_on"])); + r.PassedCount = (uint?)json["passed_count"]; + r.BlockedCount = (uint?)json["blocked_count"]; + r.UntestedCount = (uint?)json["untested_count"]; + r.RetestCount = (uint?)json["retest_count"]; + r.FailedCount = (uint?)json["failed_count"]; + r.ProjectID = (ulong?)json["project_id"]; + r.PlanID = (ulong?)json["plan_id"]; + r.CustomStatus1Count = (ulong)json["custom_status1_count"]; + r.CustomStatus2Count = (ulong)json["custom_status2_count"]; + r.CustomStatus3Count = (ulong)json["custom_status3_count"]; + r.CustomStatus4Count = (ulong)json["custom_status4_count"]; + r.CustomStatus5Count = (ulong)json["custom_status5_count"]; + r.CustomStatus6Count = (ulong)json["custom_status6_count"]; + r.CustomStatus7Count = (ulong)json["custom_status7_count"]; + r.AssignedTo = (ulong?)json["assignedto_id"]; + r.IncludeAll = (bool)json["include_all"]; + r.Url = (string)json["url"]; + return r; + } + + /// Creates a json object for this class + /// json object that represents this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (null != SuiteID) { jsonParams.suite_id = SuiteID; } + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (null != Description) { jsonParams.description = Description; } + if (null != MilestoneID) { jsonParams.milestone_id = MilestoneID; } + if (null != AssignedTo) { jsonParams.assignedto_id = AssignedTo; } + jsonParams.include_all = IncludeAll; + + if (null != CaseIDs && 0 < CaseIDs.Count) + { + JArray jarray = new JArray(); + foreach (ulong caseID in CaseIDs) + { + jarray.Add(caseID); + } + jsonParams.case_ids = jarray; + } + return jsonParams; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/Section.cs b/TestRail/Types/Section.cs new file mode 100644 index 0000000..1fbd84d --- /dev/null +++ b/TestRail/Types/Section.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a section + public class Section + { + #region Public Properties + + /// id of the section + public ulong? ID { get; set; } + + /// name of the section + public string Name { get; set; } + + /// id of the parent section of the section + public ulong? ParentID { get; set; } + + /// depth of the section + public uint? Depth { get; set; } + + /// display order of the section + public uint? DisplayOrder { get; set; } + + /// id of the suite associated with the section + public ulong? SuiteID { get; set; } + + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a section + /// json to parse + /// section corresponding to the json + public static Section Parse(JObject json) + { + Section s = new Section(); + s.ID = (ulong?)json["id"]; + s.Name = (string)json["name"]; + s.ParentID = (ulong?)json["parent_id"]; + s.Depth = (uint?)json["depth"]; + s.DisplayOrder = (uint?)json["display_order"]; + s.SuiteID = (ulong?)json["suite_id"]; + return s; + } + + /// Creates a json object for this class + /// json object that represents this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (null != SuiteID) { jsonParams.suite_id = SuiteID.Value; } + if (null != ParentID) { jsonParams.parent_id = ParentID.Value; } + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + return jsonParams; + } + + #endregion Public Methods + } +} diff --git a/TestRail/Types/Status.cs b/TestRail/Types/Status.cs new file mode 100644 index 0000000..c3936fd --- /dev/null +++ b/TestRail/Types/Status.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a status + public class Status + { + #region Public Properties + /// id of the status + public ulong ID { get; private set; } + + /// + public string Name { get; private set; } + + /// + public string label { get; private set; } + + /// + public ulong ColorDark { get; private set; } + + /// + public ulong ColorMedium { get; private set; } + + /// + public ulong ColorBright { get; private set; } + + /// + public bool IsSystem { get; private set; } + + /// + public bool IsUntested { get; private set; } + + /// + public bool IsFinal { get; private set; } + + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// Parses json into a Status object + /// json to parse + /// Status object corresponding to the json + public static Status Parse(JObject json) + { + Status s = new Status(); + s.ID = (ulong)json["id"]; + s.Name = (string)json["name"]; + s.label = (string)json["label"]; + s.ColorDark = (ulong)json["color_dark"]; + s.ColorMedium = (ulong)json["color_medium"]; + s.ColorBright = (ulong)json["color_bright"]; + s.IsSystem = (bool)json["is_system"]; + s.IsUntested = (bool)json["is_untested"]; + s.IsFinal = (bool)json["is_final"]; + return s; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/Step.cs b/TestRail/Types/Step.cs new file mode 100644 index 0000000..fa09632 --- /dev/null +++ b/TestRail/Types/Step.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a step + public class Step + { + #region Public Properties + /// description of the step + public string Description; + /// expected result for the step + public string Expected; + /// actual result for the step + public string Actual; + /// result of the step + public ResultStatus? Status; + #endregion + + #region Public Methods + /// parses json into a step + /// json to parse + /// step corresponding to the json + public static Step Parse(JObject json) + { + Step s = new Step(); + s.Description = (string)json["content"]; + s.Expected = (string)json["expected"]; + s.Actual = (string)json["actual"]; + s.Status = (null != (int?)json["status_id"]) ? (ResultStatus)((int)json["status_id"]) : (ResultStatus?)null; + return s; + } + + /// Get the json object that describes this class + /// json object for this class + public JObject GetJsonObject() + { + dynamic json = new JObject(); + if (!string.IsNullOrWhiteSpace(Description)) { json.content = Description; } + if (!string.IsNullOrWhiteSpace(Expected)) { json.expected = Expected; } + if (!string.IsNullOrWhiteSpace(Actual)) { json.actual = Actual; } + if (null != Status) { json.status_id = (int)Status; } + return json; + } + #endregion + } +} diff --git a/TestRail/Types/Suite.cs b/TestRail/Types/Suite.cs new file mode 100644 index 0000000..69c79a0 --- /dev/null +++ b/TestRail/Types/Suite.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a suite + public class Suite + { + #region Public Properties + /// id of the suite + public ulong? ID { get; set; } + + /// name of the suite + public string Name { get; set; } + + /// description of the suite + public string Description { get; set; } + + /// id of the project associated with the suite + public ulong? ProjectID { get; set; } + + /// url to view the suite + public string Url { get; set; } + #endregion Public Properties + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Name; + } + + /// parses json into a suite + /// json to parse + /// suite corresponding to the json + public static Suite Parse(JObject json) + { + Suite s = new Suite(); + s.ID = (ulong?)json["id"]; + s.Name = (string)json["name"]; + s.Description = (string)json["description"]; + s.ProjectID = (ulong?)json["project_id"]; + s.Url = (string)json["url"]; + return s; + } + + /// Creates a json object for this class + /// json object that represents this class + public JObject GetJson() + { + dynamic jsonParams = new JObject(); + if (!string.IsNullOrWhiteSpace(Name)) { jsonParams.name = Name; } + if (!string.IsNullOrWhiteSpace(Description)) { jsonParams.description = Description; } + return jsonParams; + } + #endregion Public Methods + } +} diff --git a/TestRail/Types/Test.cs b/TestRail/Types/Test.cs new file mode 100644 index 0000000..7d4c1fd --- /dev/null +++ b/TestRail/Types/Test.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a test + public class Test + { + #region Public Properties + /// id of the test + public ulong? ID { get; private set; } + + /// id of the test case + public ulong? CaseID { get; set; } + + /// id of the test run + public ulong? RunID { get; private set; } + + /// test rail status id + public ResultStatus? Status { get; private set; } + + /// id of the user the test is assigned to + public ulong? AssignedToID { get; private set; } + + /// title of the test + public string Title; + #endregion + + #region Public Methods + /// string representation of the object + /// string representation of the object + public override string ToString() + { + return Title; + } + + /// parses a test from the supplied json + /// json for the test + /// test corresponding to the json + public static Test Parse(JObject json) + { + Test t = new Test(); + t.ID = (ulong?)json["id"]; + t.CaseID = (ulong?)json["case_id"]; + t.RunID = (ulong?)json["run_id"]; + t.Status = (ResultStatus?)((int)json["status_id"]); + t.AssignedToID = (ulong?)json["assignedto_id"]; + t.Title = (string)json["title"]; + return t; + } + #endregion + } +} diff --git a/TestRail/Types/User.cs b/TestRail/Types/User.cs new file mode 100644 index 0000000..22f47fa --- /dev/null +++ b/TestRail/Types/User.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json.Linq; + +namespace TestRail.Types +{ + /// stores information about a user + public class User + { + #region Public Properties + /// + /// id of the user + /// + public ulong ID { get; private set; } + + /// + /// name of the user + /// + public string Name { get; private set; } + + /// + /// email of the user + /// + public string Email { get; private set; } + + /// + /// is the user an admin + /// + public bool IsAdmin { get; private set; } + + /// + /// role id of the user + /// + public ulong? RoleID { get; private set; } + + /// + /// Is the user active + /// + public bool IsActive { get; private set; } + #endregion Public Properties + + #region Public Methods + /// + /// Displays the User's ID : User Name + /// + /// + public override string ToString() + { + return string.Format("{0}:{1}", ID, Name); + } + + /// + /// Parses the json object and returns an User object + /// + /// json to parse + /// a user object corresponding to the json object + public static User Parse(JObject json) + { + User u = new User(); + u.ID = (ulong)json["id"]; + u.Name = (string)json["name"]; + u.Email = (string)json["email"]; + u.IsAdmin = json.Value("is_admin") ?? false; + u.RoleID = (ulong?)json["role_id"]; + u.IsActive = (bool)json["is_active"]; + return u; + } + #endregion Public Methods + } +} diff --git a/TestRail/packages.config b/TestRail/packages.config new file mode 100644 index 0000000..6c8cafc --- /dev/null +++ b/TestRail/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file