Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
958a674e2c
commit
cceae0fdcd
|
|
@ -11,98 +11,30 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
|
||||
before_action :set_non_archived_param, only: [:index, :starred]
|
||||
before_action :set_sorting
|
||||
# When your_work_projects_vue FF is enabled we load the projects via GraphQL query
|
||||
# so we don't want to preload the projects at the controller level to avoid duplicate queries.
|
||||
before_action :projects, only: [:index], unless: :your_work_projects_vue_feature_flag_enabled?
|
||||
skip_cross_project_access_check :index, :starred
|
||||
|
||||
feature_category :groups_and_projects
|
||||
urgency :low, [:starred, :index]
|
||||
|
||||
def index
|
||||
if your_work_projects_vue_feature_flag_enabled?
|
||||
return redirect_to personal_dashboard_projects_path if params[:personal] == "true"
|
||||
return redirect_to inactive_dashboard_projects_path if params[:archived] == "only"
|
||||
end
|
||||
return redirect_to personal_dashboard_projects_path if params[:personal] == "true"
|
||||
return redirect_to inactive_dashboard_projects_path if params[:archived] == "only"
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render_projects
|
||||
render
|
||||
end
|
||||
format.atom do
|
||||
load_events
|
||||
render layout: 'xml'
|
||||
end
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def starred
|
||||
unless your_work_projects_vue_feature_flag_enabled?
|
||||
@projects = load_projects(params.merge(starred: true, not_aimed_for_deletion: true))
|
||||
.includes(:forked_from_project, :topics)
|
||||
end
|
||||
|
||||
@groups = []
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
def starred; end
|
||||
|
||||
private
|
||||
|
||||
def projects
|
||||
@projects ||= load_projects(params.merge(non_public: true, not_aimed_for_deletion: true))
|
||||
end
|
||||
|
||||
def render_projects
|
||||
# n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
def load_projects(finder_params)
|
||||
@all_user_projects = ProjectsFinder.new(
|
||||
params: { non_public: true, archived: false,
|
||||
not_aimed_for_deletion: true }, current_user: current_user).execute
|
||||
@all_starred_projects = ProjectsFinder.new(
|
||||
params: { starred: true, archived: false,
|
||||
not_aimed_for_deletion: true }, current_user: current_user).execute
|
||||
|
||||
finder_params[:use_cte] = true if use_cte_for_finder?
|
||||
|
||||
projects = ProjectsFinder.new(params: finder_params, current_user: current_user).execute
|
||||
|
||||
projects = preload_associations(projects)
|
||||
projects = projects.page(finder_params[:page])
|
||||
|
||||
prepare_projects_for_rendering(projects)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def preload_associations(projects)
|
||||
projects.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]).preload(:project_feature)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def use_cte_for_finder?
|
||||
# The starred action loads public projects, which causes the CTE to be less efficient
|
||||
action_name == 'index'
|
||||
end
|
||||
|
||||
def load_events
|
||||
projects = ProjectsFinder
|
||||
.new(params: params.merge(non_public: true, not_aimed_for_deletion: true), current_user: current_user)
|
||||
|
|
@ -115,11 +47,6 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
|
||||
end
|
||||
|
||||
def set_sorting
|
||||
params[:sort] = set_sort_order
|
||||
@sort = params[:sort]
|
||||
end
|
||||
|
||||
def default_sort_order
|
||||
sort_value_name
|
||||
end
|
||||
|
|
@ -128,8 +55,9 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
Project::SORTING_PREFERENCE_FIELD
|
||||
end
|
||||
|
||||
def your_work_projects_vue_feature_flag_enabled?
|
||||
Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
def set_sorting
|
||||
params[:sort] = set_sort_order
|
||||
@sort = params[:sort]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,23 +13,11 @@ class RootController < Dashboard::ProjectsController
|
|||
|
||||
before_action :redirect_unlogged_user, if: -> { current_user.nil? }
|
||||
before_action :redirect_logged_user, if: -> { current_user.present? }
|
||||
# We only need to load the projects when the user is logged in but did not
|
||||
# configure a dashboard. In which case we render projects. We can do that straight
|
||||
# from the #index action.
|
||||
skip_before_action :projects
|
||||
|
||||
CACHE_CONTROL_HEADER = 'no-store'
|
||||
|
||||
def index
|
||||
# When your_work_projects_vue FF is enabled we load the projects via GraphQL query
|
||||
# so we don't want to preload the projects at the controller level to avoid duplicate queries.
|
||||
return if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
|
||||
# n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
projects
|
||||
super
|
||||
end
|
||||
def index # rubocop:disable Lint/UselessMethodDefinition -- we need to explicitly define this action for the `skip_before_action`
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -52,10 +40,8 @@ class RootController < Dashboard::ProjectsController
|
|||
flash.keep
|
||||
redirect_to(starred_dashboard_projects_path)
|
||||
when 'member_projects'
|
||||
if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
flash.keep
|
||||
redirect_to(member_dashboard_projects_path)
|
||||
end
|
||||
flash.keep
|
||||
redirect_to(member_dashboard_projects_path)
|
||||
when 'your_activity'
|
||||
redirect_to(activity_dashboard_path)
|
||||
when 'project_activity'
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ module PreferencesHelper
|
|||
end
|
||||
|
||||
def dashboard_value
|
||||
return current_user.dashboard if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
|
||||
return 'projects' if current_user.dashboard == 'member_projects'
|
||||
|
||||
current_user.dashboard
|
||||
end
|
||||
|
||||
|
|
@ -21,7 +17,6 @@ module PreferencesHelper
|
|||
def dashboard_choices
|
||||
dashboards = User.dashboards.keys
|
||||
|
||||
dashboards -= ['member_projects'] unless Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
validate_dashboard_choices!(dashboards)
|
||||
dashboards -= excluded_dashboard_choices
|
||||
|
||||
|
|
@ -36,16 +31,10 @@ module PreferencesHelper
|
|||
|
||||
# Maps `dashboard` values to more user-friendly option text
|
||||
def localized_dashboard_choices
|
||||
projects = if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
_("Your Contributed Projects (default)")
|
||||
else
|
||||
_("Your Projects (default)")
|
||||
end
|
||||
|
||||
{
|
||||
projects: projects,
|
||||
projects: _("Your Contributed Projects (default)"),
|
||||
stars: _("Starred Projects"),
|
||||
member_projects: (_("Member Projects") if Feature.enabled?(:your_work_projects_vue, current_user)),
|
||||
member_projects: _("Member Projects"),
|
||||
your_activity: _("Your Activity"),
|
||||
project_activity: _("Your Projects' Activity"),
|
||||
starred_project_activity: _("Starred Projects' Activity"),
|
||||
|
|
|
|||
|
|
@ -9,16 +9,8 @@
|
|||
|
||||
= render "projects/last_push"
|
||||
|
||||
- if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
- if show_dashboard_projects_welcome_page?
|
||||
= render "zero_authorized_projects"
|
||||
- else
|
||||
= render 'dashboard/projects_head'
|
||||
#js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } }
|
||||
- if show_dashboard_projects_welcome_page?
|
||||
= render "zero_authorized_projects"
|
||||
- else
|
||||
- if show_projects?(@projects, params)
|
||||
= render 'dashboard/projects_head'
|
||||
= render 'dashboard/projects_nav'
|
||||
= render 'projects'
|
||||
- else
|
||||
= render "zero_authorized_projects"
|
||||
= render 'dashboard/projects_head'
|
||||
#js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
-# TODO: This file can be removed in favor of 'dashboard/projects/index.html.haml' as part of `:your_work_projects_vue` FF rollout
|
||||
-# https://gitlab.com/gitlab-org/gitlab/-/issues/465889
|
||||
-# TODO: This file can be removed in favor of 'dashboard/projects/index.html.haml'
|
||||
-# Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/523698
|
||||
|
||||
- page_title _("Projects")
|
||||
|
||||
|
|
@ -7,14 +7,5 @@
|
|||
|
||||
= render "projects/last_push"
|
||||
|
||||
- if Feature.enabled?(:your_work_projects_vue, current_user)
|
||||
= render 'dashboard/projects_head'
|
||||
#js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } }
|
||||
- else
|
||||
= render 'dashboard/projects_head', project_tab_filter: :starred
|
||||
= render 'dashboard/projects_nav'
|
||||
|
||||
- if params[:filter_projects] || any_projects?(@projects)
|
||||
= render 'projects'
|
||||
- else
|
||||
= render empty_page
|
||||
= render 'dashboard/projects_head'
|
||||
#js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } }
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: your_work_projects_vue
|
||||
feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/13066
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155472
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/465889
|
||||
milestone: '17.1'
|
||||
type: beta
|
||||
group: group::organizations
|
||||
default_enabled: false
|
||||
|
|
@ -20,8 +20,8 @@ resource :dashboard, controller: 'dashboard', only: [] do
|
|||
|
||||
resources :projects, only: [:index] do
|
||||
collection do
|
||||
## TODO: Migrate `starred` route to 'projects#index' when removing `:your_work_projects_vue` FF
|
||||
## https://gitlab.com/gitlab-org/gitlab/-/issues/465889
|
||||
## TODO: Migrate `starred` route to 'projects#index'
|
||||
## Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/523698
|
||||
get :starred
|
||||
get :contributed, :personal, :member, :inactive, to: 'projects#index'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -144,16 +144,11 @@ To choose your home organization:
|
|||
{{< history >}}
|
||||
|
||||
- [Homepage options changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Homepage option changes generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
{{< alert type="flag" >}}
|
||||
|
||||
When the `your_work_projects_vue` feature flag is enabled, the **Your Contributed Projects** view becomes the default option, and an additional **Member Projects** option is available in the dropdown list. For more information, see the history.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
Control what page you view when you select the GitLab logo ({{< icon name="tanuki" >}}). You can set your homepage to be Your Projects (default), Your Groups, Your Activity, and other content.
|
||||
Control what page you view when you select the GitLab logo ({{< icon name="tanuki" >}}). You can set your homepage to be Your Contributed Projects (default), Your Groups, Your Activity, and other content.
|
||||
|
||||
To choose your homepage view:
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ If you are not authenticated, the list shows public projects only.
|
|||
{{< history >}}
|
||||
|
||||
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/13066) in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
@ -127,6 +128,7 @@ To view projects you have contributed to:
|
|||
{{< history >}}
|
||||
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Yours" to "Member" in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
@ -359,6 +361,7 @@ To immediately delete a project marked for deletion:
|
|||
{{< history >}}
|
||||
|
||||
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Pending deletion" to "Inactive" in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default.
|
||||
- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ module Gitlab
|
|||
Gitlab::AppLogger.info(
|
||||
event: 'mrdc_message_method_git',
|
||||
message:
|
||||
"mrdc#message called via #{caller_locations.reject { |line| line.path.include?('/gems/') }.first(4)}"
|
||||
"mrdc#message called via #{caller_locations.reject { |line| line.path.include?('/gems/') }.first(10)}"
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def format(...)
|
||||
new(...).to_s
|
||||
new(...).perform
|
||||
end
|
||||
|
||||
def fetch_attachments(text)
|
||||
|
|
@ -53,17 +53,17 @@ module Gitlab
|
|||
# exists - Boolean that indicates the user exists in the GitLab database.
|
||||
# project - An instance of `Project`.
|
||||
def initialize(text, author = nil, exists = false, project: nil)
|
||||
@text = text.to_s
|
||||
@text = text
|
||||
@author = author
|
||||
@exists = exists
|
||||
@project = project
|
||||
end
|
||||
|
||||
def to_s
|
||||
def perform
|
||||
return if text.blank?
|
||||
|
||||
# Gitlab::EncodingHelper#clean remove `null` chars from the string
|
||||
text = clean(format)
|
||||
text = clean(formatted_text)
|
||||
text = convert_ref_links(text, project) if project.present?
|
||||
wrap_mentions_in_backticks(text)
|
||||
end
|
||||
|
|
@ -72,7 +72,7 @@ module Gitlab
|
|||
|
||||
attr_reader :text, :author, :exists, :project
|
||||
|
||||
def format
|
||||
def formatted_text
|
||||
login = author.respond_to?(:fetch) ? author.fetch(:login, nil) : author.try(:login)
|
||||
return "*Created by: #{login}*\n\n#{text}" if login.present? && !exists
|
||||
|
||||
|
|
|
|||
|
|
@ -68099,9 +68099,6 @@ msgstr ""
|
|||
msgid "Your Personal Access Token was revoked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your Projects (default)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your Projects' Activity"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -48,16 +48,8 @@ module QA
|
|||
click_element('new-project-button', Page::Project::New)
|
||||
end
|
||||
|
||||
# The tab names of the HAML and Vue version of the UI differ slightly.
|
||||
# The Vue version defaults to a "Contributed" tab where-as the HAML version
|
||||
# defaults to a "Yours" tab. Here we conditionally click the "Member" tab
|
||||
# if it exists (only on the Vue version). This allows the tests to
|
||||
# pass in both versions.
|
||||
# We can remove this conditional check when code behind the
|
||||
# your_work_projects_vue feature flag becomes the default.
|
||||
def click_member_tab
|
||||
text = 'Member'
|
||||
click_link(text) if has_link?(text)
|
||||
click_link('Member')
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ
|
|||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
|
@ -35,191 +34,24 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ
|
|||
end
|
||||
end
|
||||
|
||||
it 'orders the projects by name by default' do
|
||||
get :index
|
||||
|
||||
expect(assigns(:projects)).to eq(projects)
|
||||
end
|
||||
|
||||
it 'assigns the correct all_user_projects' do
|
||||
get :index
|
||||
all_user_projects = assigns(:all_user_projects)
|
||||
|
||||
expect(all_user_projects.count).to eq(2)
|
||||
end
|
||||
|
||||
it 'assigns the correct all_starred_projects' do
|
||||
get :index
|
||||
all_starred_projects = assigns(:all_starred_projects)
|
||||
|
||||
expect(all_starred_projects.count).to eq(1)
|
||||
expect(all_starred_projects).to include(project2)
|
||||
end
|
||||
|
||||
context 'project sorting' do
|
||||
it_behaves_like 'set sort order from user preference' do
|
||||
let(:sorting_param) { 'created_asc' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with search and sort parameters' do
|
||||
render_views
|
||||
|
||||
shared_examples 'search and sort parameters' do |sort|
|
||||
it 'returns a single project with no ambiguous column errors' do
|
||||
get :index, params: { name: project2.name, sort: sort }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(assigns(:projects)).to eq([project2])
|
||||
end
|
||||
end
|
||||
|
||||
%w[latest_activity_desc latest_activity_asc stars_desc stars_asc created_desc].each do |sort|
|
||||
it_behaves_like 'search and sort parameters', sort
|
||||
end
|
||||
end
|
||||
|
||||
context 'with archived project' do
|
||||
let_it_be(:archived_project) do
|
||||
project2.tap { |p| p.update!(archived: true) }
|
||||
end
|
||||
|
||||
it 'does not display archived project' do
|
||||
get :index
|
||||
projects_result = assigns(:projects)
|
||||
|
||||
expect(projects_result).not_to include(archived_project)
|
||||
expect(projects_result).to include(project)
|
||||
end
|
||||
|
||||
it 'excludes archived project from all_user_projects' do
|
||||
get :index
|
||||
all_user_projects = assigns(:all_user_projects)
|
||||
|
||||
expect(all_user_projects.count).to eq(1)
|
||||
expect(all_user_projects).not_to include(archived_project)
|
||||
end
|
||||
|
||||
it 'excludes archived project from all_starred_projects' do
|
||||
get :index
|
||||
all_starred_projects = assigns(:all_starred_projects)
|
||||
|
||||
expect(all_starred_projects.count).to eq(0)
|
||||
expect(all_starred_projects).not_to include(archived_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with deleted project' do
|
||||
let!(:pending_delete_project) do
|
||||
project.tap { |p| p.update!(pending_delete: true) }
|
||||
end
|
||||
|
||||
it 'does not display deleted project' do
|
||||
get :index
|
||||
projects_result = assigns(:projects)
|
||||
|
||||
expect(projects_result).not_to include(pending_delete_project)
|
||||
expect(projects_result).to include(project2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with redirects' do
|
||||
context 'when feature flag your_work_projects_vue is true' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: true)
|
||||
end
|
||||
|
||||
it 'redirects ?personal=true to /personal' do
|
||||
get :index, params: { personal: true }
|
||||
|
||||
expect(response).to redirect_to(personal_dashboard_projects_path)
|
||||
end
|
||||
|
||||
it 'redirects ?archived=only to /inactive' do
|
||||
get :index, params: { archived: 'only' }
|
||||
|
||||
expect(response).to redirect_to(inactive_dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not redirect ?personal=true to /personal' do
|
||||
it 'redirects ?personal=true to /personal' do
|
||||
get :index, params: { personal: true }
|
||||
|
||||
expect(response).not_to redirect_to(personal_dashboard_projects_path)
|
||||
expect(response).to redirect_to(personal_dashboard_projects_path)
|
||||
end
|
||||
|
||||
it 'does not ?archived=only to /inactive' do
|
||||
it 'redirects ?archived=only to /inactive' do
|
||||
get :index, params: { archived: 'only' }
|
||||
|
||||
expect(response).not_to redirect_to(inactive_dashboard_projects_path)
|
||||
expect(response).to redirect_to(inactive_dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'json requests' do
|
||||
render_views
|
||||
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe 'GET /projects.json' do
|
||||
before do
|
||||
get :index, format: :json
|
||||
end
|
||||
|
||||
it { is_expected.to respond_with(:success) }
|
||||
end
|
||||
|
||||
describe 'GET /starred.json' do
|
||||
subject { get :starred, format: :json }
|
||||
|
||||
let(:projects) { create_list(:project, 2, creator: user) }
|
||||
let(:aimed_for_deletion_project) { create_list(:project, 2, :archived, creator: user, marked_for_deletion_at: 3.days.ago) }
|
||||
|
||||
before do
|
||||
projects.each do |project|
|
||||
project.add_developer(user)
|
||||
create(:users_star_project, project_id: project.id, user_id: user.id)
|
||||
end
|
||||
|
||||
aimed_for_deletion_project.each do |project|
|
||||
project.add_developer(user)
|
||||
create(:users_star_project, project_id: project.id, user_id: user.id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context "pagination" do
|
||||
before do
|
||||
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
|
||||
end
|
||||
|
||||
it 'paginates the records' do
|
||||
subject
|
||||
|
||||
expect(assigns(:projects).count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include projects aimed for deletion' do
|
||||
subject
|
||||
|
||||
expect(assigns(:projects).count).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'atom requests' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
|
@ -298,38 +130,10 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ
|
|||
end
|
||||
|
||||
describe '#starred' do
|
||||
let_it_be(:project) { create(:project, name: 'Project 1') }
|
||||
let_it_be(:project2) { create(:project, name: 'Project Two') }
|
||||
it 'does not assign all_starred_projects' do
|
||||
get :starred
|
||||
|
||||
before_all do
|
||||
project.add_developer(user)
|
||||
project2.add_developer(user)
|
||||
user.toggle_star(project2)
|
||||
end
|
||||
|
||||
context 'when your_work_projects_vue feature flag is enabled' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'does not assign all_starred_projects' do
|
||||
get :starred
|
||||
|
||||
expect(assigns(:all_starred_projects)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when your_work_projects_vue feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'assigns all_starred_projects' do
|
||||
get :starred
|
||||
|
||||
expect(assigns(:all_starred_projects)).to contain_exactly(project2)
|
||||
end
|
||||
expect(assigns(:all_starred_projects)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -79,24 +79,10 @@ RSpec.describe RootController, feature_category: :shared do
|
|||
user.dashboard = 'member_projects'
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is enabled' do
|
||||
it 'redirects to their member projects list' do
|
||||
get :index
|
||||
it 'redirects to their member projects list' do
|
||||
get :index
|
||||
|
||||
expect(response).to redirect_to member_dashboard_projects_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
|
||||
it 'does not redirect' do
|
||||
get :index
|
||||
|
||||
expect(response).not_to redirect_to member_dashboard_projects_path
|
||||
end
|
||||
expect(response).to redirect_to member_dashboard_projects_path
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Dashboard Archived Project', feature_category: :groups_and_projects do
|
||||
RSpec.describe 'Dashboard Archived Project', :js, feature_category: :groups_and_projects do
|
||||
let_it_be(:user) { create :user }
|
||||
let_it_be(:project) { create :project }
|
||||
let_it_be(:archived_project) { create(:project, :archived) }
|
||||
|
|
@ -14,70 +14,35 @@ RSpec.describe 'Dashboard Archived Project', feature_category: :groups_and_proje
|
|||
archived_project_2.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is enabled', :js do
|
||||
before do
|
||||
sign_in(user)
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'renders non archived projects' do
|
||||
expect(page).to have_link(project.name)
|
||||
expect(page).not_to have_link(archived_project.name)
|
||||
end
|
||||
|
||||
it 'renders only archived projects' do
|
||||
click_link 'Inactive'
|
||||
|
||||
expect(page).to have_content(archived_project.name)
|
||||
expect(page).not_to have_content(project.name)
|
||||
end
|
||||
|
||||
it 'searches archived projects', :js do
|
||||
click_link 'Inactive'
|
||||
|
||||
expect(page).to have_link(archived_project.name)
|
||||
expect(page).to have_link(archived_project_2.name)
|
||||
|
||||
search(archived_project.name)
|
||||
|
||||
expect(page).not_to have_link(archived_project_2.name)
|
||||
expect(page).to have_link(archived_project.name)
|
||||
end
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
sign_in(user)
|
||||
it 'renders non archived projects' do
|
||||
expect(page).to have_link(project.name)
|
||||
expect(page).not_to have_link(archived_project.name)
|
||||
end
|
||||
|
||||
visit dashboard_projects_path
|
||||
end
|
||||
it 'renders only archived projects' do
|
||||
click_link 'Inactive'
|
||||
|
||||
it 'renders non archived projects' do
|
||||
expect(page).to have_link(project.name)
|
||||
expect(page).not_to have_link(archived_project.name)
|
||||
end
|
||||
expect(page).to have_content(archived_project.name)
|
||||
expect(page).not_to have_content(project.name)
|
||||
end
|
||||
|
||||
it 'renders only archived projects' do
|
||||
click_link 'Inactive'
|
||||
it 'searches archived projects', :js do
|
||||
click_link 'Inactive'
|
||||
|
||||
expect(page).to have_content(archived_project.name)
|
||||
expect(page).not_to have_content(project.name)
|
||||
end
|
||||
expect(page).to have_link(archived_project.name)
|
||||
expect(page).to have_link(archived_project_2.name)
|
||||
|
||||
it 'searches archived projects', :js do
|
||||
click_link 'Inactive'
|
||||
search(archived_project.name)
|
||||
|
||||
expect(page).to have_link(archived_project.name)
|
||||
expect(page).to have_link(archived_project_2.name)
|
||||
|
||||
search(archived_project.name)
|
||||
|
||||
expect(page).not_to have_link(archived_project_2.name)
|
||||
expect(page).to have_link(archived_project.name)
|
||||
end
|
||||
expect(page).not_to have_link(archived_project_2.name)
|
||||
expect(page).to have_link(archived_project.name)
|
||||
end
|
||||
|
||||
def search(term)
|
||||
|
|
|
|||
|
|
@ -6,497 +6,246 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
|
|||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project, reload: true) { create(:project, :repository, creator: build(:user)) } # ensure creator != owner to avoid N+1 false-positive
|
||||
let_it_be(:project2) { create(:project, :public) }
|
||||
let_it_be(:personal_project) { create(:project, namespace: user.namespace) }
|
||||
let_it_be(:personal_project_with_stars) { create(:project, namespace: user.namespace, star_count: 10) }
|
||||
let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha, ref: project.default_branch) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is enabled' do
|
||||
let_it_be(:personal_project) { create(:project, namespace: user.namespace) }
|
||||
let_it_be(:personal_project_with_stars) { create(:project, namespace: user.namespace, star_count: 10) }
|
||||
let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha, ref: project.default_branch) }
|
||||
it 'mounts JS app and defaults to contributed tab' do
|
||||
visit dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
it 'mounts JS app and defaults to contributed tab' do
|
||||
expect(page).to have_content('Projects')
|
||||
expect(page).to have_selector('a[aria-selected="true"]', text: 'Contributed')
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do
|
||||
before do
|
||||
visit dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Projects')
|
||||
expect(page).to have_selector('a[aria-selected="true"]', text: 'Contributed')
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do
|
||||
before do
|
||||
visit dashboard_projects_path
|
||||
wait_for_requests
|
||||
end
|
||||
end
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects
|
||||
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects
|
||||
it 'links to the "Explore projects" page' do
|
||||
visit dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
it 'links to the "Explore projects" page' do
|
||||
visit dashboard_projects_path
|
||||
wait_for_requests
|
||||
expect(page).to have_link("Explore projects", href: starred_explore_projects_path)
|
||||
end
|
||||
|
||||
expect(page).to have_link("Explore projects", href: starred_explore_projects_path)
|
||||
end
|
||||
|
||||
context 'when user has access to the project' do
|
||||
it 'shows role badge' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(find_by_testid('user-access-role')).to have_content('Developer')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when last_activity_at and update_at are present', time_travel_to: '2025-01-27T09:44:07Z' do
|
||||
let_it_be(:project_with_last_activity) do
|
||||
create(
|
||||
:project,
|
||||
namespace: user.namespace,
|
||||
last_repository_updated_at: 1.hour.ago,
|
||||
last_activity_at: Time.current
|
||||
)
|
||||
end
|
||||
|
||||
it 'shows the last_activity_at attribute as the update date', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/514342' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_last_activity.id}") do
|
||||
expect(page).to have_xpath("//time[@datetime='#{project_with_last_activity.last_activity_at.iso8601}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when last_activity_at is missing', time_travel_to: '2025-01-27T09:44:07Z' do
|
||||
it 'shows the updated_at attribute as the update date' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).to have_xpath("//time[@datetime='#{project.updated_at.iso8601}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows personal projects on personal projects tab' do
|
||||
visit personal_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(personal_project.name)
|
||||
end
|
||||
|
||||
it 'sorts projects by most stars when sorting by most stars' do
|
||||
visit personal_dashboard_projects_path(sort: :stars_desc)
|
||||
wait_for_requests
|
||||
|
||||
expect(first('[data-testid*="projects-list-item"]')).to have_content(personal_project_with_stars.title)
|
||||
end
|
||||
|
||||
context 'when on Member projects tab' do
|
||||
it 'shows all projects you are a member of' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).to have_content(personal_project.name)
|
||||
expect(page).to have_content(personal_project_with_stars.name)
|
||||
expect(find('a[aria-selected="true"]')).to have_content('3')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on Starred projects tab' do
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects
|
||||
|
||||
it 'shows the empty state when there are no starred projects' do
|
||||
visit(starred_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_text("You haven't starred any projects yet.")
|
||||
end
|
||||
|
||||
it 'shows only starred projects' do
|
||||
user.toggle_star(project2)
|
||||
|
||||
visit(starred_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
expect(find('a[aria-selected="true"]')).to have_content('1')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a pipeline' do
|
||||
it 'shows that the last pipeline passed' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).to have_css("[data-testid='ci-icon']")
|
||||
expect(page).to have_css('[data-testid="status_success_borderless-icon"]')
|
||||
expect(page).to have_link('Status: Passed')
|
||||
end
|
||||
end
|
||||
|
||||
context 'guest user of project and project has private pipelines' do
|
||||
let_it_be(:guest_user) { create(:user) }
|
||||
let_it_be(:project_with_private_pipelines) { create(:project, namespace: user.namespace, public_builds: false) }
|
||||
|
||||
before_all do
|
||||
project_with_private_pipelines.add_guest(guest_user)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(guest_user)
|
||||
end
|
||||
|
||||
it 'does not show the pipeline status' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_private_pipelines.id}") do
|
||||
expect(page).not_to have_css("[data-testid='ci-icon']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when last_pipeline is missing" do
|
||||
it 'does not show the pipeline status' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{personal_project.id}") do
|
||||
expect(page).not_to have_css("[data-testid='ci-icon']")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project has topics' do
|
||||
let_it_be(:project_with_topics) { create(:project, namespace: user.namespace, topic_list: 'topic1') }
|
||||
|
||||
it 'shows project topics' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_topics.id}") do
|
||||
expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project does not have topics' do
|
||||
it 'does not show project topics' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).not_to have_selector('[data-testid="project-topics"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'last push widget', :use_clean_rails_memory_store_caching do
|
||||
before do
|
||||
event = create(:push_event, project: project, author: user)
|
||||
|
||||
create(:push_event_payload, event: event, ref: 'feature', action: :created)
|
||||
|
||||
Users::LastPushEventService.new(user).cache_last_push_event(event)
|
||||
|
||||
visit dashboard_projects_path
|
||||
end
|
||||
|
||||
it 'shows "Create merge request" button' do
|
||||
expect(page).to have_content 'You pushed to feature'
|
||||
|
||||
within('#content-body') do
|
||||
find_link('Create merge request', visible: false).click
|
||||
end
|
||||
|
||||
expect(page).to have_selector('.merge-request-form')
|
||||
expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true
|
||||
expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s
|
||||
expect(page).to have_content "From feature into master"
|
||||
end
|
||||
end
|
||||
|
||||
it 'avoids an N+1 query in dashboard index' do
|
||||
context 'when user has access to the project' do
|
||||
it 'shows role badge' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
control = ActiveRecord::QueryRecorder.new do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(find_by_testid('user-access-role')).to have_content('Developer')
|
||||
end
|
||||
|
||||
new_project = create(:project, :repository, name: 'new project')
|
||||
create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha)
|
||||
new_project.add_developer(user)
|
||||
|
||||
# There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037
|
||||
# - User#max_member_access_for_project_ids
|
||||
# - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline
|
||||
# - Ci::Pipeline#detailed_status
|
||||
|
||||
expect do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
end.not_to exceed_query_limit(control).with_threshold(4)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is disabled' do
|
||||
context 'when last_activity_at and update_at are present', time_travel_to: '2025-01-27T09:44:07Z' do
|
||||
let_it_be(:project_with_last_activity) do
|
||||
create(
|
||||
:project,
|
||||
namespace: user.namespace,
|
||||
last_repository_updated_at: 1.hour.ago,
|
||||
last_activity_at: Time.current
|
||||
)
|
||||
end
|
||||
|
||||
it 'shows the last_activity_at attribute as the update date', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/514342' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_last_activity.id}") do
|
||||
expect(page).to have_xpath("//time[@datetime='#{project_with_last_activity.last_activity_at.iso8601}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when last_activity_at is missing', time_travel_to: '2025-01-27T09:44:07Z' do
|
||||
it 'shows the updated_at attribute as the update date' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).to have_xpath("//time[@datetime='#{project.updated_at.iso8601}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows personal projects on personal projects tab' do
|
||||
visit personal_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(personal_project.name)
|
||||
end
|
||||
|
||||
it 'sorts projects by most stars when sorting by most stars' do
|
||||
visit personal_dashboard_projects_path(sort: :stars_desc)
|
||||
wait_for_requests
|
||||
|
||||
expect(first('[data-testid*="projects-list-item"]')).to have_content(personal_project_with_stars.title)
|
||||
end
|
||||
|
||||
context 'when on Member projects tab' do
|
||||
it 'shows all projects you are a member of' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).to have_content(personal_project.name)
|
||||
expect(page).to have_content(personal_project_with_stars.name)
|
||||
expect(find('a[aria-selected="true"]')).to have_content('3')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on Starred projects tab' do
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects
|
||||
|
||||
it 'shows the empty state when there are no starred projects' do
|
||||
visit(starred_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_text("You haven't starred any projects yet.")
|
||||
end
|
||||
|
||||
it 'shows only starred projects' do
|
||||
user.toggle_star(project2)
|
||||
|
||||
visit(starred_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
expect(find('a[aria-selected="true"]')).to have_content('1')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a pipeline' do
|
||||
it 'shows that the last pipeline passed' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).to have_css("[data-testid='ci-icon']")
|
||||
expect(page).to have_css('[data-testid="status_success_borderless-icon"]')
|
||||
expect(page).to have_link('Status: Passed')
|
||||
end
|
||||
end
|
||||
|
||||
context 'guest user of project and project has private pipelines' do
|
||||
let_it_be(:guest_user) { create(:user) }
|
||||
let_it_be(:project_with_private_pipelines) { create(:project, namespace: user.namespace, public_builds: false) }
|
||||
|
||||
before_all do
|
||||
project_with_private_pipelines.add_guest(guest_user)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(guest_user)
|
||||
end
|
||||
|
||||
it 'does not show the pipeline status' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_private_pipelines.id}") do
|
||||
expect(page).not_to have_css("[data-testid='ci-icon']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when last_pipeline is missing" do
|
||||
it 'does not show the pipeline status' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{personal_project.id}") do
|
||||
expect(page).not_to have_css("[data-testid='ci-icon']")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project has topics' do
|
||||
let_it_be(:project_with_topics) { create(:project, namespace: user.namespace, topic_list: 'topic1') }
|
||||
|
||||
it 'shows project topics' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project_with_topics.id}") do
|
||||
expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project does not have topics' do
|
||||
it 'does not show project topics' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
within_testid("projects-list-item-#{project.id}") do
|
||||
expect(page).not_to have_selector('[data-testid="project-topics"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'last push widget', :use_clean_rails_memory_store_caching do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
event = create(:push_event, project: project, author: user)
|
||||
|
||||
create(:push_event_payload, event: event, ref: 'feature', action: :created)
|
||||
|
||||
Users::LastPushEventService.new(user).cache_last_push_event(event)
|
||||
|
||||
it 'does not mount JS app' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_content('Projects')
|
||||
expect(page).not_to have_content('Active tab')
|
||||
end
|
||||
|
||||
it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do
|
||||
before do
|
||||
visit dashboard_projects_path
|
||||
it 'shows "Create merge request" button' do
|
||||
expect(page).to have_content 'You pushed to feature'
|
||||
|
||||
within('#content-body') do
|
||||
find_link('Create merge request', visible: false).click
|
||||
end
|
||||
|
||||
expect(page).to have_selector('.merge-request-form')
|
||||
expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true
|
||||
expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s
|
||||
expect(page).to have_content "From feature into master"
|
||||
end
|
||||
end
|
||||
|
||||
it 'avoids an N+1 query in dashboard index' do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
control = ActiveRecord::QueryRecorder.new do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects
|
||||
new_project = create(:project, :repository, name: 'new project')
|
||||
create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha)
|
||||
new_project.add_developer(user)
|
||||
|
||||
it 'links to the "Explore projects" page' do
|
||||
visit dashboard_projects_path
|
||||
# There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037
|
||||
# - User#max_member_access_for_project_ids
|
||||
# - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline
|
||||
# - Ci::Pipeline#detailed_status
|
||||
|
||||
expect(page).to have_link("Explore projects", href: starred_explore_projects_path)
|
||||
end
|
||||
|
||||
context 'when user has access to the project' do
|
||||
it 'shows role badge' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
within_testid('user-access-role') do
|
||||
expect(page).to have_content('Developer')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when role changes', :use_clean_rails_memory_store_fragment_caching do
|
||||
it 'displays the right role' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
within_testid('user-access-role') do
|
||||
expect(page).to have_content('Developer')
|
||||
end
|
||||
|
||||
project.members.last.update!(access_level: 40)
|
||||
|
||||
visit dashboard_projects_path
|
||||
|
||||
within_testid('user-access-role') do
|
||||
expect(page).to have_content('Maintainer')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when last_activity_at and update_at are present' do
|
||||
it 'shows the last_activity_at attribute as the update date' do
|
||||
project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.zone.now)
|
||||
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_xpath("//time[@datetime='#{project.last_activity_at.getutc.iso8601}']")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when last_activity_at is missing' do
|
||||
it 'shows the updated_at attribute as the update date' do
|
||||
project.update!(last_activity_at: nil)
|
||||
project.touch
|
||||
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_xpath("//time[@datetime='#{project.updated_at.getutc.iso8601}']")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on Your projects tab' do
|
||||
it 'shows all projects by default' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
end
|
||||
|
||||
it 'shows personal projects on personal projects tab' do
|
||||
project3 = create(:project, namespace: user.namespace)
|
||||
|
||||
visit dashboard_projects_path
|
||||
|
||||
click_link 'Personal'
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(project3.name)
|
||||
end
|
||||
|
||||
it 'sorts projects by most stars when sorting by most stars' do
|
||||
project_with_most_stars = create(:project, namespace: user.namespace, star_count: 10)
|
||||
|
||||
visit dashboard_projects_path(sort: :stars_desc)
|
||||
|
||||
expect(first('.project-row')).to have_content(project_with_most_stars.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on Starred projects tab' do
|
||||
it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects
|
||||
|
||||
it 'shows the empty state when there are no starred projects' do
|
||||
visit(starred_dashboard_projects_path)
|
||||
|
||||
expect(page).to have_text(s_("StarredProjectsEmptyState|You don't have starred projects yet."))
|
||||
end
|
||||
|
||||
it 'shows only starred projects' do
|
||||
user.toggle_star(project2)
|
||||
|
||||
visit(starred_dashboard_projects_path)
|
||||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
expect(find('.gl-tabs-nav li:nth-child(2) .badge-pill')).to have_content(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a pipeline', :clean_gitlab_redis_shared_state do
|
||||
let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) }
|
||||
|
||||
before do
|
||||
# Since the cache isn't updated when a new pipeline is created
|
||||
# we need the pipeline to advance in the pipeline since the cache was created
|
||||
# by visiting the login page.
|
||||
pipeline.succeed
|
||||
end
|
||||
|
||||
it 'shows that the last pipeline passed' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
within_testid('project_controls') do
|
||||
expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']")
|
||||
expect(page).to have_css("[data-testid='ci-icon']")
|
||||
expect(page).to have_css('[data-testid="status_success_borderless-icon"]')
|
||||
expect(page).to have_link('Pipeline: passed')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'hidden pipeline status' do
|
||||
it 'does not show the pipeline status' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
within_testid('project_controls') do
|
||||
expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']")
|
||||
expect(page).not_to have_css("[data-testid='ci-icon']")
|
||||
expect(page).not_to have_css('[data-testid="status_success_borderless-icon"]')
|
||||
expect(page).not_to have_link('Pipeline: passed')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'guest user of project and project has private pipelines' do
|
||||
let(:guest_user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.update!(public_builds: false)
|
||||
project.add_guest(guest_user)
|
||||
sign_in(guest_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'hidden pipeline status'
|
||||
end
|
||||
|
||||
context "when last_pipeline is missing" do
|
||||
before do
|
||||
project.last_pipeline.delete
|
||||
end
|
||||
|
||||
it_behaves_like 'hidden pipeline status'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with topics' do
|
||||
context 'when project has topics' do
|
||||
before do
|
||||
project.update_attribute(:topic_list, 'topic1')
|
||||
end
|
||||
|
||||
it 'shows project topics if exist' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_selector('[data-testid="project_topic_list"]')
|
||||
expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project does not have topics' do
|
||||
it 'does not show project topics' do
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).not_to have_selector('[data-testid="project_topic_list"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'last push widget', :use_clean_rails_memory_store_caching do
|
||||
before do
|
||||
event = create(:push_event, project: project, author: user)
|
||||
|
||||
create(:push_event_payload, event: event, ref: 'feature', action: :created)
|
||||
|
||||
Users::LastPushEventService.new(user).cache_last_push_event(event)
|
||||
|
||||
visit dashboard_projects_path
|
||||
end
|
||||
|
||||
it 'shows "Create merge request" button' do
|
||||
expect(page).to have_content 'You pushed to feature'
|
||||
|
||||
within('#content-body') do
|
||||
find_link('Create merge request', visible: false).click
|
||||
end
|
||||
|
||||
expect(page).to have_selector('.merge-request-form')
|
||||
expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true
|
||||
expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s
|
||||
expect(page).to have_content "From feature into master"
|
||||
end
|
||||
end
|
||||
|
||||
it 'avoids an N+1 query in dashboard index' do
|
||||
create(:ci_pipeline, :with_job, status: :success, project: project, ref: project.default_branch, sha: project.commit.sha)
|
||||
visit dashboard_projects_path
|
||||
|
||||
control = ActiveRecord::QueryRecorder.new { visit dashboard_projects_path }
|
||||
|
||||
new_project = create(:project, :repository, name: 'new project')
|
||||
create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha)
|
||||
new_project.add_developer(user)
|
||||
|
||||
ActiveRecord::QueryRecorder.new { visit dashboard_projects_path }.count
|
||||
|
||||
# There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037
|
||||
# - User#max_member_access_for_project_ids
|
||||
# - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline
|
||||
# - Ci::Pipeline#detailed_status
|
||||
|
||||
expect { visit dashboard_projects_path }.not_to exceed_query_limit(control).with_threshold(4)
|
||||
end
|
||||
expect do
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
end.not_to exceed_query_limit(control).with_threshold(4)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,39 +48,18 @@ RSpec.describe 'Dashboard > User filters projects', :js, feature_category: :grou
|
|||
end
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is enabled' do
|
||||
it 'searches for projects' do
|
||||
project2.add_developer(user)
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
it 'searches for projects' do
|
||||
project2.add_developer(user)
|
||||
visit member_dashboard_projects_path
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
|
||||
search(project.name)
|
||||
search(project.name)
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).not_to have_content(project2.name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
|
||||
it 'searches for projects' do
|
||||
project2.add_developer(user)
|
||||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
|
||||
search(project.name)
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).not_to have_content(project2.name)
|
||||
end
|
||||
expect(page).to have_content(project.name)
|
||||
expect(page).not_to have_content(project2.name)
|
||||
end
|
||||
|
||||
def search(term)
|
||||
|
|
|
|||
|
|
@ -26,88 +26,37 @@ RSpec.describe 'User visits the profile preferences page', :js, feature_category
|
|||
end
|
||||
end
|
||||
|
||||
context 'when your_work_projects_vue feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: true)
|
||||
visit(profile_preferences_path)
|
||||
end
|
||||
it 'sets default dashboard preference to Your Contributed Projects (default)' do
|
||||
visit(profile_preferences_path)
|
||||
|
||||
it 'sets default dashboard preference to Your Contributed Projects (default)' do
|
||||
expect(page).to have_button('Your Contributed Projects (default)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when your_work_projects_vue feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
visit(profile_preferences_path)
|
||||
end
|
||||
|
||||
it 'sets default dashboard preference to Your Projects (default)' do
|
||||
expect(page).to have_button('Your Projects (default)')
|
||||
end
|
||||
expect(page).to have_button('Your Contributed Projects (default)')
|
||||
end
|
||||
|
||||
describe 'User changes their default dashboard', :js do
|
||||
context 'when feature flag your_work_projects_vue is enabled' do
|
||||
before do
|
||||
visit(profile_preferences_path)
|
||||
end
|
||||
|
||||
it 'creates a flash message' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
|
||||
it 'updates their preference' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
find('[data-track-label="gitlab_logo_link"]').click
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content("You haven't starred any projects yet.")
|
||||
expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true
|
||||
end
|
||||
before do
|
||||
visit(profile_preferences_path)
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
visit(profile_preferences_path)
|
||||
end
|
||||
it 'creates a flash message' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
|
||||
it 'creates a flash message' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Projects', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
wait_for_requests
|
||||
|
||||
wait_for_requests
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
|
||||
expect_preferences_saved_message
|
||||
end
|
||||
it 'updates their preference' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
|
||||
it 'updates their preference' do
|
||||
select_from_listbox 'Starred Projects', from: 'Your Projects', exact_item_text: true
|
||||
click_button 'Save changes'
|
||||
wait_for_requests
|
||||
|
||||
wait_for_requests
|
||||
find('[data-track-label="gitlab_logo_link"]').click
|
||||
wait_for_requests
|
||||
|
||||
find('[data-track-label="gitlab_logo_link"]').click
|
||||
|
||||
expect(page).to have_content("You don't have starred projects yet")
|
||||
expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true
|
||||
|
||||
find('.shortcuts-activity').click
|
||||
|
||||
expect(page).not_to have_content("You don't have starred projects yet")
|
||||
expect(page).to have_current_path dashboard_projects_path, ignore_query: true
|
||||
end
|
||||
expect(page).to have_content("You haven't starred any projects yet.")
|
||||
expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Projects > Show > User interacts with project stars', feature_category: :groups_and_projects do
|
||||
RSpec.describe 'Projects > Show > User interacts with project stars', :js, feature_category: :groups_and_projects do
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
|
||||
context 'when user is signed in', :js do
|
||||
|
|
@ -32,62 +32,32 @@ RSpec.describe 'Projects > Show > User interacts with project stars', feature_ca
|
|||
expect(page).to have_css('.star-count', text: 0)
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is enabled', :js do
|
||||
it 'validates starring a project' do
|
||||
project.add_owner(user)
|
||||
it 'validates starring a project' do
|
||||
project.add_owner(user)
|
||||
|
||||
star_project
|
||||
star_project
|
||||
|
||||
visit(member_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
visit(member_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(find_link('Stars')).to have_content('1')
|
||||
end
|
||||
|
||||
it 'validates un-starring a project' do
|
||||
project.add_owner(user)
|
||||
|
||||
star_project
|
||||
|
||||
unstar_project
|
||||
|
||||
visit(member_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(find_link('Stars')).to have_content('0')
|
||||
end
|
||||
expect(find_link('Stars')).to have_content('1')
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
it 'validates un-starring a project' do
|
||||
project.add_owner(user)
|
||||
|
||||
it 'validates starring a project' do
|
||||
project.add_owner(user)
|
||||
star_project
|
||||
|
||||
star_project
|
||||
unstar_project
|
||||
|
||||
visit(dashboard_projects_path)
|
||||
visit(member_dashboard_projects_path)
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css('.stars', text: 1)
|
||||
end
|
||||
|
||||
it 'validates un-starring a project' do
|
||||
project.add_owner(user)
|
||||
|
||||
star_project
|
||||
|
||||
unstar_project
|
||||
|
||||
visit(dashboard_projects_path)
|
||||
|
||||
expect(page).to have_css('.stars', text: 0)
|
||||
end
|
||||
expect(find_link('Stars')).to have_content('0')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not signed in', :js do
|
||||
context 'when user is not signed in' do
|
||||
before do
|
||||
visit(project_path(project))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,34 +10,10 @@ RSpec.describe PreferencesHelper, feature_category: :shared do
|
|||
end
|
||||
|
||||
describe '#dashboard_value' do
|
||||
context 'when feature flag your_work_projects_vue is enabled' do
|
||||
it 'returns dashboard of current user' do
|
||||
allow(user).to receive(:dashboard).and_return('your_activity')
|
||||
it 'returns dashboard of current user' do
|
||||
allow(user).to receive(:dashboard).and_return('your_activity')
|
||||
|
||||
expect(helper.dashboard_value).to eq('your_activity')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
|
||||
context 'when dashboard of current user is member_projects' do
|
||||
it 'returns projects' do
|
||||
allow(user).to receive(:dashboard).and_return('member_projects')
|
||||
|
||||
expect(helper.dashboard_value).to eq('projects')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dashboard of current user is not member_projects' do
|
||||
it 'returns projects' do
|
||||
allow(user).to receive(:dashboard).and_return('your_activity')
|
||||
|
||||
expect(helper.dashboard_value).to eq('your_activity')
|
||||
end
|
||||
end
|
||||
expect(helper.dashboard_value).to eq('your_activity')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -58,43 +34,20 @@ RSpec.describe PreferencesHelper, feature_category: :shared do
|
|||
expect { helper.dashboard_choices }.to raise_error(KeyError)
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is enabled' do
|
||||
it 'returns expected options' do
|
||||
expect(helper.dashboard_choices).to match_array [
|
||||
{ text: "Your Contributed Projects (default)", value: 'projects' },
|
||||
{ text: "Starred Projects", value: 'stars' },
|
||||
{ text: "Member Projects", value: 'member_projects' },
|
||||
{ text: "Your Activity", value: 'your_activity' },
|
||||
{ text: "Your Projects' Activity", value: 'project_activity' },
|
||||
{ text: "Starred Projects' Activity", value: 'starred_project_activity' },
|
||||
{ text: "Followed Users' Activity", value: 'followed_user_activity' },
|
||||
{ text: "Your Groups", value: 'groups' },
|
||||
{ text: "Your To-Do List", value: 'todos' },
|
||||
{ text: "Assigned issues", value: 'issues' },
|
||||
{ text: "Assigned merge requests", value: 'merge_requests' }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
|
||||
it 'returns expected options' do
|
||||
expect(helper.dashboard_choices).to match_array [
|
||||
{ text: "Your Projects (default)", value: 'projects' },
|
||||
{ text: "Starred Projects", value: 'stars' },
|
||||
{ text: "Your Activity", value: 'your_activity' },
|
||||
{ text: "Your Projects' Activity", value: 'project_activity' },
|
||||
{ text: "Starred Projects' Activity", value: 'starred_project_activity' },
|
||||
{ text: "Followed Users' Activity", value: 'followed_user_activity' },
|
||||
{ text: "Your Groups", value: 'groups' },
|
||||
{ text: "Your To-Do List", value: 'todos' },
|
||||
{ text: "Assigned issues", value: 'issues' },
|
||||
{ text: "Assigned merge requests", value: 'merge_requests' }
|
||||
]
|
||||
end
|
||||
it 'returns expected options' do
|
||||
expect(helper.dashboard_choices).to match_array [
|
||||
{ text: "Your Contributed Projects (default)", value: 'projects' },
|
||||
{ text: "Starred Projects", value: 'stars' },
|
||||
{ text: "Member Projects", value: 'member_projects' },
|
||||
{ text: "Your Activity", value: 'your_activity' },
|
||||
{ text: "Your Projects' Activity", value: 'project_activity' },
|
||||
{ text: "Starred Projects' Activity", value: 'starred_project_activity' },
|
||||
{ text: "Followed Users' Activity", value: 'followed_user_activity' },
|
||||
{ text: "Your Groups", value: 'groups' },
|
||||
{ text: "Your To-Do List", value: 'todos' },
|
||||
{ text: "Assigned issues", value: 'issues' },
|
||||
{ text: "Assigned merge requests", value: 'merge_requests' }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -109,41 +109,41 @@ RSpec.describe Gitlab::GithubImport::MarkdownText, feature_category: :importers
|
|||
end
|
||||
end
|
||||
|
||||
describe '#to_s' do
|
||||
describe '#perform' do
|
||||
it 'returns the text when the author was found' do
|
||||
author = double(:author, login: 'Alice')
|
||||
text = described_class.new('Hello', author, true)
|
||||
|
||||
expect(text.to_s).to eq('Hello')
|
||||
expect(text.perform).to eq('Hello')
|
||||
end
|
||||
|
||||
it 'returns the text when the author has no login' do
|
||||
author = double(:author, login: nil)
|
||||
text = described_class.new('Hello', author, true)
|
||||
|
||||
expect(text.to_s).to eq('Hello')
|
||||
expect(text.perform).to eq('Hello')
|
||||
end
|
||||
|
||||
it 'returns the text with an extra header when the author was not found' do
|
||||
author = double(:author, login: 'Alice')
|
||||
text = described_class.new('Hello', author)
|
||||
|
||||
expect(text.to_s).to eq("*Created by: Alice*\n\nHello")
|
||||
expect(text.perform).to eq("*Created by: Alice*\n\nHello")
|
||||
end
|
||||
|
||||
it 'cleans invalid chars' do
|
||||
author = double(:author, login: 'Alice')
|
||||
text = described_class.format("\u0000Hello", author)
|
||||
|
||||
expect(text.to_s).to eq("*Created by: Alice*\n\nHello")
|
||||
expect(text).to eq("*Created by: Alice*\n\nHello")
|
||||
end
|
||||
|
||||
context "when the to_s is called" do
|
||||
context "when the perform is called" do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let(:text) { "I said to @sam_allen\0 the code" }
|
||||
let(:instance) { described_class.new(text, project:) }
|
||||
|
||||
subject(:format) { instance.to_s }
|
||||
subject(:format) { instance.perform }
|
||||
|
||||
it 'calls wrap_mentions_in_backticks and convert_ref_links method as a cleaning step' do
|
||||
expect(instance).to receive(:wrap_mentions_in_backticks)
|
||||
|
|
|
|||
|
|
@ -11,98 +11,43 @@ RSpec.describe 'dashboard/projects/index.html.haml', feature_category: :groups_a
|
|||
allow(view).to receive(:time_ago_with_tooltip)
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is enabled' do
|
||||
context 'when show_dashboard_projects_welcome_page? is true' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: true)
|
||||
allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(true)
|
||||
render
|
||||
end
|
||||
|
||||
context 'when show_dashboard_projects_welcome_page? is true' do
|
||||
before do
|
||||
allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(true)
|
||||
render
|
||||
end
|
||||
|
||||
it 'renders the zero_authorized_projects partial and not the projects Vue app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
it 'does not render the "New project" button' do
|
||||
expect(rendered).not_to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does not render the "Explore projects" button' do
|
||||
expect(rendered).not_to have_link('Explore projects')
|
||||
end
|
||||
it 'renders the zero_authorized_projects partial and not the projects Vue app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
context 'when show_dashboard_projects_welcome_page? is false' do
|
||||
before do
|
||||
allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(false)
|
||||
render
|
||||
end
|
||||
it 'does not render the "New project" button' do
|
||||
expect(rendered).not_to have_link('New project')
|
||||
end
|
||||
|
||||
it 'renders the projects Vue app and not the zero_authorized_projects partial' do
|
||||
expect(rendered).to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
it 'does render the "New project" button' do
|
||||
expect(rendered).to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does render the "Explore projects" button' do
|
||||
expect(rendered).to have_link('Explore projects')
|
||||
end
|
||||
it 'does not render the "Explore projects" button' do
|
||||
expect(rendered).not_to have_link('Explore projects')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is disabled' do
|
||||
context 'when show_dashboard_projects_welcome_page? is false' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(false)
|
||||
render
|
||||
end
|
||||
|
||||
context 'when projects exist' do
|
||||
before do
|
||||
assign(:projects, [build(:project, name: 'awesome stuff')])
|
||||
allow(view).to receive(:show_projects?).and_return(true)
|
||||
render
|
||||
end
|
||||
|
||||
it 'shows the project the user is a member of in the list' do
|
||||
expect(rendered).to have_content('awesome stuff')
|
||||
end
|
||||
|
||||
it 'shows the "New project" button' do
|
||||
expect(rendered).to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does not render zero_authorized_projects partial' do
|
||||
expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
it 'does not render #js-your-work-projects-app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
end
|
||||
it 'renders the projects Vue app and not the zero_authorized_projects partial' do
|
||||
expect(rendered).to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
context 'when projects do not exist' do
|
||||
before do
|
||||
allow(view).to receive(:show_projects?).and_return(false)
|
||||
render
|
||||
end
|
||||
it 'does render the "New project" button' do
|
||||
expect(rendered).to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does not show the "New project" button' do
|
||||
expect(rendered).not_to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does render zero_authorized_projects partial' do
|
||||
expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
|
||||
it 'does not render #js-your-work-projects-app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
end
|
||||
it 'does render the "Explore projects" button' do
|
||||
expect(rendered).to have_link('Explore projects')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,95 +6,13 @@ RSpec.describe 'dashboard/projects/shared/_common.html.haml', feature_category:
|
|||
let_it_be(:user) { build(:user) }
|
||||
|
||||
before do
|
||||
view.lookup_context.prefixes = ['dashboard/projects']
|
||||
|
||||
allow(view).to receive(:limited_counter_with_delimiter)
|
||||
allow(view).to receive(:current_user).and_return(user)
|
||||
allow(view).to receive(:time_ago_with_tooltip)
|
||||
allow(view).to receive(:empty_page).and_return('starred_empty_state')
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is enabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: true)
|
||||
end
|
||||
it 'renders #js-your-work-projects-app and not legacy project list' do
|
||||
render
|
||||
|
||||
context 'when projects exist' do
|
||||
before do
|
||||
assign(:projects, [build(:project, name: 'awesome stuff')])
|
||||
allow(view).to receive(:any_projects?).and_return(true)
|
||||
render
|
||||
end
|
||||
|
||||
it 'renders #js-your-work-projects-app and not legacy project list' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).not_to render_template('dashboard/projects/_projects')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when projects do not exist' do
|
||||
before do
|
||||
allow(view).to receive(:any_projects?).and_return(false)
|
||||
render
|
||||
end
|
||||
|
||||
it 'renders #js-your-work-projects-app and does not render HAML empty state' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature :your_work_projects_vue is disabled' do
|
||||
before do
|
||||
stub_feature_flags(your_work_projects_vue: false)
|
||||
end
|
||||
|
||||
context 'when projects exist' do
|
||||
before do
|
||||
assign(:projects, [build(:project, name: 'awesome stuff')])
|
||||
allow(view).to receive(:any_projects?).and_return(true)
|
||||
render
|
||||
end
|
||||
|
||||
it 'shows the project the user is a member of in the list' do
|
||||
expect(rendered).to have_content('awesome stuff')
|
||||
end
|
||||
|
||||
it 'shows the "New project" button' do
|
||||
expect(rendered).to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does not render starred_empty_state partial' do
|
||||
expect(rendered).not_to render_template('dashboard/projects/_starred_empty_state')
|
||||
end
|
||||
|
||||
it 'does not render #js-your-work-projects-app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when projects do not exist' do
|
||||
before do
|
||||
allow(view).to receive(:any_projects?).and_return(false)
|
||||
render
|
||||
end
|
||||
|
||||
it 'does show the "New project" button' do
|
||||
expect(rendered).to have_link('New project')
|
||||
end
|
||||
|
||||
it 'does render starred_empty_state partial' do
|
||||
expect(rendered).to render_template('dashboard/projects/_starred_empty_state')
|
||||
end
|
||||
|
||||
it 'does not render #js-your-work-projects-app' do
|
||||
expect(rendered).not_to have_selector('#js-your-work-projects-app')
|
||||
end
|
||||
end
|
||||
expect(rendered).to have_selector('#js-your-work-projects-app')
|
||||
expect(rendered).not_to render_template('dashboard/projects/_projects')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue