Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-08-24 06:10:15 +00:00
parent ed621e6801
commit 273d780f9e
18 changed files with 200 additions and 53 deletions

View file

@ -289,7 +289,6 @@ linters:
- "app/views/shared/issuable/_search_bar.html.haml"
- "app/views/shared/issuable/_sidebar.html.haml"
- "app/views/shared/issuable/form/_default_templates.html.haml"
- "app/views/shared/issuable/form/_issue_assignee.html.haml"
- "app/views/shared/issuable/form/_template_selector.html.haml"
- "app/views/shared/issuable/form/_title.html.haml"
- "app/views/shared/labels/_form.html.haml"

View file

@ -1,13 +1,14 @@
<script>
import { mapState } from 'vuex';
import { GlAlert } from '@gitlab/ui';
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
BoardColumn,
EpicsSwimlanes,
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
GlAlert,
},
mixins: [glFeatureFlagMixin()],
props: {
@ -42,7 +43,7 @@ export default {
},
},
computed: {
...mapState(['isShowingEpicsSwimlanes', 'boardLists']),
...mapState(['isShowingEpicsSwimlanes', 'boardLists', 'error']),
isSwimlanesOn() {
return this.glFeatures.boardsWithSwimlanes && this.isShowingEpicsSwimlanes;
},
@ -52,6 +53,9 @@ export default {
<template>
<div>
<gl-alert v-if="error" variant="danger" :dismissible="false">
{{ error }}
</gl-alert>
<div
v-if="!isSwimlanesOn"
class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap"

View file

@ -1,4 +1,5 @@
import * as mutationTypes from './mutation_types';
import { __ } from '~/locale';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@ -62,7 +63,7 @@ export default {
},
[mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE]: state => {
state.listIssueFetchFailure = true;
state.error = __('An error occurred while fetching the board issues. Please reload the page.');
state.isLoadingIssues = false;
},

View file

@ -5,7 +5,10 @@ export default () => ({
boardType: null,
isShowingLabels: true,
activeId: inactiveId,
boardLists: [],
issuesByListId: {},
isLoadingIssues: false,
listIssueFetchFailure: false,
error: undefined,
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,
});

View file

@ -0,0 +1,10 @@
fragment EpicNode on Epic {
id
iid
title
state
reference
webUrl
createdAt
closedAt
}

View file

@ -1,8 +1,8 @@
<script>
import { GlDeprecatedButton, GlProgressBar } from '@gitlab/ui';
import { __ } from '~/locale';
import { formatTime, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import { formattedTime } from '../../stores/test_reports/utils';
export default {
name: 'TestSummary',
@ -39,7 +39,7 @@ export default {
return 0;
},
formattedDuration() {
return formatTime(secondsToMilliseconds(this.report.total_time));
return formattedTime(this.report.total_time);
},
progressBarVariant() {
if (this.successPercentage < 33) {

View file

@ -1,5 +1,5 @@
import { TestStatus } from '~/pipelines/constants';
import { formatTime, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '../../../locale';
export function iconForTestStatus(status) {
switch (status) {
@ -12,7 +12,13 @@ export function iconForTestStatus(status) {
}
}
export const formattedTime = timeInSeconds => formatTime(secondsToMilliseconds(timeInSeconds));
export const formattedTime = (seconds = 0) => {
if (seconds < 1) {
const milliseconds = seconds * 1000;
return sprintf(__('%{milliseconds}ms'), { milliseconds: milliseconds.toFixed(2) });
}
return sprintf(__('%{seconds}s'), { seconds: seconds.toFixed(2) });
};
export const addIconStatus = testCase => ({
...testCase,

View file

@ -9,6 +9,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:multi_select_board, default_enabled: true)
push_frontend_feature_flag(:boards_with_swimlanes, project, default_enabled: false)
end
private

View file

@ -1,31 +0,0 @@
- issue = issuable
- assignees = issue.assignees
.block.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee_list) }
- if assignees.any?
- assignees.each do |assignee|
= link_to_member(@project, assignee, size: 24)
- else
= icon('user', 'aria-hidden': 'true')
.title.hide-collapsed
Assignee
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to 'Edit', '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.hide-collapsed
- if assignees.any?
- assignees.each do |assignee|
= link_to_member(@project, assignee, size: 32, extra_class: 'bold') do
%span.username
= assignee.to_reference
- else
%span.assign-yourself.no-value
No assignee
- if can_edit_issuable
\-
%a.js-assign-yourself{ href: '#' }
assign yourself
.selectbox.hide-collapsed
= f.hidden_field 'assignee_ids', value: issuable.assignee_ids, id: 'issue_assignee_ids'
= dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } })

View file

@ -1,8 +0,0 @@
= form.label :assignee_id, "Assignee", class: "col-form-label #{has_due_date ? "col-lg-4" : "col-sm-2"}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
= form.hidden_field :assignee_id
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: issuable.project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
= link_to 'Assign to me', '#', class: "assign-to-me-link qa-assign-to-me-link #{'hide' if issuable.assignee_id == current_user.id}"

View file

@ -0,0 +1,5 @@
---
title: Adjust format for JUnit report duration times
merge_request: 39644
author:
type: changed

View file

@ -551,6 +551,9 @@ msgstr ""
msgid "%{milestone} (expired)"
msgstr ""
msgid "%{milliseconds}ms"
msgstr ""
msgid "%{mrText}, this issue will be closed automatically."
msgstr ""
@ -663,6 +666,9 @@ msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}"
msgstr ""
msgid "%{seconds}s"
msgstr ""
msgid "%{securityScanner} is not enabled for this project. %{linkStart}More information%{linkEnd}"
msgid_plural "%{securityScanner} are not enabled for this project. %{linkStart}More information%{linkEnd}"
msgstr[0] ""
@ -2657,9 +2663,15 @@ msgstr ""
msgid "An error occurred while fetching the Service Desk address."
msgstr ""
msgid "An error occurred while fetching the board issues. Please reload the page."
msgstr ""
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
msgid "An error occurred while fetching the board swimlanes. Please reload the page."
msgstr ""
msgid "An error occurred while fetching the builds."
msgstr ""

View file

@ -0,0 +1,66 @@
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
import { mockListsWithModel } from '../mock_data';
import BoardContent from '~/boards/components/board_content.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('BoardContent', () => {
let wrapper;
const defaultState = {
isShowingEpicsSwimlanes: false,
boardLists: mockListsWithModel,
error: undefined,
};
const createStore = (state = defaultState) => {
return new Vuex.Store({
state,
actions: {
fetchIssuesForAllLists: () => {},
},
});
};
const createComponent = state => {
const store = createStore({
...defaultState,
...state,
});
wrapper = shallowMount(BoardContent, {
localVue,
propsData: {
lists: mockListsWithModel,
canAdminList: true,
groupId: 1,
disabled: false,
issueLinkBase: '/',
rootPath: '/',
boardId: '1',
},
store,
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders a BoardColumn component per list', () => {
expect(wrapper.findAll(BoardColumn)).toHaveLength(mockListsWithModel.length);
});
it('does not display EpicsSwimlanes component', () => {
expect(wrapper.contains(EpicsSwimlanes)).toBe(false);
expect(wrapper.contains(GlAlert)).toBe(false);
});
});

View file

@ -1,3 +1,5 @@
import Vue from 'vue';
import List from '~/boards/models/list';
import boardsStore from '~/boards/stores/boards_store';
export const boardObj = {
@ -165,3 +167,36 @@ export const setMockEndpoints = (opts = {}) => {
boardId,
});
};
export const mockLists = [
{
id: 'gid://gitlab/List/1',
title: 'Backlog',
position: null,
listType: 'backlog',
collapsed: false,
label: null,
assignee: null,
milestone: null,
},
{
id: 'gid://gitlab/List/2',
title: 'To Do',
position: 0,
listType: 'label',
collapsed: false,
label: {
id: 'gid://gitlab/GroupLabel/121',
title: 'To Do',
color: '#F0AD4E',
textColor: '#FFFFFF',
description: null,
},
assignee: null,
milestone: null,
},
];
export const mockListsWithModel = mockLists.map(listMock =>
Vue.observable(new List({ ...listMock, doNotFetchIssues: true })),
);

View file

@ -113,6 +113,23 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.REQUEST_ADD_ISSUE);
});
describe('RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE', () => {
it('sets isLoadingIssues to false and sets error message', () => {
state = {
...state,
isLoadingIssues: true,
error: undefined,
};
mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE(state);
expect(state.isLoadingIssues).toBe(false);
expect(state.error).toEqual(
'An error occurred while fetching the board issues. Please reload the page.',
);
});
});
describe('RECEIVE_ADD_ISSUE_SUCCESS', () => {
expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_SUCCESS);
});

