Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5426ca9908
commit
736d36d859
|
@ -9,9 +9,12 @@
|
|||
|
||||
<!-- Outline the tasks with issues that you need evaluate as a part of the implementation issue -->
|
||||
|
||||
- [ ] Add task
|
||||
- [ ] Add task
|
||||
- [ ] Add task
|
||||
- [ ] Determine feasibility of the feature
|
||||
- [ ] Create issue for implementation or update existing implementation issue description with implementation proposal
|
||||
- [ ] Set weight on implementation issue
|
||||
- [ ] If weight is greater than 5, break issue into smaller issues
|
||||
- [ ] Add task
|
||||
- [ ] Add task
|
||||
|
||||
### Risks and Implementation Considerations
|
||||
|
||||
|
|
|
@ -1,14 +1,50 @@
|
|||
/* global ace */
|
||||
|
||||
import $ from 'jquery';
|
||||
import Editor from '~/editor/editor_lite';
|
||||
import setupCollapsibleInputs from './collapsible_input';
|
||||
|
||||
export default () => {
|
||||
const editor = ace.edit('editor');
|
||||
let editor;
|
||||
|
||||
$('.snippet-form-holder form').on('submit', () => {
|
||||
$('.snippet-file-content').val(editor.getValue());
|
||||
const initAce = () => {
|
||||
editor = ace.edit('editor');
|
||||
|
||||
const form = document.querySelector('.snippet-form-holder form');
|
||||
const content = document.querySelector('.snippet-file-content');
|
||||
form.addEventListener('submit', () => {
|
||||
content.value = editor.getValue();
|
||||
});
|
||||
};
|
||||
|
||||
const initMonaco = () => {
|
||||
const editorEl = document.getElementById('editor');
|
||||
const contentEl = document.querySelector('.snippet-file-content');
|
||||
const fileNameEl = document.querySelector('.snippet-file-name');
|
||||
const form = document.querySelector('.snippet-form-holder form');
|
||||
|
||||
editor = new Editor();
|
||||
editor.createInstance({
|
||||
el: editorEl,
|
||||
blobPath: fileNameEl.value,
|
||||
blobContent: contentEl.value,
|
||||
});
|
||||
|
||||
fileNameEl.addEventListener('change', () => {
|
||||
editor.updateModelLanguage(fileNameEl.value);
|
||||
});
|
||||
|
||||
form.addEventListener('submit', () => {
|
||||
contentEl.value = editor.getValue();
|
||||
});
|
||||
};
|
||||
|
||||
export const initEditor = () => {
|
||||
if (window?.gon?.features?.monacoSnippets) {
|
||||
initMonaco();
|
||||
} else {
|
||||
initAce();
|
||||
}
|
||||
setupCollapsibleInputs();
|
||||
};
|
||||
|
||||
export default () => {
|
||||
initEditor();
|
||||
};
|
||||
|
|
|
@ -229,7 +229,14 @@ class Deployment < ApplicationRecord
|
|||
end
|
||||
|
||||
def link_merge_requests(relation)
|
||||
select = relation.select(['merge_requests.id', id]).to_sql
|
||||
# NOTE: relation.select will perform column deduplication,
|
||||
# when id == environment_id it will outputs 2 columns instead of 3
|
||||
# i.e.:
|
||||
# MergeRequest.select(1, 2).to_sql #=> SELECT 1, 2 FROM "merge_requests"
|
||||
# MergeRequest.select(1, 1).to_sql #=> SELECT 1 FROM "merge_requests"
|
||||
select = relation.select('merge_requests.id',
|
||||
"#{id} as deployment_id",
|
||||
"#{environment_id} as environment_id").to_sql
|
||||
|
||||
# We don't use `Gitlab::Database.bulk_insert` here so that we don't need to
|
||||
# first pluck lots of IDs into memory.
|
||||
|
@ -238,7 +245,7 @@ class Deployment < ApplicationRecord
|
|||
# for the same deployment, only inserting any missing merge requests.
|
||||
DeploymentMergeRequest.connection.execute(<<~SQL)
|
||||
INSERT INTO #{DeploymentMergeRequest.table_name}
|
||||
(merge_request_id, deployment_id)
|
||||
(merge_request_id, deployment_id, environment_id)
|
||||
#{select}
|
||||
ON CONFLICT DO NOTHING
|
||||
SQL
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
.js-file-title.file-title-flex-parent
|
||||
= f.text_field :file_name, placeholder: s_("Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"), class: 'form-control snippet-file-name qa-snippet-file-name'
|
||||
.file-content.code
|
||||
%pre#editor= @snippet.content
|
||||
%pre#editor{ data: { 'editor-loading': true } }= @snippet.content
|
||||
= f.hidden_field :content, class: 'snippet-file-content'
|
||||
|
||||
.form-group
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replaced ACE with Monaco editor for Snippets
|
||||
merge_request: 25465
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Include full path to an upload in api response
|
||||
merge_request: 23500
|
||||
author: briankabiro
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Don't track MR deployment multiple times
|
||||
merge_request: 25537
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEnvironmentIdToDeploymentMergeRequests < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :deployment_merge_requests, :environment_id, :integer, null: true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEnvironmentIdFkToDeploymentMergeRequests < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :deployment_merge_requests, :environments, column: :environment_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key_if_exists :deployment_merge_requests, column: :environment_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEnvironmentIdMergeRequestIdUniqIdxToDeploymentMergeRequests < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :deployment_merge_requests, [:environment_id, :merge_request_id], unique: true, name: 'idx_environment_merge_requests_unique_index'
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :deployment_merge_requests, 'idx_environment_merge_requests_unique_index'
|
||||
end
|
||||
end
|
|
@ -1372,7 +1372,9 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do
|
|||
create_table "deployment_merge_requests", id: false, force: :cascade do |t|
|
||||
t.integer "deployment_id", null: false
|
||||
t.integer "merge_request_id", null: false
|
||||
t.integer "environment_id"
|
||||
t.index ["deployment_id", "merge_request_id"], name: "idx_deployment_merge_requests_unique_index", unique: true
|
||||
t.index ["environment_id", "merge_request_id"], name: "idx_environment_merge_requests_unique_index", unique: true
|
||||
t.index ["merge_request_id"], name: "index_deployment_merge_requests_on_merge_request_id"
|
||||
end
|
||||
|
||||
|
@ -4719,6 +4721,7 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do
|
|||
add_foreign_key "deployment_clusters", "clusters", on_delete: :cascade
|
||||
add_foreign_key "deployment_clusters", "deployments", on_delete: :cascade
|
||||
add_foreign_key "deployment_merge_requests", "deployments", on_delete: :cascade
|
||||
add_foreign_key "deployment_merge_requests", "environments", name: "fk_a064ff4453", on_delete: :cascade
|
||||
add_foreign_key "deployment_merge_requests", "merge_requests", on_delete: :cascade
|
||||
add_foreign_key "deployments", "clusters", name: "fk_289bba3222", on_delete: :nullify
|
||||
add_foreign_key "deployments", "projects", name: "fk_b9a3851b82", on_delete: :cascade
|
||||
|
|
|
@ -1836,11 +1836,12 @@ Returned object:
|
|||
{
|
||||
"alt": "dk",
|
||||
"url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
|
||||
"full_path": "/namespace1/project1/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
|
||||
"markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
|
||||
}
|
||||
```
|
||||
|
||||
>**Note**: The returned `url` is relative to the project path.
|
||||
>**Note**: The returned `url` is relative to the project path. The returned `full_path` is the absolute path to the file.
|
||||
In Markdown contexts, the link is automatically expanded when the format in
|
||||
`markdown` is used.
|
||||
|
||||
|
|
|
@ -175,6 +175,21 @@ are loaded dynamically with webpack.
|
|||
Do not use `innerHTML`, `append()` or `html()` to set content. It opens up too many
|
||||
vulnerabilities.
|
||||
|
||||
## Avoid single-line conditional statements
|
||||
|
||||
Indentation is important when scanning code as it gives a quick indication of the existence of branches, loops, and return points.
|
||||
This can help to quickly understand the control flow.
|
||||
|
||||
```javascript
|
||||
// bad
|
||||
if (isThingNull) return '';
|
||||
|
||||
// good
|
||||
if (isThingNull) {
|
||||
return '';
|
||||
}
|
||||
```
|
||||
|
||||
## ESLint
|
||||
|
||||
ESLint behaviour can be found in our [tooling guide](../tooling.md).
|
||||
|
|
|
@ -37,6 +37,8 @@ Design Management requires that projects are using
|
|||
[hashed storage](../../../administration/repository_storage_types.md#hashed-storage)
|
||||
(the default storage type since v10.0).
|
||||
|
||||
If the requirements are not met, the **Designs** tab displays a message to the user.
|
||||
|
||||
### Feature Flags
|
||||
|
||||
- Reference Parsing
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectUpload < Grape::Entity
|
||||
include Gitlab::Routing
|
||||
|
||||
expose :markdown_name, as: :alt
|
||||
expose :secure_url, as: :url
|
||||
expose :full_path do |uploader|
|
||||
show_project_uploads_path(
|
||||
uploader.model,
|
||||
uploader.secret,
|
||||
uploader.filename
|
||||
)
|
||||
end
|
||||
|
||||
expose :markdown_link, as: :markdown
|
||||
end
|
||||
end
|
||||
end
|
|
@ -494,7 +494,9 @@ module API
|
|||
requires :file, type: File, desc: 'The file to be uploaded' # rubocop:disable Scalability/FileUploads
|
||||
end
|
||||
post ":id/uploads" do
|
||||
UploadService.new(user_project, params[:file]).execute.to_h
|
||||
upload = UploadService.new(user_project, params[:file]).execute
|
||||
|
||||
present upload, with: Entities::ProjectUpload
|
||||
end
|
||||
|
||||
desc 'Get the users list of a project' do
|
||||
|
|
|
@ -1293,6 +1293,15 @@ msgstr ""
|
|||
msgid "AdminArea|Stopping jobs failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Users statistics"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Users total"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Users with highest role"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
|
||||
msgstr ""
|
||||
|
||||
|
@ -6669,6 +6678,9 @@ msgstr ""
|
|||
msgid "DesignManagement|The one place for your designs"
|
||||
msgstr ""
|
||||
|
||||
msgid "DesignManagement|To enable design management, you'll need to %{requirements_link_start}meet the requirements%{requirements_link_end}. If you need help, reach out to our %{support_link_start}support team%{support_link_end} for assistance."
|
||||
msgstr ""
|
||||
|
||||
msgid "DesignManagement|Upload and view the latest designs for this issue. Consistent and easy to find, so everyone is up to date."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19358,6 +19370,9 @@ msgstr ""
|
|||
msgid "The number of times an upload record could not find its file"
|
||||
msgstr ""
|
||||
|
||||
msgid "The one place for your designs"
|
||||
msgstr ""
|
||||
|
||||
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ module QA
|
|||
private
|
||||
|
||||
def text_area
|
||||
find('#editor>textarea', visible: false)
|
||||
find('#editor textarea', visible: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Projects > Snippets > Create Snippet', :js do
|
||||
include DropzoneHelper
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
shared_examples_for 'snippet editor' do
|
||||
before do
|
||||
stub_feature_flags(monaco_snippets: flag)
|
||||
end
|
||||
|
||||
def description_field
|
||||
find('.js-description-input').find('input,textarea')
|
||||
|
@ -20,7 +19,8 @@ describe 'Projects > Snippets > Create Snippet', :js do
|
|||
fill_in 'project_snippet_description', with: 'My Snippet **Description**'
|
||||
|
||||
page.within('.file-editor') do
|
||||
find('.ace_text-input', visible: false).send_keys('Hello World!')
|
||||
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
|
||||
el.send_keys 'Hello World!'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -33,6 +33,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
|
|||
visit project_snippets_path(project)
|
||||
|
||||
click_on('New snippet')
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'shows collapsible description input' do
|
||||
|
@ -111,3 +112,22 @@ describe 'Projects > Snippets > Create Snippet', :js do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Projects > Snippets > Create Snippet', :js do
|
||||
include DropzoneHelper
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
context 'when using Monaco' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using ACE' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'User creates snippet', :js do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
shared_examples_for 'snippet editor' do
|
||||
def description_field
|
||||
find('.js-description-input').find('input,textarea')
|
||||
end
|
||||
|
@ -12,6 +10,7 @@ describe 'User creates snippet', :js do
|
|||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
stub_feature_flags(snippets_vue: false)
|
||||
stub_feature_flags(monaco_snippets: flag)
|
||||
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
|
||||
|
||||
Gitlab::CurrentSettings.update!(
|
||||
|
@ -33,7 +32,8 @@ describe 'User creates snippet', :js do
|
|||
|
||||
find('#personal_snippet_visibility_level_20').set(true)
|
||||
page.within('.file-editor') do
|
||||
find('.ace_text-input', visible: false).send_keys 'Hello World!'
|
||||
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
|
||||
el.send_keys 'Hello World!'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -80,3 +80,19 @@ describe 'User creates snippet', :js do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'User creates snippet', :js do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
context 'when using Monaco' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using ACE' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'User creates snippet', :js do
|
||||
include DropzoneHelper
|
||||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
shared_examples_for 'snippet editor' do
|
||||
before do
|
||||
stub_feature_flags(snippets_vue: false)
|
||||
stub_feature_flags(monaco_snippets: flag)
|
||||
sign_in(user)
|
||||
visit new_snippet_path
|
||||
end
|
||||
|
@ -25,7 +22,8 @@ describe 'User creates snippet', :js do
|
|||
fill_in 'personal_snippet_description', with: 'My Snippet **Description**'
|
||||
|
||||
page.within('.file-editor') do
|
||||
find('.ace_text-input', visible: false).send_keys 'Hello World!'
|
||||
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
|
||||
el.send_keys 'Hello World!'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -109,7 +107,8 @@ describe 'User creates snippet', :js do
|
|||
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
||||
page.within('.file-editor') do
|
||||
find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name'
|
||||
find('.ace_text-input', visible: false).send_keys 'Hello World!'
|
||||
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
|
||||
el.send_keys 'Hello World!'
|
||||
end
|
||||
|
||||
click_button 'Create snippet'
|
||||
|
@ -120,3 +119,21 @@ describe 'User creates snippet', :js do
|
|||
expect(page).to have_content('Hello World!')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'User creates snippet', :js do
|
||||
include DropzoneHelper
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
context 'when using Monaco' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using ACE' do
|
||||
it_behaves_like "snippet editor" do
|
||||
let(:flag) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import Editor from '~/editor/editor_lite';
|
||||
import { initEditor } from '~/snippet/snippet_bundle';
|
||||
import { setHTMLFixture } from 'helpers/fixtures';
|
||||
|
||||
jest.mock('~/editor/editor_lite', () => jest.fn());
|
||||
|
||||
describe('Snippet editor', () => {
|
||||
describe('Monaco editor for Snippets', () => {
|
||||
let oldGon;
|
||||
let editorEl;
|
||||
let contentEl;
|
||||
let fileNameEl;
|
||||
let form;
|
||||
|
||||
const mockName = 'foo.bar';
|
||||
const mockContent = 'Foo Bar';
|
||||
const updatedMockContent = 'New Foo Bar';
|
||||
|
||||
const mockEditor = {
|
||||
createInstance: jest.fn(),
|
||||
updateModelLanguage: jest.fn(),
|
||||
getValue: jest.fn().mockReturnValueOnce(updatedMockContent),
|
||||
};
|
||||
Editor.mockImplementation(() => mockEditor);
|
||||
|
||||
function setUpFixture(name, content) {
|
||||
setHTMLFixture(`
|
||||
<div class="snippet-form-holder">
|
||||
<form>
|
||||
<input class="snippet-file-name" type="text" value="${name}">
|
||||
<input class="snippet-file-content" type="hidden" value="${content}">
|
||||
<pre id="editor"></pre>
|
||||
</form>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
function bootstrap(name = '', content = '') {
|
||||
setUpFixture(name, content);
|
||||
editorEl = document.getElementById('editor');
|
||||
contentEl = document.querySelector('.snippet-file-content');
|
||||
fileNameEl = document.querySelector('.snippet-file-name');
|
||||
form = document.querySelector('.snippet-form-holder form');
|
||||
|
||||
initEditor();
|
||||
}
|
||||
|
||||
function createEvent(name) {
|
||||
return new Event(name, {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
oldGon = window.gon;
|
||||
window.gon = { features: { monacoSnippets: true } };
|
||||
bootstrap(mockName, mockContent);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.gon = oldGon;
|
||||
});
|
||||
|
||||
it('correctly initializes Editor', () => {
|
||||
expect(mockEditor.createInstance).toHaveBeenCalledWith({
|
||||
el: editorEl,
|
||||
blobPath: mockName,
|
||||
blobContent: mockContent,
|
||||
});
|
||||
});
|
||||
|
||||
it('listens to file name changes and updates syntax highlighting of code', () => {
|
||||
expect(mockEditor.updateModelLanguage).not.toHaveBeenCalled();
|
||||
|
||||
const event = createEvent('change');
|
||||
|
||||
fileNameEl.value = updatedMockContent;
|
||||
fileNameEl.dispatchEvent(event);
|
||||
|
||||
expect(mockEditor.updateModelLanguage).toHaveBeenCalledWith(updatedMockContent);
|
||||
});
|
||||
|
||||
it('listens to form submit event and populates the hidden field with most recent version of the content', () => {
|
||||
expect(contentEl.value).toBe(mockContent);
|
||||
|
||||
const event = createEvent('submit');
|
||||
|
||||
form.dispatchEvent(event);
|
||||
expect(contentEl.value).toBe(updatedMockContent);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -195,6 +195,7 @@ describe Gitlab::Profiler do
|
|||
|
||||
describe '.print_by_total_time' do
|
||||
let(:stdout) { StringIO.new }
|
||||
let(:regexp) { /^\s+\d+\.\d+\s+(\d+\.\d+)/ }
|
||||
|
||||
let(:output) do
|
||||
stdout.rewind
|
||||
|
@ -202,6 +203,8 @@ describe Gitlab::Profiler do
|
|||
end
|
||||
|
||||
let_it_be(:result) do
|
||||
Thread.new { sleep 1 }
|
||||
|
||||
RubyProf.profile do
|
||||
sleep 0.1
|
||||
1.to_s
|
||||
|
@ -212,23 +215,22 @@ describe Gitlab::Profiler do
|
|||
stub_const('STDOUT', stdout)
|
||||
end
|
||||
|
||||
it 'prints a profile result sorted by total time', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/206907' do
|
||||
it 'prints a profile result sorted by total time' do
|
||||
described_class.print_by_total_time(result)
|
||||
|
||||
total_times =
|
||||
output
|
||||
.scan(/^\s+\d+\.\d+\s+(\d+\.\d+)/)
|
||||
.map { |(total)| total.to_f }
|
||||
|
||||
expect(output).to include('Kernel#sleep')
|
||||
|
||||
if total_times != total_times.sort.reverse
|
||||
warn "Profiler test failed, output is:"
|
||||
warn output
|
||||
end
|
||||
thread_profiles = output.split('Sort by: total_time').select { |x| x =~ regexp }
|
||||
|
||||
expect(total_times).to eq(total_times.sort.reverse)
|
||||
expect(total_times).not_to eq(total_times.uniq)
|
||||
thread_profiles.each do |profile|
|
||||
total_times =
|
||||
profile
|
||||
.scan(regexp)
|
||||
.map { |(total)| total.to_f }
|
||||
|
||||
expect(total_times).to eq(total_times.sort.reverse)
|
||||
expect(total_times).not_to eq(total_times.uniq)
|
||||
end
|
||||
end
|
||||
|
||||
it 'accepts a max_percent option' do
|
||||
|
|
|
@ -1250,6 +1250,8 @@ describe API::Projects do
|
|||
expect(json_response['alt']).to eq("dk")
|
||||
expect(json_response['url']).to start_with("/uploads/")
|
||||
expect(json_response['url']).to end_with("/dk.png")
|
||||
|
||||
expect(json_response['full_path']).to start_with("/#{project.namespace.path}/#{project.path}/uploads")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -133,6 +133,34 @@ describe Deployments::LinkMergeRequestsService do
|
|||
expect(deploy.merge_requests).to include(mr1, picked_mr)
|
||||
end
|
||||
|
||||
it "doesn't link the same merge_request twice" do
|
||||
create(:merge_request, :merged, merge_commit_sha: mr1_merge_commit_sha,
|
||||
source_project: project)
|
||||
|
||||
picked_mr = create(:merge_request, :merged, merge_commit_sha: '123abc',
|
||||
source_project: project)
|
||||
|
||||
# the first MR includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
|
||||
create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
|
||||
|
||||
environment = create(:environment, project: project)
|
||||
old_deploy =
|
||||
create(:deployment, :success, project: project, environment: environment)
|
||||
|
||||
# manually linking all the MRs to the old_deploy
|
||||
old_deploy.link_merge_requests(project.merge_requests)
|
||||
|
||||
deploy =
|
||||
create(:deployment, :success, project: project, environment: environment)
|
||||
|
||||
described_class.new(deploy).link_merge_requests_for_range(
|
||||
first_deployment_sha,
|
||||
mr1_merge_commit_sha
|
||||
)
|
||||
|
||||
expect(deploy.merge_requests).to be_empty
|
||||
end
|
||||
|
||||
context 'when :track_mr_picking feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(track_mr_picking: false)
|
||||
|
|
Loading…
Reference in New Issue