Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
24ed154fa8
commit
12287a65b7
21 changed files with 177 additions and 90 deletions
5
app/assets/javascripts/blob/blob_utils.js
Normal file
5
app/assets/javascripts/blob/blob_utils.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// capture anything starting with http:// or https://
|
||||||
|
// up until a disallowed character or whitespace
|
||||||
|
export const blobLinkRegex = /https?:\/\/[^"<>\\^`{|}\s]+/g;
|
||||||
|
|
||||||
|
export default { blobLinkRegex };
|
|
@ -4,6 +4,10 @@ import Flash from '../../flash';
|
||||||
import { handleLocationHash } from '../../lib/utils/common_utils';
|
import { handleLocationHash } from '../../lib/utils/common_utils';
|
||||||
import axios from '../../lib/utils/axios_utils';
|
import axios from '../../lib/utils/axios_utils';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
import { blobLinkRegex } from '~/blob/blob_utils';
|
||||||
|
|
||||||
|
const SIMPLE_VIEWER_NAME = 'simple';
|
||||||
|
const RICH_VIEWER_NAME = 'rich';
|
||||||
|
|
||||||
export default class BlobViewer {
|
export default class BlobViewer {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -21,7 +25,7 @@ export default class BlobViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
static initRichViewer() {
|
static initRichViewer() {
|
||||||
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
const viewer = document.querySelector(`.blob-viewer[data-type="${RICH_VIEWER_NAME}"]`);
|
||||||
if (!viewer || !viewer.dataset.richType) return;
|
if (!viewer || !viewer.dataset.richType) return;
|
||||||
|
|
||||||
const initViewer = promise =>
|
const initViewer = promise =>
|
||||||
|
@ -61,8 +65,12 @@ export default class BlobViewer {
|
||||||
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
|
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
|
||||||
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
|
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
|
||||||
|
|
||||||
this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
|
this.simpleViewer = this.$fileHolder[0].querySelector(
|
||||||
this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
|
`.blob-viewer[data-type="${SIMPLE_VIEWER_NAME}"]`,
|
||||||
|
);
|
||||||
|
this.richViewer = this.$fileHolder[0].querySelector(
|
||||||
|
`.blob-viewer[data-type="${RICH_VIEWER_NAME}"]`,
|
||||||
|
);
|
||||||
|
|
||||||
this.initBindings();
|
this.initBindings();
|
||||||
|
|
||||||
|
@ -71,10 +79,10 @@ export default class BlobViewer {
|
||||||
|
|
||||||
switchToInitialViewer() {
|
switchToInitialViewer() {
|
||||||
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
|
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
|
||||||
let initialViewerName = initialViewer.getAttribute('data-type');
|
let initialViewerName = initialViewer.dataset.type;
|
||||||
|
|
||||||
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
|
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
|
||||||
initialViewerName = 'simple';
|
initialViewerName = SIMPLE_VIEWER_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.switchToViewer(initialViewerName);
|
this.switchToViewer(initialViewerName);
|
||||||
|
@ -91,35 +99,41 @@ export default class BlobViewer {
|
||||||
this.copySourceBtn.addEventListener('click', () => {
|
this.copySourceBtn.addEventListener('click', () => {
|
||||||
if (this.copySourceBtn.classList.contains('disabled')) return this.copySourceBtn.blur();
|
if (this.copySourceBtn.classList.contains('disabled')) return this.copySourceBtn.blur();
|
||||||
|
|
||||||
return this.switchToViewer('simple');
|
return this.switchToViewer(SIMPLE_VIEWER_NAME);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static linkifyURLs(viewer) {
|
||||||
|
if (viewer.dataset.linkified) return;
|
||||||
|
|
||||||
|
document.querySelectorAll('.js-blob-content .code .line').forEach(line => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
line.innerHTML = line.innerHTML.replace(blobLinkRegex, '<a href="$&">$&</a>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
viewer.dataset.linkified = true;
|
||||||
|
}
|
||||||
|
|
||||||
switchViewHandler(e) {
|
switchViewHandler(e) {
|
||||||
const target = e.currentTarget;
|
const target = e.currentTarget;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.switchToViewer(target.getAttribute('data-viewer'));
|
this.switchToViewer(target.dataset.viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCopyButtonState() {
|
toggleCopyButtonState() {
|
||||||
if (!this.copySourceBtn) return;
|
if (!this.copySourceBtn) return;
|
||||||
if (this.simpleViewer.getAttribute('data-loaded')) {
|
if (this.simpleViewer.dataset.loaded) {
|
||||||
this.copySourceBtn.setAttribute('title', __('Copy file contents'));
|
this.copySourceBtn.dataset.title = __('Copy file contents');
|
||||||
this.copySourceBtn.classList.remove('disabled');
|
this.copySourceBtn.classList.remove('disabled');
|
||||||
} else if (this.activeViewer === this.simpleViewer) {
|
} else if (this.activeViewer === this.simpleViewer) {
|
||||||
this.copySourceBtn.setAttribute(
|
this.copySourceBtn.dataset.title = __('Wait for the file to load to copy its contents');
|
||||||
'title',
|
|
||||||
__('Wait for the file to load to copy its contents'),
|
|
||||||
);
|
|
||||||
this.copySourceBtn.classList.add('disabled');
|
this.copySourceBtn.classList.add('disabled');
|
||||||
} else {
|
} else {
|
||||||
this.copySourceBtn.setAttribute(
|
this.copySourceBtn.dataset.title = __('Switch to the source to copy the file contents');
|
||||||
'title',
|
|
||||||
__('Switch to the source to copy the file contents'),
|
|
||||||
);
|
|
||||||
this.copySourceBtn.classList.add('disabled');
|
this.copySourceBtn.classList.add('disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +173,8 @@ export default class BlobViewer {
|
||||||
this.$fileHolder.trigger('highlight:line');
|
this.$fileHolder.trigger('highlight:line');
|
||||||
handleLocationHash();
|
handleLocationHash();
|
||||||
|
|
||||||
|
if (name === SIMPLE_VIEWER_NAME) BlobViewer.linkifyURLs(viewer);
|
||||||
|
|
||||||
this.toggleCopyButtonState();
|
this.toggleCopyButtonState();
|
||||||
})
|
})
|
||||||
.catch(() => new Flash(__('Error loading viewer')));
|
.catch(() => new Flash(__('Error loading viewer')));
|
||||||
|
@ -166,17 +182,17 @@ export default class BlobViewer {
|
||||||
|
|
||||||
static loadViewer(viewerParam) {
|
static loadViewer(viewerParam) {
|
||||||
const viewer = viewerParam;
|
const viewer = viewerParam;
|
||||||
const url = viewer.getAttribute('data-url');
|
const { url, loaded, loading } = viewer.dataset;
|
||||||
|
|
||||||
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
|
if (!url || loaded || loading) {
|
||||||
return Promise.resolve(viewer);
|
return Promise.resolve(viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewer.setAttribute('data-loading', 'true');
|
viewer.dataset.loading = true;
|
||||||
|
|
||||||
return axios.get(url).then(({ data }) => {
|
return axios.get(url).then(({ data }) => {
|
||||||
viewer.innerHTML = data.html;
|
viewer.innerHTML = data.html;
|
||||||
viewer.setAttribute('data-loaded', 'true');
|
viewer.dataset.loaded = true;
|
||||||
|
|
||||||
return viewer;
|
return viewer;
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,8 @@ import $ from 'jquery';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import TemplateSelectorMediator from '../blob/file_template_mediator';
|
import { blobLinkRegex } from '~/blob/blob_utils';
|
||||||
|
import TemplateSelectorMediator from '~/blob/file_template_mediator';
|
||||||
import getModeByFileExtension from '~/lib/utils/ace_utils';
|
import getModeByFileExtension from '~/lib/utils/ace_utils';
|
||||||
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
|
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ export default class EditBlob {
|
||||||
this.initModePanesAndLinks();
|
this.initModePanesAndLinks();
|
||||||
this.initSoftWrap();
|
this.initSoftWrap();
|
||||||
this.initFileSelectors();
|
this.initFileSelectors();
|
||||||
|
this.initBlobContentLinkClickability();
|
||||||
}
|
}
|
||||||
|
|
||||||
configureAceEditor() {
|
configureAceEditor() {
|
||||||
|
@ -89,6 +91,22 @@ export default class EditBlob {
|
||||||
return this.editor.focus();
|
return this.editor.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initBlobContentLinkClickability() {
|
||||||
|
this.editor.renderer.on('afterRender', () => {
|
||||||
|
document.querySelectorAll('.ace_text-layer .ace_line > *').forEach(token => {
|
||||||
|
if (token.dataset.linkified || !token.textContent.includes('http')) return;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
token.innerHTML = token.innerHTML.replace(
|
||||||
|
blobLinkRegex,
|
||||||
|
'<a target="_blank" href="$&">$&</a>',
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
token.dataset.linkified = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
initSoftWrap() {
|
initSoftWrap() {
|
||||||
this.isSoftWrapped = false;
|
this.isSoftWrapped = false;
|
||||||
this.$toggleButton = $('.soft-wrap-toggle');
|
this.$toggleButton = $('.soft-wrap-toggle');
|
||||||
|
|
|
@ -258,6 +258,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-editor {
|
||||||
|
.ace_underline {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_line a {
|
||||||
|
pointer-events: auto;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span.idiff {
|
span.idiff {
|
||||||
|
|
|
@ -29,3 +29,12 @@
|
||||||
color: $link;
|
color: $link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Links to URLs, emails, or dependencies
|
||||||
|
.code .line a {
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -193,11 +193,6 @@ $dark-il: #de935f;
|
||||||
color: $dark-highlight-color !important;
|
color: $dark-highlight-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $dark-na;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hll { background-color: $dark-hll-bg; }
|
.hll { background-color: $dark-hll-bg; }
|
||||||
.c { color: $dark-c; } /* Comment */
|
.c { color: $dark-c; } /* Comment */
|
||||||
.err { color: $dark-err; } /* Error */
|
.err { color: $dark-err; } /* Error */
|
||||||
|
|
|
@ -193,11 +193,6 @@ $monokai-gi: #a6e22e;
|
||||||
color: $black !important;
|
color: $black !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $monokai-k;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hll { background-color: $monokai-hll; }
|
.hll { background-color: $monokai-hll; }
|
||||||
.c { color: $monokai-c; } /* Comment */
|
.c { color: $monokai-c; } /* Comment */
|
||||||
.err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
|
.err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
|
||||||
|
|
|
@ -143,12 +143,6 @@
|
||||||
background-color: $white-normal;
|
background-color: $white-normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $gl-text-color;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hll { background-color: $white-light; }
|
.hll { background-color: $white-light; }
|
||||||
|
|
||||||
.gd {
|
.gd {
|
||||||
|
|
|
@ -196,11 +196,6 @@ $solarized-dark-il: #2aa198;
|
||||||
background-color: $solarized-dark-highlight !important;
|
background-color: $solarized-dark-highlight !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $solarized-dark-kd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Solarized Dark
|
/* Solarized Dark
|
||||||
|
|
||||||
For use with Jekyll and Pygments
|
For use with Jekyll and Pygments
|
||||||
|
|
|
@ -204,11 +204,6 @@ $solarized-light-il: #2aa198;
|
||||||
background-color: $solarized-light-highlight !important;
|
background-color: $solarized-light-highlight !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $solarized-light-kd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Solarized Light
|
/* Solarized Light
|
||||||
|
|
||||||
For use with Jekyll and Pygments
|
For use with Jekyll and Pygments
|
||||||
|
|
|
@ -209,11 +209,6 @@ span.highlight_word {
|
||||||
background-color: $white-highlight !important;
|
background-color: $white-highlight !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links to URLs, emails, or dependencies
|
|
||||||
.line a {
|
|
||||||
color: $white-nb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hll { background-color: $white-hll-bg; }
|
.hll { background-color: $white-hll-bg; }
|
||||||
|
|
||||||
.c { color: $white-c;
|
.c { color: $white-c;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
%a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
%a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
||||||
= link_icon
|
= link_icon
|
||||||
= i
|
= i
|
||||||
.blob-content{ data: { blob_id: blob.id } }
|
.blob-content.js-blob-content{ data: { blob_id: blob.id } }
|
||||||
%pre.code.highlight
|
%pre.code.highlight
|
||||||
%code
|
%code
|
||||||
= blob.present.highlight
|
= blob.present.highlight
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Make URLs in blob viewer and blob editor into clickable links
|
||||||
|
merge_request: 18305
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -187,14 +187,18 @@ keys must be manually replicated to the **secondary** node.
|
||||||
1. Visit the **primary** node's **Admin Area > Geo**
|
1. Visit the **primary** node's **Admin Area > Geo**
|
||||||
(`/admin/geo/nodes`) in your browser.
|
(`/admin/geo/nodes`) in your browser.
|
||||||
1. Click the **New node** button.
|
1. Click the **New node** button.
|
||||||
1. Add the **secondary** node. Use the **exact** name you inputed for `gitlab_rails['geo_node_name']` as the Name and the full URL as the URL. **Do NOT** check the
|
|
||||||
**This is a primary node** checkbox.
|
|
||||||
|
|
||||||
![Add secondary node](img/adding_a_secondary_node.png)
|
![Add secondary node](img/adding_a_secondary_node.png)
|
||||||
|
1. Fill in **Name** with the `gitlab_rails['geo_node_name']` in
|
||||||
|
`/etc/gitlab/gitlab.rb`. These values must always match *exactly*, character
|
||||||
|
for character.
|
||||||
|
1. Fill in **URL** with the `external_url` in `/etc/gitlab/gitlab.rb`. These
|
||||||
|
values must always match, but it doesn't matter if one ends with a `/` and
|
||||||
|
the other doesn't.
|
||||||
|
1. **Do NOT** check the **This is a primary node** checkbox.
|
||||||
1. Optionally, choose which groups or storage shards should be replicated by the
|
1. Optionally, choose which groups or storage shards should be replicated by the
|
||||||
**secondary** node. Leave blank to replicate all. Read more in
|
**secondary** node. Leave blank to replicate all. Read more in
|
||||||
[selective synchronization](#selective-synchronization).
|
[selective synchronization](#selective-synchronization).
|
||||||
1. Click the **Add node** button.
|
1. Click the **Add node** button to add the **secondary** node.
|
||||||
1. SSH into your GitLab **secondary** server and restart the services:
|
1. SSH into your GitLab **secondary** server and restart the services:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
@ -115,11 +115,19 @@ Any **secondary** nodes should point only to read-only instances.
|
||||||
|
|
||||||
#### Can Geo detect the current node correctly?
|
#### Can Geo detect the current node correctly?
|
||||||
|
|
||||||
Geo uses the defined node from the **Admin Area > Geo** screen, and tries to match
|
Geo finds the current machine's name in `/etc/gitlab/gitlab.rb` by first looking
|
||||||
it with the value defined in the `/etc/gitlab/gitlab.rb` configuration file.
|
for `gitlab_rails['geo_node_name']`. If it is not defined, then it defaults to
|
||||||
The relevant line looks like: `external_url "http://gitlab.example.com"`.
|
the external URL defined in e.g. `external_url "http://gitlab.example.com"`. To
|
||||||
|
get a machine's name, run:
|
||||||
|
|
||||||
To check if the node on the current machine is correctly detected type:
|
```sh
|
||||||
|
sudo gitlab-rails runner "puts GeoNode.current_node_name"
|
||||||
|
```
|
||||||
|
|
||||||
|
This name is used to look up the node with the same **Name** in
|
||||||
|
**Admin Area > Geo**.
|
||||||
|
|
||||||
|
To check if current machine is correctly finding its node:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo gitlab-rails runner "puts Gitlab::Geo.current_node.inspect"
|
sudo gitlab-rails runner "puts Gitlab::Geo.current_node.inspect"
|
||||||
|
@ -511,6 +519,20 @@ to [cleanup orphan artifact files](../../../raketasks/cleanup.md#remove-orphan-a
|
||||||
On a Geo **secondary** node, this command will also clean up all Geo
|
On a Geo **secondary** node, this command will also clean up all Geo
|
||||||
registry record related to the orphan files on disk.
|
registry record related to the orphan files on disk.
|
||||||
|
|
||||||
|
## Fixing sign in errors
|
||||||
|
|
||||||
|
### Message: The redirect URI included is not valid
|
||||||
|
|
||||||
|
If you are able to log in to the **primary** node, but you receive this error
|
||||||
|
when attempting to log into a **secondary**, you should check that the Geo
|
||||||
|
node's URL matches its external URL.
|
||||||
|
|
||||||
|
1. On the primary, visit **Admin Area > Geo**.
|
||||||
|
1. Find the affected **secondary** and click **Edit**.
|
||||||
|
1. Ensure the **URL** field matches the value found in `/etc/gitlab/gitlab.rb`
|
||||||
|
in `external_url "https://gitlab.example.com"` on the frontend server(s) of
|
||||||
|
the **secondary** node.
|
||||||
|
|
||||||
## Fixing common errors
|
## Fixing common errors
|
||||||
|
|
||||||
This section documents common errors reported in the Admin UI and how to fix them.
|
This section documents common errors reported in the Admin UI and how to fix them.
|
||||||
|
|
|
@ -87,10 +87,9 @@ not be found, or a user does not have access rights to create pipeline there,
|
||||||
the `staging` job is going to be marked as _failed_.
|
the `staging` job is going to be marked as _failed_.
|
||||||
|
|
||||||
CAUTION: **Caution:**
|
CAUTION: **Caution:**
|
||||||
`staging` will succeed as soon as a downstream pipeline gets created.
|
In the example, `staging` will be marked as succeeded as soon as a downstream pipeline
|
||||||
GitLab does not support status attribution yet, however adding first-class
|
gets created. If you want to display the downstream pipeline's status instead, see
|
||||||
`trigger` configuration syntax is ground work for implementing
|
[Mirroring status from triggered pipeline](#mirroring-status-from-triggered-pipeline).
|
||||||
[status attribution](https://gitlab.com/gitlab-org/gitlab-foss/issues/39640).
|
|
||||||
|
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
Bridge jobs do not support every configuration entry that a user can use
|
Bridge jobs do not support every configuration entry that a user can use
|
||||||
|
|
|
@ -42,6 +42,10 @@ Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
|
||||||
ActiveRecord and ActionController log output to that logger. Further options are
|
ActiveRecord and ActionController log output to that logger. Further options are
|
||||||
documented with the method source.
|
documented with the method source.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, logger: Logger.new(STDOUT))
|
||||||
|
```
|
||||||
|
|
||||||
There is also a RubyProf printer available:
|
There is also a RubyProf printer available:
|
||||||
`Gitlab::Profiler::TotalTimeFlatPrinter`. This acts like
|
`Gitlab::Profiler::TotalTimeFlatPrinter`. This acts like
|
||||||
`RubyProf::FlatPrinter`, but its `min_percent` option works on the method's
|
`RubyProf::FlatPrinter`, but its `min_percent` option works on the method's
|
||||||
|
|
|
@ -108,6 +108,10 @@ module QA
|
||||||
find_element(:more_assignees_link)
|
find_element(:more_assignees_link)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def noteable_note_item
|
||||||
|
find_element(:noteable_note_item)
|
||||||
|
end
|
||||||
|
|
||||||
def select_all_activities_filter
|
def select_all_activities_filter
|
||||||
select_filter_with_text('Show all activity')
|
select_filter_with_text('Show all activity')
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,8 +21,9 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using attachments in comments', :object_storage do
|
context 'when using attachments in comments', :object_storage do
|
||||||
|
let(:gif_file_name) { 'banana_sample.gif' }
|
||||||
let(:file_to_attach) do
|
let(:file_to_attach) do
|
||||||
File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif'))
|
File.absolute_path(File.join('spec', 'fixtures', gif_file_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -37,15 +38,7 @@ module QA
|
||||||
Page::Project::Issue::Show.perform do |show|
|
Page::Project::Issue::Show.perform do |show|
|
||||||
show.comment('See attached banana for scale', attachment: file_to_attach)
|
show.comment('See attached banana for scale', attachment: file_to_attach)
|
||||||
|
|
||||||
show.refresh
|
expect(show.noteable_note_item.find("img[src$='#{gif_file_name}']")).to be_visible
|
||||||
|
|
||||||
image_url = find('a[href$="banana_sample.gif"]')[:href]
|
|
||||||
|
|
||||||
found = show.wait(reload: false) do
|
|
||||||
show.asset_exists?(image_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(found).to be_truthy
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,6 +60,13 @@ describe 'Editing file blob', :js do
|
||||||
expect(page).to have_content 'NextFeature'
|
expect(page).to have_content 'NextFeature'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'renders a URL in the content of file as a link' do
|
||||||
|
project.repository.create_file(user, 'file.yml', '# go to https://gitlab.com', message: 'testing', branch_name: branch)
|
||||||
|
visit project_edit_blob_path(project, tree_join(branch, 'file.yml'))
|
||||||
|
|
||||||
|
expect(page).to have_selector('.ace_content .ace_line a')
|
||||||
|
end
|
||||||
|
|
||||||
context 'from blob file path' do
|
context 'from blob file path' do
|
||||||
before do
|
before do
|
||||||
visit project_blob_path(project, tree_join(branch, file_path))
|
visit project_blob_path(project, tree_join(branch, file_path))
|
||||||
|
|
|
@ -11,6 +11,13 @@ describe('Blob viewer', () => {
|
||||||
|
|
||||||
preloadFixtures('snippets/show.html');
|
preloadFixtures('snippets/show.html');
|
||||||
|
|
||||||
|
const asyncClick = () =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
|
||||||
|
|
||||||
|
setTimeout(resolve);
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
|
|
||||||
|
@ -66,19 +73,12 @@ describe('Blob viewer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('doesnt reload file if already loaded', done => {
|
it('doesnt reload file if already loaded', done => {
|
||||||
const asyncClick = () =>
|
|
||||||
new Promise(resolve => {
|
|
||||||
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
|
|
||||||
|
|
||||||
setTimeout(resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
asyncClick()
|
asyncClick()
|
||||||
.then(() => asyncClick())
|
.then(() => asyncClick())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(
|
expect(document.querySelector('.blob-viewer[data-type="simple"]').dataset.loaded).toBe(
|
||||||
document.querySelector('.blob-viewer[data-type="simple"]').getAttribute('data-loaded'),
|
'true',
|
||||||
).toBe('true');
|
);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
|
@ -100,9 +100,7 @@ describe('Blob viewer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has tooltip when disabled', () => {
|
it('has tooltip when disabled', () => {
|
||||||
expect(copyButton.getAttribute('data-original-title')).toBe(
|
expect(copyButton.dataset.title).toBe('Switch to the source to copy the file contents');
|
||||||
'Switch to the source to copy the file contents',
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is blurred when clicked and disabled', () => {
|
it('is blurred when clicked and disabled', () => {
|
||||||
|
@ -136,7 +134,7 @@ describe('Blob viewer', () => {
|
||||||
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
|
document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(copyButton.getAttribute('data-original-title')).toBe('Copy file contents');
|
expect(copyButton.dataset.title).toBe('Copy file contents');
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -177,4 +175,27 @@ describe('Blob viewer', () => {
|
||||||
expect(axios.get.calls.count()).toBe(1);
|
expect(axios.get.calls.count()).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('a URL inside the blob content', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mock.onGet('http://test.host/snippets/1.json?viewer=simple').reply(200, {
|
||||||
|
html:
|
||||||
|
'<div class="js-blob-content"><pre class="code"><code><span class="line" lang="yaml"><span class="c1">To install gitlab-shell you also need a Go compiler version 1.8 or newer. https://golang.org/dl/</span></span></code></pre></div>',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is rendered as a link in simple view', done => {
|
||||||
|
asyncClick()
|
||||||
|
.then(() => {
|
||||||
|
expect(document.querySelector('.blob-viewer[data-type="simple"]').innerHTML).toContain(
|
||||||
|
'<a href="https://golang.org/dl/">https://golang.org/dl/</a>',
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue