Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-24 09:10:15 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-24 09:10:15 +0300
commit273d780f9ede8f6ed5ebbd4f81c8a391a55f882a (patch)
treebf1f9c065bcb3c3d0c7f8e049587e8e7fecbee46
parented621e6801d74097e2da08d2afc7574261bb7da1 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.haml-lint_todo.yml1
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue10
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js3
-rw-r--r--app/assets/javascripts/boards/stores/state.js5
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/epic.fragment.graphql10
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary.vue4
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/utils.js10
-rw-r--r--app/controllers/projects/boards_controller.rb1
-rw-r--r--app/views/shared/issuable/form/_issue_assignee.html.haml31
-rw-r--r--app/views/shared/issuable/form/_metadata_merge_request_assignee.html.haml8
-rw-r--r--changelogs/unreleased/213101-junit-report-times.yml5
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/frontend/boards/components/board_content_spec.js66
-rw-r--r--spec/frontend/boards/mock_data.js35
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js17
-rw-r--r--spec/frontend/pipelines/test_reports/stores/getters_spec.js6
-rw-r--r--spec/frontend/pipelines/test_reports/stores/utils_spec.js26
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_spec.js3
18 files changed, 200 insertions, 53 deletions
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index dabf7cd9a95..bd650e7d6a2 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -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"
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index c42295792f1..ef160e954e9 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -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"
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index ca9b911ce5b..1644c118868 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -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;
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index cb6930774ed..fde9e62149c 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -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,
});
diff --git a/app/assets/javascripts/graphql_shared/fragments/epic.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/epic.fragment.graphql
new file mode 100644
index 00000000000..286ebbd019e
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/fragments/epic.fragment.graphql
@@ -0,0 +1,10 @@
+fragment EpicNode on Epic {
+ id
+ iid
+ title
+ state
+ reference
+ webUrl
+ createdAt
+ closedAt
+}
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
index 712ac5eb0e5..4a9688e0eab 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
@@ -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) {
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/utils.js b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
index 16fa6935cbe..b6d533fc8f1 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/utils.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
@@ -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,
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index db05da0bb7f..5ed35094476 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -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
diff --git a/app/views/shared/issuable/form/_issue_assignee.html.haml b/app/views/shared/issuable/form/_issue_assignee.html.haml
deleted file mode 100644
index 0d13fccaf3e..00000000000
--- a/app/views/shared/issuable/form/_issue_assignee.html.haml
+++ /dev/null
@@ -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 } })
diff --git a/app/views/shared/issuable/form/_metadata_merge_request_assignee.html.haml b/app/views/shared/issuable/form/_metadata_merge_request_assignee.html.haml
deleted file mode 100644
index 60c34094108..00000000000
--- a/app/views/shared/issuable/form/_metadata_merge_request_assignee.html.haml
+++ /dev/null
@@ -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}"
diff --git a/changelogs/unreleased/213101-junit-report-times.yml b/changelogs/unreleased/213101-junit-report-times.yml
new file mode 100644
index 00000000000..b9e295f5594
--- /dev/null
+++ b/changelogs/unreleased/213101-junit-report-times.yml
@@ -0,0 +1,5 @@
+---
+title: Adjust format for JUnit report duration times
+merge_request: 39644
+author:
+type: changed
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 055a3e7511d..7802342eb0b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
new file mode 100644
index 00000000000..defd195c6fd
--- /dev/null
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -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);
+ });
+});
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 8ef6efe23c7..7461e9dc0e9 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -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 })),
+);
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index c1f7f3dda6e..cf2f86f1e34 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -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);
});
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
index ca9ebb54138..58e8065033f 100644
--- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
@@ -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),
}));
diff --git a/spec/frontend/pipelines/test_reports/stores/utils_spec.js b/spec/frontend/pipelines/test_reports/stores/utils_spec.js
new file mode 100644
index 00000000000..7e632d099fc
--- /dev/null
+++ b/spec/frontend/pipelines/test_reports/stores/utils_spec.js
@@ -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');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js
index 79be6c168cf..dc5af7b160c 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_spec.js
@@ -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));
});
});