diff --git a/.ruby-version b/.ruby-version index 276cbf9e..2bf1c1cc 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.0 +2.3.1 diff --git a/Gemfile b/Gemfile index eec99f10..2e369e73 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,8 @@ source 'https://rubygems.org/' gem 'rails', '4.2.5.2' gem 'slimmer', '10.0.0' -gem 'govuk_frontend_toolkit', '~> 3.1.0' +gem 'govuk_elements_rails', '~> 3.0.1' +gem 'govuk_frontend_toolkit', '~> 5.1.2' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 2.7.2' @@ -15,6 +16,7 @@ gem 'airbrake', '~> 4.1.0' gem 'decent_exposure', '~> 2.3.2' gem 'gds-api-adapters', '~> 40.1' +gem 'govuk_navigation_helpers', '~> 4.0' group :development, :test do gem 'pry-byebug' @@ -23,7 +25,7 @@ group :development, :test do end group :test do - gem 'rspec-rails', '~> 3.2.1' + gem 'rspec-rails', '~> 3.2' gem 'cucumber-rails', '~> 1.4.2', require: false gem 'launchy' gem 'webmock', '~> 1.20.4' diff --git a/Gemfile.lock b/Gemfile.lock index 55d080bf..852fbefe 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,46 +41,50 @@ GEM airbrake (4.1.0) builder multi_json - arel (6.0.3) + arel (6.0.4) better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - builder (3.2.2) - byebug (5.0.0) - columnize (= 0.9.0) - capybara (2.4.4) + builder (3.2.3) + byebug (9.0.6) + capybara (2.13.0) + addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - coderay (1.1.0) - columnize (0.9.0) - concurrent-ruby (1.0.1) - crack (0.4.2) + coderay (1.1.1) + concurrent-ruby (1.0.5) + crack (0.4.3) safe_yaml (~> 1.0.0) - cucumber (1.3.20) + cucumber (2.4.0) builder (>= 2.1.2) + cucumber-core (~> 1.5.0) + cucumber-wire (~> 0.0.1) diff-lcs (>= 1.1.3) - gherkin (~> 2.12) + gherkin (~> 4.0) multi_json (>= 1.7.5, < 2.0) multi_test (>= 0.1.2) - cucumber-rails (1.4.2) + cucumber-core (1.5.0) + gherkin (~> 4.0) + cucumber-rails (1.4.5) capybara (>= 1.1.2, < 3) - cucumber (>= 1.3.8, < 2) - mime-types (>= 1.16, < 3) + cucumber (>= 1.3.8, < 4) + mime-types (>= 1.16, < 4) nokogiri (~> 1.5) - rails (>= 3, < 5) + railties (>= 3, < 5.1) + cucumber-wire (0.0.1) debug_inspector (0.0.2) - decent_exposure (2.3.2) - diff-lcs (1.2.5) + decent_exposure (2.3.3) + diff-lcs (1.3) domain_name (0.5.20170223) unf (>= 0.0.5, < 1.0.0) erubis (2.7.0) - execjs (2.6.0) + execjs (2.7.0) gds-api-adapters (40.5.0) link_header lrucache (~> 0.1.1) @@ -88,22 +92,27 @@ GEM plek (>= 1.9.0) rack-cache rest-client (~> 2.0) - gherkin (2.12.2) - multi_json (~> 1.3) - globalid (0.3.6) + gherkin (4.1.1) + globalid (0.3.7) activesupport (>= 4.1.0) govuk-content-schema-test-helpers (1.0.2) json-schema (~> 2.5.1) - govuk_frontend_toolkit (3.1.0) + govuk_elements_rails (3.0.1) + govuk_frontend_toolkit (>= 5.0.2) + rails (>= 4.1.0) + sass (>= 3.2.0) + govuk_frontend_toolkit (5.1.3) rails (>= 3.1.0) sass (>= 3.2.0) + govuk_navigation_helpers (4.0.0) + gds-api-adapters (~> 40.1) http-cookie (1.0.3) domain_name (~> 0.5) - i18n (0.7.0) - json (1.8.3) - json-schema (2.5.1) - addressable (~> 2.3.7) - kgio (2.9.3) + i18n (0.8.1) + json (1.8.6) + json-schema (2.5.2) + addressable (~> 2.3.8) + kgio (2.11.0) launchy (2.4.3) addressable (~> 2.3) link_header (0.0.8) @@ -111,25 +120,27 @@ GEM nokogiri (>= 1.5.9) lrucache (0.1.4) PriorityQueue (~> 0.1.2) - mail (2.6.3) - mime-types (>= 1.16, < 3) + mail (2.6.4) + mime-types (>= 1.16, < 4) method_source (0.8.2) - mime-types (2.99.3) - mini_portile2 (2.0.0) - minitest (5.8.4) - multi_json (1.11.2) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minitest (5.10.1) + multi_json (1.12.1) multi_test (0.1.2) netrc (0.11.0) - nokogiri (1.6.7.2) - mini_portile2 (~> 2.0.0.rc2) + nokogiri (1.6.8.1) + mini_portile2 (~> 2.1.0) null_logger (0.0.1) plek (1.11.0) - pry (0.10.3) + pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - pry-byebug (3.2.0) - byebug (~> 5.0) + pry-byebug (3.4.2) + byebug (~> 9.0) pry (~> 0.10) quiet_assets (1.1.0) railties (>= 3.1, < 5.0) @@ -151,9 +162,9 @@ GEM sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.7) + rails-dom-testing (1.0.8) activesupport (>= 4.2.0.beta, < 5.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) @@ -162,37 +173,37 @@ GEM activesupport (= 4.2.5.2) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.15.0) - rake (10.5.0) + raindrops (0.18.0) + rake (12.0.0) rest-client (2.0.1) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rspec-core (3.2.3) - rspec-support (~> 3.2.0) - rspec-expectations (3.2.1) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.2.0) - rspec-mocks (3.2.1) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.2.0) - rspec-rails (3.2.3) - actionpack (>= 3.0, < 4.3) - activesupport (>= 3.0, < 4.3) - railties (>= 3.0, < 4.3) - rspec-core (~> 3.2.0) - rspec-expectations (~> 3.2.0) - rspec-mocks (~> 3.2.0) - rspec-support (~> 3.2.0) - rspec-support (3.2.2) + rspec-support (~> 3.5.0) + rspec-rails (3.5.2) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) safe_yaml (1.0.4) - sass (3.4.16) - sass-rails (5.0.3) - railties (>= 4.0.0, < 5.0) + sass (3.4.23) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) - tilt (~> 1.1) + tilt (>= 1.1, < 3) slimmer (10.0.0) activesupport json @@ -202,28 +213,26 @@ GEM rack rest-client slop (3.6.0) - sprockets (3.5.2) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.0.3) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - thor (0.19.1) - thread_safe (0.3.5) - tilt (1.4.1) + thor (0.19.4) + thread_safe (0.3.6) + tilt (2.0.7) timecop (0.7.4) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.7.2) - execjs (>= 0.3.0) - json (>= 1.8.0) + uglifier (3.1.9) + execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicorn (4.9.0) + unicorn (5.2.0) kgio (~> 2.6) - rack raindrops (~> 0.7) webmock (1.20.4) addressable (>= 2.3.6) @@ -242,13 +251,15 @@ DEPENDENCIES decent_exposure (~> 2.3.2) gds-api-adapters (~> 40.1) govuk-content-schema-test-helpers (~> 1.0.2) - govuk_frontend_toolkit (~> 3.1.0) + govuk_elements_rails (~> 3.0.1) + govuk_frontend_toolkit (~> 5.1.2) + govuk_navigation_helpers (~> 4.0) launchy plek (~> 1.11.0) pry-byebug quiet_assets (~> 1.1.0) rails (= 4.2.5.2) - rspec-rails (~> 3.2.1) + rspec-rails (~> 3.2) sass-rails (~> 5.0) slimmer (= 10.0.0) timecop (~> 0.7.1) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 00000000..e37816b2 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,9 @@ +//= require show-hide-content.js +;(function () { + $('.js-hidden-submit').removeClass('js-hidden-submit') + $('.no-js-panel').removeClass('no-js-panel') + $('.no-js-hidden-submit').addClass('js-hidden-submit') + $('.js-hidden-submit').attr('aria-hidden', 'false') + var showHideContent = new GOVUK.ShowHideContent() + showHideContent.init() +})() \ No newline at end of file diff --git a/app/assets/javascripts/show-hide-content.js b/app/assets/javascripts/show-hide-content.js new file mode 100644 index 00000000..af162c13 --- /dev/null +++ b/app/assets/javascripts/show-hide-content.js @@ -0,0 +1,172 @@ +;(function (global) { + 'use strict' + + var $ = global.jQuery + var GOVUK = global.GOVUK || {} + + function ShowHideContent () { + var self = this + + // Radio and Checkbox selectors + var selectors = { + namespace: 'ShowHideContent', + radio: '[data-target] > input[type="radio"]', + checkbox: '[data-target] > input[type="checkbox"]' + } + + // Escape name attribute for use in DOM selector + function escapeElementName (str) { + var result = str.replace('[', '\\[').replace(']', '\\]') + return result + } + + // Adds ARIA attributes to control + associated content + function initToggledContent () { + var $control = $(this) + var $content = getToggledContent($control) + + // Set aria-controls and defaults + if ($content.length) { + $control.attr('aria-controls', $content.attr('id')) + $control.attr('aria-expanded', 'false') + $content.attr('aria-hidden', 'true') + } + } + + // Return toggled content for control + function getToggledContent ($control) { + var id = $control.attr('aria-controls') + + // ARIA attributes aren't set before init + if (!id) { + id = $control.closest('[data-target]').data('target') + } + + // Find show/hide content by id + return $('#' + id) + } + + // Show toggled content for control + function showToggledContent ($control, $content) { + // Show content + if ($content.hasClass('js-hidden')) { + $content.removeClass('js-hidden') + $content.attr('aria-hidden', 'false') + + // If the controlling input, update aria-expanded + if ($control.attr('aria-controls')) { + $control.attr('aria-expanded', 'true') + } + } + } + + // Hide toggled content for control + function hideToggledContent ($control, $content) { + $content = $content || getToggledContent($control) + + // Hide content + if (!$content.hasClass('js-hidden')) { + $content.addClass('js-hidden') + $content.attr('aria-hidden', 'true') + + // If the controlling input, update aria-expanded + if ($control.attr('aria-controls')) { + $control.attr('aria-expanded', 'false') + } + } + } + + // Handle radio show/hide + function handleRadioContent ($control, $content) { + // All radios in this group which control content + var selector = selectors.radio + '[name=' + escapeElementName($control.attr('name')) + '][aria-controls]' + var $form = $control.closest('form') + var $radios = $form.length ? $form.find(selector) : $(selector) + + // Hide content for radios in group + $radios.each(function () { + hideToggledContent($(this)) + }) + + // Select content for this control + if ($control.is('[aria-controls]')) { + showToggledContent($control, $content) + } + } + + // Handle checkbox show/hide + function handleCheckboxContent ($control, $content) { + // Show checkbox content + if ($control.is(':checked')) { + showToggledContent($control, $content) + } else { // Hide checkbox content + hideToggledContent($control, $content) + } + } + + // Set up event handlers etc + function init ($container, elementSelector, eventSelectors, handler) { + $container = $container || $(document.body) + + // Handle control clicks + function deferred () { + var $control = $(this) + handler($control, getToggledContent($control)) + } + + // Prepare ARIA attributes + var $controls = $(elementSelector) + $controls.each(initToggledContent) + + // Handle events + $.each(eventSelectors, function (idx, eventSelector) { + $container.on('click.' + selectors.namespace, eventSelector, deferred) + }) + + // Any already :checked on init? + if ($controls.is(':checked')) { + $controls.filter(':checked').each(deferred) + } + } + + // Get event selectors for all radio groups + function getEventSelectorsForRadioGroups () { + var radioGroups = [] + + // Build an array of radio group selectors + return $(selectors.radio).map(function () { + var groupName = $(this).attr('name') + + if ($.inArray(groupName, radioGroups) === -1) { + radioGroups.push(groupName) + return 'input[type="radio"][name="' + $(this).attr('name') + '"]' + } + return null + }) + } + + // Set up radio show/hide content for container + self.showHideRadioToggledContent = function ($container) { + init($container, selectors.radio, getEventSelectorsForRadioGroups(), handleRadioContent) + } + + // Set up checkbox show/hide content for container + self.showHideCheckboxToggledContent = function ($container) { + init($container, selectors.checkbox, [selectors.checkbox], handleCheckboxContent) + } + + // Remove event handlers + self.destroy = function ($container) { + $container = $container || $(document.body) + $container.off('.' + selectors.namespace) + } + } + + ShowHideContent.prototype.init = function ($container) { + this.showHideRadioToggledContent($container) + this.showHideCheckboxToggledContent($container) + } + + GOVUK.ShowHideContent = ShowHideContent + global.GOVUK = GOVUK +})(window) \ No newline at end of file diff --git a/app/assets/stylesheets/_govuk-elements.scss b/app/assets/stylesheets/_govuk-elements.scss new file mode 100644 index 00000000..d1b00663 --- /dev/null +++ b/app/assets/stylesheets/_govuk-elements.scss @@ -0,0 +1,55 @@ +// GOV.UK front end toolkit +// Sass variables, mixins and functions +// https://github.com/alphagov/govuk_frontend_toolkit/tree/master/stylesheets + +// Settings (variables) +@import "https://github.com/colours"; // Colour variables +@import "https://github.com/font_stack"; // Font family variables +@import "https://github.com/measurements"; // Widths and gutter variables + +// Mixins +@import "https://github.com/conditionals"; // Media query mixin +@import "https://github.com/device-pixels"; // Retina image mixin +@import "https://github.com/grid_layout"; // Basic grid layout mixin +@import "https://github.com/typography"; // Core bold and heading mixins, also external links +@import "https://github.com/shims"; // Inline block mixin, clearfix placeholder + +// Mixins to generate components (chunks of UI) +// @import "https://github.com/design-patterns/alpha-beta"; +@import "https://github.com/design-patterns/buttons"; +// @import "https://github.com/design-patterns/breadcrumbs"; + +// Functions +// @import "https://github.com/url-helpers"; // Function to output image-url, or prefixed path (Rails and Compass only) + +// GOV.UK elements + +@import "https://github.com/elements/helpers"; // Helper functions and classes + +// Generic (normalize/reset.css) +@import "https://github.com/elements/reset"; + +// Base (unclassed HTML elements) +// These are predefined by govuk_template +// If you're not using govuk_template, uncomment the line below. +// @import "https://github.com/elements/govuk-template-base"; // Base styles set by GOV.UK template + +// Objects (unstyled design patterns) +@import "https://github.com/elements/layout"; // Main content container. Grid layout - rows and column widths + +// Components (chunks of UI) +@import "https://github.com/elements/elements-typography"; // Typography +@import "https://github.com/elements/buttons"; // Buttons +// @import "https://github.com/elements/icons"; // Icons - numbered steps, calendar, search +@import "https://github.com/elements/lists"; // Lists - numbered, bulleted +@import "https://github.com/elements/tables"; // Tables - regular, numeric +@import "https://github.com/elements/details"; // Details summary +@import "https://github.com/elements/panels"; // Panels with a left grey border +@import "https://github.com/elements/forms"; // Form - wrappers, inputs, labels +@import "https://github.com/elements/forms/form-multiple-choice"; // Custom radio buttons and checkboxes +// @import "https://github.com/elements/forms/form-date"; // Date of birth pattern +@import "https://github.com/elements/forms/form-validation"; // Errors and validation +// @import "https://github.com/elements/breadcrumbs"; // Breadcrumbs +// @import "https://github.com/elements/phase-banner"; // Alpha and beta banners and tags +@import "https://github.com/elements/components"; // GOV.UK prefixed styles - blue highlighted box +@import "https://github.com/elements/shame"; // Hacks and workarounds that will go away eventually diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 1d292d60..6d8e149c 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,6 +1,8 @@ @import "https://github.com/typography"; @import "https://github.com/measurements"; @import "https://github.com/design-patterns/buttons"; +@import "https://github.com/govuk-elements"; +@import "https://github.com/taxonomy-signup"; #wrapper { padding-bottom: $gutter; @@ -15,3 +17,4 @@ margin: 20px 0; } } + diff --git a/app/assets/stylesheets/taxonomy-signup.scss b/app/assets/stylesheets/taxonomy-signup.scss new file mode 100644 index 00000000..cde70c2b --- /dev/null +++ b/app/assets/stylesheets/taxonomy-signup.scss @@ -0,0 +1,39 @@ +#email-alert-frontend{ + + .hidden-submit { + margin-left: 16px; + padding-left: 28px; + padding-top: 0; + } + + .js-hidden-submit { + display: none; + } + + .no-js-panel { + border-left-style: none; + padding-left: 33px; + } + + .no-js-hidden-submit{ + margin-top: $gutter; + } + + .multiple-choice { + margin-top: 10px; + margin-bottom: 10px; + } + + .email-option { + @include core-24; + margin-left: 0; + margin-top: $gutter-half; + margin-bottom: $gutter-half; + } + + .button { + clear: both; + float: left; + } + +} \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index def99843..e032b49c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ class ApplicationController < ActionController::Base + include Slimmer::Template include Slimmer::GovukComponents # Prevent CSRF attacks by raising an exception. diff --git a/app/controllers/taxonomy_signups_controller.rb b/app/controllers/taxonomy_signups_controller.rb new file mode 100644 index 00000000..a2544467 --- /dev/null +++ b/app/controllers/taxonomy_signups_controller.rb @@ -0,0 +1,51 @@ +class TaxonomySignupsController < ApplicationController + def new + redirect_to '/' and return unless valid_query_param? + + load_taxon + load_breadcrumbs + end + + def confirm + redirect_to '/' and return unless valid_query_param? + + load_taxon + load_breadcrumbs + end + + def create + load_taxon + signup = TaxonomySignup.new(@taxon.to_h) + + if signup.save + redirect_to signup.subscription_management_url + else + redirect_to confirm_taxonomy_signup_path(topic: taxon_path) + end + end + +private + + def valid_query_param? + taxon_path.present? + end + + def taxon_path + # Topic is the user-facing terminology for taxons. Expect the taxon base + # path to be provided in a param of this name. + params[:topic] + end + + def load_taxon + @taxon ||= EmailAlertFrontend + .services(:content_store) + .content_item(taxon_path) + end + + def load_breadcrumbs + @breadcrumbs = GovukNavigationHelpers::NavigationHelper.new(@taxon) + .taxon_breadcrumbs[:breadcrumbs] + @breadcrumbs.last.merge!(is_current_page: false, url: @taxon['base_path']) + @breadcrumbs << { title: 'Get email alerts', is_current_page: true } + end +end diff --git a/app/models/taxonomy_signup.rb b/app/models/taxonomy_signup.rb new file mode 100644 index 00000000..9a6fe8fd --- /dev/null +++ b/app/models/taxonomy_signup.rb @@ -0,0 +1,34 @@ +class TaxonomySignup + attr_accessor :taxon, :subscription_management_url + + def initialize(taxon) + @taxon = taxon + end + + def save + return false unless taxon.present? + self.subscription_management_url = update_subscription.dig( + 'subscriber_list', 'subscription_url' + ) + true + end + +private + + def update_subscription + EmailAlertFrontend.services(:email_alert_api) + .find_or_create_subscriber_list(subscription_params) + end + + def subscription_params + { + 'title' => taxon['title'], + 'links' => { + # 'taxon_tree' is the key used in email-alert-service for + # notifications, so create a subscriber list with this key. + 'taxon_tree' => [taxon['content_id']] + } + } + end +end + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1f3687e7..89f5e5ad 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -18,5 +18,7 @@ + + <%= javascript_include_tag 'application' %> diff --git a/app/views/taxonomy_signups/_taxon.html.erb b/app/views/taxonomy_signups/_taxon.html.erb new file mode 100644 index 00000000..21c44219 --- /dev/null +++ b/app/views/taxonomy_signups/_taxon.html.erb @@ -0,0 +1,24 @@ +<% is_parent ||= false %> +<% taxon_index ||= 0 %> + +
+ ' + value='<%= taxon['base_path'] %>' + <%= 'checked' if is_parent %>> + +
+ + diff --git a/app/views/taxonomy_signups/confirm.html.erb b/app/views/taxonomy_signups/confirm.html.erb new file mode 100644 index 00000000..3cf96ae7 --- /dev/null +++ b/app/views/taxonomy_signups/confirm.html.erb @@ -0,0 +1,45 @@ +<% content_for :title, @taxon['title'] %> + +<%= render 'govuk_component/breadcrumbs', breadcrumbs: @breadcrumbs %> + +
+
+ + <%= render partial: 'govuk_component/title', locals: { + average_title_length: "long", + title: "What you'll get" + } %> + +