View file

@ -1,6 +1,6 @@
import { getJSONFixture } from 'helpers/fixtures';
import * as getters from '~/pipelines/stores/test_reports/getters';
import { iconForTestStatus } from '~/pipelines/stores/test_reports/utils';
import { iconForTestStatus, formattedTime } from '~/pipelines/stores/test_reports/utils';
describe('Getters TestReports Store', () => {
let state;
@ -34,7 +34,7 @@ describe('Getters TestReports Store', () => {
const suites = getters.getTestSuites(state);
const expected = testReports.test_suites.map(x => ({
...x,
formattedTime: '00:00:00',
formattedTime: formattedTime(x.total_time),
}));
expect(suites).toEqual(expected);
@ -65,7 +65,7 @@ describe('Getters TestReports Store', () => {
const cases = getters.getSuiteTests(state);
const expected = testReports.test_suites[0].test_cases.map(x => ({
...x,
formattedTime: '00:00:00',
formattedTime: formattedTime(x.execution_time),
icon: iconForTestStatus(x.status),
}));

View file

@ -0,0 +1,26 @@
import { formattedTime } from '~/pipelines/stores/test_reports/utils';
describe('Test reports utils', () => {
describe('formattedTime', () => {
describe('when time is smaller than a second', () => {
it('should return time in milliseconds fixed to 2 decimals', () => {
const result = formattedTime(0.4815162342);
expect(result).toBe('481.52ms');
});
});
describe('when time is equal to a second', () => {
it('should return time in seconds fixed to 2 decimals', () => {
const result = formattedTime(1);
expect(result).toBe('1.00s');
});
});
describe('when time is greater than a second', () => {
it('should return time in seconds fixed to 2 decimals', () => {
const result = formattedTime(4.815162342);
expect(result).toBe('4.82s');
});
});
});
});

View file

@ -1,6 +1,7 @@
import { mount } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
import Summary from '~/pipelines/components/test_reports/test_summary.vue';
import { formattedTime } from '~/pipelines/stores/test_reports/utils';
describe('Test reports summary', () => {
let wrapper;
@ -76,7 +77,7 @@ describe('Test reports summary', () => {
});
it('displays the correctly formatted duration', () => {
expect(duration().text()).toBe('00:00:00');
expect(duration().text()).toBe(formattedTime(testSuite.total_time));
});
});