diff --git a/.gitignore b/.gitignore index 4d9e991..26b926c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ local.appsettings.config # User-specific files +.idea/ *.suo *.user *.userosscache diff --git a/Order.Management.Test/Approvals/ProgramTest.TestMainOutputs.approved.txt b/Order.Management.Test/Approvals/ProgramTest.TestMainOutputs.approved.txt new file mode 100644 index 0000000..535bf93 --- /dev/null +++ b/Order.Management.Test/Approvals/ProgramTest.TestMainOutputs.approved.txt @@ -0,0 +1,41 @@ +Please input your Name: Please input your Address: Please input your Due Date: +Please input the number of Red Squares: Please input the number of Blue Squares: Please input the number of Yellow Squares: +Please input the number of Red Triangles: Please input the number of Blue Triangles: Please input the number of Yellow Triangles: +Please input the number of Red Circle: Please input the number of Blue Circle: Please input the number of Yellow Circle: +Your invoice report has been generated: + +Name: David Wei Address: 26A Croydon Street, Sydenham, Christchurch 8024 Due Date: 29-09-2021 Order #: 0 +------------------------------------------------------------------------- +| | Red | Blue | Yellow | +------------------------------------------------------------------------- +| Square | 1 | 2 | 3 | +| Triangle | 4 | 7 | 2 | +| Circle | 5 | 8 | 1 | +------------------------------------------------------------------------- + +Squares 6 @ $1 ppi = $6 +Triangles 13 @ $2 ppi = $26 +Circles 14 @ $3 ppi = $42 +Red Color Surcharge 10 @ $1 ppi = $10 + +Your cutting list has been generated: + +Name: David Wei Address: 26A Croydon Street, Sydenham, Christchurch 8024 Due Date: 29-09-2021 Order #: 0 +-------------------- +| | Qty | +-------------------- +| Square | 6 | +|Triangle | 13 | +| Circle | 14 | +-------------------- + +Your painting report has been generated: + +Name: David Wei Address: 26A Croydon Street, Sydenham, Christchurch 8024 Due Date: 29-09-2021 Order #: 0 +------------------------------------------------------------------------- +| | Red | Blue | Yellow | +------------------------------------------------------------------------- +| Square | 1 | 2 | 3 | +| Triangle | 4 | 7 | 2 | +| Circle | 5 | 8 | 1 | +------------------------------------------------------------------------- diff --git a/Order.Management.Test/Approvals/input.txt b/Order.Management.Test/Approvals/input.txt new file mode 100644 index 0000000..95b6feb --- /dev/null +++ b/Order.Management.Test/Approvals/input.txt @@ -0,0 +1,12 @@ +David Wei +26A Croydon Street, Sydenham, Christchurch 8024 +29-09-2021 +1 +2 +3 +4 +7 +2 +5 +8 +1 \ No newline at end of file diff --git a/Order.Management.Test/Order.Management.Test.csproj b/Order.Management.Test/Order.Management.Test.csproj new file mode 100644 index 0000000..9ab0d69 --- /dev/null +++ b/Order.Management.Test/Order.Management.Test.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp3.1 + + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + Always + + + + + + + diff --git a/Order.Management.Test/ProgramTest.cs b/Order.Management.Test/ProgramTest.cs new file mode 100644 index 0000000..bb1112a --- /dev/null +++ b/Order.Management.Test/ProgramTest.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using System.Reflection; +using ApprovalTests; +using ApprovalTests.Namers; +using ApprovalTests.Reporters; +using Xunit; + +namespace Order.Management.Test +{ + [UseReporter(typeof(DiffReporter))] + [UseApprovalSubdirectory("Approvals")] + public class ProgramTest + { + [Fact] + public void TestMainOutputs() + { + using var fakeStdout = new StringWriter(); + Console.SetOut(fakeStdout); + + var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + using var inputNames = new StreamReader($@"{currentDir}/Approvals/input.txt"); + // Tell console to get its input from the file, not from the keyboard + Console.SetIn(inputNames); + + Program.Main(new string[] { }); + Approvals.Verify(fakeStdout.ToString()); + } + } +} \ No newline at end of file diff --git a/Order.Management.sln b/Order.Management.sln index 400236d..f34bcd4 100644 --- a/Order.Management.sln +++ b/Order.Management.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29519.87 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Order.Management", "Order.Management\Order.Management.csproj", "{1DAC7794-3F0E-4083-95A9-2E6FF9512C0C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Order.Management.Test", "Order.Management.Test\Order.Management.Test.csproj", "{AEA22CA0-3BCB-4793-994C-F81440CD8C99}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {1DAC7794-3F0E-4083-95A9-2E6FF9512C0C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DAC7794-3F0E-4083-95A9-2E6FF9512C0C}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DAC7794-3F0E-4083-95A9-2E6FF9512C0C}.Release|Any CPU.Build.0 = Release|Any CPU + {AEA22CA0-3BCB-4793-994C-F81440CD8C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEA22CA0-3BCB-4793-994C-F81440CD8C99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEA22CA0-3BCB-4793-994C-F81440CD8C99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEA22CA0-3BCB-4793-994C-F81440CD8C99}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Order.Management/Circle.cs b/Order.Management/Circle.cs index 9824ecc..9d164a9 100644 --- a/Order.Management/Circle.cs +++ b/Order.Management/Circle.cs @@ -1,4 +1,5 @@ -using System; +// 0. Remove no longer required code conveniently could make code cleaner +using System; using System.Collections.Generic; using System.Text; @@ -6,25 +7,31 @@ namespace Order.Management { class Circle : Shape { + // 1. magic number public int circlePrice = 3; public Circle(int red, int blue, int yellow) { + // 2. hard-coded string Name = "Circle"; base.Price = circlePrice; AdditionalCharge = 1; + // 3. we could use a better way: 'base(red, blue, yellow)' assigning values to the fields that defined in base class base.NumberOfRedShape = red; base.NumberOfBlueShape = blue; base.NumberOfYellowShape = yellow; } + // 4. bad method name: you cannot know the exact meaning of `total`, a better name could be PriceTotal public override int Total() { return RedCirclesTotal() + BlueCirclesTotal() + YellowCirclesTotal(); } + // 5. if I were the author, I would like use properties for the following methods public int RedCirclesTotal() { return (base.NumberOfRedShape * Price); } + public int BlueCirclesTotal() { return (base.NumberOfBlueShape * Price); diff --git a/Order.Management/CuttingListReport.cs b/Order.Management/CuttingListReport.cs index 125d45f..aecaf0e 100644 --- a/Order.Management/CuttingListReport.cs +++ b/Order.Management/CuttingListReport.cs @@ -6,9 +6,12 @@ namespace Order.Management { class CuttingListReport : Order { + // 1. Inconsistent name convention: it should change to TableWidth + // 2. This property could be readonly. public int tableWidth = 20; public CuttingListReport(string customerName, string customerAddress, string dueDate, List shapes) { + // use base(parameters) instead of the `base.FieldName` way base.CustomerName = customerName; base.Address = customerAddress; base.DueDate = dueDate; @@ -21,6 +24,7 @@ public override void GenerateReport() Console.WriteLine(base.ToString()); generateTable(); } + // 1. Inconsistent name convention: it should change to GenerateTable public void generateTable() { PrintLine(); @@ -31,11 +35,13 @@ public void generateTable() PrintRow("Circle", base.OrderedBlocks[2].TotalQuantityOfShape().ToString()); PrintLine(); } + // 3. If a method could be private, make it private public void PrintLine() { Console.WriteLine(new string('-', tableWidth)); } + // 3. If a method could be private, make it private public void PrintRow(params string[] columns) { int width = (tableWidth - columns.Length) / columns.Length; @@ -49,10 +55,12 @@ public void PrintRow(params string[] columns) Console.WriteLine(row); } + // 3. If a method could be private, make it private public string AlignCentre(string text, int width) { text = text.Length > width ? text.Substring(0, width - 3) + "..." : text; + // 4. this check should be the very first check if (string.IsNullOrEmpty(text)) { return new string(' ', width); @@ -62,7 +70,5 @@ public string AlignCentre(string text, int width) return text.PadRight(width - (width - text.Length) / 2).PadLeft(width); } } - - } } diff --git a/Order.Management/InvoiceReport.cs b/Order.Management/InvoiceReport.cs index 78443c3..349a1fc 100644 --- a/Order.Management/InvoiceReport.cs +++ b/Order.Management/InvoiceReport.cs @@ -68,6 +68,7 @@ public void PrintLine() Console.WriteLine(new string('-', tableWidth)); } + // Duplicate Code, should move to a base Report Class public void PrintRow(params string[] columns) { int width = (tableWidth - columns.Length) / columns.Length; @@ -81,8 +82,10 @@ public void PrintRow(params string[] columns) Console.WriteLine(row); } + // Duplicate code public string AlignCentre(string text, int width) { + // Should extract into a getTrimmedText method text = text.Length > width ? text.Substring(0, width - 3) + "..." : text; if (string.IsNullOrEmpty(text)) diff --git a/Order.Management/Order.cs b/Order.Management/Order.cs index 235c789..9d72b18 100644 --- a/Order.Management/Order.cs +++ b/Order.Management/Order.cs @@ -6,6 +6,7 @@ namespace Order.Management { abstract class Order { + // 1. the following two fields could save into a Customer class public string CustomerName { get; set; } public string Address { get; set; } public string DueDate { get; set; } diff --git a/Order.Management/PaintingReport.cs b/Order.Management/PaintingReport.cs index 9b61c83..da65b5b 100644 --- a/Order.Management/PaintingReport.cs +++ b/Order.Management/PaintingReport.cs @@ -4,6 +4,7 @@ namespace Order.Management { + // 1. Reports should have its own base class instead of inheriting from Order though they have some common information class PaintingReport : Order { public int tableWidth = 73; @@ -26,12 +27,13 @@ public void generateTable() PrintLine(); PrintRow(" ", " Red ", " Blue ", " Yellow "); PrintLine(); + // 2. Please don't use [index] way unless you have no other choice PrintRow("Square", base.OrderedBlocks[0].NumberOfRedShape.ToString(), base.OrderedBlocks[0].NumberOfBlueShape.ToString(), base.OrderedBlocks[0].NumberOfYellowShape.ToString()); PrintRow("Triangle", base.OrderedBlocks[1].NumberOfRedShape.ToString(), base.OrderedBlocks[1].NumberOfBlueShape.ToString(), base.OrderedBlocks[1].NumberOfYellowShape.ToString()); PrintRow("Circle", base.OrderedBlocks[2].NumberOfRedShape.ToString(), base.OrderedBlocks[2].NumberOfBlueShape.ToString(), base.OrderedBlocks[2].NumberOfYellowShape.ToString()); PrintLine(); } - + public void PrintLine() { Console.WriteLine(new string('-', tableWidth)); diff --git a/Order.Management/Program.cs b/Order.Management/Program.cs index 1422f85..d996c06 100644 --- a/Order.Management/Program.cs +++ b/Order.Management/Program.cs @@ -3,10 +3,11 @@ namespace Order.Management { - class Program + + public static class Program { // Main entry - static void Main(string[] args) + public static void Main(string[] args) { var (customerName, address, dueDate) = CustomerInfoInput(); @@ -18,9 +19,9 @@ static void Main(string[] args) PaintingReport(customerName, address, dueDate, orderedShapes); } - + // Order Circle Input - public static Circle OrderCirclesInput() + private static Circle OrderCirclesInput() { Console.Write("\nPlease input the number of Red Circle: "); int redCircle = Convert.ToInt32(userInput()); @@ -32,9 +33,9 @@ public static Circle OrderCirclesInput() Circle circle = new Circle(redCircle, blueCircle, yellowCircle); return circle; } - + // Order Squares Input - public static Square OrderSquaresInput() + private static Square OrderSquaresInput() { Console.Write("\nPlease input the number of Red Squares: "); int redSquare = Convert.ToInt32(userInput()); @@ -48,7 +49,7 @@ public static Square OrderSquaresInput() } // Order Triangles Input - public static Triangle OrderTrianglesInput() + private static Triangle OrderTrianglesInput() { Console.Write("\nPlease input the number of Red Triangles: "); int redTriangle = Convert.ToInt32(userInput()); @@ -74,21 +75,21 @@ public static string userInput() return input; } - // Generate Painting Report + // Generate Painting Report private static void PaintingReport(string customerName, string address, string dueDate, List orderedShapes) { PaintingReport paintingReport = new PaintingReport(customerName, address, dueDate, orderedShapes); paintingReport.GenerateReport(); } - // Generate Painting Report + // Generate Painting Report private static void CuttingListReport(string customerName, string address, string dueDate, List orderedShapes) { CuttingListReport cuttingListReport = new CuttingListReport(customerName, address, dueDate, orderedShapes); cuttingListReport.GenerateReport(); } - // Generate Invoice Report + // Generate Invoice Report private static void InvoiceReport(string customerName, string address, string dueDate, List orderedShapes) { InvoiceReport invoiceReport = new InvoiceReport(customerName, address, dueDate, orderedShapes); diff --git a/Order.Management/Shape.cs b/Order.Management/Shape.cs index 7f5c61c..50f9492 100644 --- a/Order.Management/Shape.cs +++ b/Order.Management/Shape.cs @@ -8,10 +8,12 @@ abstract class Shape { public string Name { get; set; } public int Price { get; set; } + // 1. the following four fields are actually not attribute of a shape, it should belong to Order Information public int AdditionalCharge { get; set; } public int NumberOfRedShape { get; set; } public int NumberOfBlueShape { get; set; } public int NumberOfYellowShape { get; set; } + // 2. the following methods are similar to the fields above, it should move into the order class public int TotalQuantityOfShape() { return NumberOfRedShape + NumberOfBlueShape + NumberOfYellowShape; @@ -21,6 +23,7 @@ public int AdditionalChargeTotal() { return NumberOfRedShape * AdditionalCharge; } + // 3. bad method name, PriceTotal could be a better candidate public abstract int Total(); }