From 8457a0dd0c894cbbb651d8fc7cfdf51fdee885a1 Mon Sep 17 00:00:00 2001 From: gangelo Date: Sat, 27 Jan 2024 07:41:59 -0500 Subject: [PATCH] dsu use subcommand --- lib/dsu/models/project.rb | 37 +-- .../presenters/project/delete_presenter.rb | 14 +- .../project/use_by_number_presenter.rb | 49 ++++ lib/dsu/presenters/project/use_presenter.rb | 67 +----- lib/dsu/subcommands/project.rb | 30 ++- lib/dsu/views/project/delete.rb | 4 +- lib/dsu/views/project/use.rb | 16 +- lib/dsu/views/project/use_by_number.rb | 23 ++ lib/locales/en/subcommands.yml | 9 +- spec/dsu/views/project/delete_spec.rb | 216 ++++++++++++++++++ spec/dsu/views/project/use_by_number_spec.rb | 190 +++++++++++++++ spec/dsu/views/project/use_spec.rb | 92 +------- spec/factories/use_by_number_presenters.rb | 35 +++ spec/factories/use_presenters.rb | 46 +--- 14 files changed, 605 insertions(+), 223 deletions(-) create mode 100644 lib/dsu/presenters/project/use_by_number_presenter.rb create mode 100644 lib/dsu/views/project/use_by_number.rb create mode 100644 spec/dsu/views/project/delete_spec.rb create mode 100644 spec/dsu/views/project/use_by_number_spec.rb create mode 100644 spec/factories/use_by_number_presenters.rb diff --git a/lib/dsu/models/project.rb b/lib/dsu/models/project.rb index 54d406e9..1f3d829a 100644 --- a/lib/dsu/models/project.rb +++ b/lib/dsu/models/project.rb @@ -132,10 +132,6 @@ def all end end - # def any? - # project_metadata.any? - # end - def can_delete?(project_name:) exist?(project_name: project_name) && # Cannot delete the last project. @@ -217,20 +213,20 @@ def find(project_name:) raise I18n.t('models.project.errors.does_not_exist', project_name: project_name) end - project_file_path = project_file_for(project_name: project_name) + project_file = project_file_for(project_name: project_name) unless project_file_exist?(project_name: project_name) - raise I18n.t('models.project.errors.project_file_not_exist', project_file: project_file_path) + raise I18n.t('models.project.errors.project_file_not_exist', project_file: project_file) end - project_hash = Crud::JsonFile.read!(file_path: project_file_path) + project_hash = Crud::JsonFile.read!(file_path: project_file) Services::Project::HydratorService.new(project_hash: project_hash).call end # project_number is 1 based. def find_by_number(project_number:) project = project_metadata.find do |metadata| - metadata[:project_number] == project_number + metadata[:project_number] == project_number.to_i end return unless project @@ -239,26 +235,17 @@ def find_by_number(project_number:) # def find_or_create(project_name:) # find_or_initialize(project_name: project_name).tap do |project| - # project.write! unless project.persisted? + # project.save! unless project.persisted? # end # end - # def find_or_initialize(project_name:) - # project_path = project_folder_for(project_name: project_name) - # # Dif.exist?(project_path) do |project_hash| - # # Services::Project::HydratorService.new(project_hash: project_hash).call - # # end || new(project_name: project_name) - # if Dir.exist?(project_path) - # Crud::JsonFile.read!(file_path: project_path).fetch(:project).tap do |project_name| - # # description = "#{project_name.capitalize} project}" - # # unless project_exist?(project_name: project_name) - # # create!(project_name: project_name, description: description) - # # end - # end - # else - # # TODO: Create - # end - # end + def find_or_initialize(project_name:) + return Models::Project.new(project_name: project_name) unless project_file_exist?(project_name: project_name) + + project_file = project_file_for(project_name: project_name) + project_hash = Crud::JsonFile.read!(file_path: project_file) + Services::Project::HydratorService.new(project_hash: project_hash).call + end # def update(project_name:, description:, version:, options:) # # TODO: Update the project diff --git a/lib/dsu/presenters/project/delete_presenter.rb b/lib/dsu/presenters/project/delete_presenter.rb index 944cf476..58ca57a7 100644 --- a/lib/dsu/presenters/project/delete_presenter.rb +++ b/lib/dsu/presenters/project/delete_presenter.rb @@ -6,7 +6,7 @@ module Dsu module Presenters module Project - class UsePresenter < BasePresenterEx + class DeletePresenter < BasePresenterEx attr_writer :project_name_or_number def initialize(project_name_or_number:, options: {}) @@ -21,6 +21,10 @@ def respond(response:) project.delete! if project&.present? end + def project_name + project.project_name + end + def project_name_or_number return project_name if delete_by_project_name? return project_number if delete_by_project_number? @@ -76,11 +80,11 @@ def project_errors? project&.invalid? end - def project_name - return unless delete_by_project_name? + # def project_name + # return unless delete_by_project_name? - @project_name_or_number - end + # @project_name_or_number + # end def project_number return -1 unless delete_by_project_number? diff --git a/lib/dsu/presenters/project/use_by_number_presenter.rb b/lib/dsu/presenters/project/use_by_number_presenter.rb new file mode 100644 index 00000000..a60ee820 --- /dev/null +++ b/lib/dsu/presenters/project/use_by_number_presenter.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative '../../models/project' +require_relative '../base_presenter_ex' + +module Dsu + module Presenters + module Project + class UseByNumberPresenter < BasePresenterEx + attr_reader :project_number + + delegate :project_name, to: :project, allow_nil: true + delegate :description, to: :project, prefix: true, allow_nil: true + + def initialize(project_number:, options: {}) + super(options: options) + + raise ArgumentError, 'project_number is blank' if project_number.blank? + + self.project_number = project_number + end + + def respond(response:) + return false unless response + + project.use! if project&.present? + end + + def project_does_not_exist? + !project&.exist? + end + + def project_errors + return false unless project&.persisted? + + project.errors.full_messages + end + + private + + attr_writer :project_number + + def project + @project ||= Models::Project.find_by_number(project_number: project_number) + end + end + end + end +end diff --git a/lib/dsu/presenters/project/use_presenter.rb b/lib/dsu/presenters/project/use_presenter.rb index d2b992b5..7ed7fb9f 100644 --- a/lib/dsu/presenters/project/use_presenter.rb +++ b/lib/dsu/presenters/project/use_presenter.rb @@ -7,12 +7,16 @@ module Dsu module Presenters module Project class UsePresenter < BasePresenterEx - attr_writer :project_name_or_number + attr_reader :project_name - def initialize(project_name_or_number:, options: {}) + delegate :description, to: :project, prefix: true, allow_nil: true + + def initialize(project_name:, options: {}) super(options: options) - @project_name_or_number = project_name_or_number + raise ArgumentError, 'project_name is blank' if project_name.blank? + + self.project_name = project_name end def respond(response:) @@ -21,71 +25,22 @@ def respond(response:) project.use! if project&.present? end - def project_name_or_number - return project_name if use_by_project_name? - return project_number if use_by_project_number? - - Models::Project.default_project_name - end - - def project_description - return unless project&.present? - - project.description - end - def project_does_not_exist? - !project&.exist? + !project.exist? end def project_errors - return [] unless project_errors? + return false unless project.persisted? project.errors.full_messages end - def use_by_project_name? - !use_by_project_number? && !use_by_project_default? - end - - def use_by_project_number? - /\A\d+\z/.match?(@project_name_or_number.to_s) - end - - def use_by_project_default? - @project_name_or_number.blank? - end - private - attr_reader :options + attr_writer :project_name def project - return @project if defined?(@project) - - @project = if use_by_project_name? && Dsu::Models::Project.project_initialized?(project_name: project_name) - Dsu::Models::Project.find(project_name: project_name) - elsif use_by_project_number? - Dsu::Models::Project.find_by_number(project_number: project_number) - elsif use_by_project_default? - Dsu::Models::Project.default_project - end - end - - def project_errors? - project&.invalid? - end - - def project_name - return unless use_by_project_name? - - @project_name_or_number - end - - def project_number - return -1 unless use_by_project_number? - - @project_name_or_number.to_i + @project ||= Models::Project.find_or_initialize(project_name: project_name) end end end diff --git a/lib/dsu/subcommands/project.rb b/lib/dsu/subcommands/project.rb index b844dc5a..07ed9d13 100644 --- a/lib/dsu/subcommands/project.rb +++ b/lib/dsu/subcommands/project.rb @@ -3,9 +3,11 @@ require_relative '../presenters/project/create_presenter' require_relative '../presenters/project/delete_presenter' require_relative '../presenters/project/list_presenter' +require_relative '../presenters/project/use_by_number_presenter' require_relative '../presenters/project/use_presenter' require_relative '../views/project/create' require_relative '../views/project/use' +require_relative '../views/project/use_by_number' require_relative '../views/shared/error' require_relative 'base_subcommand' @@ -65,9 +67,31 @@ def show option :prompts, type: :hash, default: {}, hide: true, aliases: '-p' def use(project_name_or_number = nil) options = configuration.to_h.merge(self.options).with_indifferent_access - presenter = Presenters::Project::UsePresenter.new(project_name_or_number: project_name_or_number, - options: options) - Views::Project::Use.new(presenter: presenter, options: options).render + presenter = use_presenter_for(project_name_or_number, options: options) + use_view_for(project_name_or_number, presenter: presenter, options: options).render + end + + private + + def use_view_for(project_name, presenter:, options:) + if project_number?(project_name) + Views::Project::UseByNumber.new(presenter: presenter, options: options) + else + Views::Project::Use.new(presenter: presenter, options: options) + end + end + + def use_presenter_for(project_name, options:) + if project_number?(project_name) + Presenters::Project::UseByNumberPresenter.new(project_number: project_name.to_i, options: options) + else + project_name = Models::Project.default_project_name if project_name.blank? + Presenters::Project::UsePresenter.new(project_name: project_name, options: options) + end + end + + def project_number?(project_name) + /^[+-]?\d+(\.\d+)?$/.match?(project_name.to_s) end end end diff --git a/lib/dsu/views/project/delete.rb b/lib/dsu/views/project/delete.rb index 8e19cd33..e7668766 100644 --- a/lib/dsu/views/project/delete.rb +++ b/lib/dsu/views/project/delete.rb @@ -71,14 +71,14 @@ def display_project_does_not_exists end def display_deleted_project_message - message = I18n.t('subcommands.project.delete.messages.using_project', + message = I18n.t('subcommands.project.delete.messages.deleted_project', project_name: presenter.project_name_or_number) puts apply_theme(message, theme_color: color_theme.success) end def delete_prompt I18n.t('subcommands.project.delete.prompts.delete_confirm', - project_name: presenter.project_name_or_number, description: presenter.project_description) + project_name: presenter.project_name, description: presenter.project_description) end def delete_prompt_options diff --git a/lib/dsu/views/project/use.rb b/lib/dsu/views/project/use.rb index 7b50e82d..a0464292 100644 --- a/lib/dsu/views/project/use.rb +++ b/lib/dsu/views/project/use.rb @@ -50,7 +50,7 @@ def display_project_use_prompt end def display_use_project_cancelled_message - message = I18n.t('subcommands.project.messages.cancelled', project_name: presenter.project_name_or_number) + message = I18n.t('subcommands.project.messages.cancelled') puts apply_theme(message, theme_color: color_theme.info) end @@ -60,25 +60,21 @@ def display_project_errors end def display_project_does_not_exists - message = if presenter.use_by_project_number? - I18n.t('subcommands.project.messages.number_does_not_exist', - project_number: presenter.project_name_or_number) - else - I18n.t('subcommands.project.messages.does_not_exist', - project_name: presenter.project_name_or_number) - end + message = I18n.t('subcommands.project.messages.does_not_exist', + project_name: presenter.project_name) puts apply_theme(message, theme_color: color_theme.error) end def display_using_project_message message = I18n.t('subcommands.project.use.messages.using_project', - project_name: presenter.project_name_or_number) + project_name: presenter.project_name) puts apply_theme(message, theme_color: color_theme.success) end def use_prompt I18n.t('subcommands.project.use.prompts.use_confirm', - project_name: presenter.project_name_or_number, description: presenter.project_description) + project_name: presenter.project_name, + description: presenter.project_description) end def use_prompt_options diff --git a/lib/dsu/views/project/use_by_number.rb b/lib/dsu/views/project/use_by_number.rb new file mode 100644 index 00000000..f2641291 --- /dev/null +++ b/lib/dsu/views/project/use_by_number.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require_relative '../../env' +require_relative '../../models/color_theme' +require_relative '../../support/ask' +require_relative '../../support/color_themable' +require_relative 'use' + +module Dsu + module Views + module Project + class UseByNumber < Use + private + + def display_project_does_not_exists + message = I18n.t('subcommands.project.messages.number_does_not_exist', + project_number: presenter.project_number) + puts apply_theme(message, theme_color: color_theme.error) + end + end + end + end +end diff --git a/lib/locales/en/subcommands.yml b/lib/locales/en/subcommands.yml index 09e8c472..2e5307c8 100644 --- a/lib/locales/en/subcommands.yml +++ b/lib/locales/en/subcommands.yml @@ -525,6 +525,13 @@ en: $ dsu project delete -n "My project" $ dsu p d -n "My project" + messages: + deleted: Deleted project "%{project_name}". + prompts: + delete_confirm: Delete project "%{project_name}"? + delete_options: + - y + - n list: desc: list|l usage: Lists all the DSU projects @@ -592,7 +599,7 @@ en: messages: using_project: Now using project "%{project_name}". prompts: - use_confirm: "Use project %{project_name}: \"%{description}\"?" + use_confirm: "Use project \"%{project_name} - %{description}\"?" use_options: - y - n diff --git a/spec/dsu/views/project/delete_spec.rb b/spec/dsu/views/project/delete_spec.rb new file mode 100644 index 00000000..9f12663d --- /dev/null +++ b/spec/dsu/views/project/delete_spec.rb @@ -0,0 +1,216 @@ +# frozen_string_literal: true + +RSpec.describe Dsu::Views::Project::Delete do + subject(:delete_view) do + described_class.new(presenter: presenter, options: options) + end + + shared_examples 'the project is the current project' do + it 'is the current project' do + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + + shared_examples 'the project is not the current project' do + it 'is not the current project' do + expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) + end + end + + before do + allow($stdin).to receive(:getch).and_return(response) + end + + let(:options) { nil } + let(:project_name) { 'xyz' } + + describe '#render' do + context 'when using a project name' do + let(:presenter) do + build(:delete_presenter, :with_project_name, project_name_or_number: project_name, options: options) + end + + context "when the user confirmation is 'y'" do + let(:response) { 'y' } + + it_behaves_like 'the project is not the current project' + + it 'deletes the project and sets it to the current project' do + delete_view.render + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + + context "when the user confirmation is 'n'" do + let(:response) { 'n' } + + it_behaves_like 'the project is not the current project' + + it 'does not delete the project and does not change the current project' do + delete_view.render + expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) + end + end + end + + context 'when using a project number' do + let(:presenter) do + build(:delete_presenter, :with_project_number, project_name_or_number: project_name, options: options) + end + + context "when using a project number and user confirmation is 'y'" do + let(:project_name) { 'Xyz' } + let(:response) { 'y' } + + it_behaves_like 'the project is not the current project' + + it 'deletes the project and sets it to the current project' do + delete_view.render + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + + context "when using a project number and user confirmation is 'n'" do + let(:project_name) { 'Xyz' } + let(:response) { 'n' } + + it_behaves_like 'the project is not the current project' + + it 'does not delete the project and does not change the current project' do + delete_view.render + expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) + end + end + end + + context 'when not using a project name or project number' do + before do + project + end + + let(:project) do + create(:project, :current_project, project_name: project_name, options: options) + end + let(:presenter) do + build(:delete_presenter, :without_project_name, options: options) + end + + context "when the user confirmation is 'y'" do + let(:response) { 'y' } + + it_behaves_like 'the project is the current project' + + it 'deletes the default project and sets it to the current project' do + delete_view.render + default_project_name = Dsu::Models::Configuration.new.default_project + expect(Dsu::Models::Project.current_project.project_name).to eq(default_project_name) + end + end + + context "when the user confirmation is 'n'" do + let(:response) { 'n' } + + it_behaves_like 'the project is the current project' + + it 'does not delete the project and does not change the current project' do + delete_view.render + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + end + + context 'when the project returns errors' do + before do + allow(presenter).to receive_messages(project_errors?: true, + project_errors: expected_errors) + end + + let(:presenter) do + build(:delete_presenter, :with_project_name, project_name_or_number: project_name, options: options) + end + let(:response) { 'unused' } + let(:expected_errors) do + [ + 'Project error 1', + 'Project error 2' + ] + end + + it 'displays the errors' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + delete_view.render + end.chomp)).to eq(expected_errors.join("\n")) + end + end + + context 'when an error is raised that is not rescued' do + before do + allow(presenter).to receive(:project_does_not_exist?).and_raise(StandardError, expected_error) + end + + let(:presenter) do + build(:delete_presenter, :with_project_name, project_name_or_number: project_name, options: options) + end + let(:response) { 'unused' } + let(:expected_error) { 'Test error' } + + it 'captures and displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + delete_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the project does not exist' do + before do + allow(presenter).to receive(:project_does_not_exist?).and_return(true) + end + + let(:presenter) do + build(:delete_presenter, :with_project_name, project_name_or_number: project_name, options: options) + end + let(:response) { 'unused' } + + context 'when the presenter is using a project number' do + before do + allow(presenter).to receive(:delete_by_project_number?).and_return(true) + end + + let(:expected_error) { 'A project for number 0 does not exist.' } + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + delete_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the presenter is using a project that is not the default' do + let(:expected_error) do + "Project \"#{project_name}\" does not exist." + end + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + delete_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the presenter is using the default project' do + let(:presenter) do + build(:delete_presenter, :without_project_name, options: options) + end + let(:expected_error) do + "Project \"#{presenter.project_name_or_number}\" does not exist." + end + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + delete_view.render + end.chomp)).to include(expected_error) + end + end + end + end +end diff --git a/spec/dsu/views/project/use_by_number_spec.rb b/spec/dsu/views/project/use_by_number_spec.rb new file mode 100644 index 00000000..e4dfa86a --- /dev/null +++ b/spec/dsu/views/project/use_by_number_spec.rb @@ -0,0 +1,190 @@ +# frozen_string_literal: true + +RSpec.describe Dsu::Views::Project::UseByNumber do + subject(:use_by_number_view) do + described_class.new(presenter: presenter, options: options) + end + + shared_examples 'the project is the current project' do + it 'is the current project' do + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + + shared_examples 'the project is not the current project' do + it 'is not the current project' do + expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) + end + end + + before do + allow($stdin).to receive(:getch).and_return(response) + end + + let(:project_name) { 'xyz' } + let(:options) { nil } + + describe '#render' do + context 'when using a project number' do + before do + create(:project, project_name: project_name, options: options) + end + + let(:presenter) do + build(:use_by_number_presenter, :with_project_number, options: options) + end + + context "when using a project number and user confirmation is 'y'" do + let(:response) { 'y' } + + it_behaves_like 'the project is not the current project' + + it 'uses the project and sets it to the current project' do + use_by_number_view.render + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + + context "when using a project number and user confirmation is 'n'" do + let(:response) { 'n' } + + it_behaves_like 'the project is not the current project' + + it 'does not use the project and does not change the current project' do + use_by_number_view.render + expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) + end + end + end + + context 'when not using a project number' do + before do + create(:project, :current_project, project_name: project_name, options: options) + end + + let(:presenter) do + build(:use_by_number_presenter, :with_default_project, options: options) + end + + context "when the user confirmation is 'y'" do + let(:response) { 'y' } + + it_behaves_like 'the project is the current project' + + it 'uses the default project and sets it to the current project' do + use_by_number_view.render + default_project_name = Dsu::Models::Configuration.new.default_project + expect(Dsu::Models::Project.current_project.project_name).to eq(default_project_name) + end + end + + context "when the user confirmation is 'n'" do + let(:response) { 'n' } + + it_behaves_like 'the project is the current project' + + it 'does not use the project and does not change the current project' do + use_by_number_view.render + expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) + end + end + end + + context 'when the project returns errors' do + before do + create(:project, :current_project, project_name: project_name, options: options) + allow(presenter).to receive_messages(project_errors?: true, + project_errors: expected_errors) + end + + let(:presenter) do + build(:use_by_number_presenter, :with_project_number, options: options) + end + let(:response) { 'unused' } + let(:expected_errors) do + [ + 'Project error 1', + 'Project error 2' + ] + end + + it 'displays the errors' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + use_by_number_view.render + end.chomp)).to eq(expected_errors.join("\n")) + end + end + + context 'when an error is raised that is not rescued' do + before do + create(:project, :current_project, project_name: project_name, options: options) + allow(presenter).to receive(:project_does_not_exist?).and_raise(StandardError, expected_error) + end + + let(:presenter) do + build(:use_by_number_presenter, :with_project_number, options: options) + end + let(:response) { 'unused' } + let(:expected_error) { 'Test error' } + + it 'captures and displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + use_by_number_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the project does not exist' do + before do + create(:project, :current_project, project_name: project_name, options: options) + allow(presenter).to receive(:project_does_not_exist?).and_return(true) + end + + let(:presenter) do + build(:use_by_number_presenter, :with_project_number, options: options) + end + let(:response) { 'unused' } + + context 'when the presenter is using a project number' do + before do + allow(presenter).to receive(:use_by_project_number?).and_return(true) + end + + let(:expected_error) { "A project for number #{presenter.project_number} does not exist." } + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + use_by_number_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the presenter is using a project that is not the default' do + let(:expected_error) do + "A project for number #{presenter.project_number} does not exist." + end + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + use_by_number_view.render + end.chomp)).to include(expected_error) + end + end + + context 'when the presenter is using the default project' do + let(:presenter) do + build(:use_by_number_presenter, :with_default_project, options: options) + end + let(:expected_error) do + "A project for number #{presenter.project_number} does not exist." + end + + it 'displays the error' do + expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do + use_by_number_view.render + end.chomp)).to include(expected_error) + end + end + end + end +end diff --git a/spec/dsu/views/project/use_spec.rb b/spec/dsu/views/project/use_spec.rb index 76c23941..6b222225 100644 --- a/spec/dsu/views/project/use_spec.rb +++ b/spec/dsu/views/project/use_spec.rb @@ -27,7 +27,7 @@ describe '#render' do context 'when using a project name' do let(:presenter) do - build(:use_presenter, :with_project_name, project_name_or_number: project_name, options: options) + build(:use_presenter, :with_project, project_name: project_name, options: options) end context "when the user confirmation is 'y'" do @@ -53,72 +53,6 @@ end end - context 'when using a project number' do - let(:presenter) do - build(:use_presenter, :with_project_number, project_name_or_number: project_name, options: options) - end - - context "when using a project number and user confirmation is 'y'" do - let(:project_name) { 'Xyz' } - let(:response) { 'y' } - - it_behaves_like 'the project is not the current project' - - it 'uses the project and sets it to the current project' do - use_view.render - expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) - end - end - - context "when using a project number and user confirmation is 'n'" do - let(:project_name) { 'Xyz' } - let(:response) { 'n' } - - it_behaves_like 'the project is not the current project' - - it 'does not use the project and does not change the current project' do - use_view.render - expect(Dsu::Models::Project.current_project.project_name).to_not eq(project_name) - end - end - end - - context 'when not using a project name or project number' do - before do - project - end - - let(:project) do - create(:project, :current_project, project_name: project_name, options: options) - end - let(:presenter) do - build(:use_presenter, :without_project_name, options: options) - end - - context "when the user confirmation is 'y'" do - let(:response) { 'y' } - - it_behaves_like 'the project is the current project' - - it 'uses the default project and sets it to the current project' do - use_view.render - default_project_name = Dsu::Models::Configuration.new.default_project - expect(Dsu::Models::Project.current_project.project_name).to eq(default_project_name) - end - end - - context "when the user confirmation is 'n'" do - let(:response) { 'n' } - - it_behaves_like 'the project is the current project' - - it 'does not use the project and does not change the current project' do - use_view.render - expect(Dsu::Models::Project.current_project.project_name).to eq(project_name) - end - end - end - context 'when the project returns errors' do before do allow(presenter).to receive_messages(project_errors?: true, @@ -126,7 +60,7 @@ end let(:presenter) do - build(:use_presenter, :with_project_name, project_name_or_number: project_name, options: options) + build(:use_presenter, :with_project, project_name: project_name, options: options) end let(:response) { 'unused' } let(:expected_errors) do @@ -149,7 +83,7 @@ end let(:presenter) do - build(:use_presenter, :with_project_name, project_name_or_number: project_name, options: options) + build(:use_presenter, :with_project, project_name: project_name, options: options) end let(:response) { 'unused' } let(:expected_error) { 'Test error' } @@ -167,24 +101,10 @@ end let(:presenter) do - build(:use_presenter, :with_project_name, project_name_or_number: project_name, options: options) + build(:use_presenter, :with_project, project_name: project_name, options: options) end let(:response) { 'unused' } - context 'when the presenter is using a project number' do - before do - allow(presenter).to receive(:use_by_project_number?).and_return(true) - end - - let(:expected_error) { 'A project for number 0 does not exist.' } - - it 'displays the error' do - expect(strip_escapes(Dsu::Services::StdoutRedirectorService.call do - use_view.render - end.chomp)).to include(expected_error) - end - end - context 'when the presenter is using a project that is not the default' do let(:expected_error) do "Project \"#{project_name}\" does not exist." @@ -199,10 +119,10 @@ context 'when the presenter is using the default project' do let(:presenter) do - build(:use_presenter, :without_project_name, options: options) + build(:use_presenter, :with_default_project, options: options) end let(:expected_error) do - "Project \"#{presenter.project_name_or_number}\" does not exist." + "Project \"#{presenter.project_name}\" does not exist." end it 'displays the error' do diff --git a/spec/factories/use_by_number_presenters.rb b/spec/factories/use_by_number_presenters.rb new file mode 100644 index 00000000..8d35d8ab --- /dev/null +++ b/spec/factories/use_by_number_presenters.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :use_by_number_presenter, class: 'Dsu::Presenters::Project::UseByNumberPresenter' do + project_number { 2 } + options { {} } + + initialize_with do + new(project_number: project_number, options: options) + end + + trait :with_default_project do + project_number do + Dsu::Models::Project.default_project.project_number + end + + with_project_number + end + + trait :with_project_number do + after(:build) do |use_by_number_presenter, evaluator| + project_number = evaluator.project_number + project_metadata = Dsu::Models::Project.project_metadata.find do |metadata| + metadata[:project_number] == project_number.to_i + end + project_number = if project_metadata + project_metadata[:project_number] + else + raise ArgumentError, "project_number #{project_number} is not a valid project number" + end + use_by_number_presenter.send(:project_number=, project_number) + end + end + end +end diff --git a/spec/factories/use_presenters.rb b/spec/factories/use_presenters.rb index 57d47c8a..11cb03b9 100644 --- a/spec/factories/use_presenters.rb +++ b/spec/factories/use_presenters.rb @@ -2,53 +2,29 @@ FactoryBot.define do factory :use_presenter, class: 'Dsu::Presenters::Project::UsePresenter' do - project_name_or_number { 'test' } + project_name { 'test' } options { {} } - transient do - # Define transient attributes here if needed - end - initialize_with do - new(project_name_or_number: project_name_or_number, options: options) + new(project_name: project_name, options: options) end - trait :without_project_name do - project_name_or_number { nil } + trait :with_default_project do + project_name do + Dsu::Models::Project.default_project.project_name + end + + with_project end - trait :with_project_name do + trait :with_project do after(:build) do |_use_presenter, evaluator| - project_name_or_number = evaluator.project_name_or_number - if /\A\d+\z/.match?(project_name_or_number) - raise "project name \"#{project_name_or_nmber}\" should not be a number" - end - + project_name = evaluator.project_name options = evaluator.options - build(:project, project_name: project_name_or_number, options: options).tap do |project| + build(:project, project_name: project_name, options: options).tap do |project| project.save! unless project.exist? end end end - - trait :with_project_number do - after(:build) do |use_presenter, evaluator| - project_name = evaluator.project_name_or_number - raise "project name \"#{project_name}\" should not be a number" if /\A\d+\z/.match?(project_name) - - options = evaluator.options - project_metadata = Dsu::Models::Project.project_metadata.find do |metadata| - metadata[:project_name] == project_name - end - project_number = if project_metadata - project_metadata[:project_number] - else - build(:project, project_name: project_name, options: options).tap do |project| - project.save! unless project.exist? - end.project_number - end - use_presenter.project_name_or_number = project_number - end - end end end