Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c57962295c
commit
271e563cf6
|
@ -52,7 +52,7 @@ export default {
|
|||
},
|
||||
toggleFormDropdown() {
|
||||
this.isDropdownShowing = !this.isDropdownShowing;
|
||||
const { dropdown } = this.$children[2].$refs.dropdown.$refs;
|
||||
const { dropdown } = this.$refs.status.$refs.dropdown.$refs;
|
||||
if (dropdown && this.isDropdownShowing) {
|
||||
dropdown.show();
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ export default {
|
|||
</p>
|
||||
|
||||
<alert-status
|
||||
ref="status"
|
||||
:alert="alert"
|
||||
:project-path="projectPath"
|
||||
:is-dropdown-showing="isDropdownShowing"
|
||||
|
|
|
@ -19,7 +19,7 @@ module BoardsActions
|
|||
|
||||
def show
|
||||
# Add / update the board in the recent visits table
|
||||
Boards::Visits::CreateService.new(parent, current_user).execute(board) if request.format.html?
|
||||
board_visit_service.new(parent, current_user).execute(board) if request.format.html?
|
||||
|
||||
respond_with_board
|
||||
end
|
||||
|
@ -52,6 +52,10 @@ module BoardsActions
|
|||
board_klass.to_type
|
||||
end
|
||||
|
||||
def board_visit_service
|
||||
Boards::Visits::CreateService
|
||||
end
|
||||
|
||||
def serializer
|
||||
BoardSerializer.new(current_user: current_user)
|
||||
end
|
||||
|
|
|
@ -2,27 +2,19 @@
|
|||
|
||||
# Tracks which boards in a specific group a user has visited
|
||||
class BoardGroupRecentVisit < ApplicationRecord
|
||||
include BoardRecentVisit
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :group
|
||||
belongs_to :board
|
||||
|
||||
validates :user, presence: true
|
||||
validates :user, presence: true
|
||||
validates :group, presence: true
|
||||
validates :board, presence: true
|
||||
|
||||
scope :by_user_group, -> (user, group) { where(user: user, group: group) }
|
||||
scope :by_user_parent, -> (user, group) { where(user: user, group: group) }
|
||||
|
||||
def self.visited!(user, board)
|
||||
visit = find_or_create_by(user: user, group: board.group, board: board)
|
||||
visit.touch if visit.updated_at < Time.current
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
retry
|
||||
end
|
||||
|
||||
def self.latest(user, group, count: nil)
|
||||
visits = by_user_group(user, group).order(updated_at: :desc)
|
||||
visits = visits.preload(:board) if count && count > 1
|
||||
|
||||
visits.first(count)
|
||||
def self.board_parent_relation
|
||||
:group
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,27 +2,19 @@
|
|||
|
||||
# Tracks which boards in a specific project a user has visited
|
||||
class BoardProjectRecentVisit < ApplicationRecord
|
||||
include BoardRecentVisit
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :board
|
||||
|
||||
validates :user, presence: true
|
||||
validates :user, presence: true
|
||||
validates :project, presence: true
|
||||
validates :board, presence: true
|
||||
|
||||
scope :by_user_project, -> (user, project) { where(user: user, project: project) }
|
||||
scope :by_user_parent, -> (user, project) { where(user: user, project: project) }
|
||||
|
||||
def self.visited!(user, board)
|
||||
visit = find_or_create_by(user: user, project: board.project, board: board)
|
||||
visit.touch if visit.updated_at < Time.current
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
retry
|
||||
end
|
||||
|
||||
def self.latest(user, project, count: nil)
|
||||
visits = by_user_project(user, project).order(updated_at: :desc)
|
||||
visits = visits.preload(:board) if count && count > 1
|
||||
|
||||
visits.first(count)
|
||||
def self.board_parent_relation
|
||||
:project
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BoardRecentVisit
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def visited!(user, board)
|
||||
find_or_create_by(
|
||||
"user" => user,
|
||||
board_parent_relation => board.resource_parent,
|
||||
board_relation => board
|
||||
).tap do |visit|
|
||||
visit.touch
|
||||
end
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
retry
|
||||
end
|
||||
|
||||
def latest(user, parent, count: nil)
|
||||
visits = by_user_parent(user, parent).order(updated_at: :desc)
|
||||
visits = visits.preload(board_relation)
|
||||
|
||||
visits.first(count)
|
||||
end
|
||||
|
||||
def board_relation
|
||||
:board
|
||||
end
|
||||
|
||||
def board_parent_relation
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,13 +5,17 @@ module Boards
|
|||
class CreateService < Boards::BaseService
|
||||
def execute(board)
|
||||
return unless current_user && Gitlab::Database.read_write?
|
||||
return unless board.is_a?(Board) # other board types do not support board visits yet
|
||||
return unless board
|
||||
|
||||
if parent.is_a?(Group)
|
||||
BoardGroupRecentVisit.visited!(current_user, board)
|
||||
else
|
||||
BoardProjectRecentVisit.visited!(current_user, board)
|
||||
end
|
||||
model.visited!(current_user, board)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model
|
||||
return BoardGroupRecentVisit if parent.is_a?(Group)
|
||||
|
||||
BoardProjectRecentVisit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Redirect to the last visited epic board
|
||||
merge_request: 60720
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEpicBoardRecentVisitsTable < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
create_table :boards_epic_board_recent_visits do |t|
|
||||
t.references :user, index: true, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.references :epic_board, index: true, foreign_key: { to_table: :boards_epic_boards, on_delete: :cascade }, null: false
|
||||
t.references :group, index: true, foreign_key: { to_table: :namespaces, on_delete: :cascade }, null: false
|
||||
t.timestamps_with_timezone null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
drop_table :boards_epic_board_recent_visits
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexToEpicBoardRecentVisits < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
INDEX_NAME = 'index_epic_board_recent_visits_on_user_group_and_board'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :boards_epic_board_recent_visits,
|
||||
[:user_id, :group_id, :epic_board_id],
|
||||
name: INDEX_NAME,
|
||||
unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :boards_epic_board_recent_visits, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
c92824844732aad7277fc8b50bac0bf6919ad0a8d72e73b4ec3b89eafc085b7d
|
|
@ -0,0 +1 @@
|
|||
ae3c8336cb25efa7d23357a6777c0656dbe1a216efb5d4edcf923d1128f7e1e3
|
|
@ -10057,6 +10057,24 @@ CREATE SEQUENCE boards_epic_board_positions_id_seq
|
|||
|
||||
ALTER SEQUENCE boards_epic_board_positions_id_seq OWNED BY boards_epic_board_positions.id;
|
||||
|
||||
CREATE TABLE boards_epic_board_recent_visits (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
epic_board_id bigint NOT NULL,
|
||||
group_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE boards_epic_board_recent_visits_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE boards_epic_board_recent_visits_id_seq OWNED BY boards_epic_board_recent_visits.id;
|
||||
|
||||
CREATE TABLE boards_epic_boards (
|
||||
id bigint NOT NULL,
|
||||
hide_backlog_list boolean DEFAULT false NOT NULL,
|
||||
|
@ -19353,6 +19371,8 @@ ALTER TABLE ONLY boards_epic_board_labels ALTER COLUMN id SET DEFAULT nextval('b
|
|||
|
||||
ALTER TABLE ONLY boards_epic_board_positions ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_positions_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY boards_epic_board_recent_visits ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_recent_visits_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY boards_epic_boards ALTER COLUMN id SET DEFAULT nextval('boards_epic_boards_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY boards_epic_list_user_preferences ALTER COLUMN id SET DEFAULT nextval('boards_epic_list_user_preferences_id_seq'::regclass);
|
||||
|
@ -20468,6 +20488,9 @@ ALTER TABLE ONLY boards_epic_board_labels
|
|||
ALTER TABLE ONLY boards_epic_board_positions
|
||||
ADD CONSTRAINT boards_epic_board_positions_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY boards_epic_board_recent_visits
|
||||
ADD CONSTRAINT boards_epic_board_recent_visits_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY boards_epic_boards
|
||||
ADD CONSTRAINT boards_epic_boards_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -22293,6 +22316,12 @@ CREATE INDEX index_boards_epic_board_positions_on_epic_id ON boards_epic_board_p
|
|||
|
||||
CREATE INDEX index_boards_epic_board_positions_on_scoped_relative_position ON boards_epic_board_positions USING btree (epic_board_id, epic_id, relative_position);
|
||||
|
||||
CREATE INDEX index_boards_epic_board_recent_visits_on_epic_board_id ON boards_epic_board_recent_visits USING btree (epic_board_id);
|
||||
|
||||
CREATE INDEX index_boards_epic_board_recent_visits_on_group_id ON boards_epic_board_recent_visits USING btree (group_id);
|
||||
|
||||
CREATE INDEX index_boards_epic_board_recent_visits_on_user_id ON boards_epic_board_recent_visits USING btree (user_id);
|
||||
|
||||
CREATE INDEX index_boards_epic_boards_on_group_id ON boards_epic_boards USING btree (group_id);
|
||||
|
||||
CREATE INDEX index_boards_epic_list_user_preferences_on_epic_list_id ON boards_epic_list_user_preferences USING btree (epic_list_id);
|
||||
|
@ -22857,6 +22886,8 @@ CREATE INDEX index_environments_on_state_and_auto_stop_at ON environments USING
|
|||
|
||||
CREATE UNIQUE INDEX index_epic_board_list_preferences_on_user_and_list ON boards_epic_list_user_preferences USING btree (user_id, epic_list_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_epic_board_recent_visits_on_user_group_and_board ON boards_epic_board_recent_visits USING btree (user_id, group_id, epic_board_id);
|
||||
|
||||
CREATE INDEX index_epic_issues_on_epic_id ON epic_issues USING btree (epic_id);
|
||||
|
||||
CREATE INDEX index_epic_issues_on_epic_id_and_issue_id ON epic_issues USING btree (epic_id, issue_id);
|
||||
|
@ -26626,6 +26657,9 @@ ALTER TABLE ONLY packages_rubygems_metadata
|
|||
ALTER TABLE ONLY packages_pypi_metadata
|
||||
ADD CONSTRAINT fk_rails_9698717cdd FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boards_epic_board_recent_visits
|
||||
ADD CONSTRAINT fk_rails_96c2c18642 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY packages_dependency_links
|
||||
ADD CONSTRAINT fk_rails_96ef1c00d3 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
|
||||
|
||||
|
@ -26890,6 +26924,9 @@ ALTER TABLE ONLY pages_deployments
|
|||
ALTER TABLE ONLY merge_request_user_mentions
|
||||
ADD CONSTRAINT fk_rails_c440b9ea31 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boards_epic_board_recent_visits
|
||||
ADD CONSTRAINT fk_rails_c4dcba4a3e FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_job_artifacts
|
||||
ADD CONSTRAINT fk_rails_c5137cb2c1 FOREIGN KEY (job_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
|
||||
|
||||
|
@ -27067,6 +27104,9 @@ ALTER TABLE ONLY draft_notes
|
|||
ALTER TABLE ONLY namespace_package_settings
|
||||
ADD CONSTRAINT fk_rails_e773444769 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boards_epic_board_recent_visits
|
||||
ADD CONSTRAINT fk_rails_e77911cf03 FOREIGN KEY (epic_board_id) REFERENCES boards_epic_boards(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY dast_site_tokens
|
||||
ADD CONSTRAINT fk_rails_e84f721a8e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# GitLab authentication and authorization
|
||||
# GitLab authentication and authorization **(FREE SELF)**
|
||||
|
||||
GitLab integrates with the following external authentication and authorization
|
||||
providers:
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Atlassian OmniAuth Provider
|
||||
# Atlassian OmniAuth Provider **(FREE SELF)**
|
||||
|
||||
To enable the Atlassian OmniAuth provider for passwordless authentication you must register an application with Atlassian.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Authentiq OmniAuth Provider
|
||||
# Authentiq OmniAuth Provider **(FREE SELF)**
|
||||
|
||||
To enable the Authentiq OmniAuth provider for passwordless authentication you must register an application with Authentiq.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Amazon Web Services Cognito
|
||||
# Amazon Web Services Cognito **(FREE SELF)**
|
||||
|
||||
Amazon Cognito lets you add user sign-up, sign-in, and access control to your GitLab instance.
|
||||
The following documentation enables Cognito as an OAuth2 provider.
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Atlassian Crowd OmniAuth Provider
|
||||
# Atlassian Crowd OmniAuth Provider **(FREE SELF)**
|
||||
|
||||
Authenticate to GitLab using the Atlassian Crowd OmniAuth provider. Enabling
|
||||
this provider also allows Crowd authentication for Git-over-https requests.
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# JWT OmniAuth provider
|
||||
# JWT OmniAuth provider **(FREE SELF)**
|
||||
|
||||
To enable the JWT OmniAuth provider, you must register your application with JWT.
|
||||
JWT will provide you with a secret key for you to use.
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# LDAP Troubleshooting for Administrators
|
||||
# LDAP Troubleshooting for Administrators **(FREE SELF)**
|
||||
|
||||
## Common Problems & Workflows
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ group: Access
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# OpenID Connect OmniAuth provider
|
||||
# OpenID Connect OmniAuth provider **(FREE SELF)**
|
||||
|
||||
GitLab can use [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) as an OmniAuth provider.
|
||||
|
||||
|
|
|
@ -2563,6 +2563,25 @@ Input type: `IterationCadenceUpdateInput`
|
|||
| <a id="mutationiterationcadenceupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationiterationcadenceupdateiterationcadence"></a>`iterationCadence` | [`IterationCadence`](#iterationcadence) | The updated iteration cadence. |
|
||||
|
||||
### `Mutation.iterationDelete`
|
||||
|
||||
Input type: `IterationDeleteInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationiterationdeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationiterationdeleteid"></a>`id` | [`IterationID!`](#iterationid) | ID of the iteration. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationiterationdeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationiterationdeleteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationiterationdeletegroup"></a>`group` | [`Group!`](#group) | Group the iteration belongs to. |
|
||||
|
||||
### `Mutation.jiraImportStart`
|
||||
|
||||
Input type: `JiraImportStartInput`
|
||||
|
|
|
@ -588,7 +588,7 @@ NOTE:
|
|||
| `SECURE_ANALYZERS_PREFIX` | Specify the Docker registry base address from which to download the analyzer. |
|
||||
| `FUZZAPI_VERSION` | Specify API Fuzzing container version. Defaults to `latest`. |
|
||||
| `FUZZAPI_TARGET_URL` | Base URL of API testing target. |
|
||||
|[`FUZZAPI_CONFIG`](#configuration-files) | API Fuzzing configuration file. Defaults to `.gitlab-apifuzzer.yml`. |
|
||||
|[`FUZZAPI_CONFIG`](#configuration-files) | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/276395) in GitLab 13.12, replaced with default `.gitlab/gitlab-api-fuzzing-config.yml`. API Fuzzing configuration file. |
|
||||
|[`FUZZAPI_PROFILE`](#configuration-files) | Configuration profile to use during testing. Defaults to `Quick`. |
|
||||
|[`FUZZAPI_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. |
|
||||
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
RSpec.describe 'Create', quarantine: { only: { subdomain: :staging }, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/323990', type: :flaky } do
|
||||
describe 'Merge request rebasing' do
|
||||
it 'user rebases source branch of merge request', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1274' do
|
||||
it 'user rebases source branch of merge request', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1800' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
project = Resource::Project.fabricate_via_api! do |project|
|
||||
|
|
|
@ -10,9 +10,9 @@ const mockAlert = mockAlerts[0];
|
|||
|
||||
describe('Alert Details Sidebar Status', () => {
|
||||
let wrapper;
|
||||
const findStatusDropdown = () => wrapper.find(GlDropdown);
|
||||
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
|
||||
const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon);
|
||||
const findStatusDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findStatusDropdownItem = () => wrapper.findComponent(GlDropdownItem);
|
||||
const findStatusLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findStatusDropdownHeader = () => wrapper.findByTestId('dropdown-header');
|
||||
const findAlertStatus = () => wrapper.findComponent(AlertStatus);
|
||||
const findStatus = () => wrapper.findByTestId('status');
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BoardGroupRecentVisit do
|
||||
let(:user) { create(:user) }
|
||||
let(:group) { create(:group) }
|
||||
let(:board) { create(:board, group: group) }
|
||||
let_it_be(:board_parent) { create(:group) }
|
||||
let_it_be(:board) { create(:board, group: board_parent) }
|
||||
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:user) }
|
||||
|
@ -19,56 +18,9 @@ RSpec.describe BoardGroupRecentVisit do
|
|||
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
|
||||
freeze_time 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
|
||||
def create_visit(time)
|
||||
create :board_group_recent_visit, group: group, user: user, updated_at: time
|
||||
end
|
||||
|
||||
it 'returns the most recent visited' do
|
||||
create_visit(7.days.ago)
|
||||
create_visit(5.days.ago)
|
||||
recent = create_visit(1.day.ago)
|
||||
|
||||
expect(described_class.latest(user, group)).to eq recent
|
||||
end
|
||||
|
||||
it 'returns last 3 visited boards' do
|
||||
create_visit(7.days.ago)
|
||||
visit1 = create_visit(3.days.ago)
|
||||
visit2 = create_visit(2.days.ago)
|
||||
visit3 = create_visit(5.days.ago)
|
||||
|
||||
expect(described_class.latest(user, group, count: 3)).to eq([visit2, visit1, visit3])
|
||||
end
|
||||
it_behaves_like 'boards recent visit' do
|
||||
let_it_be(:board_relation) { :board }
|
||||
let_it_be(:board_parent_relation) { :group }
|
||||
let_it_be(:visit_relation) { :board_group_recent_visit }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BoardProjectRecentVisit do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:board) { create(:board, project: project) }
|
||||
let_it_be(:board_parent) { create(:project) }
|
||||
let_it_be(:board) { create(:board, project: board_parent) }
|
||||
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:user) }
|
||||
|
@ -19,56 +18,9 @@ RSpec.describe BoardProjectRecentVisit do
|
|||
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
|
||||
freeze_time 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
|
||||
def create_visit(time)
|
||||
create :board_project_recent_visit, project: project, user: user, updated_at: time
|
||||
end
|
||||
|
||||
it 'returns the most recent visited' do
|
||||
create_visit(7.days.ago)
|
||||
create_visit(5.days.ago)
|
||||
recent = create_visit(1.day.ago)
|
||||
|
||||
expect(described_class.latest(user, project)).to eq recent
|
||||
end
|
||||
|
||||
it 'returns last 3 visited boards' do
|
||||
create_visit(7.days.ago)
|
||||
visit1 = create_visit(3.days.ago)
|
||||
visit2 = create_visit(2.days.ago)
|
||||
visit3 = create_visit(5.days.ago)
|
||||
|
||||
expect(described_class.latest(user, project, count: 3)).to eq([visit2, visit1, visit3])
|
||||
end
|
||||
it_behaves_like 'boards recent visit' do
|
||||
let_it_be(:board_relation) { :board }
|
||||
let_it_be(:board_parent_relation) { :project }
|
||||
let_it_be(:visit_relation) { :board_project_recent_visit }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,47 +7,20 @@ RSpec.describe Boards::Visits::CreateService do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
context 'when a project board' do
|
||||
let(:project) { create(:project) }
|
||||
let(:project_board) { create(:board, project: project) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:board) { create(:board, project: project) }
|
||||
|
||||
subject(:service) { described_class.new(project_board.resource_parent, user) }
|
||||
let_it_be(:model) { BoardProjectRecentVisit }
|
||||
|
||||
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
|
||||
it_behaves_like 'boards recent visit create service'
|
||||
end
|
||||
|
||||
context 'when a group board' do
|
||||
let(:group) { create(:group) }
|
||||
let(:group_board) { create(:board, group: group) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:board) { create(:board, group: group) }
|
||||
let_it_be(:model) { BoardGroupRecentVisit }
|
||||
|
||||
subject(:service) { described_class.new(group_board.resource_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
|
||||
it_behaves_like 'boards recent visit create service'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'boards recent visit' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
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_it_be(:visit) do
|
||||
create(visit_relation,
|
||||
board_parent_relation => board_parent,
|
||||
board_relation => board,
|
||||
user: user,
|
||||
updated_at: 7.days.ago
|
||||
)
|
||||
end
|
||||
|
||||
it 'updates the timestamp' do
|
||||
freeze_time 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
|
||||
def create_visit(time)
|
||||
create(visit_relation, board_parent_relation => board_parent, user: user, updated_at: time)
|
||||
end
|
||||
|
||||
it 'returns the most recent visited' do
|
||||
create_visit(7.days.ago)
|
||||
create_visit(5.days.ago)
|
||||
recent = create_visit(1.day.ago)
|
||||
|
||||
expect(described_class.latest(user, board_parent)).to eq recent
|
||||
end
|
||||
|
||||
it 'returns last 3 visited boards' do
|
||||
create_visit(7.days.ago)
|
||||
visit1 = create_visit(3.days.ago)
|
||||
visit2 = create_visit(2.days.ago)
|
||||
visit3 = create_visit(5.days.ago)
|
||||
|
||||
expect(described_class.latest(user, board_parent, count: 3)).to eq([visit2, visit1, visit3])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'boards recent visit create service' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject(:service) { described_class.new(board.resource_parent, user) }
|
||||
|
||||
it 'returns nil when there is no user' do
|
||||
service.current_user = nil
|
||||
|
||||
expect(service.execute(board)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when database is read only' do
|
||||
allow(Gitlab::Database).to receive(:read_only?) { true }
|
||||
|
||||
expect(service.execute(board)).to be_nil
|
||||
end
|
||||
|
||||
it 'records the visit' do
|
||||
expect(model).to receive(:visited!).once
|
||||
|
||||
service.execute(board)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue