Commit a03d66c7 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-11-26

# Conflicts:
#	locale/gitlab.pot

[ci skip]
parents 4ef33e00 16a9080f
......@@ -938,6 +938,7 @@ qa:selectors:
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- date
- yarn run webpack-prod
<<: *except-docs
qa-frontend-node:6:
<<: *qa-frontend-node
......
......@@ -126,6 +126,7 @@ export default {
:text="commitMessage"
:placeholder="preBuiltCommitMessage"
@input="updateCommitMessage"
@submit="commitChanges"
/>
<div class="clearfix prepend-top-15">
<actions />
......
......@@ -49,6 +49,10 @@ export default {
onInput(e) {
this.$emit('input', e.target.value);
},
onCtrlEnter() {
if (!this.isFocused) return;
this.$emit('submit');
},
updateIsFocused(isFocused) {
this.isFocused = isFocused;
},
......@@ -109,6 +113,8 @@ export default {
@input="onInput"
@focus="updateIsFocused(true);"
@blur="updateIsFocused(false);"
@keydown.ctrl.enter="onCtrlEnter"
@keydown.meta.enter="onCtrlEnter"
>
</textarea>
</div>
......
......@@ -19,7 +19,7 @@ export default class LazyLoader {
}
searchLazyImages() {
requestIdleCallback(
window.requestIdleCallback(
() => {
const lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
......@@ -107,7 +107,7 @@ export default class LazyLoader {
}
scrollCheck() {
requestAnimationFrame(() => this.checkElementsInView());
window.requestAnimationFrame(() => this.checkElementsInView());
}
checkElementsInView() {
......@@ -122,7 +122,7 @@ export default class LazyLoader {
const imgBound = imgTop + imgBoundRect.height;
if (scrollTop <= imgBound && visHeight >= imgTop) {
requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
LazyLoader.loadImage(selectedImage);
});
return false;
......
......@@ -249,6 +249,13 @@
- else
%kbd ctrl p
%td Go to file
%tr
%td.shortcut
- if browser.platform.mac?
%kbd &#8984; enter
- else
%kbd ctrl enter
%td Commit (when editing commit message)
.col-lg-4
%table.shortcut-mappings
%tbody.hidden-shortcut{ style: 'display:none' }
......
......@@ -2,7 +2,7 @@
%hr
%p.lead To start serving your jobs you can either add specific Runners to your project or use shared Runners
%p.lead= _('To start serving your jobs you can either add specific Runners to your project or use shared Runners')
.row
.col-sm-6
= render 'projects/runners/specific_runners'
......
......@@ -5,21 +5,19 @@
- if Gitlab::CurrentSettings.shared_runners_text.present?
= markdown_field(Gitlab::CurrentSettings.current_application_settings, :shared_runners_text)
- else
GitLab Shared Runners execute code of different projects on the same Runner
unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is
on GitLab.com).
= _('GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com).')
%hr
- if @project.shared_runners_enabled?
= link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-close', method: :post do
Disable shared Runners
= _('Disable shared Runners')
- else
= link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-success', method: :post do
Enable shared Runners
= _('Enable shared Runners')
&nbsp; for this project
- if @shared_runners_count.zero?
= _('This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area.')
- else
%h4.underlined-title Available shared Runners : #{@shared_runners_count}
%h4.underlined-title #{_('Available shared Runners :')} #{@shared_runners_count}
%ul.bordered-list.available-shared-runners
= render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
......@@ -31,12 +31,12 @@
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path }
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
%h4.underlined-title= _('Runners activated for this project')
%ul.bordered-list.activated-specific-runners
= render partial: 'projects/runners/runner', collection: @project_runners, as: :runner
- if @assignable_runners.any?
%h4.underlined-title Available specific runners
%h4.underlined-title= _('Available specific runners')
%ul.bordered-list.available-specific-runners
= render partial: 'projects/runners/runner', collection: @assignable_runners, as: :runner
= paginate @assignable_runners, theme: "gitlab", :params => { :anchor => '#js-runners-settings' }
- page_title _('Edit'), "#{@runner.description} ##{@runner.id}", 'Runners'
- page_title _('Edit'), "#{@runner.description} ##{@runner.id}", _('Runners')
%h4 Runner ##{@runner.id}
......
---
title: "WebIDE: Pressing Ctrl-Enter while typing on the commit message now performs the commit action"
merge_request: 23049
author: Thomas Pathier
type: added
---
title: Externalize strings from `/app/views/project/runners`
merge_request: 23208
author: Tao Wang
type: other
......@@ -72,8 +72,8 @@ GET /users
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `order_by` | string | no | Return projects ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `order_by` | string | no | Return users ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
| `sort` | string | no | Return users sorted in `asc` or `desc` order. Default is `desc` |
| `two_factor` | string | no | Filter users by Two-factor authentication. Filter values are `enabled` or `disabled`. By default it returns all users |
```json
......
......@@ -17,6 +17,9 @@ This merge is done automatically in a
1. If all conflicts are resolved after your resolution is pushed, keep the merge
request assigned to you: **you are now responsible for the merge request to be
green**
1. If you are the last person to resolve the conflicts, the pipeline is green,
and you have merge rights, merge the MR, but **do not** choose to squash.
Otherwise, assign the MR to someone that can merge.
1. If you need any help, you can ping the current [release managers], or ask in
the `#ce-to-ee` Slack channel
......
......@@ -47,13 +47,13 @@ The source of these Yarn scripts can be found in `/scripts/frontend/prettier.js`
### Scripts during Conversion period
```
node ./scripts/frontend/prettier.js check ./vendor/
node ./scripts/frontend/prettier.js check-all ./vendor/
```
This will go over all files in a specific folder check it.
```
node ./scripts/frontend/prettier.js save ./vendor/
node ./scripts/frontend/prettier.js save-all ./vendor/
```
This will go over all files in a specific folder and save it.
......@@ -102,3 +102,4 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| Keyboard Shortcut | Description |
| ----------------- | ----------- |
| <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>p</kbd> | Go to file |
| <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>Enter</kbd> | Commit (when editing the commit message) |
......@@ -992,6 +992,12 @@ msgstr ""
msgid "Available group Runners : %{runners}."
msgstr ""
msgid "Available shared Runners :"
msgstr ""
msgid "Available specific runners"
msgstr ""
msgid "Avatar will be removed. Are you sure?"
msgstr ""
......@@ -2878,6 +2884,9 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
msgid "Disable shared Runners"
msgstr ""
msgid "Discard"
msgstr ""
......@@ -3055,7 +3064,11 @@ msgstr ""
msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr ""
<<<<<<< HEAD
msgid "Enable self approval of merge requests"
=======
msgid "Enable shared Runners"
>>>>>>> upstream/master
msgstr ""
msgid "Enable the Performance Bar for a given group."
......@@ -3706,7 +3719,14 @@ msgstr ""
msgid "GeoNodes|Does not match the primary storage configuration"
msgstr ""
<<<<<<< HEAD
msgid "GeoNodes|Failed"
=======
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
msgstr ""
msgid "GitLab User"
>>>>>>> upstream/master
msgstr ""
msgid "GeoNodes|Full"
......@@ -7097,6 +7117,9 @@ msgstr ""
msgid "Runners API"
msgstr ""
msgid "Runners activated for this project"
msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
......@@ -8641,6 +8664,9 @@ msgstr ""
msgid "To start serving your jobs you can add Runners to your group"
msgstr ""
msgid "To start serving your jobs you can either add specific Runners to your project or use shared Runners"
msgstr ""
msgid "To this GitLab instance"
msgstr ""
......
export default function scrollIntoViewPromise(intersectionTarget, timeout = 100, maxTries = 5) {
return new Promise((resolve, reject) => {
let intersectionObserver;
let retry = 0;
const intervalId = setInterval(() => {
if (retry >= maxTries) {
intersectionObserver.disconnect();
clearInterval(intervalId);
reject(new Error(`Could not scroll target into viewPort within ${timeout * maxTries} ms`));
}
retry += 1;
intersectionTarget.scrollIntoView();
}, timeout);
intersectionObserver = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
intersectionObserver.disconnect();
clearInterval(intervalId);
resolve();
}
});
intersectionObserver.observe(intersectionTarget);
intersectionTarget.scrollIntoView();
});
}
export default (domElement, attributes, timeout = 1500) =>
new Promise((resolve, reject) => {
let observer;
const timeoutId = setTimeout(() => {
observer.disconnect();
reject(new Error(`Could not see an attribute update within ${timeout} ms`));
}, timeout);
observer = new MutationObserver(() => {
clearTimeout(timeoutId);
observer.disconnect();
resolve();
});
observer.observe(domElement, { attributes: true, attributeFilter: attributes });
});
import LazyLoader from '~/lazy_loader';
import { TEST_HOST } from './test_constants';
let lazyLoader = null;
import scrollIntoViewPromise from './helpers/scroll_into_view_promise';
import waitForPromises from './helpers/wait_for_promises';
import waitForAttributeChange from './helpers/wait_for_attribute_change';
const execImmediately = callback => {
callback();
};
describe('LazyLoader', function() {
let lazyLoader = null;
preloadFixtures('issues/issue_with_comment.html.raw');
describe('with IntersectionObserver disabled', () => {
describe('without IntersectionObserver', () => {
beforeEach(function() {
loadFixtures('issues/issue_with_comment.html.raw');
......@@ -36,14 +39,15 @@ describe('LazyLoader', function() {
it('should copy value from data-src to src for img 1', function(done) {
const img = document.querySelectorAll('img[data-src]')[0];
const originalDataSrc = img.getAttribute('data-src');
img.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(img.getAttribute('src')).toBe(originalDataSrc);
expect(img).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(img.getAttribute('src')).toBe(originalDataSrc);
expect(img).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should lazy load dynamically added data-src images', function(done) {
......@@ -52,14 +56,18 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg.getAttribute('src')).toBe(testPath);
expect(newImg).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([
scrollIntoViewPromise(newImg),
waitForAttributeChange(newImg, ['data-src', 'src']),
])
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg.getAttribute('src')).toBe(testPath);
expect(newImg).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should not alter normal images', function(done) {
......@@ -67,13 +75,15 @@ describe('LazyLoader', function() {
const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.setAttribute('src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
}, 50);
scrollIntoViewPromise(newImg)
.then(waitForPromises)
.then(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should not load dynamically added pictures if content observer is turned off', done => {
......@@ -84,13 +94,15 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
}, 50);
scrollIntoViewPromise(newImg)
.then(waitForPromises)
.then(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should load dynamically added pictures if content observer is turned off and on again', done => {
......@@ -102,17 +114,22 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([
scrollIntoViewPromise(newImg),
waitForAttributeChange(newImg, ['data-src', 'src']),
])
.then(waitForPromises)
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
});
describe('with IntersectionObserver enabled', () => {
describe('with IntersectionObserver', () => {
beforeEach(function() {
loadFixtures('issues/issue_with_comment.html.raw');
......@@ -136,14 +153,15 @@ describe('LazyLoader', function() {
it('should copy value from data-src to src for img 1', function(done) {
const img = document.querySelectorAll('img[data-src]')[0];
const originalDataSrc = img.getAttribute('data-src');
img.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(img.getAttribute('src')).toBe(originalDataSrc);
expect(img).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(img.getAttribute('src')).toBe(originalDataSrc);
expect(img).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should lazy load dynamically added data-src images', function(done) {
......@@ -152,14 +170,18 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg.getAttribute('src')).toBe(testPath);
expect(newImg).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([
scrollIntoViewPromise(newImg),
waitForAttributeChange(newImg, ['data-src', 'src']),
])
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg.getAttribute('src')).toBe(testPath);
expect(newImg).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should not alter normal images', function(done) {
......@@ -167,13 +189,15 @@ describe('LazyLoader', function() {
const testPath = `${TEST_HOST}/img/testimg.png`;
newImg.setAttribute('src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
}, 50);
scrollIntoViewPromise(newImg)
.then(waitForPromises)
.then(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should not load dynamically added pictures if content observer is turned off', done => {
......@@ -184,13 +208,15 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
}, 50);
scrollIntoViewPromise(newImg)
.then(waitForPromises)
.then(() => {
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
expect(newImg).not.toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
it('should load dynamically added pictures if content observer is turned off and on again', done => {
......@@ -202,13 +228,17 @@ describe('LazyLoader', function() {
newImg.className = 'lazy';
newImg.setAttribute('data-src', testPath);
document.body.appendChild(newImg);
newImg.scrollIntoView();
setTimeout(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg).toHaveClass('js-lazy-loaded');
done();
}, 50);
Promise.all([
scrollIntoViewPromise(newImg),
waitForAttributeChange(newImg, ['data-src', 'src']),
])
.then(() => {
expect(LazyLoader.loadImage).toHaveBeenCalled();
expect(newImg).toHaveClass('js-lazy-loaded');
done();
})
.catch(done.fail);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment