diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 887b3c6d4..dfdfdaccd 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -4,3 +4,4 @@ $govuk-page-width: 1140px;
@import "govuk_publishing_components/all_components";
@import "downtimes";
@import "summary-card";
+@import "sidebar_components";
diff --git a/app/assets/stylesheets/sidebar_components.scss b/app/assets/stylesheets/sidebar_components.scss
new file mode 100644
index 000000000..b61b0b802
--- /dev/null
+++ b/app/assets/stylesheets/sidebar_components.scss
@@ -0,0 +1,6 @@
+.sidebar-components {
+ .govuk-button {
+ width: 100%;
+ }
+ border-top: 2px solid $govuk-brand-colour;
+}
diff --git a/app/controllers/homepage_controller.rb b/app/controllers/homepage_controller.rb
index ff55c0168..743701685 100644
--- a/app/controllers/homepage_controller.rb
+++ b/app/controllers/homepage_controller.rb
@@ -1,8 +1,54 @@
class HomepageController < ApplicationController
layout "design_system"
+ before_action :fetch_latest_popular_link
def show
- @latest_popular_links = PopularLinksEdition.last
render "homepage/popular_links/show"
end
+
+ def create
+ @latest_popular_links = @latest_popular_links.create_draft_popular_links_from_last_record
+ render "homepage/popular_links/show"
+ end
+
+ def edit
+ render "homepage/popular_links/edit"
+ end
+
+ def update
+ update_link_items
+ flash[:success] = "Popular links draft saved.".html_safe
+ redirect_to show_popular_links_path
+ rescue StandardError
+ render "homepage/popular_links/edit"
+ end
+
+ def publish
+ publish_latest_popular_links
+ render "homepage/popular_links/show"
+ end
+
+private
+
+ def fetch_latest_popular_link
+ @latest_popular_links = PopularLinksEdition.last
+ end
+
+ def update_link_items
+ @latest_popular_links.link_items = remove_leading_and_trailing_url_spaces(params[:popular_links].values)
+ @latest_popular_links.save!
+ end
+
+ def remove_leading_and_trailing_url_spaces(links)
+ link_items = []
+ links.each do |link|
+ link[:url] = link[:url].strip
+ link_items << link
+ end
+ link_items
+ end
+
+ def publish_latest_popular_links
+ @latest_popular_links.publish_popular_links
+ end
end
diff --git a/app/helpers/errors_helper.rb b/app/helpers/errors_helper.rb
index 72882f569..7bc1fddb8 100644
--- a/app/helpers/errors_helper.rb
+++ b/app/helpers/errors_helper.rb
@@ -1,14 +1,14 @@
module ErrorsHelper
- def errors_for(errors, attribute)
+ def errors_for(errors, attribute, use_full_message: true)
return nil if errors.blank?
errors.filter_map { |error|
if error.attribute == attribute
{
- text: error.full_message,
+ text: use_full_message ? error.full_message : error.message,
}
end
}
- .presence
+ .presence
end
end
diff --git a/app/helpers/popular_links_helper.rb b/app/helpers/popular_links_helper.rb
index 6c19a09ef..0bc89bb71 100644
--- a/app/helpers/popular_links_helper.rb
+++ b/app/helpers/popular_links_helper.rb
@@ -5,4 +5,31 @@ def popular_link_rows(item)
rows << { key: "URL", value: item[:url] }
rows.compact
end
+
+ def primary_button_for(model, url, text)
+ form_for model, url:, method: :post do
+ render "govuk_publishing_components/components/button", {
+ text:,
+ margin_bottom: 3,
+ }
+ end
+ end
+
+ def secondary_button_for(model, url, text)
+ form_for model, url:, method: :post do
+ render "govuk_publishing_components/components/button", {
+ text:,
+ margin_bottom: 3,
+ secondary_solid: true,
+ }
+ end
+ end
+
+ def primary_link_button_for(url, text)
+ render "govuk_publishing_components/components/button", {
+ text:,
+ href: url,
+ margin_bottom: 3,
+ }
+ end
end
diff --git a/app/models/action.rb b/app/models/action.rb
index 45af101bb..c1dc908c5 100644
--- a/app/models/action.rb
+++ b/app/models/action.rb
@@ -17,6 +17,7 @@ class Action
PUBLISH = "publish".freeze,
ARCHIVE = "archive".freeze,
NEW_VERSION = "new_version".freeze,
+ PUBLISH_POPULAR_LINKS = "publish_popular_links".freeze,
].freeze
NON_STATUS_ACTIONS = [
diff --git a/app/models/edition.rb b/app/models/edition.rb
index 8fd452196..f58dfd2c6 100644
--- a/app/models/edition.rb
+++ b/app/models/edition.rb
@@ -121,7 +121,7 @@ class ResurrectionError < RuntimeError
validates :version_number, presence: true, uniqueness: { scope: :panopticon_id }, unless: :popular_links_edition?
validates :panopticon_id, presence: true, unless: :popular_links_edition?
validates_with SafeHtml, unless: :popular_links_edition?
- validates_with LinkValidator, on: :update, unless: :archived? || :popular_links_edition?
+ validates_with LinkValidator, on: :update, unless: :archived_or_popular_links?
validates_with ReviewerValidator
validates :change_note, presence: { if: :major_change }
@@ -519,4 +519,8 @@ def common_type_specific_field_keys(target_class)
def popular_links_edition?
instance_of?(::PopularLinksEdition)
end
+
+ def archived_or_popular_links?
+ archived? || popular_links_edition?
+ end
end
diff --git a/app/models/popular_links_edition.rb b/app/models/popular_links_edition.rb
index 24848df7f..bc0dd2a6a 100644
--- a/app/models/popular_links_edition.rb
+++ b/app/models/popular_links_edition.rb
@@ -1,16 +1,36 @@
class PopularLinksEdition < Edition
field :link_items, type: Array
- validate :six_link_items_present?
- validate :all_urls_and_titles_are_present?
+ validate :six_link_items_present
+ validate :all_valid_urls_and_titles_are_present
- def six_link_items_present?
+ def six_link_items_present
errors.add(:link_items, "6 links are required") if link_items.count != 6
end
- def all_urls_and_titles_are_present?
+ def all_valid_urls_and_titles_are_present
link_items.each_with_index do |item, index|
- errors.add(:item, "A URL is required for Link #{index + 1}") unless item.key?(:url)
- errors.add(:item, "A Title is required for Link #{index + 1}") unless item.key?(:title)
+ errors.add("url#{index + 1}", "URL is required for Link #{index + 1}") unless url_present?(item)
+ errors.add("title#{index + 1}", "Title is required for Link #{index + 1}") unless title_present?(item)
+ errors.add("url#{index + 1}", "URL is invalid for Link #{index + 1}, all URLs should have at least one '.' and no spaces.") if url_present?(item) && url_has_spaces_or_has_no_dot?(item[:url])
end
end
+
+ def url_has_spaces_or_has_no_dot?(url)
+ url.include?(" ") || url.exclude?(".")
+ end
+
+ def title_present?(item)
+ item.key?(:title) && !item[:title].empty?
+ end
+
+ def url_present?(item)
+ item.key?(:url) && !item[:url].empty?
+ end
+
+ def create_draft_popular_links_from_last_record
+ last_popular_links = PopularLinksEdition.last
+ popular_links = PopularLinksEdition.new(title: last_popular_links.title, link_items: last_popular_links.link_items, version_number: last_popular_links.version_number.next)
+ popular_links.save!
+ popular_links
+ end
end
diff --git a/app/models/workflow.rb b/app/models/workflow.rb
index 077a4b478..0ee9bc56c 100644
--- a/app/models/workflow.rb
+++ b/app/models/workflow.rb
@@ -93,6 +93,10 @@ class CannotDeletePublishedPublication < RuntimeError; end
transition all => :archived, :unless => :archived?
end
+ event :publish_popular_links do
+ transition %i[draft] => :published
+ end
+
state :in_review do
validates :review_requested_at, presence: true
end
diff --git a/app/views/homepage/popular_links/_form.html.erb b/app/views/homepage/popular_links/_form.html.erb
new file mode 100644
index 000000000..a29f333ab
--- /dev/null
+++ b/app/views/homepage/popular_links/_form.html.erb
@@ -0,0 +1,25 @@
+<%= render "govuk_publishing_components/components/fieldset", {
+ legend_text: "Link #{index+1}",
+ heading_level: 2,
+ heading_size: "m",
+} do %>
+ <%= render "govuk_publishing_components/components/input", {
+ label: {
+ text: "Title",
+ },
+ name: "popular_links[#{index + 1}][title]",
+ id: "title#{index + 1}",
+ value: item[:title],
+ error_items: errors_for(form.object.errors, "title#{index + 1}".to_sym, use_full_message: false),
+ } %>
+
+ <%= render "govuk_publishing_components/components/input", {
+ label: {
+ text: "URL",
+ },
+ name: "popular_links[#{index + 1}][url]",
+ id: "url#{index + 1}",
+ value: item[:url],
+ error_items: errors_for(form.object.errors, "url#{index + 1}".to_sym, use_full_message: false),
+ } %>
+<% end %>
diff --git a/app/views/homepage/popular_links/_sidebar.html.erb b/app/views/homepage/popular_links/_sidebar.html.erb
new file mode 100644
index 000000000..b64a7e852
--- /dev/null
+++ b/app/views/homepage/popular_links/_sidebar.html.erb
@@ -0,0 +1,13 @@
+
diff --git a/app/views/homepage/popular_links/edit.html.erb b/app/views/homepage/popular_links/edit.html.erb
new file mode 100644
index 000000000..174aa495f
--- /dev/null
+++ b/app/views/homepage/popular_links/edit.html.erb
@@ -0,0 +1,32 @@
+<% content_for :page_title, 'Edit popular links' %>
+<% content_for :title, 'Edit popular links' %>
+<% content_for :title_context, 'Popular on GOV.UK' %>
+<% unless @latest_popular_links.errors.empty? %>
+ <% content_for :error_summary do %>
+ <%= render("govuk_publishing_components/components/error_summary", {
+ id: "error-summary",
+ title: "There is a problem",
+ items: @latest_popular_links.errors.map do |error|
+ {
+ text: error.message,
+ href: "##{error.attribute.to_s}",
+ }
+ end
+ }) %>
+ <% end %>
+<% end %>
+
+
+ <%= form_for @latest_popular_links, url: update_popular_links_path(@latest_popular_links), method: "patch" do |form| %>
+ <% @latest_popular_links.link_items.each_with_index do |item, index| %>
+ <%= render "homepage/popular_links/form", item:, index:, form: %>
+ <% end %>
+
+ <%= render("govuk_publishing_components/components/button", {
+ text: "Save",
+ type: "submit",
+ }) %>
+ <%= link_to("Cancel", show_popular_links_path, class: "govuk-link govuk-link--no-visited-state") %>
+
+ <% end %>
+
diff --git a/app/views/homepage/popular_links/show.html.erb b/app/views/homepage/popular_links/show.html.erb
index 88cd4ee54..5dfbf6253 100644
--- a/app/views/homepage/popular_links/show.html.erb
+++ b/app/views/homepage/popular_links/show.html.erb
@@ -7,3 +7,6 @@
<%= render "homepage/popular_links/link", item:, index: %>
<% end %>
+
+ <%= render "homepage/popular_links/sidebar" %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 7f193bde8..769a9a3bc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -72,7 +72,11 @@
get "/govuk-sitemap.xml" => "sitemap#index"
- get "/homepage/popular-links" => "homepage#show"
+ get "/homepage/popular-links" => "homepage#show", as: "show_popular_links"
+ post "/homepage/popular-links/create" => "homepage#create", as: "create_popular_links"
+ get "/homepage/popular-links/:id" => "homepage#edit", as: "edit_popular_links"
+ patch "/homepage/popular-links/:id" => "homepage#update", as: "update_popular_links"
+ post "/homepage/popular-links/:id/publish" => "homepage#publish", as: "publish_popular_links"
mount GovukAdminTemplate::Engine, at: "/style-guide"
mount Flipflop::Engine => "/flipflop"
diff --git a/test/functional/homepage_controller_test.rb b/test/functional/homepage_controller_test.rb
index f15fc5f80..75058352d 100644
--- a/test/functional/homepage_controller_test.rb
+++ b/test/functional/homepage_controller_test.rb
@@ -17,4 +17,101 @@ class HomepageControllerTest < ActionController::TestCase
assert_template "homepage/popular_links/show"
end
end
+
+ context "#create" do
+ setup do
+ @popular_links = FactoryBot.create(:popular_links, state: "published")
+ end
+
+ should "render show template" do
+ post :create, params: { id: @popular_links.id }
+
+ assert_response :ok
+ assert_template "homepage/popular_links/show"
+ end
+
+ should "create a new draft popular links" do
+ assert_equal 1, PopularLinksEdition.count
+ assert_equal "published", PopularLinksEdition.last.state
+
+ post :create, params: { id: @popular_links.id }
+
+ assert_equal 2, PopularLinksEdition.count
+ assert_equal "draft", PopularLinksEdition.last.state
+ end
+ end
+
+ context "#edit" do
+ should "render edit template" do
+ popular_links = FactoryBot.create(:popular_links, state: "published")
+
+ post :edit, params: { id: popular_links.id }
+
+ assert_response :ok
+ assert_template "homepage/popular_links/edit"
+ end
+ end
+
+ context "#update" do
+ setup do
+ @popular_links = FactoryBot.create(:popular_links, state: "draft")
+ end
+
+ should "update latest PopularLinksEdition with changed title and url" do
+ assert_equal "title1", @popular_links.link_items[0][:title]
+ assert_equal "https://www.url1.com", @popular_links.link_items[0][:url]
+
+ new_title = "title has changed"
+ new_url = "https://www.changedurl.com"
+ patch :update, params: { id: @popular_links.id,
+ "popular_links" =>
+ { "1" => { "title" => new_title, "url" => new_url },
+ "2" => { "title" => "title2", "url" => "https://www.url2.com" },
+ "3" => { "title" => "title3", "url" => "https://www.url3.com" },
+ "4" => { "title" => "title4", "url" => "https://www.url4.com" },
+ "5" => { "title" => "title5", "url" => "https://www.url5.com" },
+ "6" => { "title" => "title6", "url" => "https://www.url6.com" } } }
+
+ assert_equal new_title, PopularLinksEdition.last.link_items[0][:title]
+ assert_equal new_url, PopularLinksEdition.last.link_items[0][:url]
+ end
+
+ should "redirect to show path on success" do
+ new_title = "title has changed"
+ patch :update, params: { id: @popular_links.id,
+ "popular_links" =>
+ { "1" => { "title" => new_title, "url" => "https://www.url1.com" },
+ "2" => { "title" => "title2", "url" => "https://www.url2.com" },
+ "3" => { "title" => "title3", "url" => "https://www.url3.com" },
+ "4" => { "title" => "title4", "url" => "https://www.url4.com" },
+ "5" => { "title" => "title5", "url" => "https://www.url5.com" },
+ "6" => { "title" => "title6", "url" => "https://www.url6.com" } } }
+
+ assert_redirected_to show_popular_links_path
+ end
+
+ should "render edit template on errors" do
+ patch :update, params: { id: @popular_links.id,
+ "popular_links" =>
+ { "1" => { "title" => "title has changed", "url" => "https://www.url1.com" } } }
+
+ assert_template "homepage/popular_links/edit"
+ end
+ end
+
+ context "#publish" do
+ setup do
+ @popular_links = FactoryBot.create(:popular_links, state: "draft")
+ end
+
+ should "publish latest draft popular links and render show template" do
+ assert_equal "draft", PopularLinksEdition.last.state
+
+ post :publish, params: { id: @popular_links.id }
+
+ assert_response :ok
+ assert_template "homepage/popular_links/show"
+ assert_equal "published", PopularLinksEdition.last.state
+ end
+ end
end
diff --git a/test/integration/homepage_popular_links_test.rb b/test/integration/homepage_popular_links_test.rb
index c1932e88a..617dde2b3 100644
--- a/test/integration/homepage_popular_links_test.rb
+++ b/test/integration/homepage_popular_links_test.rb
@@ -3,30 +3,136 @@
class HomepagePopularLinksTest < JavascriptIntegrationTest
setup do
setup_users
- @popular_links = FactoryBot.create(:popular_links)
+ @popular_links = FactoryBot.create(:popular_links, state: "published")
visit_popular_links
end
- should "show page title" do
- assert_title "Popular on GOV.UK"
- end
+ context "#show" do
+ should "render page title" do
+ assert_title "Popular on GOV.UK"
+ end
+
+ should "render 'Homepage' as a page title context" do
+ assert page.has_content?("Homepage")
+ end
+
+ should "have 6 links with title and url" do
+ assert page.has_css?(".govuk-summary-card__title", count: 6)
+ assert page.has_text?("Title", count: 6)
+ assert page.has_text?("URL", count: 6)
+ end
+
+ should "have popular links version and status" do
+ assert page.has_text?("Edition")
+ assert page.has_text?(@popular_links.version_number)
+ assert page.has_text?("Status")
+ assert page.has_text?("PUBLISHED")
+ assert page.has_css?(".govuk-tag--green")
+ end
+
+ should "have 'Create new edition' button" do
+ assert page.has_text?("Create new edition")
+ end
+
+ should "navigate to create path on click of 'Create new edition'" do
+ click_button("Create new edition")
+ assert_current_path create_popular_links_path
+ end
- should "show 'Homepage' as a page title context" do
- assert page.has_content?("Homepage")
+ should "render new draft popular links with edit option when 'Create new edition' button is clicked" do
+ click_button("Create new edition")
+ within(:css, ".govuk-tag--yellow") do
+ assert page.has_content?("DRAFT")
+ end
+ end
end
- should "have 6 links with title and url" do
- assert page.has_css?(".govuk-summary-card__title", count: 6)
- assert page.has_text?("Title", count: 6)
- assert page.has_text?("URL", count: 6)
+ context "#create" do
+ should "create and show new edition with draft status and with an option to edit popular links" do
+ click_button("Create new edition")
+
+ assert page.has_text?("Edition")
+ assert page.has_text?(@popular_links.version_number)
+ assert page.has_text?("Status")
+ assert page.has_text?("DRAFT")
+ assert page.has_css?(".govuk-tag--yellow")
+ assert page.has_text?("Edit popular links")
+ end
+
+ should "create a new record with next version and 'draft' status" do
+ row = find_all(".govuk-summary-list__row")
+ assert row[0].has_text?("Edition")
+ assert row[0].has_text?("1")
+ assert row[1].has_text?("Status")
+ assert row[1].has_text?("PUBLISHED")
+
+ click_button("Create new edition")
+
+ row = find_all(".govuk-summary-list__row")
+ assert row[0].has_text?("Edition")
+ assert row[0].has_text?("2")
+ assert row[1].has_text?("Status")
+ assert row[1].has_text?("DRAFT")
+ end
end
- should "have popular links version and status" do
- assert page.has_text?("Edition")
- assert page.has_text?(@popular_links.version_number)
- assert page.has_text?("Status")
- assert page.has_text?("DRAFT")
- assert page.has_css?(".govuk-tag--yellow")
+ context "#edit" do
+ setup do
+ click_button("Create new edition")
+ click_link("Edit popular links")
+ end
+
+ should "render page title" do
+ assert_title "Edit popular links"
+ end
+
+ should "render 'Popular on GOV.UK' as a page title context" do
+ assert page.has_content?("Popular on GOV.UK")
+ end
+
+ should "have 6 links with title and url" do
+ assert page.has_css?(".govuk-input", count: 12)
+ assert page.has_text?("Title", count: 6)
+ assert page.has_text?("URL", count: 6)
+ end
+
+ should "update record when 'Save' is clicked" do
+ fill_in "popular_links[1][title]", with: "new title 1"
+ click_button("Save")
+
+ assert page.has_text?("Popular links draft saved.")
+ assert page.has_text?("new title 1")
+ end
+
+ should "show validation errors for missing link and url" do
+ fill_in "popular_links[1][title]", with: ""
+ fill_in "popular_links[1][url]", with: ""
+ click_button("Save")
+
+ assert page.has_text?("Title is required for Link 1")
+ assert page.has_text?("URL is required for Link 1")
+ end
+
+ should "trim spaces from start and end of urls" do
+ fill_in "popular_links[1][url]", with: " www.abc.com "
+ click_button("Save")
+
+ assert page.has_text?("www.abc.com")
+ assert_not page.has_text?(" www.abc.com ")
+ end
+
+ should "render create page when 'Cancel' is clicked" do
+ click_link("Cancel")
+
+ assert_current_path show_popular_links_path
+ end
+
+ should "not save any changes when 'Cancel' is clicked" do
+ fill_in "popular_links[1][url]", with: "www.abc.com"
+ click_link("Cancel")
+
+ assert_not page.has_text?("www.abc.com")
+ end
end
def visit_popular_links
diff --git a/test/models/popular_links_edition_test.rb b/test/models/popular_links_edition_test.rb
index 613503b16..d640be6d2 100644
--- a/test/models/popular_links_edition_test.rb
+++ b/test/models/popular_links_edition_test.rb
@@ -11,32 +11,46 @@ class PopularLinksEditionTest < ActiveSupport::TestCase
popular_links = FactoryBot.build(:popular_links, link_items:)
assert_not popular_links.valid?
- assert popular_links.errors[:link_items].any?
+ assert_equal "6 links are required", popular_links.errors.messages[:link_items][0]
end
- should "validate all links have url" do
- link_items = [{ title: "title1" },
- { url: "url2", title: "title2" },
- { url: "url3", title: "title3" },
- { url: "url4", title: "title4" },
- { url: "url5", title: "title5" },
- { url: "url6", title: "title6" }]
+ should "validate all links have url and title" do
+ link_items = [{ url: "https://www.url1.com", title: "" },
+ { title: "title2" },
+ { url: "https://www.url3.com", title: "title3" },
+ { url: "https://www.url4.com", title: "title4" },
+ { url: "https://www.url5.com", title: "title5" },
+ { url: "https://www.url6.com", title: "title6" }]
popular_links = FactoryBot.build(:popular_links, link_items:)
assert_not popular_links.valid?
- assert_equal popular_links.errors[:item][0], "A URL is required for Link 1"
+ assert popular_links.errors.messages[:title1].include?("Title is required for Link 1")
+ assert popular_links.errors.messages[:url2].include?("URL is required for Link 2")
end
- should "validate all links have title" do
- link_items = [{ url: "url1" },
- { url: "url2", title: "title2" },
- { url: "url3", title: "title3" },
- { url: "url4", title: "title4" },
- { url: "url5", title: "title5" },
- { url: "url6", title: "title6" }]
+ should "validate all urls are valid" do
+ link_items = [{ url: "", title: "" },
+ { url: "invalid", title: "title2" },
+ { url: "www.abc.co.uk", title: "title3" },
+ { url: "www.cde.co.uk", title: "title4" },
+ { url: "www.efg.co.uk", title: "title5" },
+ { url: "www.ijk.com", title: "title6" }]
popular_links = FactoryBot.build(:popular_links, link_items:)
assert_not popular_links.valid?
- assert_equal popular_links.errors[:item][0], "A Title is required for Link 1"
+ assert popular_links.errors.messages[:url2].include?("URL is invalid for Link 2, all URLs should have at least one '.' and no spaces.")
+ assert popular_links.errors.messages[:url1].include?("URL is required for Link 1")
+ end
+
+ should "create new record from last 'published' record with status as 'draft' and increased 'version_number'" do
+ popular_links = FactoryBot.create(:popular_links, state: "published")
+
+ assert_equal "published", PopularLinksEdition.last.state
+ assert_equal 1, PopularLinksEdition.last.version_number
+
+ popular_links.create_draft_popular_links_from_last_record
+
+ assert_equal "draft", PopularLinksEdition.last.state
+ assert_equal 2, PopularLinksEdition.last.version_number
end
end
diff --git a/test/support/factories.rb b/test/support/factories.rb
index 0006628ae..77bdac0b6 100644
--- a/test/support/factories.rb
+++ b/test/support/factories.rb
@@ -183,7 +183,7 @@
factory :popular_links, class: "PopularLinksEdition" do
title { "Homepage Popular Links" }
- link_items { [{ url: "url1", title: "title1" }, { url: "url2", title: "title2" }, { url: "url3", title: "title3" }, { url: "url4", title: "title4" }, { url: "url5", title: "title5" }, { url: "url6", title: "title6" }] }
+ link_items { [{ url: "https://www.url1.com", title: "title1" }, { url: "https://www.url2.com", title: "title2" }, { url: "https://www.url3.com", title: "title3" }, { url: "https://www.url4.com", title: "title4" }, { url: "https://www.url5.com", title: "title5" }, { url: "https://www.url6.com", title: "title6" }] }
end
factory :programme_edition, parent: :edition, class: "ProgrammeEdition" do