diff --git a/Gemfile b/Gemfile
index 61d701ea3..7b30cd47c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -43,6 +43,7 @@ group :test do
gem "i18n-coverage"
gem "minitest-reporters"
gem "mocha"
+ gem "shoulda-context"
gem "simplecov"
gem "timecop"
gem "webmock", require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index cfc46ef9c..42e099718 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -567,6 +567,7 @@ GEM
concurrent-ruby (~> 1.0, >= 1.0.2)
sentry-ruby-core (5.3.1)
concurrent-ruby
+ shoulda-context (2.0.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
@@ -646,6 +647,7 @@ DEPENDENCIES
rss
rubocop-govuk
sassc-rails
+ shoulda-context
simplecov
slimmer
sprockets-rails
diff --git a/app/views/components/_heading.html.erb b/app/views/components/_heading.html.erb
deleted file mode 100644
index 20b065995..000000000
--- a/app/views/components/_heading.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<%
- type ||= nil
- big ||= false
- extra ||= false
- url ||= nil
-%>
-
<%= "-with-extra" if extra %>">
-
- <% if type %>
<%= type %>
<% end %>
-
<%= link_to_if url, heading, url, class: "govuk-link" %>
-
-
diff --git a/test/components/all_components_test.rb b/test/components/all_components_test.rb
new file mode 100644
index 000000000..00b65f81a
--- /dev/null
+++ b/test/components/all_components_test.rb
@@ -0,0 +1,57 @@
+require "test_helper"
+
+class AllComponentsTest < ActionController::TestCase
+ Dir.glob("app/views/components/*.erb").each do |filename|
+ template = filename.split("/").last
+ component_name = template.sub("_", "").sub(".html", "").sub(".erb", "").gsub("-", "_")
+
+ context component_name do
+ yaml_file = "#{__dir__}/../../app/views/components/docs/#{component_name}.yml"
+
+ should "is documented" do
+ assert File.exist?(yaml_file)
+ end
+
+ should "have the correct documentation" do
+ yaml = YAML.unsafe_load_file(yaml_file)
+
+ assert yaml["name"]
+ assert yaml["description"]
+ assert yaml["examples"]
+ assert yaml["accessibility_criteria"] || yaml["shared_accessibility_criteria"]
+ end
+
+ should "have the correct class in the ERB template" do
+ erb = File.read(filename)
+
+ class_name = "app-c-#{component_name.dasherize}"
+
+ assert_includes erb, class_name
+ end
+
+ should "have a correctly named template file" do
+ template_file = "#{__dir__}/../../app/views/components/_#{component_name}.html.erb"
+
+ assert File.exist?(template_file)
+ end
+
+ should "have a correctly named spec file" do
+ rspec_file = "#{__dir__}/../../test/components/#{component_name.tr('-', '_')}_test.rb"
+
+ assert File.exist?(rspec_file)
+ end
+
+ should "have a correctly named SCSS file" do
+ css_file = "#{__dir__}/../../app/assets/stylesheets/components/_#{component_name.tr('_', '-')}.scss"
+
+ assert File.exist?(css_file)
+ end
+
+ should "not use `html_safe`", not_applicable: component_name.in?(%w[govspeak]) do
+ file = File.read(filename)
+
+ assert_no_match file, "html_safe"
+ end
+ end
+ end
+end