Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7c38405be9
commit
219eead23f
35 changed files with 359 additions and 62 deletions
|
@ -305,7 +305,6 @@ linters:
|
|||
- 'app/views/shared/_milestone_expired.html.haml'
|
||||
- 'app/views/shared/_no_password.html.haml'
|
||||
- 'app/views/shared/_no_ssh.html.haml'
|
||||
- 'app/views/shared/_outdated_browser.html.haml'
|
||||
- 'app/views/shared/_ping_consent.html.haml'
|
||||
- 'app/views/shared/_project_limit.html.haml'
|
||||
- 'app/views/shared/boards/components/_board.html.haml'
|
||||
|
|
|
@ -25,6 +25,8 @@ import { severityLevel, severityLevelVariant, errorStatus } from './constants';
|
|||
|
||||
import query from '../queries/details.query.graphql';
|
||||
|
||||
const SENTRY_TIMEOUT = 10000;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
|
@ -87,6 +89,8 @@ export default {
|
|||
if (res.data.project?.sentryErrors?.detailedError) {
|
||||
this.$apollo.queries.error.stopPolling();
|
||||
this.setStatus(this.error.status);
|
||||
} else {
|
||||
this.onNoApolloResult();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -94,6 +98,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
error: null,
|
||||
errorLoading: true,
|
||||
errorPollTimeout: 0,
|
||||
issueCreationInProgress: false,
|
||||
isAlertVisible: false,
|
||||
closedIssueId: null,
|
||||
|
@ -158,8 +164,19 @@ export default {
|
|||
return this.errorStatus !== errorStatus.RESOLVED ? __('Resolve') : __('Unresolve');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
error(val) {
|
||||
if (val) {
|
||||
this.errorLoading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.startPollingStacktrace(this.issueStackTracePath);
|
||||
this.errorPollTimeout = Date.now() + SENTRY_TIMEOUT;
|
||||
this.$apollo.queries.error.setOptions({
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
...mapActions('details', [
|
||||
|
@ -191,6 +208,13 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
onNoApolloResult() {
|
||||
if (Date.now() > this.errorPollTimeout) {
|
||||
this.$apollo.queries.error.stopPolling();
|
||||
this.errorLoading = false;
|
||||
createFlash(__('Could not connect to Sentry. Refresh the page to try again.'), 'warning');
|
||||
}
|
||||
},
|
||||
formatDate(date) {
|
||||
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
|
||||
},
|
||||
|
@ -200,7 +224,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="$apollo.queries.error.loading" class="py-3">
|
||||
<div v-if="errorLoading" class="py-3">
|
||||
<gl-loading-icon :size="3" />
|
||||
</div>
|
||||
<div v-else-if="error" class="error-details">
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Import::GitlabProjectsController < Import::BaseController
|
||||
include WorkhorseRequest
|
||||
|
||||
before_action :whitelist_query_limiting, only: [:create]
|
||||
before_action :verify_gitlab_project_import_enabled
|
||||
|
||||
skip_before_action :verify_authenticity_token, only: [:authorize]
|
||||
before_action :verify_workhorse_api!, only: [:authorize]
|
||||
|
||||
def new
|
||||
@namespace = Namespace.find(project_params[:namespace_id])
|
||||
return render_404 unless current_user.can?(:create_projects, @namespace)
|
||||
|
@ -28,10 +33,29 @@ class Import::GitlabProjectsController < Import::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def authorize
|
||||
set_workhorse_internal_api_content_type
|
||||
|
||||
authorized = ImportExportUploader.workhorse_authorize(
|
||||
has_length: false,
|
||||
maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
|
||||
|
||||
render json: authorized
|
||||
rescue SocketError
|
||||
render json: _("Error uploading file"), status: :internal_server_error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def file_is_valid?
|
||||
# TODO: remove the condition and the private method after the WH version including
|
||||
# https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/470
|
||||
# is released and GITLAB_WORKHORSE_VERSION is updated accordingly.
|
||||
if with_workhorse_upload_acceleration?
|
||||
return false unless project_params[:file].is_a?(::UploadedFile)
|
||||
else
|
||||
return false unless project_params[:file] && project_params[:file].respond_to?(:read)
|
||||
end
|
||||
|
||||
filename = project_params[:file].original_filename
|
||||
|
||||
|
@ -51,4 +75,8 @@ class Import::GitlabProjectsController < Import::BaseController
|
|||
def whitelist_query_limiting
|
||||
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
|
||||
end
|
||||
|
||||
def with_workhorse_upload_acceleration?
|
||||
request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER].present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -227,7 +227,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def outdated_browser?
|
||||
browser.ie? && browser.version.to_i < 10
|
||||
browser.ie?
|
||||
end
|
||||
|
||||
def path_to_key(key, admin = false)
|
||||
|
|
|
@ -111,8 +111,8 @@ module BulkInsertSafe
|
|||
end
|
||||
|
||||
def _bulk_insert_reject_primary_key!(attributes, primary_key)
|
||||
if attributes.delete(primary_key)
|
||||
raise PrimaryKeySetError, "Primary key set: #{primary_key}:#{attributes[primary_key]}\n" \
|
||||
if existing_pk = attributes.delete(primary_key)
|
||||
raise PrimaryKeySetError, "Primary key set: #{primary_key}:#{existing_pk}\n" \
|
||||
"Bulk-inserts are only supported for rows that don't already have PK set"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,11 +93,14 @@ module BulkInsertableAssociations
|
|||
end
|
||||
|
||||
def _bulk_insert_configure_foreign_key(reflection, items)
|
||||
primary_key = self[reflection.active_record_primary_key]
|
||||
raise "Classes including `BulkInsertableAssociations` must define a `primary_key`" unless primary_key
|
||||
primary_key_column = reflection.active_record_primary_key
|
||||
raise "Classes including `BulkInsertableAssociations` must define a `primary_key`" unless primary_key_column
|
||||
|
||||
primary_key_value = self[primary_key_column]
|
||||
raise "No value found for primary key `#{primary_key_column}`" unless primary_key_value
|
||||
|
||||
items.each do |item|
|
||||
item[reflection.foreign_key] = primary_key
|
||||
item[reflection.foreign_key] = primary_key_value
|
||||
|
||||
if reflection.type
|
||||
item[reflection.type] = self.class.polymorphic_name
|
||||
|
|
|
@ -14,23 +14,23 @@ module NotificationRecipients
|
|||
end
|
||||
|
||||
def self.build_recipients(*args)
|
||||
Builder::Default.new(*args).notification_recipients
|
||||
::NotificationRecipients::Builder::Default.new(*args).notification_recipients
|
||||
end
|
||||
|
||||
def self.build_new_note_recipients(*args)
|
||||
Builder::NewNote.new(*args).notification_recipients
|
||||
::NotificationRecipients::Builder::NewNote.new(*args).notification_recipients
|
||||
end
|
||||
|
||||
def self.build_merge_request_unmergeable_recipients(*args)
|
||||
Builder::MergeRequestUnmergeable.new(*args).notification_recipients
|
||||
::NotificationRecipients::Builder::MergeRequestUnmergeable.new(*args).notification_recipients
|
||||
end
|
||||
|
||||
def self.build_project_maintainers_recipients(*args)
|
||||
Builder::ProjectMaintainers.new(*args).notification_recipients
|
||||
::NotificationRecipients::Builder::ProjectMaintainers.new(*args).notification_recipients
|
||||
end
|
||||
|
||||
def self.build_new_release_recipients(*args)
|
||||
Builder::NewRelease.new(*args).notification_recipients
|
||||
::NotificationRecipients::Builder::NewRelease.new(*args).notification_recipients
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
- if defined?(nav) && nav
|
||||
= render "layouts/nav/sidebar/#{nav}"
|
||||
.content-wrapper{ class: "#{@content_wrapper_class}" }
|
||||
= render 'shared/outdated_browser'
|
||||
.mobile-overlay
|
||||
.alert-wrapper
|
||||
= render 'shared/outdated_browser'
|
||||
= render_if_exists "layouts/header/ee_license_banner"
|
||||
= render "layouts/broadcast"
|
||||
= render "layouts/header/read_only_banner"
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
= render 'peek/bar'
|
||||
= header_message
|
||||
= render partial: "layouts/header/default", locals: { project: @project, group: @group }
|
||||
= render 'shared/outdated_browser'
|
||||
.mobile-overlay
|
||||
.alert-wrapper
|
||||
= render 'shared/outdated_browser'
|
||||
= render "layouts/broadcast"
|
||||
= yield :flash_message
|
||||
= render "layouts/flash"
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
- if outdated_browser?
|
||||
.flash-container
|
||||
.flash-alert.text-center
|
||||
GitLab may not work properly because you are using an outdated web browser.
|
||||
.gl-alert.gl-alert-danger.outdated-browser{ :role => "alert" }
|
||||
= sprite_icon('error', size: 16, css_class: "gl-alert-icon gl-alert-icon-no-title gl-icon")
|
||||
.gl-alert-body
|
||||
- if browser.ie? && browser.version.to_i == 11
|
||||
- feedback_link_url = 'https://gitlab.com/gitlab-org/gitlab/issues/197987'
|
||||
- feedback_link_start = '<a href="%{url}" class="gl-link" target="_blank" rel="noopener noreferrer">'.html_safe % { url: feedback_link_url }
|
||||
= s_('OutdatedBrowser|From May 2020 GitLab no longer supports Internet Explorer 11.')
|
||||
%br
|
||||
Please install a
|
||||
= link_to 'supported web browser', help_page_path('install/requirements', anchor: 'supported-web-browsers')
|
||||
for a better experience.
|
||||
= s_('OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels.').html_safe % { feedback_link_start: feedback_link_start, feedback_link_end: '</a>'.html_safe }
|
||||
- else
|
||||
= s_('OutdatedBrowser|GitLab may not work properly, because you are using an outdated web browser.')
|
||||
%br
|
||||
- browser_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('install/requirements', anchor: 'supported-web-browsers') }
|
||||
= s_('OutdatedBrowser|Please install a %{browser_link_start}supported web browser%{browser_link_end} for a better experience.').html_safe % { browser_link_start: browser_link_start, browser_link_end: '</a>'.html_safe }
|
||||
|
|
5
changelogs/unreleased/118503.yml
Normal file
5
changelogs/unreleased/118503.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix infinite spinner on error detail page
|
||||
merge_request: 26188
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove unused file_type column from packages_package_files
|
||||
merge_request: 26527
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use Workhorse acceleration for Project Import file upload via UI
|
||||
merge_request: 26278
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix package file finder for conan packages with a conan_package_reference filter
|
||||
merge_request: 26240
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Default to generating blob links for missing paths
|
||||
merge_request: 26817
|
||||
author:
|
||||
type: fixed
|
|
@ -1226,6 +1226,8 @@ test:
|
|||
gitaly:
|
||||
client_path: tmp/tests/gitaly
|
||||
token: secret
|
||||
workhorse:
|
||||
secret_file: tmp/tests/gitlab_workhorse_secret
|
||||
backup:
|
||||
path: tmp/tests/backups
|
||||
pseudonymizer:
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace :import do
|
|||
|
||||
resource :gitlab_project, only: [:create, :new] do
|
||||
post :create
|
||||
post :authorize
|
||||
end
|
||||
|
||||
resource :manifest, only: [:create, :new], controller: :manifest do
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveFileTypeFromPackagesPackageFiles < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
remove_column :packages_package_files, :file_type, :integer
|
||||
end
|
||||
end
|
|
@ -3029,7 +3029,6 @@ ActiveRecord::Schema.define(version: 2020_03_06_170531) do
|
|||
t.datetime_with_timezone "created_at", null: false
|
||||
t.datetime_with_timezone "updated_at", null: false
|
||||
t.bigint "size"
|
||||
t.integer "file_type"
|
||||
t.integer "file_store"
|
||||
t.binary "file_md5"
|
||||
t.binary "file_sha1"
|
||||
|
|
|
@ -236,18 +236,21 @@ For reference, GitLab.com's [auto-scaling shared runner](../user/gitlab_com/inde
|
|||
|
||||
## Supported web browsers
|
||||
|
||||
CAUTION: **Caution:** With GitLab 13.0 (May 2020) we are removing official support for Internet Explorer 11.
|
||||
With the release of GitLab 13.4 (September 2020) we will remove all code that supports Internet Explorer 11.
|
||||
You can provide feedback [on this issue](https://gitlab.com/gitlab-org/gitlab/issues/197987) or via your usual support channels.
|
||||
|
||||
GitLab supports the following web browsers:
|
||||
|
||||
- Firefox
|
||||
- Chrome/Chromium
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- Internet Explorer 11
|
||||
- Internet Explorer 11 (until May 2020)
|
||||
|
||||
For the listed web browsers, GitLab supports:
|
||||
|
||||
- The current and previous major versions of browsers except Internet Explorer.
|
||||
- Only version 11 of Internet Explorer.
|
||||
- The current minor version of a supported major version.
|
||||
|
||||
NOTE: **Note:** We do not support running GitLab with JavaScript disabled in the browser and have no plans of supporting that
|
||||
|
|
|
@ -74,6 +74,8 @@ You can view the exact JSON payload in the administration panel. To view the pay
|
|||
|
||||
You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/version_usage_stats_to_stage_mappings.csv).
|
||||
|
||||
Usage ping is important to GitLab as we use it to calculate our [Action Monthly Active Users (AMAU)](https://about.gitlab.com/handbook/product/metrics/#action-monthly-active-users-amau) which helps us measure the success of our features.
|
||||
|
||||
### Request flow example
|
||||
|
||||
The following example shows a basic request/response flow between the self-managed GitLab instance, GitLab Version Application,
|
||||
|
|
|
@ -131,7 +131,7 @@ module Banzai
|
|||
path = cleaned_file_path(uri)
|
||||
nested_path = relative_file_path(uri)
|
||||
|
||||
file_exists?(nested_path) ? nested_path : path
|
||||
path_exists?(nested_path) ? nested_path : path
|
||||
end
|
||||
|
||||
def cleaned_file_path(uri)
|
||||
|
@ -190,12 +190,12 @@ module Banzai
|
|||
parts.push(path).join('/')
|
||||
end
|
||||
|
||||
def file_exists?(path)
|
||||
path.present? && uri_type(path).present?
|
||||
def path_exists?(path)
|
||||
path.present? && @uri_types[path] != :unknown
|
||||
end
|
||||
|
||||
def uri_type(path)
|
||||
@uri_types[path] == :unknown ? "" : @uri_types[path]
|
||||
@uri_types[path] == :unknown ? :blob : @uri_types[path]
|
||||
end
|
||||
|
||||
def current_commit
|
||||
|
|
|
@ -5609,6 +5609,9 @@ msgstr ""
|
|||
msgid "Could not connect to FogBugz, check your URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not connect to Sentry. Refresh the page to try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not connect to Web IDE file mirror service."
|
||||
msgstr ""
|
||||
|
||||
|
@ -13697,6 +13700,18 @@ msgstr ""
|
|||
msgid "Outbound requests"
|
||||
msgstr ""
|
||||
|
||||
msgid "OutdatedBrowser|From May 2020 GitLab no longer supports Internet Explorer 11."
|
||||
msgstr ""
|
||||
|
||||
msgid "OutdatedBrowser|GitLab may not work properly, because you are using an outdated web browser."
|
||||
msgstr ""
|
||||
|
||||
msgid "OutdatedBrowser|Please install a %{browser_link_start}supported web browser%{browser_link_end} for a better experience."
|
||||
msgstr ""
|
||||
|
||||
msgid "OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels."
|
||||
msgstr ""
|
||||
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -39,4 +39,62 @@ describe Import::GitlabProjectsController do
|
|||
|
||||
it_behaves_like 'project import rate limiter'
|
||||
end
|
||||
|
||||
describe 'POST authorize' do
|
||||
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
|
||||
|
||||
before do
|
||||
request.headers['GitLab-Workhorse'] = '1.0'
|
||||
request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = workhorse_token
|
||||
end
|
||||
|
||||
it 'authorizes importing project with workhorse header' do
|
||||
post :authorize, format: :json
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
end
|
||||
|
||||
it 'rejects requests that bypassed gitlab-workhorse or have invalid header' do
|
||||
request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = 'INVALID_HEADER'
|
||||
|
||||
expect { post :authorize, format: :json }.to raise_error(JWT::DecodeError)
|
||||
end
|
||||
|
||||
context 'when using remote storage' do
|
||||
context 'when direct upload is enabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true)
|
||||
end
|
||||
|
||||
it 'responds with status 200, location of file remote store and object details' do
|
||||
post :authorize, format: :json
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response).not_to have_key('TempPath')
|
||||
expect(json_response['RemoteObject']).to have_key('ID')
|
||||
expect(json_response['RemoteObject']).to have_key('GetURL')
|
||||
expect(json_response['RemoteObject']).to have_key('StoreURL')
|
||||
expect(json_response['RemoteObject']).to have_key('DeleteURL')
|
||||
expect(json_response['RemoteObject']).to have_key('MultipartUpload')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is disabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false)
|
||||
end
|
||||
|
||||
it 'handles as a local file' do
|
||||
post :authorize, format: :json
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
|
||||
expect(json_response['RemoteObject']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -156,7 +156,7 @@ describe "User creates issue" do
|
|||
expect(page.find_field("issue_description").value).not_to match /\n\n$/
|
||||
end
|
||||
|
||||
it "cancels a file upload correctly" do
|
||||
it "cancels a file upload correctly", :capybara_ignore_server_errors do
|
||||
slow_requests do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ describe 'Project > Tags', :js do
|
|||
end
|
||||
end
|
||||
|
||||
it 'shows "Attaching a file" message on uploading 1 file', :js do
|
||||
it 'shows "Attaching a file" message on uploading 1 file', :js, :capybara_ignore_server_errors do
|
||||
slow_requests do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ describe 'User uploads file to note' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'uploading is in progress' do
|
||||
context 'uploading is in progress', :capybara_ignore_server_errors do
|
||||
it 'cancels uploading on clicking to "Cancel" button', :js do
|
||||
slow_requests do
|
||||
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
|
||||
|
|
|
@ -59,7 +59,9 @@ describe('diffs/components/commit_item', () => {
|
|||
expect(titleElement.text()).toBe(commit.title_html);
|
||||
});
|
||||
|
||||
it('renders commit description', () => {
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/209776
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('renders commit description', () => {
|
||||
const descElement = getDescElement();
|
||||
const descExpandElement = getDescExpandElement();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { __ } from '~/locale';
|
||||
import createFlash from '~/flash';
|
||||
import {
|
||||
GlButton,
|
||||
GlLoadingIcon,
|
||||
|
@ -18,6 +19,8 @@ import {
|
|||
errorStatus,
|
||||
} from '~/error_tracking/components/constants';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
|
@ -49,18 +52,6 @@ describe('ErrorDetails', () => {
|
|||
csrfToken: 'fakeToken',
|
||||
},
|
||||
});
|
||||
wrapper.setData({
|
||||
error: {
|
||||
id: 'gid://gitlab/Gitlab::ErrorTracking::DetailedError/129381',
|
||||
sentryId: 129381,
|
||||
title: 'Issue title',
|
||||
externalUrl: 'http://sentry.gitlab.net/gitlab',
|
||||
firstSeen: '2017-05-26T13:32:48Z',
|
||||
lastSeen: '2018-05-26T13:32:48Z',
|
||||
count: 12,
|
||||
userCount: 2,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -78,6 +69,7 @@ describe('ErrorDetails', () => {
|
|||
const state = {
|
||||
stacktraceData: {},
|
||||
loadingStacktrace: true,
|
||||
errorStatus: '',
|
||||
};
|
||||
|
||||
store = new Vuex.Store({
|
||||
|
@ -99,6 +91,7 @@ describe('ErrorDetails', () => {
|
|||
error: {
|
||||
loading: true,
|
||||
stopPolling: jest.fn(),
|
||||
setOptions: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -123,10 +116,61 @@ describe('ErrorDetails', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('sentry response timeout', () => {
|
||||
const initTime = 300000;
|
||||
const endTime = initTime + 10000;
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.$apollo.queries.error.loading = false;
|
||||
jest.spyOn(Date, 'now').mockReturnValue(initTime);
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('when before timeout, still shows loading', () => {
|
||||
Date.now.mockReturnValue(endTime - 1);
|
||||
|
||||
wrapper.vm.onNoApolloResult();
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
|
||||
expect(createFlash).not.toHaveBeenCalled();
|
||||
expect(mocks.$apollo.queries.error.stopPolling).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('when timeout is hit and no apollo result, stops loading and shows flash', () => {
|
||||
Date.now.mockReturnValue(endTime + 1);
|
||||
|
||||
wrapper.vm.onNoApolloResult();
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
|
||||
expect(wrapper.find(GlLink).exists()).toBe(false);
|
||||
expect(createFlash).toHaveBeenCalledWith(
|
||||
'Could not connect to Sentry. Refresh the page to try again.',
|
||||
'warning',
|
||||
);
|
||||
expect(mocks.$apollo.queries.error.stopPolling).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error details', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$apollo.queries.error.loading = false;
|
||||
mountComponent();
|
||||
wrapper.setData({
|
||||
error: {
|
||||
id: 'gid://gitlab/Gitlab::ErrorTracking::DetailedError/129381',
|
||||
sentryId: 129381,
|
||||
title: 'Issue title',
|
||||
externalUrl: 'http://sentry.gitlab.net/gitlab',
|
||||
firstSeen: '2017-05-26T13:32:48Z',
|
||||
lastSeen: '2018-05-26T13:32:48Z',
|
||||
count: 12,
|
||||
userCount: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should show Sentry error details without stacktrace', () => {
|
||||
|
@ -232,10 +276,6 @@ describe('ErrorDetails', () => {
|
|||
});
|
||||
|
||||
describe('When a user clicks the create issue button', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('should send sentry_issue_identifier', () => {
|
||||
const sentryErrorIdInput = findInput(
|
||||
'issue[sentry_issue_attributes][sentry_issue_identifier]',
|
||||
|
@ -275,7 +315,8 @@ describe('ErrorDetails', () => {
|
|||
describe('when error is unresolved', () => {
|
||||
beforeEach(() => {
|
||||
store.state.details.errorStatus = errorStatus.UNRESOLVED;
|
||||
mountComponent();
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('displays Ignore and Resolve buttons', () => {
|
||||
|
@ -301,7 +342,8 @@ describe('ErrorDetails', () => {
|
|||
describe('when error is ignored', () => {
|
||||
beforeEach(() => {
|
||||
store.state.details.errorStatus = errorStatus.IGNORED;
|
||||
mountComponent();
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('displays Undo Ignore and Resolve buttons', () => {
|
||||
|
@ -327,7 +369,8 @@ describe('ErrorDetails', () => {
|
|||
describe('when error is resolved', () => {
|
||||
beforeEach(() => {
|
||||
store.state.details.errorStatus = errorStatus.RESOLVED;
|
||||
mountComponent();
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('displays Ignore and Unresolve buttons', () => {
|
||||
|
|
|
@ -114,7 +114,7 @@ describe MarkupHelper do
|
|||
let(:requested_path) { nil }
|
||||
|
||||
it 'returns the link to the image path as a relative path' do
|
||||
expanded_path = "/#{project.full_path}/master/./#{image_file}"
|
||||
expanded_path = "/#{project.full_path}/-/blob/master/./#{image_file}"
|
||||
|
||||
expect(subject.css('a')[0].attr('href')).to eq(expanded_path)
|
||||
end
|
||||
|
|
|
@ -145,7 +145,7 @@ describe Banzai::Filter::RepositoryLinkFilter do
|
|||
it 'ignores ref if commit is passed' do
|
||||
doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') )
|
||||
expect(doc.at_css('a')['href'])
|
||||
.to eq "/#{project_path}/#{ref}/non/existent.file" # non-existent files have no leading blob/raw/tree
|
||||
.to eq "/#{project_path}/-/blob/#{ref}/non/existent.file"
|
||||
end
|
||||
|
||||
shared_examples :valid_repository do
|
||||
|
@ -201,6 +201,12 @@ describe Banzai::Filter::RepositoryLinkFilter do
|
|||
.to eq "/#{project_path}/-/blob/#{ref}/doc/api/README.md"
|
||||
end
|
||||
|
||||
it 'rebuilds relative URL for a missing file in the repo' do
|
||||
doc = filter(link('missing-file'))
|
||||
expect(doc.at_css('a')['href'])
|
||||
.to eq "/#{project_path}/-/blob/#{ref}/missing-file"
|
||||
end
|
||||
|
||||
it 'rebuilds relative URL for a file in the repo with leading ./' do
|
||||
doc = filter(link('./doc/api/README.md'))
|
||||
expect(doc.at_css('a')['href'])
|
||||
|
|
|
@ -57,16 +57,12 @@ describe BulkInsertableAssociations do
|
|||
end
|
||||
end
|
||||
|
||||
before do
|
||||
ActiveRecord::Base.connection.execute('TRUNCATE bulk_foos RESTART IDENTITY')
|
||||
end
|
||||
|
||||
context 'saving bulk insertable associations' do
|
||||
let(:parent) { BulkParent.new(name: 'parent') }
|
||||
|
||||
context 'when items already have IDs' do
|
||||
it 'stores nothing and raises an error' do
|
||||
build_items(parent: parent) { |n, item| item.id = 100 + n }
|
||||
build_items(parent: parent) { |n, item| item.id = n }
|
||||
|
||||
expect { save_with_bulk_inserts(parent) }.to raise_error(BulkInsertSafe::PrimaryKeySetError)
|
||||
expect(BulkFoo.count).to eq(0)
|
||||
|
@ -79,7 +75,7 @@ describe BulkInsertableAssociations do
|
|||
|
||||
expect(BulkFoo).to receive(:bulk_insert!).once.and_call_original
|
||||
expect { save_with_bulk_inserts(parent) }.to change { BulkFoo.count }.from(0).to(items.size)
|
||||
expect(parent.bulk_foos.pluck(:id)).to contain_exactly(*(1..10))
|
||||
expect(parent.bulk_foos.pluck(:id)).to all(be_a Integer)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -23,6 +23,18 @@ JS_CONSOLE_FILTER = Regexp.union([
|
|||
|
||||
CAPYBARA_WINDOW_SIZE = [1366, 768].freeze
|
||||
|
||||
# Run Workhorse on the given host and port, proxying to Puma on a UNIX socket,
|
||||
# for a closer-to-production experience
|
||||
Capybara.register_server :puma_via_workhorse do |app, port, host, **options|
|
||||
file = Tempfile.new
|
||||
socket_path = file.path
|
||||
file.close! # We just want the filename
|
||||
|
||||
TestEnv.with_workhorse(TestEnv.workhorse_dir, host, port, socket_path) do
|
||||
Capybara.servers[:puma].call(app, nil, socket_path, **options)
|
||||
end
|
||||
end
|
||||
|
||||
Capybara.register_driver :chrome do |app|
|
||||
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
|
||||
# This enables access to logs with `page.driver.manage.get_log(:browser)`
|
||||
|
@ -60,7 +72,7 @@ Capybara.register_driver :chrome do |app|
|
|||
)
|
||||
end
|
||||
|
||||
Capybara.server = :puma
|
||||
Capybara.server = :puma_via_workhorse
|
||||
Capybara.javascript_driver = :chrome
|
||||
Capybara.default_max_wait_time = timeout
|
||||
Capybara.ignore_hidden_elements = true
|
||||
|
@ -101,6 +113,18 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
# The :capybara_ignore_server_errors metadata means unhandled exceptions raised
|
||||
# by the application under test will not necessarily fail the server. This is
|
||||
# useful when testing conditions that are expected to raise a 500 error in
|
||||
# production; it should not be used on the happy path.
|
||||
config.around(:each, :capybara_ignore_server_errors) do |example|
|
||||
Capybara.raise_server_errors = false
|
||||
|
||||
example.run
|
||||
ensure
|
||||
Capybara.raise_server_errors = true
|
||||
end
|
||||
|
||||
config.after(:example, :js) do |example|
|
||||
# when a test fails, display any messages in the browser's console
|
||||
# but fail don't add the message if the failure is a pending test that got
|
||||
|
|
|
@ -104,6 +104,9 @@ module TestEnv
|
|||
|
||||
setup_gitaly
|
||||
|
||||
# Feature specs are run through Workhorse
|
||||
setup_workhorse
|
||||
|
||||
# Create repository for FactoryBot.create(:project)
|
||||
setup_factory_repo
|
||||
|
||||
|
@ -218,6 +221,52 @@ module TestEnv
|
|||
ENV.fetch('GITALY_REPO_URL', nil)
|
||||
end
|
||||
|
||||
def setup_workhorse
|
||||
install_workhorse_args = [workhorse_dir, workhorse_url].compact.join(',')
|
||||
|
||||
component_timed_setup(
|
||||
'GitLab Workhorse',
|
||||
install_dir: workhorse_dir,
|
||||
version: Gitlab::Workhorse.version,
|
||||
task: "gitlab:workhorse:install[#{install_workhorse_args}]"
|
||||
)
|
||||
end
|
||||
|
||||
def workhorse_dir
|
||||
@workhorse_path ||= File.join('tmp', 'tests', 'gitlab-workhorse')
|
||||
end
|
||||
|
||||
def with_workhorse(workhorse_dir, host, port, upstream, &blk)
|
||||
host = "[#{host}]" if host.include?(':')
|
||||
listen_addr = [host, port].join(':')
|
||||
|
||||
workhorse_pid = spawn(
|
||||
File.join(workhorse_dir, 'gitlab-workhorse'),
|
||||
'-authSocket', upstream,
|
||||
'-documentRoot', Rails.root.join('public').to_s,
|
||||
'-listenAddr', listen_addr,
|
||||
'-secretPath', Gitlab::Workhorse.secret_path.to_s,
|
||||
# TODO: Needed for workhorse + redis features.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/209245
|
||||
#
|
||||
# '-config', '',
|
||||
'-logFile', 'log/workhorse-test.log',
|
||||
'-logFormat', 'structured',
|
||||
'-developmentMode' # to serve assets and rich error messages
|
||||
)
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
Process.kill('TERM', workhorse_pid)
|
||||
Process.wait(workhorse_pid)
|
||||
end
|
||||
end
|
||||
|
||||
def workhorse_url
|
||||
ENV.fetch('GITLAB_WORKHORSE_URL', nil)
|
||||
end
|
||||
|
||||
def setup_factory_repo
|
||||
setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name,
|
||||
BRANCH_SHA)
|
||||
|
@ -347,6 +396,8 @@ module TestEnv
|
|||
gitlab-test_bare
|
||||
gitlab-test-fork
|
||||
gitlab-test-fork_bare
|
||||
gitlab-workhorse
|
||||
gitlab_workhorse_secret
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ RSpec.shared_examples 'wiki file attachments' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'uploading is in progress' do
|
||||
context 'uploading is in progress', :capybara_ignore_server_errors do
|
||||
it 'cancels uploading on clicking to "Cancel" button' do
|
||||
slow_requests do
|
||||
attach_with_dropzone
|
||||
|
|
Loading…
Reference in a new issue