+ You'll get alerts when the government publishes or changes anything on GOV.UK about: <%= @taxon['title'] %>. +

+ +

+ This might be from [number] updates a week. +

+ +

+ You can choose to receive email alerts: +

+ +
    +
  1. instantly (default)
  2. +
  3. once a day, with all updates made that day
  4. +
  5. once a week, with all updates made that week
  6. +
+ +

+ You can set your preferences once you've signed up. +

+ +

+ The preferences you set will apply to all of your email alerts from GOV.UK. +

+ + <%= form_tag(:action => :create) do %> + <%= hidden_field_tag 'topic', @taxon['base_path'] %> + <%= submit_tag('Sign up now', class: 'button') %> + <% end %> + +
+
diff --git a/app/views/taxonomy_signups/new.html.erb b/app/views/taxonomy_signups/new.html.erb new file mode 100644 index 00000000..3fbfa38e --- /dev/null +++ b/app/views/taxonomy_signups/new.html.erb @@ -0,0 +1,47 @@ +<% content_for :title, @taxon['title'] %> + +<%= render 'govuk_component/breadcrumbs', breadcrumbs: @breadcrumbs %> + +
+
+ +<% if @taxon.dig('links', 'child_taxons').present?%> + <%= render partial: 'govuk_component/title', locals: { + average_title_length: "long", + title: "What do you want to get alerts about?" + } %> + + <%= form_tag({action: "confirm"}, method: "get") do %> +
+ What do you want to get alerts about? + + + +
+ <% end %> + +<% else %> + <%= render partial: 'govuk_component/title', locals: { + average_title_length: "long", + title: "Get email alerts" + } %> + +

You are signing up for email alerts about: <%= @taxon['title']%>

+

This will include: <%= @taxon['description']%>

+ + Continue + +<% end %> + +
+
diff --git a/config/routes.rb b/config/routes.rb index 503e6e6f..ce58bd05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,12 @@ Rails.application.routes.draw do - get '/*base_path' => 'email_alert_signups#new', as: :email_alert_signup, constraints: { base_path: %r|.*/email-signup|} post '/*base_path' => 'email_alert_signups#create', as: :email_alert_signups, constraints: { base_path: %r|.*/email-signup|} + + get '/email-signup' => 'taxonomy_signups#new', as: :new_taxonomy_signup + get '/email-signup/confirm' => 'taxonomy_signups#confirm', as: :confirm_taxonomy_signup + post '/email-signup' => 'taxonomy_signups#create' + + if Rails.env.test? + get '/govdelivery-redirect', to: proc { [200, {}, ['']] } + end end diff --git a/features/email_alerts.feature b/features/email_alerts.feature index 27140b91..bf4e3675 100644 --- a/features/email_alerts.feature +++ b/features/email_alerts.feature @@ -3,7 +3,6 @@ Feature: Email alert signup In order to get up to date notifications about content on GOV.UK I want to be able to sign up for email alerts for relevant content - @mock-email-alert-api Scenario: signing up for email alerts for a page Given a content item exists for an email alert signup page When I access the email signup page diff --git a/features/step_definitions/email_alert_steps.rb b/features/step_definitions/email_alert_steps.rb index 018f8508..e33a1e26 100644 --- a/features/step_definitions/email_alert_steps.rb +++ b/features/step_definitions/email_alert_steps.rb @@ -19,21 +19,25 @@ When(/^I sign up to the email alerts$/) do @subscription_params = { - "title" => "Employment policy", - "tags" => @tags, + 'title' => 'Employment policy', + 'tags' => @tags, } - allow(EmailAlertFrontend.services(:email_alert_api)). - to receive(:find_or_create_subscriber_list). - with(@subscription_params). - and_return(OpenStruct.new("subscriber_list" => OpenStruct.new("subscription_url" => @base_path))) + + @subscriber_list = { + 'subscription_url' => '/govdelivery-redirect', + } + + allow(@mock_email_alert_api).to receive(:find_or_create_subscriber_list) + .with(@subscription_params) + .and_return('subscriber_list' => @subscriber_list) click_on "Create subscription" end Then(/^my subscription should be registered$/) do - expect(EmailAlertFrontend.services(:email_alert_api)) - .to have_received(:find_or_create_subscriber_list) + expect(@mock_email_alert_api).to have_received(:find_or_create_subscriber_list) .with(@subscription_params) + expect(current_path).to eq '/govdelivery-redirect' end Given(/^a government email alert page exists$/) do diff --git a/features/step_definitions/taxonomy_email_alert_steps.rb b/features/step_definitions/taxonomy_email_alert_steps.rb new file mode 100644 index 00000000..956cc220 --- /dev/null +++ b/features/step_definitions/taxonomy_email_alert_steps.rb @@ -0,0 +1,84 @@ +Given(/^a taxon in the middle of the taxonomy$/) do + @taxon = { + content_id: 'taxon-uuid', + base_path: '/education/further-education', + title: 'Further education', + description: 'Further education content', + links: { + parent_taxons: [ + { + base_path: '/education', + title: 'Education', + description: 'Education content', + links: {}, + } + ], + child_taxons: [ + { + base_path: '/education/funding', + title: 'Funding', + description: 'Funding content', + links: { + parent_taxons: [ + { + base_path: '/education/further-education', + title: 'Further education', + description: 'Further education content', + links: {}, + } + ] + } + } + ], + } + } + + content_store_has_item(@taxon[:base_path], @taxon) +end + +When(/^i visit its signup page$/) do + visit new_taxonomy_signup_path(topic: @taxon[:base_path] ) +end + +Then(/^i can subscribe to the taxon or one of its children$/) do + expect(page).to have_content(@taxon[:title]) + expect(page).to have_content(@taxon.dig(:links, :child_taxons).first[:title]) +end + +When(/^i choose to subscribe to the taxon$/) do + within('form') do + all('input[value="Continue"]').last.click + end +end + +Then(/^i see a confirmation page$/) do + expect(page).to have_content("You can set your preferences once you've signed up.") + expect(page).to have_button('Sign up now') +end + +When(/^i confirm$/) do + @subscription_params = { + 'title' => @taxon[:title], + 'links' => { 'taxon_tree' => [@taxon[:content_id]] }, + } + + @subscriber_list = { + 'subscription_url' => '/govdelivery-redirect', + } + + allow(@mock_email_alert_api).to receive(:find_or_create_subscriber_list) + .with(@subscription_params) + .and_return('subscriber_list' => @subscriber_list) + + click_button 'Sign up now' +end + +Then(/^my subscription is created$/) do + expect(@mock_email_alert_api).to have_received(:find_or_create_subscriber_list) + .with(@subscription_params) +end + +Then(/^i am redirected to manage my subscriptions off of govuk$/) do + expect(current_path).to eq '/govdelivery-redirect' +end + diff --git a/features/support/content_schema_helper.rb b/features/support/content_schema_helper.rb new file mode 100644 index 00000000..da7baea4 --- /dev/null +++ b/features/support/content_schema_helper.rb @@ -0,0 +1,7 @@ +require_relative '../../lib/govuk_content_schema_examples' + +module ContentSchemaHelper + include GovukContentSchemaExamples +end + +World(ContentSchemaHelper) diff --git a/features/support/email_alert_api.rb b/features/support/email_alert_api.rb deleted file mode 100644 index 826d5ec1..00000000 --- a/features/support/email_alert_api.rb +++ /dev/null @@ -1,4 +0,0 @@ -Before('@mock-email-alert-api') do - mock_email_alert_api = double - EmailAlertFrontend.register_service(:email_alert_api, mock_email_alert_api) -end diff --git a/features/support/email_alert_api_helper.rb b/features/support/email_alert_api_helper.rb new file mode 100644 index 00000000..b0532f5a --- /dev/null +++ b/features/support/email_alert_api_helper.rb @@ -0,0 +1,4 @@ +Before do + @mock_email_alert_api = instance_double(GdsApi::EmailAlertApi) + EmailAlertFrontend.register_service(:email_alert_api, @mock_email_alert_api) +end diff --git a/features/support/email_alert_signup_helper.rb b/features/support/email_alert_signup_helper.rb deleted file mode 100644 index 70e087ac..00000000 --- a/features/support/email_alert_signup_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module EmailAlertSignupHelper - include GovukContentSchemaExamples -end - -World(EmailAlertSignupHelper) diff --git a/features/support/env.rb b/features/support/env.rb index 66346e48..3e054e02 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -7,7 +7,6 @@ require 'cucumber/rails' require 'cucumber/rspec/doubles' require 'webmock/cucumber' -require 'slimmer/test' # Capybara defaults to CSS3 selectors rather than XPath. # If you'd prefer to use XPath, just uncomment this line and adjust any @@ -31,10 +30,4 @@ # ActionController::Base.allow_rescue = false -require_relative '../../lib/govuk_content_schema_examples' -require 'slimmer/test_helpers/govuk_components' -include Slimmer::TestHelpers::GovukComponents -Before do - stub_shared_component_locales -end diff --git a/features/support/slimmer_helper.rb b/features/support/slimmer_helper.rb new file mode 100644 index 00000000..59142f00 --- /dev/null +++ b/features/support/slimmer_helper.rb @@ -0,0 +1,8 @@ +require 'slimmer/test' +require 'slimmer/test_helpers/govuk_components' + +include Slimmer::TestHelpers::GovukComponents + +Before do + stub_shared_component_locales +end diff --git a/features/taxonomy_email_alerts.feature b/features/taxonomy_email_alerts.feature new file mode 100644 index 00000000..fbe6370b --- /dev/null +++ b/features/taxonomy_email_alerts.feature @@ -0,0 +1,14 @@ +Feature: Email alert signup + As an interested person + In order to get relevant notifications about content updates on GOV.UK + I want to be able to sign up for email alerts about topics within the taxonomy + + Scenario: Signing up for email alerts about a topic + Given a taxon in the middle of the taxonomy + When i visit its signup page + Then i can subscribe to the taxon or one of its children + When i choose to subscribe to the taxon + Then i see a confirmation page + When i confirm + Then my subscription is created + And i am redirected to manage my subscriptions off of govuk diff --git a/spec/controllers/taxonomy_signups_controller_spec.rb b/spec/controllers/taxonomy_signups_controller_spec.rb new file mode 100644 index 00000000..3c0edd11 --- /dev/null +++ b/spec/controllers/taxonomy_signups_controller_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +RSpec.describe TaxonomySignupsController do + include GdsApi::TestHelpers::ContentStore + + describe "GET new" do + it 'redirects to root unless valid query param provided' do + get :new, bad_param: '/education/some-rando-item' + + expect(response.status).to eq 302 + expect(response.location).to eq 'http://test.host/' + end + + it 'errors if no taxon found' do + content_store_does_not_have_item('/education/some-rando-item') + get :new, topic: '/education/some-rando-item' + + expect(response.status).to eq 404 + end + end +end diff --git a/spec/models/email_alert_signup_spec.rb b/spec/models/email_alert_signup_spec.rb index 14a30413..aec48e76 100644 --- a/spec/models/email_alert_signup_spec.rb +++ b/spec/models/email_alert_signup_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" require 'gds_api/test_helpers/email_alert_api' -describe EmailAlertSignup do +RSpec.describe EmailAlertSignup do include GovukContentSchemaExamples include GdsApi::TestHelpers::EmailAlertApi diff --git a/spec/models/taxonomy_signup_spec.rb b/spec/models/taxonomy_signup_spec.rb new file mode 100644 index 00000000..c185de86 --- /dev/null +++ b/spec/models/taxonomy_signup_spec.rb @@ -0,0 +1,48 @@ +require 'rails_helper' + +RSpec.describe TaxonomySignup do + describe "#save" do + let(:mock_email_alert_api) do + instance_double(EmailAlertFrontend.services(:email_alert_api).class) + end + + let(:fake_taxon) { { 'title' => 'Foo', 'content_id' => 'foo-id' } } + + before do + allow(EmailAlertFrontend) + .to receive(:services) + .with(:email_alert_api) + .and_return(mock_email_alert_api) + allow(mock_email_alert_api) + .to receive(:find_or_create_subscriber_list) + .and_return('subscriber_list' => { 'subscription_url' => '/govdelivery' }) + end + + it 'asks email-alert-api to find or create a subscriber list' do + signup = TaxonomySignup.new(fake_taxon) + + expect(signup.save).to be + expect(mock_email_alert_api) + .to have_received(:find_or_create_subscriber_list) + .with('title' => 'Foo', 'links' => { 'taxon_tree' => ['foo-id'] }) + end + + it 'sets the subscription management url' do + signup = TaxonomySignup.new(fake_taxon) + + expect(signup.save).to be + expect(signup.subscription_management_url).to eq '/govdelivery' + end + + context 'when no taxon present' do + it 'does nothing' do + signup = TaxonomySignup.new(nil) + + expect(signup.save).to_not be + expect(mock_email_alert_api).to_not have_received(:find_or_create_subscriber_list) + expect(signup.subscription_management_url).to eq nil + end + end + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d33cd4f2..65dda348 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,7 @@ require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'webmock/rspec' +require 'slimmer/rspec' require_relative '../lib/govuk_content_schema_examples' # Requires supporting ruby files with custom matchers and macros, etc,