Commit 6982ab91 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 386abf71 4ee3f688
...@@ -4,6 +4,7 @@ import initUserPopovers from '../../user_popovers'; ...@@ -4,6 +4,7 @@ import initUserPopovers from '../../user_popovers';
import highlightCurrentUser from './highlight_current_user'; import highlightCurrentUser from './highlight_current_user';
import renderMath from './render_math'; import renderMath from './render_math';
import renderMermaid from './render_mermaid'; import renderMermaid from './render_mermaid';
import renderSandboxedMermaid from './render_sandboxed_mermaid';
import renderMetrics from './render_metrics'; import renderMetrics from './render_metrics';
// Render GitLab flavoured Markdown // Render GitLab flavoured Markdown
...@@ -13,7 +14,11 @@ import renderMetrics from './render_metrics'; ...@@ -13,7 +14,11 @@ import renderMetrics from './render_metrics';
$.fn.renderGFM = function renderGFM() { $.fn.renderGFM = function renderGFM() {
syntaxHighlight(this.find('.js-syntax-highlight').get()); syntaxHighlight(this.find('.js-syntax-highlight').get());
renderMath(this.find('.js-render-math')); renderMath(this.find('.js-render-math'));
if (gon.features?.sandboxedMermaid) {
renderSandboxedMermaid(this.find('.js-render-mermaid'));
} else {
renderMermaid(this.find('.js-render-mermaid')); renderMermaid(this.find('.js-render-mermaid'));
}
highlightCurrentUser(this.find('.gfm-project_member').get()); highlightCurrentUser(this.find('.gfm-project_member').get());
initUserPopovers(this.find('.js-user-link').get()); initUserPopovers(this.find('.js-user-link').get());
......
import $ from 'jquery';
import { once, countBy } from 'lodash';
import { __ } from '~/locale';
import {
getBaseURL,
relativePathToAbsolute,
setUrlParams,
joinPaths,
} from '~/lib/utils/url_utility';
import { darkModeEnabled } from '~/lib/utils/color_utils';
import { setAttributes } from '~/lib/utils/dom_utils';
// Renders diagrams and flowcharts from text using Mermaid in any element with the
// `js-render-mermaid` class.
//
// Example markup:
//
// <pre class="js-render-mermaid">
// graph TD;
// A-- > B;
// A-- > C;
// B-- > D;
// C-- > D;
// </pre>
//
const SANDBOX_FRAME_PATH = '/-/sandbox/mermaid';
// This is an arbitrary number; Can be iterated upon when suitable.
const MAX_CHAR_LIMIT = 2000;
// Max # of mermaid blocks that can be rendered in a page.
const MAX_MERMAID_BLOCK_LIMIT = 50;
// Max # of `&` allowed in Chaining of links syntax
const MAX_CHAINING_OF_LINKS_LIMIT = 30;
// Keep a map of mermaid blocks we've already rendered.
const elsProcessingMap = new WeakMap();
let renderedMermaidBlocks = 0;
// Pages without any restrictions on mermaid rendering
const PAGES_WITHOUT_RESTRICTIONS = [
// Group wiki
'groups:wikis:show',
'groups:wikis:edit',
'groups:wikis:create',
// Project wiki
'projects:wikis:show',
'projects:wikis:edit',
'projects:wikis:create',
// Project files
'projects:show',
'projects:blob:show',
];
function shouldLazyLoadMermaidBlock(source) {
/**
* If source contains `&`, which means that it might
* contain Chaining of links a new syntax in Mermaid.
*/
if (countBy(source)['&'] > MAX_CHAINING_OF_LINKS_LIMIT) {
return true;
}
return false;
}
function fixElementSource(el) {
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
const source = el.textContent?.replace(/<br\s*\/>/g, '<br>');
// Remove any extra spans added by the backend syntax highlighting.
Object.assign(el, { textContent: source });
return { source };
}
function getSandboxFrameSrc() {
const path = joinPaths(gon.relative_url_root || '', SANDBOX_FRAME_PATH);
if (!darkModeEnabled()) {
return path;
}
const absoluteUrl = relativePathToAbsolute(path, getBaseURL());
return setUrlParams({ darkMode: darkModeEnabled() }, absoluteUrl);
}
function renderMermaidEl(el, source) {
const iframeEl = document.createElement('iframe');
setAttributes(iframeEl, {
src: getSandboxFrameSrc(),
sandbox: 'allow-scripts',
frameBorder: 0,
scrolling: 'no',
});
// Add the original source into the DOM
// to allow Copy-as-GFM to access it.
const sourceEl = document.createElement('text');
sourceEl.textContent = source;
sourceEl.classList.add('gl-display-none');
const wrapper = document.createElement('div');
wrapper.appendChild(iframeEl);
wrapper.appendChild(sourceEl);
el.closest('pre').replaceWith(wrapper);
// Event Listeners
iframeEl.addEventListener('load', () => {
// Potential risk associated with '*' discussed in below thread
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74414#note_735183398
iframeEl.contentWindow.postMessage(source, '*');
});
window.addEventListener(
'message',
(event) => {
if (event.origin !== 'null' || event.source !== iframeEl.contentWindow) {
return;
}
const { h, w } = event.data;
iframeEl.width = w;
iframeEl.height = h;
},
false,
);
}
function renderMermaids($els) {
if (!$els.length) return;
const pageName = document.querySelector('body').dataset.page;
// A diagram may have been truncated in search results which will cause errors, so abort the render.
if (pageName === 'search:show') return;
let renderedChars = 0;
$els.each((i, el) => {
// Skipping all the elements which we've already queued in requestIdleCallback
if (elsProcessingMap.has(el)) {
return;
}
const { source } = fixElementSource(el);
/**
* Restrict the rendering to a certain amount of character
* and mermaid blocks to prevent mermaidjs from hanging
* up the entire thread and causing a DoS.
*/
if (
!PAGES_WITHOUT_RESTRICTIONS.includes(pageName) &&
((source && source.length > MAX_CHAR_LIMIT) ||
renderedChars > MAX_CHAR_LIMIT ||
renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT ||
shouldLazyLoadMermaidBlock(source))
) {
const html = `
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
<div>
<div>
<div class="js-warning-text"></div>
<div class="gl-alert-actions">
<button type="button" class="js-lazy-render-mermaid btn gl-alert-action btn-warning btn-md gl-button">Display</button>
</div>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
`;
const $parent = $(el).parent();
if (!$parent.hasClass('lazy-alert-shown')) {
$parent.after(html);
$parent
.siblings()
.find('.js-warning-text')
.text(
__('Warning: Displaying this diagram might cause performance issues on this page.'),
);
$parent.addClass('lazy-alert-shown');
}
return;
}
renderedChars += source.length;
renderedMermaidBlocks += 1;
const requestId = window.requestIdleCallback(() => {
renderMermaidEl(el, source);
});
elsProcessingMap.set(el, requestId);
});
}
const hookLazyRenderMermaidEvent = once(() => {
$(document.body).on('click', '.js-lazy-render-mermaid', function eventHandler() {
const parent = $(this).closest('.js-lazy-render-mermaid-container');
const pre = parent.prev();
const el = pre.find('.js-render-mermaid');
parent.remove();
// sandbox update
const element = el.get(0);
const { source } = fixElementSource(element);
renderMermaidEl(element, source);
});
});
export default function renderMermaid($els) {
if (!$els.length) return;
const visibleMermaids = $els.filter(function filter() {
return $(this).closest('details').length === 0 && $(this).is(':visible');
});
renderMermaids(visibleMermaids);
$els.closest('details').one('toggle', function toggle() {
if (this.open) {
renderMermaids($(this).find('.js-render-mermaid'));
}
});
hookLazyRenderMermaidEvent();
}
import mermaid from 'mermaid';
import { getParameterByName } from '~/lib/utils/url_utility';
const setIframeRenderedSize = (h, w) => {
const { origin } = window.location;
window.parent.postMessage({ h, w }, origin);
};
const drawDiagram = (source) => {
const element = document.getElementById('app');
const insertSvg = (svgCode) => {
element.innerHTML = svgCode;
const height = parseInt(element.firstElementChild.getAttribute('height'), 10);
const width = parseInt(element.firstElementChild.style.maxWidth, 10);
setIframeRenderedSize(height, width);
};
mermaid.mermaidAPI.render('mermaid', source, insertSvg);
};
const darkModeEnabled = () => getParameterByName('darkMode') === 'true';
const initMermaid = () => {
let theme = 'neutral';
if (darkModeEnabled()) {
theme = 'dark';
}
mermaid.initialize({
// mermaid core options
mermaid: {
startOnLoad: false,
},
// mermaidAPI options
theme,
flowchart: {
useMaxWidth: true,
htmlLabels: true,
},
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'htmlLabels'],
securityLevel: 'strict',
});
};
const addListener = () => {
window.addEventListener(
'message',
(event) => {
if (event.origin !== window.location.origin) {
return;
}
drawDiagram(event.data);
},
false,
);
};
addListener();
initMermaid();
export default {};
...@@ -212,7 +212,9 @@ export default { ...@@ -212,7 +212,9 @@ export default {
</script> </script>
<template> <template>
<div class="js-pipeline-header-container"> <div class="js-pipeline-header-container">
<gl-alert v-if="hasError" :variant="failure.variant">{{ failure.text }}</gl-alert> <gl-alert v-if="hasError" :variant="failure.variant" :dismissible="false">{{
failure.text
}}</gl-alert>
<ci-header <ci-header
v-if="shouldRenderContent" v-if="shouldRenderContent"
:status="pipeline.detailedStatus" :status="pipeline.detailedStatus"
......
<script> <script>
import { GlIcon, GlPopover, GlTooltipDirective } from '@gitlab/ui'; import { GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale'; import { __, n__, sprintf } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
...@@ -10,6 +10,7 @@ import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscript ...@@ -10,6 +10,7 @@ import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscript
export default { export default {
components: { components: {
GlIcon, GlIcon,
GlLink,
GlPopover, GlPopover,
}, },
directives: { directives: {
...@@ -85,9 +86,6 @@ export default { ...@@ -85,9 +86,6 @@ export default {
); );
}, },
}, },
i18n: {
help: __('Work in progress- click here to find out more'),
},
}; };
</script> </script>
...@@ -97,11 +95,10 @@ export default { ...@@ -97,11 +95,10 @@ export default {
<gl-icon name="users" /> <gl-icon name="users" />
<span> {{ contactCount }} </span> <span> {{ contactCount }} </span>
</div> </div>
<div <div class="hide-collapsed help-button gl-float-right">
v-gl-tooltip.left.viewport="$options.i18n.help" <gl-link href="https://docs.gitlab.com/ee/user/crm/" target="_blank"
class="hide-collapsed help-button float-right" ><gl-icon name="question-o"
> /></gl-link>
<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/2256"><gl-icon name="question-o" /></a>
</div> </div>
<div class="title hide-collapsed gl-mb-2 gl-line-height-20"> <div class="title hide-collapsed gl-mb-2 gl-line-height-20">
{{ contactsLabel }} {{ contactsLabel }}
......
# frozen_string_literal: true
class SandboxController < ApplicationController # rubocop:disable Gitlab/NamespacedClass
skip_before_action :authenticate_user!
feature_category :not_owned
def mermaid
render layout: false
end
end
...@@ -24,7 +24,7 @@ module ResolvesPipelines ...@@ -24,7 +24,7 @@ module ResolvesPipelines
argument :source, argument :source,
GraphQL::Types::String, GraphQL::Types::String,
required: false, required: false,
description: "Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled." description: "Filter pipelines by their source."
end end
class_methods do class_methods do
...@@ -38,8 +38,6 @@ module ResolvesPipelines ...@@ -38,8 +38,6 @@ module ResolvesPipelines
end end
def resolve_pipelines(project, params = {}) def resolve_pipelines(project, params = {})
params.delete(:source) unless Feature.enabled?(:dast_view_scans, project, default_enabled: :yaml)
Ci::PipelinesFinder.new(project, context[:current_user], params).execute Ci::PipelinesFinder.new(project, context[:current_user], params).execute
end end
end end
<!DOCTYPE html>
<html>
<head>
<%= webpack_bundle_tag("sandboxed_mermaid") %>
</head>
<body>
<div id="app"></div>
</body>
</html>
--- ---
name: dast_view_scans name: sandboxed_mermaid
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69571 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74414
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340388 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349755
milestone: '14.3' milestone: '14.7'
type: development type: development
group: group::dynamic analysis group: group::analyzer frontend
default_enabled: true default_enabled: false
...@@ -8,7 +8,7 @@ product_stage: create ...@@ -8,7 +8,7 @@ product_stage: create
product_group: group::code review product_group: group::code review
product_category: code_review product_category: code_review
value_type: number value_type: number
status: active status: removed
time_frame: 28d time_frame: 28d
data_source: database data_source: database
distribution: distribution:
...@@ -20,3 +20,4 @@ tier: ...@@ -20,3 +20,4 @@ tier:
- ultimate - ultimate
performance_indicator_type: [] performance_indicator_type: []
milestone: "<13.9" milestone: "<13.9"
milestone_removed: '14.7'
...@@ -7,7 +7,7 @@ product_stage: create ...@@ -7,7 +7,7 @@ product_stage: create
product_group: group::code review product_group: group::code review
product_category: code_review product_category: code_review
value_type: number value_type: number
status: active status: removed
time_frame: all time_frame: all
data_source: database data_source: database
distribution: distribution:
...@@ -18,3 +18,4 @@ tier: ...@@ -18,3 +18,4 @@ tier:
- premium - premium
- ultimate - ultimate
milestone: "<13.9" milestone: "<13.9"
milestone_removed: '14.7'
...@@ -108,6 +108,9 @@ Rails.application.routes.draw do ...@@ -108,6 +108,9 @@ Rails.application.routes.draw do
get '/autocomplete/namespace_routes' => 'autocomplete#namespace_routes' get '/autocomplete/namespace_routes' => 'autocomplete#namespace_routes'
end end
# sandbox
get '/sandbox/mermaid' => 'sandbox#mermaid'
get '/whats_new' => 'whats_new#index' get '/whats_new' => 'whats_new#index'
# '/-/health' implemented by BasicHealthCheck middleware # '/-/health' implemented by BasicHealthCheck middleware
......
...@@ -141,6 +141,7 @@ function generateEntries() { ...@@ -141,6 +141,7 @@ function generateEntries() {
sentry: './sentry/index.js', sentry: './sentry/index.js',
performance_bar: './performance_bar/index.js', performance_bar: './performance_bar/index.js',
jira_connect_app: './jira_connect/subscriptions/index.js', jira_connect_app: './jira_connect/subscriptions/index.js',
sandboxed_mermaid: './lib/mermaid.js',
}; };
return Object.assign(manualEntries, incrementalCompiler.filterEntryPoints(autoEntries)); return Object.assign(manualEntries, incrementalCompiler.filterEntryPoints(autoEntries));
......
...@@ -9158,7 +9158,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -9158,7 +9158,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="commitpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. | | <a id="commitpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="commitpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. | | <a id="commitpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="commitpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. | | <a id="commitpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="commitpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. | | <a id="commitpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="commitpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. | | <a id="commitpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
### `ComplianceFramework` ### `ComplianceFramework`
...@@ -11911,7 +11911,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -11911,7 +11911,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="mergerequestpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. | | <a id="mergerequestpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="mergerequestpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. | | <a id="mergerequestpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="mergerequestpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. | | <a id="mergerequestpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="mergerequestpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. | | <a id="mergerequestpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="mergerequestpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. | | <a id="mergerequestpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
##### `MergeRequest.reference` ##### `MergeRequest.reference`
...@@ -12945,7 +12945,7 @@ Represents a file or directory in the project repository that has been locked. ...@@ -12945,7 +12945,7 @@ Represents a file or directory in the project repository that has been locked.
| <a id="pipelineconfigsource"></a>`configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). | | <a id="pipelineconfigsource"></a>`configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). |
| <a id="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. | | <a id="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. |
| <a id="pipelinecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. | | <a id="pipelinecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. |
| <a id="pipelinedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. Returns `null`if `dast_view_scans` feature flag is disabled. | | <a id="pipelinedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. |
| <a id="pipelinedetailedstatus"></a>`detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. | | <a id="pipelinedetailedstatus"></a>`detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. |
| <a id="pipelinedownstream"></a>`downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) | | <a id="pipelinedownstream"></a>`downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) |
| <a id="pipelineduration"></a>`duration` | [`Int`](#int) | Duration of the pipeline in seconds. | | <a id="pipelineduration"></a>`duration` | [`Int`](#int) | Duration of the pipeline in seconds. |
...@@ -13377,7 +13377,7 @@ Returns [`DastProfile`](#dastprofile). ...@@ -13377,7 +13377,7 @@ Returns [`DastProfile`](#dastprofile).
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="projectdastprofilehasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled. | | <a id="projectdastprofilehasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. |
| <a id="projectdastprofileid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the DAST Profile. | | <a id="projectdastprofileid"></a>`id` | [`DastProfileID!`](#dastprofileid) | ID of the DAST Profile. |
##### `Project.dastProfiles` ##### `Project.dastProfiles`
...@@ -13394,7 +13394,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -13394,7 +13394,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="projectdastprofileshasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled. | | <a id="projectdastprofileshasdastprofileschedule"></a>`hasDastProfileSchedule` | [`Boolean`](#boolean) | Filter DAST Profiles by whether or not they have a schedule. |
##### `Project.dastSiteProfile` ##### `Project.dastSiteProfile`
...@@ -13854,7 +13854,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -13854,7 +13854,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. | | <a id="projectpipelinesref"></a>`ref` | [`String`](#string) | Filter pipelines by the ref they are run for. |
| <a id="projectpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. | | <a id="projectpipelinesscope"></a>`scope` | [`PipelineScopeEnum`](#pipelinescopeenum) | Filter pipelines by scope. |
| <a id="projectpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. | | <a id="projectpipelinessha"></a>`sha` | [`String`](#string) | Filter pipelines by the sha of the commit they are run for. |
| <a id="projectpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. Will be ignored if `dast_view_scans` feature flag is disabled. | | <a id="projectpipelinessource"></a>`source` | [`String`](#string) | Filter pipelines by their source. |
| <a id="projectpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. | | <a id="projectpipelinesstatus"></a>`status` | [`PipelineStatusEnum`](#pipelinestatusenum) | Filter pipelines by their status. |
##### `Project.projectMembers` ##### `Project.projectMembers`
...@@ -11,7 +11,6 @@ module Projects ...@@ -11,7 +11,6 @@ module Projects
feature_category :dynamic_application_security_testing feature_category :dynamic_application_security_testing
def index def index
redirect_to new_project_on_demand_scan_path(project) unless Feature.enabled?(:dast_view_scans, @project, default_enabled: :yaml)
end end
def new def new
......
...@@ -28,15 +28,14 @@ module EE ...@@ -28,15 +28,14 @@ module EE
field :dast_profile, field :dast_profile,
::Types::Dast::ProfileType, ::Types::Dast::ProfileType,
null: true, null: true,
description: 'DAST profile associated with the pipeline. Returns `null`' \ description: 'DAST profile associated with the pipeline.'
'if `dast_view_scans` feature flag is disabled.'
def code_quality_reports def code_quality_reports
pipeline.codequality_reports.sort_degradations!.values.presence pipeline.codequality_reports.sort_degradations!.values.presence
end end
def dast_profile def dast_profile
pipeline.dast_profile if ::Feature.enabled?(:dast_view_scans, pipeline.project, default_enabled: :yaml) pipeline.dast_profile
end end
end end
end end
......
...@@ -12,7 +12,7 @@ module Resolvers ...@@ -12,7 +12,7 @@ module Resolvers
argument :has_dast_profile_schedule, ::GraphQL::Types::Boolean, argument :has_dast_profile_schedule, ::GraphQL::Types::Boolean,
required: false, required: false,
description: 'Filter DAST Profiles by whether or not they have a schedule. Will be ignored if `dast_view_scans` feature flag is disabled.' description: 'Filter DAST Profiles by whether or not they have a schedule.'
when_single do when_single do
argument :id, ::Types::GlobalIDType[::Dast::Profile], argument :id, ::Types::GlobalIDType[::Dast::Profile],
...@@ -21,7 +21,6 @@ module Resolvers ...@@ -21,7 +21,6 @@ module Resolvers
end end
def resolve_with_lookahead(**args) def resolve_with_lookahead(**args)
args.delete(:has_dast_profile_schedule) unless Feature.enabled?(:dast_view_scans, project, default_enabled: :yaml)
apply_lookahead(find_dast_profiles(args)) apply_lookahead(find_dast_profiles(args))
end end
......
...@@ -91,11 +91,7 @@ module EE ...@@ -91,11 +91,7 @@ module EE
return ::Sidebars::NilMenuItem.new(item_id: :on_demand_scans) return ::Sidebars::NilMenuItem.new(item_id: :on_demand_scans)
end end
link = if ::Feature.enabled?(:dast_view_scans, context.project, default_enabled: :yaml) link = project_on_demand_scans_path(context.project)
project_on_demand_scans_path(context.project)
else
new_project_on_demand_scan_path(context.project)
end
::Sidebars::MenuItem.new( ::Sidebars::MenuItem.new(
title: s_('OnDemandScans|On-demand scans'), title: s_('OnDemandScans|On-demand scans'),
......
...@@ -136,16 +136,8 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do ...@@ -136,16 +136,8 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
it_behaves_like 'with different scan type' do it_behaves_like 'with different scan type' do
let(:expected_configuration) do let(:expected_configuration) do
{ {
'secret-detection-0': { 'secret-detection-0': hash_including(
rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }], rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }],
script:
['if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi',
'if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi',
'git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME',
'git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt',
'export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt',
'/analyzer run',
'rm "$CI_COMMIT_SHA"_commit_list.txt'],
stage: 'test', stage: 'test',
image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION', image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION',
services: [], services: [],
...@@ -160,8 +152,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do ...@@ -160,8 +152,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
SECRETS_ANALYZER_VERSION: '3', SECRETS_ANALYZER_VERSION: '3',
SECRET_DETECTION_EXCLUDED_PATHS: '', SECRET_DETECTION_EXCLUDED_PATHS: '',
SECRET_DETECTION_HISTORIC_SCAN: 'false' SECRET_DETECTION_HISTORIC_SCAN: 'false'
} })
}
} }
end end
end end
......
...@@ -109,25 +109,6 @@ RSpec.describe 'Query.project(fullPath).dastProfiles' do ...@@ -109,25 +109,6 @@ RSpec.describe 'Query.project(fullPath).dastProfiles' do
expect { subject }.not_to exceed_query_limit(control) expect { subject }.not_to exceed_query_limit(control)
end end
context 'when `dast_view_scans` feature flag is disabled' do
before do
stub_feature_flags(dast_view_scans: false)
end
context 'when hasDastProfileSchedule is false' do
let(:query_args) { { hasDastProfileSchedule: false } }
include_examples 'returns all dastProfiles'
end
context 'when hasDastProfileSchedule is true' do
let(:query_args) { { hasDastProfileSchedule: true } }
include_examples 'returns all dastProfiles'
end
end
context 'when `dast_view_scans` feature flag is enabled' do
context 'when hasDastProfileSchedule is true' do context 'when hasDastProfileSchedule is true' do
let(:query_args) { { hasDastProfileSchedule: true } } let(:query_args) { { hasDastProfileSchedule: true } }
...@@ -138,7 +119,6 @@ RSpec.describe 'Query.project(fullPath).dastProfiles' do ...@@ -138,7 +119,6 @@ RSpec.describe 'Query.project(fullPath).dastProfiles' do
end end
end end
end end
end
def pagination_query(arguments) def pagination_query(arguments)
graphql_query_for( graphql_query_for(
......
...@@ -78,16 +78,6 @@ RSpec.describe 'Query.project(fullPath).pipelines.dastProfile' do ...@@ -78,16 +78,6 @@ RSpec.describe 'Query.project(fullPath).pipelines.dastProfile' do
expect { subject }.not_to exceed_query_limit(control) expect { subject }.not_to exceed_query_limit(control)
expect(dast_profile_data.size).to eq(6) expect(dast_profile_data.size).to eq(6)
end end
context 'when feature flag is not enabled' do
it 'does not return dast profile data' do
stub_feature_flags(dast_view_scans: false)
subject
expect(dast_profile_data).to contain_exactly(nil)
end
end
end end
end end
end end
...@@ -73,20 +73,6 @@ RSpec.describe Projects::OnDemandScansController, type: :request do ...@@ -73,20 +73,6 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
it_behaves_like 'on-demand scans page' do it_behaves_like 'on-demand scans page' do
let(:path) { project_on_demand_scans_path(project) } let(:path) { project_on_demand_scans_path(project) }
end end
context 'when dast_view_scans feature flag is disabled' do
before do
stub_licensed_features(security_on_demand_scans: true)
stub_feature_flags(dast_view_scans: false)
project.add_developer(user)
login_as(user)
get project_on_demand_scans_path(project)
end
it 'redirects to new on-demands scans form' do
expect(response).to redirect_to(new_project_on_demand_scan_path(project))
end
end
end end
describe 'GET #new' do describe 'GET #new' do
......
...@@ -29,14 +29,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d ...@@ -29,14 +29,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d
it 'returns prepared CI configuration with Secret Detection scans' do it 'returns prepared CI configuration with Secret Detection scans' do
expected_configuration = { expected_configuration = {
rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }], rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }],
script:
['if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi',
'if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi',
'git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME',
'git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt',
'export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt',
'/analyzer run',
'rm "$CI_COMMIT_SHA"_commit_list.txt'],
stage: 'test', stage: 'test',
image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION', image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION',
services: [], services: [],
...@@ -54,7 +46,7 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d ...@@ -54,7 +46,7 @@ RSpec.describe Security::SecurityOrchestrationPolicies::CiConfigurationService d
} }
} }
expect(subject.deep_symbolize_keys).to eq(expected_configuration) expect(subject.deep_symbolize_keys).to include(expected_configuration)
end end
end end
......
...@@ -210,22 +210,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -210,22 +210,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
expect(rendered).to have_link('Audit events', href: project_audit_events_path(project)) expect(rendered).to have_link('Audit events', href: project_audit_events_path(project))
end end
end end
context 'when dast_view_scans feature flag is disabled' do
before do
allow(view).to receive(:current_user).and_return(user)
stub_feature_flags(dast_view_scans: false)
stub_licensed_features(
security_on_demand_scans: true
)
render
end
it 'links to on-demand scans form instead of index page' do
expect(rendered).to have_link('On-demand scans', href: new_project_on_demand_scan_path(project))
end
end
end end
describe 'Operations' do describe 'Operations' do
......
...@@ -29,8 +29,16 @@ secret_detection: ...@@ -29,8 +29,16 @@ secret_detection:
script: script:
- if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi - if [ -n "$CI_COMMIT_TAG" ]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi
- if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi - if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi
- git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME - |
- git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME
- export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/${CI_DEFAULT_BRANCH}..refs/remotes/origin/${CI_COMMIT_REF_NAME} >${CI_COMMIT_SHA}_commit_list.txt
if [[ $(wc -l <${CI_COMMIT_SHA}_commit_list.txt) -eq "0" ]]; then
# if git log produces 0 or 1 commits we should scan $CI_COMMIT_SHA only
export SECRET_DETECTION_COMMITS=$CI_COMMIT_SHA
else
# +1 because busybox wc only counts \n and there is no trailing \n
echo "scanning $(($(wc -l <${CI_COMMIT_SHA}_commit_list.txt) + 1)) commits"
export SECRET_DETECTION_COMMITS_FILE=${CI_COMMIT_SHA}_commit_list.txt
fi
- /analyzer run - /analyzer run
- rm "$CI_COMMIT_SHA"_commit_list.txt - rm "$CI_COMMIT_SHA"_commit_list.txt
...@@ -147,7 +147,7 @@ module Gitlab ...@@ -147,7 +147,7 @@ module Gitlab
# Using 'self' in the CSP introduces several CSP bypass opportunities # Using 'self' in the CSP introduces several CSP bypass opportunities
# for this reason we list the URLs where GitLab frames itself instead # for this reason we list the URLs where GitLab frames itself instead
def self.allow_framed_gitlab_paths(directives) def self.allow_framed_gitlab_paths(directives)
['/admin/', '/assets/', '/-/speedscope/index.html'].map do |path| ['/admin/', '/assets/', '/-/speedscope/index.html', '/-/sandbox/mermaid'].map do |path|
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path)) append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
end end
end end
......
...@@ -57,6 +57,7 @@ module Gitlab ...@@ -57,6 +57,7 @@ module Gitlab
push_frontend_feature_flag(:improved_emoji_picker, default_enabled: :yaml) push_frontend_feature_flag(:improved_emoji_picker, default_enabled: :yaml)
push_frontend_feature_flag(:new_header_search, default_enabled: :yaml) push_frontend_feature_flag(:new_header_search, default_enabled: :yaml)
push_frontend_feature_flag(:bootstrap_confirmation_modals, default_enabled: :yaml) push_frontend_feature_flag(:bootstrap_confirmation_modals, default_enabled: :yaml)
push_frontend_feature_flag(:sandboxed_mermaid, default_enabled: :yaml)
end end
# Exposes the state of a feature flag to the frontend code. # Exposes the state of a feature flag to the frontend code.
......
...@@ -522,11 +522,7 @@ module Gitlab ...@@ -522,11 +522,7 @@ module Gitlab
projects_with_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: true))), projects_with_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: true))),
projects_without_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: [false, nil]))), projects_without_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: [false, nil]))),
remote_mirrors: distinct_count(::Project.with_remote_mirrors.where(time_period), :creator_id), remote_mirrors: distinct_count(::Project.with_remote_mirrors.where(time_period), :creator_id),
snippets: distinct_count(::Snippet.where(time_period), :author_id), snippets: distinct_count(::Snippet.where(time_period), :author_id)
suggestions: distinct_count(::Note.with_suggestions.where(time_period),
:author_id,
start: minimum_id(::User),
finish: maximum_id(::User))
}.tap do |h| }.tap do |h|
if time_period.present? if time_period.present?
h[:merge_requests_users] = merge_requests_users(time_period) h[:merge_requests_users] = merge_requests_users(time_period)
......
...@@ -40360,9 +40360,6 @@ msgstr "" ...@@ -40360,9 +40360,6 @@ msgstr ""
msgid "Work in progress Limit" msgid "Work in progress Limit"
msgstr "" msgstr ""
msgid "Work in progress- click here to find out more"
msgstr ""
msgid "WorkItem|Work Items" msgid "WorkItem|Work Items"
msgstr "" msgstr ""
......
...@@ -11,6 +11,7 @@ RSpec.describe "User comments on issue", :js do ...@@ -11,6 +11,7 @@ RSpec.describe "User comments on issue", :js do
before do before do
stub_feature_flags(tribute_autocomplete: false) stub_feature_flags(tribute_autocomplete: false)
stub_feature_flags(sandboxed_mermaid: false)
project.add_guest(user) project.add_guest(user)
sign_in(user) sign_in(user)
......
...@@ -5,6 +5,10 @@ require 'spec_helper' ...@@ -5,6 +5,10 @@ require 'spec_helper'
RSpec.describe 'Mermaid rendering', :js do RSpec.describe 'Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
before do
stub_feature_flags(sandboxed_mermaid: false)
end
it 'renders Mermaid diagrams correctly' do it 'renders Mermaid diagrams correctly' do
description = <<~MERMAID description = <<~MERMAID
```mermaid ```mermaid
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Sandboxed Mermaid rendering', :js do
let_it_be(:project) { create(:project, :public) }
before do
stub_feature_flags(sandboxed_mermaid: true)
end
it 'includes mermaid frame correctly' do
description = <<~MERMAID
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
MERMAID
issue = create(:issue, project: project, description: description)
visit project_issue_path(project, issue)
wait_for_requests
expected = %(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts" frameborder="0" scrolling="no")
expect(page.html).to include(expected)
end
end
...@@ -62,17 +62,6 @@ RSpec.describe ResolvesPipelines do ...@@ -62,17 +62,6 @@ RSpec.describe ResolvesPipelines do
context 'filtering by source' do context 'filtering by source' do
let_it_be(:source_pipeline) { create(:ci_pipeline, project: project, source: 'web') } let_it_be(:source_pipeline) { create(:ci_pipeline, project: project, source: 'web') }
context 'when `dast_view_scans` feature flag is disabled' do
before do
stub_feature_flags(dast_view_scans: false)
end
it 'does not filter by source' do
expect(resolve_pipelines(source: 'web')).to contain_exactly(*all_pipelines, source_pipeline)
end
end
context 'when `dast_view_scans` feature flag is enabled' do
it 'does filter by source' do it 'does filter by source' do
expect(resolve_pipelines(source: 'web')).to contain_exactly(source_pipeline) expect(resolve_pipelines(source: 'web')).to contain_exactly(source_pipeline)
end end
...@@ -81,7 +70,6 @@ RSpec.describe ResolvesPipelines do ...@@ -81,7 +70,6 @@ RSpec.describe ResolvesPipelines do
expect(resolve_pipelines).to contain_exactly(*all_pipelines, source_pipeline) expect(resolve_pipelines).to contain_exactly(*all_pipelines, source_pipeline)
end end
end end
end
it 'does not return any pipelines if the user does not have access' do it 'does not return any pipelines if the user does not have access' do
expect(resolve_pipelines({}, {})).to be_empty expect(resolve_pipelines({}, {})).to be_empty
......
...@@ -85,7 +85,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do ...@@ -85,7 +85,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://cdn.example.com") expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://cdn.example.com")
expect(directives['font_src']).to eq("'self' https://cdn.example.com") expect(directives['font_src']).to eq("'self' https://cdn.example.com")
expect(directives['worker_src']).to eq('http://localhost/assets/ blob: data: https://cdn.example.com') expect(directives['worker_src']).to eq('http://localhost/assets/ blob: data: https://cdn.example.com')
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html") expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end end
end end
...@@ -113,7 +113,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do ...@@ -113,7 +113,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end end
it 'does not add CUSTOMER_PORTAL_URL to CSP' do it 'does not add CUSTOMER_PORTAL_URL to CSP' do
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html") expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end end
end end
...@@ -123,7 +123,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do ...@@ -123,7 +123,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end end
it 'adds CUSTOMER_PORTAL_URL to CSP' do it 'adds CUSTOMER_PORTAL_URL to CSP' do
expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html") expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/ http://localhost/assets/ http://localhost/-/speedscope/index.html http://localhost/-/sandbox/mermaid")
end end
end end
end end
......
...@@ -161,7 +161,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -161,7 +161,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
another_project = create(:project, :repository, creator: another_user) another_project = create(:project, :repository, creator: another_user)
create(:remote_mirror, project: another_project, enabled: false) create(:remote_mirror, project: another_project, enabled: false)
create(:snippet, author: user) create(:snippet, author: user)
create(:suggestion, note: create(:note, project: project))
end end
expect(described_class.usage_activity_by_stage_create({})).to include( expect(described_class.usage_activity_by_stage_create({})).to include(
...@@ -171,8 +170,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -171,8 +170,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects_with_disable_overriding_approvers_per_merge_request: 2, projects_with_disable_overriding_approvers_per_merge_request: 2,
projects_without_disable_overriding_approvers_per_merge_request: 6, projects_without_disable_overriding_approvers_per_merge_request: 6,
remote_mirrors: 2, remote_mirrors: 2,
snippets: 2, snippets: 2
suggestions: 2
) )
expect(described_class.usage_activity_by_stage_create(described_class.monthly_time_range_db_params)).to include( expect(described_class.usage_activity_by_stage_create(described_class.monthly_time_range_db_params)).to include(
deploy_keys: 1, deploy_keys: 1,
...@@ -181,8 +179,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -181,8 +179,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects_with_disable_overriding_approvers_per_merge_request: 1, projects_with_disable_overriding_approvers_per_merge_request: 1,
projects_without_disable_overriding_approvers_per_merge_request: 3, projects_without_disable_overriding_approvers_per_merge_request: 3,
remote_mirrors: 1, remote_mirrors: 1,
snippets: 1, snippets: 1
suggestions: 1
) )
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe SandboxController do
describe 'GET #mermaid' do
it 'renders page without template' do
get sandbox_mermaid_path
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(layout: nil)
end
end
end
...@@ -364,6 +364,12 @@ RSpec.describe AutocompleteController, 'routing' do ...@@ -364,6 +364,12 @@ RSpec.describe AutocompleteController, 'routing' do
end end
end end
RSpec.describe SandboxController, 'routing' do
it 'to #mermaid' do
expect(get("/-/sandbox/mermaid")).to route_to('sandbox#mermaid')
end
end
RSpec.describe Snippets::BlobsController, "routing" do RSpec.describe Snippets::BlobsController, "routing" do
it "to #raw" do it "to #raw" do
expect(get('/-/snippets/1/raw/master/lib/version.rb')) expect(get('/-/snippets/1/raw/master/lib/version.rb'))
......
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