mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-15 21:39:00 +00:00
Update from merge request
This commit is contained in:
@ -107,6 +107,13 @@ module Organizations
|
||||
Gitlab::UrlBuilder.build(self, only_path: only_path)
|
||||
end
|
||||
|
||||
# For example:
|
||||
# scoped path - /o/my-org/my-group/my-project
|
||||
# unscoped path - /my-group/my-project
|
||||
def scoped_paths?
|
||||
Feature.enabled?(:organization_scoped_paths, self) && !default?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# The visibility must be broader than the visibility of any contained root groups.
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: organization_scoped_paths
|
||||
description: Derisk rollout of Organization scoped URL paths and helpers
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/553914
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/197425
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/556359
|
||||
milestone: '18.3'
|
||||
group: group::organizations
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
92
config/initializers/organization_url_helpers.rb
Normal file
92
config/initializers/organization_url_helpers.rb
Normal file
@ -0,0 +1,92 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Provides organization-aware url helpers by automatically switching between
|
||||
# organization-scoped routes (/o/:organization_path/...) and global routes
|
||||
# based on the current organization context.
|
||||
class OrganizationUrlHelpers
|
||||
ORGANIZATION_PATH_PATTERN = '/o/:organization_path'
|
||||
ORGANIZATION_PATH_REGEX = /(?<=^|_)organizations?_/
|
||||
PATH_SUFFIX = '_path'
|
||||
URL_SUFFIX = '_url'
|
||||
|
||||
mattr_accessor :already_installed, default: false
|
||||
|
||||
def self.install
|
||||
return if already_installed
|
||||
|
||||
route_pairs = find_route_pairs
|
||||
override_module = build_override_module(route_pairs)
|
||||
Rails.application.routes.url_helpers.prepend(override_module)
|
||||
|
||||
self.already_installed = true
|
||||
end
|
||||
|
||||
def self.find_route_pairs
|
||||
all_routes = Rails.application.routes.routes
|
||||
org_routes, global_routes = all_routes.partition { |route| organization_route?(route) }
|
||||
build_route_pairs(org_routes, global_routes)
|
||||
end
|
||||
|
||||
# Route name represents an Organization route.
|
||||
def self.organization_route?(route)
|
||||
route.path.spec.to_s.include?(ORGANIZATION_PATH_PATTERN)
|
||||
end
|
||||
|
||||
# Build a Hash of global route => Organization route names.
|
||||
def self.build_route_pairs(organization_routes, global_routes)
|
||||
org_route_names = organization_routes.map(&:name)
|
||||
global_route_names = global_routes.map(&:name)
|
||||
|
||||
# Global route => Organization route
|
||||
route_pairs = {}
|
||||
|
||||
org_route_names.each do |org_route_name|
|
||||
global_route_name = extract_global_route_name(org_route_name)
|
||||
next unless global_route_names.include?(global_route_name)
|
||||
|
||||
route_pairs[global_route_name] = org_route_name
|
||||
end
|
||||
|
||||
route_pairs
|
||||
end
|
||||
|
||||
# Map organization named route to global route.
|
||||
def self.extract_global_route_name(org_route_name)
|
||||
return if org_route_name.nil?
|
||||
|
||||
# Handle organization patterns with proper underscore preservation
|
||||
org_route_name.gsub(ORGANIZATION_PATH_REGEX, '')
|
||||
end
|
||||
|
||||
# Build a module that overrides URL helpers with organization-aware versions
|
||||
def self.build_override_module(route_pairs)
|
||||
Module.new do
|
||||
route_pairs.each do |global_route, org_route|
|
||||
[PATH_SUFFIX, URL_SUFFIX].each do |suffix|
|
||||
method_name = "#{global_route}#{suffix}"
|
||||
org_method_name = "#{org_route}#{suffix}"
|
||||
|
||||
define_method(method_name) do |*args, **kwargs|
|
||||
# rubocop:disable Gitlab/AvoidCurrentOrganization -- Current organization not available earlier.
|
||||
org_scoped_path = Current.organization_assigned &&
|
||||
!Current.organization.nil? &&
|
||||
Current.organization.scoped_paths?
|
||||
|
||||
if org_scoped_path
|
||||
# Call the Organization helper method
|
||||
send(org_method_name, *args, organization_path: Current.organization.path, **kwargs)
|
||||
else
|
||||
# Call the original helper method
|
||||
super(*args, **kwargs)
|
||||
end
|
||||
# rubocop:enable Gitlab/AvoidCurrentOrganization
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rails.application.config.after_routes_loaded do
|
||||
OrganizationUrlHelpers.install
|
||||
end
|
@ -86,6 +86,8 @@ InitializerConnections.raise_if_new_database_connection do
|
||||
# Terraform service discovery
|
||||
get '.well-known/terraform.json' => 'terraform/services#index', as: :terraform_services
|
||||
|
||||
draw :organizations
|
||||
|
||||
# Begin of the /-/ scope.
|
||||
# Use this scope for all new global routes.
|
||||
scope path: '-' do
|
||||
@ -170,7 +172,6 @@ InitializerConnections.raise_if_new_database_connection do
|
||||
|
||||
draw :operations
|
||||
draw :jira_connect
|
||||
draw :organizations
|
||||
|
||||
Gitlab.ee do
|
||||
draw 'remote_development/resources'
|
||||
|
@ -1,45 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
resources(:organizations, only: [:show, :index, :new], param: :organization_path, module: :organizations) do
|
||||
collection do
|
||||
post :preview_markdown
|
||||
end
|
||||
scope(
|
||||
path: '/o/:organization_path',
|
||||
constraints: { organization_path: Gitlab::PathRegex.organization_route_regex },
|
||||
as: :organization
|
||||
) do
|
||||
resources :projects, only: [:new, :create]
|
||||
end
|
||||
|
||||
member do
|
||||
get :activity
|
||||
get :groups_and_projects
|
||||
get :users
|
||||
|
||||
resource :settings, only: [], as: :settings_organization do
|
||||
get :general
|
||||
scope path: '-' do
|
||||
resources(:organizations, only: [:show, :index, :new], param: :organization_path, module: :organizations) do
|
||||
collection do
|
||||
post :preview_markdown
|
||||
end
|
||||
|
||||
resource :groups, only: [:new, :create, :destroy], as: :groups_organization
|
||||
member do
|
||||
get :activity
|
||||
get :groups_and_projects
|
||||
get :users
|
||||
|
||||
scope(
|
||||
path: 'groups/*id',
|
||||
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||
) do
|
||||
resource(
|
||||
:groups,
|
||||
path: '/',
|
||||
only: [:edit],
|
||||
as: :groups_organization
|
||||
)
|
||||
end
|
||||
resource :settings, only: [], as: :settings_organization do
|
||||
get :general
|
||||
end
|
||||
|
||||
scope(
|
||||
path: 'projects/*namespace_id',
|
||||
as: :namespace,
|
||||
constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||
) do
|
||||
resources(
|
||||
:projects,
|
||||
path: '/',
|
||||
constraints: { id: Gitlab::PathRegex.project_route_regex },
|
||||
only: [:edit],
|
||||
as: :projects_organization
|
||||
)
|
||||
resource :groups, only: [:new, :create, :destroy], as: :groups_organization
|
||||
|
||||
scope(
|
||||
path: 'groups/*id',
|
||||
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||
) do
|
||||
resource(
|
||||
:groups,
|
||||
path: '/',
|
||||
only: [:edit],
|
||||
as: :groups_organization
|
||||
)
|
||||
end
|
||||
|
||||
scope(
|
||||
path: 'projects/*namespace_id',
|
||||
as: :namespace,
|
||||
constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||
) do
|
||||
resources(
|
||||
:projects,
|
||||
path: '/',
|
||||
constraints: { id: Gitlab::PathRegex.project_route_regex },
|
||||
only: [:edit],
|
||||
as: :projects_organization
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -36,6 +36,7 @@ module Gitlab
|
||||
import
|
||||
jwt
|
||||
login
|
||||
o
|
||||
oauth
|
||||
profile
|
||||
projects
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Group show page', feature_category: :groups_and_projects do
|
||||
RSpec.describe 'Group show page', :with_current_organization, feature_category: :groups_and_projects do
|
||||
include Features::InviteMembersModalHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
@ -91,18 +91,43 @@ RSpec.describe 'Group show page', feature_category: :groups_and_projects do
|
||||
before do
|
||||
group.add_owner(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
subject(:page_content) do
|
||||
visit path
|
||||
page
|
||||
end
|
||||
|
||||
it 'shows `Create subgroup` link' do
|
||||
link = new_group_path(parent_id: group.id, anchor: 'create-group-pane')
|
||||
|
||||
expect(page).to have_link(s_('GroupsEmptyState|Create subgroup'), href: link)
|
||||
expect(page_content).to have_link(s_('GroupsEmptyState|Create subgroup'), href: link)
|
||||
end
|
||||
|
||||
it 'shows `Create project` link' do
|
||||
expect(page)
|
||||
.to have_link(s_('GroupsEmptyState|Create project'), href: new_project_path(namespace_id: group.id))
|
||||
context 'when current Organization does not have scoped paths' do
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(false)
|
||||
end
|
||||
|
||||
it 'shows `Create project` link' do
|
||||
expect(page_content)
|
||||
.to have_link(s_('GroupsEmptyState|Create project'), href: new_project_path(namespace_id: group.id))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current Organization has scoped paths' do
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(true)
|
||||
end
|
||||
|
||||
it 'shows `Create project` link' do
|
||||
expected_path = new_organization_project_path(
|
||||
namespace_id: group.id,
|
||||
organization_path: current_organization.path
|
||||
)
|
||||
expect(page_content)
|
||||
.to have_link(s_('GroupsEmptyState|Create project'), href: expected_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -383,82 +383,220 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns "Create new" menu groups without headers', :use_clean_rails_memory_store_caching do
|
||||
extra_attrs = ->(id) {
|
||||
describe '"Create new" menu groups', :use_clean_rails_memory_store_caching do
|
||||
def menu_item(href:, text:, id:, component: nil)
|
||||
{
|
||||
"data-track-label": id,
|
||||
"data-track-action": "click_link",
|
||||
"data-track-property": "nav_create_menu",
|
||||
"data-testid": 'create_menu_item',
|
||||
"data-qa-create-menu-item": id
|
||||
href: href,
|
||||
text: text,
|
||||
component: component,
|
||||
extraAttrs: {
|
||||
"data-track-label": id,
|
||||
"data-track-action": "click_link",
|
||||
"data-track-property": "nav_create_menu",
|
||||
"data-testid": 'create_menu_item',
|
||||
"data-qa-create-menu-item": id
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
expect(subject[:create_new_menu_groups]).to eq([
|
||||
{
|
||||
name: "",
|
||||
items: [
|
||||
{ href: "/projects/new", text: "New project/repository",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_project") },
|
||||
{ href: "/groups/new", text: "New group",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_group") },
|
||||
{ href: "/-/organizations/new", text: s_('Organization|New organization'),
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_organization") },
|
||||
{ href: "/-/snippets/new", text: "New snippet",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_snippet") }
|
||||
]
|
||||
}
|
||||
])
|
||||
end
|
||||
context 'without headers' do
|
||||
shared_examples '"Create new" menu groups without headers' do
|
||||
it 'returns "Create new" menu groups without headers' do
|
||||
expect(subject[:create_new_menu_groups]).to eq([
|
||||
{
|
||||
name: "",
|
||||
items: expected_menu_item_groups
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns "Create new" menu groups with headers', :use_clean_rails_memory_store_caching do
|
||||
extra_attrs = ->(id) {
|
||||
{
|
||||
"data-track-label": id,
|
||||
"data-track-action": "click_link",
|
||||
"data-track-property": "nav_create_menu",
|
||||
"data-testid": 'create_menu_item',
|
||||
"data-qa-create-menu-item": id
|
||||
}
|
||||
}
|
||||
context 'when current Organization has scoped paths' do
|
||||
let(:expected_menu_item_groups) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/o/#{current_organization.path}/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "general_new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new",
|
||||
text: "New group",
|
||||
id: "general_new_group"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/organizations/new",
|
||||
text: s_('Organization|New organization'),
|
||||
id: "general_new_organization"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/snippets/new",
|
||||
text: "New snippet",
|
||||
id: "general_new_snippet"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
allow(group).to receive(:persisted?).and_return(true)
|
||||
allow(helper).to receive(:can?).and_return(true)
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(true)
|
||||
end
|
||||
|
||||
expect(subject[:create_new_menu_groups]).to contain_exactly(
|
||||
a_hash_including(
|
||||
name: "In this group",
|
||||
items: array_including(
|
||||
{ href: "/projects/new", text: "New project/repository",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("new_project") },
|
||||
{ href: "/groups/new#create-group-pane", text: "New subgroup",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("new_subgroup") },
|
||||
{ href: nil, text: "Invite members",
|
||||
component: 'invite_members',
|
||||
extraAttrs: extra_attrs.call("invite") }
|
||||
)
|
||||
),
|
||||
a_hash_including(
|
||||
name: "In GitLab",
|
||||
items: array_including(
|
||||
{ href: "/projects/new", text: "New project/repository",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_project") },
|
||||
{ href: "/groups/new", text: "New group",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_group") },
|
||||
{ href: "/-/snippets/new", text: "New snippet",
|
||||
component: nil,
|
||||
extraAttrs: extra_attrs.call("general_new_snippet") }
|
||||
)
|
||||
)
|
||||
)
|
||||
include_examples '"Create new" menu groups without headers'
|
||||
end
|
||||
|
||||
context 'when current Organization does not have scoped paths' do
|
||||
let(:expected_menu_item_groups) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "general_new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new",
|
||||
text: "New group",
|
||||
id: "general_new_group"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/organizations/new",
|
||||
text: s_('Organization|New organization'),
|
||||
id: "general_new_organization"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/snippets/new",
|
||||
text: "New snippet",
|
||||
id: "general_new_snippet"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(false)
|
||||
end
|
||||
|
||||
include_examples '"Create new" menu groups without headers'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with headers' do
|
||||
before do
|
||||
allow(group).to receive(:persisted?).and_return(true)
|
||||
allow(helper).to receive(:can?).and_return(true)
|
||||
end
|
||||
|
||||
shared_examples '"Create new" menu groups with headers' do
|
||||
it 'returns "Create new" menu groups with headers' do
|
||||
expect(subject[:create_new_menu_groups]).to contain_exactly(
|
||||
a_hash_including(
|
||||
name: "In this group",
|
||||
items: array_including(*in_this_group_menu_items)
|
||||
),
|
||||
a_hash_including(
|
||||
name: "In GitLab",
|
||||
items: array_including(*in_gitlab_menu_items)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current Organization has scoped paths' do
|
||||
let(:in_this_group_menu_items) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/o/#{current_organization.path}/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new#create-group-pane",
|
||||
text: "New subgroup",
|
||||
id: "new_subgroup"
|
||||
),
|
||||
menu_item(
|
||||
href: nil,
|
||||
text: "Invite members",
|
||||
component: 'invite_members',
|
||||
id: "invite"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
let(:in_gitlab_menu_items) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/o/#{current_organization.path}/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "general_new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new",
|
||||
text: "New group",
|
||||
id: "general_new_group"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/snippets/new",
|
||||
text: "New snippet",
|
||||
id: "general_new_snippet"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(true)
|
||||
end
|
||||
|
||||
include_examples '"Create new" menu groups with headers'
|
||||
end
|
||||
|
||||
context 'when current Organization does not have scoped paths' do
|
||||
let(:in_this_group_menu_items) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new#create-group-pane",
|
||||
text: "New subgroup",
|
||||
id: "new_subgroup"
|
||||
),
|
||||
menu_item(
|
||||
href: nil,
|
||||
text: "Invite members",
|
||||
component: 'invite_members',
|
||||
id: "invite"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
let(:in_gitlab_menu_items) do
|
||||
[
|
||||
menu_item(
|
||||
href: "/projects/new",
|
||||
text: "New project/repository",
|
||||
id: "general_new_project"
|
||||
),
|
||||
menu_item(
|
||||
href: "/groups/new",
|
||||
text: "New group",
|
||||
id: "general_new_group"
|
||||
),
|
||||
menu_item(
|
||||
href: "/-/snippets/new",
|
||||
text: "New snippet",
|
||||
id: "general_new_snippet"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
allow(current_organization).to receive(:scoped_paths?).and_return(false)
|
||||
end
|
||||
|
||||
include_examples '"Create new" menu groups with headers'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'current context' do
|
||||
|
124
spec/initializers/organization_url_helpers_spec.rb
Normal file
124
spec/initializers/organization_url_helpers_spec.rb
Normal file
@ -0,0 +1,124 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe OrganizationUrlHelpers, feature_category: :organization do
|
||||
shared_examples 'organization aware route helper' do
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
let(:helper_url) { public_send :"#{helper}_url" }
|
||||
let(:helper_path) { public_send :"#{helper}_path" }
|
||||
|
||||
let(:expected_global_path) do
|
||||
# Call the method on a fresh url_helpers instance to get the original behavior
|
||||
original_helpers = Rails.application.routes.url_helpers.dup
|
||||
original_helpers.public_send(:"#{helper}_path")
|
||||
end
|
||||
|
||||
let(:expected_global_url) { "http://localhost#{expected_global_path}" }
|
||||
|
||||
context 'when organization context is not present' do
|
||||
before do
|
||||
allow(Current).to receive_messages(
|
||||
organization_assigned: false,
|
||||
organization: nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'automatically routes to global path' do
|
||||
expect(helper_path).to eq(expected_global_path)
|
||||
end
|
||||
|
||||
it 'automatically routes to global url' do
|
||||
expect(helper_url).to eq(expected_global_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization has path scopes' do
|
||||
let(:organization) { create(:organization) }
|
||||
|
||||
let(:organization_helper_url) do
|
||||
public_send :"#{organization_helper}_url", organization_path: organization.path
|
||||
end
|
||||
|
||||
let(:organization_helper_path) do
|
||||
public_send :"#{organization_helper}_path", organization_path: organization.path
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Current).to receive_messages(
|
||||
organization_assigned: true,
|
||||
organization: organization
|
||||
)
|
||||
end
|
||||
|
||||
context 'and they are enabled' do
|
||||
before do
|
||||
allow(organization).to receive(:scoped_paths?).and_return(true)
|
||||
end
|
||||
|
||||
it 'automatically routes to organization scoped path' do
|
||||
expect(helper_path).to eq(organization_helper_path)
|
||||
end
|
||||
|
||||
it 'automatically routes to organization scoped URL' do
|
||||
expect(helper_url).to eq(organization_helper_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and they are disabled' do
|
||||
before do
|
||||
allow(organization).to receive(:scoped_paths?).and_return(false)
|
||||
end
|
||||
|
||||
it 'automatically routes to global path' do
|
||||
expect(helper_path).to eq(expected_global_path)
|
||||
end
|
||||
|
||||
it 'automatically routes to global url' do
|
||||
expect(helper_url).to eq(expected_global_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization context is nil' do
|
||||
before do
|
||||
allow(Current).to receive_messages(
|
||||
organization_assigned: true,
|
||||
organization: nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'automatically routes to global path' do
|
||||
expect(helper_path).to eq(expected_global_path)
|
||||
end
|
||||
|
||||
it 'automatically routes to global URL' do
|
||||
expect(helper_url).to eq(expected_global_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_project_path' do
|
||||
let(:helper) { :new_project }
|
||||
let(:organization_helper) { :new_organization_project }
|
||||
|
||||
it_behaves_like 'organization aware route helper'
|
||||
end
|
||||
|
||||
describe '#projects_path' do
|
||||
let(:helper) { :projects }
|
||||
let(:organization_helper) { :organization_projects }
|
||||
|
||||
it_behaves_like 'organization aware route helper'
|
||||
end
|
||||
|
||||
describe '.install' do
|
||||
it 'only installs once' do
|
||||
# Has already been installed as part of Rails initialization.
|
||||
# Second call should not reinstall
|
||||
expect(Rails.application.routes.url_helpers).not_to receive(:prepend)
|
||||
described_class.install
|
||||
end
|
||||
end
|
||||
end
|
@ -194,7 +194,7 @@ RSpec.describe Gitlab::PathRegex do
|
||||
|
||||
# We ban new items in this list, see https://gitlab.com/gitlab-org/gitlab/-/issues/215362
|
||||
it 'does not allow expansion' do
|
||||
expect(described_class::TOP_LEVEL_ROUTES.size).to eq(38)
|
||||
expect(described_class::TOP_LEVEL_ROUTES.size).to eq(39)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -315,6 +315,10 @@ RSpec.describe Organizations::Organization, type: :model, feature_category: :org
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scoped_paths?' do
|
||||
it { expect(organization.scoped_paths?).to eq(true) }
|
||||
end
|
||||
|
||||
describe '.search' do
|
||||
let_it_be(:other_organization) { create(:organization, name: 'Other') }
|
||||
|
||||
@ -461,5 +465,20 @@ RSpec.describe Organizations::Organization, type: :model, feature_category: :org
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scoped_paths?' do
|
||||
it { expect(default_organization.scoped_paths?).to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Feature flagged #scoped_paths?' do
|
||||
# The FF enabled cases are above.
|
||||
context 'when organization_scoped_paths feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(organization_scoped_paths: false)
|
||||
end
|
||||
|
||||
it { expect(organization.scoped_paths?).to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
19
spec/routing/organizations_routing_spec.rb
Normal file
19
spec/routing/organizations_routing_spec.rb
Normal file
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Organization routes', feature_category: :organization do
|
||||
it 'ensures organization_path is constrained' do
|
||||
expect(get('/o/admin/projects/new')).to route_to_route_not_found
|
||||
end
|
||||
|
||||
describe 'projects' do
|
||||
it "to #new" do
|
||||
expect(get("/o/my-org/projects/new")).to route_to('projects#new', organization_path: 'my-org')
|
||||
end
|
||||
|
||||
it "to #create" do
|
||||
expect(post("/o/my-org/projects")).to route_to('projects#create', organization_path: 'my-org')
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user