├── .gitignore ├── README.md └── Workspace └── Tax ├── Tax.Core ├── BusinessRuleException.cs ├── Constants.cs ├── ITaxService.cs ├── IYearService.cs ├── Tax.Core.csproj ├── TaxService.cs └── YearService.cs ├── Tax.Data ├── ApplicationUser.cs ├── Migrations │ ├── 00000000000000_CreateIdentitySchema.Designer.cs │ ├── 00000000000000_CreateIdentitySchema.cs │ ├── 20180121071632_AddingUserTaxTable.Designer.cs │ ├── 20180121071632_AddingUserTaxTable.cs │ ├── 20180121073538_AddingUserTaxTableTaxDueAmount.Designer.cs │ ├── 20180121073538_AddingUserTaxTableTaxDueAmount.cs │ └── ApplicationDbContextModelSnapshot.cs ├── Tax.Data.csproj ├── TaxDbContext.cs └── UserTax.cs ├── Tax.Repository ├── IUserTaxRepository.cs ├── Tax.Repository.csproj └── UserTaxRepository.cs ├── Tax.Tests.AcceptanceTests ├── App.config ├── Default.srprofile ├── Features │ ├── SubmitTax.feature │ └── SubmitTax.feature.cs ├── PageObjects │ ├── AddTaxPage.cs │ ├── BasePage.cs │ ├── GetTaxPage.cs │ ├── LoginPage.cs │ ├── RegisterPage.cs │ └── TaxesListPage.cs ├── Properties │ └── AssemblyInfo.cs ├── SpecRunTestProfile.xsd ├── SpecRunTestProfile_2011_09.xsd ├── Steps │ └── SubmitTaxSteps.cs ├── Tax.Tests.AcceptanceTests.csproj ├── TestFixture.cs ├── packages.config └── runtests.cmd ├── Tax.Tests.Common ├── Helpers.cs ├── Tax.Tests.Common.csproj └── TestStartup.cs ├── Tax.Tests.FunctionalTests ├── Common │ └── DriverExt.cs ├── Controllers │ ├── AccountControllerTests.cs │ ├── HomeControllerTests.cs │ └── TaxControllerTests.cs ├── PageObjects │ ├── AddTaxPage.cs │ ├── BasePage.cs │ ├── GetTaxPage.cs │ ├── HomePage.cs │ ├── LoginPage.cs │ ├── RegisterPage.cs │ └── TaxesListPage.cs ├── ServerFixture.cs └── Tax.Tests.FunctionalTests.csproj ├── Tax.Tests.IntegrationTests ├── Controllers │ ├── AccountControllerTests.cs │ ├── HomeControllerTests.cs │ └── TaxControllerTests.cs ├── ServerFixture.cs └── Tax.Tests.IntegrationTests.csproj ├── Tax.Tests.UnitTests ├── Controllers │ └── TaxControllerTests.cs ├── Core │ ├── TaxServiceTests-OldWay.cs │ ├── TaxServiceTests.cs │ └── YearServiceTests.cs ├── Models │ └── TaxViewModelTests.cs └── Tax.Tests.UnitTests.csproj ├── Tax.Web ├── Controllers │ ├── AccountController.cs │ ├── HomeController.cs │ ├── ManageController.cs │ └── TaxController.cs ├── Extensions │ └── UrlHelperExtensions.cs ├── Models │ ├── AccountViewModels │ │ ├── ExternalLoginViewModel.cs │ │ ├── ForgotPasswordViewModel.cs │ │ ├── LoginViewModel.cs │ │ ├── LoginWith2faViewModel.cs │ │ ├── LoginWithRecoveryCodeViewModel.cs │ │ ├── RegisterViewModel.cs │ │ └── ResetPasswordViewModel.cs │ ├── ErrorViewModel.cs │ ├── ManageViewModels │ │ ├── ChangePasswordViewModel.cs │ │ ├── EnableAuthenticatorViewModel.cs │ │ ├── ExternalLoginsViewModel.cs │ │ ├── IndexViewModel.cs │ │ ├── RemoveLoginViewModel.cs │ │ ├── SetPasswordViewModel.cs │ │ ├── ShowRecoveryCodesViewModel.cs │ │ └── TwoFactorAuthenticationViewModel.cs │ └── TaxViewModel.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Tax.Web.csproj ├── Views │ ├── Account │ │ ├── AccessDenied.cshtml │ │ ├── ConfirmEmail.cshtml │ │ ├── ExternalLogin.cshtml │ │ ├── ForgotPassword.cshtml │ │ ├── ForgotPasswordConfirmation.cshtml │ │ ├── Lockout.cshtml │ │ ├── Login.cshtml │ │ ├── LoginWith2fa.cshtml │ │ ├── LoginWithRecoveryCode.cshtml │ │ ├── Register.cshtml │ │ ├── ResetPassword.cshtml │ │ ├── ResetPasswordConfirmation.cshtml │ │ └── SignedOut.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Manage │ │ ├── ChangePassword.cshtml │ │ ├── Disable2fa.cshtml │ │ ├── EnableAuthenticator.cshtml │ │ ├── ExternalLogins.cshtml │ │ ├── GenerateRecoveryCodes.cshtml │ │ ├── Index.cshtml │ │ ├── ManageNavPages.cs │ │ ├── ResetAuthenticator.cshtml │ │ ├── SetPassword.cshtml │ │ ├── ShowRecoveryCodes.cshtml │ │ ├── TwoFactorAuthentication.cshtml │ │ ├── _Layout.cshtml │ │ ├── _ManageNav.cshtml │ │ ├── _StatusMessage.cshtml │ │ └── _ViewImports.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LoginPartial.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── Tax │ │ ├── AddTax.cshtml │ │ ├── GetTax.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── appsettings.Development.json ├── appsettings.json ├── bundleconfig.json └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ ├── banner3.svg │ └── banner4.svg │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map └── Tax.sln /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | 84 | # Visual Studio profiler 85 | *.psess 86 | *.vsp 87 | *.vspx 88 | *.sap 89 | 90 | # TFS 2012 Local Workspace 91 | $tf/ 92 | 93 | # Guidance Automation Toolkit 94 | *.gpState 95 | 96 | # ReSharper is a .NET coding add-in 97 | _ReSharper*/ 98 | *.[Rr]e[Ss]harper 99 | *.DotSettings.user 100 | 101 | # JustCode is a .NET coding add-in 102 | .JustCode 103 | 104 | # TeamCity is a build add-in 105 | _TeamCity* 106 | 107 | # DotCover is a Code Coverage Tool 108 | *.dotCover 109 | 110 | # NCrunch 111 | _NCrunch_* 112 | .*crunch*.local.xml 113 | nCrunchTemp_* 114 | 115 | # MightyMoose 116 | *.mm.* 117 | AutoTest.Net/ 118 | 119 | # Web workbench (sass) 120 | .sass-cache/ 121 | 122 | # Installshield output folder 123 | [Ee]xpress/ 124 | 125 | # DocProject is a documentation generator add-in 126 | DocProject/buildhelp/ 127 | DocProject/Help/*.HxT 128 | DocProject/Help/*.HxC 129 | DocProject/Help/*.hhc 130 | DocProject/Help/*.hhk 131 | DocProject/Help/*.hhp 132 | DocProject/Help/Html2 133 | DocProject/Help/html 134 | 135 | # Click-Once directory 136 | publish/ 137 | 138 | # Publish Web Output 139 | *.[Pp]ublish.xml 140 | *.azurePubxml 141 | # TODO: Comment the next line if you want to checkin your web deploy settings 142 | # but database connection strings (with potential passwords) will be unencrypted 143 | *.pubxml 144 | *.publishproj 145 | 146 | # NuGet Packages 147 | *.nupkg 148 | # The packages folder can be ignored because of Package Restore 149 | **/packages/* 150 | # except build/, which is used as an MSBuild target. 151 | !**/packages/build/ 152 | # Uncomment if necessary however generally it will be regenerated when needed 153 | #!**/packages/repositories.config 154 | 155 | # Microsoft Azure Build Output 156 | csx/ 157 | *.build.csdef 158 | 159 | # Microsoft Azure Emulator 160 | ecf/ 161 | rcf/ 162 | 163 | # Microsoft Azure ApplicationInsights config file 164 | ApplicationInsights.config 165 | 166 | # Windows Store app package directory 167 | AppPackages/ 168 | BundleArtifacts/ 169 | 170 | # Visual Studio cache files 171 | # files ending in .cache can be ignored 172 | *.[Cc]ache 173 | # but keep track of directories ending in .cache 174 | !*.[Cc]ache/ 175 | 176 | # Others 177 | ClientBin/ 178 | ~$* 179 | *~ 180 | *.dbmdl 181 | *.dbproj.schemaview 182 | *.pfx 183 | *.publishsettings 184 | node_modules/ 185 | orleans.codegen.cs 186 | 187 | # RIA/Silverlight projects 188 | Generated_Code/ 189 | 190 | # Backup & report files from converting an old project file 191 | # to a newer Visual Studio version. Backup files are not needed, 192 | # because we have git ;-) 193 | _UpgradeReport_Files/ 194 | Backup*/ 195 | UpgradeLog*.XML 196 | UpgradeLog*.htm 197 | 198 | # SQL Server files 199 | *.mdf 200 | *.ldf 201 | 202 | # Business Intelligence projects 203 | *.rdl.data 204 | *.bim.layout 205 | *.bim_*.settings 206 | 207 | # Microsoft Fakes 208 | FakesAssemblies/ 209 | 210 | # GhostDoc plugin setting file 211 | *.GhostDoc.xml 212 | 213 | # Node.js Tools for Visual Studio 214 | .ntvs_analysis.dat 215 | 216 | # Visual Studio 6 build log 217 | *.plg 218 | 219 | # Visual Studio 6 workspace options file 220 | *.opt 221 | 222 | # Visual Studio LightSwitch build output 223 | **/*.HTMLClient/GeneratedArtifacts 224 | **/*.DesktopClient/GeneratedArtifacts 225 | **/*.DesktopClient/ModelManifest.xml 226 | **/*.Server/GeneratedArtifacts 227 | **/*.Server/ModelManifest.xml 228 | _Pvt_Extensions 229 | 230 | # Paket dependency manager 231 | .paket/paket.exe 232 | 233 | # FAKE - F# Make 234 | .fake/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository will be for my Automation testing course on YouTube, download the presentation from here 2 | https://www.dropbox.com/s/49oicteclgr5y4o/Automation%20Testing.pdf?dl=0 3 | 4 | ًWatch the full course from here 5 | https://www.youtube.com/watch?v=hYEKEUqBaVU&list=PLUES4tCirZnoV-FJ36XCQtxmMofnpUSkK 6 | 7 | Facebook Post 8 | https://www.facebook.com/IbraheemOsamaTech/photos/a.177990466297885.1073741828.177600373003561/177907462972852/?type=3&theater 9 | 10 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/BusinessRuleException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tax.Core 4 | { 5 | public class BusinessRuleException : Exception 6 | { 7 | public BusinessRuleException(string message) : base(message) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Tax.Core 2 | { 3 | public static class Constants 4 | { 5 | public const string YearZeroMessage = "Year can't be null"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/ITaxService.cs: -------------------------------------------------------------------------------- 1 | namespace Tax.Core 2 | { 3 | public interface ITaxService 4 | { 5 | decimal CalculateTax(int year, short numberOfChildren, decimal charityPaidAmount, decimal totalIncome); 6 | } 7 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/IYearService.cs: -------------------------------------------------------------------------------- 1 | namespace Tax.Core 2 | { 3 | public interface IYearService 4 | { 5 | decimal GetYearPercentage(int year); 6 | } 7 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/Tax.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/TaxService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tax.Core 4 | { 5 | public class TaxService : ITaxService 6 | { 7 | private readonly IYearService _yearService; 8 | public TaxService(IYearService yearService) 9 | { 10 | _yearService = yearService; 11 | } 12 | public decimal CalculateTax(int year, short numberOfChildren, decimal charityPaidAmount, decimal totalIncome) 13 | { 14 | if (year == 0) 15 | { 16 | throw new BusinessRuleException(Constants.YearZeroMessage); 17 | } 18 | 19 | var taxAmount = _yearService.GetYearPercentage(year) * (totalIncome - charityPaidAmount); 20 | 21 | if (numberOfChildren > 2) 22 | { 23 | return (taxAmount / numberOfChildren * 2); 24 | } 25 | return taxAmount; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Core/YearService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Tax.Core 4 | { 5 | public class YearService : IYearService 6 | { 7 | private static readonly Dictionary YearPercentage = new Dictionary 8 | { 9 | { 2000, 1.5/100 }, 10 | { 2016, 2.5/100 }, 11 | { 2017, 3.5/100 }, 12 | { 2018, 4.5/100 }, 13 | { 2019, 5.5/100 } 14 | }; 15 | 16 | public decimal GetYearPercentage(int year) 17 | { 18 | return (decimal)(YearPercentage.ContainsKey(year) ? YearPercentage[year] : 6.0/100); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | 3 | namespace Tax.Data 4 | { 5 | public class ApplicationUser : IdentityUser 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | 7 | namespace Tax.Data.Migrations 8 | { 9 | [DbContext(typeof(TaxDbContext))] 10 | [Migration("00000000000000_CreateIdentitySchema")] 11 | partial class CreateIdentitySchema 12 | { 13 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 14 | { 15 | modelBuilder 16 | .HasAnnotation("ProductVersion", "1.0.0-rc3") 17 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 18 | 19 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 20 | { 21 | b.Property("Id"); 22 | 23 | b.Property("ConcurrencyStamp") 24 | .IsConcurrencyToken(); 25 | 26 | b.Property("Name") 27 | .HasAnnotation("MaxLength", 256); 28 | 29 | b.Property("NormalizedName") 30 | .HasAnnotation("MaxLength", 256); 31 | 32 | b.HasKey("Id"); 33 | 34 | b.HasIndex("NormalizedName") 35 | .HasName("RoleNameIndex"); 36 | 37 | b.ToTable("AspNetRoles"); 38 | }); 39 | 40 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 41 | { 42 | b.Property("Id") 43 | .ValueGeneratedOnAdd(); 44 | 45 | b.Property("ClaimType"); 46 | 47 | b.Property("ClaimValue"); 48 | 49 | b.Property("RoleId") 50 | .IsRequired(); 51 | 52 | b.HasKey("Id"); 53 | 54 | b.HasIndex("RoleId"); 55 | 56 | b.ToTable("AspNetRoleClaims"); 57 | }); 58 | 59 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 60 | { 61 | b.Property("Id") 62 | .ValueGeneratedOnAdd(); 63 | 64 | b.Property("ClaimType"); 65 | 66 | b.Property("ClaimValue"); 67 | 68 | b.Property("UserId") 69 | .IsRequired(); 70 | 71 | b.HasKey("Id"); 72 | 73 | b.HasIndex("UserId"); 74 | 75 | b.ToTable("AspNetUserClaims"); 76 | }); 77 | 78 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 79 | { 80 | b.Property("LoginProvider"); 81 | 82 | b.Property("ProviderKey"); 83 | 84 | b.Property("ProviderDisplayName"); 85 | 86 | b.Property("UserId") 87 | .IsRequired(); 88 | 89 | b.HasKey("LoginProvider", "ProviderKey"); 90 | 91 | b.HasIndex("UserId"); 92 | 93 | b.ToTable("AspNetUserLogins"); 94 | }); 95 | 96 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 97 | { 98 | b.Property("UserId"); 99 | 100 | b.Property("RoleId"); 101 | 102 | b.HasKey("UserId", "RoleId"); 103 | 104 | b.HasIndex("RoleId"); 105 | 106 | b.HasIndex("UserId"); 107 | 108 | b.ToTable("AspNetUserRoles"); 109 | }); 110 | 111 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 112 | { 113 | b.Property("UserId"); 114 | 115 | b.Property("LoginProvider"); 116 | 117 | b.Property("Name"); 118 | 119 | b.Property("Value"); 120 | 121 | b.HasKey("UserId", "LoginProvider", "Name"); 122 | 123 | b.ToTable("AspNetUserTokens"); 124 | }); 125 | 126 | modelBuilder.Entity("Tax.Web.Models.ApplicationUser", b => 127 | { 128 | b.Property("Id"); 129 | 130 | b.Property("AccessFailedCount"); 131 | 132 | b.Property("ConcurrencyStamp") 133 | .IsConcurrencyToken(); 134 | 135 | b.Property("Email") 136 | .HasAnnotation("MaxLength", 256); 137 | 138 | b.Property("EmailConfirmed"); 139 | 140 | b.Property("LockoutEnabled"); 141 | 142 | b.Property("LockoutEnd"); 143 | 144 | b.Property("NormalizedEmail") 145 | .HasAnnotation("MaxLength", 256); 146 | 147 | b.Property("NormalizedUserName") 148 | .HasAnnotation("MaxLength", 256); 149 | 150 | b.Property("PasswordHash"); 151 | 152 | b.Property("PhoneNumber"); 153 | 154 | b.Property("PhoneNumberConfirmed"); 155 | 156 | b.Property("SecurityStamp"); 157 | 158 | b.Property("TwoFactorEnabled"); 159 | 160 | b.Property("UserName") 161 | .HasAnnotation("MaxLength", 256); 162 | 163 | b.HasKey("Id"); 164 | 165 | b.HasIndex("NormalizedEmail") 166 | .HasName("EmailIndex"); 167 | 168 | b.HasIndex("NormalizedUserName") 169 | .IsUnique() 170 | .HasName("UserNameIndex"); 171 | 172 | b.ToTable("AspNetUsers"); 173 | }); 174 | 175 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 176 | { 177 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 178 | .WithMany("Claims") 179 | .HasForeignKey("RoleId") 180 | .OnDelete(DeleteBehavior.Cascade); 181 | }); 182 | 183 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 184 | { 185 | b.HasOne("Tax.Web.Models.ApplicationUser") 186 | .WithMany("Claims") 187 | .HasForeignKey("UserId") 188 | .OnDelete(DeleteBehavior.Cascade); 189 | }); 190 | 191 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 192 | { 193 | b.HasOne("Tax.Web.Models.ApplicationUser") 194 | .WithMany("Logins") 195 | .HasForeignKey("UserId") 196 | .OnDelete(DeleteBehavior.Cascade); 197 | }); 198 | 199 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 200 | { 201 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 202 | .WithMany("Users") 203 | .HasForeignKey("RoleId") 204 | .OnDelete(DeleteBehavior.Cascade); 205 | 206 | b.HasOne("Tax.Web.Models.ApplicationUser") 207 | .WithMany("Roles") 208 | .HasForeignKey("UserId") 209 | .OnDelete(DeleteBehavior.Cascade); 210 | }); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/Migrations/20180121071632_AddingUserTaxTable.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Tax.Data.Migrations 4 | { 5 | public partial class AddingUserTaxTable : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateTable( 10 | name: "UserTaxes", 11 | columns: table => new 12 | { 13 | Year = table.Column(type: "int", nullable: false), 14 | UserId = table.Column(type: "nvarchar(450)", nullable: false), 15 | CharityPaidAmount = table.Column(type: "decimal(18, 2)", nullable: false), 16 | NumberOfChildren = table.Column(type: "smallint", nullable: false), 17 | TotalIncome = table.Column(type: "decimal(18, 2)", nullable: false) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_UserTaxes", x => new { x.Year, x.UserId }); 22 | table.ForeignKey( 23 | name: "FK_UserTaxes_AspNetUsers_UserId", 24 | column: x => x.UserId, 25 | principalTable: "AspNetUsers", 26 | principalColumn: "Id", 27 | onDelete: ReferentialAction.Cascade); 28 | }); 29 | 30 | migrationBuilder.CreateIndex( 31 | name: "IX_UserTaxes_UserId", 32 | table: "UserTaxes", 33 | column: "UserId"); 34 | } 35 | 36 | protected override void Down(MigrationBuilder migrationBuilder) 37 | { 38 | migrationBuilder.DropTable( 39 | name: "UserTaxes"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/Migrations/20180121073538_AddingUserTaxTableTaxDueAmount.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Tax.Data.Migrations 4 | { 5 | public partial class AddingUserTaxTableTaxDueAmount : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.AddColumn( 10 | name: "TaxDueAmount", 11 | table: "UserTaxes", 12 | type: "decimal(18, 2)", 13 | nullable: false, 14 | defaultValue: 0m); 15 | } 16 | 17 | protected override void Down(MigrationBuilder migrationBuilder) 18 | { 19 | migrationBuilder.DropColumn( 20 | name: "TaxDueAmount", 21 | table: "UserTaxes"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/Tax.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/TaxDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Tax.Data 5 | { 6 | public class TaxDbContext : IdentityDbContext 7 | { 8 | public TaxDbContext(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | 13 | protected override void OnModelCreating(ModelBuilder builder) 14 | { 15 | base.OnModelCreating(builder); 16 | builder.Entity().HasKey(ut => new { ut.Year, ut.UserId }); 17 | } 18 | public DbSet UserTaxes { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Data/UserTax.cs: -------------------------------------------------------------------------------- 1 | namespace Tax.Data 2 | { 3 | public class UserTax 4 | { 5 | public int Year { get; set; } 6 | public string UserId { get; set; } 7 | 8 | public decimal TotalIncome { get; set; } 9 | public decimal CharityPaidAmount { get; set; } 10 | public decimal TaxDueAmount { get; set; } 11 | public short NumberOfChildren { get; set; } 12 | public virtual ApplicationUser User { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Repository/IUserTaxRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Tax.Data; 5 | 6 | namespace Tax.Repository 7 | { 8 | public interface IUserTaxRepository 9 | { 10 | Task AddUserTax(UserTax userTax); 11 | Task GetUserTax(string userId, int year); 12 | Task> GetAllUserTax(string userId); 13 | } 14 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Repository/Tax.Repository.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Repository/UserTaxRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Tax.Data; 7 | 8 | namespace Tax.Repository 9 | { 10 | public class UserTaxRepository : IUserTaxRepository 11 | { 12 | private readonly TaxDbContext _context; 13 | 14 | public UserTaxRepository(TaxDbContext context) 15 | { 16 | _context = context; 17 | } 18 | 19 | public async Task AddUserTax(UserTax userTax) 20 | { 21 | await _context.UserTaxes.AddAsync(userTax); 22 | await _context.SaveChangesAsync(); 23 | } 24 | 25 | public Task GetUserTax(string userId, int year) 26 | { 27 | return _context.UserTaxes.FirstOrDefaultAsync(i => i.UserId == userId && i.Year == year); 28 | } 29 | 30 | public async Task> GetAllUserTax(string userId) 31 | { 32 | return await _context.UserTaxes.Where(i => i.UserId == userId).ToListAsync(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Default.srprofile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | Tax.Tests.AcceptanceTests.dll 11 | 12 | 13 | 14 | 15 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Features/SubmitTax.feature: -------------------------------------------------------------------------------- 1 | Feature: Submit Tax 2 | As a PO 3 | I want to verify submit tax page is working fine 4 | So users can submit the taxes and gov. can claim it 5 | 6 | Scenario: Submit Valid Tax Form 7 | Given login with user 8 | | email | password | 9 | | test@test.com | P@ssw0rd | 10 | And I provide the following tax details 11 | | year | totalIncome | charityPaidAmount | numberOfChildren | 12 | | 2006 | 10000 | 10 | 1 | 13 | When submit the data 14 | Then Tax due amount should be 15 | | taxDueAmount | 16 | | 599.4 | 17 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Features/SubmitTax.feature.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by SpecFlow (http://www.specflow.org/). 4 | // SpecFlow Version:2.2.0.0 5 | // SpecFlow Generator Version:2.2.0.0 6 | // 7 | // Changes to this file may cause incorrect behavior and will be lost if 8 | // the code is regenerated. 9 | // 10 | // ------------------------------------------------------------------------------ 11 | #region Designer generated code 12 | #pragma warning disable 13 | namespace Tax.Tests.AcceptanceTests.Features 14 | { 15 | using TechTalk.SpecFlow; 16 | 17 | 18 | [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "2.2.0.0")] 19 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 20 | [TechTalk.SpecRun.FeatureAttribute("Submit Tax", Description="\tAs a PO\r\n\tI want to verify submit tax page is working fine\r\n\tSo users can submit" + 21 | " the taxes and gov. can claim it", SourceFile="Features\\SubmitTax.feature", SourceLine=0)] 22 | public partial class SubmitTaxFeature 23 | { 24 | 25 | private TechTalk.SpecFlow.ITestRunner testRunner; 26 | 27 | #line 1 "SubmitTax.feature" 28 | #line hidden 29 | 30 | [TechTalk.SpecRun.FeatureInitialize()] 31 | public virtual void FeatureSetup() 32 | { 33 | testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); 34 | TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Submit Tax", "\tAs a PO\r\n\tI want to verify submit tax page is working fine\r\n\tSo users can submit" + 35 | " the taxes and gov. can claim it", ProgrammingLanguage.CSharp, ((string[])(null))); 36 | testRunner.OnFeatureStart(featureInfo); 37 | } 38 | 39 | [TechTalk.SpecRun.FeatureCleanup()] 40 | public virtual void FeatureTearDown() 41 | { 42 | testRunner.OnFeatureEnd(); 43 | testRunner = null; 44 | } 45 | 46 | public virtual void TestInitialize() 47 | { 48 | } 49 | 50 | [TechTalk.SpecRun.ScenarioCleanup()] 51 | public virtual void ScenarioTearDown() 52 | { 53 | testRunner.OnScenarioEnd(); 54 | } 55 | 56 | public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) 57 | { 58 | testRunner.OnScenarioStart(scenarioInfo); 59 | } 60 | 61 | public virtual void ScenarioCleanup() 62 | { 63 | testRunner.CollectScenarioErrors(); 64 | } 65 | 66 | [TechTalk.SpecRun.ScenarioAttribute("Submit Valid Tax Form", SourceLine=5)] 67 | public virtual void SubmitValidTaxForm() 68 | { 69 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Submit Valid Tax Form", ((string[])(null))); 70 | #line 6 71 | this.ScenarioSetup(scenarioInfo); 72 | #line hidden 73 | TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { 74 | "email", 75 | "password"}); 76 | table1.AddRow(new string[] { 77 | "test@test.com", 78 | "P@ssw0rd"}); 79 | #line 7 80 | testRunner.Given("login with user", ((string)(null)), table1, "Given "); 81 | #line hidden 82 | TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { 83 | "year", 84 | "totalIncome", 85 | "charityPaidAmount", 86 | "numberOfChildren"}); 87 | table2.AddRow(new string[] { 88 | "2006", 89 | "10000", 90 | "10", 91 | "1"}); 92 | #line 10 93 | testRunner.And("I provide the following tax details", ((string)(null)), table2, "And "); 94 | #line 13 95 | testRunner.When("submit the data", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 96 | #line hidden 97 | TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { 98 | "taxDueAmount"}); 99 | table3.AddRow(new string[] { 100 | "599.4"}); 101 | #line 14 102 | testRunner.Then("Tax due amount should be", ((string)(null)), table3, "Then "); 103 | #line hidden 104 | this.ScenarioCleanup(); 105 | } 106 | 107 | [TechTalk.SpecRun.TestRunCleanup()] 108 | public virtual void TestRunCleanup() 109 | { 110 | TechTalk.SpecFlow.TestRunnerManager.GetTestRunner().OnTestRunEnd(); 111 | } 112 | } 113 | } 114 | #pragma warning restore 115 | #endregion 116 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/AddTaxPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public class AddTaxPage : BasePage 6 | { 7 | private const string YearName = "Year"; 8 | private const string TotalIncomeName = "TotalIncome"; 9 | private const string CharityPaidAmountName = "CharityPaidAmount"; 10 | private const string NumberOfChildrenName = "NumberOfChildren"; 11 | 12 | public string YearTextBox 13 | { 14 | get 15 | { 16 | return Driver.FindElement(By.Name(YearName)).Text; 17 | } 18 | set 19 | { 20 | var element = Driver.FindElement(By.Name(YearName)); 21 | element.Clear(); 22 | element.SendKeys(value); 23 | } 24 | } 25 | 26 | public string TotalIncomeTextBox 27 | { 28 | get 29 | { 30 | return Driver.FindElement(By.Name(TotalIncomeName)).Text; 31 | } 32 | set 33 | { 34 | var element = Driver.FindElement(By.Name(TotalIncomeName)); 35 | element.Clear(); 36 | element.SendKeys(value); 37 | } 38 | } 39 | 40 | public string CharityPaidAmountTextBox 41 | { 42 | get 43 | { 44 | return Driver.FindElement(By.Name(CharityPaidAmountName)).Text; 45 | } 46 | set 47 | { 48 | var element = Driver.FindElement(By.Name(CharityPaidAmountName)); 49 | element.Clear(); 50 | element.SendKeys(value); 51 | } 52 | } 53 | 54 | public string NumberOfChildrenTextBox 55 | { 56 | get 57 | { 58 | return Driver.FindElement(By.Name(NumberOfChildrenName)).Text; 59 | } 60 | set 61 | { 62 | var element = Driver.FindElement(By.Name(NumberOfChildrenName)); 63 | element.Clear(); 64 | element.SendKeys(value); 65 | } 66 | } 67 | 68 | public void SubmitTax() 69 | { 70 | ClickSubmitButton();; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/BasePage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public abstract class BasePage 6 | { 7 | protected IWebDriver Driver => TestFixture.Driver; 8 | public const string LeftTabXPath = "//ul[@class='nav navbar-nav']//li"; 9 | public const string RightTabXPath = "//ul[@class='nav navbar-nav navbar-right']//li"; 10 | private const string SubmitButton = "//button[@type='submit' and @class='btn btn-default']"; 11 | 12 | public string HomeTabText => Driver.FindElement(By.XPath($"{LeftTabXPath}[1]//a")).Text; 13 | 14 | public string MyTaxesText => Driver.FindElement(By.XPath($"{LeftTabXPath}[2]//a")).Text; 15 | 16 | public string CurrentLoggedInUserText => Driver.FindElement(By.XPath($"{RightTabXPath}[1]//a")).Text; 17 | 18 | public int TabsCount => Driver.FindElements(By.XPath(LeftTabXPath)).Count; 19 | 20 | public void ClickSubmitButton() 21 | { 22 | Driver.FindElement(By.XPath(SubmitButton)).Click(); 23 | } 24 | 25 | public void NavigateToHomePage() 26 | { 27 | Driver.FindElement(By.XPath($"{LeftTabXPath}[1]//a")).Click(); 28 | } 29 | 30 | public void NavigateToTaxPage() 31 | { 32 | Driver.FindElement(By.XPath($"{LeftTabXPath}[2]//a")).Click(); 33 | } 34 | 35 | public void NavigateToRegisterPage() 36 | { 37 | Driver.FindElement(By.XPath($"{RightTabXPath}[1]//a")).Click(); 38 | } 39 | 40 | public void NavigateToLoginPage() 41 | { 42 | Driver.FindElement(By.XPath($"{RightTabXPath}[2]//a")).Click(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/GetTaxPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public class GetTaxPage : BasePage 6 | { 7 | public const string DisplayValueXPath = "//div[@class='form-group'][{0}]//span"; 8 | 9 | public string YearDisplay => Driver.FindElement(By.XPath(string.Format(DisplayValueXPath, 1))).Text; 10 | 11 | public string TotalIncomeDisplay => Driver.FindElement(By.XPath(string.Format(DisplayValueXPath, 2))).Text; 12 | 13 | public string CharityPaidAmountDisplay => Driver.FindElement(By.XPath(string.Format(DisplayValueXPath, 3))).Text; 14 | 15 | public string NumberOfChildrenDisplay => Driver.FindElement(By.XPath(string.Format(DisplayValueXPath, 4))).Text; 16 | 17 | public string TaxDueAmountDisplay => Driver.FindElement(By.XPath(string.Format(DisplayValueXPath, 5))).Text; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/LoginPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public class LoginPage : BasePage 6 | { 7 | protected const string EmailName = "Email"; 8 | protected const string PasswordName = "Password"; 9 | 10 | public string EmailTextBox 11 | { 12 | get 13 | { 14 | return Driver.FindElement(By.Name(EmailName)).Text; 15 | } 16 | set 17 | { 18 | var element = Driver.FindElement(By.Name(EmailName)); 19 | element.Clear(); 20 | element.SendKeys(value); 21 | } 22 | } 23 | 24 | public string PasswordTextBox 25 | { 26 | get 27 | { 28 | return Driver.FindElement(By.Name(PasswordName)).Text; 29 | } 30 | set 31 | { 32 | var element = Driver.FindElement(By.Name(PasswordName)); 33 | element.Clear(); 34 | element.SendKeys(value); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/RegisterPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public class RegisterPage : LoginPage 6 | { 7 | protected const string ConfirmPasswordName = "ConfirmPassword"; 8 | 9 | public string ConfirmPasswordTextBox 10 | { 11 | get 12 | { 13 | return Driver.FindElement(By.Name(ConfirmPasswordName)).Text; 14 | } 15 | set 16 | { 17 | var element = Driver.FindElement(By.Name(ConfirmPasswordName)); 18 | element.Clear(); 19 | element.SendKeys(value); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/PageObjects/TaxesListPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.AcceptanceTests.PageObjects 4 | { 5 | public class TaxesListPage : BasePage 6 | { 7 | public AddTaxPage ClickAddNewTax() 8 | { 9 | Driver.FindElement(By.ClassName("createNewUserAnchorSelector")).Click(); 10 | return new AddTaxPage(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Tax.Tests.AcceptanceTests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("Tax.Tests.AcceptanceTests")] 10 | [assembly: AssemblyCopyright("Copyright © 2018")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("7801e043-fefe-4d4d-a01e-c432a845dd89")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Steps/SubmitTaxSteps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Tax.Tests.AcceptanceTests.PageObjects; 4 | using TechTalk.SpecFlow; 5 | 6 | namespace Tax.Tests.AcceptanceTests.Steps 7 | { 8 | [Binding] 9 | public class SubmitTaxSteps 10 | { 11 | [Given(@"login with user")] 12 | public void GivenLoginWithUser(Table table) 13 | { 14 | var loginPage = new LoginPage(); 15 | loginPage.NavigateToLoginPage(); 16 | var email = table.Rows[0]["email"]; 17 | var password = table.Rows[0]["password"]; 18 | loginPage.EmailTextBox = email; 19 | loginPage.PasswordTextBox = password; 20 | 21 | loginPage.ClickSubmitButton(); 22 | } 23 | 24 | [Given(@"I provide the following tax details")] 25 | public void GivenIProvideTheFollowingTaxDetails(Table table) 26 | { 27 | var taxListPage = new TaxesListPage(); 28 | taxListPage.NavigateToTaxPage(); 29 | var addTaxPage = taxListPage.ClickAddNewTax(); 30 | 31 | addTaxPage.YearTextBox = table.Rows[0]["year"]; 32 | addTaxPage.TotalIncomeTextBox = table.Rows[0]["totalIncome"]; 33 | addTaxPage.CharityPaidAmountTextBox = table.Rows[0]["charityPaidAmount"]; 34 | addTaxPage.NumberOfChildrenTextBox = table.Rows[0]["numberOfChildren"]; 35 | } 36 | 37 | [When(@"submit the data")] 38 | public void WhenSubmitTheData() 39 | { 40 | var addTaxPage = new AddTaxPage(); 41 | addTaxPage.SubmitTax(); 42 | } 43 | 44 | [Then(@"Tax due amount should be")] 45 | public void ThenTaxDueAmountShouldBe(Table table) 46 | { 47 | var getTaxPage = new GetTaxPage(); 48 | Assert.AreEqual(table.Rows[0]["taxDueAmount"], getTaxPage.TaxDueAmountDisplay); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/Tax.Tests.AcceptanceTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7801E043-FEFE-4D4D-A01E-C432A845DD89} 8 | Library 9 | Properties 10 | Tax.Tests.AcceptanceTests 11 | Tax.Tests.AcceptanceTests 12 | v4.6.1 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll 43 | 44 | 45 | ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll 46 | 47 | 48 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 49 | 50 | 51 | ..\packages\SpecRun.SpecFlow.2-2-0.1.6.3\lib\net45\SpecRun.SpecFlowPlugin.dll 52 | 53 | 54 | 55 | 56 | 57 | ..\packages\SpecFlow.2.2.0\lib\net45\TechTalk.SpecFlow.dll 58 | 59 | 60 | ..\packages\SpecRun.SpecFlow.2-2-0.1.6.3\lib\net45\TechTalk.SpecRun.dll 61 | 62 | 63 | ..\packages\SpecRun.SpecFlow.2-2-0.1.6.3\lib\net45\TechTalk.SpecRun.Common.dll 64 | 65 | 66 | ..\packages\Selenium.WebDriver.3.8.0\lib\net45\WebDriver.dll 67 | 68 | 69 | 70 | 71 | True 72 | True 73 | SubmitTax.feature 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | PreserveNewest 89 | 90 | 91 | SpecFlowSingleFileGenerator 92 | SubmitTax.feature.cs 93 | 94 | 95 | 96 | 97 | Designer 98 | 99 | 100 | Designer 101 | 102 | 103 | 104 | 105 | 106 | 107 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/TestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenQA.Selenium; 3 | using OpenQA.Selenium.Chrome; 4 | using TechTalk.SpecFlow; 5 | 6 | namespace Tax.Tests.AcceptanceTests 7 | { 8 | [Binding] 9 | // تجهيزات 10 | public class TestFixture 11 | { 12 | internal static IWebDriver Driver; 13 | // Website Url 14 | private const string BaseAddress = "http://localhost:54860/"; 15 | 16 | [BeforeScenario] 17 | public static void SetUp() 18 | { 19 | Driver = new ChromeDriver() 20 | { 21 | Url = BaseAddress 22 | }; 23 | } 24 | 25 | [AfterScenario] 26 | public static void TearDown() 27 | { 28 | Driver.Dispose(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.AcceptanceTests/runtests.cmd: -------------------------------------------------------------------------------- 1 | @pushd %~dp0 2 | 3 | %windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe "" 4 | 5 | @if ERRORLEVEL 1 goto end 6 | 7 | @cd ..\packages\SpecRun.Runner.*\tools 8 | 9 | @set profile=%1 10 | @if "%profile%" == "" set profile=Default 11 | 12 | SpecRun.exe run %profile%.srprofile "/baseFolder:%~dp0\bin\Debug" /log:specrun.log %2 %3 %4 %5 13 | 14 | :end 15 | 16 | @popd 17 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.Common/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Tax.Tests.Common 5 | { 6 | public static class Helpers 7 | { 8 | public static Dictionary ToDictionary(this object data) 9 | { 10 | return data.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(data)?.ToString() ?? ""); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.Common/Tax.Tests.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.Common/TestStartup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Data.Sqlite; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Tax.Data; 7 | using Tax.Web; 8 | 9 | namespace Tax.Tests.Common 10 | { 11 | public class TestStartup : Startup 12 | { 13 | public TestStartup(IConfiguration configuration) : base(configuration) 14 | { 15 | } 16 | 17 | public override void SetupDatabase(IServiceCollection services) 18 | { 19 | var connection = new SqliteConnection($"DataSource=file:Tax-Db{Guid.NewGuid()}?mode=memory"); 20 | var options = new DbContextOptionsBuilder() 21 | .UseSqlite(connection).Options; 22 | 23 | using (var context = new TaxDbContext(options)) 24 | { 25 | context.Database.OpenConnection(); 26 | context.Database.EnsureCreated(); 27 | } 28 | 29 | services.AddTransient(x => new TaxDbContext(options)); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/Common/DriverExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using OpenQA.Selenium; 4 | using OpenQA.Selenium.Support.UI; 5 | using Tax.Tests.FunctionalTests.PageObjects; 6 | 7 | namespace Tax.Tests.FunctionalTests.Common 8 | { 9 | public static class DriverExt 10 | { 11 | public static HomePage NavigateToHomePage(this IWebDriver driver) 12 | { 13 | driver.FindElement(By.XPath($"{BasePage.LeftTabXPath}[1]//a")).Click(); 14 | return new HomePage(driver); 15 | } 16 | 17 | public static TaxesListPage NavigateToTaxPage(this IWebDriver driver) 18 | { 19 | driver.FindElement(By.XPath($"{BasePage.LeftTabXPath}[2]//a")).Click(); 20 | return new TaxesListPage(driver); 21 | } 22 | 23 | public static RegisterPage NavigateToRegisterPage(this IWebDriver driver) 24 | { 25 | driver.FindElement(By.XPath($"{BasePage.RightTabXPath}[1]//a")).Click(); 26 | return new RegisterPage(driver); 27 | } 28 | 29 | public static LoginPage NavigateToLoginPage(this IWebDriver driver) 30 | { 31 | driver.FindElement(By.XPath($"{BasePage.RightTabXPath}[2]//a")).Click(); 32 | return new LoginPage(driver); 33 | } 34 | 35 | public static HomePage GetHomePage(this IWebDriver driver) 36 | { 37 | return new HomePage(driver); 38 | } 39 | 40 | public static IWebElement FindElementWithTimeout(this IWebDriver driver, By by, int timeoutInSeconds = 3) 41 | { 42 | if (timeoutInSeconds > 0) 43 | { 44 | var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds)); 45 | return wait.Until(drv => drv.FindElement(by)); 46 | } 47 | return driver.FindElement(by); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/Controllers/AccountControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Tax.Data; 3 | using Tax.Tests.FunctionalTests.Common; 4 | using Xunit; 5 | 6 | namespace Tax.Tests.FunctionalTests.Controllers 7 | { 8 | public class AccountControllerTests : ServerFixture 9 | { 10 | [Fact] 11 | public async Task Login_SubmitValidData_UserLoggedIn() 12 | { 13 | //Arrange 14 | // Don't create users using UI unless it's part of your scenario. for performance 15 | await UserManager.CreateAsync(new ApplicationUser 16 | { 17 | Email = Email, 18 | UserName = Email 19 | }, Password); 20 | 21 | // Act 22 | var loginPage = Driver.NavigateToLoginPage(); 23 | loginPage.EmailTextBox = Email; 24 | loginPage.PasswordTextBox = Password; 25 | 26 | var homePage = loginPage.ClickLogin(); 27 | var result = homePage.CurrentLoggedInUserText; 28 | 29 | Assert.Equal($"Hello {Email}!", result); 30 | } 31 | 32 | [Fact(Skip = "To be done by you, remove Skip property in the Fact attribute to be able to run this method and see it in text explorer")] 33 | public async Task Register_SubmitValidData_UserCreated() 34 | { 35 | // TODO by you :) 36 | } 37 | 38 | public override void Dispose() 39 | { 40 | // we can use different approach which is creating random different users then on all tests tearDown we delete the database or leave the framework to remove it. 41 | //UserManager.DeleteAsync(User).Wait(); 42 | base.Dispose(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/Controllers/HomeControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using OpenQA.Selenium; 3 | using Tax.Tests.FunctionalTests.PageObjects; 4 | using Xunit; 5 | 6 | namespace Tax.Tests.FunctionalTests.Controllers 7 | { 8 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 9 | public class HomeControllerTests : ServerFixture 10 | { 11 | [Fact] 12 | public void HomePage_LandingPage_DisplayTabsAndText() 13 | { 14 | // Arrange 15 | var parentNavBar = Driver.FindElement(By.ClassName("navbar-nav")); 16 | var tabs = parentNavBar.FindElements(By.XPath("li//a")); 17 | 18 | // Assert 19 | Assert.Equal(2, tabs.Count); 20 | Assert.Equal("Home", tabs.First().Text); 21 | Assert.Equal("My Taxes", tabs.ElementAt(1).Text); 22 | } 23 | 24 | [Fact] 25 | public void HomePageObject_LandingPage_DisplayTabsAndText() 26 | { 27 | // Arrange 28 | var homePage = new HomePage(Driver); 29 | 30 | // Assert 31 | Assert.Equal(2, homePage.TabsCount); 32 | Assert.Equal("Home", homePage.HomeTabText); 33 | Assert.Equal("My Taxes", homePage.MyTaxesText); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/Controllers/TaxControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Tax.Data; 3 | using Tax.Tests.FunctionalTests.Common; 4 | using Xunit; 5 | 6 | namespace Tax.Tests.FunctionalTests.Controllers 7 | { 8 | public class TaxControllerTests : ServerFixture 9 | { 10 | [Fact] 11 | public async Task AddTaxGetTax_SubmitValidData_DataGetsDisplayedBack() 12 | { 13 | //Arrange 14 | const string year = "2000"; 15 | const string totalIncome = "10000"; 16 | const string charityPaidAmount = "10"; 17 | const string numberOfChildren = "1"; 18 | 19 | await UserManager.CreateAsync(new ApplicationUser 20 | { 21 | Email = Email, 22 | UserName = Email 23 | }, Password); 24 | 25 | // Act 26 | var loginPage = Driver.NavigateToLoginPage(); 27 | loginPage.EmailTextBox = Email; 28 | loginPage.PasswordTextBox = Password; 29 | loginPage.ClickLogin(); 30 | 31 | var taxesListPage = Driver.NavigateToTaxPage(); 32 | var addTaxPage = taxesListPage.ClickAddNewTax(); 33 | addTaxPage.YearTextBox = year; 34 | addTaxPage.TotalIncomeTextBox = totalIncome; 35 | addTaxPage.CharityPaidAmountTextBox = charityPaidAmount; 36 | addTaxPage.NumberOfChildrenTextBox = numberOfChildren; 37 | 38 | var getTaxPage = addTaxPage.SubmitTax(); 39 | 40 | Assert.Equal(year, getTaxPage.YearDisplay); 41 | Assert.Equal(totalIncome, getTaxPage.TotalIncomeDisplay); 42 | Assert.Equal(charityPaidAmount, getTaxPage.CharityPaidAmountDisplay); 43 | Assert.Equal(numberOfChildren, getTaxPage.NumberOfChildrenDisplay); 44 | Assert.Equal("149.85", getTaxPage.TaxDueAmountDisplay); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/AddTaxPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.FunctionalTests.PageObjects 4 | { 5 | public class AddTaxPage : BasePage 6 | { 7 | private const string YearName = "Year"; 8 | private const string TotalIncomeName = "TotalIncome"; 9 | private const string CharityPaidAmountName = "CharityPaidAmount"; 10 | private const string NumberOfChildrenName = "NumberOfChildren"; 11 | 12 | public AddTaxPage(IWebDriver driver) : base(driver) 13 | { 14 | } 15 | 16 | public string YearTextBox 17 | { 18 | get 19 | { 20 | return Driver.FindElement(By.Name(YearName)).Text; 21 | } 22 | set 23 | { 24 | var element = Driver.FindElement(By.Name(YearName)); 25 | element.Clear(); 26 | element.SendKeys(value); 27 | } 28 | } 29 | 30 | public string TotalIncomeTextBox 31 | { 32 | get 33 | { 34 | return Driver.FindElement(By.Name(TotalIncomeName)).Text; 35 | } 36 | set 37 | { 38 | var element = Driver.FindElement(By.Name(TotalIncomeName)); 39 | element.Clear(); 40 | element.SendKeys(value); 41 | } 42 | } 43 | 44 | public string CharityPaidAmountTextBox 45 | { 46 | get 47 | { 48 | return Driver.FindElement(By.Name(CharityPaidAmountName)).Text; 49 | } 50 | set 51 | { 52 | var element = Driver.FindElement(By.Name(CharityPaidAmountName)); 53 | element.Clear(); 54 | element.SendKeys(value); 55 | } 56 | } 57 | 58 | public string NumberOfChildrenTextBox 59 | { 60 | get 61 | { 62 | return Driver.FindElement(By.Name(NumberOfChildrenName)).Text; 63 | } 64 | set 65 | { 66 | var element = Driver.FindElement(By.Name(NumberOfChildrenName)); 67 | element.Clear(); 68 | element.SendKeys(value); 69 | } 70 | } 71 | 72 | public GetTaxPage SubmitTax() 73 | { 74 | ClickSubmitButton(); 75 | return new GetTaxPage(Driver); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/BasePage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.FunctionalTests.PageObjects 4 | { 5 | public abstract class BasePage 6 | { 7 | protected readonly IWebDriver Driver; 8 | public const string LeftTabXPath = "//ul[@class='nav navbar-nav']//li"; 9 | public const string RightTabXPath = "//ul[@class='nav navbar-nav navbar-right']//li"; 10 | private const string SubmitButton = "//button[@type='submit' and @class='btn btn-default']"; 11 | 12 | public string HomeTabText => Driver.FindElement(By.XPath($"{LeftTabXPath}[1]//a")).Text; 13 | 14 | public string MyTaxesText => Driver.FindElement(By.XPath($"{LeftTabXPath}[2]//a")).Text; 15 | 16 | public string CurrentLoggedInUserText => Driver.FindElement(By.XPath($"{RightTabXPath}[1]//a")).Text; 17 | 18 | public int TabsCount => Driver.FindElements(By.XPath(LeftTabXPath)).Count; 19 | 20 | protected BasePage(IWebDriver driver) 21 | { 22 | Driver = driver; 23 | } 24 | 25 | protected void ClickSubmitButton() 26 | { 27 | Driver.FindElement(By.XPath(SubmitButton)).Click(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/GetTaxPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using Tax.Tests.FunctionalTests.Common; 3 | 4 | namespace Tax.Tests.FunctionalTests.PageObjects 5 | { 6 | public class GetTaxPage : BasePage 7 | { 8 | public const string DisplayValueXPath = "//div[@class='form-group'][{0}]//span"; 9 | public GetTaxPage(IWebDriver driver) : base(driver) 10 | { 11 | } 12 | 13 | public string YearDisplay => Driver.FindElementWithTimeout(By.XPath(string.Format(DisplayValueXPath, 1))).Text; 14 | 15 | public string TotalIncomeDisplay => Driver.FindElementWithTimeout(By.XPath(string.Format(DisplayValueXPath, 2))).Text; 16 | 17 | public string CharityPaidAmountDisplay => Driver.FindElementWithTimeout(By.XPath(string.Format(DisplayValueXPath, 3))).Text; 18 | 19 | public string NumberOfChildrenDisplay => Driver.FindElementWithTimeout(By.XPath(string.Format(DisplayValueXPath, 4))).Text; 20 | 21 | public string TaxDueAmountDisplay => Driver.FindElementWithTimeout(By.XPath(string.Format(DisplayValueXPath, 5))).Text; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/HomePage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.FunctionalTests.PageObjects 4 | { 5 | public class HomePage : BasePage 6 | { 7 | public HomePage(IWebDriver driver) : base(driver) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/LoginPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.FunctionalTests.PageObjects 4 | { 5 | public class LoginPage : BasePage 6 | { 7 | protected const string EmailName = "Email"; 8 | protected const string PasswordName = "Password"; 9 | 10 | public LoginPage(IWebDriver driver) : base(driver) 11 | { 12 | } 13 | 14 | public string EmailTextBox 15 | { 16 | get 17 | { 18 | return Driver.FindElement(By.Name(EmailName)).Text; 19 | } 20 | set 21 | { 22 | var element = Driver.FindElement(By.Name(EmailName)); 23 | element.Clear(); 24 | element.SendKeys(value); 25 | } 26 | } 27 | 28 | public string PasswordTextBox 29 | { 30 | get 31 | { 32 | return Driver.FindElement(By.Name(PasswordName)).Text; 33 | } 34 | set 35 | { 36 | var element = Driver.FindElement(By.Name(PasswordName)); 37 | element.Clear(); 38 | element.SendKeys(value); 39 | } 40 | } 41 | 42 | public HomePage ClickLogin() 43 | { 44 | ClickSubmitButton(); 45 | return new HomePage(Driver); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/RegisterPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using OpenQA.Selenium; 5 | 6 | namespace Tax.Tests.FunctionalTests.PageObjects 7 | { 8 | public class RegisterPage : BasePage 9 | { 10 | public RegisterPage(IWebDriver driver) : base(driver) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/PageObjects/TaxesListPage.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | 3 | namespace Tax.Tests.FunctionalTests.PageObjects 4 | { 5 | public class TaxesListPage : BasePage 6 | { 7 | public TaxesListPage(IWebDriver driver) : base(driver) 8 | { 9 | } 10 | 11 | public AddTaxPage ClickAddNewTax() 12 | { 13 | Driver.FindElement(By.ClassName("createNewUserAnchorSelector")).Click(); 14 | return new AddTaxPage(Driver); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/ServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using OpenQA.Selenium; 8 | using OpenQA.Selenium.Chrome; 9 | using Tax.Data; 10 | using Tax.Repository; 11 | using Tax.Tests.Common; 12 | 13 | namespace Tax.Tests.FunctionalTests 14 | { 15 | public class ServerFixture : IDisposable 16 | { 17 | private readonly IWebHost _host; 18 | private readonly string _baseAddress = "http://localhost:{0}"; 19 | private static int _portNumber = 5410; 20 | protected const string Email = "ibraheem.osama@gmail.com"; 21 | protected const string Password = "P@ssw0rd"; 22 | 23 | protected IWebDriver Driver { get; } 24 | protected IServiceProvider ServiceProvider { get; } 25 | protected UserManager UserManager { get; } 26 | protected IUserTaxRepository UserTaxRepository { get; } 27 | protected ApplicationUser User => UserManager.Users.FirstOrDefault(x => x.Email == Email); 28 | 29 | public ServerFixture() 30 | { 31 | _baseAddress = string.Format(_baseAddress, _portNumber++); 32 | 33 | const string environemnt = "Development"; 34 | var rootLocation = 35 | $@"{Directory.GetParent(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.FullName}\Tax.Web"; 36 | 37 | _host = new WebHostBuilder() 38 | .UseKestrel() 39 | .UseUrls(_baseAddress) 40 | .UseEnvironment(environemnt) 41 | .UseContentRoot(rootLocation) 42 | .UseStartup().Build(); 43 | 44 | ServiceProvider = _host.Services; 45 | 46 | // fire and forget 47 | _host.RunAsync(); 48 | 49 | UserManager = ServiceProvider.GetService>(); 50 | UserTaxRepository = ServiceProvider.GetService(); 51 | 52 | // Selenium Tests 53 | Driver = new ChromeDriver(Environment.CurrentDirectory); 54 | Driver.Url = _baseAddress; 55 | } 56 | 57 | public virtual void Dispose() 58 | { 59 | _host.Dispose(); 60 | Driver.Dispose(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.FunctionalTests/Tax.Tests.FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.0 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | true 28 | true 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.IntegrationTests/Controllers/AccountControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using Tax.Tests.Common; 5 | using Tax.Web.Models.AccountViewModels; 6 | using Xunit; 7 | 8 | namespace Tax.Tests.IntegrationTests.Controllers 9 | { 10 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 11 | public class AccountControllerTests : ServerFixture 12 | { 13 | [Fact] 14 | public async Task Registration_CreateNewUser_UserCreated() 15 | { 16 | // Arrange 17 | var formData = new RegisterViewModel 18 | { 19 | Email = Email, 20 | Password = Password, 21 | ConfirmPassword = Password 22 | }.ToDictionary(); 23 | 24 | // Act 25 | HttpResponseMessage response = await PostAsync("/Account/Register", formData); 26 | 27 | //Assert 28 | Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); 29 | Assert.NotNull(User); 30 | Assert.Equal(Email, User.UserName); 31 | } 32 | 33 | [Fact] 34 | public async Task Login_RegisteredUser_AbleToLogin() 35 | { 36 | // Arrange 37 | var registerData = new RegisterViewModel 38 | { 39 | Email = Email, 40 | Password = Password, 41 | ConfirmPassword = Password 42 | }.ToDictionary(); 43 | 44 | var loginData = new LoginViewModel 45 | { 46 | Email = Email, 47 | Password = Password 48 | }.ToDictionary(); 49 | 50 | // Act 51 | await PostAsync("/Account/Register", registerData); 52 | var response = await PostAsync("/Account/Login", loginData); 53 | 54 | //Assert 55 | Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.IntegrationTests/Controllers/HomeControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading.Tasks; 3 | using Xunit; 4 | 5 | namespace Tax.Tests.IntegrationTests.Controllers 6 | { 7 | public class HomeControllerTests : ServerFixture 8 | { 9 | [Fact] 10 | public async Task PassingTest() 11 | { 12 | var response = await Client.GetAsync("/"); 13 | var responseString = await response.Content.ReadAsStringAsync(); 14 | Assert.False(string.IsNullOrEmpty(responseString)); 15 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.IntegrationTests/Controllers/TaxControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading.Tasks; 3 | using Tax.Tests.Common; 4 | using Tax.Data; 5 | using Tax.Web.Models; 6 | using Xunit; 7 | 8 | namespace Tax.Tests.IntegrationTests.Controllers 9 | { 10 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 11 | public class TaxControllerTests : ServerFixture 12 | { 13 | private const string AddTaxUrl = "/Tax/AddTax"; 14 | 15 | [Fact] 16 | public async Task AddTax_SubmitTax_DataCreated() 17 | { 18 | // Arrange 19 | var taxViewModel = new TaxViewModel 20 | { 21 | CharityPaidAmount = 10, 22 | NumberOfChildren = 1, 23 | TotalIncome = 10000, 24 | Year = 2000 25 | }; 26 | var taxViewModelData = taxViewModel.ToDictionary(); 27 | 28 | // Act 29 | 30 | await UserManager.CreateAsync(new ApplicationUser 31 | { 32 | Email = Email, 33 | UserName = Email 34 | }, Password); 35 | 36 | var response = await PostAsync(AddTaxUrl, taxViewModelData, Email, Password); 37 | 38 | var taxResult = await UserTaxRepository.GetUserTax(User.Id, taxViewModel.Year.Value); 39 | 40 | //Assert 41 | Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); 42 | Assert.NotNull(taxResult); 43 | 44 | // making sure the DB is retreving correctly 45 | Assert.Equal(taxViewModel.TotalIncome.Value, taxResult.TotalIncome); 46 | Assert.Equal(taxViewModel.CharityPaidAmount.Value, taxResult.CharityPaidAmount); 47 | Assert.Equal(taxViewModel.NumberOfChildren.Value, taxResult.NumberOfChildren); 48 | Assert.Equal(taxViewModel.Year.Value, taxResult.Year); 49 | 50 | // making sure calcuation with db integration is working correctly 51 | Assert.Equal((decimal)149.85, taxResult.TaxDueAmount); 52 | } 53 | 54 | // Jump to unit tests for controller tests 55 | [Fact] 56 | public async Task AddTax_SubmitInvalidYear_ThrowValidation() 57 | { 58 | // Arrange 59 | var taxViewModel = new TaxViewModel 60 | { 61 | CharityPaidAmount = 10, 62 | NumberOfChildren = 1, 63 | TotalIncome = 10000, 64 | Year = 0 65 | }; 66 | var taxViewModelData = taxViewModel.ToDictionary(); 67 | 68 | // Act 69 | await UserManager.CreateAsync(new ApplicationUser 70 | { 71 | Email = Email, 72 | UserName = Email 73 | }, Password); 74 | 75 | var response = await PostAsync(AddTaxUrl, taxViewModelData, Email, Password); 76 | var responseContent = await response.Content.ReadAsStringAsync(); 77 | var taxResult = await UserTaxRepository.GetUserTax(User.Id, taxViewModel.Year.Value); 78 | 79 | //Assert 80 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 81 | 82 | // checking no data is inserted 83 | Assert.Null(taxResult); 84 | Assert.Contains("The field Tax Year must be between 2000 and 2020.", responseContent); 85 | } 86 | 87 | [Fact(Skip = "To be done by you, remove Skip property in the Fact attribute to be able to run this method and see it in text explorer")] 88 | public async Task GetTax_WhenInsertValidData_DataRetrieved() 89 | { 90 | // TODO as Home work ;) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.IntegrationTests/ServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.Identity; 10 | using Microsoft.AspNetCore.TestHost; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Tax.Data; 13 | using Tax.Repository; 14 | using Tax.Tests.Common; 15 | using Tax.Web.Models.AccountViewModels; 16 | 17 | namespace Tax.Tests.IntegrationTests 18 | { 19 | public class ServerFixture : IDisposable 20 | { 21 | private const string BaseAddress = "http://localhost:5310"; 22 | private const string RequestVerificationToken = "__RequestVerificationToken"; 23 | private readonly TestServer _server; 24 | 25 | protected const string Email = "ibraheem.osama@gmail.com"; 26 | protected const string Password = "P@ssw0rd"; 27 | protected HttpClient Client { get; } 28 | protected IServiceProvider ServiceProvider { get; } 29 | protected UserManager UserManager { get; } 30 | protected IUserTaxRepository UserTaxRepository { get; } 31 | protected ApplicationUser User => UserManager.Users.FirstOrDefault(x => x.Email == Email); 32 | 33 | public ServerFixture() 34 | { 35 | const string environemnt = "Development"; 36 | var rootLocation = 37 | $@"{Directory.GetParent(Directory.GetCurrentDirectory()).Parent?.Parent?.Parent?.FullName}\Tax.Web"; 38 | 39 | var builder = new WebHostBuilder() 40 | .UseEnvironment(environemnt) 41 | .UseContentRoot(rootLocation) 42 | .UseStartup(); 43 | 44 | _server = new TestServer(builder); 45 | Client = _server.CreateClient(); 46 | 47 | Client.BaseAddress = new Uri(BaseAddress); 48 | ServiceProvider = _server.Host.Services; 49 | 50 | 51 | UserManager = ServiceProvider.GetService>(); 52 | UserTaxRepository = ServiceProvider.GetService(); 53 | } 54 | 55 | protected async Task PostAsync(string url, Dictionary formData, string email = null, string password = null) 56 | { 57 | IList cookies = new List(); 58 | if (email != null && password != null) 59 | { 60 | cookies = (await GetAuthTokenCookiesAsync(email, password)).ToList(); 61 | } 62 | 63 | var antiForgeryResponse = await GetAntiForgeryResponseAsync(url, cookies); 64 | var antiForgeryBodyToken = await ExtractAntiForgeryBodyTokenAsync(antiForgeryResponse); 65 | var antiForgeryCookieToken = GetAntiForgeryCookieToken(antiForgeryResponse); 66 | 67 | formData.Add(RequestVerificationToken, antiForgeryBodyToken); 68 | 69 | var content = new FormUrlEncodedContent(formData); 70 | cookies.Add(antiForgeryCookieToken); 71 | content.Headers.Add("Cookie", cookies); 72 | 73 | return await Client.PostAsync(url, content); 74 | } 75 | 76 | private async Task> GetAuthTokenCookiesAsync(string email, string password) 77 | { 78 | const string authTokenCookieName = ".AspNetCore.Identity.Application"; 79 | var payload = new LoginViewModel 80 | { 81 | Email = email, 82 | Password = password 83 | }.ToDictionary(); 84 | var response = await PostAsync("/Account/Login", payload); 85 | 86 | var setCookies = response 87 | .Headers 88 | .GetValues("Set-Cookie"); 89 | return setCookies.Where(x => x.Contains(authTokenCookieName)); 90 | } 91 | 92 | private async Task GetAntiForgeryResponseAsync(string path = null, IList cookies = null) 93 | { 94 | path = path ?? "/Account/login"; 95 | var request = new HttpRequestMessage(HttpMethod.Get, path); 96 | if (cookies != null && cookies.Any()) 97 | request.Headers.Add("Cookie", cookies); 98 | var antiForgResponse = await Client.SendAsync(request); 99 | return antiForgResponse; 100 | } 101 | 102 | private static string GetAntiForgeryCookieToken(HttpResponseMessage antiForgResponse) 103 | { 104 | var cookieValue = antiForgResponse.Headers.GetValues("Set-Cookie").First(x => x.Contains("Antiforgery")); 105 | return cookieValue.Substring(0, cookieValue.IndexOf(";", StringComparison.Ordinal) + 1); 106 | } 107 | 108 | private static async Task ExtractAntiForgeryBodyTokenAsync(HttpResponseMessage response) 109 | { 110 | var htmlResponseText = await response.Content.ReadAsStringAsync(); 111 | var match = Regex.Match(htmlResponseText, $@"\"); 112 | return match.Success ? match.Groups[1].Captures[0].Value : null; 113 | } 114 | 115 | public virtual void Dispose() 116 | { 117 | Client.Dispose(); 118 | _server.Dispose(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.IntegrationTests/Tax.Tests.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.0 4 | true 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | true 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Controllers/TaxControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using NSubstitute; 6 | using Tax.Core; 7 | using Tax.Data; 8 | using Tax.Repository; 9 | using Tax.Web.Controllers; 10 | using Tax.Web.Models; 11 | using Xunit; 12 | 13 | namespace Tax.Tests.UnitTests.Controllers 14 | { 15 | public class TaxControllerTests 16 | { 17 | [Fact] 18 | public async Task AddTax_InvalidInput_ReturnsToSameActionWithValidation() 19 | { 20 | // why all these nulls !! 21 | var userManagerStub = Substitute.For>(Substitute.For>(), null, null, null, null, null, null, null, null); 22 | var logger = Substitute.For>(); 23 | var userTaxRepo = Substitute.For(); 24 | var taxService = Substitute.For(); 25 | 26 | var taxController = new TaxController(userManagerStub, logger, userTaxRepo, taxService); 27 | 28 | taxController.ModelState.AddModelError("error", "error"); 29 | 30 | var result = await taxController.AddTax(new TaxViewModel()); 31 | 32 | Assert.True(false); 33 | Assert.True(taxController.ModelState.IsValid); 34 | Assert.IsType(result); 35 | Assert.IsNotType(result); 36 | } 37 | 38 | [Fact(Skip = "To be done by you, remove Skip property in the Fact attribute to be able to run this method and see it in text explorer")] 39 | public async Task GetTax_ValidData_ReturnsViewResults() 40 | { 41 | // TODO try to stub all dependencies inside GetTax Action inside Tax Controller :) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Core/TaxServiceTests-OldWay.cs: -------------------------------------------------------------------------------- 1 | using Tax.Core; 2 | using Xunit; 3 | 4 | namespace Tax.Tests.UnitTests.Core 5 | { 6 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 7 | public class TaxServiceOldWayTests 8 | { 9 | [Fact] 10 | public void CalculateCorrectly_ValidData_Success() 11 | { 12 | // Arrange 13 | const int year = 2000; 14 | var stub = new Stub(); 15 | 16 | // Act 17 | var uut = new TaxService(stub); 18 | var result = uut.CalculateTax(year, 2, 10, 1000); 19 | 20 | //Assert 21 | Assert.Equal(9900, result); 22 | } 23 | 24 | class Stub : IYearService 25 | { 26 | public decimal GetYearPercentage(int year) 27 | { 28 | if (year == 2000) 29 | { 30 | return 10; 31 | } 32 | 33 | return 5; 34 | } 35 | } 36 | 37 | [Fact] 38 | public void YearServiceGetCalled_ValidateCall_MethodCalledSuccessfully() 39 | { 40 | // Arrange 41 | const int year = 2000; 42 | var uut = new Mock(); 43 | 44 | // Act 45 | var taxService = new TaxService(uut); 46 | taxService.CalculateTax(year, 3, 10, 1000); 47 | 48 | //Assert 49 | Assert.True(uut.IsCalled); 50 | } 51 | 52 | class Mock : IYearService 53 | { 54 | public bool IsCalled = false; 55 | 56 | public decimal GetYearPercentage(int year) 57 | { 58 | IsCalled = true; 59 | 60 | // Here I really don't care about the value 61 | // I just care about the IsCalled 62 | return default(decimal); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Core/TaxServiceTests.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute; 2 | using Tax.Core; 3 | using Xunit; 4 | 5 | namespace Tax.Tests.UnitTests.Core 6 | { 7 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 8 | public class TaxServiceTests 9 | { 10 | private readonly IYearService _stub; 11 | public TaxServiceTests() 12 | { 13 | _stub = Substitute.For(); 14 | } 15 | 16 | [Fact] 17 | public void IsValidInput_BadData_throwException() 18 | { 19 | // Arrange 20 | const int year = 0; 21 | _stub.GetYearPercentage(year).Returns(10); 22 | 23 | // Act 24 | var uut = new TaxService(_stub); 25 | 26 | var result = Assert.Throws(() => uut.CalculateTax(year, 2, 10, 1000)); 27 | 28 | //Assert 29 | Assert.Equal(Constants.YearZeroMessage, result.Message); 30 | } 31 | 32 | [Fact] 33 | public void CalculateCorrectly_ValidData_Success() 34 | { 35 | // Arrange 36 | const int year = 2000; 37 | _stub.GetYearPercentage(year).Returns(10); 38 | 39 | // Act 40 | var uut = new TaxService(_stub); 41 | var result = uut.CalculateTax(year, 2, 10, 1000); 42 | 43 | //Assert 44 | Assert.Equal(9900, result); 45 | } 46 | 47 | [Fact] 48 | public void CalculateCorrectly_Year2000NumChild3_Success() 49 | { 50 | // Arrange 51 | const int year = 2000; 52 | _stub.GetYearPercentage(year).Returns(10); 53 | 54 | // Act 55 | var uut = new TaxService(_stub); 56 | var result = uut.CalculateTax(year, 3, 10, 1000); 57 | 58 | //Assert 59 | Assert.Equal(6600, result); 60 | } 61 | 62 | [Fact] 63 | public void YearServiceGetCalled_ValidateCall_MethodCalledSuccessfully() 64 | { 65 | // Arrange 66 | const int year = 2000; 67 | var mock = Substitute.For(); 68 | 69 | // Act 70 | var uut = new TaxService(mock); 71 | uut.CalculateTax(year, 3, 10, 1000); 72 | 73 | //Assert 74 | mock.Received().GetYearPercentage(year); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Core/YearServiceTests.cs: -------------------------------------------------------------------------------- 1 | using Tax.Core; 2 | using Xunit; 3 | 4 | namespace Tax.Tests.UnitTests.Core 5 | { 6 | //[UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior] 7 | public class YearServiceTests 8 | { 9 | [Fact] 10 | public void GetYearPercentage_SendValidData_ReturnsResult() 11 | { 12 | // Arrange 13 | var yearService = new YearService(); 14 | 15 | // Act 16 | var result = yearService.GetYearPercentage(2000); 17 | 18 | //Assert 19 | Assert.Equal((decimal)0.015, result); 20 | } 21 | 22 | [Theory] 23 | [InlineData(0.015, 2000)] 24 | [InlineData(0.025, 2016)] 25 | [InlineData(0.035, 2017)] 26 | [InlineData(0.045, 2018)] 27 | public void GetYearPercentage_SendValidManyData_ReturnsResult(decimal actual, int year) 28 | { 29 | // Arrange 30 | var yearService = new YearService(); 31 | 32 | // Act 33 | var result = yearService.GetYearPercentage(year); 34 | 35 | //Assert 36 | Assert.Equal(actual, result); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Models/TaxViewModelTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using Tax.Web.Models; 4 | using Xunit; 5 | 6 | namespace Tax.Tests.UnitTests.Models 7 | { 8 | public class TaxViewModelTests 9 | { 10 | [Fact] 11 | public void AddTax_InvalidInput_ThrowValidation() 12 | { 13 | // Arrange 14 | var model = new TaxViewModel(); 15 | var context = new ValidationContext(model, null, null); 16 | var result = new List(); 17 | 18 | // Act 19 | var valid = Validator.TryValidateObject(model, context, result, true); 20 | 21 | // Assert 22 | Assert.False(valid); 23 | Assert.Equal(4, result.Count); 24 | Assert.Contains(result, i => i.ErrorMessage == "The Tax Year field is required."); 25 | Assert.Contains(result, i => i.ErrorMessage == "The Total Income field is required."); 26 | Assert.Contains(result, i => i.ErrorMessage == "The Charity Paid Amount field is required."); 27 | Assert.Contains(result, i => i.ErrorMessage == "The Number Of Children field is required."); 28 | } 29 | 30 | [Fact] 31 | public void AddTax_InvalidYearInput_ThrowValidation() 32 | { 33 | // Arrange 34 | var model = new TaxViewModel 35 | { 36 | Year = 0, 37 | CharityPaidAmount = 1, 38 | NumberOfChildren = 1, 39 | TotalIncome = 1 40 | }; 41 | 42 | var context = new ValidationContext(model, null, null); 43 | var result = new List(); 44 | 45 | // Act 46 | var valid = Validator.TryValidateObject(model, context, result, true); 47 | 48 | // Assert 49 | Assert.False(valid); 50 | Assert.Single(result); 51 | Assert.Contains(result, i => i.ErrorMessage == "The field Tax Year must be between 2000 and 2020."); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Tests.UnitTests/Tax.Tests.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Tax.Web.Models; 8 | 9 | namespace Tax.Web.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | public IActionResult Index() 14 | { 15 | return View(); 16 | } 17 | 18 | public IActionResult Error() 19 | { 20 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Controllers/TaxController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Logging; 7 | using Tax.Core; 8 | using Tax.Data; 9 | using Tax.Repository; 10 | using Tax.Web.Models; 11 | 12 | namespace Tax.Web.Controllers 13 | { 14 | [Authorize] 15 | [Route("[controller]/[action]")] 16 | public class TaxController : Controller 17 | { 18 | private readonly ITaxService _taxService; 19 | private readonly IUserTaxRepository _userTaxRepository; 20 | private readonly UserManager _userManager; 21 | private readonly ILogger _logger; 22 | 23 | public TaxController( 24 | UserManager userManager, 25 | ILogger logger, 26 | IUserTaxRepository userTaxRepository, 27 | ITaxService taxService) 28 | { 29 | _userManager = userManager; 30 | _logger = logger; 31 | _userTaxRepository = userTaxRepository; 32 | _taxService = taxService; 33 | } 34 | 35 | [HttpGet] 36 | public async Task Index() 37 | { 38 | var user = await _userManager.GetUserAsync(User); 39 | if (user == null) 40 | { 41 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 42 | } 43 | 44 | var result = await _userTaxRepository.GetAllUserTax(user.Id); 45 | return View(result); 46 | } 47 | 48 | [HttpGet] 49 | public async Task GetTax(int? year) 50 | { 51 | if (!year.HasValue) 52 | { 53 | _logger.LogError("no year is provided"); 54 | throw new ApplicationException("Year must be provided"); 55 | } 56 | 57 | var user = await _userManager.GetUserAsync(User); 58 | if (user == null) 59 | { 60 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 61 | } 62 | 63 | var userTax = await _userTaxRepository.GetUserTax(user.Id, year.Value); 64 | 65 | var result = new TaxViewModel 66 | { 67 | CharityPaidAmount = userTax.CharityPaidAmount, 68 | NumberOfChildren = userTax.NumberOfChildren, 69 | TotalIncome = userTax.TotalIncome, 70 | Year = userTax.Year, 71 | TaxDueAmount = userTax.TaxDueAmount 72 | }; 73 | 74 | return View(result); 75 | } 76 | 77 | [HttpGet] 78 | public IActionResult AddTax() 79 | { 80 | return View(); 81 | } 82 | 83 | [HttpPost] 84 | [ValidateAntiForgeryToken] 85 | public async Task AddTax(TaxViewModel input) 86 | { 87 | if (ModelState.IsValid) 88 | { 89 | var user = await _userManager.GetUserAsync(User); 90 | if (user == null) 91 | { 92 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 93 | } 94 | 95 | await _userTaxRepository.AddUserTax(new UserTax 96 | { 97 | UserId = user.Id, 98 | CharityPaidAmount = input.CharityPaidAmount.Value, 99 | TotalIncome = input.TotalIncome.Value, 100 | NumberOfChildren = input.NumberOfChildren.Value, 101 | Year = input.Year.Value, 102 | TaxDueAmount = _taxService.CalculateTax(input.Year.Value, input.NumberOfChildren.Value, input.CharityPaidAmount.Value, input.TotalIncome.Value) 103 | }); 104 | 105 | _logger.LogInformation("New Tax Created"); 106 | 107 | return LocalRedirect($"/Tax/getTax?year={input.Year}"); 108 | } 109 | 110 | return View(input); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Extensions/UrlHelperExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Tax.Web.Controllers; 6 | 7 | namespace Microsoft.AspNetCore.Mvc 8 | { 9 | public static class UrlHelperExtensions 10 | { 11 | public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme) 12 | { 13 | return urlHelper.Action( 14 | action: nameof(AccountController.ConfirmEmail), 15 | controller: "Account", 16 | values: new { userId, code }, 17 | protocol: scheme); 18 | } 19 | 20 | public static string ResetPasswordCallbackLink(this IUrlHelper urlHelper, string userId, string code, string scheme) 21 | { 22 | return urlHelper.Action( 23 | action: nameof(AccountController.ResetPassword), 24 | controller: "Account", 25 | values: new { userId, code }, 26 | protocol: scheme); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/ExternalLoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class ExternalLoginViewModel 10 | { 11 | [Required] 12 | [EmailAddress] 13 | public string Email { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/ForgotPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class ForgotPasswordViewModel 10 | { 11 | [Required] 12 | [EmailAddress] 13 | public string Email { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class LoginViewModel 10 | { 11 | [Required] 12 | [EmailAddress] 13 | public string Email { get; set; } 14 | 15 | [Required] 16 | [DataType(DataType.Password)] 17 | public string Password { get; set; } 18 | 19 | [Display(Name = "Remember me?")] 20 | public bool RememberMe { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/LoginWith2faViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class LoginWith2faViewModel 10 | { 11 | [Required] 12 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 13 | [DataType(DataType.Text)] 14 | [Display(Name = "Authenticator code")] 15 | public string TwoFactorCode { get; set; } 16 | 17 | [Display(Name = "Remember this machine")] 18 | public bool RememberMachine { get; set; } 19 | 20 | public bool RememberMe { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/LoginWithRecoveryCodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class LoginWithRecoveryCodeViewModel 10 | { 11 | [Required] 12 | [DataType(DataType.Text)] 13 | [Display(Name = "Recovery Code")] 14 | public string RecoveryCode { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/RegisterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class RegisterViewModel 10 | { 11 | [Required] 12 | [EmailAddress] 13 | [Display(Name = "Email")] 14 | public string Email { get; set; } 15 | 16 | [Required] 17 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 18 | [DataType(DataType.Password)] 19 | [Display(Name = "Password")] 20 | public string Password { get; set; } 21 | 22 | [DataType(DataType.Password)] 23 | [Display(Name = "Confirm password")] 24 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 25 | public string ConfirmPassword { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/AccountViewModels/ResetPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.AccountViewModels 8 | { 9 | public class ResetPasswordViewModel 10 | { 11 | [Required] 12 | [EmailAddress] 13 | public string Email { get; set; } 14 | 15 | [Required] 16 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 17 | [DataType(DataType.Password)] 18 | public string Password { get; set; } 19 | 20 | [DataType(DataType.Password)] 21 | [Display(Name = "Confirm password")] 22 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 23 | public string ConfirmPassword { get; set; } 24 | 25 | public string Code { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tax.Web.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/ChangePasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class ChangePasswordViewModel 10 | { 11 | [Required] 12 | [DataType(DataType.Password)] 13 | [Display(Name = "Current password")] 14 | public string OldPassword { get; set; } 15 | 16 | [Required] 17 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 18 | [DataType(DataType.Password)] 19 | [Display(Name = "New password")] 20 | public string NewPassword { get; set; } 21 | 22 | [DataType(DataType.Password)] 23 | [Display(Name = "Confirm new password")] 24 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 25 | public string ConfirmPassword { get; set; } 26 | 27 | public string StatusMessage { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/EnableAuthenticatorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Mvc.ModelBinding; 8 | 9 | namespace Tax.Web.Models.ManageViewModels 10 | { 11 | public class EnableAuthenticatorViewModel 12 | { 13 | [Required] 14 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 15 | [DataType(DataType.Text)] 16 | [Display(Name = "Verification Code")] 17 | public string Code { get; set; } 18 | 19 | [BindNever] 20 | public string SharedKey { get; set; } 21 | 22 | [BindNever] 23 | public string AuthenticatorUri { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/ExternalLoginsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Identity; 7 | 8 | namespace Tax.Web.Models.ManageViewModels 9 | { 10 | public class ExternalLoginsViewModel 11 | { 12 | public IList CurrentLogins { get; set; } 13 | 14 | public IList OtherLogins { get; set; } 15 | 16 | public bool ShowRemoveButton { get; set; } 17 | 18 | public string StatusMessage { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/IndexViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class IndexViewModel 10 | { 11 | public string Username { get; set; } 12 | 13 | public bool IsEmailConfirmed { get; set; } 14 | 15 | [Required] 16 | [EmailAddress] 17 | public string Email { get; set; } 18 | 19 | [Phone] 20 | [Display(Name = "Phone number")] 21 | public string PhoneNumber { get; set; } 22 | 23 | public string StatusMessage { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/RemoveLoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class RemoveLoginViewModel 10 | { 11 | public string LoginProvider { get; set; } 12 | public string ProviderKey { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/SetPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class SetPasswordViewModel 10 | { 11 | [Required] 12 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 13 | [DataType(DataType.Password)] 14 | [Display(Name = "New password")] 15 | public string NewPassword { get; set; } 16 | 17 | [DataType(DataType.Password)] 18 | [Display(Name = "Confirm new password")] 19 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 20 | public string ConfirmPassword { get; set; } 21 | 22 | public string StatusMessage { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/ShowRecoveryCodesViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class ShowRecoveryCodesViewModel 10 | { 11 | public string[] RecoveryCodes { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/ManageViewModels/TwoFactorAuthenticationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Tax.Web.Models.ManageViewModels 8 | { 9 | public class TwoFactorAuthenticationViewModel 10 | { 11 | public bool HasAuthenticator { get; set; } 12 | 13 | public int RecoveryCodesLeft { get; set; } 14 | 15 | public bool Is2faEnabled { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Models/TaxViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Tax.Web.Models 4 | { 5 | public class TaxViewModel 6 | { 7 | [Required] 8 | [Display(Name = "Tax Year")] 9 | [Range(2000, 2020)] 10 | public int? Year { get; set; } 11 | 12 | [Required] 13 | [Display(Name = "Total Income")] 14 | public decimal? TotalIncome { get; set; } 15 | 16 | [Required] 17 | [Display(Name = "Charity Paid Amount")] 18 | public decimal? CharityPaidAmount { get; set; } 19 | 20 | [Required] 21 | [Display(Name = "Number Of Children")] 22 | public short? NumberOfChildren { get; set; } 23 | 24 | [Display(Name = "Tax Due Amount (What you should pay!)")] 25 | public decimal? TaxDueAmount { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Tax.Web 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | BuildWebHost(args).Run(); 18 | } 19 | 20 | public static IWebHost BuildWebHost(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup() 23 | .Build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54839/", 7 | "sslPort": 44315 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "https://localhost:44325/", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Tax.Web": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "applicationUrl": "http://localhost:54860/" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Tax.Core; 8 | using Tax.Data; 9 | using Tax.Repository; 10 | 11 | namespace Tax.Web 12 | { 13 | public class Startup 14 | { 15 | public IConfiguration Configuration { get; } 16 | 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public virtual void SetupDatabase(IServiceCollection services) 23 | { 24 | var connectionString = Configuration.GetConnectionString("DefaultConnection"); 25 | 26 | var options = new DbContextOptionsBuilder(); 27 | options.UseSqlServer(connectionString); 28 | using (var context = new TaxDbContext(options.Options)) 29 | { 30 | context.Database.Migrate(); 31 | } 32 | services.AddDbContext(o => o.UseSqlServer(connectionString)); 33 | } 34 | 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | SetupDatabase(services); 38 | 39 | services.AddIdentity() 40 | .AddEntityFrameworkStores() 41 | .AddDefaultTokenProviders(); 42 | 43 | services.AddMvc(); 44 | 45 | services.AddTransient(); 46 | services.AddTransient(); 47 | services.AddTransient(); 48 | } 49 | 50 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 51 | { 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | app.UseBrowserLink(); 56 | app.UseDatabaseErrorPage(); 57 | } 58 | else 59 | { 60 | app.UseExceptionHandler("/Error"); 61 | } 62 | 63 | app.UseStaticFiles(); 64 | app.UseAuthentication(); 65 | app.UseMvc(routes => 66 | { 67 | routes.MapRoute( 68 | name: "default", 69 | template: "{controller=Home}/{action=Index}/{id?}"); 70 | }); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Tax.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | aspnet-Tax.Web-99FB4240-A1AE-46B0-8484-DD1B38EDE918 4 | False 5 | 6 | 7 | netcoreapp2.0 8 | 9 | 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Access denied"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

