Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-14 21:12:54 +00:00
parent 5ca32d2dac
commit 3e9e2cd52e
24 changed files with 237 additions and 70 deletions

View file

@ -1 +1 @@
1.45.0 1.46.0

View file

@ -1,13 +1,23 @@
<script> <script>
import { GlButton } from '@gitlab/ui'; import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { mapActions } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { __ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
export default { export default {
components: { components: {
GlButton, GlButton,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()], mixins: [Tracking.mixin()],
computed: {
...mapState({ isNewListShowing: ({ addColumnForm }) => addColumnForm.visible }),
tooltip() {
return this.isNewListShowing ? __('The list creation wizard is already open') : '';
},
},
methods: { methods: {
...mapActions(['setAddColumnFormVisibility']), ...mapActions(['setAddColumnFormVisibility']),
handleClick() { handleClick() {
@ -19,7 +29,14 @@ export default {
</script> </script>
<template> <template>
<div class="gl-ml-3 gl-display-flex gl-align-items-center" data-testid="boards-create-list"> <div
<gl-button variant="confirm" @click="handleClick">{{ __('Create list') }} </gl-button> v-gl-tooltip="tooltip"
:tabindex="isNewListShowing ? '0' : undefined"
class="gl-ml-3 gl-display-flex gl-align-items-center"
data-testid="boards-create-list"
>
<gl-button :disabled="isNewListShowing" variant="confirm" @click="handleClick"
>{{ __('Create list') }}
</gl-button>
</div> </div>
</template> </template>

View file

@ -0,0 +1,8 @@
---
name: linear_ee_group_ancestor_scopes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70708
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341350
milestone: '14.4'
type: development
group: group::access
default_enabled: false

View file

@ -485,7 +485,7 @@ config:
agent: agent:
monitor: monitor:
eventTypes: ["drop"] eventTypes: ["drop"] # Note: possible values are documented at https://docs.cilium.io/en/stable/cmdref/cilium_monitor/
``` ```
The Cilium monitor log for traffic is logged out by the The Cilium monitor log for traffic is logged out by the

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
module BulkImports module BulkImports
module Groups module Common
module Pipelines module Pipelines
class BoardsPipeline class BoardsPipeline
include NdjsonPipeline include NdjsonPipeline

View file

@ -36,7 +36,7 @@ module BulkImports
stage: 1 stage: 1
}, },
boards: { boards: {
pipeline: BulkImports::Groups::Pipelines::BoardsPipeline, pipeline: BulkImports::Common::Pipelines::BoardsPipeline,
stage: 2 stage: 2
}, },
finisher: { finisher: {

View file

@ -23,9 +23,13 @@ module BulkImports
pipeline: BulkImports::Projects::Pipelines::IssuesPipeline, pipeline: BulkImports::Projects::Pipelines::IssuesPipeline,
stage: 3 stage: 3
}, },
boards: {
pipeline: BulkImports::Common::Pipelines::BoardsPipeline,
stage: 4
},
finisher: { finisher: {
pipeline: BulkImports::Common::Pipelines::EntityFinisher, pipeline: BulkImports::Common::Pipelines::EntityFinisher,
stage: 4 stage: 5
} }
} }
end end

View file

@ -11,14 +11,15 @@
module Gitlab module Gitlab
class SidekiqEnq class SidekiqEnq
def enqueue_jobs(now = Time.now.to_f.to_s, sorted_sets = Sidekiq::Scheduled::SETS) def enqueue_jobs(now = Time.now.to_f.to_s, sorted_sets = Sidekiq::Scheduled::SETS)
start_time = ::Gitlab::Metrics::System.monotonic_time
jobs = redundant_jobs = 0
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'start')
# A job's "score" in Redis is the time at which it should be processed. # A job's "score" in Redis is the time at which it should be processed.
# Just check Redis for the set of jobs with a timestamp before now. # Just check Redis for the set of jobs with a timestamp before now.
Sidekiq.redis do |conn| Sidekiq.redis do |conn|
sorted_sets.each do |sorted_set| sorted_sets.each do |sorted_set|
start_time = ::Gitlab::Metrics::System.monotonic_time
jobs = redundant_jobs = 0
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'start', sorted_set: sorted_set)
# Get the next item in the queue if it's score (time to execute) is <= now. # Get the next item in the queue if it's score (time to execute) is <= now.
# We need to go through the list one at a time to reduce the risk of something # We need to go through the list one at a time to reduce the risk of something
# going wrong between the time jobs are popped from the scheduled queue and when # going wrong between the time jobs are popped from the scheduled queue and when
@ -35,11 +36,16 @@ module Gitlab
redundant_jobs += 1 redundant_jobs += 1
end end
end end
end_time = ::Gitlab::Metrics::System.monotonic_time
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs',
status: 'done',
sorted_set: sorted_set,
jobs_count: jobs,
redundant_jobs_count: redundant_jobs,
duration_s: end_time - start_time)
end end
end end
end_time = ::Gitlab::Metrics::System.monotonic_time
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'done', jobs_count: jobs, redundant_jobs_count: redundant_jobs, duration_s: end_time - start_time)
end end
end end
end end

View file

@ -34065,6 +34065,9 @@ msgstr ""
msgid "The license was successfully uploaded and will be active from %{starts_at}. You can see the details below." msgid "The license was successfully uploaded and will be active from %{starts_at}. You can see the details below."
msgstr "" msgstr ""
msgid "The list creation wizard is already open"
msgstr ""
msgid "The maximum file size allowed is %{size}." msgid "The maximum file size allowed is %{size}."
msgstr "" msgstr ""

View file

@ -12,7 +12,6 @@ settings:
jest: jest:
jestConfigFile: 'jest.config.js' jestConfigFile: 'jest.config.js'
globals: globals:
getJSONFixture: false
loadFixtures: false loadFixtures: false
setFixtures: false setFixtures: false
rules: rules:

View file

@ -20,6 +20,11 @@ Did you run bin/rake frontend:fixtures?`,
return fs.readFileSync(absolutePath, 'utf8'); return fs.readFileSync(absolutePath, 'utf8');
} }
/**
* @deprecated Use `import` to load a JSON fixture instead.
* See https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html#use-fixtures,
* https://gitlab.com/gitlab-org/gitlab/-/issues/339346.
*/
export const getJSONFixture = (relativePath) => JSON.parse(getFixture(relativePath)); export const getJSONFixture = (relativePath) => JSON.parse(getFixture(relativePath));
export const resetHTMLFixture = () => { export const resetHTMLFixture = () => {

View file

@ -0,0 +1,59 @@
import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import { createStore } from '~/boards/stores';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
Vue.use(Vuex);
describe('BoardAddNewColumnTrigger', () => {
let wrapper;
const findBoardsCreateList = () => wrapper.findByTestId('boards-create-list');
const findTooltipText = () => getBinding(findBoardsCreateList().element, 'gl-tooltip');
const mountComponent = () => {
wrapper = mountExtended(BoardAddNewColumnTrigger, {
directives: {
GlTooltip: createMockDirective(),
},
store: createStore(),
});
};
beforeEach(() => {
mountComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('when button is active', () => {
it('does not show the tooltip', () => {
const tooltip = findTooltipText();
expect(tooltip.value).toBe('');
});
it('renders an enabled button', () => {
const button = wrapper.find(GlButton);
expect(button.props('disabled')).toBe(false);
});
});
describe('when button is disabled', () => {
it('shows the tooltip', async () => {
wrapper.find(GlButton).vm.$emit('click');
await wrapper.vm.$nextTick();
const tooltip = findTooltipText();
expect(tooltip.value).toBe('The list creation wizard is already open');
});
});
});

View file

@ -1,11 +1,13 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import jsYaml from 'js-yaml'; import jsYaml from 'js-yaml';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures'; import { getJSONFixture } from 'helpers/fixtures';
export const loadMarkdownApiResult = (testName) => { export const loadMarkdownApiResult = (testName) => {
const fixturePathPrefix = `api/markdown/${testName}.json`; const fixturePathPrefix = `api/markdown/${testName}.json`;
// eslint-disable-next-line import/no-deprecated
const fixture = getJSONFixture(fixturePathPrefix); const fixture = getJSONFixture(fixturePathPrefix);
return fixture.body || fixture.html; return fixture.body || fixture.html;
}; };

View file

@ -1,3 +1,5 @@
/* eslint-disable import/no-deprecated */
import { getJSONFixture } from 'helpers/fixtures'; import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { import {

View file

@ -3,6 +3,8 @@
import $ from 'jquery'; import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import '~/lib/utils/common_utils'; import '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility', () => ({ jest.mock('~/lib/utils/url_utility', () => ({
@ -66,6 +68,7 @@ describe('deprecatedJQueryDropdown', () => {
loadFixtures('static/deprecated_jquery_dropdown.html'); loadFixtures('static/deprecated_jquery_dropdown.html');
test.dropdownContainerElement = $('.dropdown.inline'); test.dropdownContainerElement = $('.dropdown.inline');
test.$dropdownMenuElement = $('.dropdown-menu', test.dropdownContainerElement); test.$dropdownMenuElement = $('.dropdown-menu', test.dropdownContainerElement);
// eslint-disable-next-line import/no-deprecated
test.projectsData = getJSONFixture('static/projects.json'); test.projectsData = getJSONFixture('static/projects.json');
}); });

View file

@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures'; import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures';
import FilterableList from '~/filterable_list'; import FilterableList from '~/filterable_list';
@ -14,6 +15,7 @@ describe('FilterableList', () => {
</div> </div>
<div class="js-projects-list-holder"></div> <div class="js-projects-list-holder"></div>
`); `);
// eslint-disable-next-line import/no-deprecated
getJSONFixture('static/projects.json'); getJSONFixture('static/projects.json');
form = document.querySelector('form#project-filter-form'); form = document.querySelector('form#project-filter-form');
filter = document.querySelector('.js-projects-list-filter'); filter = document.querySelector('.js-projects-list-filter');

View file

@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures'; import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures';
import ProjectsFilterableList from '~/projects/projects_filterable_list'; import ProjectsFilterableList from '~/projects/projects_filterable_list';
@ -14,6 +15,7 @@ describe('ProjectsFilterableList', () => {
</div> </div>
<div class="js-projects-list-holder"></div> <div class="js-projects-list-holder"></div>
`); `);
// eslint-disable-next-line import/no-deprecated
getJSONFixture('static/projects.json'); getJSONFixture('static/projects.json');
form = document.querySelector('form#project-filter-form'); form = document.querySelector('form#project-filter-form');
filter = document.querySelector('.js-projects-list-filter'); filter = document.querySelector('.js-projects-list-filter');

View file

@ -6,7 +6,7 @@ import { setGlobalDateToFakeDate } from 'helpers/fake_date';
import setWindowLocation from 'helpers/set_window_location_helper'; import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import { getJSONFixture, loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures'; import { loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures';
import { initializeTestTimeout } from './__helpers__/timeout'; import { initializeTestTimeout } from './__helpers__/timeout';
import customMatchers from './matchers'; import customMatchers from './matchers';
import { setupManualMocks } from './mocks/mocks_helper'; import { setupManualMocks } from './mocks/mocks_helper';
@ -43,7 +43,6 @@ Vue.use(Translate);
// convenience wrapper for migration from Karma // convenience wrapper for migration from Karma
Object.assign(global, { Object.assign(global, {
getJSONFixture,
loadFixtures: loadHTMLFixture, loadFixtures: loadHTMLFixture,
setFixtures: setHTMLFixture, setFixtures: setHTMLFixture,
}); });

View file

@ -1,4 +1,6 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import ProjectAvatar from '~/vue_shared/components/deprecated_project_avatar/default.vue'; import ProjectAvatar from '~/vue_shared/components/deprecated_project_avatar/default.vue';
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
@ -11,6 +13,7 @@ describe('ProjectListItem component', () => {
let vm; let vm;
let options; let options;
// eslint-disable-next-line import/no-deprecated
const project = getJSONFixture('static/projects.json')[0]; const project = getJSONFixture('static/projects.json')[0];
beforeEach(() => { beforeEach(() => {

View file

@ -2,6 +2,8 @@ import { GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils'; import { mount, createLocalVue } from '@vue/test-utils';
import { head } from 'lodash'; import { head } from 'lodash';
import Vue from 'vue'; import Vue from 'vue';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue'; import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue';
@ -11,6 +13,7 @@ const localVue = createLocalVue();
describe('ProjectSelector component', () => { describe('ProjectSelector component', () => {
let wrapper; let wrapper;
let vm; let vm;
// eslint-disable-next-line import/no-deprecated
const allProjects = getJSONFixture('static/projects.json'); const allProjects = getJSONFixture('static/projects.json');
const searchResults = allProjects.slice(0, 5); const searchResults = allProjects.slice(0, 5);
let selected = []; let selected = [];

View file

@ -0,0 +1,98 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Common::Pipelines::BoardsPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let(:board_data) do
{
"name" => "Test Board",
"lists" => [
{
"list_type" => "backlog",
"position" => 0
},
{
"list_type" => "closed",
"position" => 1
},
{
"list_type" => "label",
"position" => 2,
"label" => {
"title" => "test",
"type" => "GroupLabel",
"group_id" => group.id
}
}
]
}
end
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject { described_class.new(context) }
before do
allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor|
allow(extractor).to receive(:extract).and_return(BulkImports::Pipeline::ExtractedData.new(data: board_data))
end
group.add_owner(user)
end
context 'when issue board belongs to a project' do
let_it_be(:entity) do
create(
:bulk_import_entity,
source_type: :project_entity,
project: project,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
describe '#run' do
it 'imports issue boards into destination project' do
expect { subject.run }.to change(::Board, :count).by(1)
board = project.boards.find_by(name: board_data["name"])
expect(board).to be
expect(board.project.id).to eq(project.id)
expect(board.lists.count).to eq(3)
expect(board.lists.map(&:list_type).sort).to match_array(%w(backlog closed label))
expect(board.lists.find_by(list_type: "label").label.title).to eq("test")
end
end
end
context 'when issue board belongs to a group' do
let_it_be(:entity) do
create(
:bulk_import_entity,
group: group,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
describe '#run' do
it 'imports issue boards into destination group' do
expect { subject.run }.to change(::Board, :count).by(1)
board = group.boards.find_by(name: board_data["name"])
expect(board).to be
expect(board.group.id).to eq(group.id)
expect(board.lists.count).to eq(3)
expect(board.lists.map(&:list_type).sort).to match_array(%w(backlog closed label))
expect(board.lists.find_by(list_type: "label").label.title).to eq("test")
end
end
end
end

View file

@ -1,49 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Groups::Pipelines::BoardsPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:filepath) { 'spec/fixtures/bulk_imports/gz/boards.ndjson.gz' }
let_it_be(:entity) do
create(
:bulk_import_entity,
group: group,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
let(:tmpdir) { Dir.mktmpdir }
before do
FileUtils.copy_file(filepath, File.join(tmpdir, 'boards.ndjson.gz'))
group.add_owner(user)
end
subject { described_class.new(context) }
describe '#run' do
it 'imports group boards into destination group and removes tmpdir' do
allow(Dir).to receive(:mktmpdir).and_return(tmpdir)
allow_next_instance_of(BulkImports::FileDownloadService) do |service|
allow(service).to receive(:execute)
end
expect { subject.run }.to change(Board, :count).by(1)
lists = group.boards.find_by(name: 'first board').lists
expect(lists.count).to eq(3)
expect(lists.first.label.title).to eq('TSL')
expect(lists.second.label.title).to eq('Sosync')
end
end
end

View file

@ -14,7 +14,7 @@ RSpec.describe BulkImports::Groups::Stage do
[1, BulkImports::Common::Pipelines::LabelsPipeline], [1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Groups::Pipelines::MilestonesPipeline], [1, BulkImports::Groups::Pipelines::MilestonesPipeline],
[1, BulkImports::Groups::Pipelines::BadgesPipeline], [1, BulkImports::Groups::Pipelines::BadgesPipeline],
[2, BulkImports::Groups::Pipelines::BoardsPipeline] [2, BulkImports::Common::Pipelines::BoardsPipeline]
] ]
end end

View file

@ -9,7 +9,8 @@ RSpec.describe BulkImports::Projects::Stage do
[1, BulkImports::Projects::Pipelines::RepositoryPipeline], [1, BulkImports::Projects::Pipelines::RepositoryPipeline],
[2, BulkImports::Common::Pipelines::LabelsPipeline], [2, BulkImports::Common::Pipelines::LabelsPipeline],
[3, BulkImports::Projects::Pipelines::IssuesPipeline], [3, BulkImports::Projects::Pipelines::IssuesPipeline],
[4, BulkImports::Common::Pipelines::EntityFinisher] [4, BulkImports::Common::Pipelines::BoardsPipeline],
[5, BulkImports::Common::Pipelines::EntityFinisher]
] ]
end end