Skip to content
Snippets Groups Projects
Commit 5333cb6c authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 44baf08d
No related branches found
No related tags found
No related merge requests found
Showing
with 163 additions and 83 deletions
Loading
Loading
@@ -122,6 +122,8 @@ export default {
this.toggleAward({ awardName, noteId });
});
}
window.addEventListener('hashchange', this.handleHashChanged);
},
updated() {
this.$nextTick(() => {
Loading
Loading
@@ -131,6 +133,7 @@ export default {
},
beforeDestroy() {
this.stopPolling();
window.removeEventListener('hashchange', this.handleHashChanged);
},
methods: {
...mapActions([
Loading
Loading
@@ -138,7 +141,6 @@ export default {
'fetchDiscussions',
'poll',
'toggleAward',
'scrollToNoteIfNeeded',
'setNotesData',
'setNoteableData',
'setUserData',
Loading
Loading
@@ -151,6 +153,13 @@ export default {
'convertToDiscussion',
'stopPolling',
]),
handleHashChanged() {
const noteId = this.checkLocationHash();
if (noteId) {
this.setTargetNoteHash(getLocationHash());
}
},
fetchNotes() {
if (this.isFetching) return null;
 
Loading
Loading
@@ -194,6 +203,8 @@ export default {
this.expandDiscussion({ discussionId: discussion.id });
}
}
return noteId;
},
startReplying(discussionId) {
return this.convertToDiscussion(discussionId)
Loading
Loading
/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, no-return-assign */
/* eslint-disable func-names, consistent-return, no-return-assign */
 
import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
Loading
Loading
@@ -9,9 +9,12 @@ import sanitize from 'sanitize-html';
 
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
const highlighter = function(element, text, matches) {
var j, lastIndex, len, matchIndex, matchedChars, unmatched;
lastIndex = 0;
matchedChars = [];
let j = 0;
let len = 0;
let lastIndex = 0;
let matchedChars = [];
let matchIndex = matches[j];
let unmatched = text.substring(lastIndex, matchIndex);
for (j = 0, len = matches.length; j < len; j += 1) {
matchIndex = matches[j];
unmatched = text.substring(lastIndex, matchIndex);
Loading
Loading
@@ -55,10 +58,10 @@ export default class ProjectFindFile {
'keyup',
(function(_this) {
return function(event) {
var oldValue, ref, target, value;
target = $(event.target);
value = target.val();
oldValue = (ref = target.data('oldValue')) != null ? ref : '';
const target = $(event.target);
const value = target.val();
const ref = target.data('oldValue');
const oldValue = ref != null ? ref : '';
if (value !== oldValue) {
target.data('oldValue', value);
_this.findFile();
Loading
Loading
@@ -74,9 +77,8 @@ export default class ProjectFindFile {
}
 
findFile() {
var result, searchText;
searchText = sanitize(this.inputElement.val());
result =
const searchText = sanitize(this.inputElement.val());
const result =
searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
return this.renderList(result, searchText);
// find file
Loading
Loading
@@ -101,20 +103,21 @@ export default class ProjectFindFile {
 
// render result
renderList(filePaths, searchText) {
var blobItemUrl, filePath, html, i, len, matches, results;
let i = 0;
let len = 0;
let matches = [];
const results = [];
this.element.find('.tree-table > tbody').empty();
results = [];
for (i = 0, len = filePaths.length; i < len; i += 1) {
filePath = filePaths[i];
const filePath = filePaths[i];
if (i === 20) {
break;
}
if (searchText) {
matches = fuzzaldrinPlus.match(filePath, searchText);
}
blobItemUrl = `${this.options.blobUrlTemplate}/${encodeURIComponent(filePath)}`;
html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
const blobItemUrl = `${this.options.blobUrlTemplate}/${encodeURIComponent(filePath)}`;
const html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
results.push(this.element.find('.tree-table > tbody').append(html));
}
 
Loading
Loading
@@ -125,8 +128,7 @@ export default class ProjectFindFile {
 
// make tbody row html
static makeHtml(filePath, matches, blobItemUrl) {
var $tr;
$tr = $(
const $tr = $(
"<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>",
);
if (matches) {
Loading
Loading
@@ -141,9 +143,9 @@ export default class ProjectFindFile {
}
 
selectRow(type) {
var next, rows, selectedRow;
rows = this.element.find('.files-slider tr.tree-item');
selectedRow = this.element.find('.files-slider tr.tree-item.selected');
const rows = this.element.find('.files-slider tr.tree-item');
let selectedRow = this.element.find('.files-slider tr.tree-item.selected');
let next = selectedRow.prev();
if (rows && rows.length > 0) {
if (selectedRow && selectedRow.length > 0) {
if (type === 'UP') {
Loading
Loading
@@ -175,7 +177,7 @@ export default class ProjectFindFile {
}
 
goToBlob() {
var $link = this.element.find('.tree-item.selected .tree-item-file-name a');
const $link = this.element.find('.tree-item.selected .tree-item-file-name a');
 
if ($link.length) {
$link.get(0).click();
Loading
Loading
Loading
Loading
@@ -3,7 +3,7 @@
- flash.each do |key, value|
-# Don't show a flash message if the message is nil
- if value
%div{ class: "flash-#{key}" }
%div{ class: "flash-#{key} mb-2" }
%span= value
%div{ class: "close-icon-wrapper js-close-icon" }
= sprite_icon('close', size: 16, css_class: 'close-icon')
---
title: Add mb-2 class to global alerts
merge_request: 20081
author: 2knal
type: other
---
title: Fix expanding collapsed threads when reference link clicked
merge_request: 20148
author:
type: fixed
Loading
Loading
@@ -366,7 +366,7 @@ to start again from scratch, there are a few steps that can help you:
gitlab-ctl tail sidekiq
```
 
1. Rename repository storage folders and create new ones
1. Rename repository storage folders and create new ones. If you are not concerned about possible orphaned directories and files, then you can simply skip this step.
 
```sh
mv /var/opt/gitlab/git-data/repositories /var/opt/gitlab/git-data/repositories.old
Loading
Loading
@@ -413,7 +413,9 @@ to start again from scratch, there are a few steps that can help you:
1. Reset the Tracking Database
 
```sh
gitlab-rake geo:db:reset
gitlab-rake geo:db:drop
gitlab-ctl reconfigure
gitlab-rake geo:db:setup
```
 
1. Restart previously stopped services
Loading
Loading
@@ -653,13 +655,6 @@ Geo cannot reuse an existing tracking database.
It is safest to use a fresh secondary, or reset the whole secondary by following
[Resetting Geo secondary node replication](#resetting-geo-secondary-node-replication).
 
If you are not concerned about possible orphaned directories and files, then you
can simply reset the existing tracking database with:
```sh
sudo gitlab-rake geo:db:reset
```
### Geo node has a database that is writable which is an indication it is not configured for replication with the primary node
 
This error refers to a problem with the database replica on a **secondary** node,
Loading
Loading
Loading
Loading
@@ -539,7 +539,7 @@ type DesignCollection {
 
"""
Filters designs to only those that existed at the version. If argument is
omitted or nil then all designs will reflect the latest version.
omitted or nil then all designs will reflect the latest version
"""
atVersion: ID
 
Loading
Loading
@@ -548,13 +548,18 @@ type DesignCollection {
"""
before: String
 
"""
Filters designs by their filename
"""
filenames: [String!]
"""
Returns the first _n_ elements from the list.
"""
first: Int
 
"""
The list of IDs of designs.
Filters designs by their ID
"""
ids: [ID!]
 
Loading
Loading
Loading
Loading
@@ -7979,7 +7979,7 @@
"args": [
{
"name": "ids",
"description": "The list of IDs of designs.",
"description": "Filters designs by their ID",
"type": {
"kind": "LIST",
"name": null,
Loading
Loading
@@ -7995,9 +7995,27 @@
},
"defaultValue": null
},
{
"name": "filenames",
"description": "Filters designs by their filename",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "atVersion",
"description": "Filters designs to only those that existed at the version. If argument is omitted or nil then all designs will reflect the latest version.",
"description": "Filters designs to only those that existed at the version. If argument is omitted or nil then all designs will reflect the latest version",
"type": {
"kind": "SCALAR",
"name": "ID",
Loading
Loading
doc/ci/img/junit_test_report_ui.png

82.3 KiB

doc/ci/img/pipelines_junit_test_report_ui_v12_5.png

15.6 KiB

Loading
Loading
@@ -187,7 +187,7 @@ If JUnit XML files are generated and uploaded as part of a pipeline, these repor
can be viewed inside the pipelines details page. The **Tests** tab on this page will
display a list of test suites and cases reported from the XML file.
 
![Test Reports Widget](img/junit_test_report_ui.png)
![Test Reports Widget](img/pipelines_junit_test_report_ui_v12_5.png)
 
You can view all the known test suites and click on each of these to see further
details, including the cases that makeup the suite. Cases are ordered by status,
Loading
Loading
Loading
Loading
@@ -548,6 +548,32 @@ found, we should raise a
`Gitlab::Graphql::Errors::ResourceNotAvailable` error. Which will be
correctly rendered to the clients.
 
## Gitlab's custom scalars
### `Types::TimeType`
[`Types::TimeType`](https://gitlab.com/gitlab-org/gitlab/blob/master/app%2Fgraphql%2Ftypes%2Ftime_type.rb)
must be used as the type for all fields and arguments that deal with Ruby
`Time` and `DateTime` objects.
The type is
[a custom scalar](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/type_definitions/scalars.md#custom-scalars)
that:
- Converts Ruby's `Time` and `DateTime` objects into standardized
ISO-8601 formatted strings, when used as the type for our GraphQL fields.
- Converts ISO-8601 formatted time strings into Ruby `Time` objects,
when used as the type for our GraphQL arguments.
This allows our GraphQL API to have a standardized way that it presents time
and handles time inputs.
Example:
```ruby
field :created_at, Types::TimeType, null: false, description: 'Timestamp of when the issue was created'
```
## Testing
 
_full stack_ tests for a graphql query or mutation live in
Loading
Loading
Loading
Loading
@@ -5800,6 +5800,9 @@ msgstr ""
msgid "DesignManagement|Upload and view the latest designs for this issue. Consistent and easy to find, so everyone is up to date."
msgstr ""
 
msgid "DesignManagement|We could not delete %{design}. Please try again."
msgstr ""
msgid "DesignManagement|We could not delete design(s). Please try again."
msgstr ""
 
Loading
Loading
@@ -20127,6 +20130,9 @@ msgstr ""
msgid "a deleted user"
msgstr ""
 
msgid "a design"
msgstr ""
msgid "added %{created_at_timeago}"
msgstr ""
 
Loading
Loading
@@ -20521,6 +20527,9 @@ msgstr ""
msgid "design"
msgstr ""
 
msgid "designs"
msgstr ""
msgid "detached"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -10,6 +10,7 @@ import '~/behaviors/markdown/render_gfm';
import { setTestTimeout } from 'helpers/timeout';
// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
import * as mockData from '../../notes/mock_data';
import * as urlUtility from '~/lib/utils/url_utility';
 
setTestTimeout(1000);
 
Loading
Loading
@@ -54,7 +55,9 @@ describe('note_app', () => {
components: {
NotesApp,
},
template: '<div class="js-vue-notes-event"><notes-app v-bind="$attrs" /></div>',
template: `<div class="js-vue-notes-event">
<notes-app ref="notesApp" v-bind="$attrs" />
</div>`,
},
{
attachToDocument: true,
Loading
Loading
@@ -313,4 +316,23 @@ describe('note_app', () => {
});
});
});
describe('mounted', () => {
beforeEach(() => {
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
wrapper = mountComponent();
return waitForDiscussionsRequest();
});
it('should listen hashchange event', () => {
const notesApp = wrapper.find(NotesApp);
const hash = 'some dummy hash';
jest.spyOn(urlUtility, 'getLocationHash').mockReturnValueOnce(hash);
const setTargetNoteHash = jest.spyOn(notesApp.vm, 'setTargetNoteHash');
window.dispatchEvent(new Event('hashchange'), hash);
expect(setTargetNoteHash).toHaveBeenCalled();
});
});
});
/* eslint-disable no-var, one-var, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, vars-on-top */
/* eslint-disable no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign */
 
import $ from 'jquery';
import '~/gl_dropdown';
Loading
Loading
@@ -6,41 +6,27 @@ import initSearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
 
describe('Search autocomplete dropdown', () => {
var assertLinks,
dashboardIssuesPath,
dashboardMRsPath,
groupIssuesPath,
groupMRsPath,
groupName,
mockDashboardOptions,
mockGroupOptions,
mockProjectOptions,
projectIssuesPath,
projectMRsPath,
projectName,
userId,
widget;
var userName = 'root';
let widget = null;
 
widget = null;
const userName = 'root';
 
userId = 1;
const userId = 1;
 
dashboardIssuesPath = '/dashboard/issues';
const dashboardIssuesPath = '/dashboard/issues';
 
dashboardMRsPath = '/dashboard/merge_requests';
const dashboardMRsPath = '/dashboard/merge_requests';
 
projectIssuesPath = '/gitlab-org/gitlab-foss/issues';
const projectIssuesPath = '/gitlab-org/gitlab-foss/issues';
 
projectMRsPath = '/gitlab-org/gitlab-foss/merge_requests';
const projectMRsPath = '/gitlab-org/gitlab-foss/merge_requests';
 
groupIssuesPath = '/groups/gitlab-org/issues';
const groupIssuesPath = '/groups/gitlab-org/issues';
 
groupMRsPath = '/groups/gitlab-org/merge_requests';
const groupMRsPath = '/groups/gitlab-org/merge_requests';
 
projectName = 'GitLab Community Edition';
const projectName = 'GitLab Community Edition';
 
groupName = 'Gitlab Org';
const groupName = 'Gitlab Org';
 
const removeBodyAttributes = function() {
const $body = $('body');
Loading
Loading
@@ -76,7 +62,7 @@ describe('Search autocomplete dropdown', () => {
};
 
// Mock `gl` object in window for dashboard specific page. App code will need it.
mockDashboardOptions = function() {
const mockDashboardOptions = function() {
window.gl || (window.gl = {});
return (window.gl.dashboardOptions = {
issuesPath: dashboardIssuesPath,
Loading
Loading
@@ -85,7 +71,7 @@ describe('Search autocomplete dropdown', () => {
};
 
// Mock `gl` object in window for project specific page. App code will need it.
mockProjectOptions = function() {
const mockProjectOptions = function() {
window.gl || (window.gl = {});
return (window.gl.projectOptions = {
'gitlab-ce': {
Loading
Loading
@@ -96,7 +82,7 @@ describe('Search autocomplete dropdown', () => {
});
};
 
mockGroupOptions = function() {
const mockGroupOptions = function() {
window.gl || (window.gl = {});
return (window.gl.groupOptions = {
'gitlab-org': {
Loading
Loading
@@ -107,7 +93,7 @@ describe('Search autocomplete dropdown', () => {
});
};
 
assertLinks = function(list, issuesPath, mrsPath) {
const assertLinks = function(list, issuesPath, mrsPath) {
if (issuesPath) {
const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`;
const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`;
Loading
Loading
@@ -144,29 +130,26 @@ describe('Search autocomplete dropdown', () => {
});
 
it('should show Dashboard specific dropdown menu', function() {
var list;
addBodyAttributes();
mockDashboardOptions();
widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
const list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
});
 
it('should show Group specific dropdown menu', function() {
var list;
addBodyAttributes('group');
mockGroupOptions();
widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
const list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, groupIssuesPath, groupMRsPath);
});
 
it('should show Project specific dropdown menu', function() {
var list;
addBodyAttributes('project');
mockProjectOptions();
widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
const list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, projectIssuesPath, projectMRsPath);
});
 
Loading
Loading
@@ -180,26 +163,25 @@ describe('Search autocomplete dropdown', () => {
});
 
it('should not show category related menu if there is text in the input', function() {
var link, list;
addBodyAttributes('project');
mockProjectOptions();
widget.searchInput.val('help');
widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`;
const list = widget.wrap.find('.dropdown-menu').find('ul');
const link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`;
 
expect(list.find(link).length).toBe(0);
});
 
it('should not submit the search form when selecting an autocomplete row with the keyboard', function() {
var ENTER = 13;
var DOWN = 40;
const ENTER = 13;
const DOWN = 40;
addBodyAttributes();
mockDashboardOptions(true);
var submitSpy = spyOnEvent('form', 'submit');
const submitSpy = spyOnEvent('form', 'submit');
widget.searchInput.triggerHandler('focus');
widget.wrap.trigger($.Event('keydown', { which: DOWN }));
var enterKeyEvent = $.Event('keydown', { which: ENTER });
const enterKeyEvent = $.Event('keydown', { which: ENTER });
widget.searchInput.trigger(enterKeyEvent);
// This does not currently catch failing behavior. For security reasons,
// browsers will not trigger default behavior (form submit, in this
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment