From 76f25f850d5df5c3b3c77a0d8b47ae3f57e2e610 Mon Sep 17 00:00:00 2001 From: wg-hmrc Date: Fri, 24 Apr 2026 10:00:16 +0100 Subject: [PATCH 1/4] [DTR-4543] Adding WhatDoYouWantToAmendStandard screen --- ...atDoYouWantToAmendStandardController.scala | 72 ++++++++ ...DoYouWantToAmendStandardFormProvider.scala | 31 ++++ app/models/WhatDoYouWantToAmendStandard.scala | 45 +++++ .../WhatDoYouWantToAmendStandardPage.scala | 28 ++++ .../WhatDoYouWantToAmendStandardSummary.scala | 50 ++++++ ...hatDoYouWantToAmendStandardView.scala.html | 47 ++++++ conf/app.routes | 5 + conf/messages.en | 8 + test-utils/generators/ModelGenerators.scala | 6 + ...YouWantToAmendStandardControllerSpec.scala | 158 ++++++++++++++++++ ...uWantToAmendStandardFormProviderSpec.scala | 45 +++++ .../WhatDoYouWantToAmendStandardSpec.scala | 64 +++++++ ...WhatDoYouWantToAmendStandardViewSpec.scala | 97 +++++++++++ 13 files changed, 656 insertions(+) create mode 100644 app/controllers/amend/WhatDoYouWantToAmendStandardController.scala create mode 100644 app/forms/WhatDoYouWantToAmendStandardFormProvider.scala create mode 100644 app/models/WhatDoYouWantToAmendStandard.scala create mode 100644 app/pages/amend/WhatDoYouWantToAmendStandardPage.scala create mode 100644 app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala create mode 100644 app/views/amend/WhatDoYouWantToAmendStandardView.scala.html create mode 100644 test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala create mode 100644 test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala create mode 100644 test/models/WhatDoYouWantToAmendStandardSpec.scala create mode 100644 test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala diff --git a/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala new file mode 100644 index 00000000..8bd10774 --- /dev/null +++ b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala @@ -0,0 +1,72 @@ +/* + * 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 controllers.amend + +import controllers.actions._ +import forms.WhatDoYouWantToAmendStandardFormProvider +import javax.inject.Inject +import models.Mode +import navigation.Navigator +import pages.amend.WhatDoYouWantToAmendStandardPage +import play.api.i18n.{I18nSupport, MessagesApi} +import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} +import repositories.SessionRepository +import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController +import views.html.amend.WhatDoYouWantToAmendStandardView + +import scala.concurrent.{ExecutionContext, Future} + +class WhatDoYouWantToAmendStandardController @Inject()( + override val messagesApi: MessagesApi, + sessionRepository: SessionRepository, + navigator: Navigator, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + formProvider: WhatDoYouWantToAmendStandardFormProvider, + val controllerComponents: MessagesControllerComponents, + view: WhatDoYouWantToAmendStandardView + )(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport { + + val form = formProvider() + + def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { + implicit request => + + val preparedForm = request.userAnswers.get(WhatDoYouWantToAmendStandardPage) match { + case None => form + case Some(value) => form.fill(value) + } + + Ok(view(preparedForm, mode)) + } + + def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async { + implicit request => + + form.bindFromRequest().fold( + formWithErrors => + Future.successful(BadRequest(view(formWithErrors, mode))), + + value => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.set(WhatDoYouWantToAmendStandardPage, value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(navigator.nextPage(WhatDoYouWantToAmendStandardPage, mode, updatedAnswers)) + ) + } +} diff --git a/app/forms/WhatDoYouWantToAmendStandardFormProvider.scala b/app/forms/WhatDoYouWantToAmendStandardFormProvider.scala new file mode 100644 index 00000000..de7cbf7d --- /dev/null +++ b/app/forms/WhatDoYouWantToAmendStandardFormProvider.scala @@ -0,0 +1,31 @@ +/* + * 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 forms + +import javax.inject.Inject + +import forms.mappings.Mappings +import play.api.data.Form +import models.WhatDoYouWantToAmendStandard + +class WhatDoYouWantToAmendStandardFormProvider @Inject() extends Mappings { + + def apply(): Form[WhatDoYouWantToAmendStandard] = + Form( + "value" -> enumerable[WhatDoYouWantToAmendStandard]("amend.whatDoYouWantToAmendStandard.error.required") + ) +} diff --git a/app/models/WhatDoYouWantToAmendStandard.scala b/app/models/WhatDoYouWantToAmendStandard.scala new file mode 100644 index 00000000..00c7ba29 --- /dev/null +++ b/app/models/WhatDoYouWantToAmendStandard.scala @@ -0,0 +1,45 @@ +/* + * 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 models + +import play.api.i18n.Messages +import uk.gov.hmrc.govukfrontend.views.Aliases.Text +import uk.gov.hmrc.govukfrontend.views.viewmodels.radios.RadioItem + +sealed trait WhatDoYouWantToAmendStandard + +object WhatDoYouWantToAmendStandard extends Enumerable.Implicits { + + case object AmendToNilReturn extends WithName("amendToNilReturn") with WhatDoYouWantToAmendStandard + case object AmendPaymentOrSubcontractorDetails extends WithName("amendPaymentOrSubcontractorDetails") with WhatDoYouWantToAmendStandard + + val values: Seq[WhatDoYouWantToAmendStandard] = Seq( + AmendToNilReturn, AmendPaymentOrSubcontractorDetails + ) + + def options(implicit messages: Messages): Seq[RadioItem] = values.zipWithIndex.map { + case (value, index) => + RadioItem( + content = Text(messages(s"amend.whatDoYouWantToAmendStandard.${value.toString}")), + value = Some(value.toString), + id = Some(s"value_$index") + ) + } + + implicit val enumerable: Enumerable[WhatDoYouWantToAmendStandard] = + Enumerable(values.map(v => v.toString -> v): _*) +} diff --git a/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala b/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala new file mode 100644 index 00000000..bc0b855f --- /dev/null +++ b/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala @@ -0,0 +1,28 @@ +/* + * 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.amend + +import models.WhatDoYouWantToAmendStandard +import pages.QuestionPage +import play.api.libs.json.JsPath + +case object WhatDoYouWantToAmendStandardPage extends QuestionPage[WhatDoYouWantToAmendStandard] { + + override def path: JsPath = JsPath \ toString + + override def toString: String = "whatDoYouWantToAmendStandard" +} diff --git a/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala b/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala new file mode 100644 index 00000000..2a1056fa --- /dev/null +++ b/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala @@ -0,0 +1,50 @@ +/* + * 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 viewmodels.checkAnswers.amend + +import controllers.amend.routes +import models.{CheckMode, UserAnswers} +import pages.amend.WhatDoYouWantToAmendStandardPage +import play.api.i18n.Messages +import play.twirl.api.HtmlFormat +import uk.gov.hmrc.govukfrontend.views.viewmodels.content.HtmlContent +import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow +import viewmodels.govuk.summarylist._ +import viewmodels.implicits._ + +object WhatDoYouWantToAmendStandardSummary { + + def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] = + answers.get(WhatDoYouWantToAmendStandardPage).map { + answer => + + val value = ValueViewModel( + HtmlContent( + HtmlFormat.escape(messages(s"amend.whatDoYouWantToAmendStandard.$answer")) + ) + ) + + SummaryListRowViewModel( + key = "amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel", + value = value, + actions = Seq( + ActionItemViewModel("site.change", routes.WhatDoYouWantToAmendStandardController.onPageLoad(CheckMode).url) + .withVisuallyHiddenText(messages("amend.whatDoYouWantToAmendStandard.change.hidden")) + ) + ) + } +} diff --git a/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html new file mode 100644 index 00000000..808fec0e --- /dev/null +++ b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html @@ -0,0 +1,47 @@ +@* + * 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. + *@ + +@this( + layout: templates.Layout, + formHelper: FormWithCSRF, + govukErrorSummary: GovukErrorSummary, + govukRadios: GovukRadios, + govukButton: GovukButton +) + +@(form: Form[_], mode: Mode)(implicit request: Request[_], messages: Messages) + +@layout(pageTitle = title(form, messages("amend.whatDoYouWantToAmendStandard.title"))) { + + @formHelper(action = controllers.amend.routes.WhatDoYouWantToAmendStandardController.onSubmit(mode), Symbol("autoComplete") -> "off") { + + @if(form.errors.nonEmpty) { + @govukErrorSummary(ErrorSummaryViewModel(form, errorLinkOverrides = Map("value" -> "value_0"))) + } + + @govukRadios( + RadiosViewModel( + field = form("value"), + legend = LegendViewModel(messages("amend.whatDoYouWantToAmendStandard.heading")).asPageHeading(), + items = WhatDoYouWantToAmendStandard.options + ) + ) + + @govukButton( + ButtonViewModel(messages("site.continue")) + ) + } +} diff --git a/conf/app.routes b/conf/app.routes index fcd90f4d..59928457 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -173,3 +173,8 @@ POST /amend-monthly-return/what-do-you-want-to-amend-nil GET /manage-cis-return/amend-monthly-return/confirm-amendment controllers.amend.ConfirmAmendmentController.onPageLoad() POST /manage-cis-return/amend-monthly-return/confirm-amendment controllers.amend.ConfirmAmendmentController.onSubmit() + +GET /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad(mode: Mode = NormalMode) +POST /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = NormalMode) +GET /amend-monthly-return/change-what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad(mode: Mode = CheckMode) +POST /amend-monthly-return/change-what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = CheckMode) diff --git a/conf/messages.en b/conf/messages.en index 5be0caa9..36d8f7d6 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -551,3 +551,11 @@ whatDoYouWantToAmendNil.heading = What do you want to amend? whatDoYouWantToAmendNil.amendNilReturn = I want to amend this nil return whatDoYouWantToAmendNil.addPaymentOrSubcontractorDetails = I want to add payment or subcontractor details on this return whatDoYouWantToAmendNil.error.required = Select the type of amendment you want to make + +amend.whatDoYouWantToAmendStandard.title = What do you want to amend? +amend.whatDoYouWantToAmendStandard.heading = What do you want to amend? +amend.whatDoYouWantToAmendStandard.amendToNilReturn = I want to amend this standard return to a nil return +amend.whatDoYouWantToAmendStandard.amendPaymentOrSubcontractorDetails = I want to amend payment or subcontractor details on this return +amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel = What do you want to amend? +amend.whatDoYouWantToAmendStandard.error.required = Select the type of amendment you want to make +amend.whatDoYouWantToAmendStandard.change.hidden = what do you want to amend? diff --git a/test-utils/generators/ModelGenerators.scala b/test-utils/generators/ModelGenerators.scala index 576c07d0..72b3852c 100644 --- a/test-utils/generators/ModelGenerators.scala +++ b/test-utils/generators/ModelGenerators.scala @@ -17,6 +17,7 @@ package generators import models.amend.WhatDoYouWantToAmendNil +import models.WhatDoYouWantToAmendStandard import models.monthlyreturns.{Declaration, InactivityRequest} import org.scalacheck.{Arbitrary, Gen} @@ -27,6 +28,11 @@ trait ModelGenerators { Gen.oneOf(WhatDoYouWantToAmendNil.values.toSeq) } + implicit lazy val arbitraryWhatDoYouWantToAmendStandard: Arbitrary[WhatDoYouWantToAmendStandard] = + Arbitrary { + Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) + } + implicit lazy val arbitraryVerifySubcontractors: Arbitrary[Boolean] = Arbitrary { Gen.oneOf(true, false) diff --git a/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala new file mode 100644 index 00000000..930247a1 --- /dev/null +++ b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala @@ -0,0 +1,158 @@ +/* + * 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 controllers.amend + +import base.SpecBase +import forms.WhatDoYouWantToAmendStandardFormProvider +import models.{NormalMode, WhatDoYouWantToAmendStandard, UserAnswers} +import navigation.{FakeNavigator, Navigator} +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.when +import org.scalatestplus.mockito.MockitoSugar +import pages.amend.WhatDoYouWantToAmendStandardPage +import play.api.inject.bind +import play.api.mvc.Call +import play.api.test.FakeRequest +import play.api.test.Helpers._ +import repositories.SessionRepository +import views.html.amend.WhatDoYouWantToAmendStandardView + +import scala.concurrent.Future + +class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSugar { + + def onwardRoute = Call("GET", "/foo") + + lazy val whatDoYouWantToAmendStandardRoute = routes.WhatDoYouWantToAmendStandardController.onPageLoad(NormalMode).url + + val formProvider = new WhatDoYouWantToAmendStandardFormProvider() + val form = formProvider() + + "WhatDoYouWantToAmendStandard Controller" - { + + "must return OK and the correct view for a GET" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = FakeRequest(GET, whatDoYouWantToAmendStandardRoute) + + val result = route(application, request).value + + val view = application.injector.instanceOf[WhatDoYouWantToAmendStandardView] + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form, NormalMode)(request, messages(application)).toString + } + } + + "must populate the view correctly on a GET when the question has previously been answered" in { + + val userAnswers = UserAnswers(userAnswersId).set(WhatDoYouWantToAmendStandardPage, WhatDoYouWantToAmendStandard.values.head).success.value + + val application = applicationBuilder(userAnswers = Some(userAnswers)).build() + + running(application) { + val request = FakeRequest(GET, whatDoYouWantToAmendStandardRoute) + + val view = application.injector.instanceOf[WhatDoYouWantToAmendStandardView] + + val result = route(application, request).value + + status(result) mustEqual OK + contentAsString(result) mustEqual view(form.fill(WhatDoYouWantToAmendStandard.values.head), NormalMode)(request, messages(application)).toString + } + } + + "must redirect to the next page when valid data is submitted" in { + + val mockSessionRepository = mock[SessionRepository] + + when(mockSessionRepository.set(any())) thenReturn Future.successful(true) + + val application = + applicationBuilder(userAnswers = Some(emptyUserAnswers)) + .overrides( + bind[Navigator].toInstance(new FakeNavigator(onwardRoute)), + bind[SessionRepository].toInstance(mockSessionRepository) + ) + .build() + + running(application) { + val request = + FakeRequest(POST, whatDoYouWantToAmendStandardRoute) + .withFormUrlEncodedBody(("value", WhatDoYouWantToAmendStandard.values.head.toString)) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual onwardRoute.url + } + } + + "must return a Bad Request and errors when invalid data is submitted" in { + + val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() + + running(application) { + val request = + FakeRequest(POST, whatDoYouWantToAmendStandardRoute) + .withFormUrlEncodedBody(("value", "invalid value")) + + val boundForm = form.bind(Map("value" -> "invalid value")) + + val view = application.injector.instanceOf[WhatDoYouWantToAmendStandardView] + + val result = route(application, request).value + + status(result) mustEqual BAD_REQUEST + contentAsString(result) mustEqual view(boundForm, NormalMode)(request, messages(application)).toString + } + } + + "must redirect to Journey Recovery for a GET if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = FakeRequest(GET, whatDoYouWantToAmendStandardRoute) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual controllers.routes.JourneyRecoveryController.onPageLoad().url + } + } + + "redirect to Journey Recovery for a POST if no existing data is found" in { + + val application = applicationBuilder(userAnswers = None).build() + + running(application) { + val request = + FakeRequest(POST, whatDoYouWantToAmendStandardRoute) + .withFormUrlEncodedBody(("value", WhatDoYouWantToAmendStandard.values.head.toString)) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + + redirectLocation(result).value mustEqual controllers.routes.JourneyRecoveryController.onPageLoad().url + } + } + } +} diff --git a/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala b/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala new file mode 100644 index 00000000..2b25992c --- /dev/null +++ b/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala @@ -0,0 +1,45 @@ +/* + * 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 forms + +import forms.behaviours.OptionFieldBehaviours +import models.WhatDoYouWantToAmendStandard +import play.api.data.FormError + +class WhatDoYouWantToAmendStandardFormProviderSpec extends OptionFieldBehaviours { + + val form = new WhatDoYouWantToAmendStandardFormProvider()() + + ".value" - { + + val fieldName = "value" + val requiredKey = "amend.whatDoYouWantToAmendStandard.error.required" + + behave like optionsField[WhatDoYouWantToAmendStandard]( + form, + fieldName, + validValues = WhatDoYouWantToAmendStandard.values, + invalidError = FormError(fieldName, "error.invalid") + ) + + behave like mandatoryField( + form, + fieldName, + requiredError = FormError(fieldName, requiredKey) + ) + } +} diff --git a/test/models/WhatDoYouWantToAmendStandardSpec.scala b/test/models/WhatDoYouWantToAmendStandardSpec.scala new file mode 100644 index 00000000..cdaecbc8 --- /dev/null +++ b/test/models/WhatDoYouWantToAmendStandardSpec.scala @@ -0,0 +1,64 @@ +/* + * 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 models + +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers +import org.scalatest.OptionValues +import play.api.libs.json.{JsError, JsString, Json} + +class WhatDoYouWantToAmendStandardSpec extends AnyFreeSpec with Matchers with ScalaCheckPropertyChecks with OptionValues { + + "WhatDoYouWantToAmendStandard" - { + + "must deserialise valid values" in { + + val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) + + forAll(gen) { + whatDoYouWantToAmendStandard => + + JsString(whatDoYouWantToAmendStandard.toString).validate[WhatDoYouWantToAmendStandard].asOpt.value mustEqual whatDoYouWantToAmendStandard + } + } + + "must fail to deserialise invalid values" in { + + val gen = arbitrary[String] suchThat (!WhatDoYouWantToAmendStandard.values.map(_.toString).contains(_)) + + forAll(gen) { + invalidValue => + + JsString(invalidValue).validate[WhatDoYouWantToAmendStandard] mustEqual JsError("error.invalid") + } + } + + "must serialise" in { + + val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) + + forAll(gen) { + whatDoYouWantToAmendStandard => + + Json.toJson(whatDoYouWantToAmendStandard) mustEqual JsString(whatDoYouWantToAmendStandard.toString) + } + } + } +} diff --git a/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala new file mode 100644 index 00000000..c67dfd8d --- /dev/null +++ b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala @@ -0,0 +1,97 @@ +/* + * 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.amend + +import base.SpecBase +import forms.WhatDoYouWantToAmendStandardFormProvider +import models.{NormalMode, WhatDoYouWantToAmendStandard} +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import play.api.i18n.Messages +import play.api.test.FakeRequest +import views.html.amend.WhatDoYouWantToAmendStandardView + +class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { + + "WhatDoYouWantToAmendStandardView" - { + "must render the page with the correct html elements" in new Setup { + val doc: Document = Jsoup.parse(html.toString) + doc.title must include(messages("amend.whatDoYouWantToAmendStandard.title")) + doc.select("h1").text must include(messages("amend.whatDoYouWantToAmendStandard.heading")) + } + + "must render radio buttons with correct values" in new Setup { + val doc: Document = Jsoup.parse(html.toString) + + doc.select("input[type=radio][value=amendToNilReturn]").size() mustBe 1 + doc.select("input[type=radio][value=amendPaymentOrSubcontractorDetails]").size() mustBe 1 + } + + "must render radio buttons with correct labels" in new Setup { + val doc: Document = Jsoup.parse(html.toString) + + doc.text() must include(messages("amend.whatDoYouWantToAmendStandard.amendToNilReturn")) + doc.text() must include(messages("amend.whatDoYouWantToAmendStandard.amendPaymentOrSubcontractorDetails")) + } + + "must pre-populate the form when user has previously answered" in new Setup { + val filledForm = form.fill(WhatDoYouWantToAmendStandard.AmendToNilReturn) + val filledHtml = view(filledForm, NormalMode) + val doc: Document = Jsoup.parse(filledHtml.toString) + + doc.select("input[value=amendToNilReturn]").hasAttr("checked") mustBe true + doc.select("input[value=amendPaymentOrSubcontractorDetails]").hasAttr("checked") mustBe false + } + + "must show error summary when form has errors" in new Setup { + val formWithErrors = form.bind(Map("value" -> "")) + val errorHtml = view(formWithErrors, NormalMode) + val doc: Document = Jsoup.parse(errorHtml.toString) + + doc.title must startWith(messages("error.title.prefix")) + doc.select(".govuk-error-summary").size() mustBe 1 + doc.text() must include(messages("amend.whatDoYouWantToAmendStandard.error.required")) + } + + "must render error summary with correct link when form has errors" in new Setup { + val formWithErrors = form.bind(Map("value" -> "")) + val errorHtml = view(formWithErrors, NormalMode) + val doc: Document = Jsoup.parse(errorHtml.toString) + + doc.select(".govuk-error-summary__list a").attr("href") mustBe "#value_0" + } + + "must render the continue button" in new Setup { + val doc: Document = Jsoup.parse(html.toString) + doc.select("button[type=submit]").text mustBe messages("site.continue") + } + } + + trait Setup { + val app = applicationBuilder().build() + val view = app.injector.instanceOf[WhatDoYouWantToAmendStandardView] + val formProvider = new WhatDoYouWantToAmendStandardFormProvider() + val form = formProvider() + implicit val request: play.api.mvc.Request[_] = FakeRequest() + implicit val messages: Messages = play.api.i18n.MessagesImpl( + play.api.i18n.Lang.defaultLang, + app.injector.instanceOf[play.api.i18n.MessagesApi] + ) + + val html = view(form, NormalMode) + } +} From c48d5d9734fe2c77446229fc83a961346417fc47 Mon Sep 17 00:00:00 2001 From: wg-hmrc Date: Fri, 24 Apr 2026 10:00:50 +0100 Subject: [PATCH 2/4] [DTR-4543] Formatting fix --- ...atDoYouWantToAmendStandardController.scala | 60 ++++++++--------- ...DoYouWantToAmendStandardFormProvider.scala | 5 +- .../WhatDoYouWantToAmendStandard.scala | 23 ++++--- .../WhatDoYouWantToAmendStandardPage.scala | 2 +- .../WhatDoYouWantToAmendStandardSummary.scala | 27 ++++---- ...hatDoYouWantToAmendStandardView.scala.html | 2 + test-utils/generators/ModelGenerators.scala | 2 +- ...YouWantToAmendStandardControllerSpec.scala | 17 +++-- ...uWantToAmendStandardFormProviderSpec.scala | 45 ------------- ...uWantToAmendStandardFormProviderSpec.scala | 29 +++++++++ .../WhatDoYouWantToAmendStandardSpec.scala | 64 ------------------- .../WhatDoYouWantToAmendStandardSpec.scala | 49 ++++++++++++++ ...WhatDoYouWantToAmendStandardViewSpec.scala | 7 +- 13 files changed, 156 insertions(+), 176 deletions(-) rename app/forms/{ => amend}/WhatDoYouWantToAmendStandardFormProvider.scala (93%) rename app/models/{ => amend}/WhatDoYouWantToAmendStandard.scala (71%) delete mode 100644 test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala create mode 100644 test/forms/amend/WhatDoYouWantToAmendStandardFormProviderSpec.scala delete mode 100644 test/models/WhatDoYouWantToAmendStandardSpec.scala create mode 100644 test/models/amend/WhatDoYouWantToAmendStandardSpec.scala diff --git a/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala index 8bd10774..12d9adc7 100644 --- a/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala +++ b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala @@ -17,7 +17,7 @@ package controllers.amend import controllers.actions._ -import forms.WhatDoYouWantToAmendStandardFormProvider +import forms.amend.WhatDoYouWantToAmendStandardFormProvider import javax.inject.Inject import models.Mode import navigation.Navigator @@ -30,43 +30,43 @@ import views.html.amend.WhatDoYouWantToAmendStandardView import scala.concurrent.{ExecutionContext, Future} -class WhatDoYouWantToAmendStandardController @Inject()( - override val messagesApi: MessagesApi, - sessionRepository: SessionRepository, - navigator: Navigator, - identify: IdentifierAction, - getData: DataRetrievalAction, - requireData: DataRequiredAction, - formProvider: WhatDoYouWantToAmendStandardFormProvider, - val controllerComponents: MessagesControllerComponents, - view: WhatDoYouWantToAmendStandardView - )(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport { +class WhatDoYouWantToAmendStandardController @Inject() ( + override val messagesApi: MessagesApi, + sessionRepository: SessionRepository, + navigator: Navigator, + identify: IdentifierAction, + getData: DataRetrievalAction, + requireData: DataRequiredAction, + formProvider: WhatDoYouWantToAmendStandardFormProvider, + val controllerComponents: MessagesControllerComponents, + view: WhatDoYouWantToAmendStandardView +)(implicit ec: ExecutionContext) + extends FrontendBaseController + with I18nSupport { val form = formProvider() - def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { - implicit request => + def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request => - val preparedForm = request.userAnswers.get(WhatDoYouWantToAmendStandardPage) match { - case None => form - case Some(value) => form.fill(value) - } + val preparedForm = request.userAnswers.get(WhatDoYouWantToAmendStandardPage) match { + case None => form + case Some(value) => form.fill(value) + } - Ok(view(preparedForm, mode)) + Ok(view(preparedForm, mode)) } def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - - form.bindFromRequest().fold( - formWithErrors => - Future.successful(BadRequest(view(formWithErrors, mode))), - - value => - for { - updatedAnswers <- Future.fromTry(request.userAnswers.set(WhatDoYouWantToAmendStandardPage, value)) - _ <- sessionRepository.set(updatedAnswers) - } yield Redirect(navigator.nextPage(WhatDoYouWantToAmendStandardPage, mode, updatedAnswers)) - ) + form + .bindFromRequest() + .fold( + formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode))), + value => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.set(WhatDoYouWantToAmendStandardPage, value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(navigator.nextPage(WhatDoYouWantToAmendStandardPage, mode, updatedAnswers)) + ) } } diff --git a/app/forms/WhatDoYouWantToAmendStandardFormProvider.scala b/app/forms/amend/WhatDoYouWantToAmendStandardFormProvider.scala similarity index 93% rename from app/forms/WhatDoYouWantToAmendStandardFormProvider.scala rename to app/forms/amend/WhatDoYouWantToAmendStandardFormProvider.scala index de7cbf7d..c23db189 100644 --- a/app/forms/WhatDoYouWantToAmendStandardFormProvider.scala +++ b/app/forms/amend/WhatDoYouWantToAmendStandardFormProvider.scala @@ -14,13 +14,12 @@ * limitations under the License. */ -package forms +package forms.amend import javax.inject.Inject - import forms.mappings.Mappings import play.api.data.Form -import models.WhatDoYouWantToAmendStandard +import models.amend.WhatDoYouWantToAmendStandard class WhatDoYouWantToAmendStandardFormProvider @Inject() extends Mappings { diff --git a/app/models/WhatDoYouWantToAmendStandard.scala b/app/models/amend/WhatDoYouWantToAmendStandard.scala similarity index 71% rename from app/models/WhatDoYouWantToAmendStandard.scala rename to app/models/amend/WhatDoYouWantToAmendStandard.scala index 00c7ba29..d96e15a7 100644 --- a/app/models/WhatDoYouWantToAmendStandard.scala +++ b/app/models/amend/WhatDoYouWantToAmendStandard.scala @@ -14,8 +14,9 @@ * limitations under the License. */ -package models +package models.amend +import models.{Enumerable, WithName} import play.api.i18n.Messages import uk.gov.hmrc.govukfrontend.views.Aliases.Text import uk.gov.hmrc.govukfrontend.views.viewmodels.radios.RadioItem @@ -25,19 +26,21 @@ sealed trait WhatDoYouWantToAmendStandard object WhatDoYouWantToAmendStandard extends Enumerable.Implicits { case object AmendToNilReturn extends WithName("amendToNilReturn") with WhatDoYouWantToAmendStandard - case object AmendPaymentOrSubcontractorDetails extends WithName("amendPaymentOrSubcontractorDetails") with WhatDoYouWantToAmendStandard + case object AmendPaymentOrSubcontractorDetails + extends WithName("amendPaymentOrSubcontractorDetails") + with WhatDoYouWantToAmendStandard val values: Seq[WhatDoYouWantToAmendStandard] = Seq( - AmendToNilReturn, AmendPaymentOrSubcontractorDetails + AmendToNilReturn, + AmendPaymentOrSubcontractorDetails ) - def options(implicit messages: Messages): Seq[RadioItem] = values.zipWithIndex.map { - case (value, index) => - RadioItem( - content = Text(messages(s"amend.whatDoYouWantToAmendStandard.${value.toString}")), - value = Some(value.toString), - id = Some(s"value_$index") - ) + def options(implicit messages: Messages): Seq[RadioItem] = values.zipWithIndex.map { case (value, index) => + RadioItem( + content = Text(messages(s"amend.whatDoYouWantToAmendStandard.${value.toString}")), + value = Some(value.toString), + id = Some(s"value_$index") + ) } implicit val enumerable: Enumerable[WhatDoYouWantToAmendStandard] = diff --git a/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala b/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala index bc0b855f..cc54748f 100644 --- a/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala +++ b/app/pages/amend/WhatDoYouWantToAmendStandardPage.scala @@ -16,7 +16,7 @@ package pages.amend -import models.WhatDoYouWantToAmendStandard +import models.amend.WhatDoYouWantToAmendStandard import pages.QuestionPage import play.api.libs.json.JsPath diff --git a/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala b/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala index 2a1056fa..ac62517c 100644 --- a/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala +++ b/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala @@ -26,25 +26,24 @@ import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow import viewmodels.govuk.summarylist._ import viewmodels.implicits._ -object WhatDoYouWantToAmendStandardSummary { +object WhatDoYouWantToAmendStandardSummary { def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] = - answers.get(WhatDoYouWantToAmendStandardPage).map { - answer => + answers.get(WhatDoYouWantToAmendStandardPage).map { answer => - val value = ValueViewModel( - HtmlContent( - HtmlFormat.escape(messages(s"amend.whatDoYouWantToAmendStandard.$answer")) - ) + val value = ValueViewModel( + HtmlContent( + HtmlFormat.escape(messages(s"amend.whatDoYouWantToAmendStandard.$answer")) ) + ) - SummaryListRowViewModel( - key = "amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel", - value = value, - actions = Seq( - ActionItemViewModel("site.change", routes.WhatDoYouWantToAmendStandardController.onPageLoad(CheckMode).url) - .withVisuallyHiddenText(messages("amend.whatDoYouWantToAmendStandard.change.hidden")) - ) + SummaryListRowViewModel( + key = "amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel", + value = value, + actions = Seq( + ActionItemViewModel("site.change", routes.WhatDoYouWantToAmendStandardController.onPageLoad(CheckMode).url) + .withVisuallyHiddenText(messages("amend.whatDoYouWantToAmendStandard.change.hidden")) ) + ) } } diff --git a/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html index 808fec0e..0eb14e11 100644 --- a/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html +++ b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html @@ -14,6 +14,8 @@ * limitations under the License. *@ +@import models.amend.WhatDoYouWantToAmendStandard + @this( layout: templates.Layout, formHelper: FormWithCSRF, diff --git a/test-utils/generators/ModelGenerators.scala b/test-utils/generators/ModelGenerators.scala index 72b3852c..f394f11d 100644 --- a/test-utils/generators/ModelGenerators.scala +++ b/test-utils/generators/ModelGenerators.scala @@ -17,7 +17,7 @@ package generators import models.amend.WhatDoYouWantToAmendNil -import models.WhatDoYouWantToAmendStandard +import models.amend.WhatDoYouWantToAmendStandard import models.monthlyreturns.{Declaration, InactivityRequest} import org.scalacheck.{Arbitrary, Gen} diff --git a/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala index 930247a1..30a848c3 100644 --- a/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala +++ b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala @@ -17,8 +17,9 @@ package controllers.amend import base.SpecBase -import forms.WhatDoYouWantToAmendStandardFormProvider -import models.{NormalMode, WhatDoYouWantToAmendStandard, UserAnswers} +import forms.amend.WhatDoYouWantToAmendStandardFormProvider +import models.{NormalMode, UserAnswers} +import models.amend.WhatDoYouWantToAmendStandard import navigation.{FakeNavigator, Navigator} import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when @@ -40,7 +41,7 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu lazy val whatDoYouWantToAmendStandardRoute = routes.WhatDoYouWantToAmendStandardController.onPageLoad(NormalMode).url val formProvider = new WhatDoYouWantToAmendStandardFormProvider() - val form = formProvider() + val form = formProvider() "WhatDoYouWantToAmendStandard Controller" - { @@ -62,7 +63,10 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu "must populate the view correctly on a GET when the question has previously been answered" in { - val userAnswers = UserAnswers(userAnswersId).set(WhatDoYouWantToAmendStandardPage, WhatDoYouWantToAmendStandard.values.head).success.value + val userAnswers = UserAnswers(userAnswersId) + .set(WhatDoYouWantToAmendStandardPage, WhatDoYouWantToAmendStandard.values.head) + .success + .value val application = applicationBuilder(userAnswers = Some(userAnswers)).build() @@ -74,7 +78,10 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu val result = route(application, request).value status(result) mustEqual OK - contentAsString(result) mustEqual view(form.fill(WhatDoYouWantToAmendStandard.values.head), NormalMode)(request, messages(application)).toString + contentAsString(result) mustEqual view(form.fill(WhatDoYouWantToAmendStandard.values.head), NormalMode)( + request, + messages(application) + ).toString } } diff --git a/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala b/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala deleted file mode 100644 index 2b25992c..00000000 --- a/test/forms/WhatDoYouWantToAmendStandardFormProviderSpec.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 forms - -import forms.behaviours.OptionFieldBehaviours -import models.WhatDoYouWantToAmendStandard -import play.api.data.FormError - -class WhatDoYouWantToAmendStandardFormProviderSpec extends OptionFieldBehaviours { - - val form = new WhatDoYouWantToAmendStandardFormProvider()() - - ".value" - { - - val fieldName = "value" - val requiredKey = "amend.whatDoYouWantToAmendStandard.error.required" - - behave like optionsField[WhatDoYouWantToAmendStandard]( - form, - fieldName, - validValues = WhatDoYouWantToAmendStandard.values, - invalidError = FormError(fieldName, "error.invalid") - ) - - behave like mandatoryField( - form, - fieldName, - requiredError = FormError(fieldName, requiredKey) - ) - } -} diff --git a/test/forms/amend/WhatDoYouWantToAmendStandardFormProviderSpec.scala b/test/forms/amend/WhatDoYouWantToAmendStandardFormProviderSpec.scala new file mode 100644 index 00000000..f28284d4 --- /dev/null +++ b/test/forms/amend/WhatDoYouWantToAmendStandardFormProviderSpec.scala @@ -0,0 +1,29 @@ +package forms.amend + +import forms.behaviours.OptionFieldBehaviours +import models.amend.WhatDoYouWantToAmendStandard +import play.api.data.FormError + +class WhatDoYouWantToAmendStandardFormProviderSpec extends OptionFieldBehaviours { + + val form = new WhatDoYouWantToAmendStandardFormProvider()() + + ".value" - { + + val fieldName = "value" + val requiredKey = "amend.whatDoYouWantToAmendStandard.error.required" + + behave like optionsField[WhatDoYouWantToAmendStandard]( + form, + fieldName, + validValues = WhatDoYouWantToAmendStandard.values, + invalidError = FormError(fieldName, "error.invalid") + ) + + behave like mandatoryField( + form, + fieldName, + requiredError = FormError(fieldName, requiredKey) + ) + } +} diff --git a/test/models/WhatDoYouWantToAmendStandardSpec.scala b/test/models/WhatDoYouWantToAmendStandardSpec.scala deleted file mode 100644 index cdaecbc8..00000000 --- a/test/models/WhatDoYouWantToAmendStandardSpec.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 models - -import org.scalacheck.Arbitrary.arbitrary -import org.scalacheck.Gen -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import org.scalatest.freespec.AnyFreeSpec -import org.scalatest.matchers.must.Matchers -import org.scalatest.OptionValues -import play.api.libs.json.{JsError, JsString, Json} - -class WhatDoYouWantToAmendStandardSpec extends AnyFreeSpec with Matchers with ScalaCheckPropertyChecks with OptionValues { - - "WhatDoYouWantToAmendStandard" - { - - "must deserialise valid values" in { - - val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) - - forAll(gen) { - whatDoYouWantToAmendStandard => - - JsString(whatDoYouWantToAmendStandard.toString).validate[WhatDoYouWantToAmendStandard].asOpt.value mustEqual whatDoYouWantToAmendStandard - } - } - - "must fail to deserialise invalid values" in { - - val gen = arbitrary[String] suchThat (!WhatDoYouWantToAmendStandard.values.map(_.toString).contains(_)) - - forAll(gen) { - invalidValue => - - JsString(invalidValue).validate[WhatDoYouWantToAmendStandard] mustEqual JsError("error.invalid") - } - } - - "must serialise" in { - - val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) - - forAll(gen) { - whatDoYouWantToAmendStandard => - - Json.toJson(whatDoYouWantToAmendStandard) mustEqual JsString(whatDoYouWantToAmendStandard.toString) - } - } - } -} diff --git a/test/models/amend/WhatDoYouWantToAmendStandardSpec.scala b/test/models/amend/WhatDoYouWantToAmendStandardSpec.scala new file mode 100644 index 00000000..ce0368c4 --- /dev/null +++ b/test/models/amend/WhatDoYouWantToAmendStandardSpec.scala @@ -0,0 +1,49 @@ +package models.amend + +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers +import org.scalatest.OptionValues +import play.api.libs.json.{JsError, JsString, Json} + +class WhatDoYouWantToAmendStandardSpec + extends AnyFreeSpec + with Matchers + with ScalaCheckPropertyChecks + with OptionValues { + + "WhatDoYouWantToAmendStandard" - { + + "must deserialise valid values" in { + + val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) + + forAll(gen) { whatDoYouWantToAmendStandard => + JsString(whatDoYouWantToAmendStandard.toString) + .validate[WhatDoYouWantToAmendStandard] + .asOpt + .value mustEqual whatDoYouWantToAmendStandard + } + } + + "must fail to deserialise invalid values" in { + + val gen = arbitrary[String] suchThat (!WhatDoYouWantToAmendStandard.values.map(_.toString).contains(_)) + + forAll(gen) { invalidValue => + JsString(invalidValue).validate[WhatDoYouWantToAmendStandard] mustEqual JsError("error.invalid") + } + } + + "must serialise" in { + + val gen = Gen.oneOf(WhatDoYouWantToAmendStandard.values.toSeq) + + forAll(gen) { whatDoYouWantToAmendStandard => + Json.toJson(whatDoYouWantToAmendStandard) mustEqual JsString(whatDoYouWantToAmendStandard.toString) + } + } + } +} diff --git a/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala index c67dfd8d..86cfcbc9 100644 --- a/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala +++ b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala @@ -17,8 +17,9 @@ package views.amend import base.SpecBase -import forms.WhatDoYouWantToAmendStandardFormProvider -import models.{NormalMode, WhatDoYouWantToAmendStandard} +import forms.amend.WhatDoYouWantToAmendStandardFormProvider +import models.NormalMode +import models.amend.WhatDoYouWantToAmendStandard import org.jsoup.Jsoup import org.jsoup.nodes.Document import play.api.i18n.Messages @@ -62,7 +63,7 @@ class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { val errorHtml = view(formWithErrors, NormalMode) val doc: Document = Jsoup.parse(errorHtml.toString) - doc.title must startWith(messages("error.title.prefix")) + doc.title must startWith(messages("error.title.prefix")) doc.select(".govuk-error-summary").size() mustBe 1 doc.text() must include(messages("amend.whatDoYouWantToAmendStandard.error.required")) } From 740e8edae1eda7c8a05ee27aa390b2bf1f424a16 Mon Sep 17 00:00:00 2001 From: wg-hmrc Date: Wed, 29 Apr 2026 08:14:27 +0100 Subject: [PATCH 3/4] [DTR-4543] Resolving PR comment --- .../WhatDoYouWantToAmendStandardSummary.scala | 49 ------------------- conf/app.routes | 4 +- conf/messages.en | 2 - 3 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala diff --git a/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala b/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala deleted file mode 100644 index ac62517c..00000000 --- a/app/viewmodels/checkAnswers/amend/WhatDoYouWantToAmendStandardSummary.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 viewmodels.checkAnswers.amend - -import controllers.amend.routes -import models.{CheckMode, UserAnswers} -import pages.amend.WhatDoYouWantToAmendStandardPage -import play.api.i18n.Messages -import play.twirl.api.HtmlFormat -import uk.gov.hmrc.govukfrontend.views.viewmodels.content.HtmlContent -import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow -import viewmodels.govuk.summarylist._ -import viewmodels.implicits._ - -object WhatDoYouWantToAmendStandardSummary { - - def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] = - answers.get(WhatDoYouWantToAmendStandardPage).map { answer => - - val value = ValueViewModel( - HtmlContent( - HtmlFormat.escape(messages(s"amend.whatDoYouWantToAmendStandard.$answer")) - ) - ) - - SummaryListRowViewModel( - key = "amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel", - value = value, - actions = Seq( - ActionItemViewModel("site.change", routes.WhatDoYouWantToAmendStandardController.onPageLoad(CheckMode).url) - .withVisuallyHiddenText(messages("amend.whatDoYouWantToAmendStandard.change.hidden")) - ) - ) - } -} diff --git a/conf/app.routes b/conf/app.routes index 59928457..3ca08b6d 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -175,6 +175,4 @@ GET /manage-cis-return/amend-monthly-return/confirm-amendment controlle POST /manage-cis-return/amend-monthly-return/confirm-amendment controllers.amend.ConfirmAmendmentController.onSubmit() GET /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad(mode: Mode = NormalMode) -POST /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = NormalMode) -GET /amend-monthly-return/change-what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad(mode: Mode = CheckMode) -POST /amend-monthly-return/change-what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = CheckMode) +POST /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = NormalMode) \ No newline at end of file diff --git a/conf/messages.en b/conf/messages.en index 36d8f7d6..9d6a5d31 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -556,6 +556,4 @@ amend.whatDoYouWantToAmendStandard.title = What do you want to amend? amend.whatDoYouWantToAmendStandard.heading = What do you want to amend? amend.whatDoYouWantToAmendStandard.amendToNilReturn = I want to amend this standard return to a nil return amend.whatDoYouWantToAmendStandard.amendPaymentOrSubcontractorDetails = I want to amend payment or subcontractor details on this return -amend.whatDoYouWantToAmendStandard.checkYourAnswersLabel = What do you want to amend? amend.whatDoYouWantToAmendStandard.error.required = Select the type of amendment you want to make -amend.whatDoYouWantToAmendStandard.change.hidden = what do you want to amend? From 01cb6114e3d6f1ec8671438ca18c2253bd513580 Mon Sep 17 00:00:00 2001 From: wg-hmrc Date: Wed, 29 Apr 2026 09:48:39 +0100 Subject: [PATCH 4/4] [DTR-4543] fixing unit test --- ...atDoYouWantToAmendStandardController.scala | 29 +++++++++---------- ...hatDoYouWantToAmendStandardView.scala.html | 4 +-- conf/app.routes | 4 +-- ...YouWantToAmendStandardControllerSpec.scala | 10 +++---- ...WhatDoYouWantToAmendStandardViewSpec.scala | 9 +++--- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala index 12d9adc7..9fda39b7 100644 --- a/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala +++ b/app/controllers/amend/WhatDoYouWantToAmendStandardController.scala @@ -19,7 +19,7 @@ package controllers.amend import controllers.actions._ import forms.amend.WhatDoYouWantToAmendStandardFormProvider import javax.inject.Inject -import models.Mode +import models.NormalMode import navigation.Navigator import pages.amend.WhatDoYouWantToAmendStandardPage import play.api.i18n.{I18nSupport, MessagesApi} @@ -46,27 +46,26 @@ class WhatDoYouWantToAmendStandardController @Inject() ( val form = formProvider() - def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request => + def onPageLoad(): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request => val preparedForm = request.userAnswers.get(WhatDoYouWantToAmendStandardPage) match { case None => form case Some(value) => form.fill(value) } - Ok(view(preparedForm, mode)) + Ok(view(preparedForm)) } - def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async { - implicit request => - form - .bindFromRequest() - .fold( - formWithErrors => Future.successful(BadRequest(view(formWithErrors, mode))), - value => - for { - updatedAnswers <- Future.fromTry(request.userAnswers.set(WhatDoYouWantToAmendStandardPage, value)) - _ <- sessionRepository.set(updatedAnswers) - } yield Redirect(navigator.nextPage(WhatDoYouWantToAmendStandardPage, mode, updatedAnswers)) - ) + def onSubmit(): Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => + form + .bindFromRequest() + .fold( + formWithErrors => Future.successful(BadRequest(view(formWithErrors))), + value => + for { + updatedAnswers <- Future.fromTry(request.userAnswers.set(WhatDoYouWantToAmendStandardPage, value)) + _ <- sessionRepository.set(updatedAnswers) + } yield Redirect(navigator.nextPage(WhatDoYouWantToAmendStandardPage, NormalMode, updatedAnswers)) + ) } } diff --git a/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html index 0eb14e11..725bf697 100644 --- a/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html +++ b/app/views/amend/WhatDoYouWantToAmendStandardView.scala.html @@ -24,11 +24,11 @@ govukButton: GovukButton ) -@(form: Form[_], mode: Mode)(implicit request: Request[_], messages: Messages) +@(form: Form[_])(implicit request: Request[_], messages: Messages) @layout(pageTitle = title(form, messages("amend.whatDoYouWantToAmendStandard.title"))) { - @formHelper(action = controllers.amend.routes.WhatDoYouWantToAmendStandardController.onSubmit(mode), Symbol("autoComplete") -> "off") { + @formHelper(action = controllers.amend.routes.WhatDoYouWantToAmendStandardController.onSubmit(), Symbol("autoComplete") -> "off") { @if(form.errors.nonEmpty) { @govukErrorSummary(ErrorSummaryViewModel(form, errorLinkOverrides = Map("value" -> "value_0"))) diff --git a/conf/app.routes b/conf/app.routes index 3ca08b6d..407a46f4 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -174,5 +174,5 @@ POST /amend-monthly-return/what-do-you-want-to-amend-nil GET /manage-cis-return/amend-monthly-return/confirm-amendment controllers.amend.ConfirmAmendmentController.onPageLoad() POST /manage-cis-return/amend-monthly-return/confirm-amendment controllers.amend.ConfirmAmendmentController.onSubmit() -GET /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad(mode: Mode = NormalMode) -POST /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit(mode: Mode = NormalMode) \ No newline at end of file +GET /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onPageLoad() +POST /amend-monthly-return/what-do-you-want-to-amend-standard controllers.amend.WhatDoYouWantToAmendStandardController.onSubmit() diff --git a/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala index 30a848c3..3e4bd32a 100644 --- a/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala +++ b/test/controllers/amend/WhatDoYouWantToAmendStandardControllerSpec.scala @@ -18,7 +18,7 @@ package controllers.amend import base.SpecBase import forms.amend.WhatDoYouWantToAmendStandardFormProvider -import models.{NormalMode, UserAnswers} +import models.UserAnswers import models.amend.WhatDoYouWantToAmendStandard import navigation.{FakeNavigator, Navigator} import org.mockito.ArgumentMatchers.any @@ -38,7 +38,7 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu def onwardRoute = Call("GET", "/foo") - lazy val whatDoYouWantToAmendStandardRoute = routes.WhatDoYouWantToAmendStandardController.onPageLoad(NormalMode).url + lazy val whatDoYouWantToAmendStandardRoute = routes.WhatDoYouWantToAmendStandardController.onPageLoad().url val formProvider = new WhatDoYouWantToAmendStandardFormProvider() val form = formProvider() @@ -57,7 +57,7 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu val view = application.injector.instanceOf[WhatDoYouWantToAmendStandardView] status(result) mustEqual OK - contentAsString(result) mustEqual view(form, NormalMode)(request, messages(application)).toString + contentAsString(result) mustEqual view(form)(request, messages(application)).toString } } @@ -78,7 +78,7 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu val result = route(application, request).value status(result) mustEqual OK - contentAsString(result) mustEqual view(form.fill(WhatDoYouWantToAmendStandard.values.head), NormalMode)( + contentAsString(result) mustEqual view(form.fill(WhatDoYouWantToAmendStandard.values.head))( request, messages(application) ).toString @@ -127,7 +127,7 @@ class WhatDoYouWantToAmendStandardControllerSpec extends SpecBase with MockitoSu val result = route(application, request).value status(result) mustEqual BAD_REQUEST - contentAsString(result) mustEqual view(boundForm, NormalMode)(request, messages(application)).toString + contentAsString(result) mustEqual view(boundForm)(request, messages(application)).toString } } diff --git a/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala index 86cfcbc9..f3aee9bf 100644 --- a/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala +++ b/test/views/amend/WhatDoYouWantToAmendStandardViewSpec.scala @@ -18,7 +18,6 @@ package views.amend import base.SpecBase import forms.amend.WhatDoYouWantToAmendStandardFormProvider -import models.NormalMode import models.amend.WhatDoYouWantToAmendStandard import org.jsoup.Jsoup import org.jsoup.nodes.Document @@ -51,7 +50,7 @@ class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { "must pre-populate the form when user has previously answered" in new Setup { val filledForm = form.fill(WhatDoYouWantToAmendStandard.AmendToNilReturn) - val filledHtml = view(filledForm, NormalMode) + val filledHtml = view(filledForm) val doc: Document = Jsoup.parse(filledHtml.toString) doc.select("input[value=amendToNilReturn]").hasAttr("checked") mustBe true @@ -60,7 +59,7 @@ class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { "must show error summary when form has errors" in new Setup { val formWithErrors = form.bind(Map("value" -> "")) - val errorHtml = view(formWithErrors, NormalMode) + val errorHtml = view(formWithErrors) val doc: Document = Jsoup.parse(errorHtml.toString) doc.title must startWith(messages("error.title.prefix")) @@ -70,7 +69,7 @@ class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { "must render error summary with correct link when form has errors" in new Setup { val formWithErrors = form.bind(Map("value" -> "")) - val errorHtml = view(formWithErrors, NormalMode) + val errorHtml = view(formWithErrors) val doc: Document = Jsoup.parse(errorHtml.toString) doc.select(".govuk-error-summary__list a").attr("href") mustBe "#value_0" @@ -93,6 +92,6 @@ class WhatDoYouWantToAmendStandardViewSpec extends SpecBase { app.injector.instanceOf[play.api.i18n.MessagesApi] ) - val html = view(form, NormalMode) + val html = view(form) } }