├── .dockerignore
├── .erb_lint.yml
├── .github
├── dependabot.yml
├── pull_request_template.md
└── workflows
│ ├── actionlint.yml
│ ├── ci.yml
│ ├── copy-pr-template-to-dependabot-prs.yml
│ ├── deploy.yml
│ ├── jasmine.yml
│ ├── pact-verify.yml
│ ├── release.yml
│ └── rspec.yml
├── .gitignore
├── .govuk_dependabot_merger.yml
├── .nvmrc
├── .nycrc
├── .rspec
├── .rubocop.yml
├── .ruby-version
├── .yarn
└── releases
│ └── yarn-3.5.0.cjs
├── .yarnrc.yml
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── LICENCE.txt
├── Procfile.dev
├── README.md
├── Rakefile
├── app.json
├── app
├── assets
│ ├── builds
│ │ └── .keep
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ ├── calendars
│ │ │ ├── bunting-grey-string.svg
│ │ │ └── festive-lights-tinsel.svg
│ │ ├── campaign
│ │ │ ├── britain_is_great
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── custom-logos
│ │ │ │ └── environment-agency.png
│ │ │ ├── dvla-new-licence-rules
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ ├── 960px.jpg
│ │ │ │ └── INF45x3W.pdf
│ │ │ ├── dwp-workplace-pensions
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── fire-kills
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── know-before-you-go
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── royal_mail_shares
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── sort-my-tax
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ │ ├── uk-welcomes
│ │ │ │ └── eugo-logo.png
│ │ │ └── unimoney
│ │ │ │ ├── 320px.jpg
│ │ │ │ ├── 768px.jpg
│ │ │ │ └── 960px.jpg
│ │ ├── components
│ │ │ └── download-link
│ │ │ │ ├── calendar-icon.svg
│ │ │ │ └── download-icon.svg
│ │ ├── getsatisfaction.jpg
│ │ ├── homepage
│ │ │ ├── cost-of-living-featured.png
│ │ │ ├── find-a-job.png
│ │ │ └── national-insurance-featured.png
│ │ ├── landing_page
│ │ │ ├── logo-nhs.svg
│ │ │ └── placeholder
│ │ │ │ ├── 960x640.png
│ │ │ │ ├── chart.png
│ │ │ │ ├── desktop.png
│ │ │ │ ├── desktop_2x.png
│ │ │ │ ├── landing_page_image.png
│ │ │ │ ├── missions_featured_desktop.jpg
│ │ │ │ ├── missions_featured_desktop_2x.jpg
│ │ │ │ ├── missions_featured_mobile.jpg
│ │ │ │ ├── missions_featured_mobile_2x.jpg
│ │ │ │ ├── missions_featured_tablet.jpg
│ │ │ │ ├── missions_featured_tablet_2x.jpg
│ │ │ │ ├── missions_hero_desktop.jpg
│ │ │ │ ├── missions_hero_desktop_2x.jpg
│ │ │ │ ├── missions_hero_mobile.jpg
│ │ │ │ ├── missions_hero_mobile_2x.jpg
│ │ │ │ ├── missions_hero_tablet.jpg
│ │ │ │ ├── missions_hero_tablet_2x.jpg
│ │ │ │ ├── mobile.png
│ │ │ │ ├── mobile_2x.png
│ │ │ │ ├── tablet.png
│ │ │ │ └── tablet_2x.png
│ │ ├── ministry-of-defence-crest.png
│ │ ├── roadmap
│ │ │ ├── image.png
│ │ │ ├── updates-blog.jpg
│ │ │ ├── updates-goals.jpg
│ │ │ └── updates-work.jpg
│ │ ├── specialist-documents
│ │ │ └── protected-food-drink-names
│ │ │ │ ├── protected-designation-of-origin-pdo.png
│ │ │ │ ├── protected-geographical-indication-pgi.png
│ │ │ │ └── traditional-speciality-guaranteed-tsg.png
│ │ ├── start-pages
│ │ │ └── make-a-sorn
│ │ │ │ ├── performance-icon-2x.png
│ │ │ │ └── performance-icon.png
│ │ ├── templogo.png
│ │ └── travel-advice
│ │ │ ├── feed-icon-black.png
│ │ │ ├── mail-icon-x2.png
│ │ │ └── mail-icon.png
│ ├── javascripts
│ │ ├── application.js
│ │ ├── dependencies.js
│ │ ├── main.js
│ │ ├── modules
│ │ │ ├── main-navigation.js
│ │ │ ├── sticky-element-container.js
│ │ │ └── transaction-survey-form.js
│ │ ├── static-error-pages.js
│ │ ├── support.js
│ │ ├── test-dependencies.js
│ │ └── views
│ │ │ └── travel-advice.js
│ └── stylesheets
│ │ ├── application.scss
│ │ ├── components
│ │ ├── _calendar.scss
│ │ └── _download-link.scss
│ │ ├── helpers
│ │ ├── _content-bottom-margin.scss
│ │ ├── _inverse-background.scss
│ │ ├── _parts.scss
│ │ ├── _sticky-element-container.scss
│ │ └── _truncated-url.scss
│ │ ├── mixins
│ │ └── _margins.scss
│ │ ├── static-error-pages.scss
│ │ └── views
│ │ ├── _calendars.scss
│ │ ├── _cookie-settings.scss
│ │ ├── _csv_preview.scss
│ │ ├── _homepage.scss
│ │ ├── _homepage_header.scss
│ │ ├── _homepage_more_on_govuk.scss
│ │ ├── _landing_page.scss
│ │ ├── _landing_page
│ │ ├── block-error.scss
│ │ ├── box.scss
│ │ ├── card.scss
│ │ ├── columns_layout.scss
│ │ ├── featured.scss
│ │ ├── helpers
│ │ │ ├── _backgrounds.scss
│ │ │ ├── _borders.scss
│ │ │ └── _colours.scss
│ │ ├── hero.scss
│ │ ├── image.scss
│ │ ├── logo.scss
│ │ ├── main-navigation.scss
│ │ ├── quote.scss
│ │ ├── side-navigation.scss
│ │ └── themes
│ │ │ └── prime-ministers-office-10-downing-street.scss
│ │ ├── _local-transaction.scss
│ │ ├── _location_form.scss
│ │ ├── _place-list.scss
│ │ ├── _popular_links.scss
│ │ ├── _published-dates-button-group.scss
│ │ ├── _publisher_metadata.scss
│ │ ├── _roadmap.scss
│ │ ├── _service-toolkit.scss
│ │ ├── _sidebar-navigation.scss
│ │ ├── _specialist-document.scss
│ │ └── _travel-advice.scss
├── controllers
│ ├── account_home_controller.rb
│ ├── answer_controller.rb
│ ├── api
│ │ └── local_authority_controller.rb
│ ├── application_controller.rb
│ ├── calendar_controller.rb
│ ├── case_study_controller.rb
│ ├── concerns
│ │ ├── bank_hol_ab_testable.rb
│ │ ├── cacheable.rb
│ │ ├── previewable.rb
│ │ └── split_postcode_support.rb
│ ├── content_items_controller.rb
│ ├── corporate_information_page_controller.rb
│ ├── csv_preview_controller.rb
│ ├── development_controller.rb
│ ├── electoral_controller.rb
│ ├── error_controller.rb
│ ├── fatality_notice_controller.rb
│ ├── favicon_controller.rb
│ ├── field_of_operation_controller.rb
│ ├── fields_of_operation_controller.rb
│ ├── find_local_council_controller.rb
│ ├── flexible_page_controller.rb
│ ├── get_involved_controller.rb
│ ├── help_controller.rb
│ ├── help_page_controller.rb
│ ├── homepage_controller.rb
│ ├── landing_page_controller.rb
│ ├── licence_transaction_controller.rb
│ ├── local_transaction_controller.rb
│ ├── news_article_controller.rb
│ ├── place_controller.rb
│ ├── placeholder_controller.rb
│ ├── random_controller.rb
│ ├── roadmap_controller.rb
│ ├── service_manual_controller.rb
│ ├── service_toolkit_controller.rb
│ ├── sessions_controller.rb
│ ├── simple_smart_answers_controller.rb
│ ├── specialist_document_controller.rb
│ ├── speech_controller.rb
│ ├── static_error_pages_controller.rb
│ ├── take_part_controller.rb
│ ├── transaction_controller.rb
│ └── travel_advice_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── block_helper.rb
│ ├── calendar_helper.rb
│ ├── contents_list_helper.rb
│ ├── currency_helper.rb
│ ├── date_helper.rb
│ ├── draft_helper.rb
│ ├── error_items_helper.rb
│ ├── govuk_personalisation_helper.rb
│ ├── link_helper.rb
│ ├── locale_helper.rb
│ ├── location_form_helper.rb
│ ├── parts_navigation_helper.rb
│ ├── phone_number_helper.rb
│ ├── theme_type_helper.rb
│ ├── travel_advice_helper.rb
│ └── url_helper.rb
├── models
│ ├── calendar.rb
│ ├── calendar
│ │ ├── division.rb
│ │ ├── event.rb
│ │ └── year.rb
│ ├── case_study.rb
│ ├── concerns
│ │ ├── emphasised_organisations.rb
│ │ ├── news_image.rb
│ │ ├── parts.rb
│ │ ├── people.rb
│ │ ├── political.rb
│ │ ├── single_page_notification_button.rb
│ │ ├── updatable.rb
│ │ ├── withdrawable.rb
│ │ └── worldwide_organisations.rb
│ ├── content_item.rb
│ ├── content_item_factory.rb
│ ├── contents_outline.rb
│ ├── corporate_information_page.rb
│ ├── detailed_guide.rb
│ ├── election_postcode.rb
│ ├── fatality_notice.rb
│ ├── field_of_operation.rb
│ ├── fields_of_operation.rb
│ ├── finder.rb
│ ├── flexible_page.rb
│ ├── flexible_page
│ │ ├── flexible_section
│ │ │ ├── base.rb
│ │ │ ├── page_title.rb
│ │ │ └── rich_content.rb
│ │ └── flexible_section_factory.rb
│ ├── get_involved.rb
│ ├── homepage.rb
│ ├── landing_page.rb
│ ├── landing_page
│ │ ├── block
│ │ │ ├── action_link.rb
│ │ │ ├── base.rb
│ │ │ ├── block_error.rb
│ │ │ ├── blocks_container.rb
│ │ │ ├── box.rb
│ │ │ ├── card.rb
│ │ │ ├── columns_layout.rb
│ │ │ ├── document_list.rb
│ │ │ ├── featured.rb
│ │ │ ├── govspeak.rb
│ │ │ ├── heading.rb
│ │ │ ├── hero.rb
│ │ │ ├── image.rb
│ │ │ ├── layout_base.rb
│ │ │ ├── logo.rb
│ │ │ ├── main_navigation.rb
│ │ │ ├── quote.rb
│ │ │ ├── share_links.rb
│ │ │ ├── side_navigation.rb
│ │ │ └── two_column_layout.rb
│ │ └── block_factory.rb
│ ├── licence_transaction.rb
│ ├── local_authority.rb
│ ├── local_transaction.rb
│ ├── location_error.rb
│ ├── locations_api_postcode_response.rb
│ ├── logo.rb
│ ├── news_article.rb
│ ├── organisation.rb
│ ├── place.rb
│ ├── places_manager_response.rb
│ ├── postcode_lookup.rb
│ ├── record_not_found.rb
│ ├── service_manual_homepage.rb
│ ├── service_manual_service_toolkit.rb
│ ├── simple_smart_answer.rb
│ ├── specialist_document.rb
│ ├── speech.rb
│ ├── transaction.rb
│ ├── travel_advice.rb
│ └── uprn.rb
├── presenters
│ ├── address_list_presenter.rb
│ ├── content_item_presenter.rb
│ ├── contents_outline_presenter.rb
│ ├── corporate_information_page_presenter.rb
│ ├── electoral_presenter.rb
│ ├── faq_presenter.rb
│ ├── field_of_operation_presenter.rb
│ ├── get_involved_presenter.rb
│ ├── licence_details_presenter.rb
│ ├── local_authority_presenter.rb
│ ├── place_presenter.rb
│ ├── service_manual_homepage_presenter.rb
│ ├── simple_smart_answer_presenter.rb
│ ├── specialist_document_presenter.rb
│ ├── speech_presenter.rb
│ ├── transaction_presenter.rb
│ ├── travel_advice_index_presenter.rb
│ └── travel_advice_presenter.rb
├── queries
│ └── graphql
│ │ └── edition_query.rb
├── services
│ ├── csv_preview_service.rb
│ └── electoral_service.rb
└── views
│ ├── answer
│ └── show.html.erb
│ ├── application
│ ├── _draft_fields.html.erb
│ └── _location_form.html.erb
│ ├── calendar
│ ├── _calendar_footer.html.erb
│ ├── _calendar_head.html.erb
│ ├── bank_holidays.html.erb
│ └── when_do_the_clocks_change.html.erb
│ ├── case_study
│ └── show.html.erb
│ ├── components
│ ├── _calendar.html.erb
│ ├── _download_link.html.erb
│ └── docs
│ │ ├── calendar.yml
│ │ └── download_link.yml
│ ├── corporate_information_page
│ └── show.html.erb
│ ├── csv_preview
│ ├── access_limited.html.erb
│ ├── malformed_csv.html.erb
│ └── show.html.erb
│ ├── development
│ └── index.html.erb
│ ├── electoral
│ ├── _contact_details.html.erb
│ ├── address_picker.html.erb
│ └── results.html.erb
│ ├── fatality_notice
│ └── show.html.erb
│ ├── field_of_operation
│ └── show.html.erb
│ ├── fields_of_operation
│ └── index.html.erb
│ ├── find_local_council
│ ├── _base_page.html.erb
│ ├── district_and_county_council.html.erb
│ ├── index.html.erb
│ ├── multiple_authorities.html.erb
│ └── one_council.html.erb
│ ├── flexible_page
│ ├── flexible_sections
│ │ ├── _page_title.html.erb
│ │ └── _rich_content.html.erb
│ └── show.html.erb
│ ├── get_involved
│ └── show.html.erb
│ ├── help
│ ├── ab_testing.html.erb
│ ├── cookie_settings.html.erb
│ ├── index.html.erb
│ └── sign_in.html.erb
│ ├── help_page
│ └── show.html.erb
│ ├── homepage
│ ├── _government_activity.html.erb
│ ├── _homepage_header.html.erb
│ ├── _more_on_govuk.html.erb
│ ├── _popular_links.html.erb
│ ├── _promotion_slots.html.erb
│ ├── _services_and_information.html.erb
│ └── index.html.erb
│ ├── landing_page
│ ├── blocks
│ │ ├── _action_link.html.erb
│ │ ├── _block_error.html.erb
│ │ ├── _blocks_container.html.erb
│ │ ├── _box.html.erb
│ │ ├── _card.html.erb
│ │ ├── _columns_layout.html.erb
│ │ ├── _document_list.html.erb
│ │ ├── _featured.html.erb
│ │ ├── _govspeak.html.erb
│ │ ├── _heading.html.erb
│ │ ├── _hero.html.erb
│ │ ├── _image.html.erb
│ │ ├── _logo.html.erb
│ │ ├── _main_navigation.html.erb
│ │ ├── _quote.html.erb
│ │ ├── _share_links.html.erb
│ │ ├── _side_navigation.html.erb
│ │ └── _two_column_layout.html.erb
│ ├── show.html.erb
│ └── themes
│ │ ├── _default.html.erb
│ │ └── _prime-ministers-office-10-downing-street.html.erb
│ ├── layouts
│ └── application.html.erb
│ ├── licence_transaction
│ ├── _authority_base.html.erb
│ ├── _authority_url.html.erb
│ ├── _licensify_unavailable.html.erb
│ ├── authority.html.erb
│ ├── authority_interaction.html.erb
│ ├── continues_on.html.erb
│ ├── licence_not_found.html.erb
│ ├── multiple_authorities.html.erb
│ └── start.html.erb
│ ├── local_transaction
│ ├── _no_local_authority_url.html.erb
│ ├── devolved_administration_service.html.erb
│ ├── index.html.erb
│ ├── multiple_authorities.html.erb
│ ├── results.html.erb
│ └── unavailable_service.html.erb
│ ├── news_article
│ └── show.html.erb
│ ├── place
│ ├── _place.html.erb
│ ├── multiple_authorities.html.erb
│ └── show.html.erb
│ ├── placeholder
│ └── show.html.erb
│ ├── roadmap
│ ├── _image.html.erb
│ └── index.html.erb
│ ├── service_manual
│ └── index.html.erb
│ ├── service_toolkit
│ └── index.html.erb
│ ├── shared
│ ├── _base_page.html.erb
│ ├── _body_with_related_links.html.erb
│ ├── _email_subscribe_unsubscribe_flash.html.erb
│ ├── _footer_navigation.html.erb
│ ├── _history_notice.html.erb
│ ├── _publication_metadata.html.erb
│ ├── _published_dates_with_notification_button.html.erb
│ ├── _publisher_metadata.html.erb
│ ├── _sidebar_navigation.html.erb
│ ├── _single_page_notification_button.html.erb
│ └── _translations.html.erb
│ ├── simple_smart_answers
│ ├── _current_question.html.erb
│ ├── _outcome.html.erb
│ ├── flow.html.erb
│ └── show.html.erb
│ ├── specialist_document
│ └── show.html.erb
│ ├── speech
│ └── show.html.erb
│ ├── static_error_pages
│ ├── 400.html.erb
│ ├── 401.html.erb
│ ├── 403.html.erb
│ ├── 404.html.erb
│ ├── 405.html.erb
│ ├── 406.html.erb
│ ├── 410.html.erb
│ ├── 422.html.erb
│ ├── 429.html.erb
│ ├── 500.html.erb
│ ├── 501.html.erb
│ ├── 502.html.erb
│ ├── 503.html.erb
│ ├── 504.html.erb
│ └── _error_page.html.erb
│ ├── take_part
│ └── show.html.erb
│ ├── transaction
│ ├── _additional_information_single.html.erb
│ ├── _additional_information_tabbed.html.erb
│ └── show.html.erb
│ └── travel_advice
│ ├── _country.atom.builder
│ ├── _first_part.html.erb
│ ├── index.atom.builder
│ ├── index.html.erb
│ ├── show.atom.builder
│ ├── show.html+print.erb
│ └── show.html.erb
├── bin
├── brakeman
├── bundle
├── dev
├── rails
├── rake
├── rubocop
├── setup
├── update
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── brakeman.ignore
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── govuk_examples.yml
├── i18n-tasks.yml
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── dartsass.rb
│ ├── date_formats.rb
│ ├── filter_parameter_logging.rb
│ ├── govuk_publishing_components.rb
│ ├── inflections.rb
│ ├── local_links_manager.rb
│ ├── locations_api.rb
│ ├── mime_types.rb
│ ├── permissions_policy.rb
│ ├── places_manager.rb
│ ├── prometheus.rb
│ ├── session_store.rb
│ ├── slimmer.rb
│ ├── website_root.rb
│ └── wrap_parameters.rb
├── locales
│ ├── ar.yml
│ ├── az.yml
│ ├── be.yml
│ ├── bg.yml
│ ├── bn.yml
│ ├── cs.yml
│ ├── cy.yml
│ ├── da.yml
│ ├── de.yml
│ ├── dr.yml
│ ├── el.yml
│ ├── en.yml
│ ├── es-419.yml
│ ├── es.yml
│ ├── et.yml
│ ├── fa.yml
│ ├── fi.yml
│ ├── fr.yml
│ ├── gd.yml
│ ├── gu.yml
│ ├── he.yml
│ ├── hi.yml
│ ├── hr.yml
│ ├── hu.yml
│ ├── hy.yml
│ ├── id.yml
│ ├── is.yml
│ ├── it.yml
│ ├── ja.yml
│ ├── ka.yml
│ ├── kk.yml
│ ├── ko.yml
│ ├── ky.yml
│ ├── lt.yml
│ ├── lv.yml
│ ├── ms.yml
│ ├── mt.yml
│ ├── ne.yml
│ ├── nl.yml
│ ├── no.yml
│ ├── pa-pk.yml
│ ├── pa.yml
│ ├── pl.yml
│ ├── ps.yml
│ ├── pt.yml
│ ├── ro.yml
│ ├── ru.yml
│ ├── si.yml
│ ├── sk.yml
│ ├── sl.yml
│ ├── so.yml
│ ├── sq.yml
│ ├── sr.yml
│ ├── sv.yml
│ ├── sw.yml
│ ├── ta.yml
│ ├── th.yml
│ ├── tk.yml
│ ├── tr.yml
│ ├── uk.yml
│ ├── ur.yml
│ ├── uz.yml
│ ├── vi.yml
│ ├── yi.yml
│ ├── zh-hk.yml
│ ├── zh-tw.yml
│ └── zh.yml
├── puma.rb
├── routes.rb
├── secrets.yml
└── sidekiq.yml
├── docs
├── adr
│ ├── 0001-render-csv-previews-in-frontend.md
│ └── 0002-flexible-landing-pages.md
├── building_blocks_for_flexible_content.md
├── calendars.md
├── elections-api.md
├── images
│ └── homepage-promotion-slots.png
├── local-authorities-api.md
├── local-content-items.md
└── update-homepage-promotion-slots.md
├── lib
├── api_error_routing_constraint.rb
├── content_item_loader.rb
├── data
│ ├── bank-holidays.json
│ ├── local-content-items
│ │ └── test
│ │ │ └── flexible-page.yml
│ ├── specialist_documents
│ │ └── protected_food_drink_name
│ │ │ └── images.yaml
│ └── when-do-the-clocks-change.json
├── format_routing_constraint.rb
├── frontend.rb
├── full_path_format_routing_constraint.rb
├── ics_renderer.rb
├── postcode_sanitizer.rb
├── sanitiser
│ └── strategy.rb
├── simple_smart_answers
│ ├── base_error.rb
│ ├── flow.rb
│ ├── invalid_response.rb
│ ├── node.rb
│ └── state.rb
└── tasks
│ ├── .gitkeep
│ ├── consolidation.rake
│ ├── jasmine.rake
│ └── lint.rake
├── log
└── .gitkeep
├── package.json
├── spec
├── components
│ ├── all_components_spec.rb
│ ├── calendar_spec.rb
│ └── download_link_spec.rb
├── factories
│ ├── content_items.rb
│ └── landing_pages.rb
├── fixtures
│ ├── bank-holidays.json
│ ├── electoral-result.json
│ ├── graphql
│ │ ├── best-practice-event.json
│ │ ├── best-practice-government-response.json
│ │ ├── best-practice-news-story.json
│ │ ├── best-practice-press-release.json
│ │ ├── news_article.json
│ │ ├── news_article_government_response.json
│ │ ├── news_article_history_mode.json
│ │ ├── news_article_history_mode_translated_arabic.json
│ │ ├── news_article_news_story_translated_arabic.json
│ │ ├── news_article_press_release.json
│ │ ├── news_article_with_image_caption.json
│ │ ├── news_article_with_taxons.json
│ │ ├── news_article_withdrawn.json
│ │ ├── translated_news_article_with_taxon.json
│ │ └── world_news_story_news_article.json
│ ├── landing_page_statistics_data
│ │ ├── data_four.csv
│ │ ├── data_one.csv
│ │ ├── data_three.csv
│ │ └── data_two.csv
│ ├── local-content-items
│ │ ├── flexible-page.yml
│ │ ├── my-json-item.json
│ │ └── my-yaml-item.yml
│ ├── single-calendar.json
│ └── when-do-the-clocks-change.json
├── helpers
│ ├── application_helper_spec.rb
│ ├── block_helper_spec.rb
│ ├── contents_list_helper_spec.rb
│ ├── currency_helper_spec.rb
│ ├── date_helper_spec.rb
│ ├── error_items_helper_spec.rb
│ ├── govuk_personalisation_helper_spec.rb
│ ├── link_helper_spec.rb
│ ├── locale_helper_spec.rb
│ ├── parts_navigation_helper_spec.rb
│ ├── phone_number_helper_spec.rb
│ └── theme_type_helper_spec.rb
├── javascripts
│ ├── helpers
│ │ ├── .gitkeep
│ │ └── nyc-test-coverage-helper.mjs
│ ├── reporters
│ │ └── nyc-test-coverage-reporter.mjs
│ ├── unit
│ │ ├── foreign-travel-advice.spec.js
│ │ ├── modules
│ │ │ ├── main-navigation.spec.js
│ │ │ ├── sticky-element-container.spec.js
│ │ │ └── transaction-survey-form.spec.js
│ │ └── support.spec.js
│ └── vendor
│ │ └── jquery-1.12.4.js
├── models
│ ├── calendar
│ │ ├── division_spec.rb
│ │ ├── event_spec.rb
│ │ └── year_spec.rb
│ ├── calendar_spec.rb
│ ├── case_study_spec.rb
│ ├── content_item_factory_spec.rb
│ ├── content_item_spec.rb
│ ├── contents_outline_spec.rb
│ ├── corporate_information_page_spec.rb
│ ├── detailed_guide_spec.rb
│ ├── election_postcode_spec.rb
│ ├── fatality_notice_spec.rb
│ ├── field_of_operation_spec.rb
│ ├── fields_of_operation_spec.rb
│ ├── finder_spec.rb
│ ├── flexible_page
│ │ ├── flexible_section
│ │ │ ├── page_title_spec.rb
│ │ │ └── rich_content_spec.rb
│ │ └── flexible_section_factory_spec.rb
│ ├── get_involved_spec.rb
│ ├── homepage_spec.rb
│ ├── landing_page
│ │ ├── block
│ │ │ ├── action_link_spec.rb
│ │ │ ├── base_spec.rb
│ │ │ ├── block_error_spec.rb
│ │ │ ├── blocks_container_spec.rb
│ │ │ ├── box_spec.rb
│ │ │ ├── card_spec.rb
│ │ │ ├── columns_layout_spec.rb
│ │ │ ├── document_list_spec.rb
│ │ │ ├── featured_spec.rb
│ │ │ ├── govspeak_spec.rb
│ │ │ ├── heading_spec.rb
│ │ │ ├── hero_spec.rb
│ │ │ ├── image_spec.rb
│ │ │ ├── layout_base_spec.rb
│ │ │ ├── logo_spec.rb
│ │ │ ├── main_navigation_spec.rb
│ │ │ ├── quote_spec.rb
│ │ │ ├── share_links_spec.rb
│ │ │ ├── side_navigation_spec.rb
│ │ │ └── two_column_layout_spec.rb
│ │ └── block_factory_spec.rb
│ ├── landing_page_spec.rb
│ ├── licence_transaction_spec.rb
│ ├── local_authority_spec.rb
│ ├── local_transaction_spec.rb
│ ├── location_error_spec.rb
│ ├── logo_spec.rb
│ ├── news_article_spec.rb
│ ├── organisation_spec.rb
│ ├── place_spec.rb
│ ├── service_manual_homepage_spec.rb
│ ├── service_manual_service_toolkit_spec.rb
│ ├── simple_smart_answer_spec.rb
│ ├── specialist_document_spec.rb
│ ├── speech_spec.rb
│ ├── transaction_spec.rb
│ ├── travel_advice_spec.rb
│ └── uprn_spec.rb
├── presenter
│ ├── address_list_presenter_spec.rb
│ ├── content_item_presenter_spec.rb
│ ├── contents_outline_presenter_spec.rb
│ ├── corporate_information_page_presenter_spec.rb
│ ├── electoral_presenter_spec.rb
│ ├── faq_presenter_spec.rb
│ ├── field_of_operation_presenter_spec.rb
│ ├── get_involved_spec.rb
│ ├── licence_details_presenter_spec.rb
│ ├── local_authority_presenter_spec.rb
│ ├── place_presenter_spec.rb
│ ├── service_manual_homepage_presenter_spec.rb
│ ├── simple_smart_answer_presenter_spec.rb
│ ├── specialist_document_presenter_spec.rb
│ ├── speech_presenter_spec.rb
│ ├── transaction_presenter_spec.rb
│ ├── travel_advice_index_presenter_spec.rb
│ └── travel_advice_presenter_spec.rb
├── requests
│ ├── answer_spec.rb
│ ├── calendars_spec.rb
│ ├── case_study_spec.rb
│ ├── contact_electoral_registration_office_spec.rb
│ ├── content_items_controller_spec.rb
│ ├── content_loading_problems_spec.rb
│ ├── corporate_information_page_spec.rb
│ ├── csv_preview_spec.rb
│ ├── fatality_notice_spec.rb
│ ├── favicon_spec.rb
│ ├── field_of_operation_spec.rb
│ ├── fields_of_operation_spec.rb
│ ├── find_local_council_spec.rb
│ ├── flexible_page_spec.rb
│ ├── get_involved_spec.rb
│ ├── help_spec.rb
│ ├── homepage_spec.rb
│ ├── landing_page_spec.rb
│ ├── licence_transactions_spec.rb
│ ├── local_authority_api_spec.rb
│ ├── local_transactions_spec.rb
│ ├── news_article_spec.rb
│ ├── placeholder_spec.rb
│ ├── places_spec.rb
│ ├── random_spec.rb
│ ├── roadmap_spec.rb
│ ├── sanitiser_spec.rb
│ ├── service_manual_homepage_spec.rb
│ ├── service_toolkit_spec.rb
│ ├── sessions_spec.rb
│ ├── simple_smart_answers_spec.rb
│ ├── specialist_document_spec.rb
│ ├── speech_spec.rb
│ ├── static_error_pages_spec.rb
│ ├── take_part_spec.rb
│ ├── transactions_spec.rb
│ └── travel_advice_spec.rb
├── routing
│ ├── calendars_spec.rb
│ ├── csv_previews_spec.rb
│ ├── landing_pages_spec.rb
│ └── simple_smart_answers_spec.rb
├── service_consumers
│ └── pact_helper.rb
├── services
│ ├── csv_preview_service_spec.rb
│ └── electoral_service_spec.rb
├── spec_helper.rb
├── support
│ ├── asset_manager_helpers.rb
│ ├── calendar_helpers.rb
│ ├── component_helpers.rb
│ ├── concerns
│ │ ├── emphasised_organisations.rb
│ │ ├── news_image.rb
│ │ ├── parts.rb
│ │ ├── people.rb
│ │ ├── political.rb
│ │ ├── single_page_notification_button.rb
│ │ ├── updatable.rb
│ │ ├── withdrawable.rb
│ │ └── worldwide_organisations.rb
│ ├── content_item_helpers.rb
│ ├── content_store_helpers.rb
│ ├── election_helpers.rb
│ ├── jasmine-browser.json
│ ├── landing_page_blocks.rb
│ ├── location_helpers.rb
│ ├── matchers
│ │ ├── have_bank_holiday_table_matcher.rb
│ │ ├── have_button_as_link_matcher.rb
│ │ └── honours_content_store_ttl_matcher.rb
│ ├── meta_tags.rb
│ ├── publishing_api_graphql_helpers.rb
│ ├── schema_org_helpers.rb
│ └── search_helpers.rb
├── system
│ ├── account_home_spec.rb
│ ├── answer_spec.rb
│ ├── bank_holidays_spec.rb
│ ├── case_study_spec.rb
│ ├── corporate_information_page_spec.rb
│ ├── csv_preview_spec.rb
│ ├── developer_root_spec.rb
│ ├── electoral_look_up_spec.rb
│ ├── error_handling_spec.rb
│ ├── fatality_notice_spec.rb
│ ├── field_of_operation_spec.rb
│ ├── fields_of_operation_spec.rb
│ ├── find_local_council_spec.rb
│ ├── flexible_page_spec.rb
│ ├── get_involved_spec.rb
│ ├── gwyliau_banc_spec.rb
│ ├── help_cookies_spec.rb
│ ├── help_page_spec.rb
│ ├── help_spec.rb
│ ├── homepage_spec.rb
│ ├── icalendar_spec.rb
│ ├── json_spec.rb
│ ├── landing_page_spec.rb
│ ├── licence_transaction_spec.rb
│ ├── local_transactions_spec.rb
│ ├── news_article_spec.rb
│ ├── phase_banner_spec.rb
│ ├── place_spec.rb
│ ├── service_manual_homepage_spec.rb
│ ├── service_toolkit_spec.rb
│ ├── sessions_spec.rb
│ ├── sign_in_spec.rb
│ ├── simple_smart_answers_spec.rb
│ ├── specialist_document_spec.rb
│ ├── speech_spec.rb
│ ├── static_error_page_spec.rb
│ ├── take_part_spec.rb
│ ├── transaction_spec.rb
│ ├── travel_advice_atom_spec.rb
│ ├── travel_advice_spec.rb
│ └── when_do_the_clocks_change_spec.rb
├── unit
│ ├── api_error_routing_constraint_spec.rb
│ ├── content_item_loader_spec.rb
│ ├── format_routing_constraint_spec.rb
│ ├── full_path_format_routing_constraint_spec.rb
│ ├── ics_renderer_spec.rb
│ ├── locales_validation_spec.rb
│ ├── postcode_sanitizer_spec.rb
│ ├── sanitiser
│ │ └── strategy_spec.rb
│ └── simple_smart_answers
│ │ ├── flow_spec.rb
│ │ └── node_spec.rb
└── views
│ └── shared
│ └── _published_dates_with_notification_button.html.erb_spec.rb
├── startup.sh
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | .dockerignore
2 | .git
3 | .gitignore
4 | .github
5 | Dockerfile
6 | Procfile
7 | README.md
8 | coverage
9 | docs
10 | log
11 | node_modules
12 | spec
13 | test
14 | tmp
15 | vendor
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: /
5 | schedule:
6 | interval: daily
7 |
8 | - package-ecosystem: npm
9 | directory: /
10 | schedule:
11 | interval: daily
12 |
13 | - package-ecosystem: docker
14 | directory: /
15 | schedule:
16 | interval: weekly
17 | ignore:
18 | - dependency-name: ruby
19 |
20 | - package-ecosystem: github-actions
21 | directory: /
22 | schedule:
23 | interval: daily
24 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 | ⚠️ This repo is Continuously Deployed: make sure you [follow the guidance](https://docs.publishing.service.gov.uk/manual/development-pipeline.html#merge-your-own-pull-request) ⚠️
3 |
4 | ## What
5 |
6 |
7 |
8 | ## Why
9 |
10 | [Trello card?](url)
11 |
12 | ## How
13 |
14 | ## Screenshots?
15 |
16 |
--------------------------------------------------------------------------------
/.github/workflows/actionlint.yml:
--------------------------------------------------------------------------------
1 | name: Lint GitHub Actions
2 | on:
3 | push:
4 | paths: ['.github/**']
5 | jobs:
6 | actionlint:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | with:
11 | show-progress: false
12 | - uses: alphagov/govuk-infrastructure/.github/actions/actionlint@main
13 |
--------------------------------------------------------------------------------
/.github/workflows/jasmine.yml:
--------------------------------------------------------------------------------
1 | name: Run Jasmine
2 |
3 | on:
4 | workflow_call:
5 |
6 | jobs:
7 | run-jasmine:
8 | name: Run Jasmine
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v4
13 |
14 | - name: Setup Ruby
15 | uses: ruby/setup-ruby@v1
16 | with:
17 | bundler-cache: true
18 |
19 | - name: Setup Node
20 | uses: alphagov/govuk-infrastructure/.github/actions/setup-node@main
21 |
22 | - name: Run Jasmine
23 | run: yarn run jasmine:ci
24 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | workflow_run:
6 | workflows: [CI]
7 | types: [completed]
8 | branches: [main]
9 |
10 | jobs:
11 | release:
12 | if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
13 | name: Release
14 | uses: alphagov/govuk-infrastructure/.github/workflows/release.yml@main
15 | secrets:
16 | GH_TOKEN: ${{ secrets.GOVUK_CI_GITHUB_API_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | .bundle
8 | log/*.log
9 | tmp/
10 | coverage
11 | public/assets
12 | /app/assets/builds/*
13 | !/app/assets/builds/.keep
14 | node_modules
15 | yarn-error.log
16 | spec/reports/pacts
17 | .env
18 | .yarn/cache/
19 | .yarn/install-state.gz
20 |
21 | # vim swap files and tags
22 | *.sw[a-z]
23 | /tags
24 |
--------------------------------------------------------------------------------
/.govuk_dependabot_merger.yml:
--------------------------------------------------------------------------------
1 | api_version: 2
2 | defaults:
3 | auto_merge: true
4 | update_external_dependencies: true
5 | overrides:
6 | - dependency: rails
7 | auto_merge: false
8 | - dependency: railties
9 | auto_merge: false
10 | - dependency: actioncable
11 | auto_merge: false
12 | - dependency: actionmailbox
13 | auto_merge: false
14 | - dependency: actionmailer
15 | auto_merge: false
16 | - dependency: actionpack
17 | auto_merge: false
18 | - dependency: actiontext
19 | auto_merge: false
20 | - dependency: actionview
21 | auto_merge: false
22 | - dependency: activejob
23 | auto_merge: false
24 | - dependency: activemodel
25 | auto_merge: false
26 | - dependency: activerecord
27 | auto_merge: false
28 | - dependency: activestorage
29 | auto_merge: false
30 | - dependency: activesupport
31 | auto_merge: false
32 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18.20.4
2 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "all": true,
3 | "branches": 90,
4 | "lines": 95,
5 | "check-coverage": true,
6 | "compact": false,
7 | "exclude": [
8 | "public/assets/frontend/component_guide",
9 | "public/assets/frontend/govuk_publishing_components*",
10 | "public/assets/frontend/manifest-*",
11 | "public/assets/frontend/application-*",
12 | "public/assets/frontend/static-error-pages-*",
13 | "public/assets/frontend/test-dependencies-*",
14 | "public/assets/frontend/dependencies-*"
15 | ],
16 | "report-dir": "coverage/javascript",
17 | "reporter": [
18 | "text"
19 | ],
20 | "source-map": false,
21 | "temp-dir": "tmp/nyc_output"
22 | }
23 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_gem:
2 | rubocop-govuk:
3 | - config/default.yml
4 | - config/rails.yml
5 | - config/rspec.yml
6 |
7 | inherit_mode:
8 | merge:
9 | - Exclude
10 |
11 | # **************************************************************
12 | # TRY NOT TO ADD OVERRIDES IN THIS FILE
13 | #
14 | # This repo is configured to follow the RuboCop GOV.UK styleguide.
15 | # Any rules you override here will cause this repo to diverge from
16 | # the way we write code in all other GOV.UK repos.
17 | #
18 | # See https://github.com/alphagov/rubocop-govuk/blob/main/CONTRIBUTING.md
19 | # **************************************************************
20 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.3.6
2 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-3.5.0.cjs
2 | nodeLinker: node-modules
3 | packageExtensions:
4 | stylelint-config-recommended-scss@*:
5 | peerDependencies:
6 | postcss: '8'
7 | stylelint-config-standard-scss@*:
8 | peerDependencies:
9 | postcss: '8'
10 | stylelint-config-gds@*:
11 | peerDependencies:
12 | postcss: '8'
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG ruby_version=3.3
2 | ARG base_image=ghcr.io/alphagov/govuk-ruby-base:$ruby_version
3 | ARG builder_image=ghcr.io/alphagov/govuk-ruby-builder:$ruby_version
4 |
5 |
6 | FROM --platform=$TARGETPLATFORM $builder_image AS builder
7 |
8 | WORKDIR $APP_HOME
9 | COPY Gemfile* .ruby-version ./
10 | RUN bundle install
11 | COPY package.json yarn.lock ./
12 | RUN npm install -g yarn@1.22.19 && yarn install --immutable
13 | COPY . .
14 | RUN bootsnap precompile --gemfile .
15 | RUN rails assets:precompile && rm -fr log
16 |
17 |
18 | FROM --platform=$TARGETPLATFORM $base_image
19 |
20 | ENV GOVUK_APP_NAME=frontend
21 |
22 | WORKDIR $APP_HOME
23 | COPY --from=builder $BUNDLE_PATH $BUNDLE_PATH
24 | COPY --from=builder $BOOTSNAP_CACHE_DIR $BOOTSNAP_CACHE_DIR
25 | COPY --from=builder $APP_HOME .
26 |
27 | USER app
28 | CMD ["puma"]
29 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: bin/rails server -p 3005
2 | css: bin/rails dartsass:watch
3 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | begin
5 | require "pact/tasks"
6 | require "rspec/core/rake_task"
7 | RSpec::Core::RakeTask.new
8 | rescue LoadError
9 | # Pact/RSpec not available in all environments
10 | end
11 |
12 | require File.expand_path("config/application", __dir__)
13 |
14 | Rails.application.load_tasks
15 |
16 | Rake::Task[:default].clear if Rake::Task.task_defined?(:default)
17 | task default: %i[lint spec jasmine pact:verify]
18 |
--------------------------------------------------------------------------------
/app/assets/builds/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/builds/.keep
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link application.js
3 | //= link main.js
4 | //= link dependencies.js
5 | //= link test-dependencies.js
6 | //= link views/travel-advice.js
7 |
8 | //= link static-error-pages.js
9 |
10 | //= link_tree ../builds
11 |
--------------------------------------------------------------------------------
/app/assets/images/campaign/britain_is_great/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/britain_is_great/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/britain_is_great/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/britain_is_great/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/britain_is_great/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/britain_is_great/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/custom-logos/environment-agency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/custom-logos/environment-agency.png
--------------------------------------------------------------------------------
/app/assets/images/campaign/dvla-new-licence-rules/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dvla-new-licence-rules/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/dvla-new-licence-rules/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dvla-new-licence-rules/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/dvla-new-licence-rules/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dvla-new-licence-rules/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/dvla-new-licence-rules/INF45x3W.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dvla-new-licence-rules/INF45x3W.pdf
--------------------------------------------------------------------------------
/app/assets/images/campaign/dwp-workplace-pensions/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dwp-workplace-pensions/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/dwp-workplace-pensions/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dwp-workplace-pensions/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/dwp-workplace-pensions/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/dwp-workplace-pensions/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/fire-kills/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/fire-kills/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/fire-kills/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/fire-kills/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/fire-kills/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/fire-kills/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/know-before-you-go/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/know-before-you-go/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/know-before-you-go/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/know-before-you-go/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/know-before-you-go/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/know-before-you-go/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/royal_mail_shares/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/royal_mail_shares/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/royal_mail_shares/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/royal_mail_shares/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/royal_mail_shares/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/royal_mail_shares/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/sort-my-tax/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/sort-my-tax/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/sort-my-tax/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/sort-my-tax/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/sort-my-tax/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/sort-my-tax/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/uk-welcomes/eugo-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/uk-welcomes/eugo-logo.png
--------------------------------------------------------------------------------
/app/assets/images/campaign/unimoney/320px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/unimoney/320px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/unimoney/768px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/unimoney/768px.jpg
--------------------------------------------------------------------------------
/app/assets/images/campaign/unimoney/960px.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/campaign/unimoney/960px.jpg
--------------------------------------------------------------------------------
/app/assets/images/components/download-link/calendar-icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/assets/images/components/download-link/download-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/assets/images/getsatisfaction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/getsatisfaction.jpg
--------------------------------------------------------------------------------
/app/assets/images/homepage/cost-of-living-featured.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/homepage/cost-of-living-featured.png
--------------------------------------------------------------------------------
/app/assets/images/homepage/find-a-job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/homepage/find-a-job.png
--------------------------------------------------------------------------------
/app/assets/images/homepage/national-insurance-featured.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/homepage/national-insurance-featured.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/960x640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/960x640.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/chart.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/desktop.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/desktop_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/desktop_2x.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/landing_page_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/landing_page_image.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_desktop.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_desktop_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_desktop_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_mobile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_mobile.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_mobile_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_mobile_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_tablet.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_tablet.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_featured_tablet_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_featured_tablet_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_desktop.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_desktop_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_desktop_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_mobile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_mobile.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_mobile_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_mobile_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_tablet.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_tablet.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/missions_hero_tablet_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/missions_hero_tablet_2x.jpg
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/mobile.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/mobile_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/mobile_2x.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/tablet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/tablet.png
--------------------------------------------------------------------------------
/app/assets/images/landing_page/placeholder/tablet_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/landing_page/placeholder/tablet_2x.png
--------------------------------------------------------------------------------
/app/assets/images/ministry-of-defence-crest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/ministry-of-defence-crest.png
--------------------------------------------------------------------------------
/app/assets/images/roadmap/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/roadmap/image.png
--------------------------------------------------------------------------------
/app/assets/images/roadmap/updates-blog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/roadmap/updates-blog.jpg
--------------------------------------------------------------------------------
/app/assets/images/roadmap/updates-goals.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/roadmap/updates-goals.jpg
--------------------------------------------------------------------------------
/app/assets/images/roadmap/updates-work.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/roadmap/updates-work.jpg
--------------------------------------------------------------------------------
/app/assets/images/specialist-documents/protected-food-drink-names/protected-designation-of-origin-pdo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/specialist-documents/protected-food-drink-names/protected-designation-of-origin-pdo.png
--------------------------------------------------------------------------------
/app/assets/images/specialist-documents/protected-food-drink-names/protected-geographical-indication-pgi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/specialist-documents/protected-food-drink-names/protected-geographical-indication-pgi.png
--------------------------------------------------------------------------------
/app/assets/images/specialist-documents/protected-food-drink-names/traditional-speciality-guaranteed-tsg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/specialist-documents/protected-food-drink-names/traditional-speciality-guaranteed-tsg.png
--------------------------------------------------------------------------------
/app/assets/images/start-pages/make-a-sorn/performance-icon-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/start-pages/make-a-sorn/performance-icon-2x.png
--------------------------------------------------------------------------------
/app/assets/images/start-pages/make-a-sorn/performance-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/start-pages/make-a-sorn/performance-icon.png
--------------------------------------------------------------------------------
/app/assets/images/templogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/templogo.png
--------------------------------------------------------------------------------
/app/assets/images/travel-advice/feed-icon-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/travel-advice/feed-icon-black.png
--------------------------------------------------------------------------------
/app/assets/images/travel-advice/mail-icon-x2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/travel-advice/mail-icon-x2.png
--------------------------------------------------------------------------------
/app/assets/images/travel-advice/mail-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/app/assets/images/travel-advice/mail-icon.png
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This file is linked to in the application.
2 | //= require dependencies
3 | //= require main
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/dependencies.js:
--------------------------------------------------------------------------------
1 | // This file contains the dependencies required by the application.
2 | //= require govuk_publishing_components/lib
3 | //= require govuk_publishing_components/components/contents-list-with-body
4 | //= require govuk_publishing_components/components/error-summary
5 | //= require govuk_publishing_components/components/govspeak
6 | //= require govuk_publishing_components/components/image-card
7 | //= require govuk_publishing_components/components/intervention
8 | //= require govuk_publishing_components/components/metadata
9 | //= require govuk_publishing_components/components/print-link
10 | //= require govuk_publishing_components/components/radio
11 | //= require govuk_publishing_components/components/step-by-step-nav
12 | //= require govuk_publishing_components/components/table
13 | //= require govuk_publishing_components/components/tabs
14 |
15 | //= require govuk_web_banners/dependencies
16 |
--------------------------------------------------------------------------------
/app/assets/javascripts/main.js:
--------------------------------------------------------------------------------
1 | // This file should only contain the code being a part
2 | // of this application, not its dependencies.
3 | // It will be checked with respect to test coverage.
4 | //= require support
5 | //= require_tree ./modules
6 |
--------------------------------------------------------------------------------
/app/assets/javascripts/static-error-pages.js:
--------------------------------------------------------------------------------
1 | //= require govuk_publishing_components/dependencies
2 |
3 | //= require govuk_publishing_components/lib/trigger-event
4 | //= require govuk_publishing_components/lib/cookie-functions
5 | //= require govuk_publishing_components/lib/extend-object
6 |
7 | //= require govuk_publishing_components/components/button
8 | //= require govuk_publishing_components/components/cookie-banner
9 | //= require govuk_publishing_components/components/cross-service-header
10 | //= require govuk_publishing_components/components/feedback
11 | //= require govuk_publishing_components/components/layout-header
12 | //= require govuk_publishing_components/components/layout-super-navigation-header
13 | //= require govuk_publishing_components/components/skip-link
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/support.js:
--------------------------------------------------------------------------------
1 | /* istanbul ignore next */
2 | if (typeof window.GOVUK === 'undefined') { window.GOVUK = {} }
3 | /* istanbul ignore next */
4 | if (typeof window.GOVUK.support === 'undefined') { window.GOVUK.support = {} }
5 |
6 | window.GOVUK.support.history = function () {
7 | return window.history && window.history.pushState && window.history.replaceState
8 | }
9 |
--------------------------------------------------------------------------------
/app/assets/javascripts/test-dependencies.js:
--------------------------------------------------------------------------------
1 | // This file contains dependencies that are only needed when running in a test
2 | // environment. In the dev and live environment these are provided by static.
3 | //= require govuk_publishing_components/dependencies
4 | //= require govuk_publishing_components/lib/cookie-functions
5 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | // This flag stops the font from being included in this application's
2 | // stylesheet - the font is being served by Static across all of GOV.UK, so is
3 | // not needed here.
4 | $govuk-include-default-font-face: false;
5 |
6 | @import "govuk_publishing_components/govuk_frontend_support";
7 |
8 | // Helper stylesheets (things on more than one page layout)
9 | @import "helpers/content-bottom-margin";
10 | @import "helpers/inverse-background";
11 | @import "helpers/sticky-element-container";
12 |
13 | // frontend mixins
14 | @import "mixins/margins";
15 |
16 | .article-container {
17 | margin-bottom: govuk-spacing(6);
18 |
19 | @include govuk-media-query($from: tablet) {
20 | @include responsive-bottom-margin;
21 | }
22 | }
23 |
24 | .direction-rtl .govuk-main-wrapper {
25 | direction: rtl;
26 | text-align: start;
27 |
28 | table th {
29 | text-align: start;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/components/_calendar.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .app-c-calendar {
4 | .govuk-table {
5 | @include govuk-font($size: 16);
6 | }
7 |
8 | td:nth-child(1),
9 | td:nth-child(2) {
10 | width: 25%;
11 | }
12 |
13 | td:nth-child(3) {
14 | width: 50%;
15 | }
16 | }
17 |
18 | .app-c-calendar--clocks {
19 | margin-bottom: govuk-spacing(8);
20 |
21 | td:nth-child(1) {
22 | width: 10%;
23 | }
24 |
25 | td:nth-child(2),
26 | td:nth-child(3) {
27 | width: 45%;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/components/_download-link.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .app-c-download-link {
4 | display: inline-block;
5 | padding-left: 25px;
6 | background-image: url("components/download-link/download-icon.svg");
7 | background-repeat: no-repeat;
8 | background-position: 0 3px;
9 | background-size: 20px 20px;
10 | }
11 |
12 | .app-c-download-link--calendar {
13 | padding-left: 26px;
14 | background-image: url("components/download-link/calendar-icon.svg");
15 | background-position: 0 2px;
16 | background-size: 18px 21px;
17 | }
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/helpers/_content-bottom-margin.scss:
--------------------------------------------------------------------------------
1 | .content-bottom-margin {
2 | margin-bottom: govuk-spacing(4);
3 |
4 | @include govuk-media-query($from: tablet) {
5 | @include responsive-bottom-margin;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/helpers/_inverse-background.scss:
--------------------------------------------------------------------------------
1 | .inverse-background {
2 | background: $govuk-brand-colour;
3 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/helpers/_parts.scss:
--------------------------------------------------------------------------------
1 | @mixin parts {
2 | .part-navigation-container {
3 | margin-top: govuk-spacing(6);
4 | margin-bottom: govuk-spacing(6);
5 | padding-bottom: govuk-spacing(3);
6 | border-bottom: 1px solid govuk-colour("mid-grey");
7 |
8 | @include govuk-media-query($from: tablet) {
9 | margin-top: 0;
10 | margin-bottom: 0;
11 | }
12 | }
13 |
14 | .part-navigation {
15 | margin-left: 0;
16 |
17 | @include govuk-media-query($until: tablet) {
18 | margin-left: govuk-spacing(1);
19 | }
20 | }
21 |
22 | .part-title {
23 | margin-bottom: govuk-spacing(3);
24 | margin-top: govuk-spacing(3);
25 |
26 | @include govuk-font(27, $weight: bold);
27 |
28 | @include govuk-media-query($from: tablet) {
29 | margin-bottom: govuk-spacing(4);
30 | margin-top: govuk-spacing(4);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/helpers/_sticky-element-container.scss:
--------------------------------------------------------------------------------
1 | .govuk-frontend-supported .sticky-element {
2 | position: absolute;
3 | bottom: 0;
4 |
5 | &--stuck-to-window {
6 | bottom: 0;
7 | position: fixed;
8 | }
9 |
10 | &--enabled {
11 | transition: opacity, .3s, ease;
12 | opacity: 1;
13 |
14 | @include govuk-media-query($until: tablet) {
15 | position: static;
16 | }
17 | }
18 |
19 | &--hidden {
20 | opacity: 0;
21 | pointer-events: none;
22 | }
23 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/helpers/_truncated-url.scss:
--------------------------------------------------------------------------------
1 | .truncated-url {
2 | display: block;
3 | overflow: hidden;
4 | text-overflow: ellipsis;
5 | white-space: nowrap;
6 | max-width: 36ch;
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/static-error-pages.scss:
--------------------------------------------------------------------------------
1 |
2 | @import "govuk_publishing_components/govuk_frontend_support";
3 | @import "govuk_publishing_components/component_support";
4 |
5 | @import "govuk_publishing_components/components/button";
6 | @import "govuk_publishing_components/components/cookie-banner";
7 | @import "govuk_publishing_components/components/cross-service-header";
8 | @import "govuk_publishing_components/components/feedback";
9 | @import "govuk_publishing_components/components/label";
10 | @import "govuk_publishing_components/components/layout-footer";
11 | @import "govuk_publishing_components/components/layout-for-public";
12 | @import "govuk_publishing_components/components/layout-header";
13 | @import "govuk_publishing_components/components/layout-super-navigation-header";
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_cookie-settings.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .cookie-settings__form-wrapper {
4 | display: none;
5 |
6 | .govuk-frontend-supported & {
7 | display: block;
8 | }
9 | }
10 |
11 | .cookie-settings__no-js {
12 | .govuk-frontend-supported & {
13 | display: none;
14 | }
15 | }
16 |
17 | .cookie-settings__confirmation {
18 | display: none;
19 | margin-top: govuk-spacing(3);
20 | @include govuk-font(19);
21 | }
22 |
23 | .cookie-settings__gov-services {
24 | margin-top: govuk-spacing(8);
25 | }
26 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_homepage_more_on_govuk.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .homepage-most-active-list {
4 | list-style: none;
5 | margin: 0 0 govuk-spacing(9);
6 | padding: 0;
7 | }
8 |
9 | .homepage-most-active-list__item {
10 | margin: 0 0 govuk-spacing(4);
11 | @include govuk-font($size: 19, $weight: bold);
12 |
13 | &:last-of-type {
14 | margin-bottom: 0;
15 | }
16 |
17 | // Ensure font-size is 19px on mobile for the new homepage design
18 | @include govuk-media-query($until: "tablet") {
19 | font-size: 19px;
20 | font-size: govuk-px-to-rem(19);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/block-error.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/govuk_frontend_support";
2 |
3 | .block-error {
4 | padding: govuk-spacing(3);
5 | border: $govuk-border-width-narrow solid $govuk-error-colour;
6 | @include govuk-responsive-margin(8, "bottom");
7 |
8 | @include govuk-media-query($from: tablet) {
9 | padding: govuk-spacing(4);
10 | border-width: $govuk-border-width;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/box.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .box {
4 | padding: govuk-spacing(4);
5 | background-color: govuk-colour("light-grey");
6 | }
7 |
8 | @include govuk-media-query($media-type: print) {
9 | .box {
10 | background: none;
11 | border-top: 2pt solid $govuk-print-text-colour;
12 | padding-inline: 0;
13 |
14 | * {
15 | color: $govuk-print-text-colour !important; // stylelint-disable-line declaration-no-important
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/columns_layout.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .columns-layout {
4 | display: grid;
5 | grid-gap: govuk-spacing(6);
6 |
7 | @include govuk-media-query($from: desktop) {
8 | @for $i from 2 through 3 {
9 | &[data-columns="#{$i}"]{
10 | grid-template-columns: repeat(#{$i}, 1fr);
11 | }
12 | }
13 | }
14 |
15 | }
16 | @include govuk-media-query($media-type: print) {
17 | .columns-layout {
18 | display: block;
19 |
20 | > * {
21 | margin-bottom: govuk-spacing(6);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/helpers/_backgrounds.scss:
--------------------------------------------------------------------------------
1 | @import "colours";
2 |
3 | @each $name, $colour in $colours {
4 | .background-color--theme-#{$name} {
5 | background-color: $colour;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/helpers/_borders.scss:
--------------------------------------------------------------------------------
1 | @import "colours";
2 |
3 | @each $name, $colour in $colours {
4 | .border-top--theme-#{$name} {
5 | border-top: 20px solid $colour;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/helpers/_colours.scss:
--------------------------------------------------------------------------------
1 | $theme-1-colour: #D60061;
2 | $theme-2-colour: #92BA15;
3 | $theme-3-colour: #E68621;
4 | $theme-4-colour: #B000F9;
5 | $theme-5-colour: #255AB5;
6 | $theme-6-colour: #62162D;
7 |
8 | // The following map is used by the borders and backgrounds helpers to iterate through 'theme' color options and
9 | // generate utility classes for each color variation e.g.
10 | // .border-top--theme-1 {
11 | // border-top: 20px solid #e60067;
12 | // }
13 |
14 | $colours: (
15 | "default": govuk-colour("black"), // This helps us see where branding was missed, as we don't want to fall back to an actual brand colour.
16 | "1": $theme-1-colour,
17 | "2": $theme-2-colour,
18 | "3": $theme-3-colour,
19 | "4": $theme-4-colour,
20 | "5": $theme-5-colour,
21 | "6": $theme-6-colour,
22 | );
23 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/image.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .image {
4 | width: 100%;
5 | @include govuk-responsive-margin(6, "bottom");
6 | }
7 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/logo.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .logo {
4 | margin-bottom: govuk-spacing(6);
5 | }
6 |
7 | .logo__img {
8 | display: block;
9 | }
10 |
11 | .logo__img--nhs {
12 | max-width: 100px;
13 | }
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/quote.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .quote__svg {
4 | margin-bottom: govuk-spacing(4);
5 | }
6 |
7 | .quote__blockquote {
8 | margin: 0;
9 | }
10 |
11 | .quote__text {
12 | margin: 0 0 govuk-spacing(8);
13 | @include govuk-font(19, $weight: "regular", $tabular: false, $line-height: false);
14 | }
15 |
16 | .quote__cite {
17 | font-style: normal;
18 | @include govuk-font(19, $weight: "regular", $tabular: false, $line-height: false);
19 | }
20 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/side-navigation.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .side-nav {
4 | @include govuk-media-query($until: desktop) {
5 | display: none;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_landing_page/themes/prime-ministers-office-10-downing-street.scss:
--------------------------------------------------------------------------------
1 | .landing-page-header {
2 | // I've used the hex value for the background colour as it doesn't exist in the colour palette
3 | background-color: #231F20;
4 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_local-transaction.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .interaction {
4 | margin-bottom: govuk-spacing(6);
5 |
6 | .local-authority {
7 | font-weight: bold;
8 | }
9 | }
10 |
11 | .search-again {
12 | margin: govuk-spacing(7) 0 govuk-spacing(3);
13 | }
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_location_form.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .location-form {
4 | padding: govuk-spacing(3);
5 | margin: govuk-spacing(6) 0;
6 | background: govuk-colour("light-grey");
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_place-list.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 | @import "helpers/truncated-url";
3 |
4 | .place-list__item {
5 | margin-top: govuk-spacing(5);
6 | padding-top: govuk-spacing(2);
7 | border-top: 1px solid $govuk-border-colour;
8 | list-style: none;
9 |
10 | &:first-child {
11 | margin-top: 0;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_published-dates-button-group.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .published-dates-button-group {
4 | @include govuk-media-query($from: tablet) {
5 | display: flex;
6 | flex-direction: row;
7 | }
8 | }
9 |
10 | .published-dates-button-group form {
11 | @include govuk-media-query($until: tablet) {
12 | margin-bottom: govuk-spacing(3);
13 | }
14 | @include govuk-media-query($from: tablet) {
15 | margin-right: govuk-spacing(4);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_publisher_metadata.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .metadata-wrapper {
4 | border-top: 1px solid $govuk-border-colour;
5 | margin-left: govuk-spacing(3);
6 | margin-right: govuk-spacing(3);
7 | @include govuk-clearfix;
8 |
9 | .metadata-column {
10 | padding-left: 0;
11 | padding-top: govuk-spacing(3);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_roadmap.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .govuk-roadmap__intro {
4 | @include govuk-font(24);
5 | }
6 |
7 | .govuk-roadmap-section {
8 | border-bottom: 5px solid govuk-colour("black");
9 | margin-bottom: govuk-spacing(6);
10 | padding-bottom: govuk-spacing(6);
11 | }
12 |
13 | .govuk-roadmap-section__image {
14 | max-width: 300px;
15 | margin: 0 auto;
16 | }
17 |
18 | .govuk-roadmap-numbers__column {
19 | @include govuk-media-query($until: desktop) {
20 | width: 50%;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_service-toolkit.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .service-toolkit {
4 | border-bottom: 1px solid $govuk-border-colour;
5 | margin-bottom: govuk-spacing(5);
6 | padding-bottom: govuk-spacing(3);
7 |
8 | @include govuk-media-query($from: tablet) {
9 | margin-bottom: govuk-spacing(9);
10 | padding-bottom: govuk-spacing(6);
11 | }
12 | }
13 |
14 | .service-toolkit:last-child {
15 | border-bottom: none;
16 | margin-bottom: 0;
17 | }
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_sidebar-navigation.scss:
--------------------------------------------------------------------------------
1 | @import "govuk_publishing_components/individual_component_support";
2 |
3 | .sidebar-navigation {
4 | @include govuk-media-query($until: tablet) {
5 | margin-top: govuk-spacing(7);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/views/_specialist-document.scss:
--------------------------------------------------------------------------------
1 | .specialist-document {
2 | .protected-food-drink-name-logo {
3 | float: right;
4 | width: 150px;
5 | padding-left: 2em;
6 | padding-bottom: 2em;
7 | }
8 | }
--------------------------------------------------------------------------------
/app/controllers/account_home_controller.rb:
--------------------------------------------------------------------------------
1 | class AccountHomeController < ApplicationController
2 | include GovukPersonalisation::ControllerConcern
3 |
4 | def show
5 | redirect_with_analytics GovukPersonalisation::Urls.one_login_your_services
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/answer_controller.rb:
--------------------------------------------------------------------------------
1 | class AnswerController < ContentItemsController
2 | include Cacheable
3 |
4 | def show
5 | @presenter = ContentItemPresenter.new(content_item)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/case_study_controller.rb:
--------------------------------------------------------------------------------
1 | class CaseStudyController < ContentItemsController
2 | def show
3 | @case_study_presenter = ContentItemPresenter.new(content_item)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/concerns/cacheable.rb:
--------------------------------------------------------------------------------
1 | module Cacheable
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | before_action :set_expiry, if: -> { request.format.html? }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/concerns/previewable.rb:
--------------------------------------------------------------------------------
1 | module Previewable
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | before_action :set_edition_for_viewing_draft_content
6 | end
7 |
8 | def set_edition_for_viewing_draft_content
9 | @edition = params[:edition]
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/controllers/corporate_information_page_controller.rb:
--------------------------------------------------------------------------------
1 | class CorporateInformationPageController < ContentItemsController
2 | def show
3 | @presenter = CorporateInformationPagePresenter.new(@content_item)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/development_controller.rb:
--------------------------------------------------------------------------------
1 | class DevelopmentController < ApplicationController
2 | layout false
3 |
4 | def index
5 | @paths = YAML.load_file(Rails.root.join("config/govuk_examples.yml"))
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/error_controller.rb:
--------------------------------------------------------------------------------
1 | class ErrorController < ApplicationController
2 | def handler
3 | # We know at this point that the ContentItemLoader has stored
4 | # an exception to deal with, so just retrieve it and raise it
5 | # to be handled in ApplicationController
6 | raise ContentItemLoader.for_request(request).load(request.path)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/controllers/fatality_notice_controller.rb:
--------------------------------------------------------------------------------
1 | class FatalityNoticeController < ContentItemsController
2 | include Cacheable
3 |
4 | def show
5 | @presenter = ContentItemPresenter.new(content_item)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/favicon_controller.rb:
--------------------------------------------------------------------------------
1 | # Because favicon has to be present at root, we need a controller
2 | # to redirect it to the asset. When there's a better solution to this
3 | # problem we can remove this controller and routes.
4 | class FaviconController < ApplicationController
5 | before_action { expires_in(1.day, public: true) }
6 |
7 | def redirect_to_asset
8 | redirect_to(view_context.asset_path("favicon.ico"),
9 | status: :moved_permanently,
10 | allow_other_host: true)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/controllers/field_of_operation_controller.rb:
--------------------------------------------------------------------------------
1 | class FieldOfOperationController < ContentItemsController
2 | include Cacheable
3 | def show
4 | @presenter = FieldOfOperationPresenter.new(@content_item)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/controllers/fields_of_operation_controller.rb:
--------------------------------------------------------------------------------
1 | class FieldsOfOperationController < ContentItemsController
2 | include Cacheable
3 |
4 | def index; end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/flexible_page_controller.rb:
--------------------------------------------------------------------------------
1 | class FlexiblePageController < ContentItemsController
2 | slimmer_template "gem_layout_full_width"
3 | end
4 |
--------------------------------------------------------------------------------
/app/controllers/get_involved_controller.rb:
--------------------------------------------------------------------------------
1 | class GetInvolvedController < ContentItemsController
2 | def show
3 | @presenter = GetInvolvedPresenter.new(@content_item)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/help_controller.rb:
--------------------------------------------------------------------------------
1 | class HelpController < ContentItemsController
2 | include Cacheable
3 |
4 | skip_before_action :set_expiry, only: [:ab_testing]
5 | skip_before_action :set_locale, only: [:ab_testing]
6 |
7 | def index; end
8 |
9 | def cookie_settings; end
10 |
11 | def ab_testing
12 | ab_test = GovukAbTesting::AbTest.new("Example")
13 | @requested_variant = ab_test.requested_variant(request.headers)
14 | @requested_variant.configure_response(response)
15 | end
16 |
17 | def sign_in
18 | @search_services_facets = [
19 | {
20 | key: "content_purpose_supergroup[]",
21 | value: "services",
22 | },
23 | {
24 | key: "content_purpose_supergroup[]",
25 | value: "guidance_and_regulation",
26 | },
27 | ]
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/controllers/help_page_controller.rb:
--------------------------------------------------------------------------------
1 | class HelpPageController < ContentItemsController
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/homepage_controller.rb:
--------------------------------------------------------------------------------
1 | class HomepageController < ContentItemsController
2 | include Cacheable
3 |
4 | slimmer_template "gem_layout_homepage"
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/landing_page_controller.rb:
--------------------------------------------------------------------------------
1 | class LandingPageController < ContentItemsController
2 | slimmer_template "gem_layout_full_width"
3 | end
4 |
--------------------------------------------------------------------------------
/app/controllers/news_article_controller.rb:
--------------------------------------------------------------------------------
1 | class NewsArticleController < ContentItemsController
2 | include Cacheable
3 |
4 | def show
5 | @presenter = ContentItemPresenter.new(content_item)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/placeholder_controller.rb:
--------------------------------------------------------------------------------
1 | class PlaceholderController < ApplicationController
2 | def show; end
3 | end
4 |
--------------------------------------------------------------------------------
/app/controllers/roadmap_controller.rb:
--------------------------------------------------------------------------------
1 | class RoadmapController < ApplicationController
2 | include Cacheable
3 |
4 | def index; end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/service_manual_controller.rb:
--------------------------------------------------------------------------------
1 | class ServiceManualController < ContentItemsController
2 | include Cacheable
3 | slimmer_template "gem_layout_full_width"
4 |
5 | def index
6 | @presenter = ServiceManualHomepagePresenter.new(content_item)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/controllers/service_toolkit_controller.rb:
--------------------------------------------------------------------------------
1 | class ServiceToolkitController < ContentItemsController
2 | include Cacheable
3 | slimmer_template "gem_layout_full_width"
4 |
5 | def index; end
6 | end
7 |
--------------------------------------------------------------------------------
/app/controllers/specialist_document_controller.rb:
--------------------------------------------------------------------------------
1 | class SpecialistDocumentController < ContentItemsController
2 | include Cacheable
3 |
4 | def show
5 | @presenter = SpecialistDocumentPresenter.new(content_item)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/speech_controller.rb:
--------------------------------------------------------------------------------
1 | class SpeechController < ContentItemsController
2 | def show
3 | @presenter = SpeechPresenter.new(content_item)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/static_error_pages_controller.rb:
--------------------------------------------------------------------------------
1 | class StaticErrorPagesController < ApplicationController
2 | after_action do
3 | response.headers[Slimmer::Headers::SKIP_HEADER] = "true"
4 | end
5 |
6 | ERROR_CODES = %w[
7 | 400
8 | 401
9 | 403
10 | 404
11 | 405
12 | 406
13 | 410
14 | 422
15 | 429
16 | 500
17 | 501
18 | 502
19 | 503
20 | 504
21 | ].freeze
22 |
23 | def show
24 | if ERROR_CODES.include?(params[:error_code])
25 | render action: params[:error_code], layout: false
26 | else
27 | head :not_found
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/controllers/take_part_controller.rb:
--------------------------------------------------------------------------------
1 | class TakePartController < ContentItemsController
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/transaction_controller.rb:
--------------------------------------------------------------------------------
1 | class TransactionController < ContentItemsController
2 | include Cacheable
3 | include LocaleHelper
4 |
5 | before_action :deny_framing
6 |
7 | def show
8 | content_item.set_variant(params["variant"])
9 | @transaction_presenter = TransactionPresenter.new(content_item)
10 | end
11 |
12 | private
13 |
14 | def content_item_path
15 | "/#{params[:slug]}"
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/helpers/block_helper.rb:
--------------------------------------------------------------------------------
1 | module BlockHelper
2 | def render_block(block, options: {})
3 | render("landing_page/blocks/#{block.type}", block:, options:)
4 | rescue ActionView::MissingTemplate
5 | Rails.logger.warn("Missing template for block #{block.type}")
6 | ""
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/helpers/calendar_helper.rb:
--------------------------------------------------------------------------------
1 | module CalendarHelper
2 | def last_updated_date
3 | File.mtime(Rails.root.join("REVISION")).to_date
4 | rescue StandardError
5 | Time.zone.today
6 | end
7 |
8 | def time_tag_safe(date)
9 | tag.time(datetime: date) { l(date, format: "%e %B") }.html_safe
10 | end
11 |
12 | def format_date(date)
13 | l date, format: "%e %B %Y"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/helpers/contents_list_helper.rb:
--------------------------------------------------------------------------------
1 | module ContentsListHelper
2 | def contents_list(current_path, links)
3 | links.map do |link|
4 | content = {
5 | href: link["href"],
6 | text: link["text"],
7 | }
8 |
9 | content[:active] = true if current_path == link["href"]
10 | content[:links] = contents_list(current_path, link["links"]) if link["links"].present?
11 |
12 | content
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/helpers/currency_helper.rb:
--------------------------------------------------------------------------------
1 | module CurrencyHelper
2 | def format_amount(number)
3 | if number.present?
4 | number_to_currency(number, unit: "euros", precision: 0, format: "%n %u")
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/helpers/date_helper.rb:
--------------------------------------------------------------------------------
1 | module DateHelper
2 | def display_date(timestamp, format = "%-d %B %Y")
3 | I18n.l(Time.zone.parse(timestamp), format:, locale: I18n.locale) if timestamp
4 | end
5 |
6 | def formatted_history(history)
7 | history.map do |change|
8 | {
9 | display_time: display_date(change[:timestamp]),
10 | note: change[:note],
11 | timestamp: change[:timestamp],
12 | }
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/helpers/draft_helper.rb:
--------------------------------------------------------------------------------
1 | module DraftHelper
2 | def draft_host?
3 | ENV["PLEK_HOSTNAME_PREFIX"] == "draft-"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/helpers/error_items_helper.rb:
--------------------------------------------------------------------------------
1 | module ErrorItemsHelper
2 | def error_items(field)
3 | if flash[:validation] && flash[:validation].select { |key| key.to_s.match(field) }.any?
4 | sanitize(flash[:validation]
5 | .select { |key| key.to_s.match(field) }
6 | .map { |error| error[:text] }
7 | .join("
"))
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/helpers/govuk_personalisation_helper.rb:
--------------------------------------------------------------------------------
1 | module GovukPersonalisationHelper
2 | def email_subscription_success_banner_heading(account_flash, locale = nil)
3 | if account_flash.include?("email-subscription-success")
4 | sanitize(t("email.subscribe_title", locale:))
5 | elsif account_flash.include?("email-unsubscribe-success")
6 | sanitize(t("email.unsubscribe_title", locale:))
7 | elsif account_flash.include?("email-subscription-already-subscribed")
8 | sanitize(t("email.already_subscribed_title", locale:))
9 | end
10 | end
11 |
12 | def show_email_subscription_success_banner?(account_flash)
13 | account_flash.include?("email-subscription-success") || account_flash.include?("email-unsubscribe-success") || account_flash.include?("email-subscription-already-subscribed")
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/helpers/location_form_helper.rb:
--------------------------------------------------------------------------------
1 | module LocationFormHelper
2 | def button_text(publication_format = nil, publication_title = nil)
3 | case publication_format
4 | when "local_transaction", "licence"
5 | I18n.t("formats.local_transaction.find_council")
6 | when "place"
7 | places_button_text(publication_title)
8 | else
9 | I18n.t("find")
10 | end
11 | end
12 |
13 | def places_button_text(publication_title)
14 | publications_where_button_text_matches_title = ["Find a register office", "Darganfod swyddfa gofrestru"]
15 | publications_where_button_text_matches_title.include?(publication_title) ? publication_title : I18n.t("formats.place.find_results")
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/helpers/phone_number_helper.rb:
--------------------------------------------------------------------------------
1 | module PhoneNumberHelper
2 | UK_PHONE_REGEX = /^((\(?0\d{2}\)?\s?\d{4}\s?\d{4}))?/
3 |
4 | def phone_digits(phone_number)
5 | UK_PHONE_REGEX.match(phone_number)[0]
6 | end
7 |
8 | def phone_text(phone_number)
9 | UK_PHONE_REGEX.match(phone_number).post_match.strip
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/helpers/theme_type_helper.rb:
--------------------------------------------------------------------------------
1 | module ThemeTypeHelper
2 | def style(theme_colour)
3 | valid_numbers = (1..6)
4 | return "theme-default" unless valid_numbers.include?(theme_colour)
5 |
6 | "theme-#{theme_colour}"
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/helpers/travel_advice_helper.rb:
--------------------------------------------------------------------------------
1 | require "htmlentities"
2 |
3 | module TravelAdviceHelper
4 | def format_atom_change_description(text)
5 | # Encode basic entities([<>&'"]) as named, the rest as decimal
6 | simple_format(HTMLEntities.new.encode(text, :basic, :decimal))
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/helpers/url_helper.rb:
--------------------------------------------------------------------------------
1 | module UrlHelper
2 | def canonical_url(path)
3 | Plek.new.website_root + path
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/calendar/event.rb:
--------------------------------------------------------------------------------
1 | require "ostruct"
2 |
3 | class Calendar
4 | class Event < OpenStruct
5 | def initialize(attributes)
6 | attributes["date"] = Date.parse(attributes["date"]) unless attributes["date"].is_a?(Date)
7 | if attributes["title"] && attributes["title"].present?
8 | attributes["title"] = I18n.t(attributes["title"])
9 | end
10 | if attributes["notes"] && attributes["notes"].present?
11 | attributes["notes"] = I18n.t(attributes["notes"])
12 | end
13 | super(attributes)
14 | end
15 |
16 | def as_json
17 | @table.stringify_keys
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/calendar/year.rb:
--------------------------------------------------------------------------------
1 | class Calendar
2 | class Year
3 | def initialize(year_str, division, data = [])
4 | @year_str = year_str
5 | @division = division
6 | @data = data
7 | end
8 |
9 | def to_s
10 | @year_str
11 | end
12 |
13 | def events
14 | @events ||= @data.map do |e|
15 | Event.new(e)
16 | end
17 | end
18 |
19 | def upcoming_event
20 | @upcoming_event ||= events.find { |e| e.date >= Time.zone.today }
21 | end
22 |
23 | def upcoming_events
24 | @upcoming_events ||= events.select { |e| e.date >= Time.zone.today }
25 | end
26 |
27 | def past_events
28 | @past_events ||= events.select { |e| e.date < Time.zone.today }
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/models/case_study.rb:
--------------------------------------------------------------------------------
1 | class CaseStudy < ContentItem
2 | include EmphasisedOrganisations
3 | include Updatable
4 | include WorldwideOrganisations
5 |
6 | def contributors
7 | (organisations_ordered_by_emphasis + worldwide_organisations).uniq(&:content_id)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/concerns/emphasised_organisations.rb:
--------------------------------------------------------------------------------
1 | module EmphasisedOrganisations
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | def organisations_ordered_by_emphasis
6 | organisations.sort_by { |organisation| emphasised?(organisation) ? -1 : 1 }
7 | end
8 | end
9 |
10 | private
11 |
12 | def emphasised?(organisation)
13 | organisation.content_id.in?(emphasised_organisations)
14 | end
15 |
16 | def emphasised_organisations
17 | content_store_response.dig("details", "emphasised_organisations") || []
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/models/concerns/news_image.rb:
--------------------------------------------------------------------------------
1 | module NewsImage
2 | extend ActiveSupport::Concern
3 |
4 | def image
5 | content_store_response.dig("details", "image") || default_news_image || placeholder_image
6 | end
7 |
8 | private
9 |
10 | def default_news_image
11 | organisation = content_store_response.dig("links", "primary_publishing_organisation")
12 | organisation[0].dig("details", "default_news_image") if organisation.present?
13 | end
14 |
15 | def placeholder_image
16 | # this image has been uploaded to asset-manager
17 | return { "url" => "https://assets.publishing.service.gov.uk/media/5e985599d3bf7f3fc943bbd8/UK_government_logo.jpg" } if content_store_response["document_type"] == "world_news_story"
18 |
19 | { "url" => "https://assets.publishing.service.gov.uk/media/5e59279b86650c53b2cefbfe/placeholder.jpg" }
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/concerns/people.rb:
--------------------------------------------------------------------------------
1 | module People
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | def people
6 | linked("people")
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/concerns/political.rb:
--------------------------------------------------------------------------------
1 | module Political
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | def historically_political?
6 | political? && historical?
7 | end
8 |
9 | def publishing_government
10 | content_store_response.dig(
11 | "links", "government", 0, "title"
12 | )
13 | end
14 | end
15 |
16 | private
17 |
18 | def political?
19 | content_store_response.dig("details", "political")
20 | end
21 |
22 | def historical?
23 | government_current = content_store_response.dig(
24 | "links", "government", 0, "details", "current"
25 | )
26 |
27 | # Treat no government as not historical
28 | return false if government_current.nil?
29 |
30 | !government_current
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/models/concerns/single_page_notification_button.rb:
--------------------------------------------------------------------------------
1 | module SinglePageNotificationButton
2 | extend ActiveSupport::Concern
3 |
4 | # Add the content id of the publication, detailed_guide or consultation that should be exempt from having the single page notification button
5 | EXEMPTION_LIST = %w[
6 | c5c8d3cd-0dc2-4ca3-8672-8ca0a6e92165
7 | 70bd3a76-6606-45dd-9fb5-2b95f8667b4d
8 | a457220c-915c-4cb1-8e41-9191fba42540
9 | 5f9c6c15-7631-11e4-a3cb-005056011aef
10 | ].freeze
11 |
12 | def page_is_on_exemption_list?
13 | EXEMPTION_LIST.include? content_id
14 | end
15 |
16 | def display_single_page_notification_button?
17 | !page_is_on_exemption_list? && locale == "en"
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/models/concerns/withdrawable.rb:
--------------------------------------------------------------------------------
1 | module Withdrawable
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | def withdrawn?
6 | withdrawal_notice.present?
7 | end
8 |
9 | def withdrawn_at
10 | withdrawal_notice["withdrawn_at"]
11 | end
12 |
13 | def withdrawn_explanation
14 | withdrawal_notice["explanation"]
15 | end
16 | end
17 |
18 | private
19 |
20 | def withdrawal_notice
21 | content_store_response["withdrawn_notice"]
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/models/concerns/worldwide_organisations.rb:
--------------------------------------------------------------------------------
1 | module WorldwideOrganisations
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | def worldwide_organisations
6 | linked("worldwide_organisations")
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/content_item_factory.rb:
--------------------------------------------------------------------------------
1 | class ContentItemFactory
2 | def self.build(content_store_response)
3 | schema_name = if content_store_response["document_type"] == "licence_transaction"
4 | content_store_response["document_type"]
5 | else
6 | content_store_response["schema_name"]
7 | end
8 |
9 | content_item_class(schema_name).new(content_store_response)
10 | end
11 |
12 | def self.content_item_class(schema_name)
13 | klass = schema_name.camelize.constantize
14 | klass.superclass == ContentItem ? klass : ContentItem
15 | rescue StandardError
16 | ContentItem
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/contents_outline.rb:
--------------------------------------------------------------------------------
1 | class ContentsOutline
2 | attr_reader :items
3 |
4 | Item = Data.define(:text, :id, :level, :items)
5 |
6 | def initialize(headers_array)
7 | @items = headers_array_to_items(headers_array)
8 | end
9 |
10 | def level_two_headers?
11 | nested_level_two_headers?(items)
12 | end
13 |
14 | private
15 |
16 | def headers_array_to_items(headers_array)
17 | return [] if headers_array.blank?
18 |
19 | headers_array.map do |header|
20 | items = headers_array_to_items(header["headers"])
21 | Item.new(
22 | text: header["text"],
23 | id: header["id"],
24 | level: header["level"],
25 | items:,
26 | )
27 | end
28 | end
29 |
30 | def nested_level_two_headers?(items_array)
31 | items_array.detect { |item| item.level == 2 || nested_level_two_headers?(item.items) }.present?
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/app/models/detailed_guide.rb:
--------------------------------------------------------------------------------
1 | class DetailedGuide < ContentItem
2 | include SinglePageNotificationButton
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/election_postcode.rb:
--------------------------------------------------------------------------------
1 | class ElectionPostcode
2 | UK_POSTCODE_PATTERN = %r{
3 | \A
4 | # Outward code, for example SW1A
5 | (([A-Z][0-9]{1,2})|(([A-Z][A-HJ-Y][0-9]{1,2})|(([A-Z][0-9][A-Z])|([A-Z][A-HJ-Y][0-9][A-Z]?))))
6 | \s?
7 | [0-9][A-Z]{2} # Inward code, for example 2AA
8 | \Z
9 | }xi
10 |
11 | delegate :present?, to: :sanitized_postcode
12 |
13 | def initialize(postcode)
14 | @postcode = postcode
15 | end
16 |
17 | def sanitized_postcode
18 | @sanitized_postcode ||= PostcodeSanitizer.sanitize(postcode)
19 | end
20 |
21 | def postcode_for_api
22 | sanitized_postcode.gsub(/\s+/, "")
23 | end
24 |
25 | def valid?
26 | return false unless sanitized_postcode
27 |
28 | sanitized_postcode.match?(UK_POSTCODE_PATTERN)
29 | end
30 |
31 | def error
32 | "invalidPostcodeFormat" unless valid?
33 | end
34 |
35 | private
36 |
37 | attr_reader :postcode
38 | end
39 |
--------------------------------------------------------------------------------
/app/models/fatality_notice.rb:
--------------------------------------------------------------------------------
1 | class FatalityNotice < ContentItem
2 | include EmphasisedOrganisations
3 | include Updatable
4 |
5 | def field_of_operation
6 | linked("field_of_operation").first
7 | end
8 |
9 | def contributors
10 | organisations_ordered_by_emphasis + linked("people")
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/field_of_operation.rb:
--------------------------------------------------------------------------------
1 | class FieldOfOperation < ContentItem
2 | def fatality_notices
3 | linked("fatality_notices")
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/fields_of_operation.rb:
--------------------------------------------------------------------------------
1 | class FieldsOfOperation < ContentItem
2 | def fields_of_operation
3 | linked("fields_of_operation")
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/finder.rb:
--------------------------------------------------------------------------------
1 | class Finder < ContentItem
2 | attr_reader :facets, :show_metadata_block, :show_table_of_contents
3 |
4 | def initialize(content_store_response)
5 | super(content_store_response)
6 |
7 | @facets = content_store_response.dig("details", "facets")
8 | @show_metadata_block = content_store_response.dig("details", "show_metadata_block")
9 | @show_table_of_contents = content_store_response.dig("details", "show_table_of_contents")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/flexible_page.rb:
--------------------------------------------------------------------------------
1 | class FlexiblePage < ContentItem
2 | attr_reader :flexible_sections
3 |
4 | def initialize(content_store_response)
5 | super
6 |
7 | @flexible_sections = (content_store_response.dig("details", "flexible_sections") || []).map { |hash| FlexiblePage::FlexibleSectionFactory.build(hash, self) }
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/flexible_page/flexible_section/base.rb:
--------------------------------------------------------------------------------
1 | module FlexiblePage::FlexibleSection
2 | class Base
3 | attr_reader :flexible_section_hash, :content_item, :type
4 |
5 | def initialize(flexible_section_hash, content_item)
6 | @flexible_section_hash = flexible_section_hash
7 | @type = flexible_section_hash["type"]
8 | @content_item = content_item
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/flexible_page/flexible_section/page_title.rb:
--------------------------------------------------------------------------------
1 | module FlexiblePage::FlexibleSection
2 | class PageTitle < Base
3 | attr_reader :context, :heading_text, :lead_paragraph
4 |
5 | def initialize(flexible_section_hash, content_item)
6 | super
7 |
8 | @context = flexible_section_hash["context"]
9 | @heading_text = flexible_section_hash["heading_text"]
10 | @lead_paragraph = flexible_section_hash["lead_paragraph"]
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/flexible_page/flexible_section/rich_content.rb:
--------------------------------------------------------------------------------
1 | module FlexiblePage::FlexibleSection
2 | class RichContent < Base
3 | ContentImage = Data.define(:alt, :src)
4 |
5 | attr_reader :contents_list, :govspeak, :image
6 |
7 | def initialize(flexible_section_hash, content_item)
8 | super
9 |
10 | @contents_list = ContentsOutline.new(flexible_section_hash["contents_list"])
11 | @govspeak = flexible_section_hash["govspeak"]
12 |
13 | if flexible_section_hash["image"].present?
14 | alt, src = flexible_section_hash.fetch("image").values_at("alt", "src")
15 | @image = ContentImage.new(alt:, src:)
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/models/flexible_page/flexible_section_factory.rb:
--------------------------------------------------------------------------------
1 | class FlexiblePage::FlexibleSectionFactory
2 | def self.build(flexible_section_hash, content_item)
3 | section_class(flexible_section_hash["type"]).new(flexible_section_hash, content_item)
4 | end
5 |
6 | def self.section_class(type)
7 | "FlexiblePage::FlexibleSection::#{type.camelize}".constantize
8 | rescue StandardError
9 | raise("Couldn't identify a model class for type: #{type}")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/homepage.rb:
--------------------------------------------------------------------------------
1 | class Homepage < ContentItem
2 | def popular_links
3 | @popular_links ||= popular_links_data&.collect(&:with_indifferent_access)
4 | end
5 |
6 | private
7 |
8 | def popular_links_data
9 | @popular_links_data ||= links.dig("popular_links", 0, "details", "link_items")
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/landing_page.rb:
--------------------------------------------------------------------------------
1 | class LandingPage < ContentItem
2 | attr_reader :blocks, :navigation_groups, :breadcrumbs, :theme
3 |
4 | ADDITIONAL_CONTENT_PATH = "lib/data/landing_page_content_items".freeze
5 |
6 | def initialize(content_store_response)
7 | super
8 |
9 | @breadcrumbs = content_store_response.dig("details", "breadcrumbs")&.map { { title: _1["title"], url: _1["href"] } }
10 | @navigation_groups = (content_store_response.dig("details", "navigation_groups") || []).index_by { _1["id"] }
11 | @blocks = (content_store_response.dig("details", "blocks") || []).map { |block_hash| BlockFactory.build(block_hash, self) }
12 | @theme = safe_theme(content_store_response.dig("details", "theme"))
13 | end
14 |
15 | private
16 |
17 | def safe_theme(value)
18 | return value if value == "prime-ministers-office-10-downing-street"
19 |
20 | "default"
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/action_link.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class ActionLink < Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/base.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Base
3 | attr_reader :id, :data, :landing_page, :type
4 |
5 | def initialize(block_hash, landing_page)
6 | @data = block_hash
7 | @id = data["id"]
8 | @landing_page = landing_page
9 | @type = data["type"]
10 | end
11 |
12 | def full_width?
13 | false
14 | end
15 |
16 | def full_width_background?
17 | @data["full_width_background"]
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/block_error.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class BlockError < Base
3 | attr_reader :error
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | @error = data["error"]
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/blocks_container.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class BlocksContainer < LayoutBase
3 | alias_method :children, :blocks
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/box.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Box < Base
3 | attr_reader :box_content, :href, :content
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | @href = data["href"] || ""
9 | @content = data["content"] || ""
10 |
11 | @box_content = LandingPage::BlockFactory.build_all(data.dig("box_content", "blocks"), landing_page)
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/card.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | CardImage = Data.define(:alt, :source)
3 |
4 | class Card < Base
5 | attr_reader :image, :card_content, :href, :content
6 |
7 | def initialize(block_hash, landing_page)
8 | super
9 |
10 | @href = data["href"] || ""
11 | @content = data["content"] || ""
12 |
13 | if data["image"].present?
14 | alt, source = data.fetch("image").values_at("alt", "source")
15 | @image = CardImage.new(alt:, source:)
16 | end
17 |
18 | @card_content = LandingPage::BlockFactory.build_all(data.dig("card_content", "blocks"), landing_page)
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/columns_layout.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class ColumnsLayout < LayoutBase
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/featured.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | FeaturedImageSources = Data.define(:desktop, :desktop_2x, :tablet, :tablet_2x, :mobile, :mobile_2x)
3 | FeaturedImage = Data.define(:alt, :sources)
4 |
5 | class Featured < Base
6 | attr_reader :image, :featured_content
7 |
8 | def initialize(block_hash, landing_page)
9 | super
10 |
11 | if data["image"].present?
12 | alt, sources = data.fetch("image").values_at("alt", "sources")
13 | sources = FeaturedImageSources.new(**sources)
14 | @image = FeaturedImage.new(alt:, sources:)
15 | end
16 |
17 | @featured_content = data.dig("featured_content", "blocks")&.map { |subblock_hash| LandingPage::BlockFactory.build(subblock_hash, landing_page) }
18 | end
19 |
20 | def full_width?
21 | false
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/govspeak.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Govspeak < Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/heading.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Heading < Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/image.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | ImageSources = Data.define(:desktop, :desktop_2x, :tablet, :tablet_2x, :mobile, :mobile_2x)
3 | ImageData = Data.define(:alt, :sources)
4 |
5 | class Image < Base
6 | attr_reader :image
7 |
8 | def initialize(block_hash, landing_page)
9 | super
10 |
11 | if data["image"].present?
12 | image_config = data.fetch("image")
13 | alt = image_config.fetch("alt", "")
14 | sources = image_config.fetch("sources")
15 | sources = ImageSources.new(**sources)
16 | @image = ImageData.new(alt:, sources:)
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/layout_base.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class LayoutBase < Base
3 | attr_reader :blocks
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | @blocks = LandingPage::BlockFactory.build_all(data["blocks"], landing_page)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/logo.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Logo < Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/main_navigation.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class MainNavigation < Base
3 | attr_reader :name, :links
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | nav_group_id = data["navigation_group_id"]
9 | raise "Main Navigation block points to a missing navigation group: #{nav_group_id}" unless landing_page.navigation_groups.key?(nav_group_id)
10 |
11 | navigation_group = landing_page.navigation_groups[nav_group_id]
12 | @name = navigation_group["name"]
13 | @links = navigation_group["links"]
14 | end
15 |
16 | def full_width?
17 | true
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/quote.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class Quote < Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/share_links.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class ShareLinks < Base
3 | attr_reader :links
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | @links = data.fetch("links").map { |l| { href: l["href"], text: l["text"], icon: l["icon"], hidden_text: l["hidden_text"] } }
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/landing_page/block/side_navigation.rb:
--------------------------------------------------------------------------------
1 | module LandingPage::Block
2 | class SideNavigation < Base
3 | attr_reader :links
4 |
5 | def initialize(block_hash, landing_page)
6 | super
7 |
8 | nav_group_id = data["navigation_group_id"]
9 | raise "Side Navigation block points to a missing navigation group: #{nav_group_id}" unless landing_page.navigation_groups.key?(nav_group_id)
10 |
11 | @links = landing_page.navigation_groups[nav_group_id]["links"]
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/landing_page/block_factory.rb:
--------------------------------------------------------------------------------
1 | class LandingPage::BlockFactory
2 | def self.build_all(block_array, landing_page)
3 | (block_array || []).map { |block| build(block, landing_page) }
4 | end
5 |
6 | def self.build(block_hash, landing_page)
7 | block_class(block_hash["type"]).new(block_hash, landing_page)
8 | rescue StandardError => e
9 | LandingPage::Block::BlockError.new({ "type" => "block_error", "error" => e }, landing_page)
10 | end
11 |
12 | def self.block_class(type)
13 | "LandingPage::Block::#{type.camelize}".constantize
14 | rescue StandardError
15 | raise("Couldn't identify a model class for type: #{type}")
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/models/licence_transaction.rb:
--------------------------------------------------------------------------------
1 | class LicenceTransaction < ContentItem
2 | attr_reader :body,
3 | :licence_transaction_continuation_link,
4 | :licence_transaction_licence_identifier,
5 | :licence_transaction_will_continue_on
6 |
7 | def initialize(content_store_response)
8 | super(content_store_response)
9 |
10 | @body = content_store_response.dig("details", "body")
11 | @licence_transaction_continuation_link = content_store_response.dig("details", "metadata", "licence_transaction_continuation_link")
12 | @licence_transaction_licence_identifier = content_store_response.dig("details", "metadata", "licence_transaction_licence_identifier")
13 | @licence_transaction_will_continue_on = content_store_response.dig("details", "metadata", "licence_transaction_will_continue_on")
14 | end
15 |
16 | def slug
17 | URI.parse(base_path).path.split("/").last
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/models/locations_api_postcode_response.rb:
--------------------------------------------------------------------------------
1 | class LocationsApiPostcodeResponse
2 | attr_reader :local_custodian_codes, :error, :postcode
3 |
4 | def initialize(postcode, local_custodian_codes, error)
5 | @postcode = postcode
6 | @local_custodian_codes = local_custodian_codes
7 | @error = error
8 | end
9 |
10 | def location_not_found?
11 | postcode && local_custodian_codes.empty? && error.nil?
12 | end
13 |
14 | def invalid_postcode?
15 | postcode && local_custodian_codes.nil? && error.present?
16 | end
17 |
18 | def blank_postcode?
19 | postcode.blank?
20 | end
21 |
22 | def single_authority?
23 | local_custodian_codes.count == 1
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/models/logo.rb:
--------------------------------------------------------------------------------
1 | class Logo
2 | attr_reader :crest, :formatted_title, :image
3 |
4 | def initialize(logo)
5 | @image = get_image(logo["image"])
6 | @crest = logo["crest"]
7 | @formatted_title = logo["formatted_title"]
8 | end
9 |
10 | def get_image(logo_image)
11 | return unless logo_image
12 |
13 | OpenStruct.new(
14 | alt_text: logo_image["alt_text"],
15 | url: logo_image["url"],
16 | )
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/news_article.rb:
--------------------------------------------------------------------------------
1 | class NewsArticle < ContentItem
2 | include EmphasisedOrganisations
3 | include NewsImage
4 | include People
5 | include Political
6 | include Updatable
7 | include Withdrawable
8 | include WorldwideOrganisations
9 |
10 | def contributors
11 | (organisations_ordered_by_emphasis + worldwide_organisations + people).uniq
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/organisation.rb:
--------------------------------------------------------------------------------
1 | class Organisation < ContentItem
2 | attr_reader :logo
3 |
4 | def initialize(organisation_data)
5 | super(organisation_data)
6 | @logo = Logo.new(organisation_data.dig("details", "logo"))
7 | @organisation_data = organisation_data
8 | end
9 |
10 | def brand
11 | organisation_data.dig("details", "brand")
12 | end
13 |
14 | private
15 |
16 | attr_reader :organisation_data
17 | end
18 |
--------------------------------------------------------------------------------
/app/models/place.rb:
--------------------------------------------------------------------------------
1 | class Place < ContentItem
2 | attr_reader :introduction, :more_information, :need_to_know, :place_type
3 |
4 | def initialize(content_store_response)
5 | super(content_store_response)
6 |
7 | @introduction = content_store_response.dig("details", "introduction")
8 | @more_information = content_store_response.dig("details", "more_information")
9 | @need_to_know = content_store_response.dig("details", "need_to_know")
10 | @place_type = content_store_response.dig("details", "place_type")
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/record_not_found.rb:
--------------------------------------------------------------------------------
1 | class RecordNotFound < StandardError
2 | end
3 |
--------------------------------------------------------------------------------
/app/models/service_manual_homepage.rb:
--------------------------------------------------------------------------------
1 | class ServiceManualHomepage < ContentItem
2 | def topics
3 | linked("children")
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/service_manual_service_toolkit.rb:
--------------------------------------------------------------------------------
1 | class ServiceManualServiceToolkit < ContentItem
2 | attr_reader :collections
3 |
4 | def initialize(content_store_response)
5 | super(content_store_response)
6 |
7 | @collections = content_store_response["details"].fetch("collections", [])
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/simple_smart_answer.rb:
--------------------------------------------------------------------------------
1 | class SimpleSmartAnswer < ContentItem
2 | attr_reader :body, :nodes, :start_button_text
3 |
4 | def initialize(content_store_response)
5 | super(content_store_response)
6 |
7 | @body = content_store_response.dig("details", "body")
8 | @nodes = content_store_response.dig("details", "nodes")
9 | @start_button_text = content_store_response.dig("details", "start_button_text")
10 | end
11 |
12 | def slug
13 | @slug = URI.parse(@base_path).path.sub(%r{\A/}, "")
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/models/speech.rb:
--------------------------------------------------------------------------------
1 | class Speech < ContentItem
2 | include EmphasisedOrganisations
3 | include NewsImage
4 | include People
5 | include Political
6 | include Updatable
7 |
8 | def contributors
9 | (organisations_ordered_by_emphasis + people + speaker).compact.uniq(&:content_id)
10 | end
11 |
12 | def speaker
13 | linked("speaker")
14 | end
15 |
16 | def speaker_without_profile
17 | content_store_response.dig("details", "speaker_without_profile")
18 | end
19 |
20 | def location
21 | content_store_response.dig("details", "location")
22 | end
23 |
24 | def delivered_on_date
25 | content_store_response.dig("details", "delivered_on")
26 | end
27 |
28 | def speech_type_explanation
29 | explanation = content_store_response.dig("details", "speech_type_explanation")
30 | " (#{explanation})" if explanation
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/models/uprn.rb:
--------------------------------------------------------------------------------
1 | class Uprn
2 | UPRN_PATTERN = %r{^\d{1,12}$}
3 |
4 | delegate :present?, to: :sanitized_uprn
5 |
6 | def initialize(uprn)
7 | @uprn = uprn || ""
8 | end
9 |
10 | def valid?
11 | sanitized_uprn.match?(UPRN_PATTERN)
12 | end
13 |
14 | def sanitized_uprn
15 | uprn.strip
16 | end
17 |
18 | def error
19 | "invalidUprnFormat" unless valid?
20 | end
21 |
22 | private
23 |
24 | attr_reader :uprn
25 | end
26 |
--------------------------------------------------------------------------------
/app/presenters/address_list_presenter.rb:
--------------------------------------------------------------------------------
1 | class AddressListPresenter
2 | def initialize(addresses)
3 | @addresses = addresses
4 | end
5 |
6 | def component_options
7 | addresses_with_authority_data.map { |address| { text: address[:address], value: address[:local_authority_slug] } }
8 | end
9 |
10 | def addresses_with_authority_data
11 | @addresses_with_authority_data ||= @addresses.map do |address|
12 | local_authority = LocalAuthority.from_local_custodian_code(address.local_custodian_code)
13 | {
14 | address: address.address,
15 | local_authority_slug: local_authority.slug,
16 | local_authority_name: local_authority.name,
17 | }
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/presenters/content_item_presenter.rb:
--------------------------------------------------------------------------------
1 | class ContentItemPresenter
2 | attr_reader :content_item
3 |
4 | def initialize(content_item)
5 | @content_item = content_item
6 | end
7 |
8 | def contributor_links
9 | content_item.contributors.map do |content_item|
10 | { text: content_item.title, path: content_item.base_path }
11 | end
12 | end
13 |
14 | def page_title
15 | content_item.withdrawn? ? "[Withdrawn] #{content_item.title}" : content_item.title
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/presenters/contents_outline_presenter.rb:
--------------------------------------------------------------------------------
1 | class ContentsOutlinePresenter
2 | attr_reader :contents_outline
3 |
4 | def initialize(contents_outline)
5 | @contents_outline = contents_outline
6 | end
7 |
8 | def for_contents_list_component
9 | items_to_component_h(contents_outline.items)
10 | end
11 |
12 | private
13 |
14 | def items_to_component_h(items)
15 | items.map do |item|
16 | {
17 | href: "##{item.id}",
18 | text: item.text.gsub(/:$/, ""),
19 | items: items_to_component_h(item.items),
20 | }
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/presenters/local_authority_presenter.rb:
--------------------------------------------------------------------------------
1 | class LocalAuthorityPresenter
2 | def initialize(local_authority)
3 | @local_authority = local_authority
4 | end
5 |
6 | PASS_THROUGH_KEYS = %i[
7 | name
8 | snac
9 | gss
10 | tier
11 | homepage_url
12 | country_name
13 | ].freeze
14 |
15 | PASS_THROUGH_KEYS.each do |key|
16 | define_method key do
17 | local_authority[key.to_s]
18 | end
19 | end
20 |
21 | def url
22 | @url ||= extract_first_url
23 | end
24 |
25 | private
26 |
27 | attr_accessor :local_authority
28 |
29 | def extract_first_url
30 | homepage_url.presence || nil
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/presenters/place_presenter.rb:
--------------------------------------------------------------------------------
1 | class PlacePresenter < ContentItemPresenter
2 | attr_reader :places
3 |
4 | def initialize(content_item, places = [])
5 | super(content_item)
6 | @places = format_places(places)
7 | end
8 |
9 | def preposition
10 | return "near" unless @places.any?
11 |
12 | @places.first["gss"].blank? ? "near" : "for"
13 | end
14 |
15 | private
16 |
17 | def format_places(places)
18 | places.each do |place|
19 | place["text"] = place["url"] if place["url"]
20 | place["address"] = place
21 | .values_at("address1", "address2")
22 | .compact
23 | .map(&:strip)
24 | .join(", ")
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/presenters/service_manual_homepage_presenter.rb:
--------------------------------------------------------------------------------
1 | class ServiceManualHomepagePresenter < ContentItemPresenter
2 | def sorted_topics
3 | content_item.topics.sort_by(&:title).map do |topic|
4 | {
5 | link: {
6 | path: topic.base_path,
7 | text: topic.title,
8 | },
9 | description: topic.description,
10 | }
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/presenters/simple_smart_answer_presenter.rb:
--------------------------------------------------------------------------------
1 | class SimpleSmartAnswerPresenter < ContentItemPresenter
2 | def start_button_text
3 | if content_item.start_button_text == "Start now"
4 | I18n.t("formats.start_now")
5 | elsif content_item.start_button_text == "Continue"
6 | I18n.t("continue")
7 | else
8 | content_item.start_button_text
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/presenters/speech_presenter.rb:
--------------------------------------------------------------------------------
1 | class SpeechPresenter < ContentItemPresenter
2 | def speech_contributor_links
3 | return contributor_links unless content_item.speaker_without_profile
4 |
5 | contributor_links + [{ text: content_item.speaker_without_profile }]
6 | end
7 |
8 | def delivery_type
9 | return I18n.t("formats.speech.written_on") if content_item.document_type == "authored_article"
10 |
11 | I18n.t("formats.speech.delivered_on")
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/presenters/transaction_presenter.rb:
--------------------------------------------------------------------------------
1 | class TransactionPresenter < ContentItemPresenter
2 | def start_button_text
3 | if content_item.start_button_text.blank?
4 | return I18n.t("formats.start_now")
5 | end
6 |
7 | case content_item.start_button_text
8 | when "Start now"
9 | I18n.t("formats.start_now")
10 | when "Sign in"
11 | I18n.t("formats.transaction.sign_in")
12 | else
13 | content_item.start_button_text
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/application/_draft_fields.html.erb:
--------------------------------------------------------------------------------
1 | <% if @edition %>
2 |
3 | <% end %>
4 |
5 | <% if params[:token] %>
6 |
7 | <% end %>
8 |
9 | <% if params[:cache] %>
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/app/views/calendar/_calendar_footer.html.erb:
--------------------------------------------------------------------------------
1 |
7 | <%= I18n.t("csv_preview.access_limited.body") %> 8 |
9 |<%= name %>
7 | <% end %> 8 | 9 |<%= description %>
10 | 11 | <% if contact_details %> 12 | <%= render "govuk_publishing_components/components/govspeak", { 13 | } do %> 14 | 15 | <% contact_details.each do |address_line| %> 16 | <%= address_line %><%= t("formats.local_transaction.find_council_website") %>
5 | <%= render partial: "location_form", 6 | locals: { 7 | format: "service", 8 | publication_format: "find_local_council", 9 | postcode: @postcode, 10 | margin_top: @location_error ? 5 : 0, 11 | publication_title: t("formats.local_transaction.find_council", locale: :en), 12 | } %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/flexible_page/flexible_sections/_page_title.html.erb: -------------------------------------------------------------------------------- 1 |<%= t("ab_testing.explanation") %>
17 | 18 |<%= t("ab_testing.two_versions") %> <%= @requested_variant.variant?("A") ? t("a") : t("b") %> <%= t("ab_testing.version") %>.
19 | 20 |<%= t("ab_testing.visit_govuk_html") %>
21 |<%= t("attachment.virus_check.message") %>
11 |Go back and change the information you entered.
', 4 | status_code: 400, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/401.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "You are not permitted to see this page", 3 | intro: 'This page is restricted to certain users only.
', 4 | status_code: 401, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/403.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "You are not permitted to see this page", 3 | intro: 'This page is restricted to certain users only.
', 4 | status_code: 403, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/404.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Page not found", 3 | intro: 'If you entered a web address, check it is correct.
4 |You can browse from the homepage or use the search box above to find the information you need.
', 5 | status_code: 404, 6 | } %> 7 | -------------------------------------------------------------------------------- /app/views/static_error_pages/405.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "You are not allowed to do this action", 3 | intro: 'This page does not support the action you requested.
', 4 | status_code: 405, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/406.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "The page you’re looking for cannot be found", 3 | intro: 'If you entered a web address, check it is correct.
4 |You can browse from the homepage or use the search box above to find the information you need.
', 5 | status_code: 406, 6 | } %> 7 | -------------------------------------------------------------------------------- /app/views/static_error_pages/410.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "The page you’re looking for is no longer here", 3 | intro: 'It may have been moved or replaced.
4 |If you entered a web address, check it is correct.
5 |You can browse from the homepage or use the search box above to find the information you need.
', 6 | status_code: 410, 7 | } %> 8 | -------------------------------------------------------------------------------- /app/views/static_error_pages/422.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, there is a problem", 3 | intro: 'Go back and change the information you entered.
', 4 | status_code: 422, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/429.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, there is a problem", 3 | intro: 'There have been too many attempts to access this page.
4 |Try again later.
', 5 | status_code: 429, 6 | } %> 7 | -------------------------------------------------------------------------------- /app/views/static_error_pages/500.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, we’re experiencing technical difficulties", 3 | intro: 'Please try again in a few moments.
', 4 | status_code: 500, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/501.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, we’re experiencing technical difficulties", 3 | intro: 'Please try again in a few moments.
', 4 | status_code: 501, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/502.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, we’re experiencing technical difficulties", 3 | intro: 'Please try again in a few moments.
', 4 | status_code: 502, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/503.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, we’re experiencing technical difficulties", 3 | intro: 'Please try again in a few moments.
', 4 | status_code: 503, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/static_error_pages/504.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: "error_page", locals: { 2 | heading: "Sorry, we’re experiencing technical difficulties", 3 | intro: 'Please try again in a few moments.
', 4 | status_code: 504, 5 | } %> 6 | -------------------------------------------------------------------------------- /app/views/travel_advice/_country.atom.builder: -------------------------------------------------------------------------------- 1 | feed.entry( 2 | country, 3 | id: country.feed_id, 4 | url: country.web_url, 5 | ) do |entry| 6 | entry.title(country.title) 7 | entry.link(rel: "self", type: "application/atom+xml", href: "#{country.web_url}.atom") 8 | entry.summary(type: :xhtml) do |summary| 9 | summary << format_atom_change_description(country.change_description) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/travel_advice/index.atom.builder: -------------------------------------------------------------------------------- 1 | atom_feed(root_url: travel_advice_url, id: travel_advice_url) do |feed| 2 | feed.title("Travel Advice Summary") 3 | feed.updated(@presenter.countries_by_date.first.updated_at) 4 | feed.author do |author| 5 | author.name "GOV.UK" 6 | end 7 | render partial: "country", collection: @presenter.countries_by_date.take(20), locals: { feed: } 8 | end 9 | -------------------------------------------------------------------------------- /app/views/travel_advice/show.atom.builder: -------------------------------------------------------------------------------- 1 | atom_feed(root_url: canonical_url(@content_item.base_path), id: canonical_url(@content_item.base_path)) do |feed| 2 | feed.title("Travel Advice Summary") 3 | feed.updated Time.zone.parse(@content_item.public_updated_at) 4 | feed.author do |author| 5 | author.name "GOV.UK" 6 | end 7 | feed.entry(@content_item, id: "#{canonical_url(@content_item.base_path)}##{Time.zone.parse(@content_item.public_updated_at)}", url: canonical_url(@content_item.base_path), updated: Time.zone.parse(@content_item.public_updated_at)) do |entry| 8 | entry.title(@content_item.title) 9 | entry.link(rel: "self", type: "application/atom+xml", href: "#{canonical_url(@content_item.base_path)}.atom") 10 | entry.summary(type: :xhtml) do |summary| 11 | summary << format_atom_change_description(@content_item.change_description) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /bin/brakeman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | ARGV.unshift("--ensure-latest") 6 | 7 | load Gem.bin_path("brakeman", "brakeman") 8 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 3 | load Gem.bin_path("bundler", "bundle") 4 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if ! gem list foreman -i --silent; then 4 | echo "Installing foreman..." 5 | gem install foreman 6 | fi 7 | 8 | exec foreman start -f Procfile.dev "$@" 9 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | # explicit rubocop config increases performance slightly while avoiding config confusion. 6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) 7 | 8 | load Gem.bin_path("rubocop", "rubocop") 9 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "pathname" 3 | require "fileutils" 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path("..", __dir__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts "== Installing dependencies ==" 18 | system! "gem install bundler --conservative" 19 | system("bundle check") || system!("bundle install") 20 | 21 | puts "\n== Updating database ==" 22 | system! "bin/rails db:migrate" 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! "bin/rails log:clear tmp:clear" 26 | 27 | puts "\n== Restarting application server ==" 28 | system! "bin/rails restart" 29 | end 30 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR). 5 | select { |dir| File.expand_path(dir) != __dir__ }. 6 | product(["yarn", "yarn.cmd", "yarn.ps1"]). 7 | map { |dir, file| File.expand_path(file, dir) }. 8 | find { |file| File.executable?(file) } 9 | 10 | if yarn 11 | exec yarn, *ARGV 12 | else 13 | $stderr.puts "Yarn executable was not detected in the system." 14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 15 | exit 1 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path("../config/environment", __FILE__) 4 | run Frontend::Application 5 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/brakeman.ignore: -------------------------------------------------------------------------------- 1 | { 2 | "ignored_warnings": [ 3 | 4 | ], 5 | "updated": "2021-06-08 15:56:59 +0000", 6 | "brakeman_version": "5.0.2" 7 | } 8 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/i18n-tasks.yml: -------------------------------------------------------------------------------- 1 | data: 2 | yaml: 3 | write: 4 | line_width: -1 5 | ignore_unused: 6 | - 'formats.local_transaction.*' 7 | - 'formats.find_my_nearest.valid_postcode_no_locations' 8 | - 'common.nations.*' 9 | - 'common.substitute_day' 10 | - 'bank_holidays.*' 11 | - 'bank-holidays' 12 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Add Yarn node_modules folder to the asset load path. 8 | Rails.application.config.assets.paths << Rails.root.join("node_modules") 9 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | GovukContentSecurityPolicy.configure 2 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/date_formats.rb: -------------------------------------------------------------------------------- 1 | Date::DATE_FORMATS[:short_ordinal] = "%d %B %Y" 2 | Time::DATE_FORMATS[:short_ordinal] = "%d %B %Y" 3 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += %i[ 7 | password passw email secret token _key crypt salt certificate otp ssn cvv cvc 8 | ] 9 | -------------------------------------------------------------------------------- /config/initializers/govuk_publishing_components.rb: -------------------------------------------------------------------------------- 1 | if defined?(GovukPublishingComponents) 2 | GovukPublishingComponents.configure do |c| 3 | c.component_guide_title = "Frontend Component Guide" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/local_links_manager.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/local_links_manager" 2 | 3 | Frontend.local_links_manager_api = GdsApi::LocalLinksManager.new(Plek.new.find("local-links-manager")) 4 | -------------------------------------------------------------------------------- /config/initializers/locations_api.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/locations_api" 2 | require "plek" 3 | 4 | Frontend.locations_api = GdsApi::LocationsApi.new(Plek.new.find("locations-api")) 5 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | Mime::Type.register_alias "text/html", :video 7 | Mime::Type.register_alias "text/html", :raw 8 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 5 | 6 | # Rails.application.config.permissions_policy do |policy| 7 | # policy.camera :none 8 | # policy.gyroscope :none 9 | # policy.microphone :none 10 | # policy.usb :none 11 | # policy.fullscreen :self 12 | # policy.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /config/initializers/places_manager.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/places_manager" 2 | 3 | Frontend.places_manager_api = GdsApi::PlacesManager.new(Plek.new.find("places-manager")) 4 | 5 | Frontend::PLACES_MANAGER_QUERY_LIMIT = 10 6 | -------------------------------------------------------------------------------- /config/initializers/prometheus.rb: -------------------------------------------------------------------------------- 1 | require "govuk_app_config/govuk_prometheus_exporter" 2 | GovukPrometheusExporter.configure 3 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: "_frontend_session" 4 | -------------------------------------------------------------------------------- /config/initializers/slimmer.rb: -------------------------------------------------------------------------------- 1 | Frontend::Application.configure do 2 | config.slimmer.logger = Rails.logger 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/website_root.rb: -------------------------------------------------------------------------------- 1 | require "plek" 2 | 3 | Frontend.govuk_website_root = Plek.new.website_root 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | require "govuk_app_config/govuk_puma" 2 | GovukPuma.configure_rails(self) 3 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :concurrency: 2 3 | :queues: 4 | - default 5 | - mailers 6 | -------------------------------------------------------------------------------- /docs/images/homepage-promotion-slots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/docs/images/homepage-promotion-slots.png -------------------------------------------------------------------------------- /lib/api_error_routing_constraint.rb: -------------------------------------------------------------------------------- 1 | class ApiErrorRoutingConstraint 2 | def matches?(request) 3 | ContentItemLoader.for_request(request).load(request.path).is_a?(StandardError) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/data/specialist_documents/protected_food_drink_name/images.yaml: -------------------------------------------------------------------------------- 1 | protected-designation-of-origin-pdo: 2 | file_name: protected-designation-of-origin-pdo.png 3 | alt_text_tag: pdo_alt_text 4 | protected-geographical-indication-pgi: 5 | file_name: protected-geographical-indication-pgi.png 6 | alt_text_tag: pgi_alt_text 7 | traditional-speciality-guaranteed-tsg: 8 | file_name: traditional-speciality-guaranteed-tsg.png 9 | alt_text_tag: tsg_alt_text -------------------------------------------------------------------------------- /lib/format_routing_constraint.rb: -------------------------------------------------------------------------------- 1 | class FormatRoutingConstraint 2 | def initialize(format) 3 | @format = format 4 | end 5 | 6 | def matches?(request) 7 | content_item = ContentItemLoader.for_request(request).load(key(request)) 8 | content_item.is_a?(GdsApi::Response) && content_item["schema_name"] == @format 9 | end 10 | 11 | private 12 | 13 | def key(request) 14 | "/#{request.params.fetch(:slug)}" 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/frontend.rb: -------------------------------------------------------------------------------- 1 | module Frontend 2 | mattr_accessor :organisations_search_client 3 | mattr_accessor :search_client 4 | mattr_accessor :locations_api 5 | mattr_accessor :places_manager_api 6 | mattr_accessor :local_links_manager_api 7 | mattr_accessor :govuk_website_root 8 | end 9 | -------------------------------------------------------------------------------- /lib/full_path_format_routing_constraint.rb: -------------------------------------------------------------------------------- 1 | class FullPathFormatRoutingConstraint < FormatRoutingConstraint 2 | private 3 | 4 | def key(request) 5 | request.path 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/postcode_sanitizer.rb: -------------------------------------------------------------------------------- 1 | class PostcodeSanitizer 2 | def self.sanitize(postcode) 3 | if postcode.present? 4 | # Strip trailing whitespace, non-alphanumerics, and use the 5 | # uk_postcode gem to potentially transpose O/0 and I/1. 6 | UKPostcode.parse(postcode.gsub(/[^a-z0-9 ]/i, "").strip).to_s 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/sanitiser/strategy.rb: -------------------------------------------------------------------------------- 1 | module Sanitiser 2 | class Strategy 3 | class SanitisingError < StandardError; end 4 | 5 | class << self 6 | def call(input, sanitize_null_bytes: false) 7 | input 8 | .force_encoding(Encoding::ASCII_8BIT) 9 | .encode!(Encoding::UTF_8) 10 | if sanitize_null_bytes && Rack::UTF8Sanitizer::NULL_BYTE_REGEX.match?(input) 11 | raise NullByteInString 12 | end 13 | rescue StandardError 14 | raise SanitisingError, "Non-UTF-8 (or null) character in the query, cookie or form data" 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/simple_smart_answers/base_error.rb: -------------------------------------------------------------------------------- 1 | module SimpleSmartAnswers 2 | class BaseError < StandardError; end 3 | end 4 | -------------------------------------------------------------------------------- /lib/simple_smart_answers/flow.rb: -------------------------------------------------------------------------------- 1 | require "simple_smart_answers/node" 2 | require "simple_smart_answers/state" 3 | 4 | module SimpleSmartAnswers 5 | class Flow 6 | def initialize(node_details) 7 | @nodes = {} 8 | node_details.each_with_index do |n, i| 9 | node = Node.new(self, n) 10 | @nodes[node.slug] = node 11 | @start_node = node if i.zero? 12 | end 13 | end 14 | 15 | attr_reader :start_node 16 | 17 | def node_for_slug(slug) 18 | @nodes[slug] 19 | end 20 | 21 | def state_for_responses(responses) 22 | state = State.new(self, start_node) 23 | state.process_responses(responses) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/simple_smart_answers/invalid_response.rb: -------------------------------------------------------------------------------- 1 | module SimpleSmartAnswers 2 | class InvalidResponse < BaseError; end 3 | end 4 | -------------------------------------------------------------------------------- /lib/simple_smart_answers/state.rb: -------------------------------------------------------------------------------- 1 | module SimpleSmartAnswers 2 | class State 3 | def initialize(flow, initial_node) 4 | @flow = flow 5 | @current_node = initial_node 6 | @completed_questions = [] 7 | @error = false 8 | end 9 | 10 | attr_reader :current_node, :completed_questions 11 | 12 | def error? 13 | @error 14 | end 15 | 16 | def process_responses(responses) 17 | responses.each do |response| 18 | add_response(response) 19 | break if error? 20 | end 21 | self 22 | end 23 | 24 | def add_response(response_slug) 25 | response = @current_node.process_response(response_slug) 26 | @current_node = response.next_node 27 | @completed_questions << response 28 | rescue InvalidResponse 29 | @error = true 30 | end 31 | 32 | def current_question_number 33 | @completed_questions.size + 1 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/jasmine.rake: -------------------------------------------------------------------------------- 1 | desc "Run Javascript tests" 2 | task jasmine: [:environment] do 3 | sh "yarn run jasmine:ci" 4 | end 5 | -------------------------------------------------------------------------------- /lib/tasks/lint.rake: -------------------------------------------------------------------------------- 1 | desc "Run all linters" 2 | task lint: :environment do 3 | sh "bundle exec rubocop" 4 | sh "bundle exec erb_lint --lint-all" 5 | sh "yarn run lint" 6 | end 7 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/log/.gitkeep -------------------------------------------------------------------------------- /spec/factories/content_items.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :content_item do 3 | initialize_with { new(attributes.deep_stringify_keys) } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/fixtures/landing_page_statistics_data/data_four.csv: -------------------------------------------------------------------------------- 1 | ,Generation X,Millenials 2 | 2020,10,15 3 | 2021,20,25 4 | 2023,25,30 5 | 2024,30,35 6 | -------------------------------------------------------------------------------- /spec/fixtures/landing_page_statistics_data/data_one.csv: -------------------------------------------------------------------------------- 1 | ,Percentage of people being kinder to one and other 2 | 1/6/2013,0.500 3 | 1/6/2014,0.600 4 | 1/6/2015,0.700 5 | 1/6/2016,0.500 6 | 1/6/2017,0.600 7 | 1/6/2018,0.700 8 | 1/6/2019,0.550 9 | -------------------------------------------------------------------------------- /spec/fixtures/landing_page_statistics_data/data_three.csv: -------------------------------------------------------------------------------- 1 | ,The number of people working smarter not harder 2 | 31/12/2022,45775 3 | 31/3/2023,47518 4 | 30/6/2023,50546 5 | 30/9/2023,56042 6 | 31/12/2023,45768 7 | 31/3/2024,34530 8 | 30/6/2024,29585 -------------------------------------------------------------------------------- /spec/fixtures/landing_page_statistics_data/data_two.csv: -------------------------------------------------------------------------------- 1 | ,Percentage of people being kinder to one and other 2 | 1/6/2013,0.500 3 | 1/6/2014,0.600 4 | 1/6/2015,0.700 5 | 1/6/2016,0.500 6 | 1/6/2017,0.600 7 | 1/6/2018,0.700 8 | 1/6/2019,0.500 9 | 1/6/2020, 10 | 1/6/2021, 11 | 1/6/2022,0.500 12 | 1/6/2023,0.600 -------------------------------------------------------------------------------- /spec/fixtures/local-content-items/my-json-item.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_name": "json_page" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/local-content-items/my-yaml-item.yml: -------------------------------------------------------------------------------- 1 | schema_name: yaml_page 2 | -------------------------------------------------------------------------------- /spec/fixtures/single-calendar.json: -------------------------------------------------------------------------------- 1 | { 2 | "need_id": 42, 3 | "divisions": { 4 | "england-and-wales": { 5 | "2011": [ 6 | { 7 | "title": "New Year's Day", 8 | "date": "02/01/2011", 9 | "notes": "Substitute day" 10 | } 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/helpers/block_helper_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe BlockHelper do 2 | include described_class 3 | 4 | describe "#render_block" do 5 | it "returns an empty string when a partial template doesn't exist" do 6 | block = instance_double(LandingPage::Block::Base, type: "not_a_block") 7 | expect(render_block(block)).to be_empty 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/helpers/currency_helper_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe CurrencyHelper do 2 | include described_class 3 | 4 | let(:sample_number) { "12000" } 5 | 6 | describe "#format_amount" do 7 | it "returns a formatted number" do 8 | expect(format_amount(sample_number)).to eq("12,000 euros") 9 | end 10 | 11 | it "returns nil if passed nil" do 12 | expect(format_amount(nil)).to be_nil 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/helpers/error_items_helper_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ErrorItemsHelper do 2 | include described_class 3 | 4 | before do 5 | flash[:validation] = [ 6 | { field: "full_name", text: "Enter full name" }, 7 | { field: "job_title", text: "Enter job title" }, 8 | ] 9 | end 10 | 11 | describe "#error_items" do 12 | it "contains error items" do 13 | expect(error_items("job_title")).to eq("Enter job title") 14 | expect(error_items("email_address")).to be_nil 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/helpers/phone_number_helper_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe PhoneNumberHelper do 2 | include described_class 3 | 4 | let(:sample_phone_text) { "023 4567 8910 (with some text)" } 5 | 6 | describe "#phone_digits" do 7 | it "returns the phone number" do 8 | expect(phone_digits(sample_phone_text)).to eq("023 4567 8910") 9 | end 10 | end 11 | 12 | describe "#phone_text" do 13 | it "returns the text after the phone number" do 14 | expect(phone_text(sample_phone_text)).to eq("(with some text)") 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/helpers/theme_type_helper_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ThemeTypeHelper do 2 | include described_class 3 | 4 | describe "no theme type" do 5 | it "returns theme type default when style is empty" do 6 | expect(style("")).to eq("theme-default") 7 | end 8 | 9 | it "returns theme type default when style is invalid" do 10 | expect(style("asdfghjkl")).to eq("theme-default") 11 | end 12 | end 13 | 14 | describe "theme 2" do 15 | it "returns theme type style 2" do 16 | expect(style(2)).to eq("theme-2") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/javascripts/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphagov/frontend/03515bf4d5715110e9adf1f6eb14e285bd6b2237/spec/javascripts/helpers/.gitkeep -------------------------------------------------------------------------------- /spec/javascripts/helpers/nyc-test-coverage-helper.mjs: -------------------------------------------------------------------------------- 1 | // This file runs in the browser. 2 | 3 | // Ensure window.__coverage__ is always present. 4 | window.__coverage__ ??= {}; 5 | 6 | class NycTestCoverageReporter { 7 | // The following method runs after every Jasmine describe block. 8 | static suiteDone(result) { 9 | // Transfer test coverage information from the browser to node.js. 10 | jasmine.getEnv().setSuiteProperty("__coverage__", window.__coverage__); 11 | } 12 | } 13 | 14 | jasmine.getEnv().addReporter(NycTestCoverageReporter); 15 | -------------------------------------------------------------------------------- /spec/javascripts/reporters/nyc-test-coverage-reporter.mjs: -------------------------------------------------------------------------------- 1 | // This file runs in node.js. 2 | 3 | import fsPromises from 'node:fs/promises'; 4 | import {URL} from 'node:url'; 5 | import path from 'node:path'; 6 | 7 | export default class NycTestCoverageReporter { 8 | // The following method runs after every Jasmine describe block. 9 | suiteDone({properties}) { 10 | this.coverage = properties?.__coverage__ || this.coverage; 11 | } 12 | 13 | jasmineDone() { 14 | const rootDirname = path.dirname( 15 | new URL(import.meta.resolve('#root/package.json')).pathname 16 | ); 17 | const nycCoverageFile = path.join(rootDirname, 'tmp', 'nyc_output', 'out.json'); 18 | return fsPromises.writeFile(nycCoverageFile, JSON.stringify(this.coverage)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/javascripts/unit/support.spec.js: -------------------------------------------------------------------------------- 1 | describe('Supporting code', function () { 2 | 'use strict' 3 | 4 | describe('browser history method', function () { 5 | it('returns method replacing history state', function () { 6 | expect(window.GOVUK.support.history()).toBe(window.history.replaceState) 7 | }) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /spec/models/detailed_guide_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe DetailedGuide do 2 | let(:content_store_response) { GovukSchemas::Example.find("detailed_guide", example_name: "detailed_guide") } 3 | 4 | it_behaves_like "it can have single page notifications", "detailed_guide", "detailed_guide" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/field_of_operation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe FieldOfOperation do 2 | describe "#fatality_notices" do 3 | subject(:content_item) { described_class.new(content_store_response) } 4 | 5 | let(:content_store_response) do 6 | GovukSchemas::Example.find("field_of_operation", example_name: "field_of_operation") 7 | end 8 | 9 | it "returns the expected response" do 10 | expect(content_item.fatality_notices.first.title).to eq(content_store_response.dig("links", "fatality_notices", 0, "title")) 11 | expect(content_item.fatality_notices.first.base_path).to eq(content_store_response.dig("links", "fatality_notices", 0, "base_path")) 12 | expect(content_item.fatality_notices.first.to_h["details"]).to eq(content_store_response.dig("links", "fatality_notices", 0, "details")) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/models/fields_of_operation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe FieldsOfOperation do 2 | describe "#fields_of_operation" do 3 | subject(:content_item) { described_class.new(content_store_response) } 4 | 5 | let(:content_store_response) do 6 | GovukSchemas::Example.find("fields_of_operation", example_name: "fields_of_operation") 7 | end 8 | 9 | it "returns the expected response" do 10 | expect(content_item.fields_of_operation.first.title).to eq(content_store_response.dig("links", "fields_of_operation", 0, "title")) 11 | expect(content_item.fields_of_operation.first.base_path).to eq(content_store_response.dig("links", "fields_of_operation", 0, "base_path")) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/models/flexible_page/flexible_section/page_title_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe FlexiblePage::FlexibleSection::PageTitle do 2 | subject(:page_title) do 3 | described_class.new({ 4 | "context" => "My Context", 5 | "heading_text" => "My Heading", 6 | "lead_paragraph" => "Welcome to this page", 7 | }, FlexiblePage.new({})) 8 | end 9 | 10 | describe "#initialize" do 11 | it "sets the attributes from the contents hash" do 12 | expect(page_title.context).to eq("My Context") 13 | expect(page_title.heading_text).to eq("My Heading") 14 | expect(page_title.lead_paragraph).to eq("Welcome to this page") 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/models/flexible_page/flexible_section_factory_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe FlexiblePage::FlexibleSectionFactory do 2 | describe ".build" do 3 | it "builds sections of the correct type" do 4 | expect(described_class.build({ "type" => "base" }, FlexiblePage.new({}))).to be_instance_of(FlexiblePage::FlexibleSection::Base) 5 | end 6 | 7 | it "raises an error if the section type is not recognised" do 8 | expect { described_class.build({ "type" => "broken" }, FlexiblePage.new({})) }.to raise_error(StandardError, "Couldn't identify a model class for type: broken") 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/models/homepage_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Homepage do 2 | let!(:content_store_response) { GovukSchemas::Example.find("homepage", example_name: "homepage_with_popular_links_on_govuk") } 3 | let(:homepage) { described_class.new(content_store_response) } 4 | 5 | describe "#popular_links" do 6 | context "when popular links in the content item" do 7 | it "returns popular links from the content item" do 8 | expected_popular_links = content_store_response 9 | .dig("links", "popular_links", 0, "details", "link_items") 10 | .collect(&:with_indifferent_access) 11 | 12 | expect(homepage.popular_links).to eq(expected_popular_links) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/action_link_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::ActionLink do 2 | it_behaves_like "it is a landing-page block" 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/base_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::Base do 2 | describe "#full_width?" do 3 | it "is false by default" do 4 | expect(described_class.new({}, build(:landing_page)).full_width?).to be(false) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/block_error_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::BlockError do 2 | subject(:block_error) { described_class.new(blocks_hash, build(:landing_page)) } 3 | 4 | let(:blocks_hash) do 5 | { 6 | "type" => "block_error", 7 | "error" => StandardError.new("This error"), 8 | } 9 | end 10 | 11 | it_behaves_like "it is a landing-page block" 12 | 13 | it "has an error" do 14 | expect(block_error.error).to be_instance_of(StandardError) 15 | expect(block_error.error.message).to eq("This error") 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/blocks_container_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::BlocksContainer do 2 | subject(:blocks_container) { described_class.new(blocks_hash, build(:landing_page)) } 3 | 4 | let(:blocks_hash) do 5 | { 6 | "type" => "blocks_container", 7 | "blocks" => [ 8 | { 9 | "type" => "govspeak", 10 | "content" => "Some content!
", 11 | }, 12 | { 13 | "type" => "heading", 14 | "content" => "Porem ipsum dolor", 15 | }, 16 | { 17 | "type" => "govspeak", 18 | "content" => "Some more content!
", 19 | }, 20 | ], 21 | } 22 | end 23 | 24 | it_behaves_like "it is a landing-page block" 25 | 26 | it "has children blocks" do 27 | expect(blocks_container.children.size).to eq(3) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/columns_layout_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::ColumnsLayout do 2 | subject(:columns_layout) { described_class.new(blocks_hash, build(:landing_page)) } 3 | 4 | let(:blocks_hash) do 5 | { 6 | "type" => "columns_layout", 7 | "blocks" => [ 8 | { 9 | "type" => "govspeak", 10 | "content" => "Some content!
", 11 | }, 12 | { 13 | "type" => "govspeak", 14 | "content" => "Some more content!
", 15 | }, 16 | { 17 | "type" => "govspeak", 18 | "content" => "Even more content!
", 19 | }, 20 | ], 21 | } 22 | end 23 | 24 | it_behaves_like "it is a landing-page block" 25 | 26 | it "has column blocks" do 27 | expect(columns_layout.blocks.size).to eq(3) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/govspeak_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::Govspeak do 2 | it_behaves_like "it is a landing-page block" 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/heading_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::Heading do 2 | it_behaves_like "it is a landing-page block" 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/layout_base_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::LayoutBase do 2 | describe "#blocks" do 3 | subject(:layout_base) { described_class.new(blocks_hash, build(:landing_page)) } 4 | 5 | let(:blocks_hash) do 6 | { 7 | "blocks" => [ 8 | { "type" => "govspeak", "content" => "test1" }, 9 | { "type" => "govspeak", "content" => "test2" }, 10 | { "type" => "govspeak", "content" => "test3" }, 11 | ], 12 | } 13 | end 14 | 15 | it "builds all of the blocks" do 16 | expect(layout_base.blocks.count).to eq 3 17 | end 18 | 19 | it "builds blocks of the correct type" do 20 | expect(layout_base.blocks.first.type).to eq("govspeak") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/logo_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::Logo do 2 | it_behaves_like "it is a landing-page block" 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/landing_page/block/quote_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::Block::Quote do 2 | it_behaves_like "it is a landing-page block" 3 | end 4 | -------------------------------------------------------------------------------- /spec/models/landing_page/block_factory_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LandingPage::BlockFactory do 2 | describe ".build" do 3 | it "builds blocks of the correct type" do 4 | expect(described_class.build({ "type" => "govspeak" }, build(:landing_page)).type).to eq("govspeak") 5 | end 6 | end 7 | 8 | describe ".build_all" do 9 | it "builds many blocks" do 10 | result = described_class.build_all([ 11 | { "type" => "govspeak" }, 12 | { "type" => "govspeak" }, 13 | { "type" => "govspeak" }, 14 | { "type" => "govspeak" }, 15 | ], build(:landing_page)) 16 | expect(result.count).to eq(4) 17 | expect(result.map(&:type)).to eq(%w[govspeak govspeak govspeak govspeak]) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/models/licence_transaction_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe LicenceTransaction do 2 | describe "#slug" do 3 | it "returns the subject slug" do 4 | content_store_response = GovukSchemas::Example.find("specialist_document", example_name: "licence-transaction") 5 | content_store_response["base_path"] = "/foo/bar" 6 | expect(described_class.new(content_store_response).slug).to eq("bar") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/organisation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Organisation do 2 | let(:content_store_response) do 3 | GovukSchemas::Example.find("organisation", example_name: "organisation") 4 | end 5 | 6 | describe "#brand" do 7 | it "gets the brand" do 8 | expect(described_class.new(content_store_response).brand).not_to be_nil 9 | expect(described_class.new(content_store_response).brand).to eq(content_store_response.dig("details", "brand")) 10 | end 11 | end 12 | 13 | describe "#logo" do 14 | it "gets the logo" do 15 | expect(described_class.new(content_store_response).logo).to be_instance_of(Logo) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/models/service_manual_homepage_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ServiceManualHomepage do 2 | describe "#topics" do 3 | subject(:service_manual) { described_class.new(content_store_response) } 4 | 5 | let(:content_store_response) do 6 | GovukSchemas::Example.find("service_manual_homepage", example_name: "service_manual_homepage") 7 | end 8 | 9 | it "returns the expected response" do 10 | expect(service_manual.topics.length).to eq(4) 11 | expect(service_manual.topics.first.title).to eq(content_store_response.dig("links", "children", 0, "title")) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/models/service_manual_service_toolkit_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ServiceManualServiceToolkit do 2 | describe "#topics" do 3 | subject(:service_toolkit) { described_class.new(content_store_response) } 4 | 5 | let(:content_store_response) do 6 | GovukSchemas::Example.find("service_manual_service_toolkit", example_name: "service_manual_service_toolkit") 7 | end 8 | 9 | it "returns the expected response" do 10 | expect(service_toolkit.collections.length).to eq(2) 11 | expect(service_toolkit.collections.first["title"]).to eq(content_store_response.dig("details", "collections", 0, "title")) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/models/simple_smart_answer_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe SimpleSmartAnswer do 2 | describe "#slug" do 3 | it "returns the subject slug" do 4 | content_store_response = GovukSchemas::Example.find("simple_smart_answer", example_name: "simple-smart-answer") 5 | content_store_response["base_path"] = "/foo" 6 | expect(described_class.new(content_store_response).slug).to eq("foo") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/answer_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Answer" do 2 | before do 3 | content_store_has_example_item("/gwasanaethau-ar-lein-cymraeg-cthem", schema: :answer) 4 | end 5 | 6 | describe "GET show" do 7 | context "when visting answer page" do 8 | let(:base_path) { "/gwasanaethau-ar-lein-cymraeg-cthem" } 9 | 10 | it "returns 200" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template("show") 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/requests/case_study_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Case Study" do 2 | before do 3 | content_store_has_example_item("/government/case-studies/get-britain-building-carlisle-park", schema: :case_study) 4 | end 5 | 6 | describe "GET show" do 7 | it "returns 200" do 8 | get "/government/case-studies/get-britain-building-carlisle-park" 9 | 10 | expect(response).to have_http_status(:ok) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/content_items_controller_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ContentItemsController do 2 | before do 3 | content_store_has_example_item("/government/case-studies/get-britain-building-carlisle-park", schema: :case_study) 4 | end 5 | 6 | describe "GET path" do 7 | it "sets GOVUK-Account-Session-Flash in the Vary header" do 8 | get "/government/case-studies/get-britain-building-carlisle-park" 9 | 10 | expect(response.headers["Vary"]).to include("GOVUK-Account-Session-Flash") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/content_loading_problems_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Content Loading Problems" do 2 | context "when loading a draft page without permission" do 3 | before do 4 | endpoint = content_store_endpoint(draft: false) 5 | stub_request(:get, "#{endpoint}/content/").to_return(status: 403, headers: {}) 6 | end 7 | 8 | it "responds with a 403 (matching the response from content store)" do 9 | get "/" 10 | 11 | expect(response).to have_http_status(:forbidden) 12 | end 13 | end 14 | 15 | context "when the content-store is missing" do 16 | before do 17 | stub_content_store_isnt_available 18 | end 19 | 20 | it "responds with a 503 (matching the response from content store)" do 21 | get "/foreign-travel-advice" 22 | 23 | expect(response).to have_http_status(:service_unavailable) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/requests/fatality_notice_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Fatality Notice" do 2 | let(:base_path) { "/government/fatalities/sad-news" } 3 | 4 | before do 5 | content_store_has_example_item(base_path, schema: :fatality_notice) 6 | end 7 | 8 | describe "GET show" do 9 | it "returns 200" do 10 | get base_path 11 | 12 | expect(response).to have_http_status(:ok) 13 | end 14 | 15 | it "renders the show template" do 16 | get base_path 17 | 18 | expect(response).to render_template(:show) 19 | end 20 | 21 | it "sets cache-control headers" do 22 | get base_path 23 | expect(response).to honour_content_store_ttl 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/requests/favicon_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Favicon" do 2 | it "redirects permanently to an asset with a 1 day expiry" do 3 | get "/favicon.ico" 4 | 5 | expect(response.headers["Cache-Control"]).to eq("max-age=86400, public") 6 | expect(response.status).to eq(301) 7 | expect(response.location).to match(/http:\/\/www.example.com\/assets\/frontend\/favicon-(.+).ico/) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/field_of_operation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Field of operation" do 2 | describe "GET show" do 3 | let(:content_item) { GovukSchemas::Example.find("field_of_operation", example_name: "field_of_operation") } 4 | let(:base_path) { content_item.fetch("base_path") } 5 | 6 | before do 7 | stub_content_store_has_item(base_path, content_item) 8 | end 9 | 10 | it "succeeds" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template(:show) 20 | end 21 | 22 | it "sets cache-control headers" do 23 | get base_path 24 | 25 | expect(response).to honour_content_store_ttl 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/requests/fields_of_operation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Fields of operation" do 2 | describe "GET show" do 3 | let(:content_item) { GovukSchemas::Example.find("fields_of_operation", example_name: "fields_of_operation") } 4 | let(:base_path) { content_item.fetch("base_path") } 5 | 6 | before do 7 | stub_content_store_has_item(base_path, content_item) 8 | end 9 | 10 | it "succeeds" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template(:index) 20 | end 21 | 22 | it "sets cache-control headers" do 23 | get base_path 24 | 25 | expect(response).to honour_content_store_ttl 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/requests/find_local_council_spec.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/test_helpers/local_links_manager" 2 | 3 | RSpec.describe "Find Local Council" do 4 | include GdsApi::TestHelpers::LocalLinksManager 5 | 6 | before { content_store_has_random_item(base_path: "/find-local-council") } 7 | 8 | it "sets correct expiry headers" do 9 | get "/find-local-council" 10 | 11 | expect(response).to honour_content_store_ttl 12 | end 13 | 14 | it "returns a 404 if the local authority can't be found" do 15 | stub_local_links_manager_does_not_have_an_authority("foo") 16 | get "/find-local-council/foo" 17 | 18 | expect(response).to have_http_status(:not_found) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/requests/flexible_page_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "FlexiblePage" do 2 | describe "GET show" do 3 | context "when visiting a Flexible page" do 4 | before do 5 | ENV["ALLOW_LOCAL_CONTENT_ITEM_OVERRIDE"] = "true" 6 | stub_const("ContentItemLoader::LOCAL_ITEMS_PATH", "spec/fixtures/local-content-items") 7 | end 8 | 9 | after do 10 | ENV["ALLOW_LOCAL_CONTENT_ITEM_OVERRIDE"] = nil 11 | end 12 | 13 | let(:base_path) { "/flexible-page" } 14 | 15 | it "returns 200" do 16 | get base_path 17 | 18 | expect(response).to have_http_status(:ok) 19 | end 20 | 21 | it "renders the show template" do 22 | get base_path 23 | 24 | expect(response).to render_template("show") 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/requests/get_involved_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Get Involved" do 2 | before do 3 | stub_content_store_has_item("/government/get-involved", GovukSchemas::Example.find("get_involved", example_name: "get_involved")) 4 | stub_request(:get, /\A#{Plek.new.find('search-api')}\/search.json/).to_return(body: { "results" => [], "total" => 83 }.to_json) 5 | end 6 | 7 | describe "GET index" do 8 | it "responds with success" do 9 | get "/government/get-involved" 10 | 11 | expect(response).to have_http_status(:ok) 12 | expect(response.body).to include("Get involved") 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/requests/homepage_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Homepage" do 2 | include GovukAbTesting::RspecHelpers 3 | include ContentStoreHelpers 4 | 5 | context "when loading the homepage" do 6 | before { stub_homepage_content_item } 7 | 8 | it "responds with success" do 9 | get "/" 10 | 11 | expect(response).to have_http_status(:ok) 12 | end 13 | 14 | it "sets correct expiry headers" do 15 | get "/" 16 | 17 | expect(response).to honour_content_store_ttl 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/requests/placeholder_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Placeholder" do 2 | context "when loading the placeholder page" do 3 | it "responds with success" do 4 | stub_content_store_does_not_have_item("/government") 5 | stub_content_store_does_not_have_item("/government/placeholder") 6 | get "/government/placeholder" 7 | 8 | expect(response).to have_http_status(:ok) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/requests/roadmap_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Roadmap" do 2 | describe "GET index" do 3 | before do 4 | content_store_has_random_item(base_path: "/roadmap", schema: "special_route") 5 | end 6 | 7 | it "sets the cache expiry headers" do 8 | get "/roadmap" 9 | 10 | expect(response.headers["Cache-Control"]).to eq("max-age=#{30.minutes.to_i}, public") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/service_manual_homepage_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Service Manual homepage" do 2 | describe "GET show" do 3 | let(:content_item) { GovukSchemas::Example.find("service_manual_homepage", example_name: "service_manual_homepage") } 4 | let(:base_path) { content_item.fetch("base_path") } 5 | 6 | before do 7 | stub_content_store_has_item(base_path, content_item) 8 | end 9 | 10 | it "succeeds" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template(:index) 20 | end 21 | 22 | it "sets cache-control headers" do 23 | get base_path 24 | 25 | expect(response).to honour_content_store_ttl 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/requests/service_toolkit_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Service toolkit page" do 2 | describe "GET show" do 3 | let(:content_item) { GovukSchemas::Example.find("service_manual_service_toolkit", example_name: "service_manual_service_toolkit") } 4 | let(:base_path) { content_item.fetch("base_path") } 5 | 6 | before do 7 | stub_content_store_has_item(base_path, content_item) 8 | end 9 | 10 | it "succeeds" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template(:index) 20 | end 21 | 22 | it "sets cache-control headers" do 23 | get base_path 24 | 25 | expect(response).to honour_content_store_ttl 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/requests/speech_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Speech" do 2 | before do 3 | content_store_has_example_item("/government/speeches/vehicle-regulations", schema: :speech) 4 | end 5 | 6 | describe "GET show" do 7 | context "when visiting a Speech page" do 8 | let(:base_path) { "/government/speeches/vehicle-regulations" } 9 | 10 | it "returns 200" do 11 | get base_path 12 | 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it "renders the show template" do 17 | get base_path 18 | 19 | expect(response).to render_template("show") 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/requests/static_error_pages_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Static Error Pages" do 2 | context "when asked for an unrecognised error" do 3 | it "returns a 404 with no body" do 4 | get "/static-error-pages/555.html" 5 | 6 | expect(response).to have_http_status(:not_found) 7 | expect(response.body).to be_empty 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/requests/take_part_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Take Part" do 2 | before do 3 | content_store_has_example_item("/government/get-involved/take-part/tp1", schema: :take_part) 4 | end 5 | 6 | describe "GET index" do 7 | it "returns 200" do 8 | get "/government/get-involved/take-part/tp1" 9 | 10 | expect(response).to have_http_status(:ok) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/routing/calendars_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Calendars" do 2 | include ContentStoreHelpers 3 | 4 | before do 5 | stub_content_store_has_item("/something", schema_name: "calendar") 6 | end 7 | 8 | it "returns 404 for a non-existent calendar" do 9 | allow(Calendar).to receive(:find).and_raise(Calendar::CalendarNotFound) 10 | get "/something" 11 | 12 | expect(get("/something")).to route_to(controller: "calendar", action: "show_calendar", slug: "something") 13 | end 14 | 15 | it "does not route an invalid slug format, and does not try to look up the calendar" do 16 | stub_content_store_does_not_have_item("/something..etc-passwd", schema_name: "calendar") 17 | 18 | expect(Calendar).not_to receive(:find) 19 | expect(get("/something..etc-passwd")).not_to route_to(controller: "calendar") 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/routing/csv_previews_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "CSV previews" do 2 | it "routes old style paths to the CsvPreviewController" do 3 | expect(get("/media/000000000000000000000000/some-file.csv/preview")).to route_to( 4 | controller: "csv_preview", 5 | action: "show", 6 | id: "000000000000000000000000", 7 | filename: "some-file.csv", 8 | ) 9 | end 10 | 11 | it "routes new style paths to the CsvPreviewController" do 12 | expect(get("/csv-preview/000000000000000000000000/some-file.csv")).to route_to( 13 | controller: "csv_preview", 14 | action: "show", 15 | id: "000000000000000000000000", 16 | filename: "some-file.csv", 17 | format: "html", 18 | ) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/routing/landing_pages_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Landing Pages" do 2 | include ContentStoreHelpers 3 | 4 | before do 5 | stub_content_store_has_item("/routing/constraint/test", schema_name: "landing_page") 6 | end 7 | 8 | it "routes to the LandingPage controller" do 9 | expect(get("/routing/constraint/test")).to route_to(controller: "landing_page", action: "show", path: "routing/constraint/test") 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/support/calendar_helpers.rb: -------------------------------------------------------------------------------- 1 | module CalendarHelpers 2 | def mock_calendar_fixtures 3 | stub_const("Calendar::REPOSITORY_PATH", "spec/fixtures") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/component_helpers.rb: -------------------------------------------------------------------------------- 1 | module ComponentHelpers 2 | def component_name 3 | raise NotImplementedError, "Override this method in your test class" 4 | end 5 | 6 | def render_component(locals, &block) 7 | if block_given? 8 | render("components/#{component_name}", locals, &block) 9 | else 10 | render "components/#{component_name}", locals 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/support/concerns/people.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples "it can have people" do |document_type, example_name, data_source: :content_store| 2 | let(:content_item) { fetch_content_item(document_type, example_name, data_source:) } 3 | 4 | it "knows it has people" do 5 | expect(described_class.new(content_item).people.count).to eq(content_item["links"]["people"].count) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/concerns/withdrawable.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples "it can be withdrawn" do |document_type, example_name, data_source: :content_store| 2 | let(:content_item) { fetch_content_item(document_type, example_name, data_source:) } 3 | 4 | it "knows it is withdrawn" do 5 | expect(described_class.new(content_item).withdrawn?).to be true 6 | end 7 | 8 | it "knows when it has been withdrawn_at" do 9 | expect(described_class.new(content_item).withdrawn_at).to eq(content_item["withdrawn_notice"]["withdrawn_at"]) 10 | end 11 | 12 | it "knows it has a withdrawn_explanation" do 13 | expect(described_class.new(content_item).withdrawn_explanation).to eq(content_item["withdrawn_notice"]["explanation"]) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/concerns/worldwide_organisations.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples "it can have worldwide organisations" do |document_type, example_name, data_source: :content_store| 2 | let(:content_item) { fetch_content_item(document_type, example_name, data_source:) } 3 | 4 | it "knows it has worldwide organisations" do 5 | expect(described_class.new(content_item).worldwide_organisations.count).to eq(content_item["links"]["worldwide_organisations"].count) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/content_item_helpers.rb: -------------------------------------------------------------------------------- 1 | require "support/publishing_api_graphql_helpers" 2 | 3 | module ContentItemHelpers 4 | include PublishingApiGraphqlHelpers 5 | 6 | def fetch_content_item(document_type, example_name, data_source:) 7 | case data_source 8 | when :content_store 9 | GovukSchemas::Example.find(document_type, example_name:) 10 | when :publishing_api_graphql 11 | fetch_graphql_content_item(example_name) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/election_helpers.rb: -------------------------------------------------------------------------------- 1 | module ElectionHelpers 2 | TEST_API_URL = "https://test.example.org/api/v1".freeze 3 | TEST_API_KEY = "LetMeIn".freeze 4 | 5 | def with_electoral_api_url(&block) 6 | ClimateControl.modify ELECTIONS_API_URL: TEST_API_URL, ELECTIONS_API_KEY: TEST_API_KEY, &block 7 | end 8 | 9 | def stub_api_postcode_lookup(postcode, response: "{}", status: 200) 10 | stub_request(:get, "#{TEST_API_URL}/postcode/#{postcode}?token=#{TEST_API_KEY}") 11 | .to_return(status:, body: response) 12 | end 13 | 14 | def stub_api_address_lookup(uprn, response: "{}", status: 200) 15 | stub_request(:get, "#{TEST_API_URL}/address/#{uprn}?token=#{TEST_API_KEY}") 16 | .to_return(status:, body: response) 17 | end 18 | 19 | def api_response 20 | path = Rails.root.join("spec/fixtures/electoral-result.json") 21 | File.read(path) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/jasmine-browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "srcDir": ".", 3 | "srcFiles": [ 4 | "public/assets/frontend/test-dependencies-*.js", 5 | "public/assets/frontend/dependencies-*.js", 6 | "tmp/nyc_output/assets/**/*.js" 7 | ], 8 | "specDir": "spec/javascripts", 9 | "specFiles": [ 10 | "**/*[sS]pec.js" 11 | ], 12 | "helpers": [ 13 | "helpers/*.mjs", 14 | "vendor/*.js" 15 | ], 16 | "browser": "headlessChrome", 17 | "reporters": [ 18 | "./spec/javascripts/reporters/nyc-test-coverage-reporter.mjs" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /spec/support/landing_page_blocks.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_examples "it is a landing-page block" do 2 | let(:block_name) { described_class.name.split("::").last.underscore } 3 | 4 | # Relies on the convention that all Level 4 headings in the documentation are block names 5 | it "has a heading in the block documentation" do 6 | documentation = File.read(Rails.root.join("docs/building_blocks_for_flexible_content.md")) 7 | 8 | expect(/^#### #{block_name.titleize}\n$/.match(documentation)).not_to be_nil 9 | end 10 | 11 | it "has a correctly named partial" do 12 | expect(File).to exist(Rails.root.join("app/views/landing_page/blocks/_#{block_name}.html.erb")) 13 | end 14 | 15 | it "derives from LandingPage::Block::Base" do 16 | expect(described_class.ancestors).to include(LandingPage::Block::Base) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/support/location_helpers.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/test_helpers/locations_api" 2 | require "gds_api/test_helpers/local_links_manager" 3 | 4 | module LocationHelpers 5 | include GdsApi::TestHelpers::LocationsApi 6 | include GdsApi::TestHelpers::LocalLinksManager 7 | 8 | def configure_locations_api_and_local_authority(postcode, authorities, local_custodian_code, snac: "00BK", gss: "E06000064") 9 | stub_locations_api_has_location( 10 | postcode, 11 | [ 12 | { 13 | "latitude" => 51.5010096, 14 | "longitude" => -0.1415870, 15 | "local_custodian_code" => local_custodian_code, 16 | }, 17 | ], 18 | ) 19 | authorities.each do |authority| 20 | stub_local_links_manager_has_a_local_authority(authority, local_custodian_code:, snac:, gss:) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/matchers/have_bank_holiday_table_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_bank_holiday_table do |attrs = {}| 2 | failure_message { "'#{page}' has the wrong rows or has incorrectly formatted datetime attributes" } 3 | 4 | match do |page| 5 | table = page.find("caption", text: "#{attrs[:title]} #{attrs[:year]}").ancestor("table") 6 | if attrs[:rows] 7 | actual_rows = table.all("tr").map { |r| r.all("th, td").map(&:text).map(&:strip) } 8 | expect(attrs[:rows]).to eq(actual_rows) 9 | expect(table.first("time")[:datetime]).to match(/\d{4}-\d{2}-\d{2}/) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/matchers/have_button_as_link_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_button_as_link do |text, attrs = {}| 2 | match do |actual| 3 | actual.has_css?(process_button_attributes(attrs), text:) 4 | end 5 | end 6 | 7 | def process_button_attributes(attrs) 8 | match_assert = "" 9 | match_assert << "[rel='#{attrs[:rel]}']" if attrs[:rel] 10 | match_assert << "[href='#{attrs[:href]}']" if attrs[:href] 11 | 12 | if attrs[:data] 13 | attrs[:data].each do |key, value| 14 | match_assert << "[data-#{key}='#{value}']" 15 | end 16 | end 17 | 18 | match_assert 19 | end 20 | -------------------------------------------------------------------------------- /spec/support/matchers/honours_content_store_ttl_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :honour_content_store_ttl do 2 | match do |actual| 3 | actual.headers["Cache-Control"] == "max-age=#{15.minutes.to_i}, public" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/publishing_api_graphql_helpers.rb: -------------------------------------------------------------------------------- 1 | require "gds_api/test_helpers/publishing_api" 2 | 3 | module PublishingApiGraphqlHelpers 4 | include GdsApi::TestHelpers::PublishingApi 5 | 6 | def graphql_has_example_item(filename) 7 | graphql_response = fetch_graphql_fixture(filename) 8 | content_item = graphql_response.dig("data", "edition") 9 | 10 | stub_publishing_api_graphql_content_item( 11 | Graphql::EditionQuery.new(content_item.fetch("base_path")).query, 12 | graphql_response, 13 | ) 14 | 15 | content_item 16 | end 17 | 18 | def fetch_graphql_content_item(fixture_filename) 19 | fetch_graphql_fixture(fixture_filename).dig("data", "edition") 20 | end 21 | 22 | private 23 | 24 | def fetch_graphql_fixture(filename) 25 | json = File.read( 26 | Rails.root.join("spec", "fixtures", "graphql", "#{filename}.json"), 27 | ) 28 | JSON.parse(json) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/support/schema_org_helpers.rb: -------------------------------------------------------------------------------- 1 | module SchemaOrgHelpers 2 | def find_schemas 3 | schema_sections = page.find_all("script[type='application/ld+json']", visible: false) 4 | schema_sections.map { |section| JSON.parse(section.text(:all)) } 5 | end 6 | 7 | def find_schema_of_type(schema_type) 8 | find_schemas.detect { |schema| schema["@type"] == schema_type } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/system/account_home_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "AccountHome" do 2 | include ContentStoreHelpers 3 | 4 | before { stub_homepage_content_item } 5 | 6 | describe "/account/home" do 7 | it "redirects users to One Login's Your Services page" do 8 | visit account_home_path 9 | 10 | expect(page.current_url).to eq("#{GovukPersonalisation::Urls.one_login_your_services}/") 11 | end 12 | 13 | context "when there are cookie consent and _ga url parameters" do 14 | it "preserve them through the redirect" do 15 | visit account_home_path(cookie_consent: true, _ga: "abc123") 16 | 17 | expect(page.current_url).to include("cookie_consent=true") 18 | expect(page.current_url).to include("_ga=abc123") 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/system/developer_root_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Developer Root" do 2 | context "when visiting /development" do 3 | it "includes a link to at least one example" do 4 | visit "/development" 5 | 6 | expect(page).to have_link("/", href: "/") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/system/help_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Help" do 2 | describe "the help index page" do 3 | before do 4 | payload = { 5 | base_path: "/help", 6 | format: "special_route", 7 | title: "Help using GOV.UK", 8 | description: "", 9 | } 10 | stub_content_store_has_item("/help", payload) 11 | end 12 | 13 | it "renders the help index page correctly" do 14 | visit "/help" 15 | 16 | expect(page).to have_title("Help using GOV.UK") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/system/sign_in_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Sign in" do 2 | describe "Sign-in help page" do 3 | before do 4 | payload = { 5 | base_path: "/sign-in", 6 | format: "special_route", 7 | title: "Sign in to a service", 8 | description: "", 9 | details: { 10 | body: "", 11 | }, 12 | } 13 | stub_content_store_has_item("/sign-in", payload) 14 | end 15 | 16 | it "renders the sign-in help page correctly" do 17 | visit "/sign-in" 18 | 19 | expect(page).to have_title("Sign in to a service") 20 | 21 | expect(page).to have_text("Search GOV.UK for a service") 22 | expect(page).to have_button("Search") 23 | end 24 | 25 | it "includes search facets as hidden elements" do 26 | visit "/sign-in" 27 | 28 | expect(page).to have_field("content_purpose_supergroup[]", type: :hidden, with: "services") 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/unit/api_error_routing_constraint_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ApiErrorRoutingConstraint do 2 | include ContentStoreHelpers 3 | 4 | subject(:api_error_routing_constraint) { described_class.new } 5 | 6 | let(:request) { instance_double(ActionDispatch::Request, path: "/slug", env: {}, headers: {}, params: {}) } 7 | 8 | it "returns true if there's a cached error" do 9 | stub_content_store_does_not_have_item("/slug") 10 | 11 | expect(api_error_routing_constraint.matches?(request)).to be true 12 | end 13 | 14 | it "returns false if there was no error in API calls" do 15 | stub_content_store_has_item("/slug") 16 | 17 | expect(api_error_routing_constraint.matches?(request)).to be false 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/unit/locales_validation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Locales Validation" do 2 | it "validates all locale files" do 3 | checker = RailsTranslationManager::LocaleChecker.new("config/locales/*.yml") 4 | 5 | expect { checker.validate_locales }.to output("Locale files are in sync, nice job!\n").to_stdout 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/unit/postcode_sanitizer_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe PostcodeSanitizer do 2 | describe "#sanitize" do 3 | it "strips trailing spaces from entered postcodes" do 4 | expect(described_class.sanitize("WC2B 6NH ")).to eq("WC2B 6NH") 5 | end 6 | 7 | it "strips non-alphanumerics from entered postcodes" do 8 | expect(described_class.sanitize("WC2B -6NH]")).to eq("WC2B 6NH") 9 | end 10 | 11 | it "transposes O/0 and I/1 if necessary" do 12 | expect(described_class.sanitize("WIA OAA")).to eq("W1A 0AA") 13 | end 14 | 15 | context "when the postcode parameter is nil or an empty string" do 16 | it "returns nil and does not try to perform sanitization" do 17 | expect(described_class.sanitize("")).to be_nil 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/unit/sanitiser/strategy_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Sanitiser::Strategy do 2 | context "with sanitize_null_bytes true" do 3 | it "raises an error if the string contains a null byte" do 4 | expect { described_class.call("Hello\x00World!", sanitize_null_bytes: true) }.to raise_error(Sanitiser::Strategy::SanitisingError) 5 | end 6 | end 7 | 8 | context "with sanitize_null_bytes false" do 9 | it "does not raise an error if the string contains a null byte" do 10 | expect { described_class.call("Hello\x00World!", sanitize_null_bytes: false) }.not_to raise_error 11 | end 12 | end 13 | end 14 | --------------------------------------------------------------------------------