diff --git a/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala b/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala index fe0d4859c..01e65aa3b 100644 --- a/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala +++ b/app/controllers/taxCalculation/TaxCalculationBeforeYouStartController.scala @@ -17,24 +17,63 @@ package controllers.taxCalculation import controllers.actions.* +import controllers.routes.* +import models.NormalMode +import models.prelimQuestions.TransactionType +import models.prelimQuestions.TransactionType.GrantOfLease +import models.taxCalculation.{MissingFullReturnError, TaxCalculationResult} +import navigation.Navigator +import pages.preliminary.TransactionTypePage +import pages.taxCalculation.{IsLeaseholdAndSelfAssessedPage, TaxCalculationBeforeYouStartPage} import play.api.i18n.{I18nSupport, MessagesApi} import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import repositories.SessionRepository +import services.taxCalculation.SdltCalculationService +import uk.gov.hmrc.http.HeaderCarrier import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import uk.gov.hmrc.play.http.HeaderCarrierConverter +import utils.TaxCalculationHelper.* import views.html.taxCalculation.TaxCalculationBeforeYouStartView import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} class TaxCalculationBeforeYouStartController @Inject()( - override val messagesApi: MessagesApi, - identify: IdentifierAction, - getData: DataRetrievalAction, - requireData: DataRequiredAction, - val controllerComponents: MessagesControllerComponents, - view: TaxCalculationBeforeYouStartView - ) extends FrontendBaseController with I18nSupport { - - def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData) { + override val messagesApi: MessagesApi, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + sdltCalculationService: SdltCalculationService, + sessionRepository: SessionRepository, + navigator: Navigator, + val controllerComponents: MessagesControllerComponents, + view: TaxCalculationBeforeYouStartView + )(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport { + + def onPageLoad: Action[AnyContent] = (identify andThen getData andThen requireData).async { + implicit request => + + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) + + sdltCalculationService.calculateStampDutyLandTax(request.userAnswers).flatMap { + case Right(result) => + + val isLeasehold: Boolean = request.userAnswers.get(TransactionTypePage).contains(GrantOfLease) + + val leaseholdSelfAssessedFlag = isLeasehold && isSelfAssessedResponse(result) + + for { + updatedAnswers <- Future.fromTry(request.userAnswers.set(IsLeaseholdAndSelfAssessedPage, leaseholdSelfAssessedFlag)) + _ <- sessionRepository.set(updatedAnswers) + } yield Ok(view(leaseholdSelfAssessedFlag)) + + case Left(MissingFullReturnError) => Future.successful(Redirect(NoReturnReferenceController.onPageLoad())) + case Left(_) => Future.successful(Redirect(ReturnTaskListController.onPageLoad())) + } + } + + def onSubmit: Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request => - Ok(view()) + Redirect(navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, request.userAnswers)) } } diff --git a/app/navigation/Navigator.scala b/app/navigation/Navigator.scala index 456622f72..cf212fabd 100644 --- a/app/navigation/Navigator.scala +++ b/app/navigation/Navigator.scala @@ -26,6 +26,7 @@ import pages.vendor.* import pages.vendorAgent.* import pages.land.* import pages.transaction.* +import pages.taxCalculation.* import pages.ukResidency.* import play.api.mvc.Call @@ -65,6 +66,7 @@ class Navigator @Inject()() { case landPage if isLandSection(landPage) => landRoutes(landPage) case residencyPage if isResidencySection(residencyPage) => residencyRoutes(residencyPage) case transactionPage if isTransactionSection(transactionPage) => transactionRoutes(transactionPage) + case taxCalcPage if isTaxCalculationSection(taxCalcPage) => taxCalculationRoutes(taxCalcPage) case _ => _ => routes.IndexController.onPageLoad() } @@ -254,6 +256,30 @@ class Navigator @Inject()() { case _ => _ => routes.IndexController.onPageLoad() } + private def isTaxCalculationSection(page: Page): Boolean = page match { + case TaxCalculationBeforeYouStartPage | CalculatedSdltPage | CalculatedSdltBreakdownPage + | SelfAssessmentAmountPage | PremiumPayableTaxPage | NpvTaxPage + | TotalAmountDuePage | PenaltiesAndInterestPage | TaxCalculationCheckYourAnswersPage => true + case _ => false + } + + private def taxCalculationRoutes(page: Page): UserAnswers => Call = page match { + + case TaxCalculationBeforeYouStartPage => answers => + if (answers.get(IsLeaseholdAndSelfAssessedPage).contains(true)) routes.IndexController.onPageLoad() // TODO: replace -> PremiumPayableTaxController + else routes.IndexController.onPageLoad() // TODO: replace -> CalculatedSdltController + case CalculatedSdltPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> SelfAssessmentAmountController + case CalculatedSdltBreakdownPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> CalculatedSdltController + case SelfAssessmentAmountPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TotalAmountDueController + case PremiumPayableTaxPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> NpvTaxController + case NpvTaxPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TotalAmountDueController + case TotalAmountDuePage => _ => routes.IndexController.onPageLoad() // TODO: replace -> PenaltiesAndInterestController + case PenaltiesAndInterestPage => _ => routes.IndexController.onPageLoad() // TODO: replace -> TaxCalculationCheckYourAnswersController + case TaxCalculationCheckYourAnswersPage => _ => routes.ReturnTaskListController.onPageLoad() + + case _ => _ => routes.IndexController.onPageLoad() + } + private val checkRouteMap: Page => UserAnswers => Call = { case WhoIsMakingThePurchasePage => _ => controllers.purchaser.routes.PurchaserCheckYourAnswersController.onPageLoad() case NameOfPurchaserPage => _ => controllers.purchaser.routes.PurchaserCheckYourAnswersController.onPageLoad() @@ -315,6 +341,13 @@ class Navigator @Inject()() { case LandMineralsOrMineralRightsPage => _ => controllers.land.routes.LandCheckYourAnswersController.onPageLoad() case LandSelectMeasurementUnitPage => _ => controllers.land.routes.AreaOfLandController.onPageLoad(CheckMode) + // TODO: replace -> TaxCalculationCheckYourAnswersController + case SelfAssessmentAmountPage => _ => routes.IndexController.onPageLoad() + case PremiumPayableTaxPage => _ => routes.IndexController.onPageLoad() + case NpvTaxPage => _ => routes.IndexController.onPageLoad() + case TotalAmountDuePage => _ => routes.IndexController.onPageLoad() + case PenaltiesAndInterestPage => _ => routes.IndexController.onPageLoad() + case _ => _ => controllers.routes.ReturnTaskListController.onPageLoad() } diff --git a/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala b/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala new file mode 100644 index 000000000..35b36e15a --- /dev/null +++ b/app/pages/taxCalculation/CalculatedSdltBreakdownPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object CalculatedSdltBreakdownPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "calculatedSdltBreakdown" +} diff --git a/app/pages/taxCalculation/CalculatedSdltPage.scala b/app/pages/taxCalculation/CalculatedSdltPage.scala new file mode 100644 index 000000000..5787c274e --- /dev/null +++ b/app/pages/taxCalculation/CalculatedSdltPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object CalculatedSdltPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "calculatedSdlt" +} diff --git a/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala b/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala new file mode 100644 index 000000000..18da49269 --- /dev/null +++ b/app/pages/taxCalculation/IsLeaseholdAndSelfAssessedPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object IsLeaseholdAndSelfAssessedPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "isLeaseholdAndSelfAssessed" +} diff --git a/app/pages/taxCalculation/NpvTaxPage.scala b/app/pages/taxCalculation/NpvTaxPage.scala new file mode 100644 index 000000000..e1c56e9e7 --- /dev/null +++ b/app/pages/taxCalculation/NpvTaxPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object NpvTaxPage extends QuestionPage[BigDecimal] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "npvTax" +} diff --git a/app/pages/taxCalculation/PenaltiesAndInterestPage.scala b/app/pages/taxCalculation/PenaltiesAndInterestPage.scala new file mode 100644 index 000000000..555664447 --- /dev/null +++ b/app/pages/taxCalculation/PenaltiesAndInterestPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object PenaltiesAndInterestPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "penaltiesAndInterest" +} diff --git a/app/pages/taxCalculation/PremiumPayableTaxPage.scala b/app/pages/taxCalculation/PremiumPayableTaxPage.scala new file mode 100644 index 000000000..a6da5b028 --- /dev/null +++ b/app/pages/taxCalculation/PremiumPayableTaxPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object PremiumPayableTaxPage extends QuestionPage[BigDecimal] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "premiumPayableTax" +} diff --git a/app/pages/taxCalculation/SelfAssessmentAmountPage.scala b/app/pages/taxCalculation/SelfAssessmentAmountPage.scala new file mode 100644 index 000000000..367c03249 --- /dev/null +++ b/app/pages/taxCalculation/SelfAssessmentAmountPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object SelfAssessmentAmountPage extends QuestionPage[BigDecimal] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "selfAssessmentAmount" +} diff --git a/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala b/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala new file mode 100644 index 000000000..f800e54e3 --- /dev/null +++ b/app/pages/taxCalculation/TaxCalculationBeforeYouStartPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object TaxCalculationBeforeYouStartPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "taxCalculationBeforeYouStart" +} diff --git a/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala b/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala new file mode 100644 index 000000000..40ba2d71d --- /dev/null +++ b/app/pages/taxCalculation/TaxCalculationCheckYourAnswersPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object TaxCalculationCheckYourAnswersPage extends QuestionPage[Boolean] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "taxCalculationCheckYourAnswers" +} diff --git a/app/pages/taxCalculation/TotalAmountDuePage.scala b/app/pages/taxCalculation/TotalAmountDuePage.scala new file mode 100644 index 000000000..7e6a8107d --- /dev/null +++ b/app/pages/taxCalculation/TotalAmountDuePage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pages.taxCalculation + +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object TotalAmountDuePage extends QuestionPage[BigDecimal] { + + override def path: JsPath = JsPath \ "taxCalculationCurrent" \ toString + + override def toString: String = "totalAmountDue" +} diff --git a/app/services/taxCalculation/SdltCalculationService.scala b/app/services/taxCalculation/SdltCalculationService.scala index aab0b93b4..c5b470e4d 100644 --- a/app/services/taxCalculation/SdltCalculationService.scala +++ b/app/services/taxCalculation/SdltCalculationService.scala @@ -31,7 +31,8 @@ class SdltCalculationService @Inject()( // TODO: DTR-2815: Must Implement Self-Assessed response for Residential before 2012-03-22 - def calculateStampDutyLandTax(userAnswers: UserAnswers)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Either[MissingDataError, TaxCalculationResult]] = + def calculateStampDutyLandTax(userAnswers: UserAnswers) + (implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Either[MissingDataError, TaxCalculationResult]] = TaxCalcRequestValidator.buildRequest(userAnswers) match { case Right(request) => logger.info(s"[SdltCalculationService][calculateStampDutyLandTax] sending calculation request") diff --git a/app/services/taxCalculation/TaxCalcRequestValidator.scala b/app/services/taxCalculation/TaxCalcRequestValidator.scala index 287837c9f..db45bd621 100644 --- a/app/services/taxCalculation/TaxCalcRequestValidator.scala +++ b/app/services/taxCalculation/TaxCalcRequestValidator.scala @@ -55,7 +55,7 @@ object TaxCalcRequestValidator { effectiveDateYear = parsedDate.getYear, nonUKResident = handleNonUkResident(fullReturn.residency, parsedDate, propertyType), premium = premium, - highestRent = BigDecimal(0), + highestRent = fullReturn.lease.flatMap(_.startingRent).flatMap(v => Try(BigDecimal(v)).toOption).getOrElse(BigDecimal(0)), propertyDetails = buildPropertyDetails(propertyCode), leaseDetails = leaseDetails, relevantRentDetails = fullReturn.lease.map(buildRelevantRentDetails), @@ -82,7 +82,7 @@ object TaxCalcRequestValidator { private def handleNonUkResident(residency: Option[Residency], effectiveDate: LocalDate, propertyType: PropertyTypes.Value): Option[String] = if (effectiveDate.isBefore(APR2021_RESIDENTIAL_DATE) && propertyType == PropertyTypes.residential) None - else residency.flatMap(_.isNonUkResidents).map(_.capitalize) + else residency.flatMap(_.isNonUkResidents).map(_.toLowerCase.capitalize) private def buildPropertyDetails(propertyCode: String): Option[PropertyDetails] = propertyCode match { diff --git a/app/utils/TaxCalculationHelper.scala b/app/utils/TaxCalculationHelper.scala new file mode 100644 index 000000000..e20f1b94c --- /dev/null +++ b/app/utils/TaxCalculationHelper.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import models.UserAnswers +import models.prelimQuestions.TransactionType +import models.taxCalculation.TaxCalculationResult +import pages.preliminary.TransactionTypePage + +object TaxCalculationHelper { + + private val SELF_ASSESSED_HINT_TEXT: String = "self-assessed" + + def isSelfAssessedResponse(taxCalculationResult: TaxCalculationResult): Boolean = + taxCalculationResult + .resultHeading + .contains(SELF_ASSESSED_HINT_TEXT) + + def isLeasehold(answers: UserAnswers): Boolean = + answers.get(TransactionTypePage).contains(TransactionType.GrantOfLease) +} diff --git a/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html b/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html index 88e0abb01..44ff9627a 100644 --- a/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html +++ b/app/views/taxCalculation/TaxCalculationBeforeYouStartView.scala.html @@ -14,37 +14,43 @@ * limitations under the License. *@ -@import views.html.components.* - +@import views.html.components._ @this( - layout: templates.Layout, - govukButton: GovukButton, - h1: h1, - p: Paragraph, - caption: Caption + layout: templates.Layout, + formHelper: FormWithCSRF, + govukButton: GovukButton, + h1: h1, + p: Paragraph, + caption: Caption ) -@()(implicit request: Request[_], messages: Messages) +@(isLeaseholdAndSelfAssessed: Boolean)(implicit request: Request[_], messages: Messages) @layout(pageTitle = titleNoForm(messages("taxCalculation.beforeStart.title"))) { - @caption(messages("site.taxCalculation.caption")) - @h1(messages("site.beforeYouStart.heading")) - - @p(messages("taxCalculation.beforeStart.p1")) - @p(messages("taxCalculation.beforeStart.p2")) - - - - @govukButton( - ButtonViewModel(messages("site.continue")) - .asLink("#") - .withCssClass("govuk-!-margin-top-2") - ) + @formHelper(action = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit(), Symbol("autoComplete") -> "off") { + + @caption(messages("site.taxCalculation.caption")) + + @h1(messages("site.beforeYouStart.heading")) + + @p(messages("taxCalculation.beforeStart.p1")) + @p(messages("taxCalculation.beforeStart.p2")) + + + + @govukButton( + ButtonViewModel(messages("site.continue")) + ) + } } diff --git a/conf/app.routes b/conf/app.routes index 46677e213..b3e2b5e88 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -502,9 +502,11 @@ POST /about-the-transaction/claiming-partial-relief GET /about-the-transaction/claiming-partial-relief/change controllers.transaction.ClaimingPartialReliefAmountController.onPageLoad(mode: Mode = CheckMode) POST /about-the-transaction/claiming-partial-relief/change controllers.transaction.ClaimingPartialReliefAmountController.onSubmit(mode: Mode = CheckMode) -GET /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onPageLoad() - GET /about-the-transaction/enter-registered-charity-number controllers.transaction.CharityRegisteredNumberController.onPageLoad(mode: Mode = NormalMode) POST /about-the-transaction/enter-registered-charity-number controllers.transaction.CharityRegisteredNumberController.onSubmit(mode: Mode = NormalMode) GET /about-the-transaction/enter-registered-charity-number/change controllers.transaction.CharityRegisteredNumberController.onPageLoad(mode: Mode = CheckMode) POST /about-the-transaction/enter-registered-charity-number/change controllers.transaction.CharityRegisteredNumberController.onSubmit(mode: Mode = CheckMode) + + +GET /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onPageLoad() +POST /tax-calculation/before-you-start controllers.taxCalculation.TaxCalculationBeforeYouStartController.onSubmit() diff --git a/conf/messages.en b/conf/messages.en index 8cebee408..0130693fa 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -1390,6 +1390,7 @@ transaction.claimingPartialReliefAmount.missing taxCalculation.beforeStart.title = taxCalculation.beforeStart.p1 = In this section, you need to provide information about your tax calculation. taxCalculation.beforeStart.p2 = You will need to know: +taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet = your self-assessed SDLT calculation taxCalculation.beforeStart.bullet1 = the effective date of the transaction taxCalculation.beforeStart.bullet2 = the tax due on the total premium payable taxCalculation.beforeStart.bullet3 = the tax due on the NPV diff --git a/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala b/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala index 582378dab..bcdb04906 100644 --- a/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala +++ b/test/controllers/taxCalculation/TaxCalculationBeforeYouStartControllerSpec.scala @@ -1,5 +1,5 @@ /* - * Copyright 2025 HM Revenue & Customs + * Copyright 2026 HM Revenue & Customs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,27 +17,164 @@ package controllers.taxCalculation import base.SpecBase +import models.prelimQuestions.TransactionType +import models.taxCalculation.{MissingAboutTheTransactionError, MissingFullReturnError, TaxCalculationResult} +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar +import pages.preliminary.TransactionTypePage +import play.api.inject.bind +import play.api.test.CSRFTokenHelper.* import play.api.test.FakeRequest import play.api.test.Helpers.* +import services.taxCalculation.SdltCalculationService import views.html.taxCalculation.TaxCalculationBeforeYouStartView -class TaxCalculationBeforeYouStartControllerSpec extends SpecBase { +import scala.concurrent.Future + +class TaxCalculationBeforeYouStartControllerSpec extends SpecBase with MockitoSugar { + + private val calculatedResult = TaxCalculationResult(totalTax = 5000, resultHeading = None, resultHint = None, npv = None, taxCalcs = Seq.empty) + private val selfAssessedResult = TaxCalculationResult(totalTax = 0, resultHeading = Some("self-assessed"), resultHint = None, npv = None, taxCalcs = Seq.empty) + + private lazy val onPageLoadUrl = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onPageLoad().url "TaxCalculationBeforeYouStart Controller" - { - "must return OK and the correct view for a GET" in { + "must render the view with false when the calculation succeeds and the transaction is not leasehold" in { - val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Right(calculatedResult))) + + val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.ConveyanceTransfer).success.value + val application = applicationBuilder(userAnswers = Some(answers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() running(application) { - val request = FakeRequest(GET, controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onPageLoad().url) + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString + } + } - val result = route(application, request).value + "must render the view with true when the calculation is self-assessed and the transaction is leasehold" in { - val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Right(selfAssessedResult))) + + val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.GrantOfLease).success.value + val application = applicationBuilder(userAnswers = Some(answers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] status(result) mustEqual OK - contentAsString(result) mustEqual view()(request, messages(application)).toString + contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = true)(request, messages(application)).toString + } + } + + "must render the view with false when the calculation is self-assessed but the transaction is not leasehold" in { + + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Right(selfAssessedResult))) + + val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.ConveyanceTransfer).success.value + val application = applicationBuilder(userAnswers = Some(answers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString + } + } + + "must render the view with false when the transaction is leasehold but the calculation is not self-assessed" in { + + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Right(calculatedResult))) + + val answers = emptyUserAnswers.set(TransactionTypePage, TransactionType.GrantOfLease).success.value + val application = applicationBuilder(userAnswers = Some(answers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(isLeaseholdAndSelfAssessed = false)(request, messages(application)).toString + } + } + + "must redirect to NoReturnReferenceController when the service reports no full return" in { + + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Left(MissingFullReturnError))) + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual controllers.routes.NoReturnReferenceController.onPageLoad().url + } + } + + "must redirect to ReturnTaskListController when the service reports any other missing data" in { + + val mockService = mock[SdltCalculationService] + when(mockService.calculateStampDutyLandTax(any())(any(), any())) + .thenReturn(Future.successful(Left(MissingAboutTheTransactionError))) + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides(bind[SdltCalculationService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(GET, onPageLoadUrl).withCSRFToken + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual controllers.routes.ReturnTaskListController.onPageLoad().url + } + } + + "onSubmit must redirect to the next page determined by the navigator" in { + val onSubmitUrl = controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit().url + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = FakeRequest(POST, onSubmitUrl).withCSRFToken + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + // Navigator currently routes both branches to IndexController (placeholders until + // downstream tax-calc controllers exist) — assert we redirect somewhere, not nowhere. + redirectLocation(result) mustBe defined } } } diff --git a/test/navigation/NavigatorSpec.scala b/test/navigation/NavigatorSpec.scala index 09742f072..94a647c3d 100644 --- a/test/navigation/NavigatorSpec.scala +++ b/test/navigation/NavigatorSpec.scala @@ -28,7 +28,7 @@ import pages.transaction.* import pages.ukResidency.{CloseCompanyPage, CrownEmploymentReliefPage, NonUkResidentPurchaserPage} import pages.vendor.* import pages.vendorAgent.* - +import pages.taxCalculation.* class NavigatorSpec extends SpecBase { val navigator = new Navigator @@ -466,6 +466,70 @@ class NavigatorSpec extends SpecBase { navigator.nextPage(AgentNamePage, NormalMode, userAnswers) mustBe controllers.vendorAgent.routes.VendorAgentAddressController.redirectToAddressLookupVendorAgent() } + + "tax calculation routes" - { + + // Placeholders: downstream controllers haven't been built yet, so each page currently redirects to IndexController + + "go from TaxCalculationBeforeYouStartPage to PremiumPayableTax page when leasehold and self-assessed" in { + val answers = UserAnswers("id", storn = "TESTSTORN") + .set(IsLeaseholdAndSelfAssessedPage, true).success.value + navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, answers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.PremiumPayableTaxController.onPageLoad() + } + + "go from TaxCalculationBeforeYouStartPage to CalculatedSdlt page when flag is false" in { + val answers = UserAnswers("id", storn = "TESTSTORN") + .set(IsLeaseholdAndSelfAssessedPage, false).success.value + navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, answers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad() + } + + "go from TaxCalculationBeforeYouStartPage to CalculatedSdlt page when flag is unset" in { + navigator.nextPage(TaxCalculationBeforeYouStartPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad() + } + + "go from CalculatedSdltPage to SelfAssessmentAmount page" in { + navigator.nextPage(CalculatedSdltPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.SelfAssessmentAmountController.onPageLoad() + } + + "go from CalculatedSdltBreakdownPage to CalculatedSdlt page" in { + navigator.nextPage(CalculatedSdltBreakdownPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.CalculatedSdltController.onPageLoad() + } + + "go from SelfAssessmentAmountPage to TotalAmountDue page" in { + navigator.nextPage(SelfAssessmentAmountPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TotalAmountDueController.onPageLoad() + } + + "go from PremiumPayableTaxPage to NpvTax page" in { + navigator.nextPage(PremiumPayableTaxPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.NpvTaxController.onPageLoad() + } + + "go from NpvTaxPage to TotalAmountDue page" in { + navigator.nextPage(NpvTaxPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TotalAmountDueController.onPageLoad() + } + + "go from TotalAmountDuePage to PenaltiesAndInterest page" in { + navigator.nextPage(TotalAmountDuePage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.PenaltiesAndInterestController.onPageLoad() + } + + "go from PenaltiesAndInterestPage to TaxCalculationCheckYourAnswers page" in { + navigator.nextPage(PenaltiesAndInterestPage, NormalMode, userAnswers) mustBe + routes.IndexController.onPageLoad() // TODO: replace with controllers.taxCalculation.routes.TaxCalculationCheckYourAnswersController.onPageLoad() + } + + "go from TaxCalculationCheckYourAnswersPage to return task list" in { + navigator.nextPage(TaxCalculationCheckYourAnswersPage, NormalMode, userAnswers) mustBe + routes.ReturnTaskListController.onPageLoad() + } + } } } } \ No newline at end of file diff --git a/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala b/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala index 91e304130..f7c4eff3a 100644 --- a/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala +++ b/test/services/taxCalculation/TaxCalcRequestValidatorSpec.scala @@ -51,7 +51,8 @@ class TaxCalcRequestValidatorSpec extends SpecBase { effectiveDate: String = "2025-06-15", consideration: BigDecimal = 250000, npv: String = "100000", - annualRentOver1000: Option[String] = Some("yes") + annualRentOver1000: Option[String] = Some("yes"), + startingRent: Option[String] = None ): FullReturn = FullReturn( stornId = "STORN", returnResourceRef = "REF", land = Some(Seq(Land(propertyType = Some("01"), interestCreatedTransferred = Some("LG")))), @@ -62,7 +63,8 @@ class TaxCalcRequestValidatorSpec extends SpecBase { residency = Some(Residency(isNonUkResidents = Some("no"))), lease = Some(Lease( contractStartDate = Some(startDate), contractEndDate = Some(endDate), - isAnnualRentOver1000 = annualRentOver1000, netPresentValue = Some(npv) + isAnnualRentOver1000 = annualRentOver1000, netPresentValue = Some(npv), + startingRent = startingRent )) ) @@ -294,6 +296,13 @@ class TaxCalcRequestValidatorSpec extends SpecBase { } } + "highest rent" - { + + "must be parsed from startingRent for leasehold" in { + TaxCalcRequestValidator.buildRequest(userAnswersWith(leaseholdReturn(startingRent = Some("1500")))).toOption.get.highestRent mustBe BigDecimal(1500) + } + } + "relevant rent details" - { "must set relevantRent to 1000 when annual rent is over 1000" in { diff --git a/test/utils/TaxCalculationHelperSpec.scala b/test/utils/TaxCalculationHelperSpec.scala new file mode 100644 index 000000000..bb7de7db8 --- /dev/null +++ b/test/utils/TaxCalculationHelperSpec.scala @@ -0,0 +1,54 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import models.UserAnswers +import models.prelimQuestions.TransactionType +import models.taxCalculation.TaxCalculationResult +import org.scalatest.TryValues.convertTryToSuccessOrFailure +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers +import pages.preliminary.TransactionTypePage + +class TaxCalculationHelperSpec extends AnyFreeSpec with Matchers { + + private def result(heading: Option[String]): TaxCalculationResult = + TaxCalculationResult(totalTax = 0, resultHeading = heading, resultHint = None, npv = None, taxCalcs = Seq.empty) + + "isSelfAssessedResponse" - { + + "must return true when resultHeading equals 'self-assessed'" in { + TaxCalculationHelper.isSelfAssessedResponse(result(Some("self-assessed"))) mustBe true + } + + "must return false when resultHeading is a different string" in { + TaxCalculationHelper.isSelfAssessedResponse(result(Some("calculated"))) mustBe false + } + + "must return false when resultHeading is None" in { + TaxCalculationHelper.isSelfAssessedResponse(result(None)) mustBe false + } + + "must return false when resultHeading merely contains 'self-assessed' as a substring" in { + TaxCalculationHelper.isSelfAssessedResponse(result(Some("self-assessed result"))) mustBe false + } + + "must return false when the case does not match (different capitalisation)" in { + TaxCalculationHelper.isSelfAssessedResponse(result(Some("Self-Assessed"))) mustBe false + } + } +} diff --git a/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala b/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala new file mode 100644 index 000000000..379583881 --- /dev/null +++ b/test/views/taxCalculation/TaxCalculationBeforeYouStartViewSpec.scala @@ -0,0 +1,142 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package views.taxCalculation + +import base.SpecBase +import org.jsoup.Jsoup +import play.api.i18n.Messages +import play.api.mvc.AnyContentAsEmpty +import play.api.test.CSRFTokenHelper.* +import play.api.test.FakeRequest +import play.api.test.Helpers.* +import views.html.taxCalculation.TaxCalculationBeforeYouStartView + +class TaxCalculationBeforeYouStartViewSpec extends SpecBase { + + "TaxCalculationBeforeYouStartView" - { + + "must render the page title" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString()) + + doc.select("title").first().text() must include(messagesInstance("taxCalculation.beforeStart.title")) + } + } + + "must render the heading and caption" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString()) + + doc.select("h1").first().text() mustBe messagesInstance("site.beforeYouStart.heading") + doc.text() must include(messagesInstance("site.taxCalculation.caption")) + } + } + + "must render the two introductory paragraphs" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString()) + + doc.text() must include(messagesInstance("taxCalculation.beforeStart.p1")) + doc.text() must include(messagesInstance("taxCalculation.beforeStart.p2")) + } + } + + "when isLeaseholdAndSelfAssessed is false" - { + + "must render the standard bullet list (bullet1, bullet2, bullet3, bullet4, bullet5) and omit the leaseholdAndSelfAssessed bullets" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString()) + val bullets = doc.select("ul.govuk-list--bullet li").eachText() + + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet1")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet2")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet3")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet4")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet5")) + + bullets must not contain messagesInstance("taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet") + } + } + } + + "when isLeaseholdAndSelfAssessed is true" - { + + "must swap bullet2 and bullet3 for the leaseholdAndSelfAssessed bullet" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = true).toString()) + val bullets = doc.select("ul.govuk-list--bullet li").eachText() + + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet1")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.leaseholdAndSelfAssessed.bullet")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet4")) + bullets must contain(messagesInstance("taxCalculation.beforeStart.bullet5")) + + bullets must not contain messagesInstance("taxCalculation.beforeStart.bullet2") + } + } + } + + "must render the continue button inside a form posting to onSubmit" in { + val application = applicationBuilder().build() + + running(application) { + implicit val messagesInstance: Messages = messages(application) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]] + val view = application.injector.instanceOf[TaxCalculationBeforeYouStartView] + + val doc = Jsoup.parse(view(isLeaseholdAndSelfAssessed = false).toString()) + + val form = doc.select("form").first() + form.attr("method").toLowerCase mustBe "post" + form.attr("action") mustBe controllers.taxCalculation.routes.TaxCalculationBeforeYouStartController.onSubmit().url + + val button = doc.select("button[type=submit]").first() + button.text() mustBe messagesInstance("site.continue") + } + } + } +}