Commit 68d3f33d authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 6a4ffad4
...@@ -66,7 +66,7 @@ eslint-report.html ...@@ -66,7 +66,7 @@ eslint-report.html
/vendor/gitaly-ruby /vendor/gitaly-ruby
/builds* /builds*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/.gitlab_pages_shared_secret /.gitlab_pages_secret
/webpack-report/ /webpack-report/
/knapsack/ /knapsack/
/rspec_flaky/ /rspec_flaky/
......
...@@ -148,26 +148,6 @@ module Issuable ...@@ -148,26 +148,6 @@ module Issuable
strip_attributes :title strip_attributes :title
# The state_machine gem will reset the value of state_id unless it
# is a raw attribute passed in here:
# https://gitlab.com/gitlab-org/gitlab/issues/35746#note_241148787
#
# This assumes another initialize isn't defined. Otherwise this
# method may need to be prepended.
def initialize(attributes = nil)
if attributes.is_a?(Hash)
attr = attributes.symbolize_keys
if attr.key?(:state) && !attr.key?(:state_id)
value = attr.delete(:state)
state_id = self.class.available_states[value]
attributes[:state_id] = state_id if state_id
end
end
super(attributes)
end
# We want to use optimistic lock for cases when only title or description are involved # We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html # http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled? def locking_enabled?
......
...@@ -77,7 +77,7 @@ class Issue < ApplicationRecord ...@@ -77,7 +77,7 @@ class Issue < ApplicationRecord
attr_spammable :title, spam_title: true attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true attr_spammable :description, spam_description: true
state_machine :state_id, initial: :opened do state_machine :state_id, initial: :opened, initialize: false do
event :close do event :close do
transition [:opened] => :closed transition [:opened] => :closed
end end
......
...@@ -103,7 +103,7 @@ class MergeRequest < ApplicationRecord ...@@ -103,7 +103,7 @@ class MergeRequest < ApplicationRecord
super + [:merged, :locked] super + [:merged, :locked]
end end
state_machine :state_id, initial: :opened do state_machine :state_id, initial: :opened, initialize: false do
event :close do event :close do
transition [:opened] => :closed transition [:opened] => :closed
end end
......
---
title: Add template repository usage to the usage ping
merge_request: 20126
author: minghuan lei
type: changed
...@@ -321,8 +321,8 @@ production: &base ...@@ -321,8 +321,8 @@ production: &base
# external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages # external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages
# File that contains the shared secret key for verifying access for gitlab-pages. # File that contains the shared secret key for verifying access for gitlab-pages.
# Default is '.gitlab_pages_shared_secret' relative to Rails.root (i.e. root of the GitLab app). # Default is '.gitlab_pages_secret' relative to Rails.root (i.e. root of the GitLab app).
# secret_file: /home/git/gitlab/.gitlab_pages_shared_secret # secret_file: /home/git/gitlab/.gitlab_pages_secret
## Mattermost ## Mattermost
## For enabling Add to Mattermost button ## For enabling Add to Mattermost button
......
...@@ -291,7 +291,7 @@ Settings.pages['url'] ||= Settings.__send__(:build_pages_url) ...@@ -291,7 +291,7 @@ Settings.pages['url'] ||= Settings.__send__(:build_pages_url)
Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present? Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present?
Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present? Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present?
Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil? Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil?
Settings.pages['secret_file'] ||= Rails.root.join('.gitlab_pages_shared_secret') Settings.pages['secret_file'] ||= Rails.root.join('.gitlab_pages_secret')
# #
# Geo # Geo
......
...@@ -336,6 +336,49 @@ Generated docker images should have the program at their `Entrypoint` to create ...@@ -336,6 +336,49 @@ Generated docker images should have the program at their `Entrypoint` to create
portable commands. That way, anyone can run the image, and without parameters portable commands. That way, anyone can run the image, and without parameters
it will display its help message (if `cli` has been used). it will display its help message (if `cli` has been used).
## Distributing Go binaries
With the exception of [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner),
which publishes its own binaries, our Go binaries are created by projects
managed by the [Distribution group](https://about.gitlab.com/handbook/product/categories/#distribution-group).
The [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab) project creates a
single, monolithic operating system package containing all the binaries, while
the [Cloud-Native GitLab (CNG)](https://gitlab.com/gitlab-org/build/CNG) project
publishes a set of Docker images and Helm charts to glue them together.
Both approaches use the same version of Go for all projects, so it's important
to ensure all our Go-using projects have at least one Go version in common in
their test matrices. You can check the version of Go currently being used by
[Omnibus](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/blob/master/docker/Dockerfile_debian_10#L59),
and the version being used for [CNG](https://gitlab.com/gitlab-org/build/cng/blob/master/ci_files/variables.yml#L12).
### Updating Go version
We should always use a [supported version](https://golang.org/doc/devel/release.html#policy)
of Go, i.e., one of the three most recent minor releases, and should always use
the most recent patch-level for that version, as it may contain security fixes.
Changing the version affects every project being compiled, so it's important to
ensure that all projects have been updated to test against the new Go version
before changing the package builders to use it. Despite [Go's compatibility promise](https://golang.org/doc/go1compat),
changes between minor versions can expose bugs or cause problems in our projects.
Once you've picked a new Go version to use, the steps to update Omnibus and CNG
are:
- [Create a merge request in the CNG project](https://gitlab.com/gitlab-org/build/CNG/edit/master/ci_files/variables.yml?branch_name=update-go-version),
updating the `GO_VERSION` in `ci_files/variables.yml`.
- Create a merge request in the [`gitlab-omnibus-builder` project](https://gitlab.com/gitlab-org/gitlab-omnibus-builder),
updating every file in the `docker/` directory so the `GO_VERSION` is set
appropriately. [Here's an example](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/merge_requests/125/diffs).
- Tag a new release of `gitlab-omnibus-builder` containing the change.
- [Create a merge request in the `gitlab-omnibus` project](https://gitlab.com/gitlab-org/omnibus-gitlab/edit/master/.gitlab-ci.yml?branch_name=update-gitlab-omnibus-builder-version),
updating the `BUILDER_IMAGE_REVISION` to match the newly-created tag.
To reduce unnecessary differences between two distribution methods, Omnibus and
CNG **should always use the same Go version**.
--- ---
[Return to Development documentation](../README.md). [Return to Development documentation](../README.md).
...@@ -121,7 +121,7 @@ module Banzai ...@@ -121,7 +121,7 @@ module Banzai
def autolink_filter(text) def autolink_filter(text)
Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_PATTERN) do |link, left:, right:| Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_PATTERN) do |link, left:, right:|
autolink_match(link) autolink_match(link).html_safe
end end
end end
......
...@@ -77,7 +77,7 @@ module Banzai ...@@ -77,7 +77,7 @@ module Banzai
def spaced_link_filter(text) def spaced_link_filter(text)
Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_OR_IMAGE_PATTERN) do |link, left:, right:| Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_OR_IMAGE_PATTERN) do |link, left:, right:|
spaced_link_match(link) spaced_link_match(link).html_safe
end end
end end
......
...@@ -62,7 +62,7 @@ module Gitlab ...@@ -62,7 +62,7 @@ module Gitlab
end end
def link_tag(name, url) def link_tag(name, url)
%{<a href="#{ERB::Util.html_escape_once(url)}" rel="nofollow noreferrer noopener" target="_blank">#{ERB::Util.html_escape_once(name)}</a>} %{<a href="#{ERB::Util.html_escape_once(url)}" rel="nofollow noreferrer noopener" target="_blank">#{ERB::Util.html_escape_once(name)}</a>}.html_safe
end end
# Links package names based on regex. # Links package names based on regex.
......
...@@ -9,7 +9,7 @@ module Gitlab ...@@ -9,7 +9,7 @@ module Gitlab
def mark(line_inline_diffs, mode: nil) def mark(line_inline_diffs, mode: nil)
super(line_inline_diffs) do |text, left:, right:| super(line_inline_diffs) do |text, left:, right:|
%{<span class="#{html_class_names(left, right, mode)}">#{text}</span>} %{<span class="#{html_class_names(left, right, mode)}">#{text}</span>}.html_safe
end end
end end
......
...@@ -373,6 +373,14 @@ FactoryBot.define do ...@@ -373,6 +373,14 @@ FactoryBot.define do
end end
end end
trait :license_management do
options do
{
artifacts: { reports: { license_management: 'gl-license-management-report.json' } }
}
end
end
trait :non_playable do trait :non_playable do
status { 'created' } status { 'created' }
self.when { 'manual' } self.when { 'manual' }
......
import Vue from 'vue'; import { trimText } from 'helpers/text_helper';
import { mount } from '@vue/test-utils';
import JobItem from '~/pipelines/components/graph/job_item.vue'; import JobItem from '~/pipelines/components/graph/job_item.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('pipeline graph job item', () => { describe('pipeline graph job item', () => {
const JobComponent = Vue.extend(JobItem); let wrapper;
let component;
const createWrapper = propsData => {
wrapper = mount(JobItem, { sync: false, attachToDocument: true, propsData });
};
const delayedJobFixture = getJSONFixture('jobs/delayed.json'); const delayedJobFixture = getJSONFixture('jobs/delayed.json');
const mockJob = { const mockJob = {
...@@ -28,27 +31,25 @@ describe('pipeline graph job item', () => { ...@@ -28,27 +31,25 @@ describe('pipeline graph job item', () => {
}; };
afterEach(() => { afterEach(() => {
component.$destroy(); wrapper.destroy();
}); });
describe('name with link', () => { describe('name with link', () => {
it('should render the job name and status with a link', done => { it('should render the job name and status with a link', done => {
component = mountComponent(JobComponent, { job: mockJob }); createWrapper({ job: mockJob });
Vue.nextTick(() => { wrapper.vm.$nextTick(() => {
const link = component.$el.querySelector('a'); const link = wrapper.find('a');
expect(link.getAttribute('href')).toEqual(mockJob.status.details_path); expect(link.attributes('href')).toBe(mockJob.status.details_path);
expect(link.getAttribute('data-original-title')).toEqual( expect(link.attributes('data-original-title')).toEqual(
`${mockJob.name} - ${mockJob.status.label}`, `${mockJob.name} - ${mockJob.status.label}`,
); );
expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); expect(wrapper.find('.js-status-icon-success')).toBeDefined();
expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual( expect(trimText(wrapper.find('.ci-status-text').text())).toBe(mockJob.name);
mockJob.name,
);
done(); done();
}); });
...@@ -57,7 +58,7 @@ describe('pipeline graph job item', () => { ...@@ -57,7 +58,7 @@ describe('pipeline graph job item', () => {
describe('name without link', () => { describe('name without link', () => {
it('it should render status and name', () => { it('it should render status and name', () => {
component = mountComponent(JobComponent, { createWrapper({
job: { job: {
id: 4257, id: 4257,
name: 'test', name: 'test',
...@@ -72,36 +73,34 @@ describe('pipeline graph job item', () => { ...@@ -72,36 +73,34 @@ describe('pipeline graph job item', () => {
}, },
}); });
expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); expect(wrapper.find('.js-status-icon-success')).toBeDefined();
expect(component.$el.querySelector('a')).toBeNull(); expect(wrapper.find('a').exists()).toBe(false);
expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual( expect(trimText(wrapper.find('.ci-status-text').text())).toEqual(mockJob.name);
mockJob.name,
);
}); });
}); });
describe('action icon', () => { describe('action icon', () => {
it('it should render the action icon', () => { it('it should render the action icon', () => {
component = mountComponent(JobComponent, { job: mockJob }); createWrapper({ job: mockJob });
expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined(); expect(wrapper.find('a.ci-action-icon-container')).toBeDefined();
expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined(); expect(wrapper.find('i.ci-action-icon-wrapper')).toBeDefined();
}); });
}); });
it('should render provided class name', () => { it('should render provided class name', () => {
component = mountComponent(JobComponent, { createWrapper({
job: mockJob, job: mockJob,
cssClassJobName: 'css-class-job-name', cssClassJobName: 'css-class-job-name',
}); });
expect(component.$el.querySelector('a').classList.contains('css-class-job-name')).toBe(true); expect(wrapper.find('a').classes()).toContain('css-class-job-name');
}); });
describe('status label', () => { describe('status label', () => {
it('should not render status label when it is not provided', () => { it('should not render status label when it is not provided', () => {
component = mountComponent(JobComponent, { createWrapper({
job: { job: {
id: 4258, id: 4258,
name: 'test', name: 'test',
...@@ -111,15 +110,13 @@ describe('pipeline graph job item', () => { ...@@ -111,15 +110,13 @@ describe('pipeline graph job item', () => {
}, },
}); });
expect( expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toBe(
component.$el 'test',
.querySelector('.js-job-component-tooltip') );
.getAttribute('data-original-title'),
).toEqual('test');
}); });
it('should not render status label when it is provided', () => { it('should not render status label when it is provided', () => {
component = mountComponent(JobComponent, { createWrapper({
job: { job: {
id: 4259, id: 4259,
name: 'test', name: 'test',
...@@ -131,25 +128,21 @@ describe('pipeline graph job item', () => { ...@@ -131,25 +128,21 @@ describe('pipeline graph job item', () => {
}, },
}); });
expect( expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toEqual(
component.$el 'test - success',
.querySelector('.js-job-component-tooltip') );
.getAttribute('data-original-title'),
).toEqual('test - success');
}); });
}); });
describe('for delayed job', () => { describe('for delayed job', () => {
it('displays remaining time in tooltip', () => { it('displays remaining time in tooltip', () => {
component = mountComponent(JobComponent, { createWrapper({
job: delayedJobFixture, job: delayedJobFixture,
}); });
expect( expect(wrapper.find('.js-pipeline-graph-job-link').attributes('data-original-title')).toEqual(
component.$el `delayed job - delayed manual action (${wrapper.vm.remainingTime})`,
.querySelector('.js-pipeline-graph-job-link') );
.getAttribute('data-original-title'),
).toEqual(`delayed job - delayed manual action (${component.remainingTime})`);
}); });
}); });
}); });
import Vue from 'vue'; import { mount } from '@vue/test-utils';
import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue'; import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mockData from './linked_pipelines_mock_data'; import mockData from './linked_pipelines_mock_data';
const mockPipeline = mockData.triggered[0]; const mockPipeline = mockData.triggered[0];
describe('Linked pipeline', () => { describe('Linked pipeline', () => {
const Component = Vue.extend(LinkedPipelineComponent); let wrapper;
let vm;
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
}); });
describe('rendered output', () => { describe('rendered output', () => {
...@@ -19,57 +18,61 @@ describe('Linked pipeline', () => { ...@@ -19,57 +18,61 @@ describe('Linked pipeline', () => {
}; };
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Component, props); wrapper = mount(LinkedPipelineComponent, {
sync: false,
attachToDocument: true,
propsData: props,
});
}); });
it('should render a list item as the containing element', () => { it('should render a list item as the containing element', () => {
expect(vm.$el.tagName).toBe('LI'); expect(wrapper.is('li')).toBe(true);
}); });
it('should render a button', () => { it('should render a button', () => {
const linkElement = vm.$el.querySelector('.js-linked-pipeline-content'); const linkElement = wrapper.find('.js-linked-pipeline-content');
expect(linkElement).not.toBeNull(); expect(linkElement.exists()).toBe(true);
}); });
it('should render the project name', () => { it('should render the project name', () => {
expect(vm.$el.innerText).toContain(props.pipeline.project.name); expect(wrapper.text()).toContain(props.pipeline.project.name);
}); });
it('should render an svg within the status container', () => { it('should render an svg within the status container', () => {
const pipelineStatusElement = vm.$el.querySelector('.js-linked-pipeline-status'); const pipelineStatusElement = wrapper.find('.js-linked-pipeline-status');
expect(pipelineStatusElement.querySelector('svg')).not.toBeNull(); expect(pipelineStatusElement.find('svg').exists()).toBe(true);
}); });
it('should render the pipeline status icon svg', () => { it('should render the pipeline status icon svg', () => {
expect(vm.$el.querySelector('.js-ci-status-icon-running')).not.toBeNull(); expect(wrapper.find('.js-ci-status-icon-running').exists()).toBe(true);
expect(vm.$el.querySelector('.js-ci-status-icon-running').innerHTML).toContain('<svg'); expect(wrapper.find('.js-ci-status-icon-running').html()).toContain('<svg');
}); });
it('should have a ci-status child component', () => { it('should have a ci-status child component', () => {
expect(vm.$el.querySelector('.js-linked-pipeline-status')).not.toBeNull(); expect(wrapper.find('.js-linked-pipeline-status').exists()).toBe(true);
}); });
it('should render the pipeline id', () => { it('should render the pipeline id', () => {
expect(vm.$el.innerText).toContain(`#${props.pipeline.id}`); expect(wrapper.text()).toContain(`#${props.pipeline.id}`);
}); });
it('should correctly compute the tooltip text', () => { it('should correctly compute the tooltip text', () => {
expect(vm.tooltipText).toContain(mockPipeline.project.name); expect(wrapper.vm.tooltipText).toContain(mockPipeline.project.name);
expect(vm.tooltipText).toContain(mockPipeline.details.status.label); expect(wrapper.vm.tooltipText).toContain(mockPipeline.details.status.label);
}); });
it('should render the tooltip text as the title attribute', () => { it('should render the tooltip text as the title attribute', () => {
const tooltipRef = vm.$el.querySelector('.js-linked-pipeline-content'); const tooltipRef = wrapper.find('.js-linked-pipeline-content');
const titleAttr = tooltipRef.getAttribute('data-original-title'); const titleAttr = tooltipRef.attributes('data-original-title');
expect(titleAttr).toContain(mockPipeline.project.name); expect(titleAttr).toContain(mockPipeline.project.name);
expect(titleAttr).toContain(mockPipeline.details.status.label); expect(titleAttr).toContain(mockPipeline.details.status.label);
}); });
it('does not render the loading icon when isLoading is false', () => { it('does not render the loading icon when isLoading is false', () => {
expect(vm.$el.querySelector('.js-linked-pipeline-loading')).toBeNull(); expect(wrapper.find('.js-linked-pipeline-loading').exists()).toBe(false);
}); });
}); });
...@@ -79,11 +82,15 @@ describe('Linked pipeline', () => { ...@@ -79,11 +82,15 @@ describe('Linked pipeline', () => {
}; };
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Component, props); wrapper = mount(LinkedPipelineComponent, {
sync: false,
attachToDocument: true,
propsData: props,
});
}); });
it('renders a loading icon', () => { it('renders a loading icon', () => {
expect(vm.$el.querySelector('.js-linked-pipeline-loading')).not.toBeNull(); expect(wrapper.find('.js-linked-pipeline-loading').exists()).toBe(true);
}); });
}); });
...@@ -93,21 +100,25 @@ describe('Linked pipeline', () => { ...@@ -93,21 +100,25 @@ describe('Linked pipeline', () => {
}; };
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Component, props); wrapper = mount(LinkedPipelineComponent, {
sync: false,
attachToDocument: true,
propsData: props,
});
}); });
it('emits `pipelineClicked` event', () => { it('emits `pipelineClicked` event', () => {
spyOn(vm, '$emit'); jest.spyOn(wrapper.vm, '$emit');
vm.$el.querySelector('button').click(); wrapper.find('button').trigger('click');
expect(vm.$emit).toHaveBeenCalledWith('pipelineClicked'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('pipelineClicked');
}); });
it('should emit `bv::hide::tooltip` to close the tooltip', () => { it('should emit `bv::hide::tooltip` to close the tooltip', () => {
spyOn(vm.$root, '$emit'); jest.spyOn(wrapper.vm.$root, '$emit');
vm.$el.querySelector('button').click(); wrapper.find('button').trigger('click');
expect(vm.$root.$emit.calls.argsFor(0)).toEqual([ expect(wrapper.vm.$root.$emit.mock.calls[0]).toEqual([
'bv::hide::tooltip', 'bv::hide::tooltip',
'js-linked-pipeline-132', 'js-linked-pipeline-132',
]); ]);
......
This diff is collapsed.
import $ from 'jquery';
import { trimText } from 'helpers/text_helper';
import { shallowMount } from '@vue/test-utils';
import PipelineUrlComponent from '~/pipelines/components/pipeline_url.vue';
$.fn.popover = () => {};
describe('Pipeline Url Component', () => {
let wrapper;
const createComponent = props => {
wrapper = shallowMount(PipelineUrlComponent, {
sync: false,
attachToDocument: true,
propsData: props,
});
};
afterEach(() => {
wrapper.destroy();
});
it('should render a table cell', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.attributes('class')).toContain('table-section');
});
it('should render a link the provided path and id', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-link').attributes('href')).toBe('foo');
expect(wrapper.find('.js-pipeline-url-link span').text()).toBe('#1');
});
it('should render latest, yaml invalid, merge request, and stuck flags when provided', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
merge_request_pipeline: true,
detached_merge_request_pipeline: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-latest').text()).toContain('latest');
expect(wrapper.find('.js-pipeline-url-yaml').text()).toContain('yaml invalid');
expect(wrapper.find('.js-pipeline-url-stuck').text()).toContain('stuck');
expect(wrapper.find('.js-pipeline-url-detached').text()).toContain('detached');
});
it('should render a badge for autodevops', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
auto_devops: true,
},
},
autoDevopsHelpPath: 'foo',
});
expect(trimText(wrapper.find('.js-pipeline-url-autodevops').text())).toEqual('Auto DevOps');
});
it('should render error badge when pipeline has a failure reason set', () => {
createComponent({
pipeline: {
id: 1,
path: 'foo',
flags: {
failure_reason: true,
},
failure_reason: 'some reason',
},
autoDevopsHelpPath: 'foo',
});
expect(wrapper.find('.js-pipeline-url-failure').text()).toContain('error');
expect(wrapper.find('.js-pipeline-url-failure').attributes('data-original-title')).toContain(
'some reason',
);
});
});
import Vue from 'vue';
import pipelineUrlComp from '~/pipelines/components/pipeline_url.vue';
describe('Pipeline Url Component', () => {
let PipelineUrlComponent;
beforeEach(() => {
PipelineUrlComponent = Vue.extend(pipelineUrlComp);
});
it('should render a table cell', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.getAttribute('class')).toContain('table-section');
});
it('should render a link the provided path and id', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-link').getAttribute('href')).toEqual(
'foo',
);
expect(component.$el.querySelector('.js-pipeline-url-link span').textContent).toEqual('#1');
});
it('should render latest, yaml invalid, merge request, and stuck flags when provided', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
merge_request_pipeline: true,
detached_merge_request_pipeline: true,
},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-latest').textContent).toContain('latest');
expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain(
'yaml invalid',
);
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
expect(component.$el.querySelector('.js-pipeline-url-detached').textContent).toContain(
'detached',
);
});
it('should render a badge for autodevops', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
auto_devops: true,
},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-autodevops').textContent.trim()).toEqual(
'Auto DevOps',
);
});
it('should render error badge when pipeline has a failure reason set', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {
failure_reason: true,
},
failure_reason: 'some reason',
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.querySelector('.js-pipeline-url-failure').textContent).toContain('error');
expect(
component.$el.querySelector('.js-pipeline-url-failure').getAttribute('data-original-title'),
).toContain('some reason');
});
});
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Pages do describe Gitlab::Pages do
let(:pages_shared_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) } let(:pages_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do before do
allow(described_class).to receive(:secret).and_return(pages_shared_secret) allow(described_class).to receive(:secret).and_return(pages_secret)
end end
describe '.verify_api_request' do describe '.verify_api_request' do
......
...@@ -9,7 +9,7 @@ describe Gitlab::StringRangeMarker do ...@@ -9,7 +9,7 @@ describe Gitlab::StringRangeMarker do
inline_diffs = [2..5] inline_diffs = [2..5]
described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:| described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:|
"LEFT#{text}RIGHT" "LEFT#{text}RIGHT".html_safe
end end
end end
......
...@@ -10,7 +10,7 @@ describe Gitlab::StringRegexMarker do ...@@ -10,7 +10,7 @@ describe Gitlab::StringRegexMarker do
subject do subject do
described_class.new(raw, rich).mark(/"[^"]+":\s*"(?<name>[^"]+)"/, group: :name) do |text, left:, right:| described_class.new(raw, rich).mark(/"[^"]+":\s*"(?<name>[^"]+)"/, group: :name) do |text, left:, right:|
%{<a href="#">#{text}</a>} %{<a href="#">#{text}</a>}.html_safe
end end
end end
...@@ -26,7 +26,7 @@ describe Gitlab::StringRegexMarker do ...@@ -26,7 +26,7 @@ describe Gitlab::StringRegexMarker do
subject do subject do
described_class.new(raw, rich).mark(/<[a-z]>/) do |text, left:, right:| described_class.new(raw, rich).mark(/<[a-z]>/) do |text, left:, right:|
%{<strong>#{text}</strong>} %{<strong>#{text}</strong>}.html_safe
end end
end end
......
...@@ -4,10 +4,10 @@ require 'spec_helper' ...@@ -4,10 +4,10 @@ require 'spec_helper'
describe API::Internal::Pages do describe API::Internal::Pages do
describe "GET /internal/pages" do describe "GET /internal/pages" do
let(:pages_shared_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) } let(:pages_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do before do
allow(Gitlab::Pages).to receive(:secret).and_return(pages_shared_secret) allow(Gitlab::Pages).to receive(:secret).and_return(pages_secret)
end end
def query_host(host, headers = {}) def query_host(host, headers = {})
......
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