Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-03-11 03:13:16 +00:00
parent 958a674e2c
commit cceae0fdcd
24 changed files with 369 additions and 1287 deletions

View File

@ -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

View File

@ -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'

View File

@ -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"),

View File

@ -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 } }

View File

@ -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 } }

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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 >}}

View File

@ -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

View File

@ -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

View File

@ -68099,9 +68099,6 @@ msgstr ""
msgid "Your Personal Access Token was revoked"
msgstr ""
msgid "Your Projects (default)"
msgstr ""
msgid "Your Projects' Activity"
msgstr ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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