From 9a202ea7c6285d5d331470b9d9e0dcb363bfe3d9 Mon Sep 17 00:00:00 2001 From: Pete Robinson <77154273+peterdrobinson@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:18:36 +0100 Subject: [PATCH 1/4] NGR-2538 - Added How Much Was The Lump Sum page --- .../HowMuchWasTheLumpSumController.scala | 76 ++++++++++++ .../forms/HowMuchWasTheLumpSumForm.scala | 59 ++++++++++ .../navigation/Navigator.scala | 1 + .../pages/HowMuchWasTheLumpSumPage.scala | 27 +++++ .../views/HowMuchWasTheLumpSumView.scala.html | 55 +++++++++ conf/app.routes | 8 +- conf/messages | 8 +- .../HowMuchWasTheLumpSumControllerSpec.scala | 111 ++++++++++++++++++ .../forms/HowMuchWasTheLumpSumFormSpec.scala | 108 +++++++++++++++++ .../views/HowMuchWasTheLumpSumViewSpec.scala | 71 +++++++++++ 10 files changed, 522 insertions(+), 2 deletions(-) create mode 100644 app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumController.scala create mode 100644 app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumForm.scala create mode 100644 app/uk/gov/hmrc/ngrraldfrontend/pages/HowMuchWasTheLumpSumPage.scala create mode 100644 app/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumView.scala.html create mode 100644 test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala create mode 100644 test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala create mode 100644 test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumController.scala new file mode 100644 index 0000000..08b7018 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumController.scala @@ -0,0 +1,76 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.controllers + +import play.api.i18n.I18nSupport +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, DataRetrievalAction} +import uk.gov.hmrc.ngrraldfrontend.config.AppConfig +import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm +import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm.form +import uk.gov.hmrc.ngrraldfrontend.models.{Mode, UserAnswers} +import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator +import uk.gov.hmrc.ngrraldfrontend.pages.HowMuchWasTheLumpSumPage +import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository +import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchWasTheLumpSumView +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController + +import javax.inject.{Inject, Singleton} +import scala.concurrent.{ExecutionContext, Future} + +@Singleton +class HowMuchWasTheLumpSumController @Inject()(howMuchWasTheLumpSumView: HowMuchWasTheLumpSumView, + authenticate: AuthRetrievals, + getData: DataRetrievalAction, + sessionRepository: SessionRepository, + navigator: Navigator, + mcc: MessagesControllerComponents)(implicit appConfig: AppConfig, ec: ExecutionContext) + extends FrontendController(mcc) with I18nSupport { + + + def show(mode: Mode): Action[AnyContent] = { + (authenticate andThen getData).async { implicit request => + val preparedForm = request.userAnswers.getOrElse(UserAnswers(request.credId)).get(HowMuchWasTheLumpSumPage) match { + case None => form + case Some(value) => form.fill(HowMuchWasTheLumpSumForm(value)) + } + Future.successful(Ok(howMuchWasTheLumpSumView( + form = preparedForm, + propertyAddress = request.property.addressFull, + mode = mode + ))) + } + } + + def submit(mode: Mode): Action[AnyContent] = + (authenticate andThen getData).async { implicit request => + form.bindFromRequest().fold( + formWithErrors => { + Future.successful(BadRequest(howMuchWasTheLumpSumView( + form = formWithErrors, + propertyAddress = request.property.addressFull, + mode = mode + ))) + }, + lumpSumAmount => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.getOrElse(UserAnswers(request.credId)).set(HowMuchWasTheLumpSumPage, lumpSumAmount.lumpSum)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(navigator.nextPage(HowMuchWasTheLumpSumPage, mode, updatedAnswers)) + ) + } +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumForm.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumForm.scala new file mode 100644 index 0000000..eceaa5e --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumForm.scala @@ -0,0 +1,59 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.models.forms + +import play.api.data.* +import play.api.data.Forms.* +import play.api.data.format.Formatter +import play.api.libs.json.{Json, OFormat} + +import scala.math.BigDecimal.RoundingMode +import scala.util.Try + + +final case class HowMuchWasTheLumpSumForm(lumpSum: BigDecimal) + +object HowMuchWasTheLumpSumForm extends CommonFormValidators { + implicit val format: OFormat[HowMuchWasTheLumpSumForm] = Json.format[HowMuchWasTheLumpSumForm] + + private lazy val lumpSum = "how–much–was–the–lump–sum-value" + private lazy val lumpSumEmptyError = "howMuchWasTheLumpSum.empty.error" + private lazy val lumpSumMaxError = "howMuchWasTheLumpSum.tooLarge.error" + private lazy val lumpSumFormatError = "howMuchWasTheLumpSum.format.error" + + + def unapply(howMuchWasTheLumpSumForm: HowMuchWasTheLumpSumForm): Option[BigDecimal] = Some(howMuchWasTheLumpSumForm.lumpSum) + + val form: Form[HowMuchWasTheLumpSumForm] = Form( + mapping( + lumpSum -> text() + .transform[String](_.strip().replaceAll("[£|,|\\s]", ""), identity) + .verifying( + firstError( + isNotEmpty(lumpSum, lumpSumEmptyError), + regexp(amountRegex.pattern(),lumpSumFormatError) + ) + ) + .transform[BigDecimal](BigDecimal(_).setScale(2, RoundingMode.HALF_UP), _.toString) + .verifying( + maximumValue[BigDecimal](BigDecimal("9999999.99"), lumpSumMaxError) + ) + )(HowMuchWasTheLumpSumForm.apply)(HowMuchWasTheLumpSumForm.unapply) + ) + +} + diff --git a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala index 1ca2124..9849d1e 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala @@ -156,6 +156,7 @@ class Navigator @Inject()() { case ConfirmBreakClausePage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.LandlordController.show(NormalMode) //TODO This needs to be amended when the journey is completed //TODO Next page not made yet case RentReviewPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) + case HowMuchWasTheLumpSumPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) } //TODO change to check your answers page diff --git a/app/uk/gov/hmrc/ngrraldfrontend/pages/HowMuchWasTheLumpSumPage.scala b/app/uk/gov/hmrc/ngrraldfrontend/pages/HowMuchWasTheLumpSumPage.scala new file mode 100644 index 0000000..6757fc6 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/pages/HowMuchWasTheLumpSumPage.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.pages + +import play.api.libs.json.JsPath + +case object HowMuchWasTheLumpSumPage extends QuestionPage[BigDecimal]{ + + override def toString: String = "howMuchWasTheLumpSumPage" + + override def path: JsPath = JsPath \ toString + +} diff --git a/app/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumView.scala.html b/app/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumView.scala.html new file mode 100644 index 0000000..0794b99 --- /dev/null +++ b/app/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumView.scala.html @@ -0,0 +1,55 @@ +@* + * Copyright 2025 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. + *@ + +@import uk.gov.hmrc.govukfrontend.views.html.components._ +@import uk.gov.hmrc.govukfrontend.views.Aliases._ +@import uk.gov.hmrc.ngrraldfrontend.views.html.components._ +@import uk.gov.hmrc.ngrraldfrontend.viewmodels.govuk.all._ +@import uk.gov.hmrc.ngrraldfrontend.config.AppConfig +@import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm + +@this( +layout: Layout, +formHelper: FormWithCSRF, +govukErrorSummary: GovukErrorSummary, +inputText: components.InputText, +saveAndContinueButton: saveAndContinueButton +) + +@(form:Form[HowMuchWasTheLumpSumForm], propertyAddress: String, mode: Mode)(implicit request: RequestHeader, messages: Messages, appConfig: AppConfig) + +@layout(pageTitle = Some(messages("howMuchWasTheLumpSum.title")), showBackLink = true, fullWidth = false) { +@formHelper(action = uk.gov.hmrc.ngrraldfrontend.controllers.routes.HowMuchWasTheLumpSumController.submit(mode), Symbol("autoComplete") -> "off") { +@if(form.errors.nonEmpty) { +@govukErrorSummary(ErrorSummaryViewModel(form)) +} +@propertyAddress +

@messages("howMuchWasTheLumpSum.title")

+ +@inputText( + form = form, + id = "how–much–was–the–lump–sum-value", + name = "how–much–was–the–lump–sum-value", + label = messages("howMuchWasTheLumpSum.title"), + isPageHeading = false, + hint = None, + isVisible = false, + classes = Some("govuk-!-width-two-thirds"), + prefix = Some(PrefixOrSuffix(content = Text("£"))) +) +@saveAndContinueButton(msg = messages("service.continue"), isStartButton = false) +} +} \ No newline at end of file diff --git a/conf/app.routes b/conf/app.routes index fc7de14..311a0ec 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -153,4 +153,10 @@ POST /repairs-and-insurance/change uk.gov.hmrc.n GET /rent-review uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.show(mode: Mode = NormalMode) POST /rent-review uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = NormalMode) GET /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.show(mode: Mode = CheckMode) -POST /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = CheckMode) \ No newline at end of file +POST /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = CheckMode) + +#Rent review +GET /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = NormalMode) +POST /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = NormalMode) +GET /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = CheckMode) +POST /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = CheckMode) \ No newline at end of file diff --git a/conf/messages b/conf/messages index 4c53dba..3ef7c78 100644 --- a/conf/messages +++ b/conf/messages @@ -408,4 +408,10 @@ rentReview.rentReviewMonthsYears.months.invalid.error = The number of months for rentReview.rentReviewMonthsYears.months.maximum.12.error = The number of months for how often your rent is reviewed must be 12 or less rentReview.rentReviewMonthsYears.months.maximum.11.error = The number of months for how often your rent is reviewed must be 11 or less rentReview.rentReviewMonthsYears.years.invalid.error = The number of years for how often your rent is reviewed must be a number, like 2 -rentReview.rentReviewMonthsYears.years.maximum.1000.error = How often your rent is reviewed must be 1,000 years or less \ No newline at end of file +rentReview.rentReviewMonthsYears.years.maximum.1000.error = How often your rent is reviewed must be 1,000 years or less + +#How much was the lump sum +howMuchWasTheLumpSum.title = How much was the lump sum? +howMuchWasTheLumpSum.empty.error = Enter the lump sum, in pounds +howMuchWasTheLumpSum.tooLarge.error = Lump sum must be £9,999,999.99 or less +howMuchWasTheLumpSum.format.error = Lump sum must be a number, like 50,000 \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala new file mode 100644 index 0000000..a195645 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala @@ -0,0 +1,111 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.controllers + +import org.jsoup.Jsoup +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import play.api.http.Status.{BAD_REQUEST, OK, SEE_OTHER} +import play.api.test.FakeRequest +import play.api.test.Helpers.{await, contentAsString, defaultAwaitTimeout, redirectLocation, status} +import uk.gov.hmrc.http.{HeaderNames, NotFoundException} +import uk.gov.hmrc.ngrraldfrontend.helpers.ControllerSpecSupport +import uk.gov.hmrc.ngrraldfrontend.models.AgreementType.{NewAgreement, RenewedAgreement} +import uk.gov.hmrc.ngrraldfrontend.models.registration.CredId +import uk.gov.hmrc.ngrraldfrontend.models.{NormalMode, UserAnswers} +import uk.gov.hmrc.ngrraldfrontend.pages.{HowMuchWasTheLumpSumPage, TellUsAboutYourNewAgreementPage, TellUsAboutYourRenewedAgreementPage} +import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchWasTheLumpSumView + +import scala.concurrent.Future + + +class HowMuchWasTheLumpSumControllerSpec extends ControllerSpecSupport { + val pageTitle = "How much was the lump sum?" + val view: HowMuchWasTheLumpSumView = inject[HowMuchWasTheLumpSumView] + val controllerNoProperty: HowMuchWasTheLumpSumController = new HowMuchWasTheLumpSumController(view, fakeAuth, fakeData(None), mockSessionRepository, mockNavigator, mcc)(mockConfig) + val controllerProperty: HowMuchWasTheLumpSumController = new HowMuchWasTheLumpSumController(view, fakeAuth, fakeDataProperty(Some(property),None), mockSessionRepository, mockNavigator, mcc)(mockConfig) + lazy val howMuchIsTotalAnnualRentAnswers: Option[UserAnswers] = UserAnswers("id").set(HowMuchWasTheLumpSumPage, BigDecimal(1234.67)).toOption + lazy val filledController: Option[UserAnswers] => HowMuchWasTheLumpSumController = answers => HowMuchWasTheLumpSumController( + view, fakeAuth, fakeDataProperty(Some(property), answers), mockSessionRepository, mockNavigator, mcc + ) + + "HowMuchWasTheLumpSumControllerSpec" must { + "method show" must { + "Return OK and the correct view" in { + val result = controllerProperty.show(NormalMode)(authenticatedFakeRequest) + status(result) mustBe OK + val content = contentAsString(result) + content must include(pageTitle) + } + "Return OK and the correct with prepopulated answers" in { + val result = filledController(howMuchIsTotalAnnualRentAnswers).show(NormalMode)(authenticatedFakeRequest) + status(result) mustBe OK + val content = contentAsString(result) + val document = Jsoup.parse(content) + document.select("input[name=how–much–was–the–lump–sum-value]").attr("value") mustBe "1234.67" + } + "Return NotFoundException when property is not found in the mongo" in { + when(mockNGRConnector.getLinkedProperty(any[CredId])(any())).thenReturn(Future.successful(None)) + val exception = intercept[NotFoundException] { + await(controllerNoProperty.show(NormalMode)(authenticatedFakeRequest)) + } + exception.getMessage contains "Could not find answers in backend mongo" mustBe true + } + } + + "method submit" must { + "Return See_Other and the correct view if its a renewedAgreement" in { + when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) + val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "10000")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = filledController(renewedAgreementAnswers).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) + } + "Return See_Other and the correct view if its a newAgreement" in { + + when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) + val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "10000")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = filledController(newAgreementAnswers).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe SEE_OTHER + redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) + } + "Return BAD_REQUEST for missing input and the correct view" in { + val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + + val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + } + "Return Exception if no address is in the mongo" in { + val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode)) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "")) + .withHeaders(HeaderNames.authorisation -> "Bearer 1") + val exception = intercept[NotFoundException] { + await(controllerNoProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest))) + } + exception.getMessage contains "Could not find answers in backend mongo" mustBe true + } + } + } +} diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala new file mode 100644 index 0000000..41770e3 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala @@ -0,0 +1,108 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.models.forms + +import org.scalatest.matchers.should.Matchers +import org.scalatest.matchers.should.Matchers.{should, shouldBe} +import org.scalatest.wordspec.AnyWordSpec +import play.api.data.FormError +import play.api.libs.json.Json + +import scala.collection.immutable.ArraySeq + +class HowMuchWasTheLumpSumFormSpec extends AnyWordSpec with Matchers { + + "HowMuchWasTheLumpSumForm" should { + + "bind valid input" in { + val data = Map("how–much–was–the–lump–sum-value" -> "123456.78") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(HowMuchWasTheLumpSumForm(BigDecimal("123456.78"))) + } + + "bind amount with commas" in { + val data = Map( + "how–much–was–the–lump–sum-value" -> "9,999,999.99", + ) + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(HowMuchWasTheLumpSumForm(BigDecimal("9999999.99"))) + } + + "fail to bind empty input" in { + val data = Map("how–much–was–the–lump–sum-value" -> "") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe true + boundForm.errors should contain(FormError("how–much–was–the–lump–sum-value", List("howMuchWasTheLumpSum.empty.error"), ArraySeq("how–much–was–the–lump–sum-value"))) + } + + "fail to bind non-numeric input" in { + val data = Map("how–much–was–the–lump–sum-value" -> "abc") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.errors should contain(FormError("how–much–was–the–lump–sum-value", List("howMuchWasTheLumpSum.format.error"), ArraySeq("([0-9]+\\.[0-9]+|[0-9]+)"))) + } + + "fail to bind input greater than 9999999.99" in { + val data = Map("how–much–was–the–lump–sum-value" -> "10000000.00") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe true + boundForm.errors should contain(FormError("how–much–was–the–lump–sum-value", List("howMuchWasTheLumpSum.tooLarge.error"), ArraySeq(9999999.99))) + } + + "bind edge case of exactly 9999999.99" in { + val data = Map("how–much–was–the–lump–sum-value" -> "9999999.99") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(HowMuchWasTheLumpSumForm(BigDecimal("9999999.99"))) + } + } + + "serialize to JSON correctly" in { + val form = HowMuchWasTheLumpSumForm(BigDecimal("9999999.99")) + val json = Json.toJson(form) + + json shouldBe Json.obj( + "lumpSum" -> 9999999.99 + ) + } + + "deserialize from JSON correctly" in { + val json = Json.obj( + "lumpSum" -> 9999999.99 + ) + val result = json.validate[HowMuchWasTheLumpSumForm] + + result.isSuccess shouldBe true + result.get shouldBe HowMuchWasTheLumpSumForm(BigDecimal("9999999.99")) + } + + "fail deserialization if value is missing" in { + val json = Json.obj() + val result = json.validate[HowMuchWasTheLumpSumForm] + + result.isError shouldBe true + } +} + + diff --git a/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala new file mode 100644 index 0000000..08088bd --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala @@ -0,0 +1,71 @@ +/* + * Copyright 2025 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 uk.gov.hmrc.ngrraldfrontend.views + +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import uk.gov.hmrc.ngrraldfrontend.helpers.ViewBaseSpec +import uk.gov.hmrc.ngrraldfrontend.models.NormalMode +import uk.gov.hmrc.ngrraldfrontend.models.components.* +import uk.gov.hmrc.ngrraldfrontend.models.forms.HowMuchWasTheLumpSumForm +import uk.gov.hmrc.ngrraldfrontend.views.html.HowMuchWasTheLumpSumView + +class HowMuchWasTheLumpSumViewSpec extends ViewBaseSpec { + lazy val view: HowMuchWasTheLumpSumView = inject[HowMuchWasTheLumpSumView] + + object Strings { + val heading = "How much was the lump sum?" + val saveAndContinue = "Continue" + } + + object Selectors { + val heading = "#main-content > div > div.govuk-grid-column-two-thirds > form > h1" + val saveAndContinue = "#continue" + } + + val address = "5 Brixham Marina, Berry Head Road, Brixham, Devon, TQ5 9BW" + + val form = HowMuchWasTheLumpSumForm.form.fillAndValidate(HowMuchWasTheLumpSumForm(10000)) + + "TellUsAboutYourNewAgreementView" must { + val HowMuchWasTheLumpSumView = view(form, address, NormalMode) + lazy implicit val document: Document = Jsoup.parse(HowMuchWasTheLumpSumView.body) + val htmlApply = view.apply(form, address, NormalMode).body + val htmlRender = view.render(form, address, NormalMode, request, messages, mockConfig).body + lazy val htmlF = view.f(form, address, NormalMode) + + "htmlF is not empty" in { + htmlF.toString() must not be empty + } + + "apply must be the same as render" in { + htmlApply mustBe htmlRender + } + + "render is not empty" in { + htmlRender must not be empty + } + + "show correct heading" in { + elementText(Selectors.heading) mustBe Strings.heading + } + + "show correct continue button" in { + elementText(Selectors.saveAndContinue) mustBe Strings.saveAndContinue + } + } +} From 18e46e56b57d34b4f132d3ae31d8686a04e175c2 Mon Sep 17 00:00:00 2001 From: Pete Robinson <77154273+peterdrobinson@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:20:13 +0100 Subject: [PATCH 2/4] NGR-1538 - Added note to Nav --- app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala index 9849d1e..205710a 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/navigation/Navigator.scala @@ -156,7 +156,7 @@ class Navigator @Inject()() { case ConfirmBreakClausePage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.LandlordController.show(NormalMode) //TODO This needs to be amended when the journey is completed //TODO Next page not made yet case RentReviewPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) - case HowMuchWasTheLumpSumPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) + case HowMuchWasTheLumpSumPage => _ => uk.gov.hmrc.ngrraldfrontend.controllers.routes.CheckRentFreePeriodController.show(NormalMode) //TODO This needs to be amended when the journey is completed } //TODO change to check your answers page From 8f5c58369146bbdd5d6d28d296cc9d49e965e7c5 Mon Sep 17 00:00:00 2001 From: Pete Robinson <77154273+peterdrobinson@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:23:44 +0100 Subject: [PATCH 3/4] WIP --- conf/app.routes | 8 ++++---- .../controllers/HowMuchWasTheLumpSumControllerSpec.scala | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/conf/app.routes b/conf/app.routes index 311a0ec..3bd52f3 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -156,7 +156,7 @@ GET /rent-review/change uk.gov.hmrc.ngrraldfron POST /rent-review/change uk.gov.hmrc.ngrraldfrontend.controllers.RentReviewController.submit(mode: Mode = CheckMode) #Rent review -GET /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = NormalMode) -POST /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = NormalMode) -GET /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = CheckMode) -POST /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = CheckMode) \ No newline at end of file +GET /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = NormalMode) +POST /how-much-was-the-lump-sum uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = NormalMode) +GET /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.show(mode: Mode = CheckMode) +POST /how-much-was-the-lump-sum/change uk.gov.hmrc.ngrraldfrontend.controllers.HowMuchWasTheLumpSumController.submit(mode: Mode = CheckMode) \ No newline at end of file diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala index a195645..d7ac586 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala @@ -96,6 +96,9 @@ class HowMuchWasTheLumpSumControllerSpec extends ControllerSpecSupport { val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include(pageTitle) + content must include("Enter the lump sum, in pounds") } "Return Exception if no address is in the mongo" in { val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode)) From 15b63fbbb238940c2b98476637890f5f81a805dc Mon Sep 17 00:00:00 2001 From: Pete Robinson <77154273+peterdrobinson@users.noreply.github.com> Date: Fri, 10 Oct 2025 15:36:35 +0100 Subject: [PATCH 4/4] NGR-2538 - amended as per Anna's comments --- .../HowMuchWasTheLumpSumControllerSpec.scala | 30 +++++++------------ .../forms/HowMuchWasTheLumpSumFormSpec.scala | 9 ++++++ .../views/HowMuchWasTheLumpSumViewSpec.scala | 5 ++++ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala index d7ac586..236ec08 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/controllers/HowMuchWasTheLumpSumControllerSpec.scala @@ -68,37 +68,27 @@ class HowMuchWasTheLumpSumControllerSpec extends ControllerSpecSupport { } "method submit" must { - "Return See_Other and the correct view if its a renewedAgreement" in { - when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) - val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) - .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "10000")) - .withHeaders(HeaderNames.authorisation -> "Bearer 1") - - val result = filledController(renewedAgreementAnswers).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) - status(result) mustBe SEE_OTHER - redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) - } - "Return See_Other and the correct view if its a newAgreement" in { - - when(mockSessionRepository.set(any())).thenReturn(Future.successful(true)) + "Return BAD_REQUEST for missing input and the correct view" in { val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) - .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "10000")) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "")) .withHeaders(HeaderNames.authorisation -> "Bearer 1") - val result = filledController(newAgreementAnswers).submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) - status(result) mustBe SEE_OTHER - redirectLocation(result) mustBe Some(routes.CheckRentFreePeriodController.show(NormalMode).url) + val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) + status(result) mustBe BAD_REQUEST + val content = contentAsString(result) + content must include(pageTitle) + content must include("Enter the lump sum, in pounds") } - "Return BAD_REQUEST for missing input and the correct view" in { + "Return BAD_REQUEST for incorrect input and the correct view" in { val fakePostRequest = FakeRequest(routes.HowMuchWasTheLumpSumController.submit(NormalMode)) - .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "")) + .withFormUrlEncodedBody(("how–much–was–the–lump–sum-value", "xyz")) .withHeaders(HeaderNames.authorisation -> "Bearer 1") val result = controllerProperty.submit(NormalMode)(authenticatedFakePostRequest(fakePostRequest)) status(result) mustBe BAD_REQUEST val content = contentAsString(result) content must include(pageTitle) - content must include("Enter the lump sum, in pounds") + content must include("Lump sum must be a number, like 50,000") } "Return Exception if no address is in the mongo" in { val fakePostRequest = FakeRequest(routes.WhatTypeOfLeaseRenewalController.submit(NormalMode)) diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala index 41770e3..79f6dd1 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/forms/HowMuchWasTheLumpSumFormSpec.scala @@ -36,6 +36,15 @@ class HowMuchWasTheLumpSumFormSpec extends AnyWordSpec with Matchers { boundForm.value shouldBe Some(HowMuchWasTheLumpSumForm(BigDecimal("123456.78"))) } + "bind valid input when rounding up" in { + val data = Map("how–much–was–the–lump–sum-value" -> "123456.78561") + val boundForm = HowMuchWasTheLumpSumForm.form.bind(data) + + boundForm.hasErrors shouldBe false + boundForm.value shouldBe Some(HowMuchWasTheLumpSumForm(BigDecimal("123456.79"))) + } + + "bind amount with commas" in { val data = Map( "how–much–was–the–lump–sum-value" -> "9,999,999.99", diff --git a/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala index 08088bd..8e18536 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/views/HowMuchWasTheLumpSumViewSpec.scala @@ -34,6 +34,7 @@ class HowMuchWasTheLumpSumViewSpec extends ViewBaseSpec { object Selectors { val heading = "#main-content > div > div.govuk-grid-column-two-thirds > form > h1" + val textInput = "#how–much–was–the–lump–sum-value" val saveAndContinue = "#continue" } @@ -63,6 +64,10 @@ class HowMuchWasTheLumpSumViewSpec extends ViewBaseSpec { "show correct heading" in { elementText(Selectors.heading) mustBe Strings.heading } + + "show correct textInput" in { + elementText(Selectors.textInput) contains("£") + } "show correct continue button" in { elementText(Selectors.saveAndContinue) mustBe Strings.saveAndContinue