├── docs
├── 01_Introduction.md
├── Images
│ ├── Living_Documentation.png
│ ├── ParkingCostCalculator_OK.png
│ ├── ParkingCostCalculator_Mockup.png
│ └── ParkingCostCalculator_WelcomePage.png
└── 02_Requirements.md
├── .dockerignore
├── src
├── ParkCostCalc.Infrastructure
│ ├── Class1.cs
│ └── ParkCostCalc.Infrastructure.csproj
├── ParkCostCalc.Core
│ ├── Services
│ │ ├── CostCalculators
│ │ │ ├── ICostCalc.cs
│ │ │ ├── Economy.cs
│ │ │ ├── LongTermGarage.cs
│ │ │ ├── LongTermSurface.cs
│ │ │ ├── CalculatorFactory.cs
│ │ │ ├── Valet.cs
│ │ │ ├── CalculatorBase.cs
│ │ │ └── ShortTerm.cs
│ │ └── ParkCostCalcService.cs
│ ├── Models
│ │ ├── ParkTypeEnum.cs
│ │ ├── ParkRequest.cs
│ │ ├── CostDetails.cs
│ │ └── Contact.cs
│ ├── Interfaces
│ │ └── IParkCostCalcService.cs
│ ├── ParkCostCalc.Core.csproj
│ └── Helpers
│ │ └── MinuteConvertor.cs
└── ParkCostCalc.Api
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── Program.cs
│ ├── ParkCostCalc.Api.csproj
│ ├── Controllers
│ └── CostCalculatorController.cs
│ ├── Properties
│ └── launchSettings.json
│ └── Startup.cs
├── tests
├── ParkCostCalc.AcceptanceTests
│ ├── specflow.json
│ ├── Models
│ │ ├── CostResponse.cs
│ │ ├── ParkTypeEnum.cs
│ │ └── Contact.cs
│ ├── Properties
│ │ ├── launchSettings.json
│ │ └── AssemblyInfo.cs
│ ├── Drivers
│ │ └── CostCalculator
│ │ │ ├── ICostCalculatorDriver.cs
│ │ │ └── CostCalculatorApiDriver.cs
│ ├── Support
│ │ ├── DependencyRegister.cs
│ │ └── Hooks.cs
│ ├── Dsl
│ │ └── CostCalculatorDsl.cs
│ ├── Features
│ │ ├── ShortTerm.feature
│ │ ├── Valet.feature
│ │ ├── Economy.feature
│ │ ├── LongTermSurface.feature
│ │ └── LongTermGarage.feature
│ ├── ParkCostCalc.AcceptanceTests.csproj
│ ├── StepDefinitions
│ │ └── CostCalculatorSteps.cs
│ └── Helpers
│ │ └── Parser.cs
└── ParkCostCalc.UnitTests
│ ├── ParkCostCalc.UnitTests.csproj
│ └── CostCalculators
│ ├── ShortTermTest.cs
│ ├── ValetTest.cs
│ ├── LongTermSurfaceTest.cs
│ ├── EconomyTest.cs
│ └── LongTermGarageTest.cs
├── .github
└── workflows
│ └── ci-cd.yaml
├── .gitattributes
├── ParkingCostCalculator.sln
├── README.md
└── .gitignore
/docs/01_Introduction.md:
--------------------------------------------------------------------------------
1 | #Introduction
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/docs/Images/Living_Documentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tawfiknouri/BDD-TDD_ParkingCostCalculator_SpecFlow/HEAD/docs/Images/Living_Documentation.png
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .dockerignore
2 | .env
3 | .git
4 | .gitignore
5 | .vs
6 | .vscode
7 | docker-compose.yml
8 | docker-compose.*.yml
9 | */bin
10 | */obj
11 |
--------------------------------------------------------------------------------
/docs/Images/ParkingCostCalculator_OK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tawfiknouri/BDD-TDD_ParkingCostCalculator_SpecFlow/HEAD/docs/Images/ParkingCostCalculator_OK.png
--------------------------------------------------------------------------------
/src/ParkCostCalc.Infrastructure/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.Infrastructure
4 | {
5 | public class Class1
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/Images/ParkingCostCalculator_Mockup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tawfiknouri/BDD-TDD_ParkingCostCalculator_SpecFlow/HEAD/docs/Images/ParkingCostCalculator_Mockup.png
--------------------------------------------------------------------------------
/docs/Images/ParkingCostCalculator_WelcomePage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tawfiknouri/BDD-TDD_ParkingCostCalculator_SpecFlow/HEAD/docs/Images/ParkingCostCalculator_WelcomePage.png
--------------------------------------------------------------------------------
/src/ParkCostCalc.Infrastructure/ParkCostCalc.Infrastructure.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/ICostCalc.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Services.CostCalculators
2 | {
3 | public interface ICostCalc
4 | {
5 | public decimal CalculateCost(double totalMinutes);
6 | }
7 | }
--------------------------------------------------------------------------------
/docs/02_Requirements.md:
--------------------------------------------------------------------------------
1 | Parking Calc GUI:
2 | 
3 |
4 | Parking Calc GUI:
5 | 
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/specflow.json:
--------------------------------------------------------------------------------
1 | {
2 | "language": {
3 | "feature": "en"
4 | },
5 | "allowDebugGeneratedFiles": true,
6 | "livingDocGenerator": {
7 | "enabled": true,
8 | "filepath": "FeatureData.json"
9 | }
10 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Models/ParkTypeEnum.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Models
2 | {
3 | public enum ParkTypeEnum
4 | {
5 | Valet,
6 | ShortTerm,
7 | LongTermGarage,
8 | LongTermSurface,
9 | Economy
10 | }
11 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Interfaces/IParkCostCalcService.cs:
--------------------------------------------------------------------------------
1 | using ParkCostCalc.Core.Models;
2 |
3 | namespace ParkCostCalc.Core.Interfaces
4 | {
5 | public interface IParkCostCalcService
6 | {
7 | public CostDetails CalculateCost(ParkRequest parkRequest);
8 | }
9 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Models/CostResponse.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace ParkCostCalc.AcceptanceTests.Models
4 | {
5 | public class CostResponse
6 | {
7 | [JsonProperty("cost")]
8 | public string Cost {get; set;}
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "ParkCostCalc.Core.Specs": {
4 | "commandName": "Project",
5 | "environmentVariables": {
6 | "MSBUILDSINGLELOADCONTEXT": "1"
7 | }
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Models/ParkTypeEnum.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.AcceptanceTests.Models
2 | {
3 | public enum ParkTypeEnum
4 | {
5 | Valet,
6 | ShortTerm,
7 | LongTermGarage,
8 | LongTermSurface,
9 | Economy
10 | }
11 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/ParkCostCalc.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Models/ParkRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.Core.Models
4 | {
5 | public class ParkRequest
6 | {
7 | public ParkTypeEnum? ParkType { get; set; }
8 | public DateTime? EntryDate { get; set; }
9 | public DateTime? ExitDate { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Drivers/CostCalculator/ICostCalculatorDriver.cs:
--------------------------------------------------------------------------------
1 | using ParkCostCalc.AcceptanceTests.Models;
2 |
3 | namespace ParkCostCalc.AcceptanceTests.Drivers.CostCalculator
4 | {
5 | public interface ICostCalculatorDriver
6 | {
7 | public decimal CalculateCost(ParkTypeEnum parkingLot, string duration);
8 | }
9 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using NUnit.Framework;
4 |
5 |
6 | [assembly: Parallelizable(ParallelScope.Fixtures)]
7 |
8 | [assembly: LevelOfParallelism(5)]
9 |
10 | [assembly: ComVisible(false)]
11 |
12 | [assembly: Guid("2ff50688-c53f-4b11-8d86-6ff1549b88b3")]
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Models/CostDetails.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Models
2 | {
3 | public class CostDetails
4 | {
5 | public decimal Cost { get; set; }
6 | public string Currency { get; set; } = "€";
7 | public double Days { get; set; }
8 | public double Hours { get; set; }
9 | public double Minutes { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Models/Contact.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.Core.Models
4 | {
5 | public class Contact
6 | {
7 | public int Id { get; set; }
8 | public string Name { get; set; }
9 | public string Email { get; set; }
10 | public string Subject { get; set; }
11 | public string Message { get; set; }
12 | public DateTime? CreateDate { get; set; } = DateTime.Now;
13 | }
14 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Support/DependencyRegister.cs:
--------------------------------------------------------------------------------
1 | using BoDi;
2 | using ParkCostCalc.AcceptanceTests.Drivers.CostCalculator;
3 |
4 | namespace ParkCostCalc.AcceptanceTests.Support
5 | {
6 | public static class DependencyRegister
7 | {
8 | public static void RegisterDependencies(IObjectContainer objectContainer)
9 | {
10 | objectContainer.RegisterTypeAs();
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Models/Contact.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.AcceptanceTests.Models
4 | {
5 | public class Contact
6 | {
7 | public int Id { get; set; }
8 |
9 | public string Name { get; set; }
10 |
11 | public string Email { get; set; }
12 |
13 | public string Subject { get; set; }
14 |
15 | public string Message { get; set; }
16 |
17 | public DateTime? CreateDate { get; set; } = DateTime.Now;
18 | }
19 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/Economy.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Services.CostCalculators
2 | {
3 | public class Economy : CalculatorBase, ICostCalc
4 | {
5 | private const decimal MAX_COST_PER_WEEK = 54;
6 | private const decimal MAX_COST_PER_DAY = 9;
7 | private const decimal MAX_COST_PER_HOUR = 2;
8 |
9 | public decimal CalculateCost(double totalMinutes)
10 | {
11 | return CalculateCost(totalMinutes, MAX_COST_PER_WEEK, MAX_COST_PER_DAY, MAX_COST_PER_HOUR);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/LongTermGarage.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Services.CostCalculators
2 | {
3 | public class LongTermGarage : CalculatorBase, ICostCalc
4 | {
5 | private const int MAX_COST_PER_WEEK = 72;
6 | private const int MAX_COST_PER_DAY = 12;
7 | private const int MAX_COST_PER_HOUR = 2;
8 |
9 | public decimal CalculateCost(double totalMinutes)
10 | {
11 | return CalculateCost(totalMinutes, MAX_COST_PER_WEEK, MAX_COST_PER_DAY, MAX_COST_PER_HOUR);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/LongTermSurface.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Services.CostCalculators
2 | {
3 | public class LongTermSurface : CalculatorBase, ICostCalc
4 | {
5 | private const int MAX_COST_PER_WEEK = 60;
6 | private const int MAX_COST_PER_DAY = 10;
7 | private const int MAX_COST_PER_HOUR = 2;
8 |
9 | public decimal CalculateCost(double totalMinutes)
10 | {
11 | return CalculateCost(totalMinutes, MAX_COST_PER_WEEK, MAX_COST_PER_DAY, MAX_COST_PER_HOUR);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace ParkCostCalc.Api
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IHostBuilder CreateHostBuilder(string[] args)
14 | {
15 | return Host.CreateDefaultBuilder(args)
16 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); });
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Dsl/CostCalculatorDsl.cs:
--------------------------------------------------------------------------------
1 | using ParkCostCalc.AcceptanceTests.Drivers.CostCalculator;
2 | using ParkCostCalc.AcceptanceTests.Models;
3 |
4 | namespace ParkCostCalc.AcceptanceTests.Dsl
5 | {
6 | public class CostCalculatorDsl
7 | {
8 | private readonly ICostCalculatorDriver _driver;
9 |
10 | protected CostCalculatorDsl(ICostCalculatorDriver driver)
11 | {
12 | _driver = driver;
13 | }
14 |
15 | public decimal CalculateCost(ParkTypeEnum parkingLot, string duration)
16 | {
17 | return _driver.CalculateCost(parkingLot, duration);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/CalculatorFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace ParkCostCalc.Core.Services.CostCalculators
5 | {
6 | public static class CalculatorFactory
7 | {
8 | public static T Get(string name) where T : class
9 | {
10 | return typeof(T)
11 | .Assembly
12 | .GetTypes()
13 | .Where(type => type.GetInterfaces().Contains(typeof(T)))
14 | .Where(type => type.Name.Equals(name))
15 | .Select(type => Activator.CreateInstance(type) as T)
16 | .SingleOrDefault();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Features/ShortTerm.feature:
--------------------------------------------------------------------------------
1 | @fast
2 | Feature: Short-Term Parking feature
3 | The parking lot calculator can calculate costs for ShortTerm Parking.
4 |
5 | Scenario Outline: Calculate Short-Term Parking Cost
6 | Given parking lot is ShortTerm
7 | And parking duration is
8 | When the cost estimate is calculated
9 | Then the parking cost should be
10 |
11 | Examples:
12 | | duration | cost |
13 | | 0 minute | 0.00€ |
14 | | 30 minutes | 2.00€ |
15 | | 1 hour | 2.00€ |
16 | | 3 hours 30 minutes | 7.00€ |
17 | | 12 hours 30 minutes | 24.00€ |
18 | | 1 day 30 minutes | 25.00€ |
19 | | 1 day 1 hour | 26.00€ |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/ParkCostCalc.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Features/Valet.feature:
--------------------------------------------------------------------------------
1 | @fast
2 | Feature: Valet Parking feature
3 | The parking lot calculator can calculate costs for Valet Parking.
4 |
5 | Scenario Outline: Calculate Valet Parking Cost
6 | Given parking lot is Valet
7 | And parking duration is
8 | When the cost estimate is calculated
9 | Then the parking cost should be
10 |
11 | Examples:
12 | | duration | cost |
13 | | 0 minute | 0.00€ |
14 | | 30 minutes | 12.00€ |
15 | | 3 hours | 12.00€ |
16 | | 5 hours | 12.00€ |
17 | | 5 hours, 1 minute | 18.00€ |
18 | | 12 hours | 18.00€ |
19 | | 24 hours | 18.00€ |
20 | | 1 day, 1 minute | 36.00€ |
21 | | 3 days | 54.00€ |
22 | | 1 week | 126.00€ |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Support/Hooks.cs:
--------------------------------------------------------------------------------
1 | using BoDi;
2 | using TechTalk.SpecFlow;
3 |
4 | namespace ParkCostCalc.AcceptanceTests.Support
5 | {
6 | [Binding]
7 | public class Hooks
8 | {
9 | private readonly IObjectContainer _objectContainer;
10 |
11 | public Hooks(IObjectContainer objectContainer)
12 | {
13 | _objectContainer = objectContainer;
14 | }
15 |
16 | [BeforeScenario("db")]
17 | public void InitializeDataBaseDependencies()
18 | {
19 | // todo: setup data for acceptance tests
20 | }
21 |
22 | [BeforeScenario]
23 | public void InitializeDependencies()
24 | {
25 | DependencyRegister.RegisterDependencies(_objectContainer);
26 | }
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/ParkCostCalc.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Helpers/MinuteConvertor.cs:
--------------------------------------------------------------------------------
1 | namespace ParkCostCalc.Core.Helpers
2 | {
3 | public class MinuteConvertor
4 | {
5 | private const int MinutesPerHour = 60;
6 | private const int HoursPerDay = 24;
7 |
8 | public static int Hours(int hours)
9 | {
10 | return DaysAndHoursAndMinutes(0, hours, 0);
11 | }
12 |
13 | public static int Days(int days)
14 | {
15 | return DaysAndHoursAndMinutes(days, 0, 0);
16 | }
17 |
18 | public static int HoursAndMinutes(int hours, int minutes)
19 | {
20 | return DaysAndHoursAndMinutes(0, hours, minutes);
21 | }
22 |
23 | public static int DaysAndHoursAndMinutes(int days, int hours, int minutes)
24 | {
25 | return (days * HoursPerDay + hours) * MinutesPerHour + minutes;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Features/Economy.feature:
--------------------------------------------------------------------------------
1 | @fast
2 | Feature: Economy Parking feature
3 | The parking lot calculator can calculate costs for Economy parking.
4 |
5 | Scenario Outline: Calculate Economy Parking Cost
6 | Given parking lot is Economy
7 | And parking duration is
8 | When the cost estimate is calculated
9 | Then the parking cost should be
10 |
11 | Examples:
12 | | duration | cost |
13 | | 0 minute | 0.00€ |
14 | | 30 minutes | 2.00€ |
15 | | 1 hour | 2.00€ |
16 | | 4 hours | 8.00€ |
17 | | 5 hours | 9.00€ |
18 | | 6 hours | 9.00€ |
19 | | 24 hours | 9.00€ |
20 | | 1 day, 1 hour | 11.00€ |
21 | | 1 day, 3 hours | 15.00€ |
22 | | 1 day, 5 hours | 18.00€ |
23 | | 6 days | 54.00€ |
24 | | 6 days, 1 hour | 54.00€ |
25 | | 7 days | 54.00€ |
26 | | 1 week, 2 days | 72.00€ |
27 | | 3 weeks | 162.00€ |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Features/LongTermSurface.feature:
--------------------------------------------------------------------------------
1 | @fast
2 | Feature: Long-Term Surface Parking feature
3 | The parking lot calculator can calculate costs for Long-Term Surface parking.
4 |
5 | Scenario Outline: Calculate LongTermSurface Parking Cost
6 | Given parking lot is LongTermSurface
7 | And parking duration is
8 | When the cost estimate is calculated
9 | Then the parking cost should be
10 |
11 | Examples:
12 | | duration | cost |
13 | | 0 minute | 0.00€ |
14 | | 30 minutes | 2.00€ |
15 | | 1 hour | 2.00€ |
16 | | 5 hours | 10.00€ |
17 | | 6 hours | 10.00€ |
18 | | 24 hours | 10.00€ |
19 | | 1 day, 1 hour | 12.00€ |
20 | | 1 day, 3 hours | 16.00€ |
21 | | 1 day, 6 hours | 20.00€ |
22 | | 6 days | 60.00€ |
23 | | 6 days, 1 hour | 60.00€ |
24 | | 7 days | 60.00€ |
25 | | 1 week, 2 days | 80.00€ |
26 | | 3 weeks | 180.00€ |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Features/LongTermGarage.feature:
--------------------------------------------------------------------------------
1 | @fast
2 | Feature: Long-Term Garage Parking feature
3 | The parking lot calculator can calculate costs for Long-Term Garage parking.
4 |
5 | Scenario Outline: Calculate LongTermGarage Parking Cost
6 | Given parking lot is LongTermGarage
7 | And parking duration is
8 | When the cost estimate is calculated
9 | Then the parking cost should be
10 |
11 | Examples:
12 | | duration | cost |
13 | | 0 minute | 0.00€ |
14 | | 30 minutes | 2.00€ |
15 | | 1 hour | 2.00€ |
16 | | 3 hours | 6.00€ |
17 | | 6 hours | 12.00€ |
18 | | 7 hours | 12.00€ |
19 | | 24 hours | 12.00€ |
20 | | 1 day, 1 hour | 14.00€ |
21 | | 1 day, 3 hours | 18.00€ |
22 | | 1 day, 7 hours | 24.00€ |
23 | | 6 days | 72.00€ |
24 | | 6 days, 1 hour | 72.00€ |
25 | | 7 days | 72.00€ |
26 | | 1 week, 2 days | 96.00€ |
27 | | 3 weeks | 216.00€ |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/Controllers/CostCalculatorController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using ParkCostCalc.Core.Interfaces;
3 | using ParkCostCalc.Core.Models;
4 | using ParkCostCalc.Core.Services;
5 |
6 | namespace ParkCostCalc.Api.Controllers
7 | {
8 | [ApiController]
9 | [Route("[controller]")]
10 | public class CostCalculatorController : ControllerBase
11 | {
12 | private readonly IParkCostCalcService _costService;
13 |
14 | public CostCalculatorController(IParkCostCalcService costService)
15 | {
16 | _costService = costService;
17 | }
18 |
19 | [HttpPost]
20 | public IActionResult GetCost([FromBody] ParkRequest parkRequest)
21 | {
22 | if (!ModelState.IsValid) return BadRequest();
23 | if ((parkRequest.ExitDate - parkRequest.EntryDate).Value.TotalMinutes < 0)
24 | return BadRequest("Entry date time cannot be less than exit date time!");
25 | var costDetails = _costService.CalculateCost(parkRequest);
26 | return Ok(costDetails);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/Valet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ParkCostCalc.Core.Helpers;
3 |
4 | namespace ParkCostCalc.Core.Services.CostCalculators
5 | {
6 | public class Valet : CalculatorBase, ICostCalc
7 | {
8 | private const decimal COST_IN_5_HOURS = 12;
9 | private const decimal COST_PER_DAY = 18;
10 |
11 | public decimal CalculateCost(double totalMinutes)
12 | {
13 | var duration = TimeSpan.FromMinutes(totalMinutes);
14 | decimal totalCost = 0;
15 | if (totalMinutes <= 0)
16 | {
17 | totalCost = 0;
18 | }
19 | else if (totalMinutes <= MinuteConvertor.Hours(5))
20 | {
21 | totalCost = COST_IN_5_HOURS;
22 | }
23 | else
24 | {
25 | var days = duration.Days;
26 | var startedDay = (duration.Hours | duration.Minutes) != 0 ? 1 : 0;
27 | var totalDays = days + startedDay;
28 | totalCost = totalDays * COST_PER_DAY;
29 | }
30 |
31 | return totalCost;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:10147",
8 | "sslPort": 44356
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "ParkCostCalc.Api": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "swagger",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | },
28 | "DockerParkCostCalc.Api": {
29 | "commandName": "Docker",
30 | "launchBrowser": false,
31 | "useSSL": true,
32 | "launchUrl": "swagger",
33 | "applicationUrl": "https://webpark-api.herokuapp.com",
34 | "environmentVariables": {
35 | "ASPNETCORE_ENVIRONMENT": "Production"
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/CostCalculators/ShortTermTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.Core.Services.CostCalculators;
3 |
4 | namespace ParkCostCalc.UnitTests.CostCalculators
5 | {
6 | [TestFixture]
7 | public class ShortTermTest
8 | {
9 | private ICostCalc _costCalculator;
10 |
11 | [SetUp]
12 | public void Setup()
13 | {
14 | _costCalculator = new ShortTerm();
15 | }
16 |
17 | [TestCase(-1, 0, TestName = "Cost for negative parking time")]
18 | [TestCase(0, 0, TestName = "Cost for 0 Minute")]
19 | [TestCase(30, 2, TestName = "Cost for 30 Minutes")]
20 | [TestCase((1 * 60), 2, TestName = "Cost for 1 Hour")]
21 | [TestCase((3 * 60) + 30, 7, TestName = "Cost for 3 Hours 30 Minutes")]
22 | [TestCase((12 * 60) + 30, 24, TestName = "Cost for 12 Hours 30 Minutes")]
23 | [TestCase((1 * 24 * 60) + 30, 25, TestName = "Cost for 1 Day 30 Minutes")]
24 | [TestCase((1 * 24 * 60) + (1 * 60), 26, TestName = "Cost for 1 Day 1 Hour")]
25 | public void Should_Charge_Expected_Cost(int totalMinutes, decimal expectedCost)
26 | {
27 | var cost = _costCalculator.CalculateCost(totalMinutes);
28 |
29 | Assert.AreEqual(expectedCost, cost);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/ParkCostCalcService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ParkCostCalc.Core.Interfaces;
3 | using ParkCostCalc.Core.Models;
4 | using ParkCostCalc.Core.Services.CostCalculators;
5 |
6 | namespace ParkCostCalc.Core.Services
7 | {
8 | public class ParkCostCalcService : IParkCostCalcService
9 | {
10 | ///
11 | /// Calculate the parking cost
12 | ///
13 | /// Request represent the parking lot (type) and parking duration
14 | ///
15 | public CostDetails CalculateCost(ParkRequest parkRequest)
16 | {
17 | var costCalculator = CalculatorFactory.Get(parkRequest.ParkType.ToString());
18 | if (costCalculator == null) return null;
19 |
20 | var totalMinutes = (parkRequest.ExitDate - parkRequest.EntryDate).Value.TotalMinutes;
21 | var totalCost = costCalculator.CalculateCost(totalMinutes);
22 |
23 | var duration = TimeSpan.FromMinutes(totalMinutes);
24 |
25 | return new CostDetails
26 | {
27 | Cost = decimal.Round(totalCost, 2),
28 | Days = duration.Days,
29 | Hours = duration.Hours,
30 | Minutes = duration.Minutes
31 | };
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/CalculatorBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.Core.Services.CostCalculators
4 | {
5 | public class CalculatorBase
6 | {
7 | protected decimal CalculateCost(double totalMinutes, decimal costsPerWeek, decimal costsPerDay,
8 | decimal costsPerHour)
9 | {
10 | decimal totalCost = 0;
11 | var duration = TimeSpan.FromMinutes(totalMinutes);
12 | if (totalMinutes <= 0)
13 | {
14 | totalCost = 0;
15 | }
16 | else
17 | {
18 | var totalWeeks = duration.Days / 7;
19 | var totalDays = duration.Days % 7;
20 |
21 | var startedHour = duration.Minutes > 0 ? 1 : 0;
22 |
23 | var totalHours = duration.Hours + startedHour;
24 |
25 | var weeksCost = totalWeeks * costsPerWeek;
26 |
27 | var daysCost = totalDays * costsPerDay;
28 |
29 | // 23h * 2 = 46 or max per day = 9
30 | var hoursCost = Math.Min(totalHours * costsPerHour, costsPerDay);
31 |
32 | var daysAndHoursCost = Math.Min(daysCost + hoursCost, costsPerWeek);
33 |
34 | totalCost = weeksCost + daysAndHoursCost;
35 | }
36 |
37 | return totalCost;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/ParkCostCalc.Core/Services/CostCalculators/ShortTerm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ParkCostCalc.Core.Services.CostCalculators
4 | {
5 | public class ShortTerm : CalculatorBase, ICostCalc
6 | {
7 | private const decimal COST_FIRST_HOUR = 2;
8 | private const decimal MAX_COST_PER_DAY = 24;
9 | private const decimal MAX_COST_PER_HALF_HOUR = 1;
10 | private const int ONE_MINUTE = 1;
11 | private const int HALF_HOUR = ONE_MINUTE * 30;
12 | private const int ONE_HOUR = ONE_MINUTE * 60;
13 |
14 | public decimal CalculateCost(double totalMinutes)
15 | {
16 | decimal totalCost = 0;
17 | var duration = TimeSpan.FromMinutes(totalMinutes);
18 | if (totalMinutes <= 0)
19 | {
20 | totalCost = 0;
21 | }
22 | else if (totalMinutes <= ONE_HOUR)
23 | {
24 | totalCost = COST_FIRST_HOUR;
25 | }
26 | else
27 | {
28 | var daysCost = duration.Days * MAX_COST_PER_DAY;
29 |
30 | var totalHalfHours = duration.Hours * 2 + duration.Minutes / HALF_HOUR + duration.Minutes % HALF_HOUR;
31 | var halfHoursCost = Math.Min(totalHalfHours * MAX_COST_PER_HALF_HOUR, MAX_COST_PER_DAY);
32 |
33 | totalCost = daysCost + halfHoursCost;
34 | }
35 |
36 | return totalCost;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/.github/workflows/ci-cd.yaml:
--------------------------------------------------------------------------------
1 | name: commit-and-acceptance-stages
2 |
3 | on: [push, workflow_dispatch]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Enable git longpaths
12 | run: git config --global core.longpaths true
13 | - name: Setup .NET Core SDK (v3.1.102)
14 | uses: actions/setup-dotnet@v1
15 | with:
16 | dotnet-version: 3.1.102
17 | - name: Checkout
18 | uses: actions/checkout@v2.0.0
19 | - name: Build solution
20 | run: |
21 | dotnet restore
22 | dotnet build
23 | - name: Run UnitTests (ParkCostCalc.Core.UnitTests)
24 | run: |
25 | dotnet test tests/ParkCostCalc.UnitTests -v m
26 |
27 | - name: Create RC & Deploy
28 | run: |
29 | echo "RC"
30 | echo "Deploy RC"
31 |
32 | - name: Run AcceptanceTests
33 | run: |
34 | dotnet test tests/ParkCostCalc.AcceptanceTests -v m
35 |
36 | - name: Installing SpecFlow.Plus.LivingDoc.CLI
37 | run: |
38 | dotnet tool install --global SpecFlow.Plus.LivingDoc.CLI
39 |
40 | - name: Living-Doc
41 | run: |
42 | livingdoc feature-data tests/ParkCostCalc.AcceptanceTests/bin/Debug/netcoreapp3.1/FeatureData.json
43 |
44 | - name: Upload report
45 | uses: actions/upload-artifact@v2
46 | with:
47 | name: ParkCostCalc
48 | path: ./LivingDoc.html
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/CostCalculators/ValetTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.Core.Services.CostCalculators;
3 |
4 | namespace ParkCostCalc.UnitTests.CostCalculators
5 | {
6 | [TestFixture]
7 | public class ValetTest
8 | {
9 | private ICostCalc _costCalculator;
10 |
11 | [SetUp]
12 | public void Setup()
13 | {
14 | _costCalculator = new Valet();
15 | }
16 |
17 | [TestCase(-1, 0, TestName = "Cost for negative parking time")]
18 | [TestCase(0, 0, TestName = "Cost for 0 Minute")]
19 | [TestCase(30, 12, TestName = "Cost for 30 Minutes")]
20 | [TestCase((3 * 60), 12, TestName = "Cost for 3 Hours")]
21 | [TestCase((5 * 60), 12, TestName = "Cost for 5 Hours")]
22 | [TestCase((5 * 60) + 1, 18, TestName = "Cost for 5 Hours 1 Minute")]
23 | [TestCase((12 * 60), 18, TestName = "Cost for 12 Hours")]
24 | [TestCase((12 * 60), 18, TestName = "Cost for 24 Hours")]
25 | [TestCase((1 * 24 * 60) + 1, 36, TestName = "Cost for 1 Day 1 Minute")]
26 | [TestCase((3 * 24 * 60), 54, TestName = "Cost for 3 Days")]
27 | [TestCase((1 * 7 * 24 * 60), 126, TestName = "Cost for 1 Week")]
28 | public void Should_Charge_Expected_Cost(int totalMinutes, decimal expectedCost)
29 | {
30 | var cost = _costCalculator.CalculateCost(totalMinutes);
31 |
32 | Assert.AreEqual(expectedCost, cost);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/ParkCostCalc.AcceptanceTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Always
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Drivers/CostCalculator/CostCalculatorApiDriver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ParkCostCalc.AcceptanceTests.Helpers;
3 | using ParkCostCalc.AcceptanceTests.Models;
4 | using RestSharp;
5 | using RestSharp.Serialization.Json;
6 |
7 | namespace ParkCostCalc.AcceptanceTests.Drivers.CostCalculator
8 | {
9 | public class CostCalculatorApiDriver : ICostCalculatorDriver
10 | {
11 | public decimal CalculateCost(ParkTypeEnum parkingType, string duration)
12 | {
13 | var apiBaseUrl = "https://webpark-api.herokuapp.com";
14 | var requestUrl = "/CostCalculator";
15 |
16 | var totalMinutes = Parser.ParseDuration(duration);
17 | DateTime entryDate = DateTime.Now;
18 | DateTime exitDate = entryDate.AddMinutes(totalMinutes);
19 |
20 | var requestData = new
21 | {
22 | parkType = parkingType.ToString(),
23 | entryDate = entryDate,
24 | exitDate = exitDate
25 | };
26 |
27 | var restClient = new RestClient(apiBaseUrl);
28 | var request = new RestRequest(requestUrl, Method.POST);
29 | request.RequestFormat = RestSharp.DataFormat.Json;
30 | request.AddJsonBody(requestData);
31 | request.Timeout = 600000;
32 | var response = restClient.Execute(request);
33 | CostResponse costResponse = new JsonDeserializer().Deserialize(response);
34 | return Decimal.Parse(costResponse.Cost);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/CostCalculators/LongTermSurfaceTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.Core.Services.CostCalculators;
3 |
4 | namespace ParkCostCalc.UnitTests.CostCalculators
5 | {
6 | [TestFixture]
7 | public class LongTermSurfaceTest
8 | {
9 | private ICostCalc _costCalculator;
10 |
11 | [SetUp]
12 | public void Setup()
13 | {
14 | _costCalculator = new LongTermSurface();
15 | }
16 |
17 | [TestCase(-1, 0, TestName = "Cost for negative parking time")]
18 | [TestCase(0, 0, TestName = "Cost for 0 Minute")]
19 | [TestCase(30, 2, TestName = "Cost for 30 Minutes")]
20 | [TestCase((1 * 60), 2, TestName = "Cost for 1 Hour")]
21 | [TestCase((5 * 60), 10, TestName = "Cost for 5 Hours")]
22 | [TestCase((6 * 60), 10, TestName = "Cost for 6 Hours")]
23 | [TestCase((24 * 60), 10, TestName = "Cost for 24 Hours")]
24 | [TestCase((1 * 24 * 60) + (1 * 60), 12, TestName = "Cost for 1 Day 1 Hour")]
25 | [TestCase((1 * 24 * 60) + (3 * 60), 16, TestName = "Cost for 1 Day 3 Hours")]
26 | [TestCase((1 * 24 * 60) + (6 * 60), 20, TestName = "Cost for 1 Day 6 Hours")]
27 | [TestCase((6 * 24 * 60), 60, TestName = "Cost for 6 Days")]
28 | [TestCase((6 * 24 * 60) + (1 * 60), 60, TestName = "Cost for 6 Days 1 Hour")]
29 | [TestCase((7 * 24 * 60), 60, TestName = "Cost for 7 Days")]
30 | [TestCase((1 * 7 * 24 * 60) + (2 * 24 * 60), 80, TestName = "Cost for 1 Week 2 Days")]
31 | [TestCase((3 * 7 * 24 * 60), 180, TestName = "Cost for 3 Weeks")]
32 | public void Should_Charge_Expected_Cost(int totalMinutes, decimal expectedCost)
33 | {
34 | var cost = _costCalculator.CalculateCost(totalMinutes);
35 |
36 | Assert.AreEqual(expectedCost, cost);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/CostCalculators/EconomyTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.Core.Services.CostCalculators;
3 |
4 | namespace ParkCostCalc.UnitTests.CostCalculators
5 | {
6 | [TestFixture]
7 | public class EconomyTest
8 | {
9 | private ICostCalc _costCalculator;
10 |
11 | [SetUp]
12 | public void Setup()
13 | {
14 | _costCalculator = new Economy();
15 | }
16 |
17 | [TestCase(-1, 0, TestName = "Cost for negative parking time")]
18 | [TestCase(0, 0, TestName = "Cost for 0 Minute")]
19 | [TestCase(30, 2, TestName = "Cost for 30 Minutes")]
20 | [TestCase((1 * 60), 2, TestName = "Cost for 1 Hour")]
21 | [TestCase((4 * 60), 8, TestName = "Cost for 4 Hours")]
22 | [TestCase((5 * 60), 9, TestName = "Cost for 5 Hours")]
23 | [TestCase((6 * 60), 9, TestName = "Cost for 6 Hours")]
24 | [TestCase((24 * 60), 9, TestName = "Cost for 24 Hours")]
25 | [TestCase((1 * 24 * 60) + (1 * 60), 11, TestName = "Cost for 1 Day 1 Hour")]
26 | [TestCase((1 * 24 * 60) + (3 * 60), 15, TestName = "Cost for 1 Day 3 Hours")]
27 | [TestCase((1 * 24 * 60) + (5 * 60), 18, TestName = "Cost for 1 Day 5 Hours")]
28 | [TestCase((6 * 24 * 60), 54, TestName = "Cost for 6 Days")]
29 | [TestCase((6 * 24 * 60) + (1 * 60), 54, TestName = "Cost for 6 Days 1 Hour")]
30 | [TestCase((7 * 24 * 60), 54, TestName = "Cost for 7 Days")]
31 | [TestCase((1 * 7 * 24 * 60) + (2 * 24 * 60), 72, TestName = "Cost for 1 Week 2 Days")]
32 | [TestCase((3 * 7 * 24 * 60), 162, TestName = "Cost for 3 Weeks")]
33 | public void Should_Charge_Expected_Cost(int totalMinutes, decimal expectedCost)
34 | {
35 | var cost = _costCalculator.CalculateCost(totalMinutes);
36 | Assert.AreEqual(expectedCost, cost);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/StepDefinitions/CostCalculatorSteps.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.AcceptanceTests.Dsl;
3 | using ParkCostCalc.AcceptanceTests.Helpers;
4 | using ParkCostCalc.AcceptanceTests.Models;
5 | using TechTalk.SpecFlow;
6 |
7 | namespace ParkCostCalc.AcceptanceTests.StepDefinitions
8 | {
9 | [Binding]
10 | public class CostCalculatorSteps
11 | {
12 | private readonly ScenarioContext _scenarioContext;
13 | private readonly CostCalculatorDsl _costCalcDsl;
14 |
15 | public CostCalculatorSteps(ScenarioContext scenarioContext, CostCalculatorDsl costCalculatorDsl)
16 | {
17 | _scenarioContext = scenarioContext;
18 | _costCalcDsl = costCalculatorDsl;
19 | }
20 |
21 | [Given(@"parking lot is (.*)")]
22 | public void GivenParkingLotIs(ParkTypeEnum parkingLot)
23 | {
24 | _scenarioContext.Add("parkingLot", parkingLot);
25 | }
26 |
27 | [Given(@"parking duration is (.*)")]
28 | public void GivenParkingDuration(string duration)
29 | {
30 | _scenarioContext.Add("duration", duration);
31 | }
32 |
33 | [When(@"the cost estimate is calculated")]
34 | public void WhenTheCostEstimateIsCalculated()
35 | {
36 | _scenarioContext.TryGetValue("duration", out string duration);
37 | _scenarioContext.TryGetValue("parkingLot", out ParkTypeEnum parkType);
38 |
39 | var cost = _costCalcDsl.CalculateCost(parkType, duration);
40 | _scenarioContext.Add("cost", cost);
41 | }
42 |
43 | [Then(@"the parking cost should be (.*)")]
44 | public void ThenTheParkingCostShouldBe(string expectedCost)
45 | {
46 | _scenarioContext.TryGetValue("cost", out decimal cost);
47 | Assert.AreEqual(Parser.ParseCost(expectedCost), cost);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.UnitTests/CostCalculators/LongTermGarageTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ParkCostCalc.Core.Services.CostCalculators;
3 |
4 | namespace ParkCostCalc.UnitTests.CostCalculators
5 | {
6 | [TestFixture]
7 | public class LongTermGarageTest
8 | {
9 | private ICostCalc _costCalculator;
10 |
11 | [SetUp]
12 | public void Setup()
13 | {
14 | _costCalculator = new LongTermGarage();
15 | }
16 |
17 | [TestCase(-1, 0, TestName = "Cost for negative parking time")]
18 | [TestCase(0, 0, TestName = "Cost for 0 Minute")]
19 | [TestCase(30, 2, TestName = "Cost for 30 Minutes")]
20 | [TestCase((1 * 60), 2, TestName = "Cost for 1 Hour")]
21 | [TestCase((3 * 60), 6, TestName = "Cost for 3 Hours")]
22 | [TestCase((6 * 60), 12, TestName = "Cost for 6 Hours")]
23 | [TestCase((7 * 60), 12, TestName = "Cost for 7 Hours")]
24 | [TestCase((24 * 60), 12, TestName = "Cost for 24 Hours")]
25 | [TestCase((1 * 24 * 60) + (1 * 60), 14, TestName = "Cost for 1 Day 1 Hour")]
26 | [TestCase((1 * 24 * 60) + (3 * 60), 18, TestName = "Cost for 1 Day 3 Hours")]
27 | [TestCase((1 * 24 * 60) + (7 * 60), 24, TestName = "Cost for 1 Day 7 Hours")]
28 | [TestCase((6 * 24 * 60), 72, TestName = "Cost for 6 Days")]
29 | [TestCase((6 * 24 * 60) + (1 * 60), 72, TestName = "Cost for 6 Days 1 Hour")]
30 | [TestCase((7 * 24 * 60), 72, TestName = "Cost for 7 Days")]
31 | [TestCase((1 * 7 * 24 * 60) + (2 * 24 * 60), 96, TestName = "Cost for 1 Week 2 Days")]
32 | [TestCase((3 * 7 * 24 * 60), 216, TestName = "Cost for 3 Weeks")]
33 | public void Should_Charge_Expected_Cost(int totalMinutes, decimal expectedCost)
34 | {
35 | var cost = _costCalculator.CalculateCost(totalMinutes);
36 |
37 | Assert.AreEqual(expectedCost, cost);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/src/ParkCostCalc.Api/Startup.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using Microsoft.OpenApi.Models;
8 | using ParkCostCalc.Core.Interfaces;
9 | using ParkCostCalc.Core.Services;
10 |
11 | namespace ParkCostCalc.Api
12 | {
13 | public class Startup
14 | {
15 | public Startup(IConfiguration configuration)
16 | {
17 | Configuration = configuration;
18 | }
19 |
20 | public IConfiguration Configuration { get; }
21 |
22 | // This method gets called by the runtime. Use this method to add services to the container.
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.AddControllers().AddJsonOptions(opts =>
26 | {
27 | opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
28 | });
29 |
30 | // Services
31 | services.AddTransient();
32 |
33 | //Update as appropriate for origin, method, header
34 | services.AddCors(options =>
35 | {
36 | options.AddPolicy("AllowAnyOrigin",
37 | builder => builder
38 | .AllowAnyOrigin()
39 | .AllowAnyMethod()
40 | .AllowAnyHeader()
41 | );
42 | });
43 |
44 | services.AddSwaggerGen(c =>
45 | {
46 | c.SwaggerDoc("v1", new OpenApiInfo {Title = "WebPark API by Tawfik NOURI", Version = "v1"});
47 | });
48 | }
49 |
50 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
51 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
52 | {
53 | if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
54 |
55 | app.UseHttpsRedirection();
56 |
57 | app.UseRouting();
58 |
59 | app.UseAuthorization();
60 |
61 | app.UseCors("AllowAnyOrigin");
62 |
63 | app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
64 |
65 | // Enable middleware to serve generated Swagger as a JSON endpoint
66 | app.UseSwagger();
67 |
68 | // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
69 | // Visit http://localhost:5000/swagger
70 | app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Parking Cost Calculator API V1"); });
71 |
72 | app.UseStatusCodePagesWithRedirects("/swagger/index.html");
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/tests/ParkCostCalc.AcceptanceTests/Helpers/Parser.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace ParkCostCalc.AcceptanceTests.Helpers
4 | {
5 | public static class Parser
6 | {
7 | private const int MinutesPerHour = 60;
8 | private const int MinutesPerDay = 24 * MinutesPerHour;
9 | private const int MinutesPerWeek = 7 * MinutesPerDay;
10 |
11 | public static int ParseDuration(string duration)
12 | {
13 | int totalMinutes = 0;
14 |
15 | totalMinutes += ParseWeeks(duration) * MinutesPerWeek;
16 | totalMinutes += ParseDays(duration) * MinutesPerDay;
17 | totalMinutes += ParseHours(duration) * MinutesPerHour;
18 | totalMinutes += ParseMinutes(duration);
19 |
20 | return totalMinutes;
21 | }
22 |
23 | public static decimal ParseCost(string duration)
24 | {
25 | string dayPattern = @"(\d+)";
26 | Match match = Regex.Match(duration, dayPattern);
27 | if (match.Success)
28 | {
29 | string minText = match.Groups[1].Value;
30 | return decimal.Parse(minText);
31 | }
32 |
33 | return 0;
34 | }
35 |
36 |
37 | private static int ParseWeeks(string duration)
38 | {
39 | return ParseNumberAccordingToPattern(@"(\d+)\s?week(s)?", duration);
40 | // return ParseNumberAccordingToPattern(@"(\d+)\s?(week(s)|semaine(s))?", duration);
41 | }
42 |
43 | private static int ParseDays(string duration)
44 | {
45 | return ParseNumberAccordingToPattern(@"(\d+)\s?day(s)?", duration);
46 | }
47 |
48 | private static int ParseHours(string duration)
49 | {
50 | Match match = Regex.Match(duration, @"(\d+)\s?hour(s)?");
51 | if (match.Success)
52 | {
53 | string minText = match.Groups[1].Value;
54 | return int.Parse(minText);
55 | }
56 |
57 | return 0;
58 | }
59 |
60 | private static int ParseMinutes(string duration)
61 | {
62 | Match match = Regex.Match(duration, @"(\d+)\s?minute(s)?");
63 | if (match.Success)
64 | {
65 | string minText = match.Groups[1].Value;
66 | return int.Parse(minText);
67 | }
68 |
69 | return 0;
70 | }
71 |
72 | private static int ParseNumberAccordingToPattern(string dayPattern, string duration)
73 | {
74 | Match match = Regex.Match(duration, dayPattern);
75 | if (match.Success)
76 | {
77 | string minText = match.Groups[1].Value;
78 | return int.Parse(minText);
79 | }
80 |
81 | return 0;
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/ParkingCostCalculator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29806.167
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParkCostCalc.Core", "src\ParkCostCalc.Core\ParkCostCalc.Core.csproj", "{07375135-2088-46CD-92BD-52324239FAE3}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParkCostCalc.Api", "src\ParkCostCalc.Api\ParkCostCalc.Api.csproj", "{E081E434-4337-4BA5-BCD2-7344C0EB12D6}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F0A54108-8349-414C-AB99-4F95115BBC04}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A38FD08F-DCEC-4676-9627-5AE723A699A6}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParkCostCalc.AcceptanceTests", "tests\ParkCostCalc.AcceptanceTests\ParkCostCalc.AcceptanceTests.csproj", "{6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParkCostCalc.UnitTests", "tests\ParkCostCalc.UnitTests\ParkCostCalc.UnitTests.csproj", "{8193D81F-31F1-439A-80A8-9A969F3A0704}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {07375135-2088-46CD-92BD-52324239FAE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {07375135-2088-46CD-92BD-52324239FAE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {07375135-2088-46CD-92BD-52324239FAE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {07375135-2088-46CD-92BD-52324239FAE3}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {E081E434-4337-4BA5-BCD2-7344C0EB12D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {E081E434-4337-4BA5-BCD2-7344C0EB12D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {E081E434-4337-4BA5-BCD2-7344C0EB12D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {E081E434-4337-4BA5-BCD2-7344C0EB12D6}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {8193D81F-31F1-439A-80A8-9A969F3A0704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {8193D81F-31F1-439A-80A8-9A969F3A0704}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {8193D81F-31F1-439A-80A8-9A969F3A0704}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {8193D81F-31F1-439A-80A8-9A969F3A0704}.Release|Any CPU.Build.0 = Release|Any CPU
40 | EndGlobalSection
41 | GlobalSection(SolutionProperties) = preSolution
42 | HideSolutionNode = FALSE
43 | EndGlobalSection
44 | GlobalSection(NestedProjects) = preSolution
45 | {07375135-2088-46CD-92BD-52324239FAE3} = {A38FD08F-DCEC-4676-9627-5AE723A699A6}
46 | {E081E434-4337-4BA5-BCD2-7344C0EB12D6} = {A38FD08F-DCEC-4676-9627-5AE723A699A6}
47 | {6BB3EA4F-9F89-4E37-A1BD-CD0D7529F013} = {F0A54108-8349-414C-AB99-4F95115BBC04}
48 | {8193D81F-31F1-439A-80A8-9A969F3A0704} = {F0A54108-8349-414C-AB99-4F95115BBC04}
49 | EndGlobalSection
50 | GlobalSection(ExtensibilityGlobals) = postSolution
51 | SolutionGuid = {925F0B4E-D04A-4F10-9F43-E68302BEBA7D}
52 | EndGlobalSection
53 | EndGlobal
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BDD/TDD With SpecFlow & .NET Core
2 |
3 | [](https://github.com/tawfiknouri/BDD-TDD_ParkingCostCalculator_SpecFlow/actions)
4 |
5 | The intention of this project is not create the best parking cost calculator in the world.
6 | We want the simplest parking cost calculator in the world so we can focus on Behavior Driven Development (BDD) and Test Driven Development (TDD) practice
7 |
8 | The main goal is to share knowledge !
9 |
10 | Please do not hesitate to contact me if you have any questions or PR.
11 |
12 |
13 | **The example also appears in the official specflow documentation.**
14 |
15 | See here for details: **https://docs.specflow.org/en/latest/Examples.html#community-sample-projects**
16 |
17 |
18 | Thanks for enjoying!
19 |
20 |
21 | # Requirements
22 |
23 | Imagine we were to design a parking cost calculator that calculates the price of parking tickets at the airport. There could be different parking sites like
24 |
25 | * Valet Parking
26 | * Short-Term Parking
27 | * Long-Term Garage Parking
28 | * Long-Term Surface Parking
29 | * Economy Lot Parking
30 |
31 | With each site having its own set of rules how a ticket price is calculated:
32 | ## Valet Parking
33 | - 18.00€ per day
34 | - 12.00€ for five hours or less
35 | ## Short-Term Parking
36 | - 2.00€ first hour; 1.00€ each additional 1/2 hour
37 | - 24.00€ daily maximum
38 | ## Long-Term Garage Parking
39 | - 2.00€ per hour
40 | - 2.00€ daily maximum
41 | - 72.00€ per week (7th day free)
42 | ## Long-Term Surface Parking
43 | - 2.00€ per hour
44 | - 10.00€ daily maximum
45 | - 60.00€ per week (7th day free)
46 | ## Economy Lot Parking
47 | - 2.00€ per hour
48 | - 9.00€ daily maximum
49 | - 54.00€ per week (7th day free)
50 |
51 | # The Technologies
52 | * [SpecFlow](https://specflow.org/)
53 | * [.NET Core](https://dotnet.microsoft.com/download)
54 | * [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core)
55 | * [Nunit](https://nunit.org/)
56 |
57 |
58 | # BDD practices
59 | ## Discovery
60 | ## Formulation
61 |
62 | ### Valet Parking
63 |
64 | ``` gherkin
65 | @fast
66 | Feature: Valet Parking feature
67 | The parking lot calculator can calculate costs for Valet Parking.
68 |
69 | Scenario Outline: Calculate Valet Parking Cost
70 | Given parking lot is Valet
71 | And parking duration is
72 | When the cost estimate is calculated
73 | Then the parking cost should be
74 |
75 | Examples:
76 | | duration | cost |
77 | | 0 minute | 0.00€ |
78 | | 30 minutes | 12.00€ |
79 | | 3 hours | 12.00€ |
80 | | 5 hours | 12.00€ |
81 | | 5 hours, 1 minute | 18.00€ |
82 | | 12 hours | 18.00€ |
83 | | 24 hours | 18.00€ |
84 | | 1 day, 1 minute | 36.00€ |
85 | | 3 days | 54.00€ |
86 | | 1 week | 126.00€ |
87 | ```
88 |
89 | ### Short-Term Parking
90 |
91 | ``` gherkin
92 | @fast
93 | Feature: Short-Term Parking feature
94 | The parking lot calculator can calculate costs for ShortTerm Parking.
95 |
96 | Scenario Outline: Calculate Short-Term Parking Cost
97 | Given parking lot is ShortTerm
98 | And parking duration is
99 | When the cost estimate is calculated
100 | Then the parking cost should be
101 |
102 | Examples:
103 | | duration | cost |
104 | | 0 minute | 0.00€ |
105 | | 30 minutes | 2.00€ |
106 | | 1 hour | 2.00€ |
107 | | 3 hours 30 minutes | 7.00€ |
108 | | 12 hours 30 minutes | 24.00€ |
109 | | 1 day 30 minutes | 25.00€ |
110 | | 1 day 1 hour | 26.00€ |
111 | ```
112 |
113 | ### Long-Term Garage Parking
114 |
115 | ``` gherkin
116 | @fast
117 | Feature: Long-Term Garage Parking feature
118 | The parking lot calculator can calculate costs for Long-Term Garage parking.
119 |
120 | Scenario Outline: Calculate LongTermGarage Parking Cost
121 | Given parking lot is LongTermGarage
122 | And parking duration is
123 | When the cost estimate is calculated
124 | Then the parking cost should be
125 |
126 | Examples:
127 | | duration | cost |
128 | | 0 minute | 0.00€ |
129 | | 30 minutes | 2.00€ |
130 | | 1 hour | 2.00€ |
131 | | 3 hours | 6.00€ |
132 | | 6 hours | 12.00€ |
133 | | 7 hours | 12.00€ |
134 | | 24 hours | 12.00€ |
135 | | 1 day, 1 hour | 14.00€ |
136 | | 1 day, 3 hours | 18.00€ |
137 | | 1 day, 7 hours | 24.00€ |
138 | | 6 days | 72.00€ |
139 | | 6 days, 1 hour | 72.00€ |
140 | | 7 days | 72.00€ |
141 | | 1 week, 2 days | 96.00€ |
142 | | 3 weeks | 216.00€ |
143 | ```
144 |
145 |
146 | ## Automation
147 |
148 | ### Living Documentation
149 |
150 | 
151 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 |
5 | # specflow feature files
6 |
7 | *.feature.cs
8 | # User-specific files
9 | *.suo
10 | *.user
11 | *.userosscache
12 | *.sln.docstates
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | [Ll]og/
28 |
29 | # Visual Studio 2015 cache/options directory
30 | .vs/
31 | # Uncomment if you have tasks that create the project's static files in wwwroot
32 | #wwwroot/
33 |
34 | # MSTest test Results
35 | [Tt]est[Rr]esult*/
36 | [Bb]uild[Ll]og.*
37 |
38 | # NUNIT
39 | *.VisualState.xml
40 | TestResult.xml
41 |
42 | # Build Results of an ATL Project
43 | [Dd]ebugPS/
44 | [Rr]eleasePS/
45 | dlldata.c
46 |
47 | # DNX
48 | project.lock.json
49 | project.fragment.lock.json
50 | artifacts/
51 |
52 | *_i.c
53 | *_p.c
54 | *_i.h
55 | *.ilk
56 | *.meta
57 | *.obj
58 | *.pch
59 | *.pdb
60 | *.pgc
61 | *.pgd
62 | *.rsp
63 | *.sbr
64 | *.tlb
65 | *.tli
66 | *.tlh
67 | *.tmp
68 | *.tmp_proj
69 | *.log
70 | *.vspscc
71 | *.vssscc
72 | .builds
73 | *.pidb
74 | *.svclog
75 | *.scc
76 |
77 | # Chutzpah Test files
78 | _Chutzpah*
79 |
80 | # Visual C++ cache files
81 | ipch/
82 | *.aps
83 | *.ncb
84 | *.opendb
85 | *.opensdf
86 | *.sdf
87 | *.cachefile
88 | *.VC.db
89 | *.VC.VC.opendb
90 |
91 | # Visual Studio profiler
92 | *.psess
93 | *.vsp
94 | *.vspx
95 | *.sap
96 |
97 | # TFS 2012 Local Workspace
98 | $tf/
99 |
100 | # Guidance Automation Toolkit
101 | *.gpState
102 |
103 | # ReSharper is a .NET coding add-in
104 | _ReSharper*/
105 | *.[Rr]e[Ss]harper
106 | *.DotSettings.user
107 |
108 | # JustCode is a .NET coding add-in
109 | .JustCode
110 |
111 | # TeamCity is a build add-in
112 | _TeamCity*
113 |
114 | # DotCover is a Code Coverage Tool
115 | *.dotCover
116 |
117 | # NCrunch
118 | _NCrunch_*
119 | .*crunch*.local.xml
120 | nCrunchTemp_*
121 |
122 | # MightyMoose
123 | *.mm.*
124 | AutoTest.Net/
125 |
126 | # Web workbench (sass)
127 | .sass-cache/
128 |
129 | # Installshield output folder
130 | [Ee]xpress/
131 |
132 | # DocProject is a documentation generator add-in
133 | DocProject/buildhelp/
134 | DocProject/Help/*.HxT
135 | DocProject/Help/*.HxC
136 | DocProject/Help/*.hhc
137 | DocProject/Help/*.hhk
138 | DocProject/Help/*.hhp
139 | DocProject/Help/Html2
140 | DocProject/Help/html
141 |
142 | # Click-Once directory
143 | publish/
144 |
145 | # Publish Web Output
146 | *.[Pp]ublish.xml
147 | *.azurePubxml
148 | # TODO: Comment the next line if you want to checkin your web deploy settings
149 | # but database connection strings (with potential passwords) will be unencrypted
150 | #*.pubxml
151 | *.publishproj
152 |
153 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
154 | # checkin your Azure Web App publish settings, but sensitive information contained
155 | # in these scripts will be unencrypted
156 | PublishScripts/
157 |
158 | # NuGet Packages
159 | *.nupkg
160 | # The packages folder can be ignored because of Package Restore
161 | **/packages/*
162 | # except build/, which is used as an MSBuild target.
163 | !**/packages/build/
164 | # Uncomment if necessary however generally it will be regenerated when needed
165 | #!**/packages/repositories.config
166 | # NuGet v3's project.json files produces more ignoreable files
167 | *.nuget.props
168 | *.nuget.targets
169 |
170 | # Microsoft Azure Build Output
171 | csx/
172 | *.build.csdef
173 |
174 | # Microsoft Azure Emulator
175 | ecf/
176 | rcf/
177 |
178 | # Windows Store app package directories and files
179 | AppPackages/
180 | BundleArtifacts/
181 | Package.StoreAssociation.xml
182 | _pkginfo.txt
183 |
184 | # Visual Studio cache files
185 | # files ending in .cache can be ignored
186 | *.[Cc]ache
187 | # but keep track of directories ending in .cache
188 | !*.[Cc]ache/
189 |
190 | # Others
191 | ClientBin/
192 | ~$*
193 | *~
194 | *.dbmdl
195 | *.dbproj.schemaview
196 | *.jfm
197 | *.pfx
198 | *.publishsettings
199 | node_modules/
200 | orleans.codegen.cs
201 |
202 | # Since there are multiple workflows, uncomment next line to ignore bower_components
203 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
204 | #bower_components/
205 |
206 | # RIA/Silverlight projects
207 | Generated_Code/
208 |
209 |
210 | # Specflow reporting
211 | TestResults/
212 | TestResults/.html
213 | TestResults/.log
214 |
215 | # Backup & report files from converting an old project file
216 | # to a newer Visual Studio version. Backup files are not needed,
217 | # because we have git ;-)
218 | _UpgradeReport_Files/
219 | Backup*/
220 | UpgradeLog*.XML
221 | UpgradeLog*.htm
222 |
223 | # SQL Server files
224 | *.mdf
225 | *.ldf
226 |
227 | # Business Intelligence projects
228 | *.rdl.data
229 | *.bim.layout
230 | *.bim_*.settings
231 |
232 | # Microsoft Fakes
233 | FakesAssemblies/
234 |
235 | # GhostDoc plugin setting file
236 | *.GhostDoc.xml
237 |
238 | # Node.js Tools for Visual Studio
239 | .ntvs_analysis.dat
240 |
241 | # Visual Studio 6 build log
242 | *.plg
243 |
244 | # Visual Studio 6 workspace options file
245 | *.opt
246 |
247 | # Visual Studio LightSwitch build output
248 | **/*.HTMLClient/GeneratedArtifacts
249 | **/*.DesktopClient/GeneratedArtifacts
250 | **/*.DesktopClient/ModelManifest.xml
251 | **/*.Server/GeneratedArtifacts
252 | **/*.Server/ModelManifest.xml
253 | _Pvt_Extensions
254 |
255 | # Paket dependency manager
256 | .paket/paket.exe
257 | paket-files/
258 |
259 | # FAKE - F# Make
260 | .fake/
261 |
262 | # JetBrains Rider
263 | .idea/
264 | *.sln.iml
265 |
266 | # CodeRush
267 | .cr/
268 |
269 | # Python Tools for Visual Studio (PTVS)
270 | __pycache__/
271 | *.pyc
272 |
--------------------------------------------------------------------------------