diff options
author | Brett Walker <bwalker@gitlab.com> | 2018-10-26 15:49:16 +0300 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2018-10-26 15:49:16 +0300 |
commit | 7aeab58f4861144fcc1d334907cb1b465c645001 (patch) | |
tree | cc37daf4788c2f7c3d951e3fdc3164e1b7ae0fd9 /spec | |
parent | 9791f82b4b4f873c153e6698fbb2148b8fa5babe (diff) |
Automatically navigate to last board visited
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/groups/boards_controller_spec.rb | 58 | ||||
-rw-r--r-- | spec/controllers/projects/boards_controller_spec.rb | 56 | ||||
-rw-r--r-- | spec/factories/board_group_recent_visit.rb | 9 | ||||
-rw-r--r-- | spec/factories/board_project_recent_visit.rb | 9 | ||||
-rw-r--r-- | spec/models/board_group_recent_visit_spec.rb | 64 | ||||
-rw-r--r-- | spec/models/board_project_recent_visit_spec.rb | 64 | ||||
-rw-r--r-- | spec/services/boards/visits/create_service_spec.rb | 53 | ||||
-rw-r--r-- | spec/services/boards/visits/latest_service_spec.rb | 47 |
8 files changed, 357 insertions, 3 deletions
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb index bf41aa0706f..d1d08391164 100644 --- a/spec/controllers/groups/boards_controller_spec.rb +++ b/spec/controllers/groups/boards_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Groups::BoardsController do let(:group) { create(:group) } - let(:user) { create(:user) } + let(:user) { create(:user) } before do group.add_maintainer(user) @@ -22,6 +22,27 @@ describe Groups::BoardsController do expect(response.content_type).to eq 'text/html' end + it 'redirects to latest visited board' do + board = create(:board, group: group) + create(:board_group_recent_visit, group: board.group, board: board, user: user) + + list_boards + + expect(response).to redirect_to(group_board_path(id: board.id)) + end + + it 'renders template if visited board is not found' do + visited = double + + allow(visited).to receive(:board_id).and_return(12) + allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited) + + list_boards + + expect(response).to render_template :index + expect(response.content_type).to eq 'text/html' + end + context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) @@ -35,12 +56,30 @@ describe Groups::BoardsController do expect(response.content_type).to eq 'text/html' end end + + context 'when user is signed out' do + let(:group) { create(:group, :public) } + + it 'renders template' do + sign_out(user) + + board = create(:board, group: group) + create(:board_group_recent_visit, group: board.group, board: board, user: user) + + list_boards + + expect(response).to render_template :index + expect(response.content_type).to eq 'text/html' + end + end end context 'when format is JSON' do it 'return an array with one group board' do create(:board, group: group) + expect(Boards::Visits::LatestService).not_to receive(:new) + list_boards format: :json parsed_response = JSON.parse(response.body) @@ -74,7 +113,7 @@ describe Groups::BoardsController do context 'when format is HTML' do it 'renders template' do - read_board board: board + expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(1) expect(response).to render_template :show expect(response.content_type).to eq 'text/html' @@ -93,10 +132,25 @@ describe Groups::BoardsController do expect(response.content_type).to eq 'text/html' end end + + context 'when user is signed out' do + let(:group) { create(:group, :public) } + + it 'does not save visit' do + sign_out(user) + + expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(0) + + expect(response).to render_template :show + expect(response.content_type).to eq 'text/html' + end + end end context 'when format is JSON' do it 'returns project board' do + expect(Boards::Visits::CreateService).not_to receive(:new) + read_board board: board, format: :json expect(response).to match_response_schema('board') diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb index 096efc1c7b2..667eaa5e34f 100644 --- a/spec/controllers/projects/boards_controller_spec.rb +++ b/spec/controllers/projects/boards_controller_spec.rb @@ -28,6 +28,27 @@ describe Projects::BoardsController do expect(response.content_type).to eq 'text/html' end + it 'redirects to latest visited board' do + board = create(:board, project: project) + create(:board_project_recent_visit, project: board.project, board: board, user: user) + + list_boards + + expect(response).to redirect_to(namespace_project_board_path(id: board.id)) + end + + it 'renders template if visited board is not found' do + visited = double + + allow(visited).to receive(:board_id).and_return(12) + allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited) + + list_boards + + expect(response).to render_template :index + expect(response.content_type).to eq 'text/html' + end + context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) @@ -41,12 +62,30 @@ describe Projects::BoardsController do expect(response.content_type).to eq 'text/html' end end + + context 'when user is signed out' do + let(:project) { create(:project, :public) } + + it 'renders template' do + sign_out(user) + + board = create(:board, project: project) + create(:board_project_recent_visit, project: board.project, board: board, user: user) + + list_boards + + expect(response).to render_template :index + expect(response.content_type).to eq 'text/html' + end + end end context 'when format is JSON' do it 'returns a list of project boards' do create_list(:board, 2, project: project) + expect(Boards::Visits::LatestService).not_to receive(:new) + list_boards format: :json parsed_response = JSON.parse(response.body) @@ -98,7 +137,7 @@ describe Projects::BoardsController do context 'when format is HTML' do it 'renders template' do - read_board board: board + expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(1) expect(response).to render_template :show expect(response.content_type).to eq 'text/html' @@ -117,10 +156,25 @@ describe Projects::BoardsController do expect(response.content_type).to eq 'text/html' end end + + context 'when user is signed out' do + let(:project) { create(:project, :public) } + + it 'does not save visit' do + sign_out(user) + + expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(0) + + expect(response).to render_template :show + expect(response.content_type).to eq 'text/html' + end + end end context 'when format is JSON' do it 'returns project board' do + expect(Boards::Visits::CreateService).not_to receive(:new) + read_board board: board, format: :json expect(response).to match_response_schema('board') diff --git a/spec/factories/board_group_recent_visit.rb b/spec/factories/board_group_recent_visit.rb new file mode 100644 index 00000000000..97ad5d6d068 --- /dev/null +++ b/spec/factories/board_group_recent_visit.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :board_group_recent_visit do + user + group + board + end +end diff --git a/spec/factories/board_project_recent_visit.rb b/spec/factories/board_project_recent_visit.rb new file mode 100644 index 00000000000..49ae4d7b391 --- /dev/null +++ b/spec/factories/board_project_recent_visit.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :board_project_recent_visit do + user + project + board + end +end diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb new file mode 100644 index 00000000000..59ad4e5417e --- /dev/null +++ b/spec/models/board_group_recent_visit_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe BoardGroupRecentVisit do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:board) { create(:board, group: group) } + + describe 'relationships' do + it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:group) } + it { is_expected.to belong_to(:board) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:user) } + it { is_expected.to validate_presence_of(:group) } + it { is_expected.to validate_presence_of(:board) } + end + + describe '#visited' do + it 'creates a visit if one does not exists' do + expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1) + end + + shared_examples 'was visited previously' do + let!(:visit) { create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago } + + it 'updates the timestamp' do + Timecop.freeze do + described_class.visited!(user, board) + + expect(described_class.count).to eq 1 + expect(described_class.first.updated_at).to be_like_time(Time.zone.now) + end + end + end + + it_behaves_like 'was visited previously' + + context 'when we try to create a visit that is not unique' do + before do + expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique') + expect(described_class).to receive(:find_or_create_by).and_return(visit) + end + + it_behaves_like 'was visited previously' + end + end + + describe '#latest' do + it 'returns the most recent visited' do + board2 = create(:board, group: group) + board3 = create(:board, group: group) + + create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago + create :board_group_recent_visit, group: board2.group, board: board2, user: user, updated_at: 5.days.ago + recent = create :board_group_recent_visit, group: board3.group, board: board3, user: user, updated_at: 1.day.ago + + expect(described_class.latest(user, group)).to eq recent + end + end +end diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb new file mode 100644 index 00000000000..275581945fa --- /dev/null +++ b/spec/models/board_project_recent_visit_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe BoardProjectRecentVisit do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + + describe 'relationships' do + it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:board) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:user) } + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:board) } + end + + describe '#visited' do + it 'creates a visit if one does not exists' do + expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1) + end + + shared_examples 'was visited previously' do + let!(:visit) { create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago } + + it 'updates the timestamp' do + Timecop.freeze do + described_class.visited!(user, board) + + expect(described_class.count).to eq 1 + expect(described_class.first.updated_at).to be_like_time(Time.zone.now) + end + end + end + + it_behaves_like 'was visited previously' + + context 'when we try to create a visit that is not unique' do + before do + expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique') + expect(described_class).to receive(:find_or_create_by).and_return(visit) + end + + it_behaves_like 'was visited previously' + end + end + + describe '#latest' do + it 'returns the most recent visited' do + board2 = create(:board, project: project) + board3 = create(:board, project: project) + + create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago + create :board_project_recent_visit, project: board2.project, board: board2, user: user, updated_at: 5.days.ago + recent = create :board_project_recent_visit, project: board3.project, board: board3, user: user, updated_at: 1.day.ago + + expect(described_class.latest(user, project)).to eq recent + end + end +end diff --git a/spec/services/boards/visits/create_service_spec.rb b/spec/services/boards/visits/create_service_spec.rb new file mode 100644 index 00000000000..6baf7ac9deb --- /dev/null +++ b/spec/services/boards/visits/create_service_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Boards::Visits::CreateService do + describe '#execute' do + let(:user) { create(:user) } + + context 'when a project board' do + let(:project) { create(:project) } + let(:project_board) { create(:board, project: project) } + + subject(:service) { described_class.new(project_board.parent, user) } + + it 'returns nil when there is no user' do + service.current_user = nil + + expect(service.execute(project_board)).to eq nil + end + + it 'returns nil when database is read only' do + allow(Gitlab::Database).to receive(:read_only?) { true } + + expect(service.execute(project_board)).to eq nil + end + + it 'records the visit' do + expect(BoardProjectRecentVisit).to receive(:visited!).once + + service.execute(project_board) + end + end + + context 'when a group board' do + let(:group) { create(:group) } + let(:group_board) { create(:board, group: group) } + + subject(:service) { described_class.new(group_board.parent, user) } + + it 'returns nil when there is no user' do + service.current_user = nil + + expect(service.execute(group_board)).to eq nil + end + + it 'records the visit' do + expect(BoardGroupRecentVisit).to receive(:visited!).once + + service.execute(group_board) + end + end + end +end diff --git a/spec/services/boards/visits/latest_service_spec.rb b/spec/services/boards/visits/latest_service_spec.rb new file mode 100644 index 00000000000..e55d599e2cc --- /dev/null +++ b/spec/services/boards/visits/latest_service_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Boards::Visits::LatestService do + describe '#execute' do + let(:user) { create(:user) } + + context 'when a project board' do + let(:project) { create(:project) } + let(:project_board) { create(:board, project: project) } + + subject(:service) { described_class.new(project_board.parent, user) } + + it 'returns nil when there is no user' do + service.current_user = nil + + expect(service.execute).to eq nil + end + + it 'queries for most recent visit' do + expect(BoardProjectRecentVisit).to receive(:latest).once + + service.execute + end + end + + context 'when a group board' do + let(:group) { create(:group) } + let(:group_board) { create(:board, group: group) } + + subject(:service) { described_class.new(group_board.parent, user) } + + it 'returns nil when there is no user' do + service.current_user = nil + + expect(service.execute).to eq nil + end + + it 'queries for most recent visit' do + expect(BoardGroupRecentVisit).to receive(:latest).once + + service.execute + end + end + end +end |