You do not have access to this resource.

8 |
9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Confirm email"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |
7 |

8 | Thank you for confirming your email. 9 |

10 |
11 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ExternalLogin.cshtml: -------------------------------------------------------------------------------- 1 | @model ExternalLoginViewModel 2 | @{ 3 | ViewData["Title"] = "Register"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Associate your @ViewData["LoginProvider"] account.

8 |
9 | 10 |

11 | You've successfully authenticated with @ViewData["LoginProvider"]. 12 | Please enter an email address for this site below and click the Register button to finish 13 | logging in. 14 |

15 | 16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 | @section Scripts { 31 | @await Html.PartialAsync("_ValidationScriptsPartial") 32 | } 33 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ForgotPasswordViewModel 2 | @{ 3 | ViewData["Title"] = "Forgot your password?"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Enter your email.

8 |
9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 | 23 | @section Scripts { 24 | @await Html.PartialAsync("_ValidationScriptsPartial") 25 | } 26 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ForgotPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Forgot password confirmation"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |

7 | Please check your email to reset your password. 8 |

9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Locked out"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

This account has been locked out, please try again later.

8 |
9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Collections.Generic 2 | @using System.Linq 3 | @using Microsoft.AspNetCore.Http 4 | @using Microsoft.AspNetCore.Http.Authentication 5 | @model LoginViewModel 6 | @inject SignInManager SignInManager 7 | 8 | @{ 9 | ViewData["Title"] = "Log in"; 10 | } 11 | 12 |

@ViewData["Title"]

13 |
14 |
15 |
16 |
17 |

Use a local account to log in.

18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 |
31 |
32 | 36 |
37 |
38 |
39 | 40 |
41 |
42 |

43 | Forgot your password? 44 |

45 |

46 | Register as a new user? 47 |

48 |
49 |
50 |
51 |
52 |
53 |
54 |

Use another service to log in.

55 |
56 | @{ 57 | var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList(); 58 | if (loginProviders.Count == 0) 59 | { 60 |
61 |

62 | There are no external authentication services configured. See this article 63 | for details on setting up this ASP.NET application to support logging in via external services. 64 |

65 |
66 | } 67 | else 68 | { 69 |
70 |
71 |

72 | @foreach (var provider in loginProviders) 73 | { 74 | 75 | } 76 |

77 |
78 |
79 | } 80 | } 81 |
82 |
83 |
84 | 85 | @section Scripts { 86 | @await Html.PartialAsync("_ValidationScriptsPartial") 87 | } 88 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/LoginWith2fa.cshtml: -------------------------------------------------------------------------------- 1 | @model LoginWith2faViewModel 2 | @{ 3 | ViewData["Title"] = "Two-factor authentication"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |
8 |

Your login is protected with an authenticator app. Enter your authenticator code below.

9 |
10 |
11 |
12 | 13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 |
21 | 25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 |

34 | Don't have access to your authenticator device? You can 35 | log in with a recovery code. 36 |

37 | 38 | @section Scripts { 39 | @await Html.PartialAsync("_ValidationScriptsPartial") 40 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/LoginWithRecoveryCode.cshtml: -------------------------------------------------------------------------------- 1 | @model LoginWithRecoveryCodeViewModel 2 | @{ 3 | ViewData["Title"] = "Recovery code verification"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |
8 |

9 | You have requested to login with a recovery code. This login will not be remembered until you provide 10 | an authenticator app code at login or disable 2FA and login again. 11 |

12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | @section Scripts { 27 | @await Html.PartialAsync("_ValidationScriptsPartial") 28 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model RegisterViewModel 2 | @{ 3 | ViewData["Title"] = "Register"; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 |
9 |
10 |
11 |

Create a new account.

12 |
13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | @section Scripts { 35 | @await Html.PartialAsync("_ValidationScriptsPartial") 36 | } 37 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ResetPasswordViewModel 2 | @{ 3 | ViewData["Title"] = "Reset password"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Reset your password.

8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | @section Scripts { 35 | @await Html.PartialAsync("_ValidationScriptsPartial") 36 | } 37 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/ResetPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Reset password confirmation"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |

7 | Your password has been reset. Please click here to log in. 8 |

9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Account/SignedOut.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Signed out"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |

7 | You have successfully signed out. 8 |

9 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 | 67 | 68 | 107 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/ChangePassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ChangePasswordViewModel 2 | @{ 3 | ViewData["Title"] = "Change password"; 4 | ViewData.AddActivePage(ManageNavPages.ChangePassword); 5 | } 6 | 7 |

@ViewData["Title"]

8 | @Html.Partial("_StatusMessage", Model.StatusMessage) 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 | @section Scripts { 34 | @await Html.PartialAsync("_ValidationScriptsPartial") 35 | } 36 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/Disable2fa.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Disable two-factor authentication (2FA)"; 3 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | 19 | 20 |
21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/EnableAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @model EnableAuthenticatorViewModel 2 | @{ 3 | ViewData["Title"] = "Enable authenticator"; 4 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

To use an authenticator app go through the following steps:

10 |
    11 |
  1. 12 |

    13 | Download a two-factor authenticator app like Microsoft Authenticator for 14 | Windows Phone, 15 | Android and 16 | iOS or 17 | Google Authenticator for 18 | Android and 19 | iOS. 20 |

    21 |
  2. 22 |
  3. 23 |

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    24 |
    To enable QR code generation please read our documentation.
    25 |
    26 |
    27 |
  4. 28 |
  5. 29 |

    30 | Once you have scanned the QR code or input the key above, your two factor authentication app will provide you 31 | with a unique code. Enter the code in the confirmation box below. 32 |

    33 |
    34 |
    35 |
    36 |
    37 | 38 | 39 | 40 |
    41 | 42 |
    43 |
    44 |
    45 |
    46 |
  6. 47 |
48 |
49 | 50 | @section Scripts { 51 | @await Html.PartialAsync("_ValidationScriptsPartial") 52 | } 53 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/ExternalLogins.cshtml: -------------------------------------------------------------------------------- 1 | @model ExternalLoginsViewModel 2 | @{ 3 | ViewData["Title"] = "Manage your external logins"; 4 | ViewData.AddActivePage(ManageNavPages.ExternalLogins); 5 | } 6 | 7 | @Html.Partial("_StatusMessage", Model.StatusMessage) 8 | @if (Model.CurrentLogins?.Count > 0) 9 | { 10 |

Registered Logins

11 | 12 | 13 | @foreach (var login in Model.CurrentLogins) 14 | { 15 | 16 | 17 | 33 | 34 | } 35 | 36 |
@login.LoginProvider 18 | @if (Model.ShowRemoveButton) 19 | { 20 |
21 |
22 | 23 | 24 | 25 |
26 |
27 | } 28 | else 29 | { 30 | @:   31 | } 32 |
37 | } 38 | @if (Model.OtherLogins?.Count > 0) 39 | { 40 |

Add another service to log in.

41 |
42 |
43 |
44 |

45 | @foreach (var provider in Model.OtherLogins) 46 | { 47 | 48 | } 49 |

50 |
51 |
52 | } 53 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/GenerateRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; 3 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 | 21 | 22 |
23 |
24 | 25 |
26 |
27 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IndexViewModel 2 | @{ 3 | ViewData["Title"] = "Profile"; 4 | ViewData.AddActivePage(ManageNavPages.Index); 5 | } 6 | 7 |

@ViewData["Title"]

8 | @Html.Partial("_StatusMessage", Model.StatusMessage) 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | @if (Model.IsEmailConfirmed) 20 | { 21 |
22 | 23 | 24 |
25 | } 26 | else 27 | { 28 | 29 | 30 | } 31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 | @section Scripts { 44 | @await Html.PartialAsync("_ValidationScriptsPartial") 45 | } 46 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/ManageNavPages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.Rendering; 6 | using Microsoft.AspNetCore.Mvc.ViewFeatures; 7 | 8 | namespace Tax.Web.Views.Manage 9 | { 10 | public static class ManageNavPages 11 | { 12 | public static string ActivePageKey => "ActivePage"; 13 | 14 | public static string Index => "Index"; 15 | 16 | public static string ChangePassword => "ChangePassword"; 17 | 18 | public static string ExternalLogins => "ExternalLogins"; 19 | 20 | public static string TwoFactorAuthentication => "TwoFactorAuthentication"; 21 | 22 | public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); 23 | 24 | public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); 25 | 26 | public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); 27 | 28 | public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); 29 | 30 | public static string PageNavClass(ViewContext viewContext, string page) 31 | { 32 | var activePage = viewContext.ViewData["ActivePage"] as string; 33 | return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; 34 | } 35 | 36 | public static void AddActivePage(this ViewDataDictionary viewData, string activePage) => viewData[ActivePageKey] = activePage; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/ResetAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Reset authenticator key"; 3 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 4 | } 5 | 6 |

@ViewData["Title"]

7 | 17 |
18 |
19 | 20 |
21 |
-------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/SetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model SetPasswordViewModel 2 | @{ 3 | ViewData["Title"] = "Set password"; 4 | ViewData.AddActivePage(ManageNavPages.ChangePassword); 5 | } 6 | 7 |

Set your password

8 | @Html.Partial("_StatusMessage", Model.StatusMessage) 9 |

10 | You do not have a local username/password for this site. Add a local 11 | account so you can log in without an external login. 12 |

13 |
14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | @section Scripts { 33 | @await Html.PartialAsync("_ValidationScriptsPartial") 34 | } 35 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/ShowRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @model ShowRecoveryCodesViewModel 2 | @{ 3 | ViewData["Title"] = "Recovery codes"; 4 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 5 | } 6 | 7 |

@ViewData["Title"]

8 | 17 |
18 |
19 | @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) 20 | { 21 | @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
22 | } 23 |
24 |
-------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/TwoFactorAuthentication.cshtml: -------------------------------------------------------------------------------- 1 | @model TwoFactorAuthenticationViewModel 2 | @{ 3 | ViewData["Title"] = "Two-factor authentication"; 4 | ViewData.AddActivePage(ManageNavPages.TwoFactorAuthentication); 5 | } 6 | 7 |

@ViewData["Title"]

8 | @if (Model.Is2faEnabled) 9 | { 10 | if (Model.RecoveryCodesLeft == 0) 11 | { 12 |
13 | You have no recovery codes left. 14 |

You must generate a new set of recovery codes before you can log in with a recovery code.

15 |
16 | } 17 | else if (Model.RecoveryCodesLeft == 1) 18 | { 19 |
20 | You have 1 recovery code left. 21 |

You can generate a new set of recovery codes.

22 |
23 | } 24 | else if (Model.RecoveryCodesLeft <= 3) 25 | { 26 |
27 | You have @Model.RecoveryCodesLeft recovery codes left. 28 |

You should generate a new set of recovery codes.

29 |
30 | } 31 | 32 | Disable 2FA 33 | Reset recovery codes 34 | } 35 | 36 |
Authenticator app
37 | @if (!Model.HasAuthenticator) 38 | { 39 | Add authenticator app 40 | } 41 | else 42 | { 43 | Configure authenticator app 44 | Reset authenticator key 45 | } 46 | 47 | @section Scripts { 48 | @await Html.PartialAsync("_ValidationScriptsPartial") 49 | } 50 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Views/Shared/_Layout.cshtml"; 3 | } 4 | 5 |

Manage your account

6 | 7 |
8 |

Change your account settings

9 |
10 |
11 |
12 | @await Html.PartialAsync("_ManageNav") 13 |
14 |
15 | @RenderBody() 16 |
17 |
18 |
19 | 20 | @section Scripts { 21 | @RenderSection("Scripts", required: false) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/_ManageNav.cshtml: -------------------------------------------------------------------------------- 1 | @using Tax.Web.Views.Manage 2 | @inject SignInManager SignInManager 3 | @{ 4 | var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); 5 | } 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!String.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } 11 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Manage/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Tax.Web.Views.Manage -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 22 |

23 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - Tax.Web 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 40 |
41 | @RenderBody() 42 |
43 |
44 |

© 2018 - Tax.Web

45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 66 | 67 | 68 | 69 | @RenderSection("Scripts", required: false) 70 | 71 | 72 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using Tax.Web.Models 3 | 4 | @inject SignInManager SignInManager 5 | @inject UserManager UserManager 6 | 7 | @if (SignInManager.IsSignedIn(User)) 8 | { 9 | 19 | } 20 | else 21 | { 22 | 26 | } 27 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Tax/AddTax.cshtml: -------------------------------------------------------------------------------- 1 | @model TaxViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Add Tax"; 5 | } 6 | 7 |
8 |
9 |
10 |

Submit Tax

11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 | @section Scripts { 39 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 40 | } 41 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Tax/GetTax.cshtml: -------------------------------------------------------------------------------- 1 | @model TaxViewModel 2 | @{ 3 | ViewData["Title"] = "Tax Per Year"; 4 | } 5 |

Your tax info for year @Model.Year

6 |
7 |
8 |
9 | 10 | @Model.Year 11 |
12 |
13 | 14 | @Model.TotalIncome.Value.ToString("G29") 15 |
16 |
17 | 18 | @Model.CharityPaidAmount.Value.ToString("G29") 19 |
20 |
21 | 22 | @Model.NumberOfChildren 23 |
24 |
25 | 26 | @Model.TaxDueAmount.Value.ToString("G29") 27 |
28 |
29 |
30 | @section Scripts { 31 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 32 | } 33 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/Tax/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewData["Title"] = "Taxes"; 4 | } 5 |

List Taxes

6 | Create new Entry 7 |
8 |
9 |
10 | @foreach (var item in Model) 11 | { 12 |
13 |
14 |
15 | 16 | 17 | @item.Year 18 | 19 | 20 | @item.TaxDueAmount 21 |
22 |
23 |
24 | } 25 | @section Scripts { 26 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 27 | } 28 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using Tax.Web 3 | @using Tax.Web.Models 4 | @using Tax.Web.Models.AccountViewModels 5 | @using Tax.Web.Models.ManageViewModels 6 | @using Tax.Data 7 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 8 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TaxDb;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "LogLevel": { 8 | "Default": "Warning" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/images/banner1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbraheemOsama/WebAutomationTestingCourse/55a88c1befd3ef85ec6e8ea7210c36707d8c8510/Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /Workspace/Tax/Tax.Web/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Workspace/Tax/Tax.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2006 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Data", "Tax.Data\Tax.Data.csproj", "{421D969D-2FA8-4FFF-B785-BF431B6A990F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Repository", "Tax.Repository\Tax.Repository.csproj", "{9F006905-5C87-4D67-AC17-94ED3872C08F}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Core", "Tax.Core\Tax.Core.csproj", "{D8277527-C9FA-4EB0-B80D-14F157FEDD49}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{A67C2253-3438-46AB-AB15-7A818E439DCB}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Tests.FunctionalTests", "Tax.Tests.FunctionalTests\Tax.Tests.FunctionalTests.csproj", "{8497E66B-301A-42A5-948B-29A05E2EEC54}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Tests.Common", "Tax.Tests.Common\Tax.Tests.Common.csproj", "{2A0BA80A-D7C1-475A-93BB-CC2C60F62755}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Tests.UnitTests", "Tax.Tests.UnitTests\Tax.Tests.UnitTests.csproj", "{3B9A6237-E3D2-4AA6-B13D-978321E63D03}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Web", "Tax.Web\Tax.Web.csproj", "{A83E4AA1-AECA-4071-9EED-71EC09962930}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tax.Tests.IntegrationTests", "Tax.Tests.IntegrationTests\Tax.Tests.IntegrationTests.csproj", "{DB47A722-A205-4E44-910F-B7FB91F2C0B8}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tax.Tests.AcceptanceTests", "Tax.Tests.AcceptanceTests\Tax.Tests.AcceptanceTests.csproj", "{7801E043-FEFE-4D4D-A01E-C432A845DD89}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {421D969D-2FA8-4FFF-B785-BF431B6A990F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {421D969D-2FA8-4FFF-B785-BF431B6A990F}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {421D969D-2FA8-4FFF-B785-BF431B6A990F}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {421D969D-2FA8-4FFF-B785-BF431B6A990F}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {9F006905-5C87-4D67-AC17-94ED3872C08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {9F006905-5C87-4D67-AC17-94ED3872C08F}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {9F006905-5C87-4D67-AC17-94ED3872C08F}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {9F006905-5C87-4D67-AC17-94ED3872C08F}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {D8277527-C9FA-4EB0-B80D-14F157FEDD49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {D8277527-C9FA-4EB0-B80D-14F157FEDD49}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {D8277527-C9FA-4EB0-B80D-14F157FEDD49}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {D8277527-C9FA-4EB0-B80D-14F157FEDD49}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {8497E66B-301A-42A5-948B-29A05E2EEC54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {8497E66B-301A-42A5-948B-29A05E2EEC54}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {8497E66B-301A-42A5-948B-29A05E2EEC54}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {8497E66B-301A-42A5-948B-29A05E2EEC54}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {2A0BA80A-D7C1-475A-93BB-CC2C60F62755}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {2A0BA80A-D7C1-475A-93BB-CC2C60F62755}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {2A0BA80A-D7C1-475A-93BB-CC2C60F62755}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {2A0BA80A-D7C1-475A-93BB-CC2C60F62755}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {3B9A6237-E3D2-4AA6-B13D-978321E63D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {3B9A6237-E3D2-4AA6-B13D-978321E63D03}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {3B9A6237-E3D2-4AA6-B13D-978321E63D03}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {3B9A6237-E3D2-4AA6-B13D-978321E63D03}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {A83E4AA1-AECA-4071-9EED-71EC09962930}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {A83E4AA1-AECA-4071-9EED-71EC09962930}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {A83E4AA1-AECA-4071-9EED-71EC09962930}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {A83E4AA1-AECA-4071-9EED-71EC09962930}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {DB47A722-A205-4E44-910F-B7FB91F2C0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {DB47A722-A205-4E44-910F-B7FB91F2C0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {DB47A722-A205-4E44-910F-B7FB91F2C0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {DB47A722-A205-4E44-910F-B7FB91F2C0B8}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {7801E043-FEFE-4D4D-A01E-C432A845DD89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {7801E043-FEFE-4D4D-A01E-C432A845DD89}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {7801E043-FEFE-4D4D-A01E-C432A845DD89}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {7801E043-FEFE-4D4D-A01E-C432A845DD89}.Release|Any CPU.Build.0 = Release|Any CPU 68 | EndGlobalSection 69 | GlobalSection(SolutionProperties) = preSolution 70 | HideSolutionNode = FALSE 71 | EndGlobalSection 72 | GlobalSection(NestedProjects) = preSolution 73 | {8497E66B-301A-42A5-948B-29A05E2EEC54} = {A67C2253-3438-46AB-AB15-7A818E439DCB} 74 | {2A0BA80A-D7C1-475A-93BB-CC2C60F62755} = {A67C2253-3438-46AB-AB15-7A818E439DCB} 75 | {3B9A6237-E3D2-4AA6-B13D-978321E63D03} = {A67C2253-3438-46AB-AB15-7A818E439DCB} 76 | {DB47A722-A205-4E44-910F-B7FB91F2C0B8} = {A67C2253-3438-46AB-AB15-7A818E439DCB} 77 | {7801E043-FEFE-4D4D-A01E-C432A845DD89} = {A67C2253-3438-46AB-AB15-7A818E439DCB} 78 | EndGlobalSection 79 | GlobalSection(ExtensibilityGlobals) = postSolution 80 | SolutionGuid = {DEBEC023-E3A7-476B-B6C5-D25FE085F486} 81 | EndGlobalSection 82 | EndGlobal 83 | --------------------------------------------------------------------------------