diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index da57487dc..0adb800a2 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,7 +13,22 @@ jobs: steps: - name: Check out code uses: actions/checkout@v2 + - id: cache-docker + uses: actions/cache@v2 + with: + path: /tmp/docker-save + key: + docker-save-${{ hashFiles('Dockerfile', 'Gemfile.lock', + 'package-lock.json') }} + - name: Load cached Docker image + run: docker load -i /tmp/docker-save/snapshot.tar || true + if: steps.cache-docker.outputs.cache-hit == 'true' - name: Build - run: docker-compose -f docker-compose.ci.yml build + run: docker-compose -f docker-compose.ci.yml -p app build - name: Test run: docker-compose -f docker-compose.ci.yml run --rm test script/test + - name: Prepare Docker cache + run: + mkdir -p /tmp/docker-save && docker save app_test:latest -o + /tmp/docker-save/snapshot.tar && ls -lh /tmp/docker-save + if: always() && steps.cache-docker.outputs.cache-hit != 'true' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5a175e6b2..5bbc44296 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,6 @@ env: CF_PASSWORD: ${{ secrets.CF_PASSWORD }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - GITHUB_SECRETS_JSON: ${{ toJson(secrets) }} jobs: build: @@ -48,11 +47,12 @@ jobs: deploy: needs: build runs-on: ubuntu-latest + env: + TF_VAR_docker_image: ${{needs.build.outputs.tf_var_docker_image}} + GITHUB_SECRETS_JSON: ${{ toJson(secrets) }} steps: - name: Check out code uses: actions/checkout@v2 - - name: Set TFVAR Docker Image environment variable - run: echo "TF_VAR_docker_image=${{needs.build.outputs.tf_var_docker_image}}" >> $GITHUB_ENV - name: Deploy terraform to staging env: TF_VAR_environment: "staging" diff --git a/Brewfile b/Brewfile new file mode 100644 index 000000000..c885b8017 --- /dev/null +++ b/Brewfile @@ -0,0 +1,3 @@ +brew "postgresql" +brew "redis" +brew "rbenv" diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c550876d..d2be14466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,16 @@ The format is based on [Keep a Changelog 1.0.0]. ## [Unreleased] -## [release-010] - 2021-05-27 +## [release-011] - 2021-05-10 + +- error pages are styled +- document how to manage live environment variables +- Update all fixtures to include tasks +- Update the specs to work with the new task-enabled fixtures +- Remove `Journey#section_groups` and sever the direct Journey -> Steps association +- tasks now have their completion status indicated on the task list + +## [release-010] - 2021-04-27 - add header and footer information for feedback and data requests - force SSL in production to only accept HTTPS traffic, enable HSTS and secure tower cookies @@ -21,18 +30,20 @@ The format is based on [Keep a Changelog 1.0.0]. - existing specification page displays useful message when no specs exist - fix text input field width to fit full screen width - document where to find the service in the readme +- cache CI builds to reduce build times +- log information about contentful cache busting webhooks for debugging -## [release-009] - 2021-05-21 +## [release-009] - 2021-04-21 - fix multiple specification fields - content security policy - remove humans.txt -## [release-008] - 2021-05-19 +## [release-008] - 2021-04-19 - auto deploy research and preview environments -## [release-007] - 2021-05-19 +## [release-007] - 2021-04-19 - Add `noindex,nofollow` meta tag to all pages, as per Gov.UK guidance - fix API auth by switching mechanism from Basic to Token @@ -149,7 +160,8 @@ Contentful fixture - Contentful can redirect users to preview endpoints - users can be asked to answer a long text question -[unreleased]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-010...HEAD +[unreleased]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-011...HEAD +[release-011]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-010...release-011 [release-010]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-009...release-010 [release-009]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-008...release-009 [release-008]: https://github.com/DFE-Digital/buy-for-your-school/compare/release-007...release-008 diff --git a/Dockerfile b/Dockerfile index ffd1c31eb..3e18e9aa5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,72 +1,101 @@ -# BUILD STAGE # -FROM ruby:2.6.6 AS build +# ------------------------------------------------------------------------------ +# Base +# ------------------------------------------------------------------------------ +FROM ruby:2.6.6 as base MAINTAINER dxw -ENV INSTALL_PATH /srv/app -ARG RAILS_ENV -ENV RAILS_ENV=${RAILS_ENV:-production} -ENV RACK_ENV=${RAILS_ENV:-production} - -WORKDIR $INSTALL_PATH +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash +RUN curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - +RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update && apt-get install -qq -y \ build-essential \ libpq-dev \ --fix-missing --no-install-recommends -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ - && apt-get install -y nodejs -COPY package.json ./package.json -COPY package-lock.json ./package-lock.json +ENV APP_HOME /srv/app +ENV DEPS_HOME /deps -RUN npm install +ARG RAILS_ENV +ENV RAILS_ENV ${RAILS_ENV:-production} +ENV NODE_ENV ${RAILS_ENV:-production} -COPY Gemfile* ./ -RUN gem install bundler:2.1.4 --no-document +# ------------------------------------------------------------------------------ +# Dependencies +# ------------------------------------------------------------------------------ +FROM base AS dependencies -ARG BUNDLE_EXTRA_GEM_GROUPS -ENV BUNDLE_GEM_GROUPS=${BUNDLE_EXTRA_GEM_GROUPS:-"production"} -RUN bundle config set no-cache "true" -RUN bundle config set with $BUNDLE_GEM_GROUPS -RUN bundle install --no-binstubs --retry=3 --jobs=4 +RUN mkdir -p ${DEPS_HOME} +WORKDIR $DEPS_HOME -# Copy app code (sorted by vague frequency of change for caching) -RUN mkdir -p ${INSTALL_PATH}/log -RUN mkdir -p ${INSTALL_PATH}/tmp - -COPY config.ru ${INSTALL_PATH}/config.ru -COPY Rakefile ${INSTALL_PATH}/Rakefile - -COPY public ${INSTALL_PATH}/public -COPY vendor ${INSTALL_PATH}/vendor -COPY bin ${INSTALL_PATH}/bin -COPY lib ${INSTALL_PATH}/lib -COPY config ${INSTALL_PATH}/config -COPY db ${INSTALL_PATH}/db -COPY script ${INSTALL_PATH}/script -COPY spec ${INSTALL_PATH}/spec -COPY app ${INSTALL_PATH}/app -# End +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ + && apt-get install -y nodejs -# RELEASE STAGE # -FROM ruby:2.6.6 AS release +# Install Javascript dependencies +COPY package-lock.json $DEPS_HOME/package-lock.json +COPY package.json $DEPS_HOME/package.json +RUN npm install -ENV INSTALL_PATH /srv/app -ARG RAILS_ENV -ENV RAILS_ENV=${RAILS_ENV:-production} -ENV RACK_ENV=${RAILS_ENV:-production} +# Install Ruby dependencies +COPY Gemfile $DEPS_HOME/Gemfile +COPY Gemfile.lock $DEPS_HOME/Gemfile.lock +RUN gem update --system +RUN gem install bundler -v 2.2.16 -WORKDIR $INSTALL_PATH +ENV BUNDLE_GEM_GROUPS=$RAILS_ENV +RUN bundle config set frozen "true" +RUN bundle config set no-cache "true" +RUN bundle config set with $BUNDLE_GEM_GROUPS +RUN bundle install --no-binstubs --retry=10 --jobs=4 -RUN gem install bundler:2.1.4 --no-document +# ------------------------------------------------------------------------------ +# Web +# ------------------------------------------------------------------------------ +FROM dependencies AS web -COPY --from=build /usr/local/bundle/ /usr/local/bundle/ -COPY --from=build $INSTALL_PATH $INSTALL_PATH +RUN mkdir -p ${APP_HOME} +WORKDIR ${APP_HOME} + +# Copy app code (sorted by vague frequency of change for caching) +RUN mkdir -p ${APP_HOME}/log +RUN mkdir -p ${APP_HOME}/tmp + +COPY config.ru ${APP_HOME}/config.ru +COPY Rakefile ${APP_HOME}/Rakefile + +COPY Gemfile $APP_HOME/Gemfile +COPY Gemfile.lock $APP_HOME/Gemfile.lock + +COPY public ${APP_HOME}/public +COPY vendor ${APP_HOME}/vendor +COPY bin ${APP_HOME}/bin +COPY lib ${APP_HOME}/lib +COPY config ${APP_HOME}/config +COPY db ${APP_HOME}/db +COPY script ${APP_HOME}/script +COPY app ${APP_HOME}/app +# End -# Compiling assets requires a key to exist: https://github.com/rails/rails/issues/32947 -RUN if [ "$RAILS_ENV" = "production" ]; then \ - RAILS_ENV=production SECRET_KEY_BASE="key" bundle exec rake assets:precompile; \ - fi +# Create tmp/pids +RUN mkdir -p tmp/pids + +# This must be ordered before rake assets:precompile +RUN cp -R $DEPS_HOME/node_modules $APP_HOME/node_modules +RUN cp -R $DEPS_HOME/node_modules/govuk-frontend/govuk/assets $APP_HOME/app/assets + +RUN RAILS_ENV=production \ + SECRET_KEY_BASE="key" \ + APPLICATION_URL= \ + CONTENTFUL_URL= \ + CONTENTFUL_SPACE= \ + CONTENTFUL_ENVIRONMENT= \ + CONTENTFUL_ACCESS_TOKEN= \ + CONTENTFUL_DEFAULT_CATEGORY_ENTRY_ID= \ + CONTENTFUL_PREVIEW_APP= \ + CONTENTFUL_ENTRY_CACHING= \ + SUPPORT_EMAIL= \ + REDIS_URL= \ + bundle exec rake assets:precompile COPY ./docker-entrypoint.sh / RUN chmod +x /docker-entrypoint.sh @@ -75,3 +104,16 @@ ENTRYPOINT ["/docker-entrypoint.sh"] EXPOSE 3000 CMD ["bundle", "exec", "rails", "server"] + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +FROM web as test + +RUN apt-get install -qq -y shellcheck + +COPY package.json ${APP_HOME}/package.json +COPY package-lock.json ${APP_HOME}/package-lock.json + +COPY .rspec ${APP_HOME}/.rspec +COPY spec ${APP_HOME}/spec diff --git a/Gemfile.lock b/Gemfile.lock index b9f09ba0a..1fd7b20d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,60 +1,60 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.1.3.1) - actionpack (= 6.1.3.1) - activesupport (= 6.1.3.1) + actioncable (6.1.3.2) + actionpack (= 6.1.3.2) + activesupport (= 6.1.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.3.1) - actionpack (= 6.1.3.1) - activejob (= 6.1.3.1) - activerecord (= 6.1.3.1) - activestorage (= 6.1.3.1) - activesupport (= 6.1.3.1) + actionmailbox (6.1.3.2) + actionpack (= 6.1.3.2) + activejob (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) mail (>= 2.7.1) - actionmailer (6.1.3.1) - actionpack (= 6.1.3.1) - actionview (= 6.1.3.1) - activejob (= 6.1.3.1) - activesupport (= 6.1.3.1) + actionmailer (6.1.3.2) + actionpack (= 6.1.3.2) + actionview (= 6.1.3.2) + activejob (= 6.1.3.2) + activesupport (= 6.1.3.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.1.3.1) - actionview (= 6.1.3.1) - activesupport (= 6.1.3.1) + actionpack (6.1.3.2) + actionview (= 6.1.3.2) + activesupport (= 6.1.3.2) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.3.1) - actionpack (= 6.1.3.1) - activerecord (= 6.1.3.1) - activestorage (= 6.1.3.1) - activesupport (= 6.1.3.1) + actiontext (6.1.3.2) + actionpack (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) nokogiri (>= 1.8.5) - actionview (6.1.3.1) - activesupport (= 6.1.3.1) + actionview (6.1.3.2) + activesupport (= 6.1.3.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.3.1) - activesupport (= 6.1.3.1) + activejob (6.1.3.2) + activesupport (= 6.1.3.2) globalid (>= 0.3.6) - activemodel (6.1.3.1) - activesupport (= 6.1.3.1) - activerecord (6.1.3.1) - activemodel (= 6.1.3.1) - activesupport (= 6.1.3.1) - activestorage (6.1.3.1) - actionpack (= 6.1.3.1) - activejob (= 6.1.3.1) - activerecord (= 6.1.3.1) - activesupport (= 6.1.3.1) + activemodel (6.1.3.2) + activesupport (= 6.1.3.2) + activerecord (6.1.3.2) + activemodel (= 6.1.3.2) + activesupport (= 6.1.3.2) + activestorage (6.1.3.2) + actionpack (= 6.1.3.2) + activejob (= 6.1.3.2) + activerecord (= 6.1.3.2) + activesupport (= 6.1.3.2) marcel (~> 1.0.0) mini_mime (~> 1.0.2) - activesupport (6.1.3.1) + activesupport (6.1.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -71,9 +71,9 @@ GEM rack (>= 0.9.0) bindata (2.4.8) bindex (0.8.1) - bootsnap (1.7.4) + bootsnap (1.7.5) msgpack (~> 1.0) - brakeman (5.0.0) + brakeman (5.0.1) builder (3.2.4) bullet (6.1.4) activesupport (>= 3.0.0) @@ -99,7 +99,7 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.1.8) connection_pool (2.2.3) - contentful (2.16.0) + contentful (2.16.1) http (> 0.8, < 5.0) multi_json (~> 1) crack (0.4.5) @@ -187,14 +187,14 @@ GEM nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (1.0.0) + marcel (1.0.1) method_source (1.0.0) mini_mime (1.0.3) - mini_portile2 (2.5.0) + mini_portile2 (2.5.1) mini_racer (0.4.0) libv8-node (~> 15.14.0.0) minitest (5.14.4) - mock_redis (0.27.3) + mock_redis (0.28.0) ruby2_keywords msgpack (1.4.2) multi_json (1.15.0) @@ -243,20 +243,20 @@ GEM rack (>= 2.1.0) rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.1.3.1) - actioncable (= 6.1.3.1) - actionmailbox (= 6.1.3.1) - actionmailer (= 6.1.3.1) - actionpack (= 6.1.3.1) - actiontext (= 6.1.3.1) - actionview (= 6.1.3.1) - activejob (= 6.1.3.1) - activemodel (= 6.1.3.1) - activerecord (= 6.1.3.1) - activestorage (= 6.1.3.1) - activesupport (= 6.1.3.1) + rails (6.1.3.2) + actioncable (= 6.1.3.2) + actionmailbox (= 6.1.3.2) + actionmailer (= 6.1.3.2) + actionpack (= 6.1.3.2) + actiontext (= 6.1.3.2) + actionview (= 6.1.3.2) + activejob (= 6.1.3.2) + activemodel (= 6.1.3.2) + activerecord (= 6.1.3.2) + activestorage (= 6.1.3.2) + activesupport (= 6.1.3.2) bundler (>= 1.15.0) - railties (= 6.1.3.1) + railties (= 6.1.3.2) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) @@ -264,9 +264,9 @@ GEM rails-html-sanitizer (1.3.0) loofah (~> 2.3) rails_layout (1.0.42) - railties (6.1.3.1) - actionpack (= 6.1.3.1) - activesupport (= 6.1.3.1) + railties (6.1.3.2) + actionpack (= 6.1.3.2) + activesupport (= 6.1.3.2) method_source rake (>= 0.8.7) thor (~> 1.0) @@ -330,7 +330,7 @@ GEM rubocop (>= 0.90.0, < 2.0) rubocop-ast (>= 0.4.0) ruby-progressbar (1.11.0) - ruby2_keywords (0.0.2) + ruby2_keywords (0.0.4) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) diff --git a/app/controllers/api/contentful/entries_controller.rb b/app/controllers/api/contentful/entries_controller.rb index 867848e77..b568a0fe5 100644 --- a/app/controllers/api/contentful/entries_controller.rb +++ b/app/controllers/api/contentful/entries_controller.rb @@ -5,7 +5,7 @@ class Api::Contentful::EntriesController < ApplicationController skip_before_action :verify_authenticity_token def changed - Rollbar.info("Accepted request to cache bust key: #{cache_key}", params: params) + Rollbar.info("Accepted request to cache bust Contentful Entry", cache_key: cache_key) Cache.delete(key: cache_key) render json: {status: "OK"}, status: :ok diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ab911d644..e2ad2d0c2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -37,6 +37,6 @@ def current_journey def check_user_belongs_to_journey? return true if current_journey.user == current_user - render file: "public/404.html", status: :not_found, layout: false + render "errors/not_found", status: 404 end end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb new file mode 100644 index 000000000..615431d63 --- /dev/null +++ b/app/controllers/errors_controller.rb @@ -0,0 +1,18 @@ +class ErrorsController < ApplicationController + skip_before_action :authenticate_user! + + def internal_server_error + render "errors/internal_server_error", + status: 500 + end + + def not_found + render "errors/not_found", + status: 404 + end + + def unacceptable + render "errors/unacceptable", + status: 422 + end +end diff --git a/app/controllers/preview/entries_controller.rb b/app/controllers/preview/entries_controller.rb index faaad3f99..5e2fbb1b4 100644 --- a/app/controllers/preview/entries_controller.rb +++ b/app/controllers/preview/entries_controller.rb @@ -26,6 +26,6 @@ def entry_id def check_app_is_running_in_preview_env return if ENV["CONTENTFUL_PREVIEW_APP"].eql?("true") - render file: "public/404.html", status: :not_found, layout: false + render "errors/not_found", status: 404 end end diff --git a/app/helpers/journey_helper.rb b/app/helpers/journey_helper.rb deleted file mode 100644 index 87e6dbf12..000000000 --- a/app/helpers/journey_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module JourneyHelper - def section_group_with_steps(journey:, steps:) - step_lookup = steps.group_by(&:contentful_id) - - journey.section_groups.each do |section| - ordered_step_objects = section["steps"].each_with_object([]) { |step, result| - next unless step_lookup[step["contentful_id"]] - result[step["order"]] = step_lookup[step["contentful_id"]].first - } - section["steps"] = ordered_step_objects.compact - end - end -end diff --git a/app/models/step.rb b/app/models/step.rb index d27f8e0d1..575d816cd 100644 --- a/app/models/step.rb +++ b/app/models/step.rb @@ -25,6 +25,10 @@ def answer end end + def answered? + !answer.nil? + end + def primary_call_to_action_text return I18n.t("generic.button.next") unless super.present? super diff --git a/app/models/task.rb b/app/models/task.rb index de484c626..ffbf6b5d0 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -5,11 +5,45 @@ class Task < ApplicationRecord validates :title, :contentful_id, presence: true + attr_accessor + + NOT_STARTED = 0 + IN_PROGRESS = 1 + COMPLETED = 2 + def visible_steps steps.where(hidden: false) end + def visible_steps_count + visible_steps.count + end + def has_single_visible_step? - visible_steps.count == 1 + visible_steps_count == 1 + end + + def answered_questions_count + visible_steps.includes([ + :short_text_answer, + :long_text_answer, + :radio_answer, + :checkbox_answers, + :currency_answer, + :number_answer, + :single_date_answer + ]).count { |step| step.answered? } + end + + def status + if answered_questions_count == visible_steps_count + return COMPLETED + end + + if answered_questions_count > 0 + return IN_PROGRESS + end + + NOT_STARTED end end diff --git a/app/services/create_journey.rb b/app/services/create_journey.rb index 6b3d15946..68e5c26f6 100644 --- a/app/services/create_journey.rb +++ b/app/services/create_journey.rb @@ -17,52 +17,24 @@ def call liquid_template: category.combined_specification_template ) - journey.section_groups = build_section_groupings(sections: contentful_sections) journey.save! contentful_sections.each do |contentful_section| section = CreateSection.new(journey: journey, contentful_section: contentful_section).call - # The entire `if` block here exists to support our fixtures which do not nest steps - # within tasks. Once we update our fixtures to the "new way" of doing things, this - # entire `if` block can be discarded. - if contentful_section.respond_to?(:tasks) && !contentful_section.tasks.any? - question_entries = GetStepsFromSection.new(section: contentful_section).call - question_entries.each do |entry| - fake_contentful_task = OpenStruct.new(title: entry.title, id: entry.id) - task = CreateTask.new(section: section, contentful_task: fake_contentful_task).call + contentful_tasks = GetTasksFromSection.new(section: contentful_section).call + contentful_tasks.each do |contentful_task| + task = CreateTask.new(section: section, contentful_task: contentful_task).call + question_entries = GetStepsFromTask.new(task: contentful_task).call + question_entries.each do |entry| CreateJourneyStep.new( - task: task, contentful_entry: entry + contentful_entry: entry, task: task ).call end - else - contentful_tasks = GetTasksFromSection.new(section: contentful_section).call - contentful_tasks.each do |contentful_task| - task = CreateTask.new(section: section, contentful_task: contentful_task).call - - question_entries = GetStepsFromTask.new(task: contentful_task).call - question_entries.each do |entry| - CreateJourneyStep.new( - contentful_entry: entry, task: task - ).call - end - end end end journey end - - private def build_section_groupings(sections:) - sections.each_with_object([]).with_index { |(section, result), index| - result[index] = { - order: index, - title: section.title, - steps: section.steps.each_with_object([]).with_index { |(step, result), index| - result << {contentful_id: step.id, order: index} - } - } - } - end end diff --git a/app/services/get_steps_from_section.rb b/app/services/get_steps_from_section.rb index 33dd1d47b..ec20989b1 100644 --- a/app/services/get_steps_from_section.rb +++ b/app/services/get_steps_from_section.rb @@ -8,12 +8,14 @@ def initialize(section:) def call question_entry_ids = [] - section.steps.each do |step| - if question_entry_ids.include?(step.id) - send_rollbar_error(message: "A repeated Contentful entry was found in the same section", entry_id: step.id) - raise RepeatEntryDetected.new(step.id) - else - question_entry_ids << step.id + section.tasks.each do |task| + task.steps.each do |step| + if question_entry_ids.include?(step.id) + send_rollbar_error(message: "A repeated Contentful entry was found in the same section", entry_id: step.id) + raise RepeatEntryDetected.new(step.id) + else + question_entry_ids << step.id + end end end diff --git a/app/views/errors/internal_server_error.html.erb b/app/views/errors/internal_server_error.html.erb new file mode 100644 index 000000000..bfebe5005 --- /dev/null +++ b/app/views/errors/internal_server_error.html.erb @@ -0,0 +1,16 @@ +<%= content_for :page_title_prefix, t("errors.internal_server_error.page_title") %> +
+
+
+

+ <%= t("errors.internal_server_error.page_title") %> +

+

+ <%= t("errors.internal_server_error.page_body") %> +

+

+ <%= link_to t("errors.go_home"), root_path, class: "govuk-link" %> +

+
+
+
diff --git a/app/views/errors/not_found.html.erb b/app/views/errors/not_found.html.erb new file mode 100644 index 000000000..78d0e4f58 --- /dev/null +++ b/app/views/errors/not_found.html.erb @@ -0,0 +1,16 @@ +<%= content_for :page_title_prefix, t("errors.not_found.page_title") %> +
+
+
+

+ <%= t("errors.not_found.page_title") %> +

+

+ <%= t("errors.not_found.page_body") %> +

+

+ <%= link_to t("errors.go_home"), root_path, class: "govuk-link" %> +

+
+
+
diff --git a/app/views/errors/unacceptable.html.erb b/app/views/errors/unacceptable.html.erb new file mode 100644 index 000000000..23ea85f2a --- /dev/null +++ b/app/views/errors/unacceptable.html.erb @@ -0,0 +1,16 @@ +<%= content_for :page_title_prefix, t("errors.unacceptable.page_title") %> +
+
+
+

+ <%= t("errors.unacceptable.page_title") %> +

+

+ <%= t("errors.unacceptable.page_body") %> +

+

+ <%= link_to t("errors.go_home"), root_path, class: "govuk-link" %> +

+
+
+
diff --git a/app/views/journeys/show.html.erb b/app/views/journeys/show.html.erb index 572b11d12..358b9118f 100644 --- a/app/views/journeys/show.html.erb +++ b/app/views/journeys/show.html.erb @@ -1,6 +1,6 @@ <%= content_for :title, @journey.category.capitalize %> -<%= link_to I18n.t("generic.button.back"), dashboard_path, class: "govuk-back-link" %> +<%= link_to I18n.t("generic.button.back"), journeys_path, class: "govuk-back-link" %>

<%= I18n.t("specifying.start_page.page_title") %>

    @@ -31,6 +31,13 @@ <%= link_to task.title, journey_task_path(@journey, task), class: "govuk-link", 'aria-describedby': task.id + "-status"%> + <% if task.status == Task::COMPLETED %> + "><%= I18n.t("task_list.status.completed") %> + <% elsif task.status == Task::IN_PROGRESS %> + "><%= I18n.t("task_list.status.in_progress") %> + <% elsif task.status == Task::NOT_STARTED %> + "><%= I18n.t("task_list.status.not_started") %> + <% end %> <% end %> <% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c27cff712..d952ea5d7 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -23,12 +23,14 @@ <%= csrf_meta_tags %> + + <%= csp_meta_tag %> - + <% end -%> Skip to main content @@ -99,9 +101,9 @@ <%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %> - + <% end -%> diff --git a/app/views/tasks/show.html.erb b/app/views/tasks/show.html.erb index 81ae2753d..d67050a78 100644 --- a/app/views/tasks/show.html.erb +++ b/app/views/tasks/show.html.erb @@ -4,7 +4,6 @@

    <%= @task.title %>

      -

      <%= @task.title %>

      • <% @steps.each do |step| %> @@ -23,10 +22,9 @@ <% end %> <% end %> -
      -
    1. - <% end %> + <% end %> + +
    -<%= link_to I18n.t("journey.specification.button"), journey_specification_path(@journey), class: "govuk-button" %> - +<%= link_to I18n.t("task.buttons.back"), journey_path(@journey), class: "govuk-button" %> diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 731b1d880..d42d2573a 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -21,7 +21,7 @@ end # If you are using UJS then enable automatic nonce generation -# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } +Rails.application.config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) } # Set the nonce only to specific directives # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) diff --git a/config/locales/en.yml b/config/locales/en.yml index 8ea1dcf2a..cf7b3dfea 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -85,13 +85,27 @@ en: task_list: status: not_started: Not started + in_progress: In progress completed: Completed + task: + buttons: + back: Back to all questions journey_map: page_title: "Contentful entry map" edit_step_link_text: "Edit step in Contentful" preview_step_link_text: "Preview step in service" spec_template_tag_title: "Specification tag" errors: + go_home: "Back to the service" + internal_server_error: + page_title: "Internal server error" + page_body: "Sorry, there is a problem with the service. Please try again later." + not_found: + page_title: "Page not found" + page_body: "Page not found. If you typed the web address, check it is correct. If you pasted the web address, check you copied the entire address." + unacceptable: + page_title: "Unacceptable request" + page_body: "There was a problem with your request." contentful_entry_not_found: page_title: "An unexpected error occurred" page_body: "The service has had a problem trying to retrieve the required step. The team have been notified of this problem and you should be able to retry shortly." diff --git a/config/routes.rb b/config/routes.rb index 83b497b30..16013d55b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,4 +27,9 @@ end get "dashboard", to: "dashboard#show" + + # Errors + get "/404", to: "errors#not_found" + get "/422", to: "errors#unacceptable" + get "/500", to: "errors#internal_server_error" end diff --git a/db/migrate/20210427154238_remove_section_groups_from_journeys.rb b/db/migrate/20210427154238_remove_section_groups_from_journeys.rb new file mode 100644 index 000000000..7a7931a86 --- /dev/null +++ b/db/migrate/20210427154238_remove_section_groups_from_journeys.rb @@ -0,0 +1,5 @@ +class RemoveSectionGroupsFromJourneys < ActiveRecord::Migration[6.1] + def change + remove_column :journeys, :section_groups, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 42d48e356..8a9155438 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_04_19_082411) do +ActiveRecord::Schema.define(version: 2021_04_27_154238) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" @@ -40,7 +40,6 @@ t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.jsonb "liquid_template", null: false - t.jsonb "section_groups" t.uuid "user_id" t.boolean "started", default: true t.datetime "last_worked_on" diff --git a/doc/managing-environment-variables.md b/doc/managing-environment-variables.md index 2da653fb3..ee8bef68f 100644 --- a/doc/managing-environment-variables.md +++ b/doc/managing-environment-variables.md @@ -1,5 +1,7 @@ # Managing environment variables +## Locally + We use [Dotenv](https://github.com/bkeepers/dotenv) to manage our environment variables locally. The repository will include safe defaults for development in `/.env.example` and for test in `/.env.test`. We use 'example' instead of 'development' (from the Dotenv docs) to be consistent with current dxw conventions and to make it more explicit that these values are not to be committed. @@ -8,3 +10,57 @@ To manage sensitive environment variables: 1. Add the new key and safe default value to the `/.env.example` file eg. `ROLLBAR_TOKEN=ROLLBAR_TOKEN` 2. Add the new key and real value to your local `/.env.development.local` file, which should never be checked into Git. This file will look something like `ROLLBAR_TOKEN=123456789` + +If the environment variable is critical whereby it is required to start the application: + +- Add it to [the Dotenv initialiser](../config/initializers/_dotenv.rb). +- Add it to the Dockerfile where `rake assets:precompile` is run. + +## Live environments + +We use [GitHub Secrets as the canonical source of our environment variables](https://github.com/DFE-Digital/buy-for-your-school/settings/secrets/actions). + +Environment variables are all managed in the same way. Secrets are kept secret by taking advantage of the protection GitHub Secrets offers. Configuration options which don't necessarily need to be kept secret are still managed through environment variables as part of being [a 12 factor app](https://12factor.net/config). + +### Adding a variable + +#### Application + +A variable will usually be required to be added into each environment through [a new repository secret](https://github.com/DFE-Digital/buy-for-your-school/settings/secrets/actions/new). For this we use prefixes in our environment variable names: + +``` +APP_ENV_PROD_ +APP_ENV_STAGING_ +APP_ENV_RESEARCH_ +APP_ENV_PREVIEW_ +``` + +For instance if the app needed to access `ENV["RAILS_ENV"]` in production we would add this as the key: + +``` +APP_ENV_PROD_RAILS_ENV +``` + + +**Important!** +To propagate these new environment variables to the live environments we must finally deploy them manually. [Find the most recent passing deploy](https://github.com/DFE-Digital/buy-for-your-school/actions/workflows/deploy.yml) for the environment you want to provision the new variable on, view it and select "re-run jobs". + +### Infrastructure + +We can also make variables available for Terraform so that it can be configured. + +Here's an example of how `_TF_VAR` is used together with environment prefixes to pass `SYSLOG_DRAIN_URL` information. In this case it configures where logs should be forwarded to at the infrastructure level: + +``` +PREVIEW_TF_VAR_SYSLOG_DRAIN_URL +``` + +## Viewing a variable + +There is no way to view variables through GitHub Secrets once set. + +Though the GitHub secret API could be used, the easiest way to get application variables is to [access the console for the environment you want](console-access.md) and ask Rails for it over the console. + +``` +$irb> ENV["RAILS_ENV"] +``` diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 97865e5c3..8615636fd 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -3,9 +3,12 @@ services: test: build: context: . + target: test args: RAILS_ENV: "test" - BUNDLE_EXTRA_GEM_GROUPS: "test" + cache_from: + - app_test:latest + image: app_test command: bundle exec rake ports: - "3000:3000" diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 841f123ec..48b9c5692 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -3,9 +3,9 @@ services: test: build: context: . + target: test args: RAILS_ENV: "test" - BUNDLE_EXTRA_GEM_GROUPS: "test" command: bundle exec rake ports: - "3000:3000" diff --git a/docker-compose.yml b/docker-compose.yml index 4e6d8ed59..d0ed5c53d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,9 +3,9 @@ services: web: build: context: . + target: web args: RAILS_ENV: "development" - BUNDLE_EXTRA_GEM_GROUPS: "development test" command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" ports: - "3000:3000" diff --git a/public/404.html b/public/404.html deleted file mode 100644 index 2be3af26f..000000000 --- a/public/404.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The page you were looking for doesn't exist (404) - - - - - - -
    -
    -

    The page you were looking for doesn't exist.

    -

    You may have mistyped the address or the page may have moved.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/public/422.html b/public/422.html deleted file mode 100644 index c08eac0d1..000000000 --- a/public/422.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The change you wanted was rejected (422) - - - - - - -
    -
    -

    The change you wanted was rejected.

    -

    Maybe you tried to change something you didn't have access to.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/public/500.html b/public/500.html deleted file mode 100644 index 78a030af2..000000000 --- a/public/500.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - We're sorry, but something went wrong (500) - - - - - - -
    -
    -

    We're sorry, but something went wrong.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/script/bootstrap b/script/bootstrap new file mode 100755 index 000000000..701c6d659 --- /dev/null +++ b/script/bootstrap @@ -0,0 +1,64 @@ +#!/bin/sh + +# script/bootstrap: Resolve all dependencies that the application requires to +# run. + +set -e + +cd "$(dirname "$0")/.." + +if [ -f .gitmodules ]; then + echo "==> Updating Git submodules..." + git submodule update --init +fi + +if [ -z "$CI" ]; then + if [ -f Brewfile ] && [ "$(uname -s)" = "Darwin" ]; then + if ! brew bundle check >/dev/null 2>&1; then + echo "==> Installing Homebrew dependencies..." + brew bundle install --verbose --no-lock + fi + fi + + if [ -f .ruby-version ]; then + eval "$(rbenv init -)" + + if [ -z "$(rbenv version-name 2>/dev/null)" ]; then + echo "==> Installing Ruby..." + rbenv install --skip-existing + rbenv rehash + fi + fi + + if [ -f .node-version ]; then + eval "$(nodenv init -)" + + if [ -z "$(nodenv version-name 2>/dev/null)" ]; then + echo "==> Installing Node..." + nodenv install --skip-existing + nodenv rehash + fi + fi +fi + +if ! command -v bundle >/dev/null 2>&1; then + echo "==> Installing Bundler..." + gem install bundler + + if [ -z "$CI" ]; then + rbenv rehash + fi +fi + +if ! bundle check >/dev/null 2>&1; then + echo "==> Installing Ruby dependencies..." + bundle config set path vendor/bundle + bundle install +fi + +# if [ -f package.json ]; then +# if ! yarn check --verify-tree >/dev/null 2>&1; then +# echo "==> Installing JS dependencies..." +# yarn install +# fi +# fi diff --git a/script/test b/script/test index dab93225a..f76732c34 100755 --- a/script/test +++ b/script/test @@ -12,6 +12,7 @@ if [ -n "$DEBUG" ]; then fi echo "==> Updating..." +script/update TEST_FILE=$1 @@ -31,5 +32,5 @@ else bundle exec rspec echo "==> Running Brakeman" - bundle exec brakeman + bundle exec brakeman -o /dev/stdout fi diff --git a/script/update b/script/update new file mode 100755 index 000000000..78beb598c --- /dev/null +++ b/script/update @@ -0,0 +1,16 @@ +#!/bin/sh + +# script/update: Update application to run for its current checkout. + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Bootstrapping..." +script/bootstrap + +echo "==> Running database migrations..." +bundle exec rails db:migrate + +echo "==> Running test database migrations..." +RAILS_ENV="test" bundle exec rails db:migrate diff --git a/spec/factories/journey.rb b/spec/factories/journey.rb index dbae336c7..4f0e6e6f4 100644 --- a/spec/factories/journey.rb +++ b/spec/factories/journey.rb @@ -2,7 +2,6 @@ factory :journey do category { "catering" } liquid_template { "Your answer was {{ answer_47EI2X2T5EDTpJX9WjRR9p }}" } - section_groups { [] } started { true } last_worked_on { Time.zone.now } diff --git a/spec/features/school_buying_professionals/edit_their_answers_spec.rb b/spec/features/school_buying_professionals/edit_their_answers_spec.rb index 8ca1b549d..903250ca8 100644 --- a/spec/features/school_buying_professionals/edit_their_answers_spec.rb +++ b/spec/features/school_buying_professionals/edit_their_answers_spec.rb @@ -6,21 +6,7 @@ before do journey = answer.step.journey - journey.update( - user: user, - section_groups: [ - { - "order" => 0, - "title" => "Section A", - "steps" => [ - { - "contentful_id" => answer.step.contentful_id, - "order" => 0 - } - ] - } - ] - ) + journey.update(user: user) end let(:answer) { create(:short_text_answer, response: "answer") } diff --git a/spec/features/school_buying_professionals/see_the_map_of_journey_steps_page_spec.rb b/spec/features/school_buying_professionals/see_the_map_of_journey_steps_page_spec.rb index 4365369b6..9798c88ba 100644 --- a/spec/features/school_buying_professionals/see_the_map_of_journey_steps_page_spec.rb +++ b/spec/features/school_buying_professionals/see_the_map_of_journey_steps_page_spec.rb @@ -95,8 +95,7 @@ it "returns an error message" do stub_contentful_category( - fixture_filename: "journey-with-repeat-entries.json", - stub_steps: false + fixture_filename: "journey-with-repeat-entries.json" ) visit new_journey_map_path diff --git a/spec/features/school_buying_professionals/view_a_list_of_tasks_spec.rb b/spec/features/school_buying_professionals/view_a_list_of_tasks_spec.rb index 69547d9db..5811d3e85 100644 --- a/spec/features/school_buying_professionals/view_a_list_of_tasks_spec.rb +++ b/spec/features/school_buying_professionals/view_a_list_of_tasks_spec.rb @@ -23,11 +23,61 @@ end end - scenario "user can navigate back to the dashboard" do + scenario "user can navigate back to the task list from a task view" do + start_journey_with_tasks_from_category(category: "section-with-multiple-tasks.json") + + within(".app-task-list") do + click_on "Task with multiple steps" + end + + click_on(I18n.t("generic.button.back")) + + expect(page).to have_content(I18n.t("specifying.start_page.page_title")) + end + + scenario "user can navigate back to the list of journeys from a task list" do start_journey_from_category(category: "extended-radio-question.json") click_on(I18n.t("generic.button.back")) + expect(page).to have_content(I18n.t("journey.index.existing.header")) + end + + context "When a task has one question" do + scenario "user can navigate back to the task list from a question" do + start_journey_with_tasks_from_category(category: "section-with-single-task.json") + + within(".app-task-list") do + click_on "Everyday services that are required and need to be considered" + end + + click_on(I18n.t("generic.button.back")) + + expect(page).to have_content(I18n.t("specifying.start_page.page_title")) + end + end + + context "When a task has more than one question" do + scenario "user can navigate back to the task view from a question" do + start_journey_with_tasks_from_category(category: "section-with-multiple-tasks.json") + + within(".app-task-list") do + click_on "Task with multiple steps" + end + + click_on "Everyday services that are required and need to be considered" + click_on(I18n.t("generic.button.back")) + + expect(page).to have_content("Task with multiple steps") + end + end + + scenario "user can navigate back to the dashboard from a question" do + start_journey_from_category(category: "extended-radio-question.json") + + click_on(I18n.t("generic.button.back")) + click_on(I18n.t("generic.button.back")) + expect(page).to have_content(I18n.t("dashboard.header")) end @@ -36,25 +86,12 @@ stub_contentful_category(fixture_filename: "multiple-sections.json") answer = create(:short_text_answer, response: "answer") - answer.step.journey.update( - user: user, - section_groups: [ - { - "order" => 0, - "title" => "Section A", - "steps" => [ - { - "contentful_id" => answer.step.contentful_id, - "order" => 0 - } - ] - } - ] - ) + journey = answer.step.journey + journey.update(user: user) user_starts_the_journey - visit journey_path(answer.step.journey) + visit journey_path(journey) expect(page).to have_content(I18n.t("task_list.status.completed")) end diff --git a/spec/features/school_buying_professionals/view_existing_journeys_spec.rb b/spec/features/school_buying_professionals/view_existing_journeys_spec.rb index 268d77f6b..d5d4b1c93 100644 --- a/spec/features/school_buying_professionals/view_existing_journeys_spec.rb +++ b/spec/features/school_buying_professionals/view_existing_journeys_spec.rb @@ -36,7 +36,6 @@ start_journey_from_category(category: "radio-question.json") click_on(I18n.t("generic.button.back")) - click_on(I18n.t("dashboard.existing.link")) expect(page).to have_content("15 February 2021") expect(page).not_to have_content("20 March 2021") diff --git a/spec/fixtures/contentful/sections/checkboxes-section.json b/spec/fixtures/contentful/sections/checkboxes-section.json index e2668ad17..2f51ce50a 100644 --- a/spec/fixtures/contentful/sections/checkboxes-section.json +++ b/spec/fixtures/contentful/sections/checkboxes-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "checkboxes-question" + "id": "checkboxes-task" } } ] diff --git a/spec/fixtures/contentful/sections/currency-section.json b/spec/fixtures/contentful/sections/currency-section.json index e73ea86f0..8f32bf589 100644 --- a/spec/fixtures/contentful/sections/currency-section.json +++ b/spec/fixtures/contentful/sections/currency-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "currency-question" + "id": "currency-task" } } ] diff --git a/spec/fixtures/contentful/sections/extended-checkboxes-section.json b/spec/fixtures/contentful/sections/extended-checkboxes-section.json index d3f612812..dd2a27e7a 100644 --- a/spec/fixtures/contentful/sections/extended-checkboxes-section.json +++ b/spec/fixtures/contentful/sections/extended-checkboxes-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "extended-checkboxes-question" + "id": "extended-checkboxes-task" } } ] diff --git a/spec/fixtures/contentful/sections/extended-long-answer-checkboxes-section.json b/spec/fixtures/contentful/sections/extended-long-answer-checkboxes-section.json index e364a8487..02f8368c2 100644 --- a/spec/fixtures/contentful/sections/extended-long-answer-checkboxes-section.json +++ b/spec/fixtures/contentful/sections/extended-long-answer-checkboxes-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "extended-long-answer-checkboxes-question" + "id": "extended-long-answer-checkboxes-task" } } ] diff --git a/spec/fixtures/contentful/sections/extended-long-answer-radio-section.json b/spec/fixtures/contentful/sections/extended-long-answer-radio-section.json index 08f4d4555..79747c27c 100644 --- a/spec/fixtures/contentful/sections/extended-long-answer-radio-section.json +++ b/spec/fixtures/contentful/sections/extended-long-answer-radio-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "extended-long-answer-radio-question" + "id": "extended-long-answer-radio-task" } } ] diff --git a/spec/fixtures/contentful/sections/extended-radio-section.json b/spec/fixtures/contentful/sections/extended-radio-section.json index 0547e9fe3..94e4a27f7 100644 --- a/spec/fixtures/contentful/sections/extended-radio-section.json +++ b/spec/fixtures/contentful/sections/extended-radio-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "extended-radio-question" + "id": "extended-radio-task" } } ] diff --git a/spec/fixtures/contentful/sections/hidden-section.json b/spec/fixtures/contentful/sections/hidden-section.json index e6776e1b4..e781915c4 100644 --- a/spec/fixtures/contentful/sections/hidden-section.json +++ b/spec/fixtures/contentful/sections/hidden-section.json @@ -30,19 +30,19 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "hidden-field" + "id": "hidden-field-task" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "shown-field" + "id": "shown-field-task" } } ] diff --git a/spec/fixtures/contentful/sections/hidden-task-section.json b/spec/fixtures/contentful/sections/hidden-task-section.json index d4add894d..2173f77a1 100644 --- a/spec/fixtures/contentful/sections/hidden-task-section.json +++ b/spec/fixtures/contentful/sections/hidden-task-section.json @@ -34,7 +34,7 @@ "sys": { "type": "Link", "linkType": "Entry", - "id": "hidden-task" + "id": "hidden-field-task" } }] } diff --git a/spec/fixtures/contentful/sections/journey-with-multiple-entries-section.json b/spec/fixtures/contentful/sections/journey-with-multiple-entries-section.json index 63a6a0612..dbc9b34fd 100644 --- a/spec/fixtures/contentful/sections/journey-with-multiple-entries-section.json +++ b/spec/fixtures/contentful/sections/journey-with-multiple-entries-section.json @@ -30,26 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "radio-question" - } - }, - { - "sys": { - "type": "Link", - "linkType": "Entry", - "id": "short-text-question" - } - }, - { - "sys": { - "type": "Link", - "linkType": "Entry", - "id": "long-text-question" + "id": "checkboxes-and-radio-task" } } ] diff --git a/spec/fixtures/contentful/sections/journey-with-repeat-entries-section.json b/spec/fixtures/contentful/sections/journey-with-repeat-entries-section.json index 14b9ea73e..778179658 100644 --- a/spec/fixtures/contentful/sections/journey-with-repeat-entries-section.json +++ b/spec/fixtures/contentful/sections/journey-with-repeat-entries-section.json @@ -30,20 +30,13 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { - "sys": { - "type": "Link", - "linkType": "Entry", - "id": "radio-question" - } - }, - { - "sys": { - "type": "Link", - "linkType": "Entry", - "id": "radio-question" - } + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "repeat-entries-task" + } } ] } diff --git a/spec/fixtures/contentful/sections/long-text-section.json b/spec/fixtures/contentful/sections/long-text-section.json index 3ad55bccd..07fdba9fc 100644 --- a/spec/fixtures/contentful/sections/long-text-section.json +++ b/spec/fixtures/contentful/sections/long-text-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section B", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "long-text-question" + "id": "long-text-task" } } ] diff --git a/spec/fixtures/contentful/sections/markdown-help-text-section.json b/spec/fixtures/contentful/sections/markdown-help-text-section.json index 77e5cbf13..16c0dfcf5 100644 --- a/spec/fixtures/contentful/sections/markdown-help-text-section.json +++ b/spec/fixtures/contentful/sections/markdown-help-text-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "markdown-help-text" + "id": "markdown-help-text-task" } } ] diff --git a/spec/fixtures/contentful/sections/multiple-long-text-section.json b/spec/fixtures/contentful/sections/multiple-long-text-section.json index 49ddfd5b6..1d198a6a8 100644 --- a/spec/fixtures/contentful/sections/multiple-long-text-section.json +++ b/spec/fixtures/contentful/sections/multiple-long-text-section.json @@ -30,19 +30,19 @@ }, "fields": { "title": "Section B", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "long-text-question" + "id": "long-text-task" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "short-text-question" + "id": "short-text-task" } } ] diff --git a/spec/fixtures/contentful/sections/multiple-radio-section.json b/spec/fixtures/contentful/sections/multiple-radio-section.json index ecad2304a..986415c9b 100644 --- a/spec/fixtures/contentful/sections/multiple-radio-section.json +++ b/spec/fixtures/contentful/sections/multiple-radio-section.json @@ -30,19 +30,19 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "radio-question" + "id": "radio-task" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "single-date-question" + "id": "single-date-task" } } ] diff --git a/spec/fixtures/contentful/sections/multiple-tasks-section.json b/spec/fixtures/contentful/sections/multiple-tasks-section.json index b37814c63..a706ca66f 100644 --- a/spec/fixtures/contentful/sections/multiple-tasks-section.json +++ b/spec/fixtures/contentful/sections/multiple-tasks-section.json @@ -37,13 +37,14 @@ "linkType": "Entry", "id": "checkboxes-task" } - }, - { - "sys": { - "type": "Link", - "linkType": "Entry", - "id": "checkboxes-and-radio-task" + }, + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "checkboxes-and-radio-task" + } } - }] + ] } } diff --git a/spec/fixtures/contentful/sections/multiple-visible-and-hidden-tasks-section.json b/spec/fixtures/contentful/sections/multiple-visible-and-hidden-tasks-section.json index 3e57a581e..829535eab 100644 --- a/spec/fixtures/contentful/sections/multiple-visible-and-hidden-tasks-section.json +++ b/spec/fixtures/contentful/sections/multiple-visible-and-hidden-tasks-section.json @@ -7,7 +7,7 @@ "id": "rwl7tyzv9sys" } }, - "id": "tasks-section", + "id": "multiple-visible-and-hidden-tasks-section", "type": "Entry", "createdAt": "2021-04-12T10:41:36.073Z", "updatedAt": "2021-04-12T10:42:51.119Z", diff --git a/spec/fixtures/contentful/sections/nil-help-text-radios-section.json b/spec/fixtures/contentful/sections/nil-help-text-radios-section.json index 6ed3d42eb..81d146558 100644 --- a/spec/fixtures/contentful/sections/nil-help-text-radios-section.json +++ b/spec/fixtures/contentful/sections/nil-help-text-radios-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "nil-help-text-radios" + "id": "nil-help-text-radios-task" } } ] diff --git a/spec/fixtures/contentful/sections/nil-help-text-short-text-section.json b/spec/fixtures/contentful/sections/nil-help-text-short-text-section.json index a2b62bb78..ae4d9d488 100644 --- a/spec/fixtures/contentful/sections/nil-help-text-short-text-section.json +++ b/spec/fixtures/contentful/sections/nil-help-text-short-text-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "nil-help-text-short-text" + "id": "nil-help-text-short-text-task" } } ] diff --git a/spec/fixtures/contentful/sections/number-section.json b/spec/fixtures/contentful/sections/number-section.json index ddba3ea4a..ae825bb1b 100644 --- a/spec/fixtures/contentful/sections/number-section.json +++ b/spec/fixtures/contentful/sections/number-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "number-question" + "id": "number-task" } } ] diff --git a/spec/fixtures/contentful/sections/radio-section.json b/spec/fixtures/contentful/sections/radio-section.json index 027cbf571..2444c5ea6 100644 --- a/spec/fixtures/contentful/sections/radio-section.json +++ b/spec/fixtures/contentful/sections/radio-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "radio-question" + "id": "radio-task" } } ] diff --git a/spec/fixtures/contentful/sections/radio-with-separator-section.json b/spec/fixtures/contentful/sections/radio-with-separator-section.json index 216461309..771c78109 100644 --- a/spec/fixtures/contentful/sections/radio-with-separator-section.json +++ b/spec/fixtures/contentful/sections/radio-with-separator-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "radio-with-separator-question" + "id": "radio-with-separator-task" } } ] diff --git a/spec/fixtures/contentful/sections/short-text-section.json b/spec/fixtures/contentful/sections/short-text-section.json index a64641194..5b1cb1b50 100644 --- a/spec/fixtures/contentful/sections/short-text-section.json +++ b/spec/fixtures/contentful/sections/short-text-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "short-text-question" + "id": "short-text-task" } } ] diff --git a/spec/fixtures/contentful/sections/show-one-additional-question-section.json b/spec/fixtures/contentful/sections/show-one-additional-question-section.json index e907521ad..7e8686723 100644 --- a/spec/fixtures/contentful/sections/show-one-additional-question-section.json +++ b/spec/fixtures/contentful/sections/show-one-additional-question-section.json @@ -30,26 +30,26 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "show-one-additional-question" + "id": "show-one-additional-question-task" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "hidden-field-that-shows-an-additional-question" + "id": "hidden-field-that-shows-an-additional-question-task" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "hidden-field" + "id": "hidden-field-task" } } ] diff --git a/spec/fixtures/contentful/sections/single-date-section.json b/spec/fixtures/contentful/sections/single-date-section.json index 8cd9ef8a9..aeef77d95 100644 --- a/spec/fixtures/contentful/sections/single-date-section.json +++ b/spec/fixtures/contentful/sections/single-date-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "single-date-question" + "id": "single-date-task" } } ] diff --git a/spec/fixtures/contentful/sections/skippable-checkboxes-section.json b/spec/fixtures/contentful/sections/skippable-checkboxes-section.json index 27cb9fb5f..c8a8c5b43 100644 --- a/spec/fixtures/contentful/sections/skippable-checkboxes-section.json +++ b/spec/fixtures/contentful/sections/skippable-checkboxes-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "skippable-checkboxes-question" + "id": "skippable-checkboxes-task" } } ] diff --git a/spec/fixtures/contentful/sections/static-content-section.json b/spec/fixtures/contentful/sections/static-content-section.json index c340bc3f7..d7038382e 100644 --- a/spec/fixtures/contentful/sections/static-content-section.json +++ b/spec/fixtures/contentful/sections/static-content-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "static-content" + "id": "static-content-task" } } ] diff --git a/spec/fixtures/contentful/sections/unexpected-contentful-question-type-section.json b/spec/fixtures/contentful/sections/unexpected-contentful-question-type-section.json index bdec0acc9..6325eea49 100644 --- a/spec/fixtures/contentful/sections/unexpected-contentful-question-type-section.json +++ b/spec/fixtures/contentful/sections/unexpected-contentful-question-type-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "unexpected-contentful-question-type" + "id": "unexpected-contentful-question-type-task" } } ] diff --git a/spec/fixtures/contentful/sections/unexpected-contentful-type-section.json b/spec/fixtures/contentful/sections/unexpected-contentful-type-section.json index 9cd4e6b19..fd8c77f0f 100644 --- a/spec/fixtures/contentful/sections/unexpected-contentful-type-section.json +++ b/spec/fixtures/contentful/sections/unexpected-contentful-type-section.json @@ -30,12 +30,12 @@ }, "fields": { "title": "Section A", - "steps": [ + "tasks": [ { "sys": { "type": "Link", "linkType": "Entry", - "id": "unexpected-contentful-type" + "id": "unexpected-contentful-type-task" } } ] diff --git a/spec/fixtures/contentful/tasks/checkboxes-and-radio-task.json b/spec/fixtures/contentful/tasks/checkboxes-and-radio-task.json index aa70ce43b..9763c749c 100644 --- a/spec/fixtures/contentful/tasks/checkboxes-and-radio-task.json +++ b/spec/fixtures/contentful/tasks/checkboxes-and-radio-task.json @@ -10,7 +10,7 @@ "id": "rwl7tyzv9sys" } }, - "id": "checkboxes-task", + "id": "checkboxes-and-radio-task", "type": "Entry", "createdAt": "2021-04-12T09:38:48.990Z", "updatedAt": "2021-04-12T10:11:05.670Z", @@ -38,14 +38,28 @@ "sys": { "type": "Link", "linkType": "Entry", - "id": "checkboxes-question" + "id": "radio-question" } }, { "sys": { "type": "Link", "linkType": "Entry", - "id": "radio-question" + "id": "short-text-question" + } + }, + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "long-text-question" + } + }, + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "checkboxes-question" } } ] diff --git a/spec/fixtures/contentful/tasks/currency-task.json b/spec/fixtures/contentful/tasks/currency-task.json new file mode 100644 index 000000000..da471eea9 --- /dev/null +++ b/spec/fixtures/contentful/tasks/currency-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "currency-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Currency task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "currency-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/extended-checkboxes-task.json b/spec/fixtures/contentful/tasks/extended-checkboxes-task.json new file mode 100644 index 000000000..bf596836a --- /dev/null +++ b/spec/fixtures/contentful/tasks/extended-checkboxes-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "extended-checkboxes-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Extended checkboxes task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "extended-checkboxes-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/extended-long-answer-checkboxes-task.json b/spec/fixtures/contentful/tasks/extended-long-answer-checkboxes-task.json new file mode 100644 index 000000000..a797c806b --- /dev/null +++ b/spec/fixtures/contentful/tasks/extended-long-answer-checkboxes-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "extended-long-answer-checkboxes-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Extended long answer checkboxes task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "extended-long-answer-checkboxes-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/extended-long-answer-radio-task.json b/spec/fixtures/contentful/tasks/extended-long-answer-radio-task.json new file mode 100644 index 000000000..09033fe0e --- /dev/null +++ b/spec/fixtures/contentful/tasks/extended-long-answer-radio-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "extended-long-answer-radio-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Extended long answer radio task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "extended-long-answer-radio-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/extended-radio-task.json b/spec/fixtures/contentful/tasks/extended-radio-task.json new file mode 100644 index 000000000..dafdd5439 --- /dev/null +++ b/spec/fixtures/contentful/tasks/extended-radio-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "extended-radio-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Extended radio task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "extended-radio-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/hidden-task.json b/spec/fixtures/contentful/tasks/hidden-field-task.json similarity index 96% rename from spec/fixtures/contentful/tasks/hidden-task.json rename to spec/fixtures/contentful/tasks/hidden-field-task.json index f9ae5bdf0..4139cf076 100644 --- a/spec/fixtures/contentful/tasks/hidden-task.json +++ b/spec/fixtures/contentful/tasks/hidden-field-task.json @@ -10,7 +10,7 @@ "id": "rwl7tyzv9sys" } }, - "id": "checkboxes-task", + "id": "hidden-field-task", "type": "Entry", "createdAt": "2021-04-12T09:38:48.990Z", "updatedAt": "2021-04-12T10:11:05.670Z", diff --git a/spec/fixtures/contentful/tasks/hidden-field-that-shows-an-additional-question-task.json b/spec/fixtures/contentful/tasks/hidden-field-that-shows-an-additional-question-task.json new file mode 100644 index 000000000..7a6fe1866 --- /dev/null +++ b/spec/fixtures/contentful/tasks/hidden-field-that-shows-an-additional-question-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "hidden-field-that-shows-an-additional-question-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Hidden field with additional question task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "hidden-field-that-shows-an-additional-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/long-text-task.json b/spec/fixtures/contentful/tasks/long-text-task.json new file mode 100644 index 000000000..7389f47be --- /dev/null +++ b/spec/fixtures/contentful/tasks/long-text-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "long-text-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Long text task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "long-text-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/markdown-help-text-task.json b/spec/fixtures/contentful/tasks/markdown-help-text-task.json new file mode 100644 index 000000000..1d28a5b1e --- /dev/null +++ b/spec/fixtures/contentful/tasks/markdown-help-text-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "markdown-help-text-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Markdown help text task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "markdown-help-text" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/nil-help-text-radios-task.json b/spec/fixtures/contentful/tasks/nil-help-text-radios-task.json new file mode 100644 index 000000000..cae965456 --- /dev/null +++ b/spec/fixtures/contentful/tasks/nil-help-text-radios-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "nil-help-text-radios-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Nil help text radios task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "nil-help-text-radios" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/nil-help-text-short-text-task.json b/spec/fixtures/contentful/tasks/nil-help-text-short-text-task.json new file mode 100644 index 000000000..94178b457 --- /dev/null +++ b/spec/fixtures/contentful/tasks/nil-help-text-short-text-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "nil-help-text-short-text-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Nil help text short text task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "nil-help-text-short-text" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/no-hidden-field-task.json b/spec/fixtures/contentful/tasks/no-hidden-field-task.json new file mode 100644 index 000000000..dfb28f613 --- /dev/null +++ b/spec/fixtures/contentful/tasks/no-hidden-field-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "no-hidden-field-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "No hidden field task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "no-hidden-field" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/no-primary-button-task.json b/spec/fixtures/contentful/tasks/no-primary-button-task.json new file mode 100644 index 000000000..1147bb150 --- /dev/null +++ b/spec/fixtures/contentful/tasks/no-primary-button-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "no-primary-button-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "No primary button task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "no-primary-button" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/number-task.json b/spec/fixtures/contentful/tasks/number-task.json new file mode 100644 index 000000000..2c19595f0 --- /dev/null +++ b/spec/fixtures/contentful/tasks/number-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "number-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Number task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "number-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/primary-button-task.json b/spec/fixtures/contentful/tasks/primary-button-task.json new file mode 100644 index 000000000..b8221d7b7 --- /dev/null +++ b/spec/fixtures/contentful/tasks/primary-button-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "primary-button-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Primary button task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "primary-button" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/radio-and-hidden-task.json b/spec/fixtures/contentful/tasks/radio-and-hidden-task.json index 6442b9777..c361bd891 100644 --- a/spec/fixtures/contentful/tasks/radio-and-hidden-task.json +++ b/spec/fixtures/contentful/tasks/radio-and-hidden-task.json @@ -10,7 +10,7 @@ "id": "rwl7tyzv9sys" } }, - "id": "checkboxes-task", + "id": "radio-and-hidden-task", "type": "Entry", "createdAt": "2021-04-12T09:38:48.990Z", "updatedAt": "2021-04-12T10:11:05.670Z", diff --git a/spec/fixtures/contentful/tasks/radio-task.json b/spec/fixtures/contentful/tasks/radio-task.json new file mode 100644 index 000000000..c0045e199 --- /dev/null +++ b/spec/fixtures/contentful/tasks/radio-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "radio-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Radio task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "radio-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/radio-with-separator-task.json b/spec/fixtures/contentful/tasks/radio-with-separator-task.json new file mode 100644 index 000000000..e4b48a2d8 --- /dev/null +++ b/spec/fixtures/contentful/tasks/radio-with-separator-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "radio-with-separator-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Radio with separator task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "radio-with-separator-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/repeat-entries-task.json b/spec/fixtures/contentful/tasks/repeat-entries-task.json new file mode 100644 index 000000000..fee2b0e27 --- /dev/null +++ b/spec/fixtures/contentful/tasks/repeat-entries-task.json @@ -0,0 +1,60 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "repeat-entries-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Repeat entries task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "radio-question" + } + }, + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "radio-question" + } + }, + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "radio-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/short-text-task.json b/spec/fixtures/contentful/tasks/short-text-task.json new file mode 100644 index 000000000..903aa24bb --- /dev/null +++ b/spec/fixtures/contentful/tasks/short-text-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "short-text-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Short text task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "short-text-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/show-multiple-additional-questions-task.json b/spec/fixtures/contentful/tasks/show-multiple-additional-questions-task.json new file mode 100644 index 000000000..c485cf5b1 --- /dev/null +++ b/spec/fixtures/contentful/tasks/show-multiple-additional-questions-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "show-multiple-additional-questions-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Multiple additional questions task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "show-multiple-additional-questions" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/show-one-additional-question-task.json b/spec/fixtures/contentful/tasks/show-one-additional-question-task.json new file mode 100644 index 000000000..4ec5a100d --- /dev/null +++ b/spec/fixtures/contentful/tasks/show-one-additional-question-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "show-one-additional-question-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "One additional question task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "show-one-additional-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/shown-field-task.json b/spec/fixtures/contentful/tasks/shown-field-task.json new file mode 100644 index 000000000..d93983515 --- /dev/null +++ b/spec/fixtures/contentful/tasks/shown-field-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "shown-field-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Shown field task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "shown-field" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/single-date-task.json b/spec/fixtures/contentful/tasks/single-date-task.json new file mode 100644 index 000000000..b39c8e83c --- /dev/null +++ b/spec/fixtures/contentful/tasks/single-date-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "single-date-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Single date task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "single-date-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/skippable-checkboxes-task.json b/spec/fixtures/contentful/tasks/skippable-checkboxes-task.json new file mode 100644 index 000000000..43d1fce5d --- /dev/null +++ b/spec/fixtures/contentful/tasks/skippable-checkboxes-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "skippable-checkboxes-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Skippable checkboxes task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "skippable-checkboxes-question" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/static-content-task.json b/spec/fixtures/contentful/tasks/static-content-task.json new file mode 100644 index 000000000..77372f200 --- /dev/null +++ b/spec/fixtures/contentful/tasks/static-content-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "static-content-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Static content task", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "static-content" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/unexpected-contentful-question-type-task.json b/spec/fixtures/contentful/tasks/unexpected-contentful-question-type-task.json new file mode 100644 index 000000000..a3dea22a4 --- /dev/null +++ b/spec/fixtures/contentful/tasks/unexpected-contentful-question-type-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "unexpected-contentful-question-type-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Unexpected Contentful question type", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "unexpected-contentful-question-type" + } + } + ] + } +} diff --git a/spec/fixtures/contentful/tasks/unexpected-contentful-type-task.json b/spec/fixtures/contentful/tasks/unexpected-contentful-type-task.json new file mode 100644 index 000000000..635665878 --- /dev/null +++ b/spec/fixtures/contentful/tasks/unexpected-contentful-type-task.json @@ -0,0 +1,46 @@ +{ + "metadata": { + "tags": [] + }, + "sys": { + "space": { + "sys": { + "type": "Link", + "linkType": "Space", + "id": "rwl7tyzv9sys" + } + }, + "id": "unexpected-contentful-type-task", + "type": "Entry", + "createdAt": "2021-04-12T09:38:48.990Z", + "updatedAt": "2021-04-12T10:11:05.670Z", + "environment": { + "sys": { + "id": "develop", + "type": "Link", + "linkType": "Environment" + } + }, + "revision": 3, + "contentType": { + "sys": { + "type": "Link", + "linkType": "ContentType", + "id": "task" + } + }, + "locale": "en-US" + }, + "fields": { + "title": "Unexpected Contentful type", + "steps": [ + { + "sys": { + "type": "Link", + "linkType": "Entry", + "id": "unexpected-contentful-type" + } + } + ] + } +} diff --git a/spec/helpers/journey_helper_spec.rb b/spec/helpers/journey_helper_spec.rb deleted file mode 100644 index 27ab24c60..000000000 --- a/spec/helpers/journey_helper_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -require "rails_helper" - -RSpec.describe JourneyHelper, type: :helper do - describe "#section_group_with_steps" do - it "returns an ordered array of steps" do - journey = create(:journey, section_groups: "") - section = create(:section, journey: journey) - task = create(:task, section: section) - step_1 = StepPresenter.new(create(:step, :radio, task: task)) - step_2 = StepPresenter.new(create(:step, :long_text, task: task)) - step_3 = StepPresenter.new(create(:step, :short_text, task: task)) - - section_groups = [ - { - "order" => 0, - "title" => "Objectives", - "steps" => [ - { - "contentful_id" => step_1.contentful_id, - "order" => 0 - }, - { - "contentful_id" => step_2.contentful_id, - "order" => 1 - } - ] - }, - { - "order" => 1, - "title" => "Social value", - "steps" => [ - { - "contentful_id" => step_3.contentful_id, - "order" => 0 - } - ] - } - ] - journey.update(section_groups: section_groups) - - result = helper.section_group_with_steps( - journey: journey, steps: [step_1, step_2, step_3] - ) - - expect(result.first["steps"]).to eq([step_1, step_2]) - expect(result.second["steps"]).to eq([step_3]) - end - end - - context "when the ordering we want does not match the order saved in the database" do - it "the ordering defined by 'order' is preserved" do - journey = create(:journey, section_groups: "") - section = create(:section, journey: journey) - task = create(:task, section: section) - step_1 = StepPresenter.new(create(:step, :radio, title: "First question", task: task)) - step_2 = StepPresenter.new(create(:step, :long_text, title: "Second question", task: task)) - step_3 = StepPresenter.new(create(:step, :short_text, title: "Third question", task: task)) - step_4 = StepPresenter.new(create(:step, :short_text, title: "Fourth question", task: task)) - - section_groups = [ - { - "order" => 1, - "title" => "Objectives", - "steps" => [ - { - "contentful_id" => step_2.contentful_id, - "order" => 2 - }, - { - "contentful_id" => step_1.contentful_id, - "order" => 1 - }, - { - "contentful_id" => step_3.contentful_id, - "order" => 3 - } - ] - }, - { - "order" => 0, - "title" => "Social value", - "steps" => [ - { - "contentful_id" => step_4.contentful_id, - "order" => 0 - } - ] - } - ] - journey.update(section_groups: section_groups) - - result = helper.section_group_with_steps( - journey: journey, steps: [step_1, step_2, step_3, step_4] - ) - - expect(result.first["steps"]).to eq([step_1, step_2, step_3]) - expect(result.second["steps"]).to eq([step_4]) - end - end -end diff --git a/spec/jobs/warm_entry_cache_job_spec.rb b/spec/jobs/warm_entry_cache_job_spec.rb index 2fd8dd8fc..38751c156 100644 --- a/spec/jobs/warm_entry_cache_job_spec.rb +++ b/spec/jobs/warm_entry_cache_job_spec.rb @@ -44,8 +44,7 @@ context "when the journey order cannot be built" do it "does not add new items to the cache" do stub_contentful_category( - fixture_filename: "journey-with-multiple-entries.json", - stub_steps: false + fixture_filename: "journey-with-multiple-entries.json" ) allow_any_instance_of(GetStepsFromSection) @@ -62,8 +61,7 @@ RedisCache.redis.set("contentful:entry:contentful-starting-step", "\"{\\}\"") stub_contentful_category( - fixture_filename: "journey-with-multiple-entries.json", - stub_steps: false + fixture_filename: "journey-with-multiple-entries.json" ) allow_any_instance_of(GetStepsFromSection) diff --git a/spec/models/task_spec.rb b/spec/models/task_spec.rb index 641e4fcf9..529c347f9 100644 --- a/spec/models/task_spec.rb +++ b/spec/models/task_spec.rb @@ -19,6 +19,16 @@ end end + describe "#visible_steps_count" do + it "returns the number of visible steps" do + task = create(:task) + create(:step, :radio, hidden: false, task: task) + create(:step, :radio, hidden: true, task: task) + + expect(task.visible_steps_count).to eq 1 + end + end + describe "#has_single_visible_step?" do context "when the task has one visible step" do it "returns true" do @@ -40,4 +50,54 @@ end end end + + describe "#answered_questions_count" do + it "returns the number of visible questions which have been answered" do + task = create(:task) + step_1 = create(:step, :radio, hidden: false, task: task) + create(:radio_answer, step: step_1) + step_2 = create(:step, :radio, hidden: true, task: task) + create(:radio_answer, step: step_2) + create(:step, :radio, hidden: false, task: task) + create(:step, :radio, hidden: true, task: task) + + expect(task.answered_questions_count).to eq 1 + end + end + + describe "#status" do + it "returns NOT_STARTED when no visible questions have been answered" do + task = create(:task) + create(:step, :radio, hidden: false, task: task) + step_2 = create(:step, :radio, hidden: true, task: task) + create(:radio_answer, step: step_2) + + expect(task.status).to eq Task::NOT_STARTED + end + + it "returns IN_PROGRESS when some but not all visible questions have been answered" do + task = create(:task) + step_1 = create(:step, :radio, hidden: false, task: task) + create(:radio_answer, step: step_1) + step_2 = create(:step, :radio, hidden: true, task: task) + create(:radio_answer, step: step_2) + create(:step, :radio, hidden: true, task: task) + create(:step, :radio, hidden: false, task: task) + + expect(task.status).to eq Task::IN_PROGRESS + end + + it "returns COMPLETED when all visible questions have been answered" do + task = create(:task) + step_1 = create(:step, :radio, hidden: false, task: task) + create(:radio_answer, step: step_1) + step_2 = create(:step, :radio, hidden: true, task: task) + create(:radio_answer, step: step_2) + step_3 = create(:step, :radio, hidden: false, task: task) + create(:radio_answer, step: step_3) + create(:step, :radio, hidden: true, task: task) + + expect(task.status).to eq Task::COMPLETED + end + end end diff --git a/spec/requests/authentication_spec.rb b/spec/requests/authentication_spec.rb index 500ecc255..2afc02996 100644 --- a/spec/requests/authentication_spec.rb +++ b/spec/requests/authentication_spec.rb @@ -1,6 +1,11 @@ require "rails_helper" RSpec.describe "Authentication", type: :request do + after(:each) do + RedisSessions.redis.flushdb + RedisSessionLookup.redis.flushdb + end + describe "Endpoints that don't require authentication" do it "the health_check endpoint is not authenticated" do get health_check_path diff --git a/spec/requests/cache_invalidation_spec.rb b/spec/requests/cache_invalidation_spec.rb index 86607c668..953ea088a 100644 --- a/spec/requests/cache_invalidation_spec.rb +++ b/spec/requests/cache_invalidation_spec.rb @@ -34,6 +34,32 @@ expect(RedisCache.redis.get("contentful:entry:6zeSz4F4YtD66gT5SFpnSB")).to eq(nil) end + it "logs information of the event in Rollbar for debugging" do + fake_contentful_webook_payload = { + "entityId": "6zeSz4F4YtD66gT5SFpnSB", + "spaceId": "rwl7tyzv9sys", + "parameters": { + "text": "Entity version: 62" + } + } + + credentials = ActionController::HttpAuthentication::Token + .encode_credentials(ENV["CONTENTFUL_WEBHOOK_API_KEY"]) + headers = {"AUTHORIZATION" => credentials} + + expect(Rollbar).to receive(:info) + .with( + "Accepted request to cache bust Contentful Entry", + cache_key: "contentful:entry:6zeSz4F4YtD66gT5SFpnSB" + ).and_call_original + + post "/api/contentful/entry_updated", { + params: fake_contentful_webook_payload, + headers: headers, + as: :json + } + end + context "when no basic auth was provided" do it "does not delete anything from the cache and returns 401" do RedisCache.redis.set("contentful:entry:6zeSz4F4YtD66gT5SFpnSB", "a dummy value") diff --git a/spec/requests/contentful_caching_spec.rb b/spec/requests/contentful_caching_spec.rb index 5cfe49bae..ea58a48be 100644 --- a/spec/requests/contentful_caching_spec.rb +++ b/spec/requests/contentful_caching_spec.rb @@ -29,6 +29,9 @@ raw_section_response = File.read("#{Rails.root}/spec/fixtures/contentful/sections/radio-section.json") RedisCache.redis.set("contentful:entry:radio-section", JSON.dump(raw_section_response)) + raw_step_response = File.read("#{Rails.root}/spec/fixtures/contentful/tasks/radio-task.json") + RedisCache.redis.set("contentful:entry:radio-task", JSON.dump(raw_step_response)) + raw_step_response = File.read("#{Rails.root}/spec/fixtures/contentful/steps/radio-question.json") RedisCache.redis.set("contentful:entry:radio-question", JSON.dump(raw_step_response)) diff --git a/spec/requests/errors_spec.rb b/spec/requests/errors_spec.rb new file mode 100644 index 000000000..0bd0489ee --- /dev/null +++ b/spec/requests/errors_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +RSpec.describe "Errors", type: :request do + describe "internal_server_error" do + it "the 500 endpoint returns the expected status and error message" do + get "/500" + expect(response).to have_http_status(:internal_server_error) + expect(response.body).to include(I18n.t("errors.internal_server_error.page_title")) + expect(response.body).to include(I18n.t("errors.internal_server_error.page_body")) + end + end + + describe "not_found" do + it "the 404 endpoint returns the expected status and error message" do + get "/404" + expect(response).to have_http_status(:not_found) + expect(response.body).to include(I18n.t("errors.not_found.page_title")) + expect(response.body).to include(I18n.t("errors.not_found.page_body")) + end + end + + describe "unacceptable" do + it "the 422 endpoint returns the expected status and error message" do + get "/422" + expect(response).to have_http_status(:unprocessable_entity) + expect(response.body).to include(I18n.t("errors.unacceptable.page_title")) + expect(response.body).to include(I18n.t("errors.unacceptable.page_body")) + end + end +end diff --git a/spec/services/create_journey_spec.rb b/spec/services/create_journey_spec.rb index 74a7082e4..75992a123 100644 --- a/spec/services/create_journey_spec.rb +++ b/spec/services/create_journey_spec.rb @@ -12,8 +12,7 @@ describe "#call" do it "creates a new journey" do stub_contentful_category( - fixture_filename: "category-with-no-steps.json", - stub_steps: false + fixture_filename: "category-with-no-steps.json" ) expect { described_class.new(category_name: "catering", user: build(:user)).call } .to change { Journey.count }.by(1) @@ -22,8 +21,7 @@ it "associates the new journey with the given user" do stub_contentful_category( - fixture_filename: "category-with-no-steps.json", - stub_steps: false + fixture_filename: "category-with-no-steps.json" ) user = create(:user) @@ -34,8 +32,7 @@ it "sets started to true (until questions have been answered)" do stub_contentful_category( - fixture_filename: "category-with-no-steps.json", - stub_steps: false + fixture_filename: "category-with-no-steps.json" ) described_class.new(category_name: "catering", user: build(:user)).call expect(Journey.last.started).to eq(true) @@ -44,8 +41,7 @@ it "sets last_worked_on to now" do travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) stub_contentful_category( - fixture_filename: "category-with-no-steps.json", - stub_steps: false + fixture_filename: "category-with-no-steps.json" ) described_class.new(category_name: "catering", user: build(:user)).call @@ -64,41 +60,11 @@ .to eql("

    Liquid {{templating}}

    ") end - it "stores the section grouping on the journey in the expected order" do - stub_contentful_category( - fixture_filename: "multiple-sections-and-steps.json" - ) - - described_class.new(category_name: "catering", user: build(:user)).call - - journey = Journey.last - - expect(journey.reload.section_groups).to eq([ - { - "order" => 0, - "title" => "Section A", - "steps" => [ - {"contentful_id" => "radio-question", "order" => 0}, - {"contentful_id" => "single-date-question", "order" => 1} - ] - }, - { - "order" => 1, - "title" => "Section B", - "steps" => [ - {"contentful_id" => "long-text-question", "order" => 0}, - {"contentful_id" => "short-text-question", "order" => 1} - ] - } - ]) - end - context "when the journey cannot be saved" do it "raises an error" do stub_contentful_category( fixture_filename: "category-with-liquid-template.json", - stub_sections: true, - stub_steps: false + stub_sections: true ) # Force a validation error by not providing a category_name diff --git a/spec/services/get_sections_from_category_spec.rb b/spec/services/get_sections_from_category_spec.rb index 33c602b4d..a6ede93a4 100644 --- a/spec/services/get_sections_from_category_spec.rb +++ b/spec/services/get_sections_from_category_spec.rb @@ -4,8 +4,7 @@ describe "#call" do it "returns an array of sections" do category = stub_contentful_category( - fixture_filename: "radio-question.json", - stub_steps: false + fixture_filename: "radio-question.json" ) result = described_class.new(category: category).call diff --git a/spec/services/get_steps_from_section_spec.rb b/spec/services/get_steps_from_section_spec.rb index 557eb2a53..a0251317a 100644 --- a/spec/services/get_steps_from_section_spec.rb +++ b/spec/services/get_steps_from_section_spec.rb @@ -6,7 +6,6 @@ section = fake_contentful_section( contentful_fixture_filename: "sections/journey-with-multiple-entries-section.json" ) - stub_contentful_section_steps(sections: [section]) result = described_class.new(section: section).call diff --git a/spec/services/get_steps_from_task_spec.rb b/spec/services/get_steps_from_task_spec.rb index 6c60ddc21..8db1588b9 100644 --- a/spec/services/get_steps_from_task_spec.rb +++ b/spec/services/get_steps_from_task_spec.rb @@ -3,7 +3,7 @@ RSpec.describe GetStepsFromTask do describe "#call" do it "returns the list of entry objects referenced by the step list" do - task = fake_contentful_section( + task = fake_contentful_task( contentful_fixture_filename: "tasks/checkboxes-task.json" ) stub_contentful_task_steps(tasks: [task]) diff --git a/spec/support/contentful_helpers.rb b/spec/support/contentful_helpers.rb index e8ccd4bd3..a02b2c6ea 100644 --- a/spec/support/contentful_helpers.rb +++ b/spec/support/contentful_helpers.rb @@ -13,8 +13,7 @@ def stub_contentful_entry( def stub_contentful_category( fixture_filename:, stub_sections: true, - stub_steps: true, - stub_tasks: false, + stub_tasks: true, contentful_connector: instance_double(ContentfulConnector) # TODO: I suspect the double doesn't do anything and we need stub_contentful_connector ) category = fake_contentful_category(contentful_fixture_filename: fixture_filename) @@ -28,9 +27,6 @@ def stub_contentful_category( if stub_sections sections = stub_contentful_sections(category: category, contentful_connector: contentful_connector) - if stub_steps - stub_contentful_section_steps(sections: sections, contentful_connector: contentful_connector) - end if stub_tasks stub_contentful_section_tasks(sections: sections, contentful_connector: contentful_connector) end @@ -104,7 +100,7 @@ def stub_contentful_section_tasks( sections.each do |section| fake_tasks = section.tasks.map { |task| fake_task = fake_contentful_task(contentful_fixture_filename: "tasks/#{task.id}.json") - expect(contentful_connector).to receive(:get_entry_by_id) + allow(contentful_connector).to receive(:get_entry_by_id) .with(fake_task.id) .and_return(fake_task) fake_task @@ -179,17 +175,27 @@ def fake_contentful_category(contentful_fixture_filename:) def fake_contentful_section(contentful_fixture_filename:) raw_response = File.read("#{Rails.root}/spec/fixtures/contentful/#{contentful_fixture_filename}") hash_response = JSON.parse(raw_response) + contentful_connector = instance_double(ContentfulConnector) - steps = hash_response.dig("fields", "steps") tasks = hash_response.dig("fields", "tasks") - double( + section = double( Contentful::Entry, id: hash_response.dig("sys", "id"), title: hash_response.dig("fields", "title"), - steps: steps.nil? ? {} : steps.map { |step_hash| double(id: step_hash.dig("sys", "id")) }, - tasks: tasks.nil? ? {} : tasks.map { |task_hash| double(id: task_hash.dig("sys", "id")) } + tasks: tasks.map { |task_hash| double(id: task_hash.dig("sys", "id")) } ) + + fake_tasks = section.tasks.map { |task| + fake_task = fake_contentful_task(contentful_fixture_filename: "tasks/#{task.id}.json") + allow(contentful_connector).to receive(:get_entry_by_id) + .with(fake_task.id) + .and_return(fake_task) + fake_task + } + allow(section).to receive(:tasks).and_return(fake_tasks) + stub_contentful_task_steps(tasks: fake_tasks, contentful_connector: contentful_connector) + section end def fake_contentful_task(contentful_fixture_filename:) diff --git a/spec/support/journey_helpers.rb b/spec/support/journey_helpers.rb index b0d71d939..020c1c10b 100644 --- a/spec/support/journey_helpers.rb +++ b/spec/support/journey_helpers.rb @@ -17,7 +17,6 @@ def start_journey_with_tasks_from_category(category:) stub_contentful_category( fixture_filename: category, stub_sections: true, - stub_steps: false, stub_tasks: true )