From 469b55ce13ff8fd4ec2071e8b0a3198b24377dc9 Mon Sep 17 00:00:00 2001 From: anna-shen-hmrc Date: Fri, 26 Sep 2025 14:24:26 +0100 Subject: [PATCH 1/3] NGR-3053: adding tests for Rent Periods page --- .../controllers/RentPeriodsController.scala | 3 +- .../ngrraldfrontend/models/UserAnswers.scala | 19 ++ .../repo/SessionRepository.scala | 2 +- .../helpers/ViewBaseSpec.scala | 6 +- .../models/NavigationBarComponentSpec.scala | 3 +- .../views/RentPeriodsViewSpec.scala | 164 ++++++++++++++++++ 6 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 test/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodsViewSpec.scala diff --git a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala index 5583b231..41e356e2 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/controllers/RentPeriodsController.scala @@ -24,12 +24,11 @@ import uk.gov.hmrc.govukfrontend.views.viewmodels.table.{Table, TableRow} import uk.gov.hmrc.http.NotFoundException import uk.gov.hmrc.ngrraldfrontend.actions.{AuthRetrievals, DataRetrievalAction} import uk.gov.hmrc.ngrraldfrontend.config.AppConfig -import uk.gov.hmrc.ngrraldfrontend.models.{Mode, NGRDate, ProvideDetailsOfFirstSecondRentPeriod} +import uk.gov.hmrc.ngrraldfrontend.models.{Mode, NGRDate, ProvideDetailsOfFirstSecondRentPeriod, UserAnswers} import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm.form -import uk.gov.hmrc.ngrraldfrontend.models.UserAnswers import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator import uk.gov.hmrc.ngrraldfrontend.pages.{ProvideDetailsOfFirstSecondRentPeriodPage, RentPeriodsPage} import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository diff --git a/app/uk/gov/hmrc/ngrraldfrontend/models/UserAnswers.scala b/app/uk/gov/hmrc/ngrraldfrontend/models/UserAnswers.scala index c691fc06..c64a825f 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/models/UserAnswers.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/models/UserAnswers.scala @@ -48,6 +48,25 @@ final case class UserAnswers( } } + def remove[A](page: Settable[A])(implicit writes: Writes[A]): Try[UserAnswers] = { + val updatedData = data.keys.contains(page.toString) match { + case true => + data.removeObject(page.path) match { + case JsSuccess(jsValue, _) => + Success(jsValue) + case JsError(errors) => + Failure(JsResultException(errors)) + } + case false => Success(data) + } + + updatedData.flatMap { + d => + val updatedAnswers = copy(data = d) + page.cleanup(None, updatedAnswers) + } + } + def getCurrentJourneyUserAnswers[A](page: Gettable[A], userAnswers: UserAnswers, credId: String)(implicit rds: Reads[A]): UserAnswers = userAnswers.get(page) match case Some(_) => userAnswers diff --git a/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala b/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala index fbff1c03..26a9c859 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala @@ -75,7 +75,7 @@ class SessionRepository @Inject()( } def set(answers: UserAnswers): Future[Boolean] = Mdc.preservingMdc { - + println(Console.GREEN + s"*************** ${answers.data}" + Console.RESET) val updatedAnswers = answers.copy(lastUpdated = Instant.now(clock)) collection diff --git a/test/uk/gov/hmrc/ngrraldfrontend/helpers/ViewBaseSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/helpers/ViewBaseSpec.scala index 1f0029ad..1c199942 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/helpers/ViewBaseSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/helpers/ViewBaseSpec.scala @@ -25,12 +25,16 @@ import org.scalatestplus.play.PlaySpec import org.scalatestplus.play.guice.GuiceOneAppPerSuite import play.api.i18n.{Lang, Messages, MessagesApi, MessagesImpl} import play.api.inject.Injector -import play.api.mvc.{AnyContentAsEmpty, RequestHeader} +import play.api.mvc.{AnyContentAsEmpty, MessagesControllerComponents, RequestHeader} import play.api.test.{FakeRequest, Injecting} import play.twirl.api.Html import uk.gov.hmrc.http.HeaderCarrier +import uk.gov.hmrc.ngrraldfrontend.actions.{FakeAuthenticatedRequest, FakeDataRetrievalAction} import uk.gov.hmrc.ngrraldfrontend.mocks.MockAppConfig +import uk.gov.hmrc.ngrraldfrontend.models.UserAnswers import uk.gov.hmrc.ngrraldfrontend.models.registration.Email +import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator +import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository trait ViewBaseSpec extends PlaySpec with GuiceOneAppPerSuite with Injecting with BeforeAndAfterEach with Matchers { def injector: Injector = app.injector diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/NavigationBarComponentSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/NavigationBarComponentSpec.scala index bef9ced4..f48b7fce 100644 --- a/test/uk/gov/hmrc/ngrraldfrontend/models/NavigationBarComponentSpec.scala +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/NavigationBarComponentSpec.scala @@ -20,10 +20,11 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers.shouldBe import play.api.mvc.Call import uk.gov.hmrc.ngrraldfrontend.helpers.ViewBaseSpec +import uk.gov.hmrc.ngrraldfrontend.mocks.MockAppConfig import uk.gov.hmrc.ngrraldfrontend.models.components.{NavBarContents, NavBarCurrentPage, NavBarPageContents, NavButton} class NavigationBarComponentSpec extends ViewBaseSpec { - + "NavButton" should { "store all fields correctly" in { val call = Call("GET", "/test") diff --git a/test/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodsViewSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodsViewSpec.scala new file mode 100644 index 00000000..e3e70cb2 --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/views/RentPeriodsViewSpec.scala @@ -0,0 +1,164 @@ +/* + * 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 org.scalatestplus.mockito.MockitoSugar.mock +import play.api.data.Form +import play.api.mvc.MessagesControllerComponents +import uk.gov.hmrc.ngrraldfrontend.actions.{FakeAuthenticatedRequest, FakeDataRetrievalAction} +import uk.gov.hmrc.ngrraldfrontend.controllers.RentPeriodsController +import uk.gov.hmrc.ngrraldfrontend.helpers.ViewBaseSpec +import uk.gov.hmrc.ngrraldfrontend.models.components.NGRRadio.buildRadios +import uk.gov.hmrc.ngrraldfrontend.models.forms.RentPeriodsForm +import uk.gov.hmrc.ngrraldfrontend.models.{NormalMode, ProvideDetailsOfFirstSecondRentPeriod, UserAnswers} +import uk.gov.hmrc.ngrraldfrontend.navigation.Navigator +import uk.gov.hmrc.ngrraldfrontend.repo.SessionRepository +import uk.gov.hmrc.ngrraldfrontend.views.html.RentPeriodView + +import scala.concurrent.ExecutionContext + +class RentPeriodsViewSpec extends ViewBaseSpec { + implicit lazy val ec: ExecutionContext = inject[ExecutionContext] + lazy val mcc: MessagesControllerComponents = inject[MessagesControllerComponents] + val fakeAuth = new FakeAuthenticatedRequest(mcc.parsers.defaultBodyParser) + val mockSessionRepository: SessionRepository = mock[SessionRepository] + val mockNavigator: Navigator = inject[Navigator] + + def fakeData(answers: Option[UserAnswers]) = new FakeDataRetrievalAction(answers, None) + + lazy val view: RentPeriodView = inject[RentPeriodView] + val rentPeriodsController: RentPeriodsController = new RentPeriodsController(view, fakeAuth, fakeData(None), mcc, mockSessionRepository, mockNavigator)(mockConfig, ec) + val address = "5 Brixham Marina, Berry Head Road, Brixham, Devon, TQ5 9BW" + + val heading = "Rent periods" + val title = s"$heading - GOV.UK" + val firsPeriodStartDate = "1 February 2025" + val firstPeriodEndDate = "1 March 2025" + val secondPeriodStartDate = "1 April 2025" + val secondPeriodEndDate = "1 August 2025" + val firstPeriodRentQuestion = "Yes" + val firstRentAmount = "£2,300.46" + val secondRentAmount = "£1,350" + val additionPeriodQuestion = "Do you need to add another rent period?" + val yesRadio = "Yes" + val noRadio = "No" + val saveButton = "Continue" + + val firstSecondRentPeriods = ProvideDetailsOfFirstSecondRentPeriod( + firstDateStart = "2025-02-01", + firstDateEnd = "2025-03-01", + firstRentPeriodRadio = true, + firstRentPeriodAmount = Some("2300.4567"), + secondDateStart = "2025-04-01", + secondDateEnd = "2025-08-01", + secondHowMuchIsRent = "1350") + val firstTable = rentPeriodsController.firstTable(firstSecondRentPeriods) + val secondTable = rentPeriodsController.secondTable(firstSecondRentPeriods) + private val form: Form[RentPeriodsForm] = RentPeriodsForm.form.fillAndValidate(RentPeriodsForm("No")) + val radio = buildRadios(form, RentPeriodsForm.ngrRadio(form)) + + object Selectors { + val navTitle = "head > title" + val heading = "#main-content > div > div.govuk-grid-column-two-thirds > form > h1" + val firsPeriodStartDate = "#first-period-start-date-id" + val firstPeriodEndDate = "#first-period-end-date-id" + val secondPeriodStartDate = "#second-period-start-date-id" + val secondPeriodEndDate = "#second-period-end-date-id" + val firstPeriodRentQuestion = "#first-period-has-pay-id" + val firstRentAmount = "#first-period-rent-value-id" + val secondRentAmount = "#second-period-rent-value-id" + val additionPeriodQuestion = "#main-content > div > div.govuk-grid-column-two-thirds > form > div > fieldset > legend > h1" + val yesRadio = "#main-content > div > div.govuk-grid-column-two-thirds > form > div > fieldset > div > div:nth-child(1) > label" + val noRadio = "#main-content > div > div.govuk-grid-column-two-thirds > form > div > fieldset > div > div:nth-child(2) > label" + val saveButton = "#continue" + } + + "RentPeriodsView" must { + val rentPeriodsView = view(address, form, firstTable, secondTable, radio, NormalMode) + lazy implicit val document: Document = Jsoup.parse(rentPeriodsView.body) + val htmlApply = view.apply(address, form, firstTable, secondTable, radio, NormalMode).body + val htmlRender = view.render(address, form, firstTable, secondTable, radio, NormalMode, request, messages, mockConfig).body + lazy val htmlF = view.f(address, form, firstTable, secondTable, radio, NormalMode) + + "htmlF is not empty" in { + htmlF.toString() must not be empty + } + + "apply must nit be the same as render" in { + htmlApply mustBe htmlRender + } + + "render is not empty" in { + htmlRender must not be empty + } + + "show the correct title" in { + elementText(Selectors.navTitle) mustBe title + } + + "show the correct heading" in { + elementText(Selectors.heading) mustBe heading + } + + "show the correct first period start date" in { + elementText(Selectors.firsPeriodStartDate) mustBe firsPeriodStartDate + } + + "show the correct first period end date" in { + elementText(Selectors.firstPeriodEndDate) mustBe firstPeriodEndDate + } + + "show the correct second period start date" in { + elementText(Selectors.secondPeriodStartDate) mustBe secondPeriodStartDate + } + + "show the correct second period end date" in { + elementText(Selectors.secondPeriodEndDate) mustBe secondPeriodEndDate + } + + "show the correct answer for if pay first period rent" in { + elementText(Selectors.firstPeriodRentQuestion) mustBe firstPeriodRentQuestion + } + + "show the correct first period rent amount" in { + elementText(Selectors.firstRentAmount) mustBe firstRentAmount + } + + "show the correct second period rent amount" in { + elementText(Selectors.secondRentAmount) mustBe secondRentAmount + } + + "show the correct radio question" in { + elementText(Selectors.additionPeriodQuestion) mustBe additionPeriodQuestion + } + + "show the correct yes radio button label" in { + elementText(Selectors.yesRadio) mustBe yesRadio + } + + "show the correct no radio button label" in { + elementText(Selectors.noRadio) mustBe noRadio + } + + "show the correct save button" in { + elementText(Selectors.saveButton) mustBe saveButton + } + } +} + From a0c1a56f79f1bdeb9701372b402cc21ee1c4af86 Mon Sep 17 00:00:00 2001 From: anna-shen-hmrc Date: Mon, 29 Sep 2025 12:26:16 +0100 Subject: [PATCH 2/3] NGR-3053: address PR bot --- app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala | 1 - project/AppDependencies.scala | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala b/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala index 26a9c859..a7014077 100644 --- a/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala +++ b/app/uk/gov/hmrc/ngrraldfrontend/repo/SessionRepository.scala @@ -75,7 +75,6 @@ class SessionRepository @Inject()( } def set(answers: UserAnswers): Future[Boolean] = Mdc.preservingMdc { - println(Console.GREEN + s"*************** ${answers.data}" + Console.RESET) val updatedAnswers = answers.copy(lastUpdated = Instant.now(clock)) collection diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala index 3d868546..bc2cd287 100644 --- a/project/AppDependencies.scala +++ b/project/AppDependencies.scala @@ -8,7 +8,7 @@ object AppDependencies { val compile: Seq[ModuleID] = Seq( "uk.gov.hmrc" %% "bootstrap-frontend-play-30" % bootstrapVersion, - "uk.gov.hmrc" %% "play-frontend-hmrc-play-30" % "12.12.0", + "uk.gov.hmrc" %% "play-frontend-hmrc-play-30" % "12.13.0", "uk.gov.hmrc.mongo" %% "hmrc-mongo-play-30" % hmrcMongoVersion, "uk.gov.hmrc" %% "centralised-authorisation-resource-client-play-30" % "1.12.0", "com.beachape" %% "enumeratum-play" % enumeratumVersion, From cf755992d517a5d2f960e3bd22788da939d05f4b Mon Sep 17 00:00:00 2001 From: anna-shen-hmrc Date: Tue, 30 Sep 2025 13:05:37 +0100 Subject: [PATCH 3/3] NGR-3053: adding more tests --- project/AppDependencies.scala | 2 +- .../models/UserAnswerSpec.scala | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/uk/gov/hmrc/ngrraldfrontend/models/UserAnswerSpec.scala diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala index bc2cd287..ee57eafe 100644 --- a/project/AppDependencies.scala +++ b/project/AppDependencies.scala @@ -8,7 +8,7 @@ object AppDependencies { val compile: Seq[ModuleID] = Seq( "uk.gov.hmrc" %% "bootstrap-frontend-play-30" % bootstrapVersion, - "uk.gov.hmrc" %% "play-frontend-hmrc-play-30" % "12.13.0", + "uk.gov.hmrc" %% "play-frontend-hmrc-play-30" % "12.14.0", "uk.gov.hmrc.mongo" %% "hmrc-mongo-play-30" % hmrcMongoVersion, "uk.gov.hmrc" %% "centralised-authorisation-resource-client-play-30" % "1.12.0", "com.beachape" %% "enumeratum-play" % enumeratumVersion, diff --git a/test/uk/gov/hmrc/ngrraldfrontend/models/UserAnswerSpec.scala b/test/uk/gov/hmrc/ngrraldfrontend/models/UserAnswerSpec.scala new file mode 100644 index 00000000..8e6339fb --- /dev/null +++ b/test/uk/gov/hmrc/ngrraldfrontend/models/UserAnswerSpec.scala @@ -0,0 +1,68 @@ +/* + * 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 + +import play.api.libs.json.{JsValue, Json} +import uk.gov.hmrc.ngrraldfrontend.helpers.TestSupport +import uk.gov.hmrc.ngrraldfrontend.models.AgreementType.RenewedAgreement +import uk.gov.hmrc.ngrraldfrontend.pages.{LandlordPage, TellUsAboutYourRenewedAgreementPage, WhatTypeOfLeaseRenewalPage} + +import java.time.Instant + +class UserAnswerSpec extends TestSupport { + + val userAnswers: UserAnswers = UserAnswers(credId.value, Json.obj( + "tellUsAboutRenewedAgreement" -> "RenewedAgreement", + "whatTypeOfLeaseRenewal" -> "SurrenderAndRenewal" + ), Instant.ofEpochSecond(1759232590)) + + val userAnswersJson: JsValue = Json.parse( + """ + |{ + | "credId":"1234", + | "data":{ + | "tellUsAboutRenewedAgreement": "RenewedAgreement", + | "whatTypeOfLeaseRenewal":"SurrenderAndRenewal" + | }, + | "lastUpdated":{ + | "$date":{ + | "$numberLong":"1759232590000" + | } + | } + |} + |""".stripMargin + ) + + "UserAnswers" should { + "deserialize to json" in { + Json.toJson(userAnswers) mustBe userAnswersJson + } + "serialize to json" in { + userAnswersJson.as[UserAnswers] mustBe userAnswers + } + } + "remove method" should { + "Remove value without error when the key isn't there and user answers shouldn't be changed" in { + val actual = userAnswers.remove(LandlordPage).get + actual mustBe userAnswers + } + "Remove the correct value and user answers shouldn't contain it any more" in { + val actual = userAnswers.remove(WhatTypeOfLeaseRenewalPage).get + actual.get(WhatTypeOfLeaseRenewalPage) mustBe None + } + } +}