Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b7ff336e24
commit
8ef0366928
19 changed files with 175 additions and 77 deletions
|
@ -239,7 +239,7 @@ export default {
|
||||||
data-testid="count-loading-icon"
|
data-testid="count-loading-icon"
|
||||||
/>
|
/>
|
||||||
<span v-if="showingAllItems">{{ showingAllItemsText }}</span>
|
<span v-if="showingAllItems">{{ showingAllItemsText }}</span>
|
||||||
<gl-intersection-observer v-else @update="onReachingListBottom">
|
<gl-intersection-observer v-else @appear="onReachingListBottom">
|
||||||
<span>{{ paginatedIssueText }}</span>
|
<span>{{ paginatedIssueText }}</span>
|
||||||
</gl-intersection-observer>
|
</gl-intersection-observer>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlNav, GlNavItemDropdown, GlDropdownForm, GlTooltip } from '@gitlab/ui';
|
import { GlNav, GlNavItemDropdown, GlDropdownForm } from '@gitlab/ui';
|
||||||
import { s__ } from '~/locale';
|
|
||||||
import TopNavDropdownMenu from './top_nav_dropdown_menu.vue';
|
import TopNavDropdownMenu from './top_nav_dropdown_menu.vue';
|
||||||
|
|
||||||
const TOOLTIP = s__('TopNav|Switch to...');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GlNav,
|
GlNav,
|
||||||
GlNavItemDropdown,
|
GlNavItemDropdown,
|
||||||
GlDropdownForm,
|
GlDropdownForm,
|
||||||
GlTooltip,
|
|
||||||
TopNavDropdownMenu,
|
TopNavDropdownMenu,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -19,15 +15,6 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
findTooltipTarget() {
|
|
||||||
// ### Why use a target function instead of `v-gl-tooltip`?
|
|
||||||
// To get the tooltip to align correctly, we need it to target the actual
|
|
||||||
// toggle button which we don't directly render.
|
|
||||||
return this.$el.querySelector('.js-top-nav-dropdown-toggle');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TOOLTIP,
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -48,12 +35,5 @@ export default {
|
||||||
/>
|
/>
|
||||||
</gl-dropdown-form>
|
</gl-dropdown-form>
|
||||||
</gl-nav-item-dropdown>
|
</gl-nav-item-dropdown>
|
||||||
<gl-tooltip
|
|
||||||
boundary="window"
|
|
||||||
:boundary-padding="0"
|
|
||||||
:target="findTooltipTarget"
|
|
||||||
placement="right"
|
|
||||||
:title="$options.TOOLTIP"
|
|
||||||
/>
|
|
||||||
</gl-nav>
|
</gl-nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -24,8 +24,12 @@ class FeatureFlagsFinder
|
||||||
private
|
private
|
||||||
|
|
||||||
def feature_flags
|
def feature_flags
|
||||||
|
if exclude_legacy_flags?
|
||||||
|
project.operations_feature_flags.new_version_only
|
||||||
|
else
|
||||||
project.operations_feature_flags
|
project.operations_feature_flags
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def by_scope(items)
|
def by_scope(items)
|
||||||
case params[:scope]
|
case params[:scope]
|
||||||
|
@ -37,4 +41,9 @@ class FeatureFlagsFinder
|
||||||
items
|
items
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exclude_legacy_flags?
|
||||||
|
Feature.enabled?(:remove_legacy_flags, project, default_enabled: :yaml) &&
|
||||||
|
Feature.disabled?(:remove_legacy_flags_override, project, default_enabled: :yaml)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,6 +49,8 @@ module Operations
|
||||||
scope :enabled, -> { where(active: true) }
|
scope :enabled, -> { where(active: true) }
|
||||||
scope :disabled, -> { where(active: false) }
|
scope :disabled, -> { where(active: false) }
|
||||||
|
|
||||||
|
scope :new_version_only, -> { where(version: :new_version_flag)}
|
||||||
|
|
||||||
enum version: {
|
enum version: {
|
||||||
legacy_flag: 1,
|
legacy_flag: 1,
|
||||||
new_version_flag: 2
|
new_version_flag: 2
|
||||||
|
|
8
config/feature_flags/development/remove_legacy_flags.yml
Normal file
8
config/feature_flags/development/remove_legacy_flags.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: remove_legacy_flags
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62484
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332243
|
||||||
|
milestone: '14.0'
|
||||||
|
type: development
|
||||||
|
group: group::release
|
||||||
|
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: remove_legacy_flags_override
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62484
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332243
|
||||||
|
milestone: '14.0'
|
||||||
|
type: development
|
||||||
|
group: group::release
|
||||||
|
default_enabled: false
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DropDevopsAdoptionNamespaceUniqueness < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_analytics_devops_adoption_segments_on_namespace_id'
|
||||||
|
NEW_INDEX_NAME = 'idx_analytics_devops_adoption_segments_on_namespace_id'
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: NEW_INDEX_NAME
|
||||||
|
remove_concurrent_index_by_name :analytics_devops_adoption_segments, INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# Clean up duplicated records
|
||||||
|
execute "DELETE FROM analytics_devops_adoption_segments WHERE id NOT IN (SELECT MIN(id) FROM analytics_devops_adoption_segments GROUP BY namespace_id)"
|
||||||
|
|
||||||
|
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: INDEX_NAME, unique: true
|
||||||
|
remove_concurrent_index_by_name :analytics_devops_adoption_segments, NEW_INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
1
db/schema_migrations/20210521073920
Normal file
1
db/schema_migrations/20210521073920
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ecef2157c20804acbad9d74df27febcf935f7f36920946fac211f3ef8b419f26
|
|
@ -22273,6 +22273,8 @@ CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_f
|
||||||
|
|
||||||
CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id);
|
CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
|
||||||
|
|
||||||
CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
|
CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
|
||||||
|
|
||||||
CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id);
|
CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id);
|
||||||
|
@ -22459,8 +22461,6 @@ CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_n
|
||||||
|
|
||||||
CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true);
|
CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
|
|
||||||
|
|
||||||
CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id);
|
CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id);
|
||||||
|
|
||||||
CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id);
|
CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id);
|
||||||
|
|
|
@ -97,8 +97,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="querydevopsadoptionsegmentsdirectdescendantsonly"></a>`directDescendantsOnly` | [`Boolean`](#boolean) | Limits segments to direct descendants of specified parent. |
|
| <a id="querydevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by display namespace. |
|
||||||
| <a id="querydevopsadoptionsegmentsparentnamespaceid"></a>`parentNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by ancestor namespace. |
|
|
||||||
|
|
||||||
### `Query.echo`
|
### `Query.echo`
|
||||||
|
|
||||||
|
@ -770,6 +769,7 @@ Input type: `BulkFindOrCreateDevopsAdoptionSegmentsInput`
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||||
|
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
|
||||||
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. |
|
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. |
|
||||||
|
|
||||||
#### Fields
|
#### Fields
|
||||||
|
@ -1105,6 +1105,7 @@ Input type: `CreateDevopsAdoptionSegmentInput`
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
| <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||||
|
| <a id="mutationcreatedevopsadoptionsegmentdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
|
||||||
| <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. |
|
| <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. |
|
||||||
|
|
||||||
#### Fields
|
#### Fields
|
||||||
|
@ -8276,6 +8277,7 @@ Segment.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="devopsadoptionsegmentdisplaynamespace"></a>`displayNamespace` | [`Namespace`](#namespace) | Namespace where data should be displayed. |
|
||||||
| <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. |
|
| <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. |
|
||||||
| <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. |
|
| <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. |
|
||||||
| <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. |
|
| <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. |
|
||||||
|
|
|
@ -213,9 +213,6 @@ actors.
|
||||||
Feature.enabled?(:some_feature, group)
|
Feature.enabled?(:some_feature, group)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Percentage of time** rollout is not a good idea if what you want is to make sure a feature
|
|
||||||
is always on or off to the users. In that case, **Percentage of actors** rollout is a better method.
|
|
||||||
|
|
||||||
Lastly, to verify that the feature is deemed stable in as many cases as possible,
|
Lastly, to verify that the feature is deemed stable in as many cases as possible,
|
||||||
you should fully roll out the feature by enabling the flag **globally** by running:
|
you should fully roll out the feature by enabling the flag **globally** by running:
|
||||||
|
|
||||||
|
@ -226,6 +223,15 @@ you should fully roll out the feature by enabling the flag **globally** by runni
|
||||||
This changes the feature flag state to be **enabled** always, which overrides the
|
This changes the feature flag state to be **enabled** always, which overrides the
|
||||||
existing gates (e.g. `--group=gitlab-org`) in the above processes.
|
existing gates (e.g. `--group=gitlab-org`) in the above processes.
|
||||||
|
|
||||||
|
##### Percentage of actors vs percentage of time rollouts
|
||||||
|
|
||||||
|
If you want to make sure a feature is always on or off for users, use a **Percentage of actors**
|
||||||
|
rollout. Avoid using percentage of _time_ rollouts in this case.
|
||||||
|
|
||||||
|
A percentage of _time_ rollout can introduce inconsistent behavior when `Feature.enabled?`
|
||||||
|
is used multiple times in the code because the feature flag value is randomized each time
|
||||||
|
`Feature.enabled?` is called on your code path.
|
||||||
|
|
||||||
##### Disabling feature flags
|
##### Disabling feature flags
|
||||||
|
|
||||||
To disable a feature flag that has been globally enabled you can run:
|
To disable a feature flag that has been globally enabled you can run:
|
||||||
|
|
|
@ -69,11 +69,22 @@ module API
|
||||||
def feature_flags
|
def feature_flags
|
||||||
return [] unless unleash_app_name.present?
|
return [] unless unleash_app_name.present?
|
||||||
|
|
||||||
legacy_flags = Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name)
|
legacy_flags =
|
||||||
|
if exclude_legacy_flags?
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name)
|
||||||
|
end
|
||||||
|
|
||||||
new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
|
new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
|
||||||
|
|
||||||
legacy_flags + new_version_flags
|
legacy_flags + new_version_flags
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exclude_legacy_flags?
|
||||||
|
Feature.enabled?(:remove_legacy_flags, project, default_enabled: :yaml) &&
|
||||||
|
Feature.disabled?(:remove_legacy_flags_override, project, default_enabled: :yaml)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,10 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_export?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def send_file
|
def send_file
|
||||||
|
|
|
@ -34330,9 +34330,6 @@ msgstr ""
|
||||||
msgid "Too many projects enabled. You will need to manage them via the console or the API."
|
msgid "Too many projects enabled. You will need to manage them via the console or the API."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "TopNav|Switch to..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Topics (optional)"
|
msgid "Topics (optional)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -147,14 +147,23 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'infinite scrolls list' do
|
it 'infinite scrolls list' do
|
||||||
create_list(:labeled_issue, 50, project: project, labels: [planning])
|
create_list(:labeled_issue, 30, project: project, labels: [planning])
|
||||||
|
|
||||||
visit_project_board_path_without_query_limit(project, board)
|
visit_project_board_path_without_query_limit(project, board)
|
||||||
|
|
||||||
page.within(find('.board:nth-child(2)')) do
|
page.within(find('.board:nth-child(2)')) do
|
||||||
expect(page.find('.board-header')).to have_content('58')
|
expect(page.find('.board-header')).to have_content('38')
|
||||||
|
expect(page).to have_selector('.board-card', count: 10)
|
||||||
|
expect(page).to have_content('Showing 10 of 38 issues')
|
||||||
|
|
||||||
|
find('.board .board-list')
|
||||||
|
|
||||||
|
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||||
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 20)
|
expect(page).to have_selector('.board-card', count: 20)
|
||||||
expect(page).to have_content('Showing 20 of 58 issues')
|
expect(page).to have_content('Showing 20 of 38 issues')
|
||||||
|
|
||||||
find('.board .board-list')
|
find('.board .board-list')
|
||||||
|
|
||||||
|
@ -162,8 +171,8 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 40)
|
expect(page).to have_selector('.board-card', count: 30)
|
||||||
expect(page).to have_content('Showing 40 of 58 issues')
|
expect(page).to have_content('Showing 30 of 38 issues')
|
||||||
|
|
||||||
find('.board .board-list')
|
find('.board .board-list')
|
||||||
|
|
||||||
|
@ -171,7 +180,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 58)
|
expect(page).to have_selector('.board-card', count: 38)
|
||||||
expect(page).to have_content('Showing all issues')
|
expect(page).to have_content('Showing all issues')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -464,7 +473,7 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'infinite scrolls list with label filter' do
|
it 'infinite scrolls list with label filter' do
|
||||||
create_list(:labeled_issue, 50, project: project, labels: [planning, testing])
|
create_list(:labeled_issue, 30, project: project, labels: [planning, testing])
|
||||||
|
|
||||||
set_filter("label", testing.title)
|
set_filter("label", testing.title)
|
||||||
click_filter_link(testing.title)
|
click_filter_link(testing.title)
|
||||||
|
@ -475,9 +484,18 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
|
||||||
page.within(find('.board:nth-child(2)')) do
|
page.within(find('.board:nth-child(2)')) do
|
||||||
expect(page.find('.board-header')).to have_content('51')
|
expect(page.find('.board-header')).to have_content('31')
|
||||||
|
expect(page).to have_selector('.board-card', count: 10)
|
||||||
|
expect(page).to have_content('Showing 10 of 31 issues')
|
||||||
|
|
||||||
|
find('.board .board-list')
|
||||||
|
|
||||||
|
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||||
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 20)
|
expect(page).to have_selector('.board-card', count: 20)
|
||||||
expect(page).to have_content('Showing 20 of 51 issues')
|
expect(page).to have_content('Showing 20 of 31 issues')
|
||||||
|
|
||||||
find('.board .board-list')
|
find('.board .board-list')
|
||||||
|
|
||||||
|
@ -485,15 +503,15 @@ RSpec.describe 'Project issue boards', :js do
|
||||||
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 40)
|
expect(page).to have_selector('.board-card', count: 30)
|
||||||
expect(page).to have_content('Showing 40 of 51 issues')
|
expect(page).to have_content('Showing 30 of 31 issues')
|
||||||
|
|
||||||
find('.board .board-list')
|
find('.board .board-list')
|
||||||
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
|
||||||
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('.board-card', count: 51)
|
expect(page).to have_selector('.board-card', count: 31)
|
||||||
expect(page).to have_content('Showing all issues')
|
expect(page).to have_content('Showing all issues')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,10 @@ RSpec.describe FeatureFlagsFinder do
|
||||||
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
|
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
|
||||||
let(:args) { {} }
|
let(:args) { {} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_feature_flags(remove_legacy_flags: false)
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns feature flags ordered by name' do
|
it 'returns feature flags ordered by name' do
|
||||||
is_expected.to eq([feature_flag_1, feature_flag_2])
|
is_expected.to eq([feature_flag_1, feature_flag_2])
|
||||||
end
|
end
|
||||||
|
@ -79,6 +83,16 @@ RSpec.describe FeatureFlagsFinder do
|
||||||
it 'returns new and legacy flags' do
|
it 'returns new and legacy flags' do
|
||||||
is_expected.to eq([feature_flag_1, feature_flag_2, feature_flag_3])
|
is_expected.to eq([feature_flag_1, feature_flag_2, feature_flag_3])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when legacy flags are disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(remove_legacy_flags_override: false, remove_legacy_flags: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only new flags' do
|
||||||
|
is_expected.to eq([feature_flag_3])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { GlNavItemDropdown, GlTooltip } from '@gitlab/ui';
|
import { GlNavItemDropdown } from '@gitlab/ui';
|
||||||
import { shallowMount, mount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import TopNavApp from '~/nav/components/top_nav_app.vue';
|
import TopNavApp from '~/nav/components/top_nav_app.vue';
|
||||||
import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue';
|
import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue';
|
||||||
import { TEST_NAV_DATA } from '../mock_data';
|
import { TEST_NAV_DATA } from '../mock_data';
|
||||||
|
@ -7,8 +7,8 @@ import { TEST_NAV_DATA } from '../mock_data';
|
||||||
describe('~/nav/components/top_nav_app.vue', () => {
|
describe('~/nav/components/top_nav_app.vue', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
const createComponent = (mountFn = shallowMount) => {
|
const createComponent = () => {
|
||||||
wrapper = mountFn(TopNavApp, {
|
wrapper = shallowMount(TopNavApp, {
|
||||||
propsData: {
|
propsData: {
|
||||||
navData: TEST_NAV_DATA,
|
navData: TEST_NAV_DATA,
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,6 @@ describe('~/nav/components/top_nav_app.vue', () => {
|
||||||
|
|
||||||
const findNavItemDropdown = () => wrapper.findComponent(GlNavItemDropdown);
|
const findNavItemDropdown = () => wrapper.findComponent(GlNavItemDropdown);
|
||||||
const findMenu = () => wrapper.findComponent(TopNavDropdownMenu);
|
const findMenu = () => wrapper.findComponent(TopNavDropdownMenu);
|
||||||
const findTooltip = () => wrapper.findComponent(GlTooltip);
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
|
@ -44,25 +43,5 @@ describe('~/nav/components/top_nav_app.vue', () => {
|
||||||
views: TEST_NAV_DATA.views,
|
views: TEST_NAV_DATA.views,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders tooltip', () => {
|
|
||||||
expect(findTooltip().attributes()).toMatchObject({
|
|
||||||
'boundary-padding': '0',
|
|
||||||
placement: 'right',
|
|
||||||
title: TopNavApp.TOOLTIP,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when full mounted', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
createComponent(mount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has dropdown toggle as tooltip target', () => {
|
|
||||||
const targetFn = findTooltip().props('target');
|
|
||||||
|
|
||||||
expect(targetFn()).toBe(wrapper.find('.js-top-nav-dropdown-toggle').element);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,22 +27,32 @@ RSpec.describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
|
||||||
expect(subject.new(url: example_url, http_method: 'whatever')).not_to be_valid
|
expect(subject.new(url: example_url, http_method: 'whatever')).not_to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'onyl allow urls as upload urls' do
|
it 'only allow urls as upload urls' do
|
||||||
expect(subject.new(url: example_url)).to be_valid
|
expect(subject.new(url: example_url)).to be_valid
|
||||||
expect(subject.new(url: 'whatever')).not_to be_valid
|
expect(subject.new(url: 'whatever')).not_to be_valid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
it 'removes the exported project file after the upload' do
|
context 'when upload succeeds' do
|
||||||
|
before do
|
||||||
allow(strategy).to receive(:send_file)
|
allow(strategy).to receive(:send_file)
|
||||||
allow(strategy).to receive(:handle_response_error)
|
allow(strategy).to receive(:handle_response_error)
|
||||||
|
end
|
||||||
|
|
||||||
expect(project).to receive(:remove_exports)
|
it 'does not remove the exported project file after the upload' do
|
||||||
|
expect(project).not_to receive(:remove_exports)
|
||||||
|
|
||||||
strategy.execute(user, project)
|
strategy.execute(user, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'has finished export status' do
|
||||||
|
strategy.execute(user, project)
|
||||||
|
|
||||||
|
expect(project.export_status).to eq(:finished)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when upload fails' do
|
context 'when upload fails' do
|
||||||
it 'stores the export error' do
|
it 'stores the export error' do
|
||||||
stub_full_request(example_url, method: :post).to_return(status: [404, 'Page not found'])
|
stub_full_request(example_url, method: :post).to_return(status: [404, 'Page not found'])
|
||||||
|
|
|
@ -590,6 +590,32 @@ RSpec.describe API::Unleash do
|
||||||
}]
|
}]
|
||||||
}])
|
}])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns new flags when legacy flags are disabled' do
|
||||||
|
stub_feature_flags(remove_legacy_flags_override: false, remove_legacy_flags: true)
|
||||||
|
|
||||||
|
feature_flag_a = create(:operations_feature_flag, :new_version_flag, project: project,
|
||||||
|
name: 'feature_a', active: true)
|
||||||
|
strategy = create(:operations_strategy, feature_flag: feature_flag_a,
|
||||||
|
name: 'userWithId', parameters: { userIds: 'user8' })
|
||||||
|
create(:operations_scope, strategy: strategy, environment_scope: 'staging')
|
||||||
|
feature_flag_b = create(:operations_feature_flag, :legacy_flag, project: project,
|
||||||
|
name: 'feature_b', active: true)
|
||||||
|
create(:operations_feature_flag_scope, feature_flag: feature_flag_b,
|
||||||
|
active: true, strategies: [{ name: 'default', parameters: {} }], environment_scope: 'staging')
|
||||||
|
|
||||||
|
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'staging' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response['features'].sort_by {|f| f['name']}).to eq([{
|
||||||
|
'name' => 'feature_a',
|
||||||
|
'enabled' => true,
|
||||||
|
'strategies' => [{
|
||||||
|
'name' => 'userWithId',
|
||||||
|
'parameters' => { 'userIds' => 'user8' }
|
||||||
|
}]
|
||||||
|
}])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue