Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ef863c1f85
commit
9d0b65f4b8
36 changed files with 324 additions and 67 deletions
|
@ -2,6 +2,7 @@
|
|||
import CodeOutput from '../code/index.vue';
|
||||
import HtmlOutput from './html.vue';
|
||||
import ImageOutput from './image.vue';
|
||||
import LatexOutput from './latex.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -35,6 +36,8 @@ export default {
|
|||
return 'image/jpeg';
|
||||
} else if (output.data['text/html']) {
|
||||
return 'text/html';
|
||||
} else if (output.data['text/latex']) {
|
||||
return 'text/latex';
|
||||
} else if (output.data['image/svg+xml']) {
|
||||
return 'image/svg+xml';
|
||||
}
|
||||
|
@ -59,6 +62,8 @@ export default {
|
|||
return ImageOutput;
|
||||
} else if (output.data['text/html']) {
|
||||
return HtmlOutput;
|
||||
} else if (output.data['text/latex']) {
|
||||
return LatexOutput;
|
||||
} else if (output.data['image/svg+xml']) {
|
||||
return HtmlOutput;
|
||||
}
|
||||
|
|
45
app/assets/javascripts/notebook/cells/output/latex.vue
Normal file
45
app/assets/javascripts/notebook/cells/output/latex.vue
Normal file
|
@ -0,0 +1,45 @@
|
|||
<script>
|
||||
import 'mathjax/es5/tex-svg';
|
||||
import Prompt from '../prompt.vue';
|
||||
|
||||
export default {
|
||||
name: 'LatexOutput',
|
||||
components: {
|
||||
Prompt,
|
||||
},
|
||||
props: {
|
||||
count: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
rawCode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
code() {
|
||||
// MathJax will not parse out the inline delimeters "$$" correctly
|
||||
// so we remove them from the raw code itself
|
||||
const parsedCode = this.rawCode.replace(/\$\$/g, '');
|
||||
const svg = window.MathJax.tex2svg(parsedCode);
|
||||
|
||||
// NOTE: This is used with `v-html` and not `v-safe-html` due to an
|
||||
// issue with dompurify stripping out xlink attributes from use tags
|
||||
return svg.outerHTML;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="output">
|
||||
<prompt type="Out" :count="count" :show-output="index === 0" />
|
||||
<!-- eslint-disable -->
|
||||
<div ref="maths" v-html="code"></div>
|
||||
</div>
|
||||
</template>
|
|
@ -68,7 +68,7 @@ $avatar-sizes: (
|
|||
);
|
||||
|
||||
$identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $identicon-blue, $identicon-teal,
|
||||
$identicon-orange, $gray-darker;
|
||||
$identicon-orange, $identicon-gray;
|
||||
|
||||
%avatar-circle {
|
||||
float: left;
|
||||
|
@ -125,8 +125,8 @@ $identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $i
|
|||
.identicon {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
color: $gray-700;
|
||||
background-color: $gray-darker;
|
||||
color: $identicon-text-color;
|
||||
background-color: $identicon-gray;
|
||||
|
||||
// Sizes
|
||||
@each $size, $size-config in $avatar-sizes {
|
||||
|
|
|
@ -629,12 +629,14 @@ $note-icon-gutter-width: 55px;
|
|||
/*
|
||||
* Identicon
|
||||
*/
|
||||
$identicon-text-color: #525252 !default;
|
||||
$identicon-red: #ffebee !default;
|
||||
$identicon-purple: #f3e5f5 !default;
|
||||
$identicon-indigo: #e8eaf6 !default;
|
||||
$identicon-blue: #e3f2fd !default;
|
||||
$identicon-teal: #e0f2f1 !default;
|
||||
$identicon-orange: #fbe9e7 !default;
|
||||
$identicon-gray: #eee !default;
|
||||
|
||||
/*
|
||||
* Calendar
|
||||
|
|
|
@ -103,18 +103,13 @@ module Repositories
|
|||
end
|
||||
|
||||
def upload_headers
|
||||
headers = {
|
||||
{
|
||||
Authorization: authorization_header,
|
||||
# git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
|
||||
# ensures that Workhorse can intercept the request.
|
||||
'Content-Type': LFS_TRANSFER_CONTENT_TYPE
|
||||
'Content-Type': LFS_TRANSFER_CONTENT_TYPE,
|
||||
'Transfer-Encoding': 'chunked'
|
||||
}
|
||||
|
||||
if Feature.enabled?(:lfs_chunked_encoding, project, default_enabled: true)
|
||||
headers['Transfer-Encoding'] = 'chunked'
|
||||
end
|
||||
|
||||
headers
|
||||
end
|
||||
|
||||
def lfs_check_batch_operation!
|
||||
|
|
|
@ -335,7 +335,8 @@ module ApplicationSettingsHelper
|
|||
:group_export_limit,
|
||||
:group_download_export_limit,
|
||||
:wiki_page_max_content_bytes,
|
||||
:container_registry_delete_tags_service_timeout
|
||||
:container_registry_delete_tags_service_timeout,
|
||||
:rate_limiting_response_text
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -400,6 +400,10 @@ class ApplicationSetting < ApplicationRecord
|
|||
validates :ci_jwt_signing_key,
|
||||
rsa_key: true, allow_nil: true
|
||||
|
||||
validates :rate_limiting_response_text,
|
||||
length: { maximum: 255, message: _('is too long (maximum is %{count} characters)') },
|
||||
allow_blank: true
|
||||
|
||||
attr_encrypted :asset_proxy_secret_key,
|
||||
mode: :per_attribute_iv,
|
||||
key: Settings.attr_encrypted_db_key_base_truncated,
|
||||
|
|
|
@ -172,7 +172,8 @@ module ApplicationSettingImplementation
|
|||
container_registry_delete_tags_service_timeout: 250,
|
||||
container_registry_expiration_policies_worker_capacity: 0,
|
||||
kroki_enabled: false,
|
||||
kroki_url: nil
|
||||
kroki_url: nil,
|
||||
rate_limiting_response_text: nil
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -49,5 +49,12 @@
|
|||
.form-group
|
||||
= f.label :throttle_authenticated_web_period_in_seconds, 'Authenticated web rate limit period in seconds', class: 'label-bold'
|
||||
= f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control'
|
||||
%hr
|
||||
%h5
|
||||
= _('Response text')
|
||||
.form-group
|
||||
= f.label :rate_limiting_response_text, class: 'label-bold' do
|
||||
= _('A plain-text response to show to clients that hit the rate limit.')
|
||||
= f.text_area :rate_limiting_response_text, placeholder: ::Gitlab::Throttle::DEFAULT_RATE_LIMITING_RESPONSE_TEXT, class: 'form-control', rows: 5
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow custom response to be set when rate limits are exceeded
|
||||
merge_request: 50693
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/fix-identicon-dark-mode.yml
Normal file
5
changelogs/unreleased/fix-identicon-dark-mode.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix identicon text color in dark mode
|
||||
merge_request: 49785
|
||||
author: "@yo"
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add LaTeX support for Jupyter Notebooks
|
||||
merge_request: 49497
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove lfs_chunked_encoding feature flag
|
||||
merge_request: 50557
|
||||
author:
|
||||
type: changed
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: lfs_chunked_encoding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48269
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285581
|
||||
milestone: '13.6'
|
||||
type: development
|
||||
group:
|
||||
default_enabled: true
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRateLimitingResponseTextToApplicationSettings < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
# rubocop:disable Migration/AddLimitToTextColumns
|
||||
# limit is added in 20210101110640_set_limit_for_rate_limiting_response_text
|
||||
def change
|
||||
add_column :application_settings, :rate_limiting_response_text, :text
|
||||
end
|
||||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SetLimitForRateLimitingResponseText < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_text_limit :application_settings, :rate_limiting_response_text, 255
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :application_settings, :rate_limiting_response_text
|
||||
end
|
||||
end
|
1
db/schema_migrations/20201230161206
Normal file
1
db/schema_migrations/20201230161206
Normal file
|
@ -0,0 +1 @@
|
|||
b3fcc73c6b61469d770e9eb9a14c88bb86398db4ab4b6dc5283718a147db0ac0
|
1
db/schema_migrations/20210101110640
Normal file
1
db/schema_migrations/20210101110640
Normal file
|
@ -0,0 +1 @@
|
|||
8aac4108b658a7a0646ec230dc2568cb51fea0535b13dfba8b8c9e6edb401d07
|
|
@ -9376,12 +9376,14 @@ CREATE TABLE application_settings (
|
|||
cloud_license_enabled boolean DEFAULT false NOT NULL,
|
||||
disable_feed_token boolean DEFAULT false NOT NULL,
|
||||
personal_access_token_prefix text,
|
||||
rate_limiting_response_text text,
|
||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||
CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)),
|
||||
CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)),
|
||||
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
|
||||
CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)),
|
||||
CONSTRAINT check_718b4458ae CHECK ((char_length(personal_access_token_prefix) <= 20)),
|
||||
CONSTRAINT check_7227fad848 CHECK ((char_length(rate_limiting_response_text) <= 255)),
|
||||
CONSTRAINT check_85a39b68ff CHECK ((char_length(encrypted_ci_jwt_signing_key_iv) <= 255)),
|
||||
CONSTRAINT check_9a719834eb CHECK ((char_length(secret_detection_token_revocation_url) <= 255)),
|
||||
CONSTRAINT check_9c6c447a13 CHECK ((char_length(maintenance_mode_message) <= 255)),
|
||||
|
|
|
@ -83,7 +83,8 @@ Example response:
|
|||
"raw_blob_request_limit": 300,
|
||||
"wiki_page_max_content_bytes": 52428800,
|
||||
"require_admin_approval_after_user_signup": false,
|
||||
"personal_access_token_prefix": "GL-"
|
||||
"personal_access_token_prefix": "GL-",
|
||||
"rate_limiting_response_text": null
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -176,7 +177,8 @@ Example response:
|
|||
"raw_blob_request_limit": 300,
|
||||
"wiki_page_max_content_bytes": 52428800,
|
||||
"require_admin_approval_after_user_signup": false,
|
||||
"personal_access_token_prefix": "GL-"
|
||||
"personal_access_token_prefix": "GL-",
|
||||
"rate_limiting_response_text": null
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -330,6 +332,7 @@ listed in the descriptions of the relevant settings.
|
|||
| `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab runs a background job that produces pseudonymized CSVs of the GitLab database to upload to your configured object storage directory.
|
||||
| `push_event_activities_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push events are created. [Bulk push events are created](../user/admin_area/settings/push_event_activities_limit.md) if it surpasses that value. |
|
||||
| `push_event_hooks_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether webhooks and services fire or not. Webhooks and services aren't submitted if it surpasses that value. |
|
||||
| `rate_limiting_response_text` | string | no | When rate limiting is enabled via the `throttle_*` settings, send this plain text response when a rate limit is exceeded. 'Retry later' is sent if this is blank. |
|
||||
| `raw_blob_request_limit` | integer | no | Max number of requests per minute for each raw path. Default: 300. To disable throttling set to 0.|
|
||||
| `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable reCAPTCHA. |
|
||||
| `recaptcha_private_key` | string | required by: `recaptcha_enabled` | Private key for reCAPTCHA. |
|
||||
|
|
|
@ -79,9 +79,10 @@ package.
|
|||
|
||||
## Installing GitLab from source
|
||||
|
||||
If the Omnibus GitLab package is not available in your distribution, you can
|
||||
install GitLab from source: Useful for unsupported systems like \*BSD. For an
|
||||
overview of the directory structure, read the [structure documentation](installation.md#gitlab-directory-structure).
|
||||
If the Omnibus GitLab package isn't available for your distribution, you can
|
||||
install GitLab from source. This can be useful with unsupported systems, like
|
||||
\*BSD. For an overview of the directory structure, see the
|
||||
[structure documentation](installation.md#gitlab-directory-structure).
|
||||
|
||||
[**> Install GitLab from source.**](installation.md)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ as the hardware requirements that are needed to install and use GitLab.
|
|||
|
||||
- Ubuntu (16.04/18.04/20.04)
|
||||
- Debian (9/10)
|
||||
- CentOS (6/7/8)
|
||||
- CentOS (7/8)
|
||||
- openSUSE (Leap 15.1/Enterprise Server 12.2)
|
||||
- Red Hat Enterprise Linux (please use the CentOS packages and instructions)
|
||||
- Scientific Linux (please use the CentOS packages and instructions)
|
||||
|
@ -35,6 +35,9 @@ For the installation options, see [the main installation page](README.md).
|
|||
Installation of GitLab on these operating systems is possible, but not supported.
|
||||
Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information.
|
||||
|
||||
Please see [OS versions that are no longer supported](https://docs.gitlab.com/omnibus/package-information/deprecated_os.html) for Omnibus installs page
|
||||
for a list of supported and unsupported OS versions as well as the last support GitLab version for that OS.
|
||||
|
||||
### Microsoft Windows
|
||||
|
||||
GitLab is developed for Linux-based operating systems.
|
||||
|
@ -163,11 +166,11 @@ Support for [PostgreSQL 9.6 and 10 has been removed in GitLab 13.0](https://abou
|
|||
|
||||
#### Additional requirements for GitLab Geo
|
||||
|
||||
If you're using [GitLab Geo](../administration/geo/index.md):
|
||||
|
||||
- We strongly recommend running Omnibus-managed instances as they are actively
|
||||
developed and tested. We aim to be compatible with most external (not managed
|
||||
by Omnibus) databases (for example, [AWS Relational Database Service (RDS)](https://aws.amazon.com/rds/)) but we don't guarantee compatibility.
|
||||
If you're using [GitLab Geo](../administration/geo/index.md), we strongly
|
||||
recommend running Omnibus GitLab-managed instances, as we actively develop and
|
||||
test based on those. We try to be compatible with most external (not managed by
|
||||
Omnibus GitLab) databases (for example, [AWS Relational Database Service (RDS)](https://aws.amazon.com/rds/)),
|
||||
but we can't guarantee compatibility.
|
||||
|
||||
## Puma settings
|
||||
|
||||
|
|
|
@ -25,6 +25,17 @@ By default, all Git operations are first tried unathenticated. Because of this,
|
|||
|
||||
![user-and-ip-rate-limits](img/user_and_ip_rate_limits.png)
|
||||
|
||||
## Response text
|
||||
|
||||
A request that exceeds a rate limit will get a 429 response code and a
|
||||
plain-text body, which by default is:
|
||||
|
||||
```plaintext
|
||||
Retry later
|
||||
```
|
||||
|
||||
It is possible to customize this response text in the admin area.
|
||||
|
||||
## Use an HTTP header to bypass rate limiting
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/622) in GitLab 13.6.
|
||||
|
|
|
@ -10,8 +10,17 @@ module Gitlab
|
|||
def self.configure(rack_attack)
|
||||
# This adds some methods used by our throttles to the `Rack::Request`
|
||||
rack_attack::Request.include(Gitlab::RackAttack::Request)
|
||||
# Send the Retry-After header so clients (e.g. python-gitlab) can make good choices about delays
|
||||
Rack::Attack.throttled_response_retry_after_header = true
|
||||
|
||||
# This is Rack::Attack::DEFAULT_THROTTLED_RESPONSE, modified to allow a custom response
|
||||
Rack::Attack.throttled_response = lambda do |env|
|
||||
# Send the Retry-After header so clients (e.g. python-gitlab) can make good choices about delays
|
||||
match_data = env['rack.attack.match_data']
|
||||
now = match_data[:epoch_time]
|
||||
retry_after = match_data[:period] - (now % match_data[:period])
|
||||
|
||||
[429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, [Gitlab::Throttle.rate_limiting_response_text]]
|
||||
end
|
||||
|
||||
# Configure the throttles
|
||||
configure_throttles(rack_attack)
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Gitlab
|
||||
class Throttle
|
||||
DEFAULT_RATE_LIMITING_RESPONSE_TEXT = 'Retry later'
|
||||
|
||||
def self.settings
|
||||
Gitlab::CurrentSettings.current_application_settings
|
||||
end
|
||||
|
@ -46,5 +48,9 @@ module Gitlab
|
|||
|
||||
{ limit: limit_proc, period: period_proc }
|
||||
end
|
||||
|
||||
def self.rate_limiting_response_text
|
||||
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1304,6 +1304,9 @@ msgstr ""
|
|||
msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
|
||||
msgstr ""
|
||||
|
||||
msgid "A plain-text response to show to clients that hit the rate limit."
|
||||
msgstr ""
|
||||
|
||||
msgid "A platform value can be web, mob or app."
|
||||
msgstr ""
|
||||
|
||||
|
@ -23928,6 +23931,9 @@ msgstr ""
|
|||
msgid "Response metrics (NGINX)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Response text"
|
||||
msgstr ""
|
||||
|
||||
msgid "Restart Terminal"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
"katex": "^0.10.0",
|
||||
"lodash": "^4.17.20",
|
||||
"marked": "^0.3.12",
|
||||
"mathjax": "3",
|
||||
"mermaid": "^8.5.2",
|
||||
"mersenne-twister": "1.1.0",
|
||||
"minimatch": "^3.0.4",
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import Vuex from 'vuex';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import createDiffsStore from '~/diffs/store/modules';
|
||||
import createNotesStore from '~/notes/stores/modules';
|
||||
import diffFileMockDataReadable from '../mock_data/diff_file';
|
||||
|
@ -118,14 +121,17 @@ const changeViewerType = (store, newType, index = 0) =>
|
|||
describe('DiffFile', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let axiosMock;
|
||||
|
||||
beforeEach(() => {
|
||||
axiosMock = new MockAdapter(axios);
|
||||
({ wrapper, store } = createComponent({ file: getReadableFile() }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
axiosMock.restore();
|
||||
});
|
||||
|
||||
describe('bus events', () => {
|
||||
|
@ -353,8 +359,10 @@ describe('DiffFile', () => {
|
|||
|
||||
describe('loading', () => {
|
||||
it('should have loading icon while loading a collapsed diffs', async () => {
|
||||
const { load_collapsed_diff_url } = store.state.diffs.diffFiles[0];
|
||||
axiosMock.onGet(load_collapsed_diff_url).reply(httpStatus.OK, getReadableFile());
|
||||
makeFileAutomaticallyCollapsed(store);
|
||||
wrapper.vm.isLoadingCollapsedDiff = true;
|
||||
wrapper.vm.requestDiff();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
|
|
@ -18,12 +18,14 @@ describe('Output component', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// This is the output after rendering a jupyter notebook
|
||||
json = getJSONFixture('blob/notebook/basic.json');
|
||||
});
|
||||
|
||||
describe('text output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[2].outputs[0]);
|
||||
const textType = json.cells[2];
|
||||
createComponent(textType.outputs[0]);
|
||||
|
||||
setImmediate(() => {
|
||||
done();
|
||||
|
@ -41,7 +43,8 @@ describe('Output component', () => {
|
|||
|
||||
describe('image output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[3].outputs[0]);
|
||||
const imageType = json.cells[3];
|
||||
createComponent(imageType.outputs[0]);
|
||||
|
||||
setImmediate(() => {
|
||||
done();
|
||||
|
@ -55,23 +58,42 @@ describe('Output component', () => {
|
|||
|
||||
describe('html output', () => {
|
||||
it('renders raw HTML', () => {
|
||||
createComponent(json.cells[4].outputs[0]);
|
||||
const htmlType = json.cells[4];
|
||||
createComponent(htmlType.outputs[0]);
|
||||
|
||||
expect(vm.$el.querySelector('p')).not.toBeNull();
|
||||
expect(vm.$el.querySelectorAll('p').length).toBe(1);
|
||||
expect(vm.$el.querySelectorAll('p')).toHaveLength(1);
|
||||
expect(vm.$el.textContent.trim()).toContain('test');
|
||||
});
|
||||
|
||||
it('renders multiple raw HTML outputs', () => {
|
||||
createComponent([json.cells[4].outputs[0], json.cells[4].outputs[0]]);
|
||||
const htmlType = json.cells[4];
|
||||
createComponent([htmlType.outputs[0], htmlType.outputs[0]]);
|
||||
|
||||
expect(vm.$el.querySelectorAll('p').length).toBe(2);
|
||||
expect(vm.$el.querySelectorAll('p')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('LaTeX output', () => {
|
||||
it('renders LaTeX', () => {
|
||||
const output = {
|
||||
data: {
|
||||
'text/latex': ['$$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx$$'],
|
||||
'text/plain': ['<IPython.core.display.Latex object>'],
|
||||
},
|
||||
metadata: {},
|
||||
output_type: 'display_data',
|
||||
};
|
||||
createComponent(output);
|
||||
|
||||
expect(vm.$el.querySelector('.MathJax')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[5].outputs[0]);
|
||||
const svgType = json.cells[5];
|
||||
createComponent(svgType.outputs[0]);
|
||||
|
||||
setImmediate(() => {
|
||||
done();
|
||||
|
@ -85,7 +107,8 @@ describe('Output component', () => {
|
|||
|
||||
describe('default to plain text', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[6].outputs[0]);
|
||||
const unknownType = json.cells[6];
|
||||
createComponent(unknownType.outputs[0]);
|
||||
|
||||
setImmediate(() => {
|
||||
done();
|
||||
|
@ -102,7 +125,8 @@ describe('Output component', () => {
|
|||
});
|
||||
|
||||
it("renders as plain text when doesn't recognise other types", (done) => {
|
||||
createComponent(json.cells[7].outputs[0]);
|
||||
const unknownType = json.cells[7];
|
||||
createComponent(unknownType.outputs[0]);
|
||||
|
||||
setImmediate(() => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
|
|
40
spec/frontend/notebook/cells/output/latex_spec.js
Normal file
40
spec/frontend/notebook/cells/output/latex_spec.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import LatexOutput from '~/notebook/cells/output/latex.vue';
|
||||
import Prompt from '~/notebook/cells/prompt.vue';
|
||||
|
||||
describe('LaTeX output cell', () => {
|
||||
beforeEach(() => {
|
||||
window.MathJax = {
|
||||
tex2svg: jest.fn((code) => ({ outerHTML: code })),
|
||||
};
|
||||
});
|
||||
|
||||
const inlineLatex = '$$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx$$';
|
||||
const count = 12345;
|
||||
|
||||
const createComponent = (rawCode, index) =>
|
||||
shallowMount(LatexOutput, {
|
||||
propsData: {
|
||||
count,
|
||||
index,
|
||||
rawCode,
|
||||
},
|
||||
});
|
||||
|
||||
it.each`
|
||||
index | expectation
|
||||
${0} | ${true}
|
||||
${1} | ${false}
|
||||
`('sets `Prompt.show-output` to $expectation when index is $index', ({ index, expectation }) => {
|
||||
const wrapper = createComponent(inlineLatex, index);
|
||||
const prompt = wrapper.find(Prompt);
|
||||
|
||||
expect(prompt.props().count).toEqual(count);
|
||||
expect(prompt.props().showOutput).toEqual(expectation);
|
||||
});
|
||||
|
||||
it('strips the `$$` delimter from LaTeX', () => {
|
||||
createComponent(inlineLatex, 0);
|
||||
expect(window.MathJax.tex2svg).toHaveBeenCalledWith(expect.not.stringContaining('$$'));
|
||||
});
|
||||
});
|
|
@ -22,8 +22,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
|
|||
stub_const("Rack::Attack", fake_rack_attack)
|
||||
stub_const("Rack::Attack::Request", fake_rack_attack_request)
|
||||
|
||||
# Expect rather than just allow, because this is actually fairly important functionality
|
||||
expect(fake_rack_attack).to receive(:throttled_response_retry_after_header=).with(true)
|
||||
allow(fake_rack_attack).to receive(:throttled_response=)
|
||||
allow(fake_rack_attack).to receive(:throttle)
|
||||
allow(fake_rack_attack).to receive(:track)
|
||||
allow(fake_rack_attack).to receive(:safelist)
|
||||
|
@ -36,6 +35,12 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
|
|||
expect(fake_rack_attack_request).to include(described_class::Request)
|
||||
end
|
||||
|
||||
it 'configures the throttle response' do
|
||||
described_class.configure(fake_rack_attack)
|
||||
|
||||
expect(fake_rack_attack).to have_received(:throttled_response=).with(an_instance_of(Proc))
|
||||
end
|
||||
|
||||
it 'configures the safelist' do
|
||||
described_class.configure(fake_rack_attack)
|
||||
|
||||
|
|
|
@ -30,4 +30,32 @@ RSpec.describe Gitlab::Throttle do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.rate_limiting_response_text' do
|
||||
subject { described_class.rate_limiting_response_text }
|
||||
|
||||
context 'when the setting is not present' do
|
||||
before do
|
||||
stub_application_setting(rate_limiting_response_text: '')
|
||||
end
|
||||
|
||||
it 'returns the default value with a trailing newline' do
|
||||
expect(subject).to eq(described_class::DEFAULT_RATE_LIMITING_RESPONSE_TEXT + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the setting is present' do
|
||||
let(:response_text) do
|
||||
'Rate limit exceeded; see https://docs.gitlab.com/ee/user/gitlab_com/#gitlabcom-specific-rate-limits for more details'
|
||||
end
|
||||
|
||||
before do
|
||||
stub_application_setting(rate_limiting_response_text: response_text)
|
||||
end
|
||||
|
||||
it 'returns the default value with a trailing newline' do
|
||||
expect(subject).to eq(response_text + "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -193,10 +193,8 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
subject(:request) { post_lfs_json batch_url(project), body, headers }
|
||||
|
||||
let(:response) { request && super() }
|
||||
let(:lfs_chunked_encoding) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(lfs_chunked_encoding: lfs_chunked_encoding)
|
||||
project.lfs_objects << lfs_object
|
||||
end
|
||||
|
||||
|
@ -480,20 +478,6 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
expect(headers['Transfer-Encoding']).to eq('chunked')
|
||||
end
|
||||
|
||||
context 'when lfs_chunked_encoding feature is disabled' do
|
||||
let(:lfs_chunked_encoding) { false }
|
||||
|
||||
it 'responds with upload hypermedia link' do
|
||||
expect(json_response['objects']).to be_kind_of(Array)
|
||||
expect(json_response['objects'].first).to include(sample_object)
|
||||
expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
|
||||
|
||||
headers = json_response['objects'].first['actions']['upload']['header']
|
||||
expect(headers['Content-Type']).to eq('application/octet-stream')
|
||||
expect(headers['Transfer-Encoding']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'process authorization header', renew_authorization: renew_authorization
|
||||
end
|
||||
|
||||
|
|
|
@ -60,6 +60,24 @@ RSpec.describe 'Rack Attack global throttles' do
|
|||
expect_rejection { get url_that_does_not_require_authentication }
|
||||
end
|
||||
|
||||
context 'with custom response text' do
|
||||
before do
|
||||
stub_application_setting(rate_limiting_response_text: 'Custom response')
|
||||
end
|
||||
|
||||
it 'rejects requests over the rate limit' do
|
||||
# At first, allow requests under the rate limit.
|
||||
requests_per_period.times do
|
||||
get url_that_does_not_require_authentication
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
# the last straw
|
||||
expect_rejection { get url_that_does_not_require_authentication }
|
||||
expect(response.body).to eq("Custom response\n")
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows requests after throttling and then waiting for the next period' do
|
||||
requests_per_period.times do
|
||||
get url_that_does_not_require_authentication
|
||||
|
|
|
@ -25,6 +25,7 @@ module RackAttackSpecHelpers
|
|||
yield
|
||||
|
||||
expect(response).to have_gitlab_http_status(:too_many_requests)
|
||||
expect(response).to have_header('Retry-After')
|
||||
end
|
||||
|
||||
def expect_ok(&block)
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -3057,7 +3057,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
|||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@2, commander@^2.10.0, commander@^2.16.0, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
|
||||
commander@2, commander@^2.10.0, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
|
||||
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
|
||||
|
@ -7476,11 +7476,11 @@ karma@^4.2.0:
|
|||
useragent "2.3.0"
|
||||
|
||||
katex@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/katex/-/katex-0.10.0.tgz#da562e5d0d5cc3aa602e27af8a9b8710bfbce765"
|
||||
integrity sha512-/WRvx+L1eVBrLwX7QzKU1dQuaGnE7E8hDvx3VWfZh9HbMiCfsKWJNnYZ0S8ZMDAfAyDSofdyXIrH/hujF1fYXg==
|
||||
version "0.10.2"
|
||||
resolved "https://registry.yarnpkg.com/katex/-/katex-0.10.2.tgz#39973edbb65eda5b6f9e7f41648781e557dd4932"
|
||||
integrity sha512-cQOmyIRoMloCoSIOZ1+gEwsksdJZ1EW4SWm3QzxSza/QsnZr6D4U1V9S4q+B/OLm2OQ8TCBecQ8MaIfnScI7cw==
|
||||
dependencies:
|
||||
commander "^2.16.0"
|
||||
commander "^2.19.0"
|
||||
|
||||
keyv@^3.0.0:
|
||||
version "3.1.0"
|
||||
|
@ -8054,6 +8054,11 @@ marked@^0.3.12, marked@~0.3.6:
|
|||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790"
|
||||
integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==
|
||||
|
||||
mathjax@3:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/mathjax/-/mathjax-3.1.2.tgz#95c0d45ce2330ef7b6a815cebe7d61ecc26bbabd"
|
||||
integrity sha512-BojKspBv4nNWzO1wC6VEI+g9gHDOhkaGHGgLxXkasdU4pwjdO5AXD5M/wcLPkXYPjZ/N+6sU8rjQTlyvN2cWiQ==
|
||||
|
||||
mathml-tag-names@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc"
|
||||
|
|
Loading…
Reference in a new issue