Commit e1549c75 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 5d75b2b9
...@@ -202,7 +202,6 @@ GitlabSecurity/PublicSend: ...@@ -202,7 +202,6 @@ GitlabSecurity/PublicSend:
Gitlab/DuplicateSpecLocation: Gitlab/DuplicateSpecLocation:
Exclude: Exclude:
- ee/spec/controllers/projects/jobs_controller_spec.rb
- ee/spec/helpers/auth_helper_spec.rb - ee/spec/helpers/auth_helper_spec.rb
- ee/spec/lib/gitlab/gl_repository_spec.rb - ee/spec/lib/gitlab/gl_repository_spec.rb
- ee/spec/lib/gitlab/usage_data_spec.rb - ee/spec/lib/gitlab/usage_data_spec.rb
...@@ -214,7 +213,6 @@ Gitlab/DuplicateSpecLocation: ...@@ -214,7 +213,6 @@ Gitlab/DuplicateSpecLocation:
- ee/spec/services/merge_requests/refresh_service_spec.rb - ee/spec/services/merge_requests/refresh_service_spec.rb
- ee/spec/services/merge_requests/update_service_spec.rb - ee/spec/services/merge_requests/update_service_spec.rb
- ee/spec/services/system_hooks_service_spec.rb - ee/spec/services/system_hooks_service_spec.rb
- ee/spec/controllers/ee/projects/jobs_controller_spec.rb
- ee/spec/helpers/ee/auth_helper_spec.rb - ee/spec/helpers/ee/auth_helper_spec.rb
- ee/spec/lib/ee/gitlab/gl_repository_spec.rb - ee/spec/lib/ee/gitlab/gl_repository_spec.rb
- ee/spec/lib/ee/gitlab/usage_data_spec.rb - ee/spec/lib/ee/gitlab/usage_data_spec.rb
......
<script> <script>
import _ from 'underscore'; import { escape as esc } from 'lodash';
import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg'; import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png'; import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
...@@ -130,7 +130,7 @@ export default { ...@@ -130,7 +130,7 @@ export default {
}, },
ingressDescription() { ingressDescription() {
return sprintf( return sprintf(
_.escape( esc(
s__( s__(
`ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`, `ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`,
), ),
...@@ -138,14 +138,14 @@ export default { ...@@ -138,14 +138,14 @@ export default {
{ {
pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|pricing'))}</a>`, ${esc(s__('ClusterIntegration|pricing'))}</a>`,
}, },
false, false,
); );
}, },
certManagerDescription() { certManagerDescription() {
return sprintf( return sprintf(
_.escape( esc(
s__( s__(
`ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. `ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates.
Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates
...@@ -155,14 +155,14 @@ export default { ...@@ -155,14 +155,14 @@ export default {
{ {
letsEncrypt: `<a href="https://letsencrypt.org/" letsEncrypt: `<a href="https://letsencrypt.org/"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__("ClusterIntegration|Let's Encrypt"))}</a>`, ${esc(s__("ClusterIntegration|Let's Encrypt"))}</a>`,
}, },
false, false,
); );
}, },
crossplaneDescription() { crossplaneDescription() {
return sprintf( return sprintf(
_.escape( esc(
s__( s__(
`ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}. `ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}.
Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`, Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`,
...@@ -171,7 +171,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity ...@@ -171,7 +171,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
{ {
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane" gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}</a>`, ${esc(s__('ClusterIntegration|Gitlab Integration'))}</a>`,
kubectl: `<code>kubectl</code>`, kubectl: `<code>kubectl</code>`,
}, },
false, false,
...@@ -180,7 +180,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity ...@@ -180,7 +180,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
prometheusDescription() { prometheusDescription() {
return sprintf( return sprintf(
_.escape( esc(
s__( s__(
`ClusterIntegration|Prometheus is an open-source monitoring system `ClusterIntegration|Prometheus is an open-source monitoring system
with %{gitlabIntegrationLink} to monitor deployed applications.`, with %{gitlabIntegrationLink} to monitor deployed applications.`,
...@@ -189,7 +189,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity ...@@ -189,7 +189,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
{ {
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, ${esc(s__('ClusterIntegration|GitLab Integration'))}</a>`,
}, },
false, false,
); );
...@@ -215,11 +215,11 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity ...@@ -215,11 +215,11 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
installedVia() { installedVia() {
if (this.cloudRun) { if (this.cloudRun) {
return sprintf( return sprintf(
_.escape(s__(`ClusterIntegration|installed via %{installed_via}`)), esc(s__(`ClusterIntegration|installed via %{installed_via}`)),
{ {
installed_via: `<a href="${ installed_via: `<a href="${
this.cloudRunHelpPath this.cloudRunHelpPath
}" target="_blank" rel="noopener noreferrer">${_.escape( }" target="_blank" rel="noopener noreferrer">${esc(
s__('ClusterIntegration|Cloud Run'), s__('ClusterIntegration|Cloud Run'),
)}</a>`, )}</a>`,
}, },
......
<script> <script>
import _ from 'underscore'; import { escape as esc } from 'lodash';
import SplitButton from '~/vue_shared/components/split_button.vue'; import SplitButton from '~/vue_shared/components/split_button.vue';
import { GlModal, GlButton, GlFormInput } from '@gitlab/ui'; import { GlModal, GlButton, GlFormInput } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
...@@ -82,7 +82,7 @@ export default { ...@@ -82,7 +82,7 @@ export default {
) )
: s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:'), : s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:'),
{ {
clusterName: `<code>${_.escape(this.clusterName)}</code>`, clusterName: `<code>${esc(this.clusterName)}</code>`,
}, },
false, false,
); );
......
...@@ -47,7 +47,7 @@ module BroadcastMessagesHelper ...@@ -47,7 +47,7 @@ module BroadcastMessagesHelper
end end
def render_broadcast_message(broadcast_message) def render_broadcast_message(broadcast_message)
if Feature.enabled?(:broadcast_message_placeholders) if broadcast_message.notification?
Banzai.render_field_and_post_process(broadcast_message, :message, { Banzai.render_field_and_post_process(broadcast_message, :message, {
current_user: current_user, current_user: current_user,
skip_project_check: true, skip_project_check: true,
......
...@@ -220,7 +220,7 @@ module MergeRequests ...@@ -220,7 +220,7 @@ module MergeRequests
def append_closes_description def append_closes_description
return unless issue&.to_reference.present? return unless issue&.to_reference.present?
closes_issue = "Closes #{issue.to_reference}" closes_issue = "#{target_project.autoclose_referenced_issues ? 'Closes' : 'Related to'} #{issue.to_reference}"
if description.present? if description.present?
descr_parts = [merge_request.description, closes_issue] descr_parts = [merge_request.description, closes_issue]
......
...@@ -12,10 +12,8 @@ module Secpick ...@@ -12,10 +12,8 @@ module Secpick
BRANCH_PREFIX = 'security'.freeze BRANCH_PREFIX = 'security'.freeze
STABLE_SUFFIX = 'stable'.freeze STABLE_SUFFIX = 'stable'.freeze
DEFAULT_REMOTE = 'dev'.freeze DEFAULT_REMOTE = 'security'.freeze
SECURITY_REMOTE = 'security'.freeze
NEW_MR_URL = 'https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/new'.freeze
SECURITY_MR_URL = 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new'.freeze SECURITY_MR_URL = 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new'.freeze
class SecurityFix class SecurityFix
...@@ -66,18 +64,10 @@ module Secpick ...@@ -66,18 +64,10 @@ module Secpick
end end
def new_mr_url def new_mr_url
if @options[:security_remote] if ee?
if ee? SECURITY_MR_URL
SECURITY_MR_URL
else
SECURITY_MR_URL.sub('/gitlab/', '/gitlab-foss/')
end
else else
if ee? SECURITY_MR_URL.sub('/gitlab/', '/gitlab-foss/')
NEW_MR_URL.sub('gitlabhq', 'gitlab-ee')
else
NEW_MR_URL
end
end end
end end
...@@ -124,19 +114,10 @@ module Secpick ...@@ -124,19 +114,10 @@ module Secpick
options[:sha] = sha options[:sha] = sha
end end
opts.on('-r', '--remote abcd', 'Git remote name of dev.gitlab.org (optional, defaults to `dev`)') do |remote| opts.on('-r', '--remote dev', "Git remote name of security repo (optional, defaults to `#{DEFAULT_REMOTE}`)") do |remote|
options[:remote] = remote options[:remote] = remote
end end
opts.on('--security-remote', 'Use the new Security group-based workflow on gitlab.com (note: mutually exclusive to --remote)') do
unless options[:remote].nil?
abort('Cannot use --security-remote with --remote')
end
options[:security_remote] = true
options[:remote] = SECURITY_REMOTE
end
opts.on('-d', '--dry-run', 'Only show Git commands, without calling them') do opts.on('-d', '--dry-run', 'Only show Git commands, without calling them') do
options[:try] = true options[:try] = true
end end
...@@ -144,11 +125,6 @@ module Secpick ...@@ -144,11 +125,6 @@ module Secpick
opts.on('-h', '--help', 'Displays Help') do opts.on('-h', '--help', 'Displays Help') do
puts opts puts opts
puts
puts 'NOTE: If `--security-remote` is used, commands will default ' \
'to using a `security` remote, and merge requests will be created ' \
'on gitlab.com/gitlab-org/security/ rather than dev.gitlab.org.'
exit exit
end end
end end
......
---
title: Add placeholders to broadcast message notifications
merge_request:
author:
type: added
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized: The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
```plaintext ```plaintext
0 => No access
10 => Guest access 10 => Guest access
20 => Reporter access 20 => Reporter access
30 => Developer access 30 => Developer access
......
...@@ -415,6 +415,84 @@ test += " world" ...@@ -415,6 +415,84 @@ test += " world"
When adding new Ruby files, please check that you can add the above header, When adding new Ruby files, please check that you can add the above header,
as omitting it may lead to style check failures. as omitting it may lead to style check failures.
## Reading from files and other data sources
Ruby offers several convenience functions that deal with file contents specifically
or I/O streams in general. Functions such as `IO.read` and `IO.readlines` make
it easy to read data into memory, but they can be inefficient when the
data grows large. Because these functions read the entire contents of a data
source into memory, memory use will grow by _at least_ the size of the data source.
In the case of `readlines`, it will grow even further, due to extra bookkeeping
the Ruby VM has to perform to represent each line.
Consider the following program, which reads a text file that is 750MB on disk:
```ruby
File.readlines('large_file.txt').each do |line|
puts line
end
```
Here is a process memory reading from while the program was running, showing
how we indeed kept the entire file in memory (RSS reported in kilobytes):
```shell
$ ps -o rss -p <pid>
RSS
783436
```
And here is an excerpt of what the garbage collector was doing:
```ruby
pp GC.stat
{
:heap_live_slots=>2346848,
:malloc_increase_bytes=>30895288,
...
}
```
We can see that `heap_live_slots` (the number of reachable objects) jumped to ~2.3M,
which is roughly two orders of magnitude more compared to reading the file line by
line instead. It was not just the raw memory usage that increased, but also how the garbage collector (GC)
responded to this change in anticipation of future memory use. We can see that `malloc_increase_bytes` jumped
to ~30MB, which compares to just ~4kB for a "fresh" Ruby program. This figure specifies how
much additional heap space the Ruby GC will claim from the operating system next time it runs out of memory.
Not only did we occupy more memory, we also changed the behavior of the application
to increase memory use at a faster rate.
The `IO.read` function exhibits similar behavior, with the difference that no extra memory will
be allocated for each line object.
### Recommendations
Instead of reading data sources into memory in full, it is better to read them line by line
instead. This is not always an option, for instance when you need to convert a YAML file
into a Ruby `Hash`, but whenever you have data where each row represents some entity that
can be processed and then discarded, you can use the following approaches.
First, replace calls to `readlines.each` with either `each` or `each_line`.
The `each_line` and `each` functions read the data source line by line without keeping
already visited lines in memory:
```ruby
File.new('file').each { |line| puts line }
```
Alternatively, you can read individual lines explicitly using `IO.readline` or `IO.gets` functions:
```ruby
while line = file.readline
# process line
end
```
This might be preferable if there is a condition that allows exiting the loop early, saving not
just memory but also unnecessary time spent in CPU and I/O for processing lines you're not interested in.
## Anti-Patterns ## Anti-Patterns
This is a collection of [anti-patterns][anti-pattern] that should be avoided This is a collection of [anti-patterns][anti-pattern] that should be avoided
......
...@@ -13,3 +13,4 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec ...@@ -13,3 +13,4 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#quarantining-tests), will run in a separate job that only includes quarantined tests, and is allowed to fail. The test will be skipped in its regular job so that if it fails it will not hold up the pipeline. | | `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#quarantining-tests), will run in a separate job that only includes quarantined tests, and is allowed to fail. The test will be skipped in its regular job so that if it fails it will not hold up the pipeline. |
| `:reliable` | The test has been [promoted to a reliable test](https://about.gitlab.com/handbook/engineering/quality/guidelines/reliable-tests/#promoting-an-existing-test-to-reliable) meaning it passes consistently in all pipelines, including merge requests. | | `:reliable` | The test has been [promoted to a reliable test](https://about.gitlab.com/handbook/engineering/quality/guidelines/reliable-tests/#promoting-an-existing-test-to-reliable) meaning it passes consistently in all pipelines, including merge requests. |
| `:requires_admin` | The test requires an admin account. Tests with the tag are excluded when run against Canary and Production environments. | | `:requires_admin` | The test requires an admin account. Tests with the tag are excluded when run against Canary and Production environments. |
| `:runner` | The test depends on and will set up a GitLab Runner instance, typically to run a pipeline. |
...@@ -547,16 +547,26 @@ The more challenging part are mocks, which can be used for functions or even dep ...@@ -547,16 +547,26 @@ The more challenging part are mocks, which can be used for functions or even dep
### Manual module mocks ### Manual module mocks
Jest supports [manual module mocks](https://jestjs.io/docs/en/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module. **Don't do this.** We want to keep all of our test-related code in one place (the `spec/` folder), and the logic that Jest uses to apply mocks from `__mocks__/` is rather inconsistent. Manual mocks are used to mock modules across the entire Jest environment. This is a very powerful testing tool that helps simplify
unit testing by mocking out modules which cannot be easily consumned in our test environment.
Instead, our test runner detects manual mocks from `spec/frontend/mocks/`. Any mock placed here is automatically picked up and injected whenever you import its source module. > NOTE: Do not use manual mocks if a mock should not be consistently applied (i.e. it's only needed by a few specs).
> Instead, consider using `jest.mock` in the relevant spec file.
#### Where should I put manual mocks?
Jest supports [manual module mocks](https://jestjs.io/docs/en/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module
(e.g. `app/assets/javascripts/ide/__mocks__`). **Don't do this.** We want to keep all of our test-related code in one place (the `spec/` folder).
If a manual mock is needed for a `node_modules` package, please use the `spec/frontend/__mocks__` folder. Here's an example of
a [Jest mock for the package `monaco-editor`](https://gitlab.com/gitlab-org/gitlab/blob/b7f914cddec9fc5971238cdf12766e79fa1629d7/spec/frontend/__mocks__/monaco-editor/index.js#L1).
If a manual mock is needed for a CE module, please place it in `spec/frontend/mocks/ce`.
- Files in `spec/frontend/mocks/ce` will mock the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path. - Files in `spec/frontend/mocks/ce` will mock the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path.
- Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` will mock the module `~/lib/utils/axios_utils`. - Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` will mock the module `~/lib/utils/axios_utils`.
- Files in `spec/frontend/mocks/node` will mock NPM packages of the same name or path.
- We don't support mocking EE modules yet. - We don't support mocking EE modules yet.
- If a mock is found for which a source module doesn't exist, the test suite will fail. 'Virtual' mocks, or mocks that don't have a 1-to-1 association with a source module, are not supported yet.
If a mock is found for which a source module doesn't exist, the test suite will fail. 'Virtual' mocks, or mocks that don't have a 1-to-1 association with a source module, are not supported yet.
### Writing a mock ### Writing a mock
......
...@@ -205,8 +205,11 @@ This stage would not work because the end event has already happened when the st ...@@ -205,8 +205,11 @@ This stage would not work because the end event has already happened when the st
To prevent such invalid stages, the UI prohibits incompatible start and end events. After you select To prevent such invalid stages, the UI prohibits incompatible start and end events. After you select
the start event, the stop event dropdown will only list the compatible events. the start event, the stop event dropdown will only list the compatible events.
NOTE: **Note:** ### Re-ordering stages
The ability to re-order the stages is [planned](https://gitlab.com/gitlab-org/gitlab/issues/196698).
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196698) in GitLab 12.10.
Once a custom stage has been added, you can "drag and drop" stages to rearrange their order. These changes are automatically saved to the system.
### Label based stages ### Label based stages
......
...@@ -260,6 +260,8 @@ Sign in and re-enable two-factor authentication as soon as possible. ...@@ -260,6 +260,8 @@ Sign in and re-enable two-factor authentication as soon as possible.
- The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because - The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
the U2F key has only been registered on `first.host.xyz`. the U2F key has only been registered on `first.host.xyz`.
- To enforce 2FA at the system or group levels see [Enforce Two-factor Authentication](../../../security/two_factor_authentication.md).
## Troubleshooting ## Troubleshooting
If you are receiving an `invalid pin code` error, this may indicate that there is a time sync issue between the authentication application and the GitLab instance itself. If you are receiving an `invalid pin code` error, this may indicate that there is a time sync issue between the authentication application and the GitLab instance itself.
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
context 'Verify', :docker do context 'Verify', :docker, :runner do
describe 'Pipeline creation and processing' do describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:max_wait) { 30 } let(:max_wait) { 30 }
...@@ -12,7 +12,7 @@ module QA ...@@ -12,7 +12,7 @@ module QA
end end
end end
before do let!(:runner) do
Resource::Runner.fabricate! do |runner| Resource::Runner.fabricate! do |runner|
runner.project = project runner.project = project
runner.name = executor runner.name = executor
...@@ -21,7 +21,7 @@ module QA ...@@ -21,7 +21,7 @@ module QA
end end
after do after do
Service::DockerRun::GitlabRunner.new(executor).remove! runner.remove_via_api!
end end
it 'users creates a pipeline which gets processed', :smoke do it 'users creates a pipeline which gets processed', :smoke do
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
context 'Verify', :docker do context 'Verify', :docker, :runner do
describe 'Runner registration' do describe 'Runner registration' do
let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:executor) { "qa-runner-#{Time.now.to_i}" }
let!(:runner) do
Resource::Runner.fabricate! do |runner|
runner.name = executor
end
end
after do after do
Service::DockerRun::GitlabRunner.new(executor).remove! runner.remove_via_api!
end end
it 'user registers a new specific runner' do it 'user registers a new specific runner' do
Flow::Login.sign_in Flow::Login.sign_in
Resource::Runner.fabricate! do |runner| runner.project.visit!
runner.name = executor
end.project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings) Page::Project::Menu.perform(&:go_to_ci_cd_settings)
Page::Project::Settings::CICD.perform do |settings| Page::Project::Settings::CICD.perform do |settings|
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'digest/sha1' require 'digest/sha1'
module QA module QA
context 'Release', :docker do context 'Release', :docker, :runner do
describe 'Git clone using a deploy key' do describe 'Git clone using a deploy key' do
before do before do
Flow::Login.sign_in Flow::Login.sign_in
...@@ -16,7 +16,7 @@ module QA ...@@ -16,7 +16,7 @@ module QA
@repository_location = @project.repository_ssh_location @repository_location = @project.repository_ssh_location
Resource::Runner.fabricate_via_api! do |resource| @runner = Resource::Runner.fabricate_via_api! do |resource|
resource.project = @project resource.project = @project
resource.name = @runner_name resource.name = @runner_name
resource.tags = %w[qa docker] resource.tags = %w[qa docker]
...@@ -25,7 +25,7 @@ module QA ...@@ -25,7 +25,7 @@ module QA
end end
after do after do
Service::DockerRun::GitlabRunner.new(@runner_name).remove! @runner.remove_via_api!
end end
keys = [ keys = [
......
...@@ -3,12 +3,23 @@ ...@@ -3,12 +3,23 @@
require 'spec_helper' require 'spec_helper'
describe 'Broadcast Messages' do describe 'Broadcast Messages' do
shared_examples 'a Broadcast Messages' do let_it_be(:user) { create(:user) }
shared_examples 'a Broadcast Messages' do |type|
it 'shows broadcast message' do it 'shows broadcast message' do
visit root_path visit root_path
expect(page).to have_content 'SampleMessage' expect(page).to have_content 'SampleMessage'
end end
it 'renders styled links' do
create(:broadcast_message, type, message: "<a href='gitlab.com' style='color: purple'>click me</a>")
visit root_path
expected_html = "<p><a href=\"gitlab.com\" style=\"color: purple\">click me</a></p>"
expect(page.body).to include(expected_html)
end
end end
shared_examples 'a dismissable Broadcast Messages' do shared_examples 'a dismissable Broadcast Messages' do
...@@ -35,11 +46,21 @@ describe 'Broadcast Messages' do ...@@ -35,11 +46,21 @@ describe 'Broadcast Messages' do
it_behaves_like 'a Broadcast Messages' it_behaves_like 'a Broadcast Messages'
it 'shows broadcast message' do it 'is not dismissable' do
visit root_path visit root_path
expect(page).not_to have_selector('.js-dismiss-current-broadcast-notification') expect(page).not_to have_selector('.js-dismiss-current-broadcast-notification')
end end
it 'does not replace placeholders' do
create(:broadcast_message, message: 'Hi {{name}}')
sign_in(user)
visit root_path
expect(page).to have_content 'Hi {{name}}'
end
end end
describe 'dismissable banner type' do describe 'dismissable banner type' do
...@@ -51,33 +72,20 @@ describe 'Broadcast Messages' do ...@@ -51,33 +72,20 @@ describe 'Broadcast Messages' do
end end
describe 'notification type' do describe 'notification type' do
let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') } let!(:broadcast_message) { create(:broadcast_message, :notification, message: 'SampleMessage') }
it_behaves_like 'a Broadcast Messages' it_behaves_like 'a Broadcast Messages', :notification
it_behaves_like 'a dismissable Broadcast Messages' it_behaves_like 'a dismissable Broadcast Messages'
end
it 'renders broadcast message with placeholders' do
create(:broadcast_message, broadcast_type: 'notification', message: 'Hi {{name}}')
user = create(:user) it 'replaces placeholders' do
sign_in(user) create(:broadcast_message, :notification, message: 'Hi {{name}}')
visit root_path sign_in(user)
expect(page).to have_content "Hi #{user.name}" visit root_path
end
it 'renders broadcast message with placeholders and styled links' do
create(:broadcast_message, broadcast_type: 'notification', message: "Hi {{name}} <a href='gitlab.com' style='color: purple'>click</a>")
user = create(:user)
sign_in(user)
visit root_path
expected_html = "<p>Hi #{user.name} <a href=\"gitlab.com\" style=\"color: purple\">click</a></p>" expect(page).to have_content "Hi #{user.name}"
expect(page.body).to include(expected_html) end
end end
end end
...@@ -16,7 +16,6 @@ const prefixMap = [ ...@@ -16,7 +16,6 @@ const prefixMap = [
// E.g. the mock ce/foo/bar maps to require path ~/foo/bar // E.g. the mock ce/foo/bar maps to require path ~/foo/bar
{ mocksRoot: 'ce', requirePrefix: '~' }, { mocksRoot: 'ce', requirePrefix: '~' },
// { mocksRoot: 'ee', requirePrefix: 'ee' }, // We'll deal with EE-specific mocks later // { mocksRoot: 'ee', requirePrefix: 'ee' }, // We'll deal with EE-specific mocks later
{ mocksRoot: 'node', requirePrefix: '' },
// { mocksRoot: 'virtual', requirePrefix: '' }, // We'll deal with virtual mocks later // { mocksRoot: 'virtual', requirePrefix: '' }, // We'll deal with virtual mocks later
]; ];
......
...@@ -34,9 +34,8 @@ describe('mocks_helper.js', () => { ...@@ -34,9 +34,8 @@ describe('mocks_helper.js', () => {
it('enumerates through mock file roots', () => { it('enumerates through mock file roots', () => {
setupManualMocks(); setupManualMocks();
expect(fs.existsSync).toHaveBeenCalledTimes(2); expect(fs.existsSync).toHaveBeenCalledTimes(1);
expect(fs.existsSync).toHaveBeenNthCalledWith(1, absPath('ce')); expect(fs.existsSync).toHaveBeenNthCalledWith(1, absPath('ce'));
expect(fs.existsSync).toHaveBeenNthCalledWith(2, absPath('node'));
expect(readdir.sync).toHaveBeenCalledTimes(0); expect(readdir.sync).toHaveBeenCalledTimes(0);
}); });
...@@ -66,19 +65,6 @@ describe('mocks_helper.js', () => { ...@@ -66,19 +65,6 @@ describe('mocks_helper.js', () => {
expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util'); expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util');
}); });
it('sets up mocks for node_modules', () => {
fs.existsSync.mockImplementation(root => root.endsWith('node'));
readdir.sync.mockReturnValue(['jquery', '@babel/core']);
setupManualMocks();
expect(readdir.sync).toHaveBeenCalledTimes(1);
expect(readdir.sync.mock.calls[0][0]).toBe(absPath('node'));
expect(setMock).toHaveBeenCalledTimes(2);
expect(setMock).toHaveBeenNthCalledWith(1, 'jquery', './node/jquery');
expect(setMock).toHaveBeenNthCalledWith(2, '@babel/core', './node/@babel/core');
});
it('sets up mocks for all roots', () => { it('sets up mocks for all roots', () => {
const files = { const files = {
[absPath('ce')]: ['root', 'lib/utils/util'], [absPath('ce')]: ['root', 'lib/utils/util'],
...@@ -89,15 +75,12 @@ describe('mocks_helper.js', () => { ...@@ -89,15 +75,12 @@ describe('mocks_helper.js', () => {
readdir.sync.mockImplementation(root => files[root]); readdir.sync.mockImplementation(root => files[root]);
setupManualMocks(); setupManualMocks();
expect(readdir.sync).toHaveBeenCalledTimes(2); expect(readdir.sync).toHaveBeenCalledTimes(1);
expect(readdir.sync.mock.calls[0][0]).toBe(absPath('ce')); expect(readdir.sync.mock.calls[0][0]).toBe(absPath('ce'));
expect(readdir.sync.mock.calls[1][0]).toBe(absPath('node'));
expect(setMock).toHaveBeenCalledTimes(4); expect(setMock).toHaveBeenCalledTimes(2);
expect(setMock).toHaveBeenNthCalledWith(1, '~/root', './ce/root'); expect(setMock).toHaveBeenNthCalledWith(1, '~/root', './ce/root');
expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util'); expect(setMock).toHaveBeenNthCalledWith(2, '~/lib/utils/util', './ce/lib/utils/util');
expect(setMock).toHaveBeenNthCalledWith(3, 'jquery', './node/jquery');
expect(setMock).toHaveBeenNthCalledWith(4, '@babel/core', './node/@babel/core');
}); });
it('fails when given a virtual mock', () => { it('fails when given a virtual mock', () => {
......